From f4f909522f0902914a2cf62e8d0311aaab0e6db2 Mon Sep 17 00:00:00 2001 From: luboslenco Date: Sun, 10 Nov 2024 22:11:29 +0100 Subject: [PATCH] Add armorcore --- armorcore/make | 5 + armorcore/make.bat | 1 + armorcore/project.js | 392 + armorcore/readme.md | 5 + armorcore/shaders/g2_colored.frag.glsl | 8 + armorcore/shaders/g2_colored.vert.glsl | 12 + armorcore/shaders/g2_image.frag.glsl | 13 + armorcore/shaders/g2_image.vert.glsl | 15 + armorcore/shaders/g2_text.frag.glsl | 11 + armorcore/shaders/g2_text.vert.glsl | 15 + armorcore/sources/.clang-format | 84 + .../android/android_native_app_glue.c | 441 + .../android/android_native_app_glue.h | 349 + .../android/java/arm/AndroidHttpRequest.java | 31 + .../android/java/tech/kinc/KincActivity.kt | 267 + .../android/java/tech/kinc/KincMoviePlayer.kt | 50 + .../java/tech/kinc/KincMovieTexture.kt | 62 + .../backends/android/kinc/backend/Android.h | 18 + .../kinc/backend/android_file_dialog.c | 111 + .../kinc/backend/android_file_dialog.h | 15 + .../kinc/backend/android_http_request.c | 28 + .../kinc/backend/android_http_request.h | 16 + .../android/kinc/backend/androidunit.c | 5 + .../backends/android/kinc/backend/audio.c.h | 133 + .../backends/android/kinc/backend/display.c.h | 106 + .../backends/android/kinc/backend/system.c.h | 1298 + .../backends/android/kinc/backend/video.c.h | 581 + .../backends/android/kinc/backend/video.h | 49 + .../backends/android/kinc/backend/window.c.h | 79 + .../android/kinc/backend/windowdata.h | 1 + .../backends/apple/kinc/backend/appleunit.m | 4 + .../backends/apple/kinc/backend/http.m.h | 53 + .../backends/apple/kinc/backend/system.m.h | 24 + .../backends/apple/kinc/backend/thread.m.h | 50 + .../backends/apple/kinc/backend/video.h | 56 + .../backends/apple/kinc/backend/video.m.h | 311 + .../backends/data/android/app/CMakeLists.txt | 21 + .../data/android/app/build.gradle.kts | 61 + .../data/android/app/proguard-rules.pro | 34 + .../backends/data/android/build.gradle.kts | 5 + .../backends/data/android/gradle.properties | 23 + .../android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../sources/backends/data/android/gradlew | 185 + .../sources/backends/data/android/gradlew.bat | 89 + .../data/android/main/AndroidManifest.xml | 26 + .../data/android/main/res/values/strings.xml | 3 + .../backends/data/android/settings.gradle.kts | 18 + .../data/wasm/JS-Sources/audio-thread.js | 202 + .../backends/data/wasm/JS-Sources/index.html | 6 + .../backends/data/wasm/JS-Sources/start.js | 541 + .../data/wasm/JS-Sources/thread_starter.js | 24 + .../kinc/backend/graphics4/Direct3D11.c.h | 1226 + .../kinc/backend/graphics4/Direct3D11.h | 41 + .../kinc/backend/graphics4/ShaderHash.c.h | 11 + .../kinc/backend/graphics4/ShaderHash.h | 19 + .../kinc/backend/graphics4/compute.c.h | 195 + .../kinc/backend/graphics4/compute.h | 43 + .../kinc/backend/graphics4/d3d11unit.c | 107 + .../kinc/backend/graphics4/indexbuffer.c.h | 89 + .../kinc/backend/graphics4/indexbuffer.h | 13 + .../kinc/backend/graphics4/pipeline.c.h | 866 + .../kinc/backend/graphics4/pipeline.h | 37 + .../kinc/backend/graphics4/rendertarget.c.h | 456 + .../kinc/backend/graphics4/rendertarget.h | 27 + .../kinc/backend/graphics4/shader.c.h | 112 + .../kinc/backend/graphics4/shader.h | 60 + .../kinc/backend/graphics4/texture.c.h | 371 + .../kinc/backend/graphics4/texture.h | 24 + .../kinc/backend/graphics4/vertexbuffer.c.h | 95 + .../kinc/backend/graphics4/vertexbuffer.h | 13 + .../sources/backends/direct3d12/d3dx12.h | 2654 + .../kinc/backend/graphics5/Direct3D12.c.h | 622 + .../kinc/backend/graphics5/ShaderHash.c.h | 11 + .../kinc/backend/graphics5/ShaderHash.h | 19 + .../kinc/backend/graphics5/commandlist.c.h | 611 + .../kinc/backend/graphics5/commandlist.h | 46 + .../kinc/backend/graphics5/compute.c.h | 167 + .../kinc/backend/graphics5/compute.h | 30 + .../kinc/backend/graphics5/constantbuffer.c.h | 69 + .../kinc/backend/graphics5/constantbuffer.h | 21 + .../kinc/backend/graphics5/d3d12mini.h | 56 + .../kinc/backend/graphics5/d3d12unit.cpp | 106 + .../kinc/backend/graphics5/graphics.h | 6 + .../kinc/backend/graphics5/indexbuffer.c.h | 130 + .../kinc/backend/graphics5/indexbuffer.h | 32 + .../kinc/backend/graphics5/pipeline.c.h | 559 + .../kinc/backend/graphics5/pipeline.h | 77 + .../kinc/backend/graphics5/raytrace.c.h | 752 + .../kinc/backend/graphics5/raytrace.h | 24 + .../kinc/backend/graphics5/rendertarget.c.h | 287 + .../kinc/backend/graphics5/rendertarget.h | 28 + .../kinc/backend/graphics5/sampler.c.h | 145 + .../kinc/backend/graphics5/sampler.h | 7 + .../kinc/backend/graphics5/shader.c.h | 87 + .../kinc/backend/graphics5/shader.h | 36 + .../kinc/backend/graphics5/texture.c.h | 425 + .../kinc/backend/graphics5/texture.h | 32 + .../kinc/backend/graphics5/vertexbuffer.c.h | 123 + .../kinc/backend/graphics5/vertexbuffer.h | 30 + .../g4ong5/kinc/backend/graphics4/G4.c.h | 852 + .../g4ong5/kinc/backend/graphics4/G4.h | 4 + .../g4ong5/kinc/backend/graphics4/compute.c.h | 35 + .../g4ong5/kinc/backend/graphics4/compute.h | 7 + .../kinc/backend/graphics4/g4ong5unit.c | 10 + .../g4ong5/kinc/backend/graphics4/graphics.h | 6 + .../kinc/backend/graphics4/indexbuffer.c.h | 35 + .../kinc/backend/graphics4/indexbuffer.h | 15 + .../kinc/backend/graphics4/pipeline.c.h | 62 + .../g4ong5/kinc/backend/graphics4/pipeline.h | 25 + .../kinc/backend/graphics4/rendertarget.c.h | 36 + .../kinc/backend/graphics4/rendertarget.h | 21 + .../kinc/backend/graphics4/samplers.c.h | 50 + .../g4ong5/kinc/backend/graphics4/shader.c.h | 49 + .../g4ong5/kinc/backend/graphics4/shader.h | 16 + .../g4ong5/kinc/backend/graphics4/texture.c.h | 70 + .../g4ong5/kinc/backend/graphics4/texture.h | 29 + .../kinc/backend/graphics4/vertexbuffer.c.h | 64 + .../kinc/backend/graphics4/vertexbuffer.h | 20 + .../g5ong4/kinc/backend/graphics5/G5onG4.c.h | 67 + .../g5ong4/kinc/backend/graphics5/G5onG4.h | 4 + .../kinc/backend/graphics5/commandlist.c.h | 384 + .../kinc/backend/graphics5/commandlist.h | 21 + .../g5ong4/kinc/backend/graphics5/compute.c | 28 + .../g5ong4/kinc/backend/graphics5/compute.h | 7 + .../kinc/backend/graphics5/constantbuffer.c.h | 33 + .../kinc/backend/graphics5/constantbuffer.h | 15 + .../kinc/backend/graphics5/g5ong4unit.c | 10 + .../g5ong4/kinc/backend/graphics5/graphics.h | 6 + .../kinc/backend/graphics5/indexbuffer.c.h | 34 + .../kinc/backend/graphics5/indexbuffer.h | 16 + .../kinc/backend/graphics5/pipeline.c.h | 63 + .../g5ong4/kinc/backend/graphics5/pipeline.h | 33 + .../kinc/backend/graphics5/rendertarget.c.h | 45 + .../kinc/backend/graphics5/rendertarget.h | 14 + .../g5ong4/kinc/backend/graphics5/sampler.c.h | 5 + .../g5ong4/kinc/backend/graphics5/sampler.h | 5 + .../g5ong4/kinc/backend/graphics5/shader.c.h | 10 + .../g5ong4/kinc/backend/graphics5/shader.h | 15 + .../g5ong4/kinc/backend/graphics5/texture.c.h | 50 + .../g5ong4/kinc/backend/graphics5/texture.h | 20 + .../kinc/backend/graphics5/vertexbuffer.c.h | 39 + .../kinc/backend/graphics5/vertexbuffer.h | 17 + .../backends/ios/kinc/backend/GLView.h | 50 + .../backends/ios/kinc/backend/GLView.m.h | 460 + .../ios/kinc/backend/GLViewController.h | 17 + .../ios/kinc/backend/GLViewController.m.h | 130 + .../ios/kinc/backend/KoreAppDelegate.h | 8 + .../ios/kinc/backend/KoreAppDelegate.m.h | 142 + .../ios/kinc/backend/LaunchScreen.storyboard | 27 + .../backends/ios/kinc/backend/audio.m.h | 223 + .../backends/ios/kinc/backend/display.h | 12 + .../backends/ios/kinc/backend/display.m.h | 44 + .../backends/ios/kinc/backend/displaydata.h | 5 + .../backends/ios/kinc/backend/ios.plist | 53 + .../ios/kinc/backend/ios_file_dialog.h | 7 + .../ios/kinc/backend/ios_file_dialog.m | 25 + .../backends/ios/kinc/backend/iosunit.m | 9 + .../backends/ios/kinc/backend/motion.h | 11 + .../backends/ios/kinc/backend/motion.m.h | 26 + .../backends/ios/kinc/backend/mouse.c.h | 19 + .../backends/ios/kinc/backend/system.m.h | 184 + .../backends/ios/kinc/backend/window.c.h | 71 + .../backends/ios/kinc/backend/windowdata.h | 5 + armorcore/sources/backends/license.txt | 19 + .../backends/linux/kinc/backend/display.c.h | 43 + .../backends/linux/kinc/backend/funcs.h | 67 + .../backends/linux/kinc/backend/gamepad.c.h | 199 + .../backends/linux/kinc/backend/gamepad.h | 5 + .../backends/linux/kinc/backend/linuxunit.c | 160 + .../backends/linux/kinc/backend/mouse.c.h | 36 + .../backends/linux/kinc/backend/sound.c.h | 233 + .../backends/linux/kinc/backend/system.c.h | 427 + .../backends/linux/kinc/backend/video.c.h | 59 + .../backends/linux/kinc/backend/video.h | 31 + .../linux/kinc/backend/wayland/display.c.h | 52 + .../linux/kinc/backend/wayland/system.c.h | 1327 + .../linux/kinc/backend/wayland/wayland-funs.h | 54 + .../linux/kinc/backend/wayland/wayland.h | 367 + .../linux/kinc/backend/wayland/window.c.h | 469 + .../backends/linux/kinc/backend/window.c.h | 140 + .../linux/kinc/backend/x11/display.c.h | 221 + .../linux/kinc/backend/x11/system.c.h | 1057 + .../linux/kinc/backend/x11/window.c.h | 206 + .../backends/linux/kinc/backend/x11/x11.h | 200 + .../macos/kinc/backend/BasicOpenGLView.h | 69 + .../macos/kinc/backend/BasicOpenGLView.m.h | 483 + .../macos/kinc/backend/HIDGamepad.c.h | 384 + .../backends/macos/kinc/backend/HIDGamepad.h | 23 + .../macos/kinc/backend/HIDManager.c.h | 140 + .../backends/macos/kinc/backend/HIDManager.h | 28 + .../backends/macos/kinc/backend/audio.c.h | 158 + .../backends/macos/kinc/backend/display.m.h | 112 + .../backends/macos/kinc/backend/displaydata.h | 6 + .../backends/macos/kinc/backend/mac.plist | 34 + .../backends/macos/kinc/backend/macosunit.m | 24 + .../backends/macos/kinc/backend/mouse.m.h | 49 + .../backends/macos/kinc/backend/system.c.h | 62 + .../backends/macos/kinc/backend/system.m.h | 323 + .../backends/macos/kinc/backend/window.c.h | 58 + .../backends/macos/kinc/backend/windowdata.h | 5 + .../metal/kinc/backend/graphics5/Metal.h | 5 + .../metal/kinc/backend/graphics5/Metal.m.h | 253 + .../kinc/backend/graphics5/commandlist.h | 7 + .../kinc/backend/graphics5/commandlist.m.h | 379 + .../metal/kinc/backend/graphics5/compute.h | 8 + .../metal/kinc/backend/graphics5/compute.m.h | 112 + .../kinc/backend/graphics5/constantbuffer.h | 8 + .../kinc/backend/graphics5/constantbuffer.m.h | 40 + .../metal/kinc/backend/graphics5/graphics.h | 6 + .../kinc/backend/graphics5/indexbuffer.h | 10 + .../kinc/backend/graphics5/indexbuffer.m.h | 73 + .../metal/kinc/backend/graphics5/metalunit.m | 22 + .../metal/kinc/backend/graphics5/pipeline.h | 24 + .../metal/kinc/backend/graphics5/pipeline.m.h | 433 + .../metal/kinc/backend/graphics5/raytrace.h | 9 + .../metal/kinc/backend/graphics5/raytrace.m.h | 216 + .../kinc/backend/graphics5/rendertarget.h | 7 + .../kinc/backend/graphics5/rendertarget.m.h | 164 + .../metal/kinc/backend/graphics5/sampler.h | 5 + .../metal/kinc/backend/graphics5/sampler.m.h | 67 + .../metal/kinc/backend/graphics5/shader.h | 6 + .../metal/kinc/backend/graphics5/shader.m.h | 56 + .../metal/kinc/backend/graphics5/texture.h | 14 + .../metal/kinc/backend/graphics5/texture.m.h | 291 + .../kinc/backend/graphics5/vertexbuffer.h | 14 + .../kinc/backend/graphics5/vertexbuffer.m.h | 111 + .../microsoft/kinc/backend/MiniWindows.h | 102 + .../kinc/backend/SystemMicrosoft.c.h | 46 + .../microsoft/kinc/backend/SystemMicrosoft.h | 17 + .../backends/microsoft/kinc/backend/atomic.h | 53 + .../backends/microsoft/kinc/backend/event.c.h | 25 + .../backends/microsoft/kinc/backend/event.h | 13 + .../backends/microsoft/kinc/backend/fiber.c.h | 25 + .../backends/microsoft/kinc/backend/fiber.h | 15 + .../microsoft/kinc/backend/microsoftunit.c | 56 + .../backends/microsoft/kinc/backend/mutex.c.h | 60 + .../backends/microsoft/kinc/backend/mutex.h | 26 + .../microsoft/kinc/backend/semaphore.c.h | 22 + .../microsoft/kinc/backend/semaphore.h | 5 + .../microsoft/kinc/backend/thread.c.h | 71 + .../backends/microsoft/kinc/backend/thread.h | 15 + .../microsoft/kinc/backend/threadlocal.c.h | 18 + .../microsoft/kinc/backend/threadlocal.h | 13 + .../opengl/kinc/backend/graphics4/OpenGL.c.h | 1166 + .../opengl/kinc/backend/graphics4/OpenGL.h | 17 + .../kinc/backend/graphics4/OpenGLWindow.c.h | 212 + .../kinc/backend/graphics4/OpenGLWindow.h | 34 + .../opengl/kinc/backend/graphics4/compute.c.h | 195 + .../opengl/kinc/backend/graphics4/compute.h | 20 + .../opengl/kinc/backend/graphics4/graphics.h | 6 + .../kinc/backend/graphics4/indexbuffer.c.h | 105 + .../kinc/backend/graphics4/indexbuffer.h | 20 + .../opengl/kinc/backend/graphics4/ogl.h | 66 + .../kinc/backend/graphics4/openglunit.c | 14 + .../kinc/backend/graphics4/pipeline.c.h | 482 + .../opengl/kinc/backend/graphics4/pipeline.h | 16 + .../kinc/backend/graphics4/rendertarget.c.h | 412 + .../kinc/backend/graphics4/rendertarget.h | 20 + .../opengl/kinc/backend/graphics4/shader.c.h | 53 + .../opengl/kinc/backend/graphics4/shader.h | 27 + .../opengl/kinc/backend/graphics4/texture.c.h | 625 + .../opengl/kinc/backend/graphics4/texture.h | 19 + .../kinc/backend/graphics4/vertexbuffer.c.h | 304 + .../kinc/backend/graphics4/vertexbuffer.h | 29 + .../backends/posix/kinc/backend/atomic.h | 103 + .../backends/posix/kinc/backend/event.c.h | 78 + .../backends/posix/kinc/backend/event.h | 18 + .../backends/posix/kinc/backend/mutex.c.h | 40 + .../backends/posix/kinc/backend/mutex.h | 19 + .../backends/posix/kinc/backend/posixunit.c | 5 + .../backends/posix/kinc/backend/semaphore.c.h | 56 + .../backends/posix/kinc/backend/semaphore.h | 23 + .../backends/posix/kinc/backend/thread.c.h | 82 + .../backends/posix/kinc/backend/thread.h | 17 + .../posix/kinc/backend/threadlocal.c.h | 17 + .../backends/posix/kinc/backend/threadlocal.h | 15 + .../kinc/backend/graphics5/MiniVulkan.h | 3 + .../kinc/backend/graphics5/ShaderHash.c.h | 11 + .../kinc/backend/graphics5/ShaderHash.h | 19 + .../vulkan/kinc/backend/graphics5/Vulkan.c.h | 1237 + .../kinc/backend/graphics5/commandlist.c.h | 931 + .../kinc/backend/graphics5/commandlist.h | 9 + .../vulkan/kinc/backend/graphics5/compute.c.h | 237 + .../vulkan/kinc/backend/graphics5/compute.h | 25 + .../kinc/backend/graphics5/constantbuffer.c.h | 89 + .../kinc/backend/graphics5/constantbuffer.h | 13 + .../vulkan/kinc/backend/graphics5/graphics.h | 6 + .../kinc/backend/graphics5/indexbuffer.c.h | 95 + .../kinc/backend/graphics5/indexbuffer.h | 12 + .../kinc/backend/graphics5/named_number.h | 8 + .../kinc/backend/graphics5/pipeline.c.h | 1224 + .../vulkan/kinc/backend/graphics5/pipeline.h | 40 + .../kinc/backend/graphics5/raytrace.c.h | 1069 + .../vulkan/kinc/backend/graphics5/raytrace.h | 32 + .../kinc/backend/graphics5/rendertarget.c.h | 331 + .../kinc/backend/graphics5/rendertarget.h | 25 + .../vulkan/kinc/backend/graphics5/sampler.c.h | 78 + .../vulkan/kinc/backend/graphics5/sampler.h | 7 + .../vulkan/kinc/backend/graphics5/shader.c.h | 16 + .../vulkan/kinc/backend/graphics5/shader.h | 7 + .../vulkan/kinc/backend/graphics5/texture.c.h | 420 + .../vulkan/kinc/backend/graphics5/texture.h | 20 + .../kinc/backend/graphics5/vertexbuffer.c.h | 119 + .../kinc/backend/graphics5/vertexbuffer.h | 21 + .../vulkan/kinc/backend/graphics5/vulkan.h | 94 + .../kinc/backend/graphics5/vulkanunit.c | 89 + .../backends/wasapi/kinc/backend/wasapi.c | 308 + armorcore/sources/backends/wasm/GL/gl.h | 246 + .../backends/wasm/kinc/backend/atomic.h | 13 + .../backends/wasm/kinc/backend/audio.c.h | 18 + .../backends/wasm/kinc/backend/display.c.h | 47 + .../backends/wasm/kinc/backend/event.c.h | 15 + .../backends/wasm/kinc/backend/event.h | 13 + .../backends/wasm/kinc/backend/html5unit.c | 11 + .../backends/wasm/kinc/backend/mouse.c.h | 17 + .../backends/wasm/kinc/backend/mutex.c.h | 23 + .../backends/wasm/kinc/backend/mutex.h | 17 + .../backends/wasm/kinc/backend/semaphore.c.h | 13 + .../backends/wasm/kinc/backend/semaphore.h | 13 + .../backends/wasm/kinc/backend/system.c.h | 100 + .../backends/wasm/kinc/backend/thread.c.h | 15 + .../backends/wasm/kinc/backend/thread.h | 13 + .../wasm/kinc/backend/threadlocal.c.h | 13 + .../backends/wasm/kinc/backend/threadlocal.h | 13 + .../backends/wasm/kinc/backend/video.c.h | 57 + .../backends/wasm/kinc/backend/video.h | 27 + .../backends/wasm/kinc/backend/window.c.h | 81 + .../backends/wasm/kinc/backend/windowdata.h | 8 + .../webgpu/kinc/backend/graphics5/WebGPU.c | 92 + .../webgpu/kinc/backend/graphics5/WebGPU.h | 4 + .../kinc/backend/graphics5/commandlist.c | 148 + .../kinc/backend/graphics5/commandlist.h | 17 + .../webgpu/kinc/backend/graphics5/compute.c | 16 + .../webgpu/kinc/backend/graphics5/compute.h | 5 + .../kinc/backend/graphics5/constantbuffer.c | 34 + .../kinc/backend/graphics5/constantbuffer.h | 15 + .../webgpu/kinc/backend/graphics5/graphics.h | 6 + .../kinc/backend/graphics5/indexbuffer.c | 52 + .../kinc/backend/graphics5/indexbuffer.h | 17 + .../webgpu/kinc/backend/graphics5/pipeline.c | 223 + .../webgpu/kinc/backend/graphics5/pipeline.h | 25 + .../kinc/backend/graphics5/rendertarget.c | 21 + .../kinc/backend/graphics5/rendertarget.h | 15 + .../webgpu/kinc/backend/graphics5/sampler.c | 5 + .../webgpu/kinc/backend/graphics5/sampler.h | 5 + .../webgpu/kinc/backend/graphics5/shader.c | 19 + .../webgpu/kinc/backend/graphics5/shader.h | 17 + .../webgpu/kinc/backend/graphics5/texture.c | 55 + .../webgpu/kinc/backend/graphics5/texture.h | 15 + .../kinc/backend/graphics5/vertexbuffer.c | 50 + .../kinc/backend/graphics5/vertexbuffer.h | 17 + .../backends/windows/kinc/backend/Windows.c.h | 1 + .../backends/windows/kinc/backend/Windows.h | 25 + .../backends/windows/kinc/backend/base.c.h | 31 + .../backends/windows/kinc/backend/display.c.h | 201 + .../backends/windows/kinc/backend/http.c.h | 111 + .../backends/windows/kinc/backend/mouse.c.h | 52 + .../backends/windows/kinc/backend/system.c.h | 1463 + .../backends/windows/kinc/backend/video.c.h | 59 + .../backends/windows/kinc/backend/video.h | 31 + .../backends/windows/kinc/backend/window.c.h | 471 + .../windows/kinc/backend/windowsunit.c | 127 + armorcore/sources/const_data.c | 12 + armorcore/sources/const_data.h | 9 + armorcore/sources/iron.c | 1 + armorcore/sources/iron.h | 2995 + armorcore/sources/iron_armpack.c | 749 + armorcore/sources/iron_armpack.h | 99 + armorcore/sources/iron_array.c | 615 + armorcore/sources/iron_array.h | 149 + armorcore/sources/iron_gc.c | 131 + armorcore/sources/iron_gc.h | 20 + armorcore/sources/iron_json.c | 416 + armorcore/sources/iron_json.h | 24 + armorcore/sources/iron_map.c | 220 + armorcore/sources/iron_map.h | 53 + armorcore/sources/iron_mat3.c | 82 + armorcore/sources/iron_mat3.h | 15 + armorcore/sources/iron_mat4.c | 556 + armorcore/sources/iron_mat4.h | 49 + armorcore/sources/iron_obj.c | 642 + armorcore/sources/iron_obj.h | 29 + armorcore/sources/iron_quat.c | 175 + armorcore/sources/iron_quat.h | 18 + armorcore/sources/iron_string.c | 273 + armorcore/sources/iron_string.h | 34 + armorcore/sources/iron_ui.c | 2579 + armorcore/sources/iron_ui.h | 365 + armorcore/sources/iron_ui_ext.c | 729 + armorcore/sources/iron_ui_ext.h | 24 + armorcore/sources/iron_ui_nodes.c | 1578 + armorcore/sources/iron_ui_nodes.h | 154 + armorcore/sources/iron_vec2.c | 72 + armorcore/sources/iron_vec2.h | 16 + armorcore/sources/iron_vec3.c | 4 + armorcore/sources/iron_vec3.h | 3 + armorcore/sources/iron_vec4.c | 194 + armorcore/sources/iron_vec4.h | 43 + armorcore/sources/kinc/audio1/a1unit.c | 25 + armorcore/sources/kinc/audio1/audio.c.h | 257 + armorcore/sources/kinc/audio1/audio.h | 97 + armorcore/sources/kinc/audio1/sound.c.h | 224 + armorcore/sources/kinc/audio1/sound.h | 58 + armorcore/sources/kinc/audio1/soundstream.c.h | 128 + armorcore/sources/kinc/audio1/soundstream.h | 116 + armorcore/sources/kinc/audio2/audio.c | 3 + armorcore/sources/kinc/audio2/audio.h | 123 + armorcore/sources/kinc/color.h | 46 + armorcore/sources/kinc/display.h | 86 + armorcore/sources/kinc/error.h | 200 + armorcore/sources/kinc/global.h | 62 + armorcore/sources/kinc/graphics2/g2.c | 1114 + armorcore/sources/kinc/graphics2/g2.h | 64 + armorcore/sources/kinc/graphics2/g2_ext.c | 124 + armorcore/sources/kinc/graphics2/g2_ext.h | 5 + armorcore/sources/kinc/graphics2/g2_font.h | 1036 + .../sources/kinc/graphics2/stb_truetype.h | 5077 ++ armorcore/sources/kinc/graphics4/compute.h | 58 + .../sources/kinc/graphics4/constantlocation.h | 22 + armorcore/sources/kinc/graphics4/g4unit.c | 4 + armorcore/sources/kinc/graphics4/graphics.c.h | 11 + armorcore/sources/kinc/graphics4/graphics.h | 418 + .../sources/kinc/graphics4/indexbuffer.h | 84 + armorcore/sources/kinc/graphics4/pipeline.c.h | 55 + armorcore/sources/kinc/graphics4/pipeline.h | 159 + .../sources/kinc/graphics4/rendertarget.c.h | 11 + .../sources/kinc/graphics4/rendertarget.h | 135 + armorcore/sources/kinc/graphics4/shader.h | 58 + armorcore/sources/kinc/graphics4/texture.h | 107 + .../sources/kinc/graphics4/textureunit.h | 21 + armorcore/sources/kinc/graphics4/usage.h | 19 + .../sources/kinc/graphics4/vertexbuffer.c.h | 20 + .../sources/kinc/graphics4/vertexbuffer.h | 101 + .../sources/kinc/graphics4/vertexstructure.h | 157 + .../sources/kinc/graphics5/commandlist.c.h | 1 + .../sources/kinc/graphics5/commandlist.h | 333 + armorcore/sources/kinc/graphics5/compute.h | 54 + .../sources/kinc/graphics5/constantbuffer.c.h | 112 + .../sources/kinc/graphics5/constantbuffer.h | 175 + .../sources/kinc/graphics5/constantlocation.h | 21 + armorcore/sources/kinc/graphics5/g5unit.c | 7 + armorcore/sources/kinc/graphics5/graphics.c.h | 22 + armorcore/sources/kinc/graphics5/graphics.h | 111 + .../sources/kinc/graphics5/indexbuffer.h | 73 + armorcore/sources/kinc/graphics5/pipeline.c.h | 47 + armorcore/sources/kinc/graphics5/pipeline.h | 154 + armorcore/sources/kinc/graphics5/raytrace.h | 51 + .../sources/kinc/graphics5/rendertarget.c.h | 16 + .../sources/kinc/graphics5/rendertarget.h | 134 + armorcore/sources/kinc/graphics5/sampler.c.h | 19 + armorcore/sources/kinc/graphics5/sampler.h | 78 + armorcore/sources/kinc/graphics5/shader.h | 50 + armorcore/sources/kinc/graphics5/texture.c.h | 32 + armorcore/sources/kinc/graphics5/texture.h | 98 + .../sources/kinc/graphics5/textureunit.h | 24 + .../sources/kinc/graphics5/vertexbuffer.h | 85 + .../sources/kinc/graphics5/vertexstructure.h | 45 + armorcore/sources/kinc/image.h | 750 + armorcore/sources/kinc/input/acceleration.h | 46 + armorcore/sources/kinc/input/gamepad.h | 143 + armorcore/sources/kinc/input/inputunit.c | 9 + armorcore/sources/kinc/input/keyboard.h | 294 + armorcore/sources/kinc/input/mouse.h | 297 + armorcore/sources/kinc/input/pen.h | 138 + armorcore/sources/kinc/input/rotation.h | 46 + armorcore/sources/kinc/input/surface.h | 82 + armorcore/sources/kinc/io/filereader.h | 586 + armorcore/sources/kinc/io/filewriter.h | 132 + armorcore/sources/kinc/io/iounit.c | 4 + armorcore/sources/kinc/libs/lz4x.h | 299 + armorcore/sources/kinc/libs/neon_mathfun.h | 301 + armorcore/sources/kinc/libs/sse_mathfun.h | 711 + armorcore/sources/kinc/libs/stb_image.h | 7554 +++ armorcore/sources/kinc/libs/stb_sprintf.h | 1906 + armorcore/sources/kinc/libs/stb_vorbis.c | 5520 ++ armorcore/sources/kinc/license.txt | 19 + armorcore/sources/kinc/log.h | 137 + armorcore/sources/kinc/math/core.h | 70 + armorcore/sources/kinc/math/mathunit.c | 5 + armorcore/sources/kinc/math/matrix.h | 227 + armorcore/sources/kinc/math/quaternion.h | 22 + armorcore/sources/kinc/math/random.h | 98 + armorcore/sources/kinc/math/vector.h | 33 + armorcore/sources/kinc/network/http.h | 49 + armorcore/sources/kinc/network/networkunit.c | 4 + armorcore/sources/kinc/network/socket.h | 665 + armorcore/sources/kinc/readme.md | 2 + armorcore/sources/kinc/rootunit.c | 15 + armorcore/sources/kinc/simd/float32x4.h | 719 + armorcore/sources/kinc/simd/int16x8.h | 463 + armorcore/sources/kinc/simd/int32x4.h | 387 + armorcore/sources/kinc/simd/int8x16.h | 620 + .../sources/kinc/simd/type_conversions.h | 800 + armorcore/sources/kinc/simd/types.h | 180 + armorcore/sources/kinc/simd/uint16x8.h | 501 + armorcore/sources/kinc/simd/uint32x4.h | 417 + armorcore/sources/kinc/simd/uint8x16.h | 615 + armorcore/sources/kinc/system.h | 662 + armorcore/sources/kinc/threads/atomic.h | 9 + armorcore/sources/kinc/threads/event.h | 62 + armorcore/sources/kinc/threads/fiber.h | 47 + armorcore/sources/kinc/threads/mutex.h | 86 + armorcore/sources/kinc/threads/semaphore.h | 58 + armorcore/sources/kinc/threads/thread.h | 66 + armorcore/sources/kinc/threads/threadlocal.h | 46 + armorcore/sources/kinc/video.h | 102 + armorcore/sources/kinc/window.h | 215 + armorcore/sources/libs/dir.c | 94 + armorcore/sources/libs/dir.h | 16 + armorcore/sources/libs/gc.c | 503 + armorcore/sources/libs/gc.h | 21 + armorcore/sources/libs/jo_mpeg.h | 261 + armorcore/sources/libs/jsmn.h | 471 + armorcore/sources/libs/miniClib/assert.h | 7 + armorcore/sources/libs/miniClib/errno.h | 3 + armorcore/sources/libs/miniClib/license.txt | 19 + armorcore/sources/libs/miniClib/math.c | 100 + armorcore/sources/libs/miniClib/math.h | 35 + armorcore/sources/libs/miniClib/memory.c | 68 + armorcore/sources/libs/miniClib/memory.h | 11 + armorcore/sources/libs/miniClib/stdbool.h | 7 + armorcore/sources/libs/miniClib/stdio.c | 62 + armorcore/sources/libs/miniClib/stdio.h | 37 + armorcore/sources/libs/miniClib/stdlib.c | 21 + armorcore/sources/libs/miniClib/stdlib.h | 32 + armorcore/sources/libs/miniClib/string.c | 173 + armorcore/sources/libs/miniClib/string.h | 46 + armorcore/sources/libs/miniClib/time.h | 11 + armorcore/sources/libs/nfd/LICENSE | 16 + armorcore/sources/libs/nfd/common.h | 21 + armorcore/sources/libs/nfd/nfd.h | 74 + armorcore/sources/libs/nfd/nfd_cocoa.m | 286 + armorcore/sources/libs/nfd/nfd_common.c | 142 + armorcore/sources/libs/nfd/nfd_common.h | 39 + armorcore/sources/libs/nfd/nfd_gtk.c | 379 + armorcore/sources/libs/nfd/nfd_win.cpp | 762 + armorcore/sources/libs/nfd/simple_exec.h | 218 + armorcore/sources/libs/quickjs/LICENSE | 24 + armorcore/sources/libs/quickjs/VERSION | 2 + armorcore/sources/libs/quickjs/cutils.c | 1407 + armorcore/sources/libs/quickjs/cutils.h | 539 + .../sources/libs/quickjs/dirent_compat.h | 1166 + armorcore/sources/libs/quickjs/libbf.c | 8417 +++ armorcore/sources/libs/quickjs/libbf.h | 545 + .../sources/libs/quickjs/libregexp-opcode.h | 59 + armorcore/sources/libs/quickjs/libregexp.c | 2729 + armorcore/sources/libs/quickjs/libregexp.h | 95 + .../sources/libs/quickjs/libunicode-table.h | 4484 ++ armorcore/sources/libs/quickjs/libunicode.c | 1504 + armorcore/sources/libs/quickjs/libunicode.h | 126 + armorcore/sources/libs/quickjs/list.h | 107 + armorcore/sources/libs/quickjs/quickjs-atom.h | 255 + .../sources/libs/quickjs/quickjs-c-atomics.h | 54 + armorcore/sources/libs/quickjs/quickjs-libc.c | 4091 ++ armorcore/sources/libs/quickjs/quickjs-libc.h | 60 + .../sources/libs/quickjs/quickjs-opcode.h | 371 + armorcore/sources/libs/quickjs/quickjs.c | 53217 ++++++++++++++++ armorcore/sources/libs/quickjs/quickjs.h | 1034 + armorcore/sources/libs/sdefl.h | 790 + armorcore/sources/libs/sinfl.h | 613 + armorcore/sources/libs/stb_image_write.h | 1690 + armorcore/sources/ts/anim.ts | 275 + armorcore/sources/ts/anim_bone.ts | 720 + armorcore/sources/ts/anim_object.ts | 230 + armorcore/sources/ts/app.ts | 223 + armorcore/sources/ts/armature.ts | 103 + armorcore/sources/ts/audio.ts | 12 + armorcore/sources/ts/camera_data.ts | 21 + armorcore/sources/ts/camera_object.ts | 198 + armorcore/sources/ts/const_data.ts | 72 + armorcore/sources/ts/data.ts | 341 + armorcore/sources/ts/g2.ts | 227 + armorcore/sources/ts/g4.ts | 706 + armorcore/sources/ts/input.ts | 968 + armorcore/sources/ts/iron.ts | 569 + armorcore/sources/ts/light_data.ts | 21 + armorcore/sources/ts/light_object.ts | 43 + armorcore/sources/ts/lz4.ts | 225 + armorcore/sources/ts/material_data.ts | 116 + armorcore/sources/ts/mesh_data.ts | 381 + armorcore/sources/ts/mesh_object.ts | 311 + armorcore/sources/ts/object.ts | 171 + armorcore/sources/ts/particle_data.ts | 21 + armorcore/sources/ts/particle_sys.ts | 254 + armorcore/sources/ts/raycast.ts | 326 + armorcore/sources/ts/render_path.ts | 508 + armorcore/sources/ts/scene.ts | 800 + armorcore/sources/ts/shader_data.ts | 436 + armorcore/sources/ts/speaker_object.ts | 124 + armorcore/sources/ts/sys.ts | 437 + armorcore/sources/ts/tilesheet.ts | 118 + armorcore/sources/ts/time.ts | 24 + armorcore/sources/ts/transform.ts | 227 + armorcore/sources/ts/tween.ts | 220 + armorcore/sources/ts/ui.ts | 523 + armorcore/sources/ts/uniforms.ts | 658 + armorcore/sources/ts/world_data.ts | 79 + armorcore/tools/amake/aimage.c | 169 + armorcore/tools/amake/alang.js | 1681 + armorcore/tools/amake/alang.md | 43 + armorcore/tools/amake/alang.ts | 1743 + armorcore/tools/amake/ashader.c | 1078 + armorcore/tools/amake/iron.h | 72 + armorcore/tools/amake/main.c | 156 + armorcore/tools/amake/project.js | 61 + armorcore/tools/amake/stb_image_resize.h | 2634 + armorcore/tools/bin/linux_arm64/keepme | 0 armorcore/tools/bin/linux_x64/amake | Bin 0 -> 4336832 bytes armorcore/tools/bin/macos/amake | Bin 0 -> 1072944 bytes armorcore/tools/bin/windows_x64/amake.exe | Bin 0 -> 1425920 bytes armorcore/tools/icon.png | Bin 0 -> 11663 bytes armorcore/tools/io_export_arm.py | 807 + armorcore/tools/make.js | 3354 + armorcore/tools/platform.sh | 10 + armorcore/tools/tcc/COPYING | 504 + armorcore/tools/tcc/VERSION | 1 + armorcore/tools/tcc/include/float.h | 57 + armorcore/tools/tcc/include/stdarg.h | 79 + armorcore/tools/tcc/include/stdbool.h | 11 + armorcore/tools/tcc/include/stddef.h | 54 + armorcore/tools/tcc/include/varargs.h | 12 + armorcore/tools/tcc/libtcc1.a | Bin 0 -> 239412 bytes armorcore/tools/tcc/main.c | 6 + armorcore/tools/tcc/make.sh | 1 + armorcore/tools/tcc/tcc | Bin 0 -> 256888 bytes armorcore/tools/tcc/tcclib.h | 80 + armorcore/tools/tests/cube/assets/cube.arm | Bin 0 -> 1405 bytes armorcore/tools/tests/cube/assets/texture.png | Bin 0 -> 11210 bytes armorcore/tools/tests/cube/project.js | 12 + .../tools/tests/cube/shaders/mesh.frag.glsl | 11 + .../tools/tests/cube/shaders/mesh.vert.glsl | 13 + armorcore/tools/tests/cube/sources/main.ts | 133 + armorcore/tools/tests/cube/tsconfig.json | 3 + armorcore/tools/tests/triangle/main.ts | 128 + armorcore/tools/tests/triangle/project.js | 8 + armorcore/tools/to_spirv/glslang/LICENSE.txt | 108 + .../glslang/OGLCompilersDLL/InitializeDll.cpp | 165 + .../glslang/OGLCompilersDLL/InitializeDll.h | 49 + .../to_spirv/glslang/SPIRV/GLSL.ext.AMD.h | 108 + .../to_spirv/glslang/SPIRV/GLSL.ext.EXT.h | 39 + .../to_spirv/glslang/SPIRV/GLSL.ext.KHR.h | 51 + .../to_spirv/glslang/SPIRV/GLSL.ext.NV.h | 81 + .../to_spirv/glslang/SPIRV/GLSL.std.450.h | 131 + .../to_spirv/glslang/SPIRV/GlslangToSpv.cpp | 8727 +++ .../to_spirv/glslang/SPIRV/GlslangToSpv.h | 61 + .../glslang/SPIRV/InReadableOrder.cpp | 131 + .../tools/to_spirv/glslang/SPIRV/Logger.cpp | 72 + .../tools/to_spirv/glslang/SPIRV/Logger.h | 83 + .../glslang/SPIRV/NonSemanticDebugPrintf.h | 50 + .../to_spirv/glslang/SPIRV/SPVRemapper.cpp | 1487 + .../to_spirv/glslang/SPIRV/SPVRemapper.h | 304 + .../to_spirv/glslang/SPIRV/SpvBuilder.cpp | 3130 + .../tools/to_spirv/glslang/SPIRV/SpvBuilder.h | 838 + .../to_spirv/glslang/SPIRV/SpvPostProcess.cpp | 450 + .../tools/to_spirv/glslang/SPIRV/SpvTools.cpp | 217 + .../tools/to_spirv/glslang/SPIRV/SpvTools.h | 82 + .../tools/to_spirv/glslang/SPIRV/bitutils.h | 81 + .../to_spirv/glslang/SPIRV/disassemble.cpp | 743 + .../to_spirv/glslang/SPIRV/disassemble.h | 53 + .../tools/to_spirv/glslang/SPIRV/doc.cpp | 2888 + armorcore/tools/to_spirv/glslang/SPIRV/doc.h | 258 + .../tools/to_spirv/glslang/SPIRV/hex_float.h | 1078 + .../tools/to_spirv/glslang/SPIRV/spirv.hpp | 2114 + .../tools/to_spirv/glslang/SPIRV/spvIR.h | 485 + .../glslang/StandAlone/ResourceLimits.cpp | 496 + .../glslang/StandAlone/ResourceLimits.h | 57 + .../to_spirv/glslang/StandAlone/Worklist.h | 95 + .../glslang/GenericCodeGen/CodeGen.cpp | 76 + .../glslang/glslang/GenericCodeGen/Link.cpp | 91 + .../glslang/glslang/Include/BaseTypes.h | 571 + .../to_spirv/glslang/glslang/Include/Common.h | 292 + .../glslang/glslang/Include/ConstantUnion.h | 974 + .../glslang/glslang/Include/InfoSink.h | 144 + .../glslang/Include/InitializeGlobals.h | 44 + .../glslang/glslang/Include/PoolAlloc.h | 316 + .../glslang/glslang/Include/ResourceLimits.h | 150 + .../glslang/glslang/Include/ShHandle.h | 176 + .../to_spirv/glslang/glslang/Include/Types.h | 2492 + .../to_spirv/glslang/glslang/Include/arrays.h | 341 + .../glslang/Include/glslang_c_shader_types.h | 182 + .../glslang/glslang/Include/intermediate.h | 1805 + .../glslang/glslang/Include/revision.h | 3 + .../glslang/MachineIndependent/Constant.cpp | 1428 + .../glslang/MachineIndependent/InfoSink.cpp | 113 + .../glslang/MachineIndependent/Initialize.cpp | 9198 +++ .../glslang/MachineIndependent/Initialize.h | 112 + .../MachineIndependent/IntermTraverse.cpp | 302 + .../MachineIndependent/Intermediate.cpp | 3992 ++ .../MachineIndependent/LiveTraverser.h | 138 + .../MachineIndependent/ParseContextBase.cpp | 641 + .../MachineIndependent/ParseHelper.cpp | 8442 +++ .../glslang/MachineIndependent/ParseHelper.h | 526 + .../glslang/MachineIndependent/PoolAlloc.cpp | 315 + .../glslang/MachineIndependent/RemoveTree.cpp | 118 + .../glslang/MachineIndependent/RemoveTree.h | 41 + .../glslang/MachineIndependent/Scan.cpp | 1833 + .../glslang/glslang/MachineIndependent/Scan.h | 276 + .../glslang/MachineIndependent/ScanContext.h | 93 + .../glslang/MachineIndependent/ShaderLang.cpp | 2118 + .../MachineIndependent/SymbolTable.cpp | 448 + .../glslang/MachineIndependent/SymbolTable.h | 885 + .../glslang/MachineIndependent/Versions.cpp | 1205 + .../glslang/MachineIndependent/Versions.h | 331 + .../glslang/MachineIndependent/attribute.cpp | 346 + .../glslang/MachineIndependent/attribute.h | 149 + .../glslang/MachineIndependent/gl_types.h | 210 + .../glslang/MachineIndependent/glslang.y | 3894 ++ .../MachineIndependent/glslang_tab.cpp | 10716 ++++ .../MachineIndependent/glslang_tab.cpp.h | 525 + .../glslang/MachineIndependent/intermOut.cpp | 1565 + .../glslang/MachineIndependent/iomapper.cpp | 1267 + .../glslang/MachineIndependent/iomapper.h | 301 + .../glslang/MachineIndependent/limits.cpp | 200 + .../MachineIndependent/linkValidate.cpp | 1779 + .../MachineIndependent/localintermediate.h | 1026 + .../glslang/MachineIndependent/parseConst.cpp | 214 + .../MachineIndependent/parseVersions.h | 236 + .../MachineIndependent/preprocessor/Pp.cpp | 1338 + .../preprocessor/PpAtom.cpp | 181 + .../preprocessor/PpContext.cpp | 120 + .../preprocessor/PpContext.h | 703 + .../preprocessor/PpScanner.cpp | 1315 + .../preprocessor/PpTokens.cpp | 221 + .../preprocessor/PpTokens.h | 179 + .../propagateNoContraction.cpp | 870 + .../propagateNoContraction.h | 55 + .../glslang/MachineIndependent/reflection.cpp | 1204 + .../glslang/MachineIndependent/reflection.h | 223 + .../glslang/OSDependent/Unix/ossource.cpp | 207 + .../glslang/glslang/OSDependent/osinclude.h | 63 + .../glslang/glslang/Public/ShaderLang.h | 927 + armorcore/tools/to_spirv/license.txt | 19 + armorcore/tools/to_spirv/project.js | 9 + armorcore/tools/to_spirv/to_spirv.cpp | 1891 + armorcore/tools/wasm/README.md | 11 + armorcore/tools/wasm/index.html | 11 + armorcore/tools/wasm/project.js | 7 + 738 files changed, 305397 insertions(+) create mode 100755 armorcore/make create mode 100644 armorcore/make.bat create mode 100644 armorcore/project.js create mode 100644 armorcore/readme.md create mode 100644 armorcore/shaders/g2_colored.frag.glsl create mode 100644 armorcore/shaders/g2_colored.vert.glsl create mode 100644 armorcore/shaders/g2_image.frag.glsl create mode 100644 armorcore/shaders/g2_image.vert.glsl create mode 100644 armorcore/shaders/g2_text.frag.glsl create mode 100644 armorcore/shaders/g2_text.vert.glsl create mode 100644 armorcore/sources/.clang-format create mode 100644 armorcore/sources/backends/android/android_native_app_glue.c create mode 100644 armorcore/sources/backends/android/android_native_app_glue.h create mode 100644 armorcore/sources/backends/android/java/arm/AndroidHttpRequest.java create mode 100644 armorcore/sources/backends/android/java/tech/kinc/KincActivity.kt create mode 100644 armorcore/sources/backends/android/java/tech/kinc/KincMoviePlayer.kt create mode 100644 armorcore/sources/backends/android/java/tech/kinc/KincMovieTexture.kt create mode 100644 armorcore/sources/backends/android/kinc/backend/Android.h create mode 100644 armorcore/sources/backends/android/kinc/backend/android_file_dialog.c create mode 100644 armorcore/sources/backends/android/kinc/backend/android_file_dialog.h create mode 100644 armorcore/sources/backends/android/kinc/backend/android_http_request.c create mode 100644 armorcore/sources/backends/android/kinc/backend/android_http_request.h create mode 100644 armorcore/sources/backends/android/kinc/backend/androidunit.c create mode 100644 armorcore/sources/backends/android/kinc/backend/audio.c.h create mode 100644 armorcore/sources/backends/android/kinc/backend/display.c.h create mode 100644 armorcore/sources/backends/android/kinc/backend/system.c.h create mode 100644 armorcore/sources/backends/android/kinc/backend/video.c.h create mode 100644 armorcore/sources/backends/android/kinc/backend/video.h create mode 100644 armorcore/sources/backends/android/kinc/backend/window.c.h create mode 100644 armorcore/sources/backends/android/kinc/backend/windowdata.h create mode 100644 armorcore/sources/backends/apple/kinc/backend/appleunit.m create mode 100644 armorcore/sources/backends/apple/kinc/backend/http.m.h create mode 100644 armorcore/sources/backends/apple/kinc/backend/system.m.h create mode 100644 armorcore/sources/backends/apple/kinc/backend/thread.m.h create mode 100644 armorcore/sources/backends/apple/kinc/backend/video.h create mode 100644 armorcore/sources/backends/apple/kinc/backend/video.m.h create mode 100644 armorcore/sources/backends/data/android/app/CMakeLists.txt create mode 100644 armorcore/sources/backends/data/android/app/build.gradle.kts create mode 100644 armorcore/sources/backends/data/android/app/proguard-rules.pro create mode 100644 armorcore/sources/backends/data/android/build.gradle.kts create mode 100644 armorcore/sources/backends/data/android/gradle.properties create mode 100644 armorcore/sources/backends/data/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 armorcore/sources/backends/data/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 armorcore/sources/backends/data/android/gradlew create mode 100644 armorcore/sources/backends/data/android/gradlew.bat create mode 100644 armorcore/sources/backends/data/android/main/AndroidManifest.xml create mode 100644 armorcore/sources/backends/data/android/main/res/values/strings.xml create mode 100644 armorcore/sources/backends/data/android/settings.gradle.kts create mode 100644 armorcore/sources/backends/data/wasm/JS-Sources/audio-thread.js create mode 100644 armorcore/sources/backends/data/wasm/JS-Sources/index.html create mode 100644 armorcore/sources/backends/data/wasm/JS-Sources/start.js create mode 100644 armorcore/sources/backends/data/wasm/JS-Sources/thread_starter.js create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/Direct3D11.c.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/Direct3D11.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/ShaderHash.c.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/ShaderHash.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/compute.c.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/compute.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/d3d11unit.c create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/indexbuffer.c.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/indexbuffer.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/pipeline.c.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/pipeline.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/rendertarget.c.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/rendertarget.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/shader.c.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/shader.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/texture.c.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/texture.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/vertexbuffer.c.h create mode 100644 armorcore/sources/backends/direct3d11/kinc/backend/graphics4/vertexbuffer.h create mode 100644 armorcore/sources/backends/direct3d12/d3dx12.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/Direct3D12.c.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/ShaderHash.c.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/ShaderHash.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/commandlist.c.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/commandlist.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/compute.c.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/compute.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/constantbuffer.c.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/constantbuffer.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/d3d12mini.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/d3d12unit.cpp create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/graphics.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/indexbuffer.c.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/indexbuffer.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/pipeline.c.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/pipeline.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/raytrace.c.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/raytrace.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/rendertarget.c.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/rendertarget.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/sampler.c.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/sampler.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/shader.c.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/shader.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/texture.c.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/texture.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/vertexbuffer.c.h create mode 100644 armorcore/sources/backends/direct3d12/kinc/backend/graphics5/vertexbuffer.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/G4.c.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/G4.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/compute.c.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/compute.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/g4ong5unit.c create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/graphics.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/indexbuffer.c.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/indexbuffer.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/pipeline.c.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/pipeline.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/rendertarget.c.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/rendertarget.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/samplers.c.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/shader.c.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/shader.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/texture.c.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/texture.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/vertexbuffer.c.h create mode 100644 armorcore/sources/backends/g4ong5/kinc/backend/graphics4/vertexbuffer.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/G5onG4.c.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/G5onG4.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/commandlist.c.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/commandlist.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/compute.c create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/compute.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/constantbuffer.c.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/constantbuffer.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/g5ong4unit.c create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/graphics.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/indexbuffer.c.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/indexbuffer.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/pipeline.c.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/pipeline.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/rendertarget.c.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/rendertarget.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/sampler.c.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/sampler.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/shader.c.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/shader.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/texture.c.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/texture.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/vertexbuffer.c.h create mode 100644 armorcore/sources/backends/g5ong4/kinc/backend/graphics5/vertexbuffer.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/GLView.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/GLView.m.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/GLViewController.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/GLViewController.m.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/KoreAppDelegate.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/KoreAppDelegate.m.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/LaunchScreen.storyboard create mode 100644 armorcore/sources/backends/ios/kinc/backend/audio.m.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/display.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/display.m.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/displaydata.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/ios.plist create mode 100644 armorcore/sources/backends/ios/kinc/backend/ios_file_dialog.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/ios_file_dialog.m create mode 100644 armorcore/sources/backends/ios/kinc/backend/iosunit.m create mode 100644 armorcore/sources/backends/ios/kinc/backend/motion.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/motion.m.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/mouse.c.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/system.m.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/window.c.h create mode 100644 armorcore/sources/backends/ios/kinc/backend/windowdata.h create mode 100644 armorcore/sources/backends/license.txt create mode 100644 armorcore/sources/backends/linux/kinc/backend/display.c.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/funcs.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/gamepad.c.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/gamepad.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/linuxunit.c create mode 100644 armorcore/sources/backends/linux/kinc/backend/mouse.c.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/sound.c.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/system.c.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/video.c.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/video.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/wayland/display.c.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/wayland/system.c.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/wayland/wayland-funs.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/wayland/wayland.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/wayland/window.c.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/window.c.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/x11/display.c.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/x11/system.c.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/x11/window.c.h create mode 100644 armorcore/sources/backends/linux/kinc/backend/x11/x11.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/BasicOpenGLView.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/BasicOpenGLView.m.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/HIDGamepad.c.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/HIDGamepad.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/HIDManager.c.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/HIDManager.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/audio.c.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/display.m.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/displaydata.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/mac.plist create mode 100644 armorcore/sources/backends/macos/kinc/backend/macosunit.m create mode 100644 armorcore/sources/backends/macos/kinc/backend/mouse.m.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/system.c.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/system.m.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/window.c.h create mode 100644 armorcore/sources/backends/macos/kinc/backend/windowdata.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/Metal.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/Metal.m.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/commandlist.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/commandlist.m.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/compute.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/compute.m.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/constantbuffer.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/constantbuffer.m.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/graphics.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/indexbuffer.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/indexbuffer.m.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/metalunit.m create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/pipeline.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/pipeline.m.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/raytrace.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/raytrace.m.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/rendertarget.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/rendertarget.m.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/sampler.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/sampler.m.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/shader.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/shader.m.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/texture.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/texture.m.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/vertexbuffer.h create mode 100644 armorcore/sources/backends/metal/kinc/backend/graphics5/vertexbuffer.m.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/MiniWindows.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/SystemMicrosoft.c.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/SystemMicrosoft.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/atomic.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/event.c.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/event.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/fiber.c.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/fiber.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/microsoftunit.c create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/mutex.c.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/mutex.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/semaphore.c.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/semaphore.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/thread.c.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/thread.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/threadlocal.c.h create mode 100644 armorcore/sources/backends/microsoft/kinc/backend/threadlocal.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGL.c.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGL.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGLWindow.c.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGLWindow.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/compute.c.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/compute.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/graphics.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/indexbuffer.c.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/indexbuffer.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/ogl.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/openglunit.c create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/pipeline.c.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/pipeline.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/rendertarget.c.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/rendertarget.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/shader.c.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/shader.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/texture.c.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/texture.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/vertexbuffer.c.h create mode 100644 armorcore/sources/backends/opengl/kinc/backend/graphics4/vertexbuffer.h create mode 100644 armorcore/sources/backends/posix/kinc/backend/atomic.h create mode 100644 armorcore/sources/backends/posix/kinc/backend/event.c.h create mode 100644 armorcore/sources/backends/posix/kinc/backend/event.h create mode 100644 armorcore/sources/backends/posix/kinc/backend/mutex.c.h create mode 100644 armorcore/sources/backends/posix/kinc/backend/mutex.h create mode 100644 armorcore/sources/backends/posix/kinc/backend/posixunit.c create mode 100644 armorcore/sources/backends/posix/kinc/backend/semaphore.c.h create mode 100644 armorcore/sources/backends/posix/kinc/backend/semaphore.h create mode 100644 armorcore/sources/backends/posix/kinc/backend/thread.c.h create mode 100644 armorcore/sources/backends/posix/kinc/backend/thread.h create mode 100644 armorcore/sources/backends/posix/kinc/backend/threadlocal.c.h create mode 100644 armorcore/sources/backends/posix/kinc/backend/threadlocal.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/MiniVulkan.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/ShaderHash.c.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/ShaderHash.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/Vulkan.c.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/commandlist.c.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/commandlist.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/compute.c.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/compute.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/constantbuffer.c.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/constantbuffer.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/graphics.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/indexbuffer.c.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/indexbuffer.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/named_number.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/pipeline.c.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/pipeline.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/raytrace.c.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/raytrace.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/rendertarget.c.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/rendertarget.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/sampler.c.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/sampler.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/shader.c.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/shader.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/texture.c.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/texture.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/vertexbuffer.c.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/vertexbuffer.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/vulkan.h create mode 100644 armorcore/sources/backends/vulkan/kinc/backend/graphics5/vulkanunit.c create mode 100644 armorcore/sources/backends/wasapi/kinc/backend/wasapi.c create mode 100644 armorcore/sources/backends/wasm/GL/gl.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/atomic.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/audio.c.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/display.c.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/event.c.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/event.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/html5unit.c create mode 100644 armorcore/sources/backends/wasm/kinc/backend/mouse.c.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/mutex.c.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/mutex.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/semaphore.c.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/semaphore.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/system.c.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/thread.c.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/thread.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/threadlocal.c.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/threadlocal.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/video.c.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/video.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/window.c.h create mode 100644 armorcore/sources/backends/wasm/kinc/backend/windowdata.h create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/WebGPU.c create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/WebGPU.h create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/commandlist.c create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/commandlist.h create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/compute.c create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/compute.h create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/constantbuffer.c create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/constantbuffer.h create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/graphics.h create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/indexbuffer.c create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/indexbuffer.h create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/pipeline.c create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/pipeline.h create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/rendertarget.c create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/rendertarget.h create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/sampler.c create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/sampler.h create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/shader.c create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/shader.h create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/texture.c create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/texture.h create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/vertexbuffer.c create mode 100644 armorcore/sources/backends/webgpu/kinc/backend/graphics5/vertexbuffer.h create mode 100644 armorcore/sources/backends/windows/kinc/backend/Windows.c.h create mode 100644 armorcore/sources/backends/windows/kinc/backend/Windows.h create mode 100644 armorcore/sources/backends/windows/kinc/backend/base.c.h create mode 100644 armorcore/sources/backends/windows/kinc/backend/display.c.h create mode 100644 armorcore/sources/backends/windows/kinc/backend/http.c.h create mode 100644 armorcore/sources/backends/windows/kinc/backend/mouse.c.h create mode 100644 armorcore/sources/backends/windows/kinc/backend/system.c.h create mode 100644 armorcore/sources/backends/windows/kinc/backend/video.c.h create mode 100644 armorcore/sources/backends/windows/kinc/backend/video.h create mode 100644 armorcore/sources/backends/windows/kinc/backend/window.c.h create mode 100644 armorcore/sources/backends/windows/kinc/backend/windowsunit.c create mode 100644 armorcore/sources/const_data.c create mode 100644 armorcore/sources/const_data.h create mode 100644 armorcore/sources/iron.c create mode 100644 armorcore/sources/iron.h create mode 100644 armorcore/sources/iron_armpack.c create mode 100644 armorcore/sources/iron_armpack.h create mode 100644 armorcore/sources/iron_array.c create mode 100644 armorcore/sources/iron_array.h create mode 100644 armorcore/sources/iron_gc.c create mode 100644 armorcore/sources/iron_gc.h create mode 100644 armorcore/sources/iron_json.c create mode 100644 armorcore/sources/iron_json.h create mode 100644 armorcore/sources/iron_map.c create mode 100644 armorcore/sources/iron_map.h create mode 100644 armorcore/sources/iron_mat3.c create mode 100644 armorcore/sources/iron_mat3.h create mode 100644 armorcore/sources/iron_mat4.c create mode 100644 armorcore/sources/iron_mat4.h create mode 100644 armorcore/sources/iron_obj.c create mode 100644 armorcore/sources/iron_obj.h create mode 100644 armorcore/sources/iron_quat.c create mode 100644 armorcore/sources/iron_quat.h create mode 100644 armorcore/sources/iron_string.c create mode 100644 armorcore/sources/iron_string.h create mode 100644 armorcore/sources/iron_ui.c create mode 100644 armorcore/sources/iron_ui.h create mode 100644 armorcore/sources/iron_ui_ext.c create mode 100644 armorcore/sources/iron_ui_ext.h create mode 100644 armorcore/sources/iron_ui_nodes.c create mode 100644 armorcore/sources/iron_ui_nodes.h create mode 100644 armorcore/sources/iron_vec2.c create mode 100644 armorcore/sources/iron_vec2.h create mode 100644 armorcore/sources/iron_vec3.c create mode 100644 armorcore/sources/iron_vec3.h create mode 100644 armorcore/sources/iron_vec4.c create mode 100644 armorcore/sources/iron_vec4.h create mode 100644 armorcore/sources/kinc/audio1/a1unit.c create mode 100644 armorcore/sources/kinc/audio1/audio.c.h create mode 100644 armorcore/sources/kinc/audio1/audio.h create mode 100644 armorcore/sources/kinc/audio1/sound.c.h create mode 100644 armorcore/sources/kinc/audio1/sound.h create mode 100644 armorcore/sources/kinc/audio1/soundstream.c.h create mode 100644 armorcore/sources/kinc/audio1/soundstream.h create mode 100644 armorcore/sources/kinc/audio2/audio.c create mode 100644 armorcore/sources/kinc/audio2/audio.h create mode 100644 armorcore/sources/kinc/color.h create mode 100644 armorcore/sources/kinc/display.h create mode 100644 armorcore/sources/kinc/error.h create mode 100644 armorcore/sources/kinc/global.h create mode 100644 armorcore/sources/kinc/graphics2/g2.c create mode 100644 armorcore/sources/kinc/graphics2/g2.h create mode 100644 armorcore/sources/kinc/graphics2/g2_ext.c create mode 100644 armorcore/sources/kinc/graphics2/g2_ext.h create mode 100644 armorcore/sources/kinc/graphics2/g2_font.h create mode 100644 armorcore/sources/kinc/graphics2/stb_truetype.h create mode 100644 armorcore/sources/kinc/graphics4/compute.h create mode 100644 armorcore/sources/kinc/graphics4/constantlocation.h create mode 100644 armorcore/sources/kinc/graphics4/g4unit.c create mode 100644 armorcore/sources/kinc/graphics4/graphics.c.h create mode 100644 armorcore/sources/kinc/graphics4/graphics.h create mode 100644 armorcore/sources/kinc/graphics4/indexbuffer.h create mode 100644 armorcore/sources/kinc/graphics4/pipeline.c.h create mode 100644 armorcore/sources/kinc/graphics4/pipeline.h create mode 100644 armorcore/sources/kinc/graphics4/rendertarget.c.h create mode 100644 armorcore/sources/kinc/graphics4/rendertarget.h create mode 100644 armorcore/sources/kinc/graphics4/shader.h create mode 100644 armorcore/sources/kinc/graphics4/texture.h create mode 100644 armorcore/sources/kinc/graphics4/textureunit.h create mode 100644 armorcore/sources/kinc/graphics4/usage.h create mode 100644 armorcore/sources/kinc/graphics4/vertexbuffer.c.h create mode 100644 armorcore/sources/kinc/graphics4/vertexbuffer.h create mode 100644 armorcore/sources/kinc/graphics4/vertexstructure.h create mode 100644 armorcore/sources/kinc/graphics5/commandlist.c.h create mode 100644 armorcore/sources/kinc/graphics5/commandlist.h create mode 100644 armorcore/sources/kinc/graphics5/compute.h create mode 100644 armorcore/sources/kinc/graphics5/constantbuffer.c.h create mode 100644 armorcore/sources/kinc/graphics5/constantbuffer.h create mode 100644 armorcore/sources/kinc/graphics5/constantlocation.h create mode 100644 armorcore/sources/kinc/graphics5/g5unit.c create mode 100644 armorcore/sources/kinc/graphics5/graphics.c.h create mode 100644 armorcore/sources/kinc/graphics5/graphics.h create mode 100644 armorcore/sources/kinc/graphics5/indexbuffer.h create mode 100644 armorcore/sources/kinc/graphics5/pipeline.c.h create mode 100644 armorcore/sources/kinc/graphics5/pipeline.h create mode 100644 armorcore/sources/kinc/graphics5/raytrace.h create mode 100644 armorcore/sources/kinc/graphics5/rendertarget.c.h create mode 100644 armorcore/sources/kinc/graphics5/rendertarget.h create mode 100644 armorcore/sources/kinc/graphics5/sampler.c.h create mode 100644 armorcore/sources/kinc/graphics5/sampler.h create mode 100644 armorcore/sources/kinc/graphics5/shader.h create mode 100644 armorcore/sources/kinc/graphics5/texture.c.h create mode 100644 armorcore/sources/kinc/graphics5/texture.h create mode 100644 armorcore/sources/kinc/graphics5/textureunit.h create mode 100644 armorcore/sources/kinc/graphics5/vertexbuffer.h create mode 100644 armorcore/sources/kinc/graphics5/vertexstructure.h create mode 100644 armorcore/sources/kinc/image.h create mode 100644 armorcore/sources/kinc/input/acceleration.h create mode 100644 armorcore/sources/kinc/input/gamepad.h create mode 100644 armorcore/sources/kinc/input/inputunit.c create mode 100644 armorcore/sources/kinc/input/keyboard.h create mode 100644 armorcore/sources/kinc/input/mouse.h create mode 100644 armorcore/sources/kinc/input/pen.h create mode 100644 armorcore/sources/kinc/input/rotation.h create mode 100644 armorcore/sources/kinc/input/surface.h create mode 100644 armorcore/sources/kinc/io/filereader.h create mode 100644 armorcore/sources/kinc/io/filewriter.h create mode 100644 armorcore/sources/kinc/io/iounit.c create mode 100644 armorcore/sources/kinc/libs/lz4x.h create mode 100644 armorcore/sources/kinc/libs/neon_mathfun.h create mode 100644 armorcore/sources/kinc/libs/sse_mathfun.h create mode 100644 armorcore/sources/kinc/libs/stb_image.h create mode 100644 armorcore/sources/kinc/libs/stb_sprintf.h create mode 100644 armorcore/sources/kinc/libs/stb_vorbis.c create mode 100644 armorcore/sources/kinc/license.txt create mode 100644 armorcore/sources/kinc/log.h create mode 100644 armorcore/sources/kinc/math/core.h create mode 100644 armorcore/sources/kinc/math/mathunit.c create mode 100644 armorcore/sources/kinc/math/matrix.h create mode 100644 armorcore/sources/kinc/math/quaternion.h create mode 100644 armorcore/sources/kinc/math/random.h create mode 100644 armorcore/sources/kinc/math/vector.h create mode 100644 armorcore/sources/kinc/network/http.h create mode 100644 armorcore/sources/kinc/network/networkunit.c create mode 100644 armorcore/sources/kinc/network/socket.h create mode 100644 armorcore/sources/kinc/readme.md create mode 100644 armorcore/sources/kinc/rootunit.c create mode 100644 armorcore/sources/kinc/simd/float32x4.h create mode 100644 armorcore/sources/kinc/simd/int16x8.h create mode 100644 armorcore/sources/kinc/simd/int32x4.h create mode 100644 armorcore/sources/kinc/simd/int8x16.h create mode 100644 armorcore/sources/kinc/simd/type_conversions.h create mode 100644 armorcore/sources/kinc/simd/types.h create mode 100644 armorcore/sources/kinc/simd/uint16x8.h create mode 100644 armorcore/sources/kinc/simd/uint32x4.h create mode 100644 armorcore/sources/kinc/simd/uint8x16.h create mode 100644 armorcore/sources/kinc/system.h create mode 100644 armorcore/sources/kinc/threads/atomic.h create mode 100644 armorcore/sources/kinc/threads/event.h create mode 100644 armorcore/sources/kinc/threads/fiber.h create mode 100644 armorcore/sources/kinc/threads/mutex.h create mode 100644 armorcore/sources/kinc/threads/semaphore.h create mode 100644 armorcore/sources/kinc/threads/thread.h create mode 100644 armorcore/sources/kinc/threads/threadlocal.h create mode 100644 armorcore/sources/kinc/video.h create mode 100644 armorcore/sources/kinc/window.h create mode 100644 armorcore/sources/libs/dir.c create mode 100644 armorcore/sources/libs/dir.h create mode 100644 armorcore/sources/libs/gc.c create mode 100644 armorcore/sources/libs/gc.h create mode 100644 armorcore/sources/libs/jo_mpeg.h create mode 100644 armorcore/sources/libs/jsmn.h create mode 100644 armorcore/sources/libs/miniClib/assert.h create mode 100644 armorcore/sources/libs/miniClib/errno.h create mode 100644 armorcore/sources/libs/miniClib/license.txt create mode 100644 armorcore/sources/libs/miniClib/math.c create mode 100644 armorcore/sources/libs/miniClib/math.h create mode 100644 armorcore/sources/libs/miniClib/memory.c create mode 100644 armorcore/sources/libs/miniClib/memory.h create mode 100644 armorcore/sources/libs/miniClib/stdbool.h create mode 100644 armorcore/sources/libs/miniClib/stdio.c create mode 100644 armorcore/sources/libs/miniClib/stdio.h create mode 100644 armorcore/sources/libs/miniClib/stdlib.c create mode 100644 armorcore/sources/libs/miniClib/stdlib.h create mode 100644 armorcore/sources/libs/miniClib/string.c create mode 100644 armorcore/sources/libs/miniClib/string.h create mode 100644 armorcore/sources/libs/miniClib/time.h create mode 100644 armorcore/sources/libs/nfd/LICENSE create mode 100644 armorcore/sources/libs/nfd/common.h create mode 100644 armorcore/sources/libs/nfd/nfd.h create mode 100644 armorcore/sources/libs/nfd/nfd_cocoa.m create mode 100644 armorcore/sources/libs/nfd/nfd_common.c create mode 100644 armorcore/sources/libs/nfd/nfd_common.h create mode 100644 armorcore/sources/libs/nfd/nfd_gtk.c create mode 100644 armorcore/sources/libs/nfd/nfd_win.cpp create mode 100644 armorcore/sources/libs/nfd/simple_exec.h create mode 100644 armorcore/sources/libs/quickjs/LICENSE create mode 100644 armorcore/sources/libs/quickjs/VERSION create mode 100644 armorcore/sources/libs/quickjs/cutils.c create mode 100644 armorcore/sources/libs/quickjs/cutils.h create mode 100644 armorcore/sources/libs/quickjs/dirent_compat.h create mode 100644 armorcore/sources/libs/quickjs/libbf.c create mode 100644 armorcore/sources/libs/quickjs/libbf.h create mode 100644 armorcore/sources/libs/quickjs/libregexp-opcode.h create mode 100644 armorcore/sources/libs/quickjs/libregexp.c create mode 100644 armorcore/sources/libs/quickjs/libregexp.h create mode 100644 armorcore/sources/libs/quickjs/libunicode-table.h create mode 100644 armorcore/sources/libs/quickjs/libunicode.c create mode 100644 armorcore/sources/libs/quickjs/libunicode.h create mode 100644 armorcore/sources/libs/quickjs/list.h create mode 100644 armorcore/sources/libs/quickjs/quickjs-atom.h create mode 100644 armorcore/sources/libs/quickjs/quickjs-c-atomics.h create mode 100644 armorcore/sources/libs/quickjs/quickjs-libc.c create mode 100644 armorcore/sources/libs/quickjs/quickjs-libc.h create mode 100644 armorcore/sources/libs/quickjs/quickjs-opcode.h create mode 100644 armorcore/sources/libs/quickjs/quickjs.c create mode 100644 armorcore/sources/libs/quickjs/quickjs.h create mode 100644 armorcore/sources/libs/sdefl.h create mode 100644 armorcore/sources/libs/sinfl.h create mode 100644 armorcore/sources/libs/stb_image_write.h create mode 100644 armorcore/sources/ts/anim.ts create mode 100644 armorcore/sources/ts/anim_bone.ts create mode 100644 armorcore/sources/ts/anim_object.ts create mode 100644 armorcore/sources/ts/app.ts create mode 100644 armorcore/sources/ts/armature.ts create mode 100644 armorcore/sources/ts/audio.ts create mode 100644 armorcore/sources/ts/camera_data.ts create mode 100644 armorcore/sources/ts/camera_object.ts create mode 100644 armorcore/sources/ts/const_data.ts create mode 100644 armorcore/sources/ts/data.ts create mode 100644 armorcore/sources/ts/g2.ts create mode 100644 armorcore/sources/ts/g4.ts create mode 100644 armorcore/sources/ts/input.ts create mode 100644 armorcore/sources/ts/iron.ts create mode 100644 armorcore/sources/ts/light_data.ts create mode 100644 armorcore/sources/ts/light_object.ts create mode 100644 armorcore/sources/ts/lz4.ts create mode 100644 armorcore/sources/ts/material_data.ts create mode 100644 armorcore/sources/ts/mesh_data.ts create mode 100644 armorcore/sources/ts/mesh_object.ts create mode 100644 armorcore/sources/ts/object.ts create mode 100644 armorcore/sources/ts/particle_data.ts create mode 100644 armorcore/sources/ts/particle_sys.ts create mode 100644 armorcore/sources/ts/raycast.ts create mode 100644 armorcore/sources/ts/render_path.ts create mode 100644 armorcore/sources/ts/scene.ts create mode 100644 armorcore/sources/ts/shader_data.ts create mode 100644 armorcore/sources/ts/speaker_object.ts create mode 100644 armorcore/sources/ts/sys.ts create mode 100644 armorcore/sources/ts/tilesheet.ts create mode 100644 armorcore/sources/ts/time.ts create mode 100644 armorcore/sources/ts/transform.ts create mode 100644 armorcore/sources/ts/tween.ts create mode 100644 armorcore/sources/ts/ui.ts create mode 100644 armorcore/sources/ts/uniforms.ts create mode 100644 armorcore/sources/ts/world_data.ts create mode 100644 armorcore/tools/amake/aimage.c create mode 100644 armorcore/tools/amake/alang.js create mode 100644 armorcore/tools/amake/alang.md create mode 100644 armorcore/tools/amake/alang.ts create mode 100644 armorcore/tools/amake/ashader.c create mode 100644 armorcore/tools/amake/iron.h create mode 100644 armorcore/tools/amake/main.c create mode 100644 armorcore/tools/amake/project.js create mode 100644 armorcore/tools/amake/stb_image_resize.h create mode 100644 armorcore/tools/bin/linux_arm64/keepme create mode 100755 armorcore/tools/bin/linux_x64/amake create mode 100755 armorcore/tools/bin/macos/amake create mode 100644 armorcore/tools/bin/windows_x64/amake.exe create mode 100644 armorcore/tools/icon.png create mode 100644 armorcore/tools/io_export_arm.py create mode 100644 armorcore/tools/make.js create mode 100644 armorcore/tools/platform.sh create mode 100644 armorcore/tools/tcc/COPYING create mode 100644 armorcore/tools/tcc/VERSION create mode 100644 armorcore/tools/tcc/include/float.h create mode 100644 armorcore/tools/tcc/include/stdarg.h create mode 100644 armorcore/tools/tcc/include/stdbool.h create mode 100644 armorcore/tools/tcc/include/stddef.h create mode 100644 armorcore/tools/tcc/include/varargs.h create mode 100644 armorcore/tools/tcc/libtcc1.a create mode 100755 armorcore/tools/tcc/main.c create mode 100755 armorcore/tools/tcc/make.sh create mode 100755 armorcore/tools/tcc/tcc create mode 100644 armorcore/tools/tcc/tcclib.h create mode 100644 armorcore/tools/tests/cube/assets/cube.arm create mode 100644 armorcore/tools/tests/cube/assets/texture.png create mode 100644 armorcore/tools/tests/cube/project.js create mode 100644 armorcore/tools/tests/cube/shaders/mesh.frag.glsl create mode 100644 armorcore/tools/tests/cube/shaders/mesh.vert.glsl create mode 100644 armorcore/tools/tests/cube/sources/main.ts create mode 100644 armorcore/tools/tests/cube/tsconfig.json create mode 100644 armorcore/tools/tests/triangle/main.ts create mode 100644 armorcore/tools/tests/triangle/project.js create mode 100644 armorcore/tools/to_spirv/glslang/LICENSE.txt create mode 100644 armorcore/tools/to_spirv/glslang/OGLCompilersDLL/InitializeDll.cpp create mode 100644 armorcore/tools/to_spirv/glslang/OGLCompilersDLL/InitializeDll.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.AMD.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.EXT.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.KHR.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.NV.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/GLSL.std.450.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/GlslangToSpv.cpp create mode 100755 armorcore/tools/to_spirv/glslang/SPIRV/GlslangToSpv.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/InReadableOrder.cpp create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/Logger.cpp create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/Logger.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/NonSemanticDebugPrintf.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/SPVRemapper.cpp create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/SPVRemapper.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/SpvBuilder.cpp create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/SpvBuilder.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/SpvPostProcess.cpp create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/SpvTools.cpp create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/SpvTools.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/bitutils.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/disassemble.cpp create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/disassemble.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/doc.cpp create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/doc.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/hex_float.h create mode 100644 armorcore/tools/to_spirv/glslang/SPIRV/spirv.hpp create mode 100755 armorcore/tools/to_spirv/glslang/SPIRV/spvIR.h create mode 100644 armorcore/tools/to_spirv/glslang/StandAlone/ResourceLimits.cpp create mode 100644 armorcore/tools/to_spirv/glslang/StandAlone/ResourceLimits.h create mode 100644 armorcore/tools/to_spirv/glslang/StandAlone/Worklist.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/GenericCodeGen/CodeGen.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/GenericCodeGen/Link.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/Include/BaseTypes.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/Include/Common.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/Include/ConstantUnion.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/Include/InfoSink.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/Include/InitializeGlobals.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/Include/PoolAlloc.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/Include/ResourceLimits.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/Include/ShHandle.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/Include/Types.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/Include/arrays.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/Include/glslang_c_shader_types.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/Include/intermediate.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/Include/revision.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Constant.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/InfoSink.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Initialize.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Initialize.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/IntermTraverse.cpp create mode 100755 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Intermediate.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/LiveTraverser.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ParseContextBase.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ParseHelper.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ParseHelper.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/PoolAlloc.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/RemoveTree.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/RemoveTree.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Scan.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Scan.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ScanContext.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ShaderLang.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/SymbolTable.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/SymbolTable.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Versions.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Versions.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/attribute.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/attribute.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/gl_types.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/glslang.y create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/glslang_tab.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/glslang_tab.cpp.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/intermOut.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/iomapper.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/iomapper.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/limits.cpp create mode 100755 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/linkValidate.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/localintermediate.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/parseConst.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/parseVersions.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpContext.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp create mode 100755 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/propagateNoContraction.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/propagateNoContraction.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/reflection.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/reflection.h create mode 100644 armorcore/tools/to_spirv/glslang/glslang/OSDependent/Unix/ossource.cpp create mode 100644 armorcore/tools/to_spirv/glslang/glslang/OSDependent/osinclude.h create mode 100755 armorcore/tools/to_spirv/glslang/glslang/Public/ShaderLang.h create mode 100644 armorcore/tools/to_spirv/license.txt create mode 100644 armorcore/tools/to_spirv/project.js create mode 100644 armorcore/tools/to_spirv/to_spirv.cpp create mode 100644 armorcore/tools/wasm/README.md create mode 100644 armorcore/tools/wasm/index.html create mode 100644 armorcore/tools/wasm/project.js diff --git a/armorcore/make b/armorcore/make new file mode 100755 index 000000000..d9e6a4899 --- /dev/null +++ b/armorcore/make @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +. `dirname "$0"`/tools/platform.sh +MAKE="`dirname "$0"`/tools/bin/$KINC_PLATFORM/amake" +exec $MAKE `dirname "$0"`/tools/make.js "$@" diff --git a/armorcore/make.bat b/armorcore/make.bat new file mode 100644 index 000000000..454d887ca --- /dev/null +++ b/armorcore/make.bat @@ -0,0 +1 @@ +@call "%~dp0tools\bin\windows_x64\amake.exe" "%~dp0tools\make.js" %* diff --git a/armorcore/project.js b/armorcore/project.js new file mode 100644 index 000000000..06a1a9fc8 --- /dev/null +++ b/armorcore/project.js @@ -0,0 +1,392 @@ + +let project = new Project(flags.name); + +{ + project.add_define("KINC_A1"); + project.add_define("KINC_A2"); + project.add_define("KINC_G1"); + project.add_define("KINC_G2"); + let g5 = false; + + project.add_cfiles("sources/kinc/**"); + project.add_include_dir("sources"); + + function add_backend(name) { + project.add_cfiles("sources/backends/" + name + "/**"); + project.add_include_dir("sources/backends/" + name); + } + + if (platform === "windows") { + add_backend("windows"); + add_backend("microsoft"); + project.add_lib("dxguid"); + project.add_lib("dsound"); + project.add_lib("dinput8"); + project.add_define("_CRT_SECURE_NO_WARNINGS"); + project.add_define("_WINSOCK_DEPRECATED_NO_WARNINGS"); + project.add_lib("ws2_32"); + project.add_lib("Winhttp"); + project.add_lib("wbemuuid"); + + if (graphics === "direct3d11") { + add_backend("direct3d11"); + project.add_define("KINC_DIRECT3D"); + project.add_define("KINC_DIRECT3D11"); + project.add_lib("d3d11"); + } + else if (graphics === "direct3d12" || graphics === "default") { + g5 = true; + add_backend("direct3d12"); + project.add_define("KINC_DIRECT3D"); + project.add_define("KINC_DIRECT3D12"); + project.add_lib("dxgi"); + project.add_lib("d3d12"); + } + else { + throw new Error("Graphics API " + graphics + " is not available for Windows."); + } + + add_backend("wasapi"); + } + else if (platform === "macos") { + add_backend("apple"); + add_backend("macos"); + add_backend("posix"); + if (graphics === "metal" || graphics === "default") { + g5 = true; + add_backend("metal"); + project.add_define("KINC_METAL"); + project.add_lib("Metal"); + project.add_lib("MetalKit"); + } + else { + throw new Error("Graphics API " + graphics + " is not available for macOS."); + } + project.add_lib("IOKit"); + project.add_lib("Cocoa"); + project.add_lib("AppKit"); + project.add_lib("CoreAudio"); + project.add_lib("CoreData"); + project.add_lib("CoreMedia"); + project.add_lib("CoreVideo"); + project.add_lib("AVFoundation"); + project.add_lib("Foundation"); + } + else if (platform === "ios") { + add_backend("apple"); + add_backend("ios"); + add_backend("posix"); + if (graphics === "metal" || graphics === "default") { + g5 = true; + add_backend("metal"); + project.add_define("KINC_METAL"); + project.add_lib("Metal"); + } + else { + throw new Error("Graphics API " + graphics + " is not available for iOS."); + } + project.add_lib("UIKit"); + project.add_lib("Foundation"); + project.add_lib("CoreGraphics"); + project.add_lib("QuartzCore"); + project.add_lib("CoreAudio"); + project.add_lib("AudioToolbox"); + project.add_lib("CoreMotion"); + project.add_lib("AVFoundation"); + project.add_lib("CoreFoundation"); + project.add_lib("CoreVideo"); + project.add_lib("CoreMedia"); + } + else if (platform === "android") { + project.add_define("KINC_ANDROID"); + add_backend("android"); + add_backend("posix"); + if (graphics === "vulkan") { + g5 = true; + add_backend("vulkan"); + project.add_define("KINC_VULKAN"); + project.add_define("VK_USE_PLATFORM_ANDROID_KHR"); + project.add_lib("vulkan"); + project.add_define("KINC_ANDROID_API=24"); + } + else if (graphics === "opengl" || graphics === "default") { + add_backend("opengl"); + project.add_define("KINC_OPENGL"); + project.add_define("KINC_OPENGL_ES"); + project.add_define("KINC_ANDROID_API=19"); + project.add_define("KINC_EGL"); + } + else { + throw new Error("Graphics API " + graphics + " is not available for Android."); + } + project.add_lib("log"); + project.add_lib("android"); + project.add_lib("EGL"); + project.add_lib("GLESv3"); + project.add_lib("OpenSLES"); + project.add_lib("OpenMAXAL"); + } + else if (platform === "wasm") { + project.add_define("KINC_WASM"); + add_backend("wasm"); + project.add_include_dir("miniClib"); + project.add_cfiles("sources/libs/miniClib/**"); + if (graphics === "webgpu") { + g5 = true; + add_backend("webgpu"); + project.add_define("KINC_WEBGPU"); + } + else if (graphics === "opengl" || graphics === "default") { + add_backend("opengl"); + project.add_define("KINC_OPENGL"); + project.add_define("KINC_OPENGL_ES"); + } + else { + throw new Error("Graphics API " + graphics + " is not available for Wasm."); + } + } + else if (platform === "linux") { + add_backend("linux"); + add_backend("posix"); + project.add_lib("asound"); + project.add_lib("dl"); + project.add_lib("udev"); + + // try { + // if (!fs_exists("build")) { + // fs_mkdir("build"); + // } + // if (!fs_exists(path_join("build", "wayland"))) { + // fs_mkdir(path_join("build", "wayland")); + // } + // const waylandDir = path_join("build", "wayland", "wayland-generated"); + // if (!fs_exists(waylandDir)) { + // fs_mkdir(waylandDir); + // } + + // let good_wayland = false; + + // const wayland_call = os_exec("wayland-scanner", ["--version"]); + // if (wayland_call.status !== 0) { + // throw "Could not run wayland-scanner to ask for its version"; + // } + // const wayland_version = wayland_call.stderr; + + // try { + // const scanner_versions = wayland_version.split(" ")[1].split("."); + // const w_x = parseInt(scanner_versions[0]); + // const w_y = parseInt(scanner_versions[1]); + // const w_z = parseInt(scanner_versions[2]); + + // if (w_x > 1) { + // good_wayland = true; + // } + // else if (w_x === 1) { + // if (w_y > 17) { + // good_wayland = true; + // } + // else if (w_y === 17) { + // if (w_z >= 91) { + // good_wayland = true; + // } + // } + // } + // } + // catch (err) { + // console.log("Could not parse wayland-version " + wayland_version); + // } + + // let c_ending = ".c"; + // if (good_wayland) { + // c_ending = ".c.h"; + // } + + // let chfiles = []; + + // function wl_protocol(protocol, file) { + // chfiles.push(file); + // const backend_path = path_resolve(waylandDir); + // const protocol_path = path_resolve("/usr/share/wayland-protocols", protocol); + // if (os_exec("wayland-scanner", ["private-code", protocol_path, path_resolve(backend_path, file + c_ending)]).status !== 0) { + // throw "Failed to generate wayland protocol files for" + protocol; + // } + // if (os_exec("wayland-scanner", ["client-header", protocol_path, path_resolve(backend_path, file + ".h")]).status !== 0) { + // throw "Failed to generate wayland protocol header for" + protocol; + // } + // } + + // if (os_exec("wayland-scanner", ["private-code", "/usr/share/wayland/wayland.xml", path_resolve(waylandDir, "wayland-protocol" + c_ending)]).status !== 0) { + // throw "Failed to generate wayland protocol files for /usr/share/wayland/wayland.xml"; + // } + // if (os_exec("wayland-scanner", ["client-header", "/usr/share/wayland/wayland.xml", path_resolve(waylandDir, "wayland-protocol.h")]).status !== 0) { + // throw "Failed to generate wayland protocol header for /usr/share/wayland/wayland.xml"; + // } + // wl_protocol("stable/viewporter/viewporter.xml", "wayland-viewporter"); + // wl_protocol("stable/xdg-shell/xdg-shell.xml", "xdg-shell"); + // wl_protocol("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml", "xdg-decoration"); + // wl_protocol("unstable/tablet/tablet-unstable-v2.xml", "wayland-tablet"); + // wl_protocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml", "wayland-pointer-constraint"); + // wl_protocol("unstable/relative-pointer/relative-pointer-unstable-v1.xml", "wayland-relative-pointer"); + + // if (good_wayland) { + // let cfile = "#include \"wayland-protocol.c.h\"\n"; + // for (const chfile of chfiles) { + // cfile += "#include \"" + chfile + ".c.h\"\n"; + // } + // fs_writefile(path_resolve(waylandDir, "waylandunit.c"), cfile); + // } + + // project.add_include_dir(path_join("build", "wayland")); + // project.add_cfiles(path_resolve(waylandDir, "**")); + // } + // catch (err) { + // console.log("Failed to include wayland-support, setting KINC_NO_WAYLAND."); + // console.log("Wayland error was: " + err); + // project.add_define("KINC_NO_WAYLAND"); + // } + + if (graphics === "vulkan" || graphics === "default") { + g5 = true; + add_backend("vulkan"); + project.add_lib("vulkan"); + project.add_define("KINC_VULKAN"); + } + else if (graphics === "opengl") { + add_backend("opengl"); + project.add_lib("GL"); + project.add_define("KINC_OPENGL"); + project.add_lib("EGL"); + project.add_define("KINC_EGL"); + } + else { + throw new Error("Graphics API " + graphics + " is not available for Linux."); + } + project.add_define("_POSIX_C_SOURCE=200112L"); + project.add_define("_XOPEN_SOURCE=600"); + } + + project.add_define("KINC_G4"); + + if (g5) { + project.add_define("KINC_G5"); + project.add_define("KINC_G4ONG5"); + add_backend("g4ong5"); + } + else { + project.add_define("KINC_G5"); + project.add_define("KINC_G5ONG4"); + add_backend("g5ong4"); + } +} + +if (fs_exists(os_cwd() + "/icon.png")) { + project.icon = "icon.png"; + if (platform === "macos" && fs_exists(os_cwd() + "/icon_macos.png")) { + project.icon = "icon_macos.png"; + } +} + +project.add_include_dir("sources/libs"); +project.add_cfiles("sources/libs/gc.c"); +project.add_cfiles("sources/libs/dir.c"); +project.add_include_dir("sources"); +project.add_cfiles("sources/iron.c"); +project.add_define("IRON_C_PATH=\"" + os_cwd() + "/build/iron.c" + "\""); +project.add_define("EMBED_H_PATH=\"" + os_cwd() + "/build/embed.h" + "\""); + +if (flags.with_audio) { + project.add_define("WITH_AUDIO"); + project.add_define("arm_audio"); +} + +if (flags.with_eval) { + project.add_define("WITH_EVAL"); + project.add_cfiles("sources/libs/quickjs/*.c"); + if (platform === "linux") { + project.add_lib("m"); + project.add_define("_GNU_SOURCE"); + project.add_define("environ=__environ"); + project.add_define("sighandler_t=__sighandler_t"); + } + else if (platform === "windows") { + project.add_define("WIN32_LEAN_AND_MEAN"); + project.add_define("_WIN32_WINNT=0x0602"); + } +} + +if (flags.with_iron) { + project.add_define("WITH_IRON"); + project.add_cfiles("sources/*.c"); +} + +if (platform === "windows") { + project.add_lib("Dbghelp"); // Stack walk + project.add_lib("Dwmapi"); // DWMWA_USE_IMMERSIVE_DARK_MODE + if (flags.with_d3dcompiler) { + project.add_define("WITH_D3DCOMPILER"); + project.add_lib("d3d11"); + project.add_lib("d3dcompiler"); + } +} +else if (platform === "linux") { + project.add_define("KINC_NO_WAYLAND"); // TODO: kinc_wayland_display_init() not implemented +} +else if (platform === "android") { + // In app/build.gradle: + // android - defaultconfig - ndk.abiFilters "arm64-v8a" + project.add_define("IDLE_SLEEP"); + project.target_options.android.package = flags.package; + project.target_options.android.permissions = ["android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE", "android.permission.INTERNET"]; + project.target_options.android.screenOrientation = ["sensorLandscape"]; + project.target_options.android.minSdkVersion = 30; + project.target_options.android.targetSdkVersion = 33; + project.target_options.android.versionCode = 240000; + project.target_options.android.versionName = "1.0 alpha"; +} +else if (platform === "ios") { + project.add_define("IDLE_SLEEP"); +} + +if (flags.with_nfd && (platform === "windows" || platform === "linux" || platform === "macos")) { + project.add_define("WITH_NFD"); + project.add_include_dir("sources/libs/nfd"); + project.add_cfiles("sources/libs/nfd/nfd_common.c"); + + if (platform === "windows") { + project.add_cfiles("sources/libs/nfd/nfd_win.cpp"); + } + else if (platform === "linux") { + project.add_cfiles("sources/libs/nfd/nfd_gtk.c"); + project.add_include_dir("/usr/include/gtk-3.0"); + project.add_include_dir("/usr/include/glib-2.0"); + project.add_include_dir("/usr/lib/x86_64-linux-gnu/glib-2.0/include"); + project.add_include_dir("/usr/include/pango-1.0"); + project.add_include_dir("/usr/include/cairo"); + project.add_include_dir("/usr/include/gdk-pixbuf-2.0"); + project.add_include_dir("/usr/include/atk-1.0"); + project.add_include_dir("/usr/lib64/glib-2.0/include"); + project.add_include_dir("/usr/lib/glib-2.0/include"); + project.add_include_dir("/usr/include/harfbuzz"); + project.add_lib("gtk-3"); + project.add_lib("gobject-2.0"); + project.add_lib("glib-2.0"); + } + else { + project.add_cfiles("sources/libs/nfd/nfd_cocoa.m"); + } +} + +if (flags.with_compress) { + project.add_define("WITH_COMPRESS"); +} + +if (flags.with_image_write) { + project.add_define("WITH_IMAGE_WRITE"); +} + +if (flags.with_mpeg_write) { + project.add_define("WITH_MPEG_WRITE"); +} + +project.flatten(); +return project; diff --git a/armorcore/readme.md b/armorcore/readme.md new file mode 100644 index 000000000..0d6a0f432 --- /dev/null +++ b/armorcore/readme.md @@ -0,0 +1,5 @@ +# armorcore + +3D engine core for C with JS scripting. ArmorCore targets Direct3D12, Vulkan, Metal and WebGPU. + +Powered by [Kinc](https://github.com/Kode/Kinc) - low-level hardware abstraction library. diff --git a/armorcore/shaders/g2_colored.frag.glsl b/armorcore/shaders/g2_colored.frag.glsl new file mode 100644 index 000000000..6c694a501 --- /dev/null +++ b/armorcore/shaders/g2_colored.frag.glsl @@ -0,0 +1,8 @@ +#version 450 + +in vec4 fragment_color; +out vec4 frag_color; + +void main() { + frag_color = fragment_color; +} diff --git a/armorcore/shaders/g2_colored.vert.glsl b/armorcore/shaders/g2_colored.vert.glsl new file mode 100644 index 000000000..92e440f9b --- /dev/null +++ b/armorcore/shaders/g2_colored.vert.glsl @@ -0,0 +1,12 @@ +#version 450 + +uniform mat4 P; + +in vec3 pos; +in vec4 col; +out vec4 fragment_color; + +void main() { + gl_Position = mul(vec4(pos, 1.0), P); + fragment_color = col; +} diff --git a/armorcore/shaders/g2_image.frag.glsl b/armorcore/shaders/g2_image.frag.glsl new file mode 100644 index 000000000..974d36ca9 --- /dev/null +++ b/armorcore/shaders/g2_image.frag.glsl @@ -0,0 +1,13 @@ +#version 450 + +uniform sampler2D tex; + +in vec2 tex_coord; +in vec4 color; +out vec4 frag_color; + +void main() { + vec4 texcolor = texture(tex, tex_coord) * color; + texcolor.rgb = texcolor.rgb * texcolor.a * color.a; + frag_color = texcolor; +} diff --git a/armorcore/shaders/g2_image.vert.glsl b/armorcore/shaders/g2_image.vert.glsl new file mode 100644 index 000000000..0edddc84e --- /dev/null +++ b/armorcore/shaders/g2_image.vert.glsl @@ -0,0 +1,15 @@ +#version 450 + +uniform mat4 P; + +in vec3 pos; +in vec2 tex; +in vec4 col; +out vec2 tex_coord; +out vec4 color; + +void main() { + gl_Position = mul(vec4(pos, 1.0), P); + tex_coord = tex; + color = col; +} diff --git a/armorcore/shaders/g2_text.frag.glsl b/armorcore/shaders/g2_text.frag.glsl new file mode 100644 index 000000000..050bd6b0e --- /dev/null +++ b/armorcore/shaders/g2_text.frag.glsl @@ -0,0 +1,11 @@ +#version 450 + +uniform sampler2D tex; + +in vec2 tex_coord; +in vec4 fragment_color; +out vec4 frag_color; + +void main() { + frag_color = vec4(fragment_color.rgb, texture(tex, tex_coord).r * fragment_color.a); +} diff --git a/armorcore/shaders/g2_text.vert.glsl b/armorcore/shaders/g2_text.vert.glsl new file mode 100644 index 000000000..e2624c70f --- /dev/null +++ b/armorcore/shaders/g2_text.vert.glsl @@ -0,0 +1,15 @@ +#version 450 + +uniform mat4 P; + +in vec3 pos; +in vec2 tex; +in vec4 col; +out vec2 tex_coord; +out vec4 fragment_color; + +void main() { + gl_Position = mul(vec4(pos, 1.0), P); + tex_coord = tex; + fragment_color = col; +} diff --git a/armorcore/sources/.clang-format b/armorcore/sources/.clang-format new file mode 100644 index 000000000..54a178664 --- /dev/null +++ b/armorcore/sources/.clang-format @@ -0,0 +1,84 @@ +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: true + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +ColumnLimit: 160 +CommentPragmas: '^ IWYU pragma:' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: false +DerivePointerAlignment: false +DisableFormat: false +FixNamespaceComments: false +IndentCaseLabels: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 4 +UseCRLF: false +UseTab: ForIndentation +--- +Language: Cpp +--- +Language: ObjC diff --git a/armorcore/sources/backends/android/android_native_app_glue.c b/armorcore/sources/backends/android/android_native_app_glue.c new file mode 100644 index 000000000..d503d8dad --- /dev/null +++ b/armorcore/sources/backends/android/android_native_app_glue.c @@ -0,0 +1,441 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include + +#include "android_native_app_glue.h" +#include + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) +#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__)) + +/* For debug builds, always enable the debug traces in this library */ +#ifndef NDEBUG +# define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__)) +#else +# define LOGV(...) ((void)0) +#endif + +static void free_saved_state(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->savedState != NULL) { + free(android_app->savedState); + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + pthread_mutex_unlock(&android_app->mutex); +} + +int8_t android_app_read_cmd(struct android_app* android_app) { + int8_t cmd; + if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) { + switch (cmd) { + case APP_CMD_SAVE_STATE: + free_saved_state(android_app); + break; + } + return cmd; + } else { + LOGE("No data on command pipe!"); + } + return -1; +} + +static void print_cur_config(struct android_app* android_app) { + char lang[2], country[2]; + AConfiguration_getLanguage(android_app->config, lang); + AConfiguration_getCountry(android_app->config, country); + + LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " + "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " + "modetype=%d modenight=%d", + AConfiguration_getMcc(android_app->config), + AConfiguration_getMnc(android_app->config), + lang[0], lang[1], country[0], country[1], + AConfiguration_getOrientation(android_app->config), + AConfiguration_getTouchscreen(android_app->config), + AConfiguration_getDensity(android_app->config), + AConfiguration_getKeyboard(android_app->config), + AConfiguration_getNavigation(android_app->config), + AConfiguration_getKeysHidden(android_app->config), + AConfiguration_getNavHidden(android_app->config), + AConfiguration_getSdkVersion(android_app->config), + AConfiguration_getScreenSize(android_app->config), + AConfiguration_getScreenLong(android_app->config), + AConfiguration_getUiModeType(android_app->config), + AConfiguration_getUiModeNight(android_app->config)); +} + +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_INPUT_CHANGED: + LOGV("APP_CMD_INPUT_CHANGED\n"); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->inputQueue = android_app->pendingInputQueue; + if (android_app->inputQueue != NULL) { + LOGV("Attaching input queue to looper"); + AInputQueue_attachLooper(android_app->inputQueue, + android_app->looper, LOOPER_ID_INPUT, NULL, + &android_app->inputPollSource); + } + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_INIT_WINDOW: + LOGV("APP_CMD_INIT_WINDOW\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = android_app->pendingWindow; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_TERM_WINDOW: + LOGV("APP_CMD_TERM_WINDOW\n"); + pthread_cond_broadcast(&android_app->cond); + break; + + case APP_CMD_RESUME: + case APP_CMD_START: + case APP_CMD_PAUSE: + case APP_CMD_STOP: + LOGV("activityState=%d\n", cmd); + pthread_mutex_lock(&android_app->mutex); + android_app->activityState = cmd; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_CONFIG_CHANGED: + LOGV("APP_CMD_CONFIG_CHANGED\n"); + AConfiguration_fromAssetManager(android_app->config, + android_app->activity->assetManager); + print_cur_config(android_app); + break; + + case APP_CMD_DESTROY: + LOGV("APP_CMD_DESTROY\n"); + android_app->destroyRequested = 1; + break; + } +} + +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_TERM_WINDOW: + LOGV("APP_CMD_TERM_WINDOW\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = NULL; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_SAVE_STATE: + LOGV("APP_CMD_SAVE_STATE\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_RESUME: + free_saved_state(android_app); + break; + } +} + +void app_dummy() { + +} + +static void android_app_destroy(struct android_app* android_app) { + LOGV("android_app_destroy!"); + free_saved_state(android_app); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + AConfiguration_delete(android_app->config); + android_app->destroyed = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + // Can't touch android_app object after this. +} + +static void process_input(struct android_app* app, struct android_poll_source* source) { + AInputEvent* event = NULL; + while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { + LOGV("New input event: type=%d\n", AInputEvent_getType(event)); + if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { + continue; + } + int32_t handled = 0; + if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); + AInputQueue_finishEvent(app->inputQueue, event, handled); + } +} + +static void process_cmd(struct android_app* app, struct android_poll_source* source) { + int8_t cmd = android_app_read_cmd(app); + android_app_pre_exec_cmd(app, cmd); + if (app->onAppCmd != NULL) app->onAppCmd(app, cmd); + android_app_post_exec_cmd(app, cmd); +} + +static void* android_app_entry(void* param) { + struct android_app* android_app = (struct android_app*)param; + + android_app->config = AConfiguration_new(); + AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager); + + print_cur_config(android_app); + + android_app->cmdPollSource.id = LOOPER_ID_MAIN; + android_app->cmdPollSource.app = android_app; + android_app->cmdPollSource.process = process_cmd; + android_app->inputPollSource.id = LOOPER_ID_INPUT; + android_app->inputPollSource.app = android_app; + android_app->inputPollSource.process = process_input; + + ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL, + &android_app->cmdPollSource); + android_app->looper = looper; + + pthread_mutex_lock(&android_app->mutex); + android_app->running = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + + android_main(android_app); + + android_app_destroy(android_app); + return NULL; +} + +// -------------------------------------------------------------------- +// Native activity interaction (called from main thread) +// -------------------------------------------------------------------- + +static struct android_app* android_app_create(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app)); + memset(android_app, 0, sizeof(struct android_app)); + android_app->activity = activity; + + pthread_mutex_init(&android_app->mutex, NULL); + pthread_cond_init(&android_app->cond, NULL); + + if (savedState != NULL) { + android_app->savedState = malloc(savedStateSize); + android_app->savedStateSize = savedStateSize; + memcpy(android_app->savedState, savedState, savedStateSize); + } + + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGE("could not create pipe: %s", strerror(errno)); + return NULL; + } + android_app->msgread = msgpipe[0]; + android_app->msgwrite = msgpipe[1]; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&android_app->thread, &attr, android_app_entry, android_app); + + // Wait for thread to start. + pthread_mutex_lock(&android_app->mutex); + while (!android_app->running) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + return android_app; +} + +static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { + if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { + LOGE("Failure writing android_app cmd: %s\n", strerror(errno)); + } +} + +static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingInputQueue = inputQueue; + android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); + while (android_app->inputQueue != android_app->pendingInputQueue) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->pendingWindow != NULL) { + android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); + } + android_app->pendingWindow = window; + if (window != NULL) { + android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); + } + while (android_app->window != android_app->pendingWindow) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, cmd); + while (android_app->activityState != cmd) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_free(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, APP_CMD_DESTROY); + while (!android_app->destroyed) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + close(android_app->msgread); + close(android_app->msgwrite); + pthread_cond_destroy(&android_app->cond); + pthread_mutex_destroy(&android_app->mutex); + free(android_app); +} + +static void onDestroy(ANativeActivity* activity) { + LOGV("Destroy: %p\n", activity); + android_app_free((struct android_app*)activity->instance); +} + +static void onStart(ANativeActivity* activity) { + LOGV("Start: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START); +} + +static void onResume(ANativeActivity* activity) { + LOGV("Resume: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME); +} + +static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { + struct android_app* android_app = (struct android_app*)activity->instance; + void* savedState = NULL; + + LOGV("SaveInstanceState: %p\n", activity); + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 0; + android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); + while (!android_app->stateSaved) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + + if (android_app->savedState != NULL) { + savedState = android_app->savedState; + *outLen = android_app->savedStateSize; + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + + pthread_mutex_unlock(&android_app->mutex); + + return savedState; +} + +static void onPause(ANativeActivity* activity) { + LOGV("Pause: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE); +} + +static void onStop(ANativeActivity* activity) { + LOGV("Stop: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP); +} + +static void onConfigurationChanged(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)activity->instance; + LOGV("ConfigurationChanged: %p\n", activity); + android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED); +} + +static void onLowMemory(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)activity->instance; + LOGV("LowMemory: %p\n", activity); + android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY); +} + +static void onWindowFocusChanged(ANativeActivity* activity, int focused) { + LOGV("WindowFocusChanged: %p -- %d\n", activity, focused); + android_app_write_cmd((struct android_app*)activity->instance, + focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); +} + +static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { + LOGV("NativeWindowCreated: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, window); +} + +static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { + LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, NULL); +} + +static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { + LOGV("InputQueueCreated: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, queue); +} + +static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { + LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, NULL); +} + +void ANativeActivity_onCreate(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + LOGV("Creating: %p\n", activity); + activity->callbacks->onDestroy = onDestroy; + activity->callbacks->onStart = onStart; + activity->callbacks->onResume = onResume; + activity->callbacks->onSaveInstanceState = onSaveInstanceState; + activity->callbacks->onPause = onPause; + activity->callbacks->onStop = onStop; + activity->callbacks->onConfigurationChanged = onConfigurationChanged; + activity->callbacks->onLowMemory = onLowMemory; + activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; + activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; + activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; + activity->callbacks->onInputQueueCreated = onInputQueueCreated; + activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; + + activity->instance = android_app_create(activity, savedState, savedStateSize); +} diff --git a/armorcore/sources/backends/android/android_native_app_glue.h b/armorcore/sources/backends/android/android_native_app_glue.h new file mode 100644 index 000000000..97202e094 --- /dev/null +++ b/armorcore/sources/backends/android/android_native_app_glue.h @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _ANDROID_NATIVE_APP_GLUE_H +#define _ANDROID_NATIVE_APP_GLUE_H + +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The native activity interface provided by + * is based on a set of application-provided callbacks that will be called + * by the Activity's main thread when certain events occur. + * + * This means that each one of this callbacks _should_ _not_ block, or they + * risk having the system force-close the application. This programming + * model is direct, lightweight, but constraining. + * + * The 'android_native_app_glue' static library is used to provide a different + * execution model where the application can implement its own main event + * loop in a different thread instead. Here's how it works: + * + * 1/ The application must provide a function named "android_main()" that + * will be called when the activity is created, in a new thread that is + * distinct from the activity's main thread. + * + * 2/ android_main() receives a pointer to a valid "android_app" structure + * that contains references to other important objects, e.g. the + * ANativeActivity obejct instance the application is running in. + * + * 3/ the "android_app" object holds an ALooper instance that already + * listens to two important things: + * + * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX + * declarations below. + * + * - input events coming from the AInputQueue attached to the activity. + * + * Each of these correspond to an ALooper identifier returned by + * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT, + * respectively. + * + * Your application can use the same ALooper to listen to additional + * file-descriptors. They can either be callback based, or with return + * identifiers starting with LOOPER_ID_USER. + * + * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event, + * the returned data will point to an android_poll_source structure. You + * can call the process() function on it, and fill in android_app->onAppCmd + * and android_app->onInputEvent to be called for your own processing + * of the event. + * + * Alternatively, you can call the low-level functions to read and process + * the data directly... look at the process_cmd() and process_input() + * implementations in the glue to see how to do this. + * + * See the sample named "native-activity" that comes with the NDK with a + * full usage example. Also look at the JavaDoc of NativeActivity. + */ + +struct android_app; + +/** + * Data associated with an ALooper fd that will be returned as the "outData" + * when that source has data ready. + */ +struct android_poll_source { + // The identifier of this source. May be LOOPER_ID_MAIN or + // LOOPER_ID_INPUT. + int32_t id; + + // The android_app this ident is associated with. + struct android_app* app; + + // Function to call to perform the standard processing of data from + // this source. + void (*process)(struct android_app* app, struct android_poll_source* source); +}; + +/** + * This is the interface for the standard glue code of a threaded + * application. In this model, the application's code is running + * in its own thread separate from the main thread of the process. + * It is not required that this thread be associated with the Java + * VM, although it will need to be in order to make JNI calls any + * Java objects. + */ +struct android_app { + // The application can place a pointer to its own state object + // here if it likes. + void* userData; + + // Fill this in with the function to process main app commands (APP_CMD_*) + void (*onAppCmd)(struct android_app* app, int32_t cmd); + + // Fill this in with the function to process input events. At this point + // the event has already been pre-dispatched, and it will be finished upon + // return. Return 1 if you have handled the event, 0 for any default + // dispatching. + int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); + + // The ANativeActivity object instance that this app is running in. + ANativeActivity* activity; + + // The current configuration the app is running in. + AConfiguration* config; + + // This is the last instance's saved state, as provided at creation time. + // It is NULL if there was no state. You can use this as you need; the + // memory will remain around until you call android_app_exec_cmd() for + // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL. + // These variables should only be changed when processing a APP_CMD_SAVE_STATE, + // at which point they will be initialized to NULL and you can malloc your + // state and place the information here. In that case the memory will be + // freed for you later. + void* savedState; + size_t savedStateSize; + + // The ALooper associated with the app's thread. + ALooper* looper; + + // When non-NULL, this is the input queue from which the app will + // receive user input events. + AInputQueue* inputQueue; + + // When non-NULL, this is the window surface that the app can draw in. + ANativeWindow* window; + + // Current content rectangle of the window; this is the area where the + // window's content should be placed to be seen by the user. + ARect contentRect; + + // Current state of the app's activity. May be either APP_CMD_START, + // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. + int activityState; + + // This is non-zero when the application's NativeActivity is being + // destroyed and waiting for the app thread to complete. + int destroyRequested; + + // ------------------------------------------------- + // Below are "private" implementation of the glue code. + + pthread_mutex_t mutex; + pthread_cond_t cond; + + int msgread; + int msgwrite; + + pthread_t thread; + + struct android_poll_source cmdPollSource; + struct android_poll_source inputPollSource; + + int running; + int stateSaved; + int destroyed; + int redrawNeeded; + AInputQueue* pendingInputQueue; + ANativeWindow* pendingWindow; + ARect pendingContentRect; +}; + +enum { + /** + * Looper data ID of commands coming from the app's main thread, which + * is returned as an identifier from ALooper_pollOnce(). The data for this + * identifier is a pointer to an android_poll_source structure. + * These can be retrieved and processed with android_app_read_cmd() + * and android_app_exec_cmd(). + */ + LOOPER_ID_MAIN = 1, + + /** + * Looper data ID of events coming from the AInputQueue of the + * application's window, which is returned as an identifier from + * ALooper_pollOnce(). The data for this identifier is a pointer to an + * android_poll_source structure. These can be read via the inputQueue + * object of android_app. + */ + LOOPER_ID_INPUT = 2, + + /** + * Start of user-defined ALooper identifiers. + */ + LOOPER_ID_USER = 3, +}; + +enum { + /** + * Command from main thread: the AInputQueue has changed. Upon processing + * this command, android_app->inputQueue will be updated to the new queue + * (or NULL). + */ + APP_CMD_INPUT_CHANGED, + + /** + * Command from main thread: a new ANativeWindow is ready for use. Upon + * receiving this command, android_app->window will contain the new window + * surface. + */ + APP_CMD_INIT_WINDOW, + + /** + * Command from main thread: the existing ANativeWindow needs to be + * terminated. Upon receiving this command, android_app->window still + * contains the existing window; after calling android_app_exec_cmd + * it will be set to NULL. + */ + APP_CMD_TERM_WINDOW, + + /** + * Command from main thread: the current ANativeWindow has been resized. + * Please redraw with its new size. + */ + APP_CMD_WINDOW_RESIZED, + + /** + * Command from main thread: the system needs that the current ANativeWindow + * be redrawn. You should redraw the window before handing this to + * android_app_exec_cmd() in order to avoid transient drawing glitches. + */ + APP_CMD_WINDOW_REDRAW_NEEDED, + + /** + * Command from main thread: the content area of the window has changed, + * such as from the soft input window being shown or hidden. You can + * find the new content rect in android_app::contentRect. + */ + APP_CMD_CONTENT_RECT_CHANGED, + + /** + * Command from main thread: the app's activity window has gained + * input focus. + */ + APP_CMD_GAINED_FOCUS, + + /** + * Command from main thread: the app's activity window has lost + * input focus. + */ + APP_CMD_LOST_FOCUS, + + /** + * Command from main thread: the current device configuration has changed. + */ + APP_CMD_CONFIG_CHANGED, + + /** + * Command from main thread: the system is running low on memory. + * Try to reduce your memory use. + */ + APP_CMD_LOW_MEMORY, + + /** + * Command from main thread: the app's activity has been started. + */ + APP_CMD_START, + + /** + * Command from main thread: the app's activity has been resumed. + */ + APP_CMD_RESUME, + + /** + * Command from main thread: the app should generate a new saved state + * for itself, to restore from later if needed. If you have saved state, + * allocate it with malloc and place it in android_app.savedState with + * the size in android_app.savedStateSize. The will be freed for you + * later. + */ + APP_CMD_SAVE_STATE, + + /** + * Command from main thread: the app's activity has been paused. + */ + APP_CMD_PAUSE, + + /** + * Command from main thread: the app's activity has been stopped. + */ + APP_CMD_STOP, + + /** + * Command from main thread: the app's activity is being destroyed, + * and waiting for the app thread to clean up and exit before proceeding. + */ + APP_CMD_DESTROY, +}; + +/** + * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next + * app command message. + */ +int8_t android_app_read_cmd(struct android_app* android_app); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * initial pre-processing of the given command. You can perform your own + * actions for the command after calling this function. + */ +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * final post-processing of the given command. You must have done your own + * actions for the command before calling this function. + */ +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Dummy function you can call to ensure glue code isn't stripped. + */ +void app_dummy(); + +/** + * This is the function that application code must implement, representing + * the main entry to the app. + */ +extern void android_main(struct android_app* app); + +#ifdef __cplusplus +} +#endif + +#endif /* _ANDROID_NATIVE_APP_GLUE_H */ diff --git a/armorcore/sources/backends/android/java/arm/AndroidHttpRequest.java b/armorcore/sources/backends/android/java/arm/AndroidHttpRequest.java new file mode 100644 index 000000000..32bc755fd --- /dev/null +++ b/armorcore/sources/backends/android/java/arm/AndroidHttpRequest.java @@ -0,0 +1,31 @@ +package arm; + +// TODO: Move to Kinc + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +class AndroidHttpRequest { + + public static byte[] androidHttpRequest(String address) throws Exception { + // https://developer.android.com/reference/java/net/HttpURLConnection.html + URL url = new URL(address); + HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection(); + InputStream in = new BufferedInputStream(urlConnection.getInputStream()); + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int i; + byte[] data = new byte[4]; + while ((i = in.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, i); + } + buffer.flush(); + byte[] result = buffer.toByteArray(); + + urlConnection.disconnect(); + return result; + } +} diff --git a/armorcore/sources/backends/android/java/tech/kinc/KincActivity.kt b/armorcore/sources/backends/android/java/tech/kinc/KincActivity.kt new file mode 100644 index 000000000..1ccdd3d43 --- /dev/null +++ b/armorcore/sources/backends/android/java/tech/kinc/KincActivity.kt @@ -0,0 +1,267 @@ +package tech.kinc + +import android.app.NativeActivity +import android.content.Context +import android.content.Intent +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Bundle +import android.os.Handler +import android.os.Message +import android.os.Vibrator +import android.os.VibrationEffect +import android.os.Build +import android.view.KeyEvent +import android.view.View +import android.view.WindowManager +import android.view.inputmethod.InputMethodManager +import kotlin.system.exitProcess +import android.content.ContentResolver; +import android.util.Log +import android.view.DragAndDropPermissions; +import android.view.DragEvent; +import android.webkit.MimeTypeMap; +import android.view.DragEvent.ACTION_DRAG_STARTED; +import android.view.DragEvent.ACTION_DROP; +import androidx.core.database.getStringOrNull +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import kotlin.math.log + +class KincActivity: NativeActivity(), KeyEvent.Callback { + companion object { + var instance: KincActivity? = null + + @JvmStatic + fun showKeyboard() { + instance!!.inputManager!!.showSoftInput(instance!!.window.decorView, 0) + } + + @JvmStatic + fun hideKeyboard() { + instance!!.inputManager!!.hideSoftInputFromWindow(instance!!.window.decorView.windowToken, 0) + instance!!.delayedHideSystemUI() + } + + @JvmStatic + fun loadURL(url: String) { + val i = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + instance!!.startActivity(i) + } + + @JvmStatic + fun getLanguage(): String { + return java.util.Locale.getDefault().language + } + + @JvmStatic + fun vibrate(ms: Int) { + val v: Vibrator = instance!!.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + v.vibrate(VibrationEffect.createOneShot(ms.toLong(), VibrationEffect.DEFAULT_AMPLITUDE)) + } + else { + // deprecated in API 26 + v.vibrate(ms.toLong()) + } + } + + @JvmStatic + fun getRotation(): Int { + val context: Context = instance!!.applicationContext + val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + return manager.defaultDisplay.rotation + } + + @JvmStatic + fun getScreenDpi(): Int { + val context: Context = instance!!.applicationContext + val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + val metrics: android.util.DisplayMetrics = android.util.DisplayMetrics() + manager.defaultDisplay.getMetrics(metrics) + return metrics.xdpi.toInt() + } + + @JvmStatic + fun getRefreshRate(): Int { + val context: Context = instance!!.applicationContext + val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + return manager.defaultDisplay.refreshRate.toInt() + } + + @JvmStatic + fun getDisplayWidth(): Int { + val context: Context = instance!!.applicationContext + val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + val size: android.graphics.Point = android.graphics.Point() + manager.defaultDisplay.getRealSize(size) + return size.x + } + + @JvmStatic + fun getDisplayHeight(): Int { + val context: Context = instance!!.applicationContext + val manager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager + val size: android.graphics.Point = android.graphics.Point() + manager.defaultDisplay.getRealSize(size) + return size.y + } + + @JvmStatic + fun stop() { + instance!!.runOnUiThread { + fun run() { + instance!!.finish() + exitProcess(0) + } + } + } + + class MyHandler(private val kincActivity: KincActivity) : Handler() { + override fun handleMessage(msg: Message) { + kincActivity.hideSystemUI() + } + } + + @JvmStatic + public fun pickFile() { + val intent: Intent = Intent(Intent.ACTION_GET_CONTENT) + intent.type = "*/*" + instance!!.startActivityForResult(Intent.createChooser(intent, "Select File"), 1) + } + } + + var inputManager: InputMethodManager? = null + private var isDisabledStickyImmersiveMode = false + + private val hideSystemUIHandler = MyHandler(this) + + override fun onCreate(state: Bundle?) { + super.onCreate(state) + hideSystemUI() + instance = this + inputManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + isDisabledStickyImmersiveMode = try { + val ai: ApplicationInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA) + val bundle: Bundle = ai.metaData + bundle.getBoolean("disableStickyImmersiveMode") + } catch (e: PackageManager.NameNotFoundException) { + false + } catch (e: NullPointerException) { + false + } + + window.decorView.setOnDragListener( + fun (view: View, dragEvent: DragEvent): Boolean { + if (dragEvent.action == ACTION_DRAG_STARTED) return true + if (dragEvent.action == ACTION_DROP) { + val dropPermissions = requestDragAndDropPermissions(dragEvent) + importFile(dragEvent.clipData.getItemAt(0).uri) + dropPermissions.release() + return true + } + return false + } + ); + } + + private fun hideSystemUI() { + window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + } + + private fun delayedHideSystemUI() { + hideSystemUIHandler.removeMessages(0) + if (!isDisabledStickyImmersiveMode) { + hideSystemUIHandler.sendEmptyMessageDelayed(0, 300) + } + } + + override fun onWindowFocusChanged(hasFocus: Boolean) { + super.onWindowFocusChanged(hasFocus) + if (hasFocus) { + delayedHideSystemUI() + } + else { + hideSystemUIHandler.removeMessages(0) + } + } + + override fun onKeyMultiple(keyCode: Int, count: Int, event: KeyEvent): Boolean { + this.nativeKincKeyPress(event.characters) + return false + } + + private external fun nativeKincKeyPress(chars: String) + + private external fun onAndroidFilePicked(pickedPath: String) + private external fun getMobileTitle(): String + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == 1 && resultCode == RESULT_OK) { + importFile(data.data!!) + } + } + + private fun importFile(pickedFile: Uri) { + val resolver: ContentResolver = applicationContext.contentResolver + val inps: InputStream = resolver.openInputStream(pickedFile)!! + try { + val bis: BufferedInputStream = BufferedInputStream(inps) + val dir: File = File(filesDir.absolutePath + "/" + getMobileTitle()) + dir.mkdirs() + var path: List = pickedFile.path!!.split("/") + + // Samsung files app removes extension from fileName + val filePath: Array = arrayOf(android.provider.MediaStore.Images.Media.DATA) + val cursor: android.database.Cursor = contentResolver.query(pickedFile, filePath, null, null, null)!! + cursor.moveToFirst() + val pickedPath: String? = cursor.getStringOrNull(cursor.getColumnIndex(filePath[0])) + if (pickedPath != null) { + path = pickedPath.split("/") + } + cursor.close() + + var fileName: String = path[path.size - 1] + + // Extension still unknown + if (!fileName.contains(".")) { + var ext: String = MimeTypeMap.getSingleton().getExtensionFromMimeType(contentResolver.getType(pickedFile))!! + // Note: for obj/fbx file, the extension returned is bin.. + if (ext == "bin") { + bis.mark(0) + val header: StringBuilder = StringBuilder() + for (i in 0..17) { + val c: Int = bis.read() + if (c == -1) break + header.append(c.toChar()) + } + ext = if (header.toString() == "Kaydara FBX Binary") "fbx" else "obj" + bis.reset() + } + fileName += "." + ext + } + + val dst: String = filesDir.absolutePath + "/" + getMobileTitle() + "/" + fileName + val os: OutputStream = FileOutputStream(dst) + try { + val buf = ByteArray(1024) + var len = bis.read(buf) + while (len > 0) { + os.write(buf, 0, len) + len = bis.read(buf) + } + onAndroidFilePicked(dst) + } + catch (e: IOException) {} + } + catch (e: FileNotFoundException) {} + catch (e: IOException) {} + } +} diff --git a/armorcore/sources/backends/android/java/tech/kinc/KincMoviePlayer.kt b/armorcore/sources/backends/android/java/tech/kinc/KincMoviePlayer.kt new file mode 100644 index 000000000..724f52b51 --- /dev/null +++ b/armorcore/sources/backends/android/java/tech/kinc/KincMoviePlayer.kt @@ -0,0 +1,50 @@ +package tech.kinc + +import java.util.ArrayList + +import android.view.Surface + +class KincMoviePlayer(var path: String) { + companion object { + var players = ArrayList() + + @JvmStatic + fun updateAll() { + for (player in KincMoviePlayer.players) { + player!!.update() + } + } + + fun remove(id: Int) { + players[id] = null + } + } + + private var movieTexture: KincMovieTexture? = null + var id: Int = players.size + + init { + players.add(this) + } + + fun init() { + movieTexture = KincMovieTexture() + val surface = Surface(movieTexture!!.surfaceTexture) + nativeCreate(path, surface, id) + surface.release() + } + + fun getMovieTexture(): KincMovieTexture? { + return movieTexture + } + + fun update(): Boolean { + return movieTexture!!.update() + } + + fun getTextureId(): Int { + return movieTexture!!.textureId + } + + private external fun nativeCreate(path: String, surface: Surface, id: Int) +} diff --git a/armorcore/sources/backends/android/java/tech/kinc/KincMovieTexture.kt b/armorcore/sources/backends/android/java/tech/kinc/KincMovieTexture.kt new file mode 100644 index 000000000..ed058b8f8 --- /dev/null +++ b/armorcore/sources/backends/android/java/tech/kinc/KincMovieTexture.kt @@ -0,0 +1,62 @@ +package tech.kinc + +import android.graphics.SurfaceTexture +import android.graphics.SurfaceTexture.OnFrameAvailableListener +import android.opengl.GLES20 + +class KincMovieTexture: OnFrameAvailableListener { + private val GL_TEXTURE_EXTERNAL_OES: Int = 0x8D65 + + var textureId: Int = 0 + + init { + val textures = IntArray(1) + GLES20.glGenTextures(1, textures, 0) + textureId = textures[0] + + GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, textureId) + GLES20.glTexParameteri( + GL_TEXTURE_EXTERNAL_OES, + GLES20.GL_TEXTURE_MIN_FILTER, + GLES20.GL_NEAREST + ) + GLES20.glTexParameteri( + GL_TEXTURE_EXTERNAL_OES, + GLES20.GL_TEXTURE_MAG_FILTER, + GLES20.GL_LINEAR + ) + GLES20.glTexParameteri( + GL_TEXTURE_EXTERNAL_OES, + GLES20.GL_TEXTURE_WRAP_S, + GLES20.GL_CLAMP_TO_EDGE + ) + GLES20.glTexParameteri( + GL_TEXTURE_EXTERNAL_OES, + GLES20.GL_TEXTURE_WRAP_T, + GLES20.GL_CLAMP_TO_EDGE + ) + } + + var surfaceTexture = SurfaceTexture(textureId) + + init { + surfaceTexture.setOnFrameAvailableListener(this) + } + + private var updateTexture = false + + fun update(): Boolean { + val ret = updateTexture + if (updateTexture) { + surfaceTexture.updateTexImage() + updateTexture = false + } + return ret + } + + override fun onFrameAvailable(surface: SurfaceTexture) { + if (surfaceTexture == surface) { + updateTexture = true + } + } +} diff --git a/armorcore/sources/backends/android/kinc/backend/Android.h b/armorcore/sources/backends/android/kinc/backend/Android.h new file mode 100644 index 000000000..b1f91e00c --- /dev/null +++ b/armorcore/sources/backends/android/kinc/backend/Android.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// name in usual Java syntax (points, no slashes) +jclass kinc_android_find_class(JNIEnv *env, const char *name); + +ANativeActivity *kinc_android_get_activity(void); + +AAssetManager *kinc_android_get_asset_manager(void); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/android/kinc/backend/android_file_dialog.c b/armorcore/sources/backends/android/kinc/backend/android_file_dialog.c new file mode 100644 index 000000000..ff421a905 --- /dev/null +++ b/armorcore/sources/backends/android/kinc/backend/android_file_dialog.c @@ -0,0 +1,111 @@ +#include "android_file_dialog.h" +#include +#include +#include +#include +#include +#include +#include + +extern char mobile_title[1024]; + +ANativeActivity *kinc_android_get_activity(void); +jclass kinc_android_find_class(JNIEnv *env, const char *name); + +JNIEXPORT void JNICALL Java_tech_kinc_KincActivity_onAndroidFilePicked(JNIEnv *env, jobject jobj, jstring jstr) { + if (jstr == NULL) return; + const char *str = (*env)->GetStringUTFChars(env, jstr, 0); + size_t len = strlen(str); + wchar_t filePath[len + 1]; + mbstowcs(filePath, (char *)str, len); + filePath[len] = 0; + + kinc_internal_drop_files_callback(filePath); + (*env)->ReleaseStringUTFChars(env, jstr, str); +} + +JNIEXPORT jstring JNICALL Java_tech_kinc_KincActivity_getMobileTitle(JNIEnv *env, jobject jobj) { + jstring result = (*env)->NewStringUTF(env, mobile_title); + return result; +} + +void AndroidFileDialogOpen() { + ANativeActivity *activity = kinc_android_get_activity(); + JNIEnv *env; + JavaVM *vm = kinc_android_get_activity()->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + jclass kincActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity"); + (*env)->CallStaticVoidMethod(env, kincActivityClass, (*env)->GetStaticMethodID(env, kincActivityClass, "pickFile", "()V")); + (*vm)->DetachCurrentThread(vm); +} + +wchar_t *AndroidFileDialogSave() { + // kinc_android_get_activity()->externalDataPath; // /storage/emulated/0/Android/data/org.armorpaint/files + mkdir("/storage/emulated/0/Pictures/ArmorPaint", 0777); + return L"/storage/emulated/0/Pictures/ArmorPaint/untitled"; +} + +jstring android_permission_name(JNIEnv *env, const char *perm_name) { + jclass ClassManifestpermission = (*env)->FindClass(env, "android/Manifest$permission"); + jfieldID lid_PERM = (*env)->GetStaticFieldID(env, ClassManifestpermission, perm_name, "Ljava/lang/String;"); + jstring ls_PERM = (jstring)((*env)->GetStaticObjectField(env, ClassManifestpermission, lid_PERM)); + return ls_PERM; +} + +bool android_has_permission(struct android_app *app, const char *perm_name) { + ANativeActivity *activity = kinc_android_get_activity(); + JNIEnv *env; + JavaVM *vm = kinc_android_get_activity()->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + bool result = false; + jstring ls_PERM = android_permission_name(env, perm_name); + jint PERMISSION_GRANTED = (jint)(-1); + { + jclass ClassPackageManager = (*env)->FindClass(env, "android/content/pm/PackageManager"); + jfieldID lid_PERMISSION_GRANTED = (*env)->GetStaticFieldID(env, ClassPackageManager, "PERMISSION_GRANTED", "I"); + PERMISSION_GRANTED = (*env)->GetStaticIntField(env, ClassPackageManager, lid_PERMISSION_GRANTED); + } + { + jobject activity = app->activity->clazz; + jclass ClassContext = (*env)->FindClass(env, "android/content/Context"); + jmethodID MethodcheckSelfPermission = (*env)->GetMethodID(env, ClassContext, "checkSelfPermission", "(Ljava/lang/String;)I"); + jint int_result = (*env)->CallIntMethod(env, activity, MethodcheckSelfPermission, ls_PERM); + result = (int_result == PERMISSION_GRANTED); + } + (*vm)->DetachCurrentThread(vm); + return result; +} + +void android_request_file_permissions(struct android_app *app) { + ANativeActivity *activity = kinc_android_get_activity(); + JNIEnv *env; + JavaVM *vm = kinc_android_get_activity()->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + jobjectArray perm_array = (*env)->NewObjectArray(env, 2, (*env)->FindClass(env, "java/lang/String"), (*env)->NewStringUTF(env, "")); + (*env)->SetObjectArrayElement(env, perm_array, 0, android_permission_name(env, "READ_EXTERNAL_STORAGE")); + (*env)->SetObjectArrayElement(env, perm_array, 1, android_permission_name(env, "WRITE_EXTERNAL_STORAGE")); + jobject jactivity = app->activity->clazz; + jclass ClassActivity = (*env)->FindClass(env, "android/app/Activity"); + jmethodID MethodrequestPermissions = (*env)->GetMethodID(env, ClassActivity, "requestPermissions", "([Ljava/lang/String;I)V"); + (*env)->CallVoidMethod(env, jactivity, MethodrequestPermissions, perm_array, 0); + (*vm)->DetachCurrentThread(vm); +} + +void android_check_permissions() { + ANativeActivity *activity = kinc_android_get_activity(); + struct android_app *app = (struct android_app *)activity->instance; + bool hasPermissions = android_has_permission(app, "READ_EXTERNAL_STORAGE") && android_has_permission(app, "WRITE_EXTERNAL_STORAGE"); + if (!hasPermissions) android_request_file_permissions(app); + + JNIEnv *env; + JavaVM *vm = kinc_android_get_activity()->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + jclass kincActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity"); + JNINativeMethod methodTable[] = { + {"onAndroidFilePicked", "(Ljava/lang/String;)V", (void *)Java_tech_kinc_KincActivity_onAndroidFilePicked}, + {"getMobileTitle", "()Ljava/lang/String;", (void *)Java_tech_kinc_KincActivity_getMobileTitle} + }; + int methodTableSize = sizeof(methodTable) / sizeof(methodTable[0]); + (*env)->RegisterNatives(env, kincActivityClass, methodTable, methodTableSize); + (*vm)->DetachCurrentThread(vm); +} diff --git a/armorcore/sources/backends/android/kinc/backend/android_file_dialog.h b/armorcore/sources/backends/android/kinc/backend/android_file_dialog.h new file mode 100644 index 000000000..63907d0d3 --- /dev/null +++ b/armorcore/sources/backends/android/kinc/backend/android_file_dialog.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void AndroidFileDialogOpen(); +wchar_t *AndroidFileDialogSave(); +void android_check_permissions(); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/android/kinc/backend/android_http_request.c b/armorcore/sources/backends/android/kinc/backend/android_http_request.c new file mode 100644 index 000000000..f075109b1 --- /dev/null +++ b/armorcore/sources/backends/android/kinc/backend/android_http_request.c @@ -0,0 +1,28 @@ +#include "android_http_request.h" +#include +#include +#include +#include + +ANativeActivity *kinc_android_get_activity(void); +jclass kinc_android_find_class(JNIEnv *env, const char *name); + +void android_http_request(const char *url, const char *path, const char *data, int port, bool secure, int method, const char *header, + kinc_http_callback_t callback, void *callbackdata) { + ANativeActivity *activity = kinc_android_get_activity(); + JNIEnv *env; + JavaVM *vm = kinc_android_get_activity()->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + jclass activityClass = kinc_android_find_class(env, "arm.AndroidHttpRequest"); + + jstring jstr = (*env)->NewStringUTF(env, url); + jbyteArray bytes_array = (jbyteArray)((*env)->CallStaticObjectMethod(env, activityClass, (*env)->GetStaticMethodID(env, activityClass, "androidHttpRequest", "(Ljava/lang/String;)[B"), jstr)); + jsize num_bytes = (*env)->GetArrayLength(env, bytes_array); + jbyte *elements = (*env)->GetByteArrayElements(env, bytes_array, NULL); + if (elements != NULL) { + callback(0, 200, (char *)elements, callbackdata); + // (*env)->ReleaseByteArrayElements(env, bytes_array, elements, JNI_ABORT); + } + + (*vm)->DetachCurrentThread(vm); +} diff --git a/armorcore/sources/backends/android/kinc/backend/android_http_request.h b/armorcore/sources/backends/android/kinc/backend/android_http_request.h new file mode 100644 index 000000000..eb846a98e --- /dev/null +++ b/armorcore/sources/backends/android/kinc/backend/android_http_request.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +typedef void (*kinc_http_callback_t)(int error, int response, const char *body, void *callbackdata); + +#ifdef __cplusplus +extern "C" { +#endif + +void android_http_request(const char *url, const char *path, const char *data, int port, bool secure, int method, const char *header, + kinc_http_callback_t callback, void *callbackdata); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/android/kinc/backend/androidunit.c b/armorcore/sources/backends/android/kinc/backend/androidunit.c new file mode 100644 index 000000000..f4a4e00fb --- /dev/null +++ b/armorcore/sources/backends/android/kinc/backend/androidunit.c @@ -0,0 +1,5 @@ +#include "audio.c.h" +#include "display.c.h" +#include "system.c.h" +#include "window.c.h" +#include "video.c.h" \ No newline at end of file diff --git a/armorcore/sources/backends/android/kinc/backend/audio.c.h b/armorcore/sources/backends/android/kinc/backend/audio.c.h new file mode 100644 index 000000000..e33a7fd89 --- /dev/null +++ b/armorcore/sources/backends/android/kinc/backend/audio.c.h @@ -0,0 +1,133 @@ +#include + +#include +#include + +#include +#include + +static kinc_a2_buffer_t a2_buffer; + +static SLObjectItf engineObject; +static SLEngineItf engineEngine; +static SLObjectItf outputMixObject; +static SLObjectItf bqPlayerObject; +static SLPlayItf bqPlayerPlay = NULL; +static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue; +#define AUDIO_BUFFER_SIZE 1 * 1024 +static int16_t tempBuffer[AUDIO_BUFFER_SIZE]; + +static void copySample(void *buffer) { + float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location]; + float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location]; + a2_buffer.read_location += 1; + if (a2_buffer.read_location >= a2_buffer.data_size) { + a2_buffer.read_location = 0; + } + ((int16_t *)buffer)[0] = (int16_t)(left_value * 32767); + ((int16_t *)buffer)[1] = (int16_t)(right_value * 32767); +} + +static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf caller, void *context) { + if (kinc_a2_internal_callback(&a2_buffer, AUDIO_BUFFER_SIZE / 2)) { + for (int i = 0; i < AUDIO_BUFFER_SIZE; i += 2) { + copySample(&tempBuffer[i]); + } + } + else { + memset(tempBuffer, 0, sizeof(tempBuffer)); + } + SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, tempBuffer, AUDIO_BUFFER_SIZE * 2); +} + +static bool initialized = false; + +void kinc_a2_init() { + if (initialized) { + return; + } + + kinc_a2_internal_init(); + initialized = true; + + a2_buffer.read_location = 0; + a2_buffer.write_location = 0; + a2_buffer.data_size = 128 * 1024; + a2_buffer.channel_count = 2; + a2_buffer.channels[0] = (float*)malloc(a2_buffer.data_size * sizeof(float)); + a2_buffer.channels[1] = (float*)malloc(a2_buffer.data_size * sizeof(float)); + + SLresult result; + result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); + result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); + result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); + + const SLInterfaceID ids[] = {SL_IID_VOLUME}; + const SLboolean req[] = {SL_BOOLEAN_FALSE}; + result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req); + result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); + + SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; + SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, 2, + SL_SAMPLINGRATE_44_1, SL_PCMSAMPLEFORMAT_FIXED_16, + SL_PCMSAMPLEFORMAT_FIXED_16, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, + SL_BYTEORDER_LITTLEENDIAN}; + SLDataSource audioSrc = {&loc_bufq, &format_pcm}; + + SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject}; + SLDataSink audioSnk = {&loc_outmix, NULL}; + + const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE}; + const SLboolean req1[] = {SL_BOOLEAN_TRUE}; + result = (*engineEngine)->CreateAudioPlayer(engineEngine, &(bqPlayerObject), &audioSrc, &audioSnk, 1, ids1, req1); + result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); + + result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &(bqPlayerPlay)); + + result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(bqPlayerBufferQueue)); + + result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL); + + result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); + + memset(tempBuffer, 0, sizeof(tempBuffer)); + result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, tempBuffer, AUDIO_BUFFER_SIZE * 2); +} + +void pauseAudio() { + if (bqPlayerPlay == NULL) { + return; + } + SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED); +} + +void resumeAudio() { + if (bqPlayerPlay == NULL) { + return; + } + SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); +} + +void kinc_a2_update() {} + +void kinc_a2_shutdown() { + if (bqPlayerObject != NULL) { + (*bqPlayerObject)->Destroy(bqPlayerObject); + bqPlayerObject = NULL; + bqPlayerPlay = NULL; + bqPlayerBufferQueue = NULL; + } + if (outputMixObject != NULL) { + (*outputMixObject)->Destroy(outputMixObject); + outputMixObject = NULL; + } + if (engineObject != NULL) { + (*engineObject)->Destroy(engineObject); + engineObject = NULL; + engineEngine = NULL; + } +} + +uint32_t kinc_a2_samples_per_second(void) { + return 44100; +} diff --git a/armorcore/sources/backends/android/kinc/backend/display.c.h b/armorcore/sources/backends/android/kinc/backend/display.c.h new file mode 100644 index 000000000..5ed5f4feb --- /dev/null +++ b/armorcore/sources/backends/android/kinc/backend/display.c.h @@ -0,0 +1,106 @@ +#include + +#include +#include + +typedef struct { + bool available; + int x; + int y; + int width; + int height; + bool primary; + int number; +} kinc_display_t; + +static kinc_display_t display; + +int kinc_count_displays(void) { + return 1; +} + +int kinc_primary_display(void) { + return 0; +} + +static int width() { + JNIEnv *env; + JavaVM *vm = kinc_android_get_activity()->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity"); + jmethodID koreActivityGetScreenDpi = (*env)->GetStaticMethodID(env, koreActivityClass, "getDisplayWidth", "()I"); + int width = (*env)->CallStaticIntMethod(env, koreActivityClass, koreActivityGetScreenDpi); + (*vm)->DetachCurrentThread(vm); + return width; +} + +static int height() { + JNIEnv *env; + JavaVM *vm = kinc_android_get_activity()->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity"); + jmethodID koreActivityGetScreenDpi = (*env)->GetStaticMethodID(env, koreActivityClass, "getDisplayHeight", "()I"); + int height = (*env)->CallStaticIntMethod(env, koreActivityClass, koreActivityGetScreenDpi); + (*vm)->DetachCurrentThread(vm); + return height; +} + +static int pixelsPerInch() { + JNIEnv *env; + JavaVM *vm = kinc_android_get_activity()->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity"); + jmethodID koreActivityGetScreenDpi = (*env)->GetStaticMethodID(env, koreActivityClass, "getScreenDpi", "()I"); + int dpi = (*env)->CallStaticIntMethod(env, koreActivityClass, koreActivityGetScreenDpi); + (*vm)->DetachCurrentThread(vm); + return dpi; +} + +static int refreshRate() { + JNIEnv *env; + JavaVM *vm = kinc_android_get_activity()->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity"); + jmethodID koreActivityGetScreenDpi = (*env)->GetStaticMethodID(env, koreActivityClass, "getRefreshRate", "()I"); + int dpi = (*env)->CallStaticIntMethod(env, koreActivityClass, koreActivityGetScreenDpi); + (*vm)->DetachCurrentThread(vm); + return dpi; +} + +void kinc_display_init() {} + +kinc_display_mode_t kinc_display_available_mode(int display_index, int mode_index) { + kinc_display_mode_t mode; + mode.x = 0; + mode.y = 0; + mode.width = width(); + mode.height = height(); + mode.frequency = refreshRate(); + mode.bits_per_pixel = 32; + mode.pixels_per_inch = pixelsPerInch(); + return mode; +} + +int kinc_display_count_available_modes(int display_index) { + return 1; +} + +kinc_display_mode_t kinc_display_current_mode(int display) { + kinc_display_mode_t mode; + mode.x = 0; + mode.y = 0; + mode.width = width(); + mode.height = height(); + mode.frequency = refreshRate(); + mode.bits_per_pixel = 32; + mode.pixels_per_inch = pixelsPerInch(); + return mode; +} + +const char *kinc_display_name(int display) { + return "Display"; +} + +bool kinc_display_available(int display) { + return display == 0; +} diff --git a/armorcore/sources/backends/android/kinc/backend/system.c.h b/armorcore/sources/backends/android/kinc/backend/system.c.h new file mode 100644 index 000000000..2e6a53188 --- /dev/null +++ b/armorcore/sources/backends/android/kinc/backend/system.c.h @@ -0,0 +1,1298 @@ +#define EGL_NO_PLATFORM_SPECIFIC_TYPES +#include +#include +// #include +#include +#include +#include +#include +#include +// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +void pauseAudio(); +void resumeAudio(); + +static struct android_app *app = NULL; +static ANativeActivity *activity = NULL; +static ASensorManager *sensorManager = NULL; +static const ASensor *accelerometerSensor = NULL; +static const ASensor *gyroSensor = NULL; +static ASensorEventQueue *sensorEventQueue = NULL; + +static bool started = false; +static bool paused = true; +static bool displayIsInitialized = false; +static bool appIsForeground = false; +static bool activityJustResized = false; + +#include +#include + +#ifdef KINC_EGL + +EGLDisplay kinc_egl_get_display() { + return eglGetDisplay(EGL_DEFAULT_DISPLAY); +} + +EGLNativeWindowType kinc_egl_get_native_window(EGLDisplay display, EGLConfig config, int window) { + kinc_affirm(window == 0); + EGLint format; + eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format); + int e = ANativeWindow_setBuffersGeometry(app->window, 0, 0, format); + if (e < 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to set ANativeWindow buffer geometry."); + } + return app->window; +} + +#endif + +#ifdef KINC_VULKAN + +#include +#include + +VkResult kinc_vulkan_create_surface(VkInstance instance, int window_index, VkSurfaceKHR *surface) { + assert(app->window != NULL); + VkAndroidSurfaceCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; + createInfo.pNext = NULL; + createInfo.flags = 0; + createInfo.window = app->window; + return vkCreateAndroidSurfaceKHR(instance, &createInfo, NULL, surface); +} + +void kinc_vulkan_get_instance_extensions(const char **names, int *index, int max) { + assert(*index + 1 < max); + names[(*index)++] = VK_KHR_ANDROID_SURFACE_EXTENSION_NAME; +} + +VkBool32 kinc_vulkan_get_physical_device_presentation_support(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex) { + // https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VK_KHR_android_surface.html#_issues + // + // 1) Does Android need a way to query for compatibility between a particular physical device (and queue family?) + // and a specific Android display? + // RESOLVED: No. Currently on Android, any physical device is expected to be able to present to the system compositor, + // and all queue families must support the necessary image layout transitions and synchronization operations. + return true; +} +#endif + +#ifndef KINC_VULKAN +void kinc_egl_init_window(int window); +void kinc_egl_destroy_window(int window); +#endif +#ifdef KINC_VULKAN +void kinc_vulkan_init_window(int window); +#endif + +static void initDisplay() { +#ifndef KINC_VULKAN + kinc_egl_init_window(0); +#endif +#ifdef KINC_VULKAN + kinc_vulkan_init_window(0); +#endif +} + +static void termDisplay() { +#ifndef KINC_VULKAN + kinc_egl_destroy_window(0); +#endif +} + +static void updateAppForegroundStatus(bool displayIsInitializedValue, bool appIsForegroundValue) { + bool oldStatus = displayIsInitialized && appIsForeground; + displayIsInitialized = displayIsInitializedValue; + appIsForeground = appIsForegroundValue; + bool newStatus = displayIsInitialized && appIsForeground; + if (oldStatus != newStatus) { + if (newStatus) { + kinc_internal_foreground_callback(); + } + else { + kinc_internal_background_callback(); + } + } +} + +static bool isGamepadEvent(AInputEvent *event) { + return ((AInputEvent_getSource(event) & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD || + (AInputEvent_getSource(event) & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK || + (AInputEvent_getSource(event) & AINPUT_SOURCE_DPAD) == AINPUT_SOURCE_DPAD); +} + +static bool isPenEvent(AInputEvent *event) { + return (AInputEvent_getSource(event) & AINPUT_SOURCE_STYLUS) == AINPUT_SOURCE_STYLUS; +} + +static void touchInput(AInputEvent *event) { + int action = AMotionEvent_getAction(event); + int index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + int id = AMotionEvent_getPointerId(event, index); + float x = AMotionEvent_getX(event, index); + float y = AMotionEvent_getY(event, index); + switch (action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + if (id == 0) { + kinc_internal_mouse_trigger_press(0, 0, x, y); + } + if (isPenEvent(event)) { + kinc_internal_pen_trigger_press(0, x, y, AMotionEvent_getPressure(event, index)); + } + kinc_internal_surface_trigger_touch_start(id, x, y); + break; + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_HOVER_MOVE: { + size_t count = AMotionEvent_getPointerCount(event); + for (int i = 0; i < count; ++i) { + id = AMotionEvent_getPointerId(event, i); + x = AMotionEvent_getX(event, i); + y = AMotionEvent_getY(event, i); + if (id == 0) { + kinc_internal_mouse_trigger_move(0, x, y); + } + if (isPenEvent(event)) { + kinc_internal_pen_trigger_move(0, x, y, AMotionEvent_getPressure(event, index)); + } + kinc_internal_surface_trigger_move(id, x, y); + } + } break; + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_POINTER_UP: + if (id == 0) { + kinc_internal_mouse_trigger_release(0, 0, x, y); + } + if (isPenEvent(event)) { + kinc_internal_pen_trigger_release(0, x, y, AMotionEvent_getPressure(event, index)); + } + kinc_internal_surface_trigger_touch_end(id, x, y); + break; + case AMOTION_EVENT_ACTION_SCROLL: + if (id == 0) { + float scroll = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_VSCROLL, 0); + kinc_internal_mouse_trigger_scroll(0, -(int)scroll); + } + break; + } +} + +static float last_x = 0.0f; +static float last_y = 0.0f; +static float last_l = 0.0f; +static float last_r = 0.0f; +static bool last_hat_left = false; +static bool last_hat_right = false; +static bool last_hat_up = false; +static bool last_hat_down = false; + +static int32_t input(struct android_app *app, AInputEvent *event) { + if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { + int source = AInputEvent_getSource(event); + if (((source & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN) || ((source & AINPUT_SOURCE_MOUSE) == AINPUT_SOURCE_MOUSE)) { + touchInput(event); + return 1; + } + else if ((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) { + // int id = AInputEvent_getDeviceId(event); + + float x = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_X, 0); + if (x != last_x) { + kinc_internal_gamepad_trigger_axis(0, 0, x); + last_x = x; + } + + float y = -AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_Y, 0); + if (y != last_y) { + kinc_internal_gamepad_trigger_axis(0, 1, y); + last_y = y; + } + + float l = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_LTRIGGER, 0); + if (l != last_l) { + kinc_internal_gamepad_trigger_button(0, 6, l); + last_l = l; + } + + float r = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_RTRIGGER, 0); + if (r != last_r) { + kinc_internal_gamepad_trigger_button(0, 7, r); + last_r = r; + } + + float hat_x = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0); + + bool hat_left = false; + bool hat_right = false; + if (hat_x < -0.5f) { + hat_left = true; + } + else if (hat_x > 0.5f) { + hat_right = true; + } + + float hat_y = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0); + + bool hat_up = false; + bool hat_down = false; + if (hat_y < -0.5f) { + hat_up = true; + } + else if (hat_y > 0.5f) { + hat_down = true; + } + + if (hat_left != last_hat_left) { + kinc_internal_gamepad_trigger_button(0, 14, hat_left ? 1.0f : 0.0f); + last_hat_left = hat_left; + } + + if (hat_right != last_hat_right) { + kinc_internal_gamepad_trigger_button(0, 15, hat_right ? 1.0f : 0.0f); + last_hat_right = hat_right; + } + + if (hat_up != last_hat_up) { + kinc_internal_gamepad_trigger_button(0, 12, hat_up ? 1.0f : 0.0f); + last_hat_up = hat_up; + } + + if (hat_down != last_hat_down) { + kinc_internal_gamepad_trigger_button(0, 13, hat_down ? 1.0f : 0.0f); + last_hat_down = hat_down; + } + + return 1; + } + } + else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { + int32_t code = AKeyEvent_getKeyCode(event); + + if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) { + int shift = AKeyEvent_getMetaState(event) & AMETA_SHIFT_ON; + if (shift) { + switch (code) { + case AKEYCODE_1: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_EXCLAMATION); + kinc_internal_keyboard_trigger_key_press('!'); + return 1; + case AKEYCODE_4: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_DOLLAR); + kinc_internal_keyboard_trigger_key_press('$'); + return 1; + case AKEYCODE_5: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_PERCENT); + kinc_internal_keyboard_trigger_key_press('%'); + return 1; + case AKEYCODE_6: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_CIRCUMFLEX); + kinc_internal_keyboard_trigger_key_press('^'); + return 1; + case AKEYCODE_7: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_AMPERSAND); + kinc_internal_keyboard_trigger_key_press('&'); + return 1; + case AKEYCODE_9: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_OPEN_PAREN); + kinc_internal_keyboard_trigger_key_press('('); + return 1; + case AKEYCODE_0: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_CLOSE_PAREN); + kinc_internal_keyboard_trigger_key_press(')'); + return 1; + case AKEYCODE_COMMA: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_LESS_THAN); + kinc_internal_keyboard_trigger_key_press('<'); + return 1; + case AKEYCODE_PERIOD: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_GREATER_THAN); + kinc_internal_keyboard_trigger_key_press('>'); + return 1; + case AKEYCODE_MINUS: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_UNDERSCORE); + kinc_internal_keyboard_trigger_key_press('_'); + return 1; + case AKEYCODE_SLASH: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_QUESTIONMARK); + kinc_internal_keyboard_trigger_key_press('?'); + return 1; + case AKEYCODE_BACKSLASH: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_PIPE); + kinc_internal_keyboard_trigger_key_press('|'); + return 1; + case AKEYCODE_LEFT_BRACKET: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_OPEN_CURLY_BRACKET); + kinc_internal_keyboard_trigger_key_press('{'); + return 1; + case AKEYCODE_RIGHT_BRACKET: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_CLOSE_CURLY_BRACKET); + kinc_internal_keyboard_trigger_key_press('}'); + return 1; + case AKEYCODE_SEMICOLON: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_COLON); + kinc_internal_keyboard_trigger_key_press(':'); + return 1; + case AKEYCODE_APOSTROPHE: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_DOUBLE_QUOTE); + kinc_internal_keyboard_trigger_key_press('"'); + return 1; + case AKEYCODE_GRAVE: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_TILDE); + kinc_internal_keyboard_trigger_key_press('~'); + return 1; + } + } + switch (code) { + case AKEYCODE_SHIFT_LEFT: + case AKEYCODE_SHIFT_RIGHT: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_SHIFT); + return 1; + case AKEYCODE_DEL: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_BACKSPACE); + return 1; + case AKEYCODE_ENTER: + case AKEYCODE_NUMPAD_ENTER: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_RETURN); + return 1; + case AKEYCODE_DPAD_CENTER: + case AKEYCODE_BUTTON_B: + kinc_internal_gamepad_trigger_button(0, 1, 1); + return 1; + case AKEYCODE_BACK: + if (AKeyEvent_getMetaState(event) & AMETA_ALT_ON) { // Xperia Play + kinc_internal_gamepad_trigger_button(0, 1, 1); + return 1; + } + else { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_BACK); + return 1; + } + case AKEYCODE_BUTTON_A: + kinc_internal_gamepad_trigger_button(0, 0, 1); + return 1; + case AKEYCODE_BUTTON_Y: + kinc_internal_gamepad_trigger_button(0, 3, 1); + return 1; + case AKEYCODE_BUTTON_X: + kinc_internal_gamepad_trigger_button(0, 2, 1); + return 1; + case AKEYCODE_BUTTON_L1: + kinc_internal_gamepad_trigger_button(0, 4, 1); + return 1; + case AKEYCODE_BUTTON_R1: + kinc_internal_gamepad_trigger_button(0, 5, 1); + return 1; + case AKEYCODE_BUTTON_L2: + kinc_internal_gamepad_trigger_button(0, 6, 1); + return 1; + case AKEYCODE_BUTTON_R2: + kinc_internal_gamepad_trigger_button(0, 7, 1); + return 1; + case AKEYCODE_BUTTON_SELECT: + kinc_internal_gamepad_trigger_button(0, 8, 1); + return 1; + case AKEYCODE_BUTTON_START: + kinc_internal_gamepad_trigger_button(0, 9, 1); + return 1; + case AKEYCODE_BUTTON_THUMBL: + kinc_internal_gamepad_trigger_button(0, 10, 1); + return 1; + case AKEYCODE_BUTTON_THUMBR: + kinc_internal_gamepad_trigger_button(0, 11, 1); + return 1; + case AKEYCODE_DPAD_UP: + if (isGamepadEvent(event)) + kinc_internal_gamepad_trigger_button(0, 12, 1); + else + kinc_internal_keyboard_trigger_key_down(KINC_KEY_UP); + return 1; + case AKEYCODE_DPAD_DOWN: + if (isGamepadEvent(event)) + kinc_internal_gamepad_trigger_button(0, 13, 1); + else + kinc_internal_keyboard_trigger_key_down(KINC_KEY_DOWN); + return 1; + case AKEYCODE_DPAD_LEFT: + if (isGamepadEvent(event)) + kinc_internal_gamepad_trigger_button(0, 14, 1); + else + kinc_internal_keyboard_trigger_key_down(KINC_KEY_LEFT); + return 1; + case AKEYCODE_DPAD_RIGHT: + if (isGamepadEvent(event)) + kinc_internal_gamepad_trigger_button(0, 15, 1); + else + kinc_internal_keyboard_trigger_key_down(KINC_KEY_RIGHT); + return 1; + case AKEYCODE_BUTTON_MODE: + kinc_internal_gamepad_trigger_button(0, 16, 1); + return 1; + case AKEYCODE_STAR: + case AKEYCODE_NUMPAD_MULTIPLY: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_MULTIPLY); + kinc_internal_keyboard_trigger_key_press('*'); + return 1; + case AKEYCODE_POUND: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_HASH); + kinc_internal_keyboard_trigger_key_press('#'); + return 1; + case AKEYCODE_COMMA: + case AKEYCODE_NUMPAD_COMMA: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_COMMA); + kinc_internal_keyboard_trigger_key_press(','); + return 1; + case AKEYCODE_PERIOD: + case AKEYCODE_NUMPAD_DOT: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_PERIOD); + kinc_internal_keyboard_trigger_key_press('.'); + return 1; + case AKEYCODE_SPACE: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_SPACE); + kinc_internal_keyboard_trigger_key_press(' '); + return 1; + case AKEYCODE_MINUS: + case AKEYCODE_NUMPAD_SUBTRACT: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_HYPHEN_MINUS); + kinc_internal_keyboard_trigger_key_press('-'); + return 1; + case AKEYCODE_EQUALS: + case AKEYCODE_NUMPAD_EQUALS: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_EQUALS); + kinc_internal_keyboard_trigger_key_press('='); + return 1; + case AKEYCODE_LEFT_BRACKET: + case AKEYCODE_NUMPAD_LEFT_PAREN: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_OPEN_BRACKET); + kinc_internal_keyboard_trigger_key_press('['); + return 1; + case AKEYCODE_RIGHT_BRACKET: + case AKEYCODE_NUMPAD_RIGHT_PAREN: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_CLOSE_BRACKET); + kinc_internal_keyboard_trigger_key_press(']'); + return 1; + case AKEYCODE_BACKSLASH: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_BACK_SLASH); + kinc_internal_keyboard_trigger_key_press('\\'); + return 1; + case AKEYCODE_SEMICOLON: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_SEMICOLON); + kinc_internal_keyboard_trigger_key_press(';'); + return 1; + case AKEYCODE_APOSTROPHE: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_QUOTE); + kinc_internal_keyboard_trigger_key_press('\''); + return 1; + case AKEYCODE_GRAVE: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_BACK_QUOTE); + kinc_internal_keyboard_trigger_key_press('`'); + return 1; + case AKEYCODE_SLASH: + case AKEYCODE_NUMPAD_DIVIDE: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_SLASH); + kinc_internal_keyboard_trigger_key_press('/'); + return 1; + case AKEYCODE_AT: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_AT); + kinc_internal_keyboard_trigger_key_press('@'); + return 1; + case AKEYCODE_PLUS: + case AKEYCODE_NUMPAD_ADD: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_PLUS); + kinc_internal_keyboard_trigger_key_press('+'); + return 1; + // (DK) Amazon FireTV remote/controller mappings + // (DK) TODO handle multiple pads (up to 4 possible) + case AKEYCODE_MENU: + kinc_internal_gamepad_trigger_button(0, 9, 1); + return 1; + case AKEYCODE_MEDIA_REWIND: + kinc_internal_gamepad_trigger_button(0, 10, 1); + return 1; + case AKEYCODE_MEDIA_FAST_FORWARD: + kinc_internal_gamepad_trigger_button(0, 11, 1); + return 1; + case AKEYCODE_MEDIA_PLAY_PAUSE: + kinc_internal_gamepad_trigger_button(0, 12, 1); + return 1; + // (DK) /Amazon FireTV remote/controller mappings + default: + if (code >= AKEYCODE_NUMPAD_0 && code <= AKEYCODE_NUMPAD_9) { + kinc_internal_keyboard_trigger_key_down(code + KINC_KEY_NUMPAD_0 - AKEYCODE_NUMPAD_0); + kinc_internal_keyboard_trigger_key_press(code + KINC_KEY_NUMPAD_0 - AKEYCODE_NUMPAD_0); + return 1; + } + else if (code >= AKEYCODE_0 && code <= AKEYCODE_9) { + kinc_internal_keyboard_trigger_key_down(code + KINC_KEY_0 - AKEYCODE_0); + kinc_internal_keyboard_trigger_key_press(code + KINC_KEY_0 - AKEYCODE_0); + return 1; + } + else if (code >= AKEYCODE_A && code <= AKEYCODE_Z) { + kinc_internal_keyboard_trigger_key_down(code + KINC_KEY_A - AKEYCODE_A); + kinc_internal_keyboard_trigger_key_press(code + (shift ? 'A' : 'a') - AKEYCODE_A); + return 1; + } + } + } + else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_UP) { + int shift = AKeyEvent_getMetaState(event) & AMETA_SHIFT_ON; + if (shift) { + switch (code) { + case AKEYCODE_1: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_EXCLAMATION); + return 1; + case AKEYCODE_4: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_DOLLAR); + return 1; + case AKEYCODE_5: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_PERCENT); + return 1; + case AKEYCODE_6: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_CIRCUMFLEX); + return 1; + case AKEYCODE_7: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_AMPERSAND); + return 1; + case AKEYCODE_9: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_OPEN_PAREN); + return 1; + case AKEYCODE_0: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_CLOSE_PAREN); + return 1; + case AKEYCODE_COMMA: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_LESS_THAN); + return 1; + case AKEYCODE_PERIOD: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_GREATER_THAN); + return 1; + case AKEYCODE_MINUS: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_UNDERSCORE); + return 1; + case AKEYCODE_SLASH: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_QUESTIONMARK); + return 1; + case AKEYCODE_BACKSLASH: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_PIPE); + return 1; + case AKEYCODE_LEFT_BRACKET: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_OPEN_CURLY_BRACKET); + return 1; + case AKEYCODE_RIGHT_BRACKET: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_CLOSE_CURLY_BRACKET); + return 1; + case AKEYCODE_SEMICOLON: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_COLON); + return 1; + case AKEYCODE_APOSTROPHE: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_DOUBLE_QUOTE); + return 1; + case AKEYCODE_GRAVE: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_TILDE); + return 1; + } + } + switch (code) { + case AKEYCODE_SHIFT_LEFT: + case AKEYCODE_SHIFT_RIGHT: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_SHIFT); + return 1; + case AKEYCODE_DEL: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_BACKSPACE); + return 1; + case AKEYCODE_ENTER: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_RETURN); + return 1; + case AKEYCODE_DPAD_CENTER: + case AKEYCODE_BUTTON_B: + kinc_internal_gamepad_trigger_button(0, 1, 0); + return 1; + case AKEYCODE_BACK: + if (AKeyEvent_getMetaState(event) & AMETA_ALT_ON) { // Xperia Play + kinc_internal_gamepad_trigger_button(0, 1, 0); + return 1; + } + else { + kinc_internal_keyboard_trigger_key_up(KINC_KEY_BACK); + return 1; + } + case AKEYCODE_BUTTON_A: + kinc_internal_gamepad_trigger_button(0, 0, 0); + return 1; + case AKEYCODE_BUTTON_Y: + kinc_internal_gamepad_trigger_button(0, 3, 0); + return 1; + case AKEYCODE_BUTTON_X: + kinc_internal_gamepad_trigger_button(0, 2, 0); + return 1; + case AKEYCODE_BUTTON_L1: + kinc_internal_gamepad_trigger_button(0, 4, 0); + return 1; + case AKEYCODE_BUTTON_R1: + kinc_internal_gamepad_trigger_button(0, 5, 0); + return 1; + case AKEYCODE_BUTTON_L2: + kinc_internal_gamepad_trigger_button(0, 6, 0); + return 1; + case AKEYCODE_BUTTON_R2: + kinc_internal_gamepad_trigger_button(0, 7, 0); + return 1; + case AKEYCODE_BUTTON_SELECT: + kinc_internal_gamepad_trigger_button(0, 8, 0); + return 1; + case AKEYCODE_BUTTON_START: + kinc_internal_gamepad_trigger_button(0, 9, 0); + return 1; + case AKEYCODE_BUTTON_THUMBL: + kinc_internal_gamepad_trigger_button(0, 10, 0); + return 1; + case AKEYCODE_BUTTON_THUMBR: + kinc_internal_gamepad_trigger_button(0, 11, 0); + return 1; + case AKEYCODE_DPAD_UP: + if (isGamepadEvent(event)) + kinc_internal_gamepad_trigger_button(0, 12, 0); + else + kinc_internal_keyboard_trigger_key_up(KINC_KEY_UP); + return 1; + case AKEYCODE_DPAD_DOWN: + if (isGamepadEvent(event)) + kinc_internal_gamepad_trigger_button(0, 13, 0); + else + kinc_internal_keyboard_trigger_key_up(KINC_KEY_DOWN); + return 1; + case AKEYCODE_DPAD_LEFT: + if (isGamepadEvent(event)) + kinc_internal_gamepad_trigger_button(0, 14, 0); + else + kinc_internal_keyboard_trigger_key_up(KINC_KEY_LEFT); + return 1; + case AKEYCODE_DPAD_RIGHT: + if (isGamepadEvent(event)) + kinc_internal_gamepad_trigger_button(0, 15, 0); + else + kinc_internal_keyboard_trigger_key_up(KINC_KEY_RIGHT); + return 1; + case AKEYCODE_BUTTON_MODE: + kinc_internal_gamepad_trigger_button(0, 16, 0); + return 1; + case AKEYCODE_STAR: + case AKEYCODE_NUMPAD_MULTIPLY: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_MULTIPLY); + return 1; + case AKEYCODE_POUND: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_HASH); + return 1; + case AKEYCODE_COMMA: + case AKEYCODE_NUMPAD_COMMA: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_COMMA); + return 1; + case AKEYCODE_PERIOD: + case AKEYCODE_NUMPAD_DOT: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_PERIOD); + return 1; + case AKEYCODE_SPACE: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_SPACE); + return 1; + case AKEYCODE_MINUS: + case AKEYCODE_NUMPAD_SUBTRACT: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_HYPHEN_MINUS); + return 1; + case AKEYCODE_EQUALS: + case AKEYCODE_NUMPAD_EQUALS: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_EQUALS); + return 1; + case AKEYCODE_LEFT_BRACKET: + case AKEYCODE_NUMPAD_LEFT_PAREN: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_OPEN_BRACKET); + return 1; + case AKEYCODE_RIGHT_BRACKET: + case AKEYCODE_NUMPAD_RIGHT_PAREN: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_CLOSE_BRACKET); + return 1; + case AKEYCODE_BACKSLASH: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_BACK_SLASH); + return 1; + case AKEYCODE_SEMICOLON: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_SEMICOLON); + return 1; + case AKEYCODE_APOSTROPHE: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_QUOTE); + return 1; + case AKEYCODE_GRAVE: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_BACK_QUOTE); + return 1; + case AKEYCODE_SLASH: + case AKEYCODE_NUMPAD_DIVIDE: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_SLASH); + return 1; + case AKEYCODE_AT: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_AT); + return 1; + case AKEYCODE_PLUS: + case AKEYCODE_NUMPAD_ADD: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_PLUS); + return 1; + // (DK) Amazon FireTV remote/controller mappings + // (DK) TODO handle multiple pads (up to 4 possible) + case AKEYCODE_MENU: + kinc_internal_gamepad_trigger_button(0, 9, 0); + return 1; + case AKEYCODE_MEDIA_REWIND: + kinc_internal_gamepad_trigger_button(0, 10, 0); + return 1; + case AKEYCODE_MEDIA_FAST_FORWARD: + kinc_internal_gamepad_trigger_button(0, 11, 0); + return 1; + case AKEYCODE_MEDIA_PLAY_PAUSE: + kinc_internal_gamepad_trigger_button(0, 12, 0); + return 1; + // (DK) /Amazon FireTV remote/controller mappings + default: + if (code >= AKEYCODE_NUMPAD_0 && code <= AKEYCODE_NUMPAD_9) { + kinc_internal_keyboard_trigger_key_up(code + KINC_KEY_NUMPAD_0 - AKEYCODE_NUMPAD_0); + return 1; + } + else if (code >= AKEYCODE_0 && code <= AKEYCODE_9) { + kinc_internal_keyboard_trigger_key_up(code + KINC_KEY_0 - AKEYCODE_0); + return 1; + } + else if (code >= AKEYCODE_A && code <= AKEYCODE_Z) { + kinc_internal_keyboard_trigger_key_up(code + KINC_KEY_A - AKEYCODE_A); + return 1; + } + } + } + } + return 0; +} + +static void cmd(struct android_app *app, int32_t cmd) { + switch (cmd) { + case APP_CMD_SAVE_STATE: + break; + case APP_CMD_INIT_WINDOW: + if (app->window != NULL) { + if (!started) { + started = true; + } + else { + initDisplay(); + kinc_g4_swap_buffers(); + } + + updateAppForegroundStatus(true, appIsForeground); + } + break; + case APP_CMD_TERM_WINDOW: + termDisplay(); + updateAppForegroundStatus(false, appIsForeground); + break; + case APP_CMD_GAINED_FOCUS: + if (accelerometerSensor != NULL) { + ASensorEventQueue_enableSensor(sensorEventQueue, accelerometerSensor); + ASensorEventQueue_setEventRate(sensorEventQueue, accelerometerSensor, (1000L / 60) * 1000); + } + if (gyroSensor != NULL) { + ASensorEventQueue_enableSensor(sensorEventQueue, gyroSensor); + ASensorEventQueue_setEventRate(sensorEventQueue, gyroSensor, (1000L / 60) * 1000); + } + break; + case APP_CMD_LOST_FOCUS: + if (accelerometerSensor != NULL) { + ASensorEventQueue_disableSensor(sensorEventQueue, accelerometerSensor); + } + if (gyroSensor != NULL) { + ASensorEventQueue_disableSensor(sensorEventQueue, gyroSensor); + } + break; + case APP_CMD_START: + updateAppForegroundStatus(displayIsInitialized, true); + break; + case APP_CMD_RESUME: + kinc_internal_resume_callback(); + resumeAudio(); + paused = false; + break; + case APP_CMD_PAUSE: + kinc_internal_pause_callback(); + pauseAudio(); + paused = true; + break; + case APP_CMD_STOP: + updateAppForegroundStatus(displayIsInitialized, false); + break; + case APP_CMD_DESTROY: + kinc_internal_shutdown_callback(); + break; + case APP_CMD_CONFIG_CHANGED: { + + break; + } + } +} + +static void resize(ANativeActivity *activity, ANativeWindow *window) { + activityJustResized = true; +} + +ANativeActivity *kinc_android_get_activity(void) { + return activity; +} + +AAssetManager *kinc_android_get_asset_manager(void) { + return activity->assetManager; +} + +jclass kinc_android_find_class(JNIEnv *env, const char *name) { + jobject nativeActivity = activity->clazz; + jclass acl = (*env)->GetObjectClass(env, nativeActivity); + jmethodID getClassLoader = (*env)->GetMethodID(env, acl, "getClassLoader", "()Ljava/lang/ClassLoader;"); + jobject cls = (*env)->CallObjectMethod(env, nativeActivity, getClassLoader); + jclass classLoader = (*env)->FindClass(env, "java/lang/ClassLoader"); + jmethodID findClass = (*env)->GetMethodID(env, classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + jstring strClassName = (*env)->NewStringUTF(env, name); + jclass clazz = (jclass)((*env)->CallObjectMethod(env, cls, findClass, strClassName)); + (*env)->DeleteLocalRef(env, strClassName); + return clazz; +} + +#define UNICODE_STACK_SIZE 256 +static uint16_t unicode_stack[UNICODE_STACK_SIZE]; +static int unicode_stack_index = 0; +static kinc_mutex_t unicode_mutex; + +JNIEXPORT void JNICALL Java_tech_kinc_KincActivity_nativeKincKeyPress(JNIEnv *env, jobject jobj, jstring chars) { + const jchar *text = (*env)->GetStringChars(env, chars, NULL); + const jsize length = (*env)->GetStringLength(env, chars); + + kinc_mutex_lock(&unicode_mutex); + for (jsize i = 0; i < length && unicode_stack_index < UNICODE_STACK_SIZE; ++i) { + unicode_stack[unicode_stack_index++] = text[i]; + } + kinc_mutex_unlock(&unicode_mutex); + + (*env)->ReleaseStringChars(env, chars, text); +} + +void KincAndroidKeyboardInit() { + JNIEnv *env; + (*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL); + + jclass clazz = kinc_android_find_class(env, "tech.kinc.KincActivity"); + + // String chars + JNINativeMethod methodTable[] = {{"nativeKincKeyPress", "(Ljava/lang/String;)V", (void *)Java_tech_kinc_KincActivity_nativeKincKeyPress}}; + + int methodTableSize = sizeof(methodTable) / sizeof(methodTable[0]); + + int failure = (*env)->RegisterNatives(env, clazz, methodTable, methodTableSize); + if (failure != 0) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Failed to register KincActivity.nativeKincKeyPress"); + } + + (*activity->vm)->DetachCurrentThread(activity->vm); +} + +static bool keyboard_active = false; + +void kinc_keyboard_show() { + keyboard_active = true; + JNIEnv *env; + (*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL); + jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity"); + (*env)->CallStaticVoidMethod(env, koreActivityClass, (*env)->GetStaticMethodID(env, koreActivityClass, "showKeyboard", "()V")); + (*activity->vm)->DetachCurrentThread(activity->vm); +} + +void kinc_keyboard_hide() { + keyboard_active = false; + JNIEnv *env; + (*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL); + jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity"); + (*env)->CallStaticVoidMethod(env, koreActivityClass, (*env)->GetStaticMethodID(env, koreActivityClass, "hideKeyboard", "()V")); + (*activity->vm)->DetachCurrentThread(activity->vm); +} + +bool kinc_keyboard_active() { + return keyboard_active; +} + +void kinc_load_url(const char *url) { + JNIEnv *env; + (*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL); + jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity"); + jstring jurl = (*env)->NewStringUTF(env, url); + (*env)->CallStaticVoidMethod(env, koreActivityClass, (*env)->GetStaticMethodID(env, koreActivityClass, "loadURL", "(Ljava/lang/String;)V"), jurl); + (*activity->vm)->DetachCurrentThread(activity->vm); +} + +void kinc_vibrate(int ms) { + JNIEnv *env; + (*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL); + jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity"); + (*env)->CallStaticVoidMethod(env, koreActivityClass, (*env)->GetStaticMethodID(env, koreActivityClass, "vibrate", "(I)V"), ms); + (*activity->vm)->DetachCurrentThread(activity->vm); +} + +const char *kinc_language() { + JNIEnv *env; + (*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL); + jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity"); + jstring s = (jstring)(*env)->CallStaticObjectMethod(env, koreActivityClass, + (*env)->GetStaticMethodID(env, koreActivityClass, "getLanguage", "()Ljava/lang/String;")); + const char *str = (*env)->GetStringUTFChars(env, s, 0); + (*activity->vm)->DetachCurrentThread(activity->vm); + return str; +} + +#ifdef KINC_VULKAN +bool kinc_vulkan_internal_get_size(int *width, int *height); +#endif + +#ifdef KINC_EGL +extern int kinc_egl_width(int window); +extern int kinc_egl_height(int window); +#endif + +int kinc_android_width() { +#if defined(KINC_EGL) + return kinc_egl_width(0); +#elif defined(KINC_VULKAN) + int width, height; + if (kinc_vulkan_internal_get_size(&width, &height)) { + return width; + } + else +#endif + { return ANativeWindow_getWidth(app->window); } +} + +int kinc_android_height() { +#if defined(KINC_EGL) + return kinc_egl_height(0); +#elif defined(KINC_VULKAN) + int width, height; + if (kinc_vulkan_internal_get_size(&width, &height)) { + return height; + } + else +#endif + { return ANativeWindow_getHeight(app->window); } +} + +const char *kinc_internal_save_path() { + return kinc_android_get_activity()->internalDataPath; +} + +const char *kinc_system_id() { + return "Android"; +} + +static const char *videoFormats[] = {"ts", NULL}; + +const char **kinc_video_formats() { + return videoFormats; +} + +void kinc_set_keep_screen_on(bool on) { + if (on) { + ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0); + } + else { + ANativeActivity_setWindowFlags(activity, 0, AWINDOW_FLAG_KEEP_SCREEN_ON); + } +} + +#include +#include +#include +#include +#include + +static __kernel_time_t start_sec = 0; + +double kinc_frequency() { + return 1000000.0; +} + +kinc_ticks_t kinc_timestamp() { + struct timeval now; + gettimeofday(&now, NULL); + return (kinc_ticks_t)(now.tv_sec - start_sec) * 1000000 + (kinc_ticks_t)(now.tv_usec); +} + +double kinc_time() { + struct timeval now; + gettimeofday(&now, NULL); + return (double)(now.tv_sec - start_sec) + (now.tv_usec / 1000000.0); +} + +void kinc_internal_resize(int window, int width, int height); + +bool kinc_internal_handle_messages(void) { + kinc_mutex_lock(&unicode_mutex); + for (int i = 0; i < unicode_stack_index; ++i) { + kinc_internal_keyboard_trigger_key_press(unicode_stack[i]); + } + unicode_stack_index = 0; + kinc_mutex_unlock(&unicode_mutex); + + int ident; + int events; + struct android_poll_source *source; + + while ((ident = ALooper_pollAll(paused ? -1 : 0, NULL, &events, (void **)&source)) >= 0) { + if (source != NULL) { + source->process(app, source); + } + + if (ident == LOOPER_ID_USER) { + if (accelerometerSensor != NULL) { + ASensorEvent event; + while (ASensorEventQueue_getEvents(sensorEventQueue, &event, 1) > 0) { + if (event.type == ASENSOR_TYPE_ACCELEROMETER) { + kinc_internal_on_acceleration(event.acceleration.x, event.acceleration.y, event.acceleration.z); + } + else if (event.type == ASENSOR_TYPE_GYROSCOPE) { + kinc_internal_on_rotation(event.vector.x, event.vector.y, event.vector.z); + } + } + } + } + + if (app->destroyRequested != 0) { + termDisplay(); + kinc_stop(); + return true; + } + } + + if (activityJustResized && app->window != NULL) { + activityJustResized = false; + int32_t width = kinc_android_width(); + int32_t height = kinc_android_height(); +#ifdef KINC_VULKAN + kinc_internal_resize(0, width, height); +#endif + kinc_internal_call_resize_callback(0, width, height); + } + + // Get screen rotation + /* + JNIEnv* env; + (*activity->vm)->AttachCurrentThread(&env, NULL); + jclass koreActivityClass = KoreAndroid::findClass(env, "tech.kode.kore.KoreActivity"); + jmethodID koreActivityGetRotation = (*env)->GetStaticMethodID(koreActivityClass, "getRotation", "()I"); + screenRotation = (*env)->CallStaticIntMethod(koreActivityClass, koreActivityGetRotation); + (*activity->vm)->DetachCurrentThread(); + */ + + return true; +} + +bool kinc_mouse_can_lock(void) { + return false; +} + +void kinc_mouse_show() {} + +void kinc_mouse_hide() {} + +void kinc_mouse_set_position(int window, int x, int y) {} + +void kinc_internal_mouse_lock(int window) {} + +void kinc_internal_mouse_unlock(void) {} + +void kinc_mouse_get_position(int window, int *x, int *y) { + x = 0; + y = 0; +} + +void kinc_mouse_set_cursor(int cursor_index) {} + +void kinc_login() {} + +void kinc_unlock_achievement(int id) {} + +bool kinc_gamepad_connected(int num) { + return num == 0; +} + +void kinc_gamepad_rumble(int gamepad, float left, float right) {} + +void initAndroidFileReader(); +void KoreAndroidVideoInit(); + +void android_main(struct android_app *application) { + app_dummy(); + + struct timeval now; + gettimeofday(&now, NULL); + start_sec = now.tv_sec; + + app = application; + activity = application->activity; + initAndroidFileReader(); + KoreAndroidVideoInit(); + KincAndroidKeyboardInit(); + application->onAppCmd = cmd; + application->onInputEvent = input; + activity->callbacks->onNativeWindowResized = resize; + // #ifndef KINC_VULKAN + // glContext = ndk_helper::GLContext::GetInstance(); + // #endif + sensorManager = ASensorManager_getInstance(); + accelerometerSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_ACCELEROMETER); + gyroSensor = ASensorManager_getDefaultSensor(sensorManager, ASENSOR_TYPE_GYROSCOPE); + sensorEventQueue = ASensorManager_createEventQueue(sensorManager, application->looper, LOOPER_ID_USER, NULL, NULL); + + JNIEnv *env = NULL; + (*kinc_android_get_activity()->vm)->AttachCurrentThread(kinc_android_get_activity()->vm, &env, NULL); + + jclass koreMoviePlayerClass = kinc_android_find_class(env, "tech.kinc.KincMoviePlayer"); + jmethodID updateAll = (*env)->GetStaticMethodID(env, koreMoviePlayerClass, "updateAll", "()V"); + + while (!started) { + kinc_internal_handle_messages(); + (*env)->CallStaticVoidMethod(env, koreMoviePlayerClass, updateAll); + } + (*kinc_android_get_activity()->vm)->DetachCurrentThread(kinc_android_get_activity()->vm); + kickstart(0, NULL); + + (*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL); + jclass koreActivityClass = kinc_android_find_class(env, "tech.kinc.KincActivity"); + jmethodID FinishHim = (*env)->GetStaticMethodID(env, koreActivityClass, "stop", "()V"); + (*env)->CallStaticVoidMethod(env, koreActivityClass, FinishHim); + (*activity->vm)->DetachCurrentThread(activity->vm); +} + +int kinc_init(const char *name, int width, int height, struct kinc_window_options *win, struct kinc_framebuffer_options *frame) { + kinc_mutex_init(&unicode_mutex); + + kinc_window_options_t default_win; + if (win == NULL) { + kinc_window_options_set_defaults(&default_win); + win = &default_win; + } + win->width = width; + win->height = height; + + kinc_framebuffer_options_t default_frame; + if (frame == NULL) { + kinc_framebuffer_options_set_defaults(&default_frame); + frame = &default_frame; + } + + kinc_g4_internal_init(); + kinc_g4_internal_init_window(0, frame->depth_bits, frame->stencil_bits, true); + + kinc_internal_gamepad_trigger_connect(0); + + return 0; +} + +void kinc_internal_shutdown(void) { + kinc_internal_gamepad_trigger_disconnect(0); +} + +const char *kinc_gamepad_vendor(int gamepad) { + return "Google"; +} + +const char *kinc_gamepad_product_name(int gamepad) { + return "gamepad"; +} + +#include + +#define CLASS_NAME "android/app/NativeActivity" + +void initAndroidFileReader(void) { + if (activity == NULL) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Android activity is NULL"); + return; + } + + JNIEnv *env; + + (*activity->vm)->AttachCurrentThread(activity->vm, &env, NULL); + + jclass android_app_NativeActivity = (*env)->FindClass(env, CLASS_NAME); + jmethodID getExternalFilesDir = (*env)->GetMethodID(env, android_app_NativeActivity, "getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;"); + jobject file = (*env)->CallObjectMethod(env, activity->clazz, getExternalFilesDir, NULL); + jclass java_io_File = (*env)->FindClass(env, "java/io/File"); + jmethodID getPath = (*env)->GetMethodID(env, java_io_File, "getPath", "()Ljava/lang/String;"); + jstring jPath = (*env)->CallObjectMethod(env, file, getPath); + + const char *path = (*env)->GetStringUTFChars(env, jPath, NULL); + char *externalFilesDir = malloc(strlen(path) + 1); + strcpy(externalFilesDir, path); + kinc_internal_set_files_location(externalFilesDir); + + (*env)->ReleaseStringUTFChars(env, jPath, path); + (*env)->DeleteLocalRef(env, jPath); + (*activity->vm)->DetachCurrentThread(activity->vm); +} + +static bool kinc_aasset_reader_close(kinc_file_reader_t *reader) { + AAsset_close((struct AAsset *)reader->data); + return true; +} + +static size_t kinc_aasset_reader_read(kinc_file_reader_t *reader, void *data, size_t size) { + return AAsset_read((struct AAsset *)reader->data, data, size); +} + +static size_t kinc_aasset_reader_pos(kinc_file_reader_t *reader) { + return (size_t)AAsset_seek((struct AAsset *)reader->data, 0, SEEK_CUR); +} + +static bool kinc_aasset_reader_seek(kinc_file_reader_t *reader, size_t pos) { + AAsset_seek((struct AAsset *)reader->data, pos, SEEK_SET); + return true; +} + +static bool kinc_aasset_reader_open(kinc_file_reader_t *reader, const char *filename, int type) { + if (type != KINC_FILE_TYPE_ASSET) + return false; + reader->data = AAssetManager_open(kinc_android_get_asset_manager(), filename, AASSET_MODE_RANDOM); + if (reader->data == NULL) + return false; + reader->size = AAsset_getLength((struct AAsset *)reader->data); + reader->close = kinc_aasset_reader_close; + reader->read = kinc_aasset_reader_read; + reader->pos = kinc_aasset_reader_pos; + reader->seek = kinc_aasset_reader_seek; + return true; +} + +bool kinc_file_reader_open(kinc_file_reader_t *reader, const char *filename, int type) { + memset(reader, 0, sizeof(*reader)); + return kinc_internal_file_reader_callback(reader, filename, type) || + kinc_internal_file_reader_open(reader, filename, type) || + kinc_aasset_reader_open(reader, filename, type); +} + +int kinc_cpu_cores(void) { + return kinc_hardware_threads(); +} + +int kinc_hardware_threads(void) { + return sysconf(_SC_NPROCESSORS_ONLN); +} diff --git a/armorcore/sources/backends/android/kinc/backend/video.c.h b/armorcore/sources/backends/android/kinc/backend/video.c.h new file mode 100644 index 000000000..08931db35 --- /dev/null +++ b/armorcore/sources/backends/android/kinc/backend/video.c.h @@ -0,0 +1,581 @@ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN) +#include +#include +#endif +#include +#include +#include +#include +#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN) +#include +#include +#include +#endif + +void kinc_video_sound_stream_impl_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency) { + stream->bufferSize = 1; + stream->bufferReadPosition = 0; + stream->bufferWritePosition = 0; + stream->read = 0; + stream->written = 0; +} + +void kinc_video_sound_stream_impl_destroy(kinc_internal_video_sound_stream_t *stream) {} + +void kinc_video_sound_stream_impl_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count) {} + +static float samples[2] = {0}; + +float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream) { + return samples; +} + +bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream) { + return false; +} + +#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN) + +#define videosCount 10 +static kinc_video_t *videos[videosCount] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + +#define NB_MAXAL_INTERFACES 3 // XAAndroidBufferQueueItf, XAStreamInformationItf and XAPlayItf +#define NB_BUFFERS 8 +#define MPEG2_TS_PACKET_SIZE 188 +#define PACKETS_PER_BUFFER 10 +#define BUFFER_SIZE (PACKETS_PER_BUFFER * MPEG2_TS_PACKET_SIZE) +static const int kEosBufferCntxt = 1980; // a magic value we can compare against + +typedef struct kinc_android_video { + XAObjectItf engineObject; + XAEngineItf engineEngine; + XAObjectItf outputMixObject; + const char *path; + + AAsset *file; + XAObjectItf playerObj; + XAPlayItf playerPlayItf; + XAAndroidBufferQueueItf playerBQItf; + XAStreamInformationItf playerStreamInfoItf; + XAVolumeItf playerVolItf; + char dataCache[BUFFER_SIZE * NB_BUFFERS]; + ANativeWindow *theNativeWindow; + jboolean reachedEof; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool discontinuity; +} kinc_android_video_t; + +void kinc_android_video_init(kinc_android_video_t *video) { + video->engineObject = NULL; + video->engineEngine = NULL; + video->outputMixObject = NULL; + video->file = NULL; + video->playerObj = NULL; + video->playerPlayItf = NULL; + video->playerBQItf = NULL; + video->playerStreamInfoItf = NULL; + video->playerVolItf = NULL; + video->theNativeWindow = NULL; + video->reachedEof = JNI_FALSE; + memset(&video->mutex, 0, sizeof(video->mutex)); // mutex = PTHREAD_MUTEX_INITIALIZER; // simple assign stopped working in Android Studio 2.2 + memset(&video->cond, 0, sizeof(video->cond)); // cond = PTHREAD_COND_INITIALIZER; // simple assign stopped working in Android Studio 2.2 + video->discontinuity = false; +} + +bool kinc_android_video_enqueue_initial_buffers(kinc_android_video_t *video, bool discontinuity) { + // Fill our cache. + // We want to read whole packets (integral multiples of MPEG2_TS_PACKET_SIZE). + // fread returns units of "elements" not bytes, so we ask for 1-byte elements + // and then check that the number of elements is a multiple of the packet size. + // + size_t bytesRead; + // bytesRead = fread(dataCache, 1, BUFFER_SIZE * NB_BUFFERS, file); + bytesRead = AAsset_read(video->file, video->dataCache, BUFFER_SIZE * NB_BUFFERS); + if (bytesRead <= 0) { + // could be premature EOF or I/O error + return false; + } + if ((bytesRead % MPEG2_TS_PACKET_SIZE) != 0) { + kinc_log(KINC_LOG_LEVEL_INFO, "Dropping last packet because it is not whole"); + } + size_t packetsRead = bytesRead / MPEG2_TS_PACKET_SIZE; + kinc_log(KINC_LOG_LEVEL_INFO, "Initially queueing %zu packets", packetsRead); + + // Enqueue the content of our cache before starting to play, + // we don't want to starve the player + size_t i; + for (i = 0; i < NB_BUFFERS && packetsRead > 0; i++) { + // compute size of this buffer + size_t packetsThisBuffer = packetsRead; + if (packetsThisBuffer > PACKETS_PER_BUFFER) { + packetsThisBuffer = PACKETS_PER_BUFFER; + } + size_t bufferSize = packetsThisBuffer * MPEG2_TS_PACKET_SIZE; + XAresult res; + if (discontinuity) { + // signal discontinuity + XAAndroidBufferItem items[1]; + items[0].itemKey = XA_ANDROID_ITEMKEY_DISCONTINUITY; + items[0].itemSize = 0; + // DISCONTINUITY message has no parameters, + // so the total size of the message is the size of the key + // plus the size if itemSize, both XAuint32 + res = (*video->playerBQItf) + ->Enqueue(video->playerBQItf, NULL /*pBufferContext*/, video->dataCache + i * BUFFER_SIZE, bufferSize, items /*pMsg*/, + sizeof(XAuint32) * 2 /*msgLength*/); + discontinuity = JNI_FALSE; + } + else { + res = (*video->playerBQItf)->Enqueue(video->playerBQItf, NULL /*pBufferContext*/, video->dataCache + i * BUFFER_SIZE, bufferSize, NULL, 0); + } + assert(XA_RESULT_SUCCESS == res); + packetsRead -= packetsThisBuffer; + } + + return true; +} + +static XAresult AndroidBufferQueueCallback(XAAndroidBufferQueueItf caller, void *pCallbackContext, /* input */ + void *pBufferContext, /* input */ + void *pBufferData, /* input */ + XAuint32 dataSize, /* input */ + XAuint32 dataUsed, /* input */ + const XAAndroidBufferItem *pItems, /* input */ + XAuint32 itemsLength /* input */) { + kinc_android_video_t *self = (kinc_android_video_t *)pCallbackContext; + XAresult res; + int ok; + + // pCallbackContext was specified as NULL at RegisterCallback and is unused here + // assert(NULL == pCallbackContext); + + // note there is never any contention on this mutex unless a discontinuity request is active + ok = pthread_mutex_lock(&self->mutex); + assert(0 == ok); + + // was a discontinuity requested? + if (self->discontinuity) { + // Note: can't rewind after EOS, which we send when reaching EOF + // (don't send EOS if you plan to play more content through the same player) + if (!self->reachedEof) { + // clear the buffer queue + res = (*self->playerBQItf)->Clear(self->playerBQItf); + assert(XA_RESULT_SUCCESS == res); + // rewind the data source so we are guaranteed to be at an appropriate point + // rewind(file); + AAsset_seek(self->file, 0, SEEK_SET); + // Enqueue the initial buffers, with a discontinuity indicator on first buffer + kinc_android_video_enqueue_initial_buffers(self, JNI_TRUE); + } + // acknowledge the discontinuity request + self->discontinuity = JNI_FALSE; + ok = pthread_cond_signal(&self->cond); + assert(0 == ok); + goto exit; + } + + if ((pBufferData == NULL) && (pBufferContext != NULL)) { + const int processedCommand = *(int *)pBufferContext; + if (kEosBufferCntxt == processedCommand) { + kinc_log(KINC_LOG_LEVEL_INFO, "EOS was processed"); + // our buffer with the EOS message has been consumed + assert(0 == dataSize); + goto exit; + } + } + + // pBufferData is a pointer to a buffer that we previously Enqueued + assert((dataSize > 0) && ((dataSize % MPEG2_TS_PACKET_SIZE) == 0)); + assert(self->dataCache <= (char *)pBufferData && (char *)pBufferData < &self->dataCache[BUFFER_SIZE * NB_BUFFERS]); + assert(0 == (((char *)pBufferData - self->dataCache) % BUFFER_SIZE)); + + // don't bother trying to read more data once we've hit EOF + if (self->reachedEof) { + goto exit; + } + + size_t nbRead; + // note we do call fread from multiple threads, but never concurrently + size_t bytesRead; + // bytesRead = fread(pBufferData, 1, BUFFER_SIZE, file); + bytesRead = AAsset_read(self->file, pBufferData, BUFFER_SIZE); + if (bytesRead > 0) { + if ((bytesRead % MPEG2_TS_PACKET_SIZE) != 0) { + kinc_log(KINC_LOG_LEVEL_INFO, "Dropping last packet because it is not whole"); + } + size_t packetsRead = bytesRead / MPEG2_TS_PACKET_SIZE; + size_t bufferSize = packetsRead * MPEG2_TS_PACKET_SIZE; + res = (*caller)->Enqueue(caller, NULL /*pBufferContext*/, pBufferData /*pData*/, bufferSize /*dataLength*/, NULL /*pMsg*/, 0 /*msgLength*/); + assert(XA_RESULT_SUCCESS == res); + } + else { + // EOF or I/O error, signal EOS + XAAndroidBufferItem msgEos[1]; + msgEos[0].itemKey = XA_ANDROID_ITEMKEY_EOS; + msgEos[0].itemSize = 0; + // EOS message has no parameters, so the total size of the message is the size of the key + // plus the size if itemSize, both XAuint32 + res = (*caller)->Enqueue(caller, (void *)&kEosBufferCntxt /*pBufferContext*/, NULL /*pData*/, 0 /*dataLength*/, msgEos /*pMsg*/, + sizeof(XAuint32) * 2 /*msgLength*/); + assert(XA_RESULT_SUCCESS == res); + self->reachedEof = JNI_TRUE; + } + +exit: + ok = pthread_mutex_unlock(&self->mutex); + assert(0 == ok); + return XA_RESULT_SUCCESS; +} + +static void StreamChangeCallback(XAStreamInformationItf caller, XAuint32 eventId, XAuint32 streamIndex, void *pEventData, void *pContext) { + kinc_log(KINC_LOG_LEVEL_INFO, "StreamChangeCallback called for stream %u", streamIndex); + kinc_android_video_t *self = (kinc_android_video_t *)pContext; + // pContext was specified as NULL at RegisterStreamChangeCallback and is unused here + // assert(NULL == pContext); + switch (eventId) { + case XA_STREAMCBEVENT_PROPERTYCHANGE: { + // From spec 1.0.1: + // "This event indicates that stream property change has occurred. + // The streamIndex parameter identifies the stream with the property change. + // The pEventData parameter for this event is not used and shall be ignored." + // + + XAresult res; + XAuint32 domain; + res = (*caller)->QueryStreamType(caller, streamIndex, &domain); + assert(XA_RESULT_SUCCESS == res); + switch (domain) { + case XA_DOMAINTYPE_VIDEO: { + XAVideoStreamInformation videoInfo; + res = (*caller)->QueryStreamInformation(caller, streamIndex, &videoInfo); + assert(XA_RESULT_SUCCESS == res); + kinc_log(KINC_LOG_LEVEL_INFO, "Found video size %u x %u, codec ID=%u, frameRate=%u, bitRate=%u, duration=%u ms", videoInfo.width, videoInfo.height, + videoInfo.codecId, videoInfo.frameRate, videoInfo.bitRate, videoInfo.duration); + } break; + default: + kinc_log(KINC_LOG_LEVEL_ERROR, "Unexpected domain %u\n", domain); + break; + } + } break; + default: + kinc_log(KINC_LOG_LEVEL_ERROR, "Unexpected stream event ID %u\n", eventId); + break; + } +} + +bool kinc_android_video_open(kinc_android_video_t *video, const char *filename) { + XAresult res; + + // create engine + res = xaCreateEngine(&video->engineObject, 0, NULL, 0, NULL, NULL); + assert(XA_RESULT_SUCCESS == res); + + // realize the engine + res = (*video->engineObject)->Realize(video->engineObject, XA_BOOLEAN_FALSE); + assert(XA_RESULT_SUCCESS == res); + + // get the engine interface, which is needed in order to create other objects + res = (*video->engineObject)->GetInterface(video->engineObject, XA_IID_ENGINE, &video->engineEngine); + assert(XA_RESULT_SUCCESS == res); + + // create output mix + res = (*video->engineEngine)->CreateOutputMix(video->engineEngine, &video->outputMixObject, 0, NULL, NULL); + assert(XA_RESULT_SUCCESS == res); + + // realize the output mix + res = (*video->outputMixObject)->Realize(video->outputMixObject, XA_BOOLEAN_FALSE); + assert(XA_RESULT_SUCCESS == res); + + // open the file to play + video->file = AAssetManager_open(kinc_android_get_asset_manager(), filename, AASSET_MODE_STREAMING); + if (video->file == NULL) { + kinc_log(KINC_LOG_LEVEL_INFO, "Could not find video file."); + return false; + } + + // configure data source + XADataLocator_AndroidBufferQueue loc_abq = {XA_DATALOCATOR_ANDROIDBUFFERQUEUE, NB_BUFFERS}; + XADataFormat_MIME format_mime = {XA_DATAFORMAT_MIME, XA_ANDROID_MIME_MP2TS, XA_CONTAINERTYPE_MPEG_TS}; + XADataSource dataSrc = {&loc_abq, &format_mime}; + + // configure audio sink + XADataLocator_OutputMix loc_outmix = {XA_DATALOCATOR_OUTPUTMIX, video->outputMixObject}; + XADataSink audioSnk = {&loc_outmix, NULL}; + + // configure image video sink + XADataLocator_NativeDisplay loc_nd = { + XA_DATALOCATOR_NATIVEDISPLAY, // locatorType + // the video sink must be an ANativeWindow created from a Surface or SurfaceTexture + (void *)video->theNativeWindow, // hWindow + // must be NULL + NULL // hDisplay + }; + XADataSink imageVideoSink = {&loc_nd, NULL}; + + // declare interfaces to use + XAboolean required[NB_MAXAL_INTERFACES] = {XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE, XA_BOOLEAN_TRUE}; + XAInterfaceID iidArray[NB_MAXAL_INTERFACES] = {XA_IID_PLAY, XA_IID_ANDROIDBUFFERQUEUESOURCE, XA_IID_STREAMINFORMATION}; + + // create media player + res = (*video->engineEngine) + ->CreateMediaPlayer(video->engineEngine, &video->playerObj, &dataSrc, NULL, &audioSnk, &imageVideoSink, NULL, NULL, + NB_MAXAL_INTERFACES /*XAuint32 numInterfaces*/, iidArray /*const XAInterfaceID *pInterfaceIds*/, + required /*const XAboolean *pInterfaceRequired*/); + assert(XA_RESULT_SUCCESS == res); + + // realize the player + res = (*video->playerObj)->Realize(video->playerObj, XA_BOOLEAN_FALSE); + assert(XA_RESULT_SUCCESS == res); + + // get the play interface + res = (*video->playerObj)->GetInterface(video->playerObj, XA_IID_PLAY, &video->playerPlayItf); + assert(XA_RESULT_SUCCESS == res); + + // get the stream information interface (for video size) + res = (*video->playerObj)->GetInterface(video->playerObj, XA_IID_STREAMINFORMATION, &video->playerStreamInfoItf); + assert(XA_RESULT_SUCCESS == res); + + // get the volume interface + res = (*video->playerObj)->GetInterface(video->playerObj, XA_IID_VOLUME, &video->playerVolItf); + assert(XA_RESULT_SUCCESS == res); + + // get the Android buffer queue interface + res = (*video->playerObj)->GetInterface(video->playerObj, XA_IID_ANDROIDBUFFERQUEUESOURCE, &video->playerBQItf); + assert(XA_RESULT_SUCCESS == res); + + // specify which events we want to be notified of + res = (*video->playerBQItf)->SetCallbackEventsMask(video->playerBQItf, XA_ANDROIDBUFFERQUEUEEVENT_PROCESSED); + assert(XA_RESULT_SUCCESS == res); + + // register the callback from which OpenMAX AL can retrieve the data to play + res = (*video->playerBQItf)->RegisterCallback(video->playerBQItf, AndroidBufferQueueCallback, video); + assert(XA_RESULT_SUCCESS == res); + + // we want to be notified of the video size once it's found, so we register a callback for that + res = (*video->playerStreamInfoItf)->RegisterStreamChangeCallback(video->playerStreamInfoItf, StreamChangeCallback, video); + assert(XA_RESULT_SUCCESS == res); + + // enqueue the initial buffers + if (!kinc_android_video_enqueue_initial_buffers(video, false)) { + kinc_log(KINC_LOG_LEVEL_INFO, "Could not enqueue initial buffers for video decoding."); + return false; + } + + // prepare the player + res = (*video->playerPlayItf)->SetPlayState(video->playerPlayItf, XA_PLAYSTATE_PAUSED); + assert(XA_RESULT_SUCCESS == res); + + // set the volume + res = (*video->playerVolItf)->SetVolumeLevel(video->playerVolItf, 0); + assert(XA_RESULT_SUCCESS == res); + + // start the playback + res = (*video->playerPlayItf)->SetPlayState(video->playerPlayItf, XA_PLAYSTATE_PLAYING); + assert(XA_RESULT_SUCCESS == res); + + kinc_log(KINC_LOG_LEVEL_INFO, "Successfully loaded video."); + + return true; +} + +void kinc_android_video_shutdown(kinc_android_video_t *video) { + // destroy streaming media player object, and invalidate all associated interfaces + if (video->playerObj != NULL) { + (*video->playerObj)->Destroy(video->playerObj); + video->playerObj = NULL; + video->playerPlayItf = NULL; + video->playerBQItf = NULL; + video->playerStreamInfoItf = NULL; + video->playerVolItf = NULL; + } + + // destroy output mix object, and invalidate all associated interfaces + if (video->outputMixObject != NULL) { + (*video->outputMixObject)->Destroy(video->outputMixObject); + video->outputMixObject = NULL; + } + + // destroy engine object, and invalidate all associated interfaces + if (video->engineObject != NULL) { + (*video->engineObject)->Destroy(video->engineObject); + video->engineObject = NULL; + video->engineEngine = NULL; + } + + // close the file + if (video->file != NULL) { + AAsset_close(video->file); + video->file = NULL; + } + + // make sure we don't leak native windows + if (video->theNativeWindow != NULL) { + ANativeWindow_release(video->theNativeWindow); + video->theNativeWindow = NULL; + } +} + +#endif + +JNIEXPORT void JNICALL Java_tech_kinc_KincMoviePlayer_nativeCreate(JNIEnv *env, jobject jobj, jstring jpath, jobject surface, jint id) { +#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN) + const char *path = (*env)->GetStringUTFChars(env, jpath, NULL); + kinc_android_video_t *av = malloc(sizeof *av); + kinc_android_video_init(av); + av->theNativeWindow = ANativeWindow_fromSurface(env, surface); + kinc_android_video_open(av, path); + for (int i = 0; i < 10; ++i) { + if (videos[i] != NULL && videos[i]->impl.id == id) { + videos[i]->impl.androidVideo = av; + break; + } + } + (*env)->ReleaseStringUTFChars(env, jpath, path); +#endif +} + +void KoreAndroidVideoInit() { + JNIEnv *env; + (*kinc_android_get_activity()->vm)->AttachCurrentThread(kinc_android_get_activity()->vm, &env, NULL); + + jclass clazz = kinc_android_find_class(env, "tech.kinc.KincMoviePlayer"); + + // String path, Surface surface, int id + JNINativeMethod methodTable[] = {{"nativeCreate", "(Ljava/lang/String;Landroid/view/Surface;I)V", (void *)Java_tech_kinc_KincMoviePlayer_nativeCreate}}; + + int methodTableSize = sizeof(methodTable) / sizeof(methodTable[0]); + + int failure = (*env)->RegisterNatives(env, clazz, methodTable, methodTableSize); + if (failure != 0) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Failed to register KincMoviePlayer.nativeCreate"); + } + + (*kinc_android_get_activity()->vm)->DetachCurrentThread(kinc_android_get_activity()->vm); +} + +void kinc_video_init(kinc_video_t *video, const char *filename) { + video->impl.playing = false; + video->impl.sound = NULL; +#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN) + kinc_log(KINC_LOG_LEVEL_INFO, "Opening video %s.", filename); + video->impl.myWidth = 1023; + video->impl.myHeight = 684; + + video->impl.next = 0; + video->impl.audioTime = 0; + + JNIEnv *env = NULL; + (*kinc_android_get_activity()->vm)->AttachCurrentThread(kinc_android_get_activity()->vm, &env, NULL); + jclass koreMoviePlayerClass = kinc_android_find_class(env, "tech.kinc.KincMoviePlayer"); + jmethodID constructor = (*env)->GetMethodID(env, koreMoviePlayerClass, "", "(Ljava/lang/String;)V"); + jobject object = (*env)->NewObject(env, koreMoviePlayerClass, constructor, (*env)->NewStringUTF(env, filename)); + + jmethodID getId = (*env)->GetMethodID(env, koreMoviePlayerClass, "getId", "()I"); + video->impl.id = (*env)->CallIntMethod(env, object, getId); + + for (int i = 0; i < videosCount; ++i) { + if (videos[i] == NULL) { + videos[i] = video; + break; + } + } + + jmethodID jinit = (*env)->GetMethodID(env, koreMoviePlayerClass, "init", "()V"); + (*env)->CallVoidMethod(env, object, jinit); + + jmethodID getTextureId = (*env)->GetMethodID(env, koreMoviePlayerClass, "getTextureId", "()I"); + int texid = (*env)->CallIntMethod(env, object, getTextureId); + + (*kinc_android_get_activity()->vm)->DetachCurrentThread(kinc_android_get_activity()->vm); + + kinc_g4_texture_init_from_id(&video->impl.image, texid); +#endif +} + +void kinc_video_destroy(kinc_video_t *video) { +#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN) + kinc_video_stop(video); + kinc_android_video_t *av = (kinc_android_video_t *)video->impl.androidVideo; + kinc_android_video_shutdown(av); + for (int i = 0; i < 10; ++i) { + if (videos[i] == video) { + videos[i] = NULL; + break; + } + } +#endif +} + +void kinc_video_play(kinc_video_t *video, bool loop) { +#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN) + video->impl.playing = true; + video->impl.start = kinc_time(); +#endif +} + +void kinc_video_pause(kinc_video_t *video) { +#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN) + video->impl.playing = false; +#endif +} + +void kinc_video_stop(kinc_video_t *video) { +#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN) + kinc_video_pause(video); +#endif +} + +void kinc_video_update(kinc_video_t *video, double time) {} + +int kinc_video_width(kinc_video_t *video) { +#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN) + return video->impl.myWidth; +#else + return 512; +#endif +} + +int kinc_video_height(kinc_video_t *video) { +#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN) + return video->impl.myHeight; +#else + return 512; +#endif +} + +kinc_g4_texture_t *kinc_video_current_image(kinc_video_t *video) { +#if KINC_ANDROID_API >= 15 && !defined(KINC_VULKAN) + return &video->impl.image; +#else + return NULL; +#endif +} + +double kinc_video_duration(kinc_video_t *video) { + return 0.0; +} + +double kinc_video_position(kinc_video_t *video) { + return 0.0; +} + +bool kinc_video_finished(kinc_video_t *video) { + return false; +} + +bool kinc_video_paused(kinc_video_t *video) { + return !video->impl.playing; +} diff --git a/armorcore/sources/backends/android/kinc/backend/video.h b/armorcore/sources/backends/android/kinc/backend/video.h new file mode 100644 index 000000000..471e32fc0 --- /dev/null +++ b/armorcore/sources/backends/android/kinc/backend/video.h @@ -0,0 +1,49 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void *assetReader; + void *videoTrackOutput; + void *audioTrackOutput; + double start; + double next; + // double audioTime; + unsigned long long audioTime; + bool playing; + void *sound; + void *androidVideo; + int id; + kinc_g4_texture_t image; + double lastTime; + int myWidth; + int myHeight; +} kinc_video_impl_t; + +typedef struct kinc_internal_video_sound_stream { + void *audioTrackOutput; + float *buffer; + int bufferSize; + int bufferWritePosition; + int bufferReadPosition; + uint64_t read; + uint64_t written; +} kinc_internal_video_sound_stream_t; + +void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency); + +void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream); + +void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count); + +float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream); + +bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/android/kinc/backend/window.c.h b/armorcore/sources/backends/android/kinc/backend/window.c.h new file mode 100644 index 000000000..2eeb18f37 --- /dev/null +++ b/armorcore/sources/backends/android/kinc/backend/window.c.h @@ -0,0 +1,79 @@ +#include +#include +#include + +static void (*resizeCallback)(int x, int y, void *data) = NULL; +static void *resizeCallbackData = NULL; + +int kinc_count_windows(void) { + return 1; +} + +int kinc_window_x(int window_index) { + return 0; +} + +int kinc_window_y(int window_index) { + return 0; +} + +int kinc_android_width(); + +int kinc_window_width(int window_index) { + return kinc_android_width(); +} + +int kinc_android_height(); + +int kinc_window_height(int window_index) { + return kinc_android_height(); +} + +void kinc_window_resize(int window_index, int width, int height) {} + +void kinc_window_move(int window_index, int x, int y) {} + +void kinc_internal_change_framebuffer(int window, struct kinc_framebuffer_options *frame); + +void kinc_window_change_framebuffer(int window_index, kinc_framebuffer_options_t *frame) { + kinc_internal_change_framebuffer(0, frame); +} + +void kinc_window_change_features(int window_index, int features) {} + +void kinc_window_change_mode(int window_index, kinc_window_mode_t mode) {} + +void kinc_window_destroy(int window_index) {} + +void kinc_window_show(int window_index) {} + +void kinc_window_hide(int window_index) {} + +void kinc_window_set_title(int window_index, const char *title) {} + +int kinc_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) { + return 0; +} + +void kinc_window_set_resize_callback(int window_index, void (*callback)(int x, int y, void *data), void *data) { + resizeCallback = callback; + resizeCallbackData = data; +} + +void kinc_internal_call_resize_callback(int window_index, int width, int height) { + if (resizeCallback != NULL) { + resizeCallback(width, height, resizeCallbackData); + } +} + +void kinc_window_set_ppi_changed_callback(int window_index, void (*callback)(int ppi, void *data), void *data) {} + +void kinc_window_set_close_callback(int window, bool (*callback)(void *), void *data) {} + +kinc_window_mode_t kinc_window_get_mode(int window_index) { + return KINC_WINDOW_MODE_FULLSCREEN; +} + +int kinc_window_display(int window) { + return 0; +} diff --git a/armorcore/sources/backends/android/kinc/backend/windowdata.h b/armorcore/sources/backends/android/kinc/backend/windowdata.h new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/armorcore/sources/backends/android/kinc/backend/windowdata.h @@ -0,0 +1 @@ +#pragma once diff --git a/armorcore/sources/backends/apple/kinc/backend/appleunit.m b/armorcore/sources/backends/apple/kinc/backend/appleunit.m new file mode 100644 index 000000000..1c8535c88 --- /dev/null +++ b/armorcore/sources/backends/apple/kinc/backend/appleunit.m @@ -0,0 +1,4 @@ +#include "http.m.h" +#include "system.m.h" +#include "thread.m.h" +#include "video.m.h" diff --git a/armorcore/sources/backends/apple/kinc/backend/http.m.h b/armorcore/sources/backends/apple/kinc/backend/http.m.h new file mode 100644 index 000000000..d68a51eba --- /dev/null +++ b/armorcore/sources/backends/apple/kinc/backend/http.m.h @@ -0,0 +1,53 @@ +#include + +#import + +void kinc_http_request(const char *url, const char *path, const char *data, int port, bool secure, int method, const char *header, + kinc_http_callback_t callback, void *callbackdata) { + NSString *urlstring = secure ? @"https://" : @"http://"; + urlstring = [urlstring stringByAppendingString:[NSString stringWithUTF8String:url]]; + urlstring = [urlstring stringByAppendingString:@":"]; + urlstring = [urlstring stringByAppendingString:[[NSNumber numberWithInt:port] stringValue]]; + urlstring = [urlstring stringByAppendingString:@"/"]; + urlstring = [urlstring stringByAppendingString:[NSString stringWithUTF8String:path]]; + + NSURL *aUrl = [NSURL URLWithString:urlstring]; + + NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; + sessionConfiguration.HTTPAdditionalHeaders = @{@"Content-Type" : @"application/json"}; + NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aUrl]; + if (data != 0) { + // printf("Sending %s\n\n", data); + NSString *datastring = [NSString stringWithUTF8String:data]; + request.HTTPBody = [datastring dataUsingEncoding:NSUTF8StringEncoding]; + } + + switch (method) { + case KINC_HTTP_GET: + request.HTTPMethod = @"GET"; + break; + case KINC_HTTP_POST: + request.HTTPMethod = @"POST"; + break; + case KINC_HTTP_PUT: + request.HTTPMethod = @"PUT"; + break; + case KINC_HTTP_DELETE: + request.HTTPMethod = @"DELETE"; + break; + } + + NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request + completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + int statusCode = (int)[httpResponse statusCode]; + + NSMutableData *responseData = [[NSMutableData alloc] init]; + [responseData appendData:data]; + [responseData appendBytes:"\0" length:1]; + + callback(error == nil ? 0 : 1, statusCode, (const char *)[responseData bytes], callbackdata); + }]; + [dataTask resume]; +} diff --git a/armorcore/sources/backends/apple/kinc/backend/system.m.h b/armorcore/sources/backends/apple/kinc/backend/system.m.h new file mode 100644 index 000000000..9ec6c3d79 --- /dev/null +++ b/armorcore/sources/backends/apple/kinc/backend/system.m.h @@ -0,0 +1,24 @@ +#include + +int kinc_hardware_threads(void) { + return (int)[[NSProcessInfo processInfo] processorCount]; +} + +#ifdef KINC_APPLE_SOC + +int kinc_cpu_cores(void) { + return kinc_hardware_threads(); +} + +#else + +#include + +int kinc_cpu_cores(void) { + uint32_t proper_cpu_count = 1; + size_t count_length = sizeof(count_length); + sysctlbyname("hw.physicalcpu", &proper_cpu_count, &count_length, 0, 0); + return (int)proper_cpu_count; +} + +#endif diff --git a/armorcore/sources/backends/apple/kinc/backend/thread.m.h b/armorcore/sources/backends/apple/kinc/backend/thread.m.h new file mode 100644 index 000000000..d6a9ae1ce --- /dev/null +++ b/armorcore/sources/backends/apple/kinc/backend/thread.m.h @@ -0,0 +1,50 @@ +#include +#include + +#include + +#include +#include + +#include +#include +#include + +static void *ThreadProc(void *arg) { + @autoreleasepool { + kinc_thread_t *t = (kinc_thread_t *)arg; + t->impl.thread(t->impl.param); + pthread_exit(NULL); + return NULL; + } +} + +void kinc_thread_init(kinc_thread_t *t, void (*thread)(void *param), void *param) { + t->impl.param = param; + t->impl.thread = thread; + pthread_attr_t attr; + pthread_attr_init(&attr); + // pthread_attr_setstacksize(&attr, 1024 * 64); + struct sched_param sp; + memset(&sp, 0, sizeof(sp)); + sp.sched_priority = 0; + pthread_attr_setschedparam(&attr, &sp); + pthread_create(&t->impl.pthread, &attr, &ThreadProc, t); + // Kt::affirmD(ret == 0); + pthread_attr_destroy(&attr); +} + +void kinc_thread_wait_and_destroy(kinc_thread_t *thread) { + int ret; + do { + ret = pthread_join(thread->impl.pthread, NULL); + } while (ret != 0); +} + +bool kinc_thread_try_to_destroy(kinc_thread_t *thread) { + return pthread_join(thread->impl.pthread, NULL) == 0; +} + +void kinc_threads_init(void) {} + +void kinc_threads_quit(void) {} diff --git a/armorcore/sources/backends/apple/kinc/backend/video.h b/armorcore/sources/backends/apple/kinc/backend/video.h new file mode 100644 index 000000000..ac3995691 --- /dev/null +++ b/armorcore/sources/backends/apple/kinc/backend/video.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + double start; + double videoStart; + double next; + // double audioTime; + unsigned long long audioTime; + bool playing; + bool loop; + void *sound; + bool image_initialized; + kinc_g4_texture_t image; + double lastTime; + float duration; + bool finished; + int myWidth; + int myHeight; + + id videoAsset; + id assetReader; + id videoTrackOutput; + id audioTrackOutput; + id url; +} kinc_video_impl_t; + +typedef struct kinc_internal_video_sound_stream { + float *buffer; + int bufferSize; + int bufferWritePosition; + int bufferReadPosition; + uint64_t read; + uint64_t written; +} kinc_internal_video_sound_stream_t; + +void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency); + +void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream); + +void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count); + +float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream); + +bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/apple/kinc/backend/video.m.h b/armorcore/sources/backends/apple/kinc/backend/video.m.h new file mode 100644 index 000000000..b07c2f603 --- /dev/null +++ b/armorcore/sources/backends/apple/kinc/backend/video.m.h @@ -0,0 +1,311 @@ +#include + +#import +#include +#include +#include +#include +#include +#include +#include +#include + +extern const char *iphonegetresourcepath(void); +extern const char *macgetresourcepath(void); + +void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency) { + stream->bufferSize = 1024 * 100; + stream->bufferReadPosition = 0; + stream->bufferWritePosition = 0; + stream->read = 0; + stream->written = 0; + stream->buffer = (float *)malloc(stream->bufferSize * sizeof(float)); +} + +void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream) { + free(stream->buffer); +} + +void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count) { + for (int i = 0; i < sample_count; ++i) { + float value = data[i]; // / 32767.0; + stream->buffer[stream->bufferWritePosition++] = value; + ++stream->written; + if (stream->bufferWritePosition >= stream->bufferSize) { + stream->bufferWritePosition = 0; + } + } +} + +static float samples[2] = {0}; + +float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream) { + ++stream->read; + if (stream->written <= stream->read) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Out of audio\n"); + return 0; + } + + if (stream->bufferReadPosition >= stream->bufferSize) { + stream->bufferReadPosition = 0; + kinc_log(KINC_LOG_LEVEL_INFO, "buffer read back - %i\n", (int)(stream->written - stream->read)); + } + samples[0] = stream->buffer[stream->bufferReadPosition++]; + + if (stream->bufferReadPosition >= stream->bufferSize) { + stream->bufferReadPosition = 0; + kinc_log(KINC_LOG_LEVEL_INFO, "buffer read back - %i\n", (int)(stream->written - stream->read)); + } + samples[1] = stream->buffer[stream->bufferReadPosition++]; + + return samples; +} + +bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream) { + return false; +} + +static void load(kinc_video_t *video, double startTime) { + video->impl.videoStart = startTime; + AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:video->impl.url options:nil]; + video->impl.videoAsset = asset; + + video->impl.duration = [asset duration].value / [asset duration].timescale; + + AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; + NSDictionary *videoOutputSettings = + [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey, nil]; + AVAssetReaderTrackOutput *videoOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:videoTrack outputSettings:videoOutputSettings]; + [videoOutput setSupportsRandomAccess:YES]; + + bool hasAudio = [[asset tracksWithMediaType:AVMediaTypeAudio] count] > 0; + AVAssetReaderAudioMixOutput *audioOutput = NULL; + if (hasAudio) { + AVAssetTrack *audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; + NSDictionary *audioOutputSettings = [NSDictionary + dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey, [NSNumber numberWithFloat:44100.0], AVSampleRateKey, + [NSNumber numberWithInt:32], AVLinearPCMBitDepthKey, [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved, + [NSNumber numberWithBool:YES], AVLinearPCMIsFloatKey, [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey, nil]; + audioOutput = [AVAssetReaderAudioMixOutput assetReaderAudioMixOutputWithAudioTracks:@[ audioTrack ] audioSettings:audioOutputSettings]; + [audioOutput setSupportsRandomAccess:YES]; + } + + AVAssetReader *reader = [AVAssetReader assetReaderWithAsset:asset error:nil]; + + if (startTime > 0) { + CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(startTime * 1000, 1000), kCMTimePositiveInfinity); + reader.timeRange = timeRange; + } + + [reader addOutput:videoOutput]; + if (hasAudio) { + [reader addOutput:audioOutput]; + } + + video->impl.assetReader = reader; + video->impl.videoTrackOutput = videoOutput; + if (hasAudio) { + video->impl.audioTrackOutput = audioOutput; + } + else { + video->impl.audioTrackOutput = NULL; + } + + if (video->impl.myWidth < 0) + video->impl.myWidth = [videoTrack naturalSize].width; + if (video->impl.myHeight < 0) + video->impl.myHeight = [videoTrack naturalSize].height; + int framerate = [videoTrack nominalFrameRate]; + kinc_log(KINC_LOG_LEVEL_INFO, "Framerate: %i\n", framerate); + video->impl.next = video->impl.videoStart; + video->impl.audioTime = video->impl.videoStart * 44100; +} + +void kinc_video_init(kinc_video_t *video, const char *filename) { + video->impl.playing = false; + video->impl.sound = NULL; + video->impl.image_initialized = false; + char name[2048]; +#ifdef KINC_IOS + strcpy(name, iphonegetresourcepath()); +#else + strcpy(name, macgetresourcepath()); +#endif + strcat(name, "/"); + strcat(name, KINC_DEBUGDIR); + strcat(name, "/"); + strcat(name, filename); + video->impl.url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:name]]; + video->impl.myWidth = -1; + video->impl.myHeight = -1; + video->impl.finished = false; + video->impl.duration = 0; + load(video, 0); +} + +void kinc_video_destroy(kinc_video_t *video) { + kinc_video_stop(video); +} + +#ifdef KINC_IOS +void iosPlayVideoSoundStream(kinc_internal_video_sound_stream_t *video); +void iosStopVideoSoundStream(void); +#else +void macPlayVideoSoundStream(kinc_internal_video_sound_stream_t *video); +void macStopVideoSoundStream(void); +#endif + +void kinc_video_play(kinc_video_t *video, bool loop) { + AVAssetReader *reader = video->impl.assetReader; + [reader startReading]; + + kinc_internal_video_sound_stream_t *stream = (kinc_internal_video_sound_stream_t *)malloc(sizeof(kinc_internal_video_sound_stream_t)); + kinc_internal_video_sound_stream_init(stream, 2, 44100); + video->impl.sound = stream; +#ifdef KINC_IOS + iosPlayVideoSoundStream((kinc_internal_video_sound_stream_t *)video->impl.sound); +#else + macPlayVideoSoundStream((kinc_internal_video_sound_stream_t *)video->impl.sound); +#endif + + video->impl.playing = true; + video->impl.start = kinc_time() - video->impl.videoStart; + video->impl.loop = loop; +} + +void kinc_video_pause(kinc_video_t *video) { + video->impl.playing = false; + if (video->impl.sound != NULL) { +// Mixer::stop(sound); +#ifdef KINC_IOS + iosStopVideoSoundStream(); +#else + macStopVideoSoundStream(); +#endif + kinc_internal_video_sound_stream_destroy((kinc_internal_video_sound_stream_t *)video->impl.sound); + free(video->impl.sound); + video->impl.sound = NULL; + } +} + +void kinc_video_stop(kinc_video_t *video) { + kinc_video_pause(video); + video->impl.finished = true; +} + +static void updateImage(kinc_video_t *video) { + if (!video->impl.playing) + return; + + { + AVAssetReaderTrackOutput *videoOutput = video->impl.videoTrackOutput; + CMSampleBufferRef buffer = [videoOutput copyNextSampleBuffer]; + if (!buffer) { + if (video->impl.loop) { + CMTimeRange timeRange = CMTimeRangeMake(CMTimeMake(0, 1000), kCMTimePositiveInfinity); + [videoOutput resetForReadingTimeRanges:[NSArray arrayWithObject:[NSValue valueWithCMTimeRange:timeRange]]]; + + AVAssetReaderAudioMixOutput *audioOutput = video->impl.audioTrackOutput; + CMSampleBufferRef audio_buffer = [audioOutput copyNextSampleBuffer]; + while (audio_buffer) { + audio_buffer = [audioOutput copyNextSampleBuffer]; + } + [audioOutput resetForReadingTimeRanges:[NSArray arrayWithObject:[NSValue valueWithCMTimeRange:timeRange]]]; + + buffer = [videoOutput copyNextSampleBuffer]; + + video->impl.start = kinc_time() - video->impl.videoStart; + } + else { + kinc_video_stop(video); + return; + } + } + video->impl.next = CMTimeGetSeconds(CMSampleBufferGetOutputPresentationTimeStamp(buffer)); + + CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(buffer); + + if (!video->impl.image_initialized) { + CGSize size = CVImageBufferGetDisplaySize(pixelBuffer); + video->impl.myWidth = size.width; + video->impl.myHeight = size.height; + kinc_g4_texture_init(&video->impl.image, kinc_video_width(video), kinc_video_height(video), KINC_IMAGE_FORMAT_BGRA32); + video->impl.image_initialized = true; + } + + if (pixelBuffer != NULL) { + CVPixelBufferLockBaseAddress(pixelBuffer, 0); +#ifdef KINC_OPENGL + kinc_g4_texture_upload(&video->impl.image, (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer), + (int)(CVPixelBufferGetBytesPerRow(pixelBuffer) / 4)); +#else + kinc_g4_texture_upload(&video->impl.image, (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer), (int)(CVPixelBufferGetBytesPerRow(pixelBuffer))); +#endif + CVPixelBufferUnlockBaseAddress(pixelBuffer, 0); + } + CFRelease(buffer); + } + + if (video->impl.audioTrackOutput != NULL) { + AVAssetReaderAudioMixOutput *audioOutput = video->impl.audioTrackOutput; + while (video->impl.audioTime / 44100.0 < video->impl.next + 0.1) { + CMSampleBufferRef buffer = [audioOutput copyNextSampleBuffer]; + if (!buffer) + return; + CMItemCount numSamplesInBuffer = CMSampleBufferGetNumSamples(buffer); + AudioBufferList audioBufferList; + CMBlockBufferRef blockBufferOut = nil; + CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(buffer, NULL, &audioBufferList, sizeof(audioBufferList), NULL, NULL, + kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBufferOut); + for (int bufferCount = 0; bufferCount < audioBufferList.mNumberBuffers; ++bufferCount) { + float *samples = (float *)audioBufferList.mBuffers[bufferCount].mData; + kinc_internal_video_sound_stream_t *sound = (kinc_internal_video_sound_stream_t *)video->impl.sound; + if (video->impl.audioTime / 44100.0 > video->impl.next - 0.1) { + kinc_internal_video_sound_stream_insert_data(sound, samples, (int)numSamplesInBuffer * 2); + } + else { + // Send some data anyway because the buffers are huge + kinc_internal_video_sound_stream_insert_data(sound, samples, (int)numSamplesInBuffer); + } + video->impl.audioTime += numSamplesInBuffer; + } + CFRelease(blockBufferOut); + CFRelease(buffer); + } + } +} + +void kinc_video_update(kinc_video_t *video, double time) { + if (video->impl.playing && time >= video->impl.start + video->impl.next) { + updateImage(video); + } +} + +int kinc_video_width(kinc_video_t *video) { + return video->impl.myWidth; +} + +int kinc_video_height(kinc_video_t *video) { + return video->impl.myHeight; +} + +kinc_g4_texture_t *kinc_video_current_image(kinc_video_t *video) { + kinc_video_update(video, kinc_time()); + return &video->impl.image; +} + +double kinc_video_duration(kinc_video_t *video) { + return video->impl.duration; +} + +bool kinc_video_finished(kinc_video_t *video) { + return video->impl.finished; +} + +bool kinc_video_paused(kinc_video_t *video) { + return !video->impl.playing; +} + +double kinc_video_position(kinc_video_t *video) { + return video->impl.next - video->impl.start; +} diff --git a/armorcore/sources/backends/data/android/app/CMakeLists.txt b/armorcore/sources/backends/data/android/app/CMakeLists.txt new file mode 100644 index 000000000..de38cc6f0 --- /dev/null +++ b/armorcore/sources/backends/data/android/app/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.22.1) + +project("Kinc") + +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}{debug_defines}") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}{debug_defines}") +set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}{release_defines}") +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}{release_defines}") + +include_directories( +{includes}) + +add_library( + kinc + SHARED +{files}) + +{libraries1} +target_link_libraries( + kinc +{libraries2}) diff --git a/armorcore/sources/backends/data/android/app/build.gradle.kts b/armorcore/sources/backends/data/android/app/build.gradle.kts new file mode 100644 index 000000000..f3109e53f --- /dev/null +++ b/armorcore/sources/backends/data/android/app/build.gradle.kts @@ -0,0 +1,61 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} + +android { + namespace = "{package}" + compileSdk = {compileSdkVersion} + + defaultConfig { + applicationId = "{package}" + minSdk = {minSdkVersion} + targetSdk = {targetSdkVersion} + versionCode = {versionCode} + versionName = "{versionName}" + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + ndk { + abiFilters.add("arm64-v8a") + } + } + sourceSets.getByName("main") { + java.setSrcDirs(listOf({javasources})) + } + buildTypes { + release { + isMinifyEnabled = true + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + {architecture} + } + debug { + isMinifyEnabled = false + {architecture} + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + externalNativeBuild { + cmake { + path = file("CMakeLists.txt") + version = "3.22.1" + } + } + buildFeatures { + viewBinding = true + } +} + +dependencies { + implementation("androidx.core:core-ktx:1.9.0") + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("com.google.android.material:material:1.8.0") + implementation("androidx.constraintlayout:constraintlayout:2.1.4") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") +} diff --git a/armorcore/sources/backends/data/android/app/proguard-rules.pro b/armorcore/sources/backends/data/android/app/proguard-rules.pro new file mode 100644 index 000000000..5206dd13c --- /dev/null +++ b/armorcore/sources/backends/data/android/app/proguard-rules.pro @@ -0,0 +1,34 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +-keep public class tech.kinc.KincActivity { + public ; +} +-keep public class tech.kinc.KincMoviePlayer { + public ; +} +-keep public class tech.kinc.KincMovieTexture { + public ; +} +-keepclasseswithmembernames,includedescriptorclasses class * { + native ; +} diff --git a/armorcore/sources/backends/data/android/build.gradle.kts b/armorcore/sources/backends/data/android/build.gradle.kts new file mode 100644 index 000000000..b12e2fc64 --- /dev/null +++ b/armorcore/sources/backends/data/android/build.gradle.kts @@ -0,0 +1,5 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id("com.android.application") version "8.1.0" apply false + id("org.jetbrains.kotlin.android") version "1.8.0" apply false +} \ No newline at end of file diff --git a/armorcore/sources/backends/data/android/gradle.properties b/armorcore/sources/backends/data/android/gradle.properties new file mode 100644 index 000000000..3c5031eb7 --- /dev/null +++ b/armorcore/sources/backends/data/android/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/armorcore/sources/backends/data/android/gradle/wrapper/gradle-wrapper.jar b/armorcore/sources/backends/data/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f GIT binary patch literal 59203 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

K7UpUK$>l@ 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 E2O(~oYXATM literal 0 HcmV?d00001 diff --git a/armorcore/sources/backends/data/android/gradle/wrapper/gradle-wrapper.properties b/armorcore/sources/backends/data/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..1289b12f7 --- /dev/null +++ b/armorcore/sources/backends/data/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Aug 04 18:00:20 CEST 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/armorcore/sources/backends/data/android/gradlew b/armorcore/sources/backends/data/android/gradlew new file mode 100644 index 000000000..4f906e0c8 --- /dev/null +++ b/armorcore/sources/backends/data/android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/armorcore/sources/backends/data/android/gradlew.bat b/armorcore/sources/backends/data/android/gradlew.bat new file mode 100644 index 000000000..ac1b06f93 --- /dev/null +++ b/armorcore/sources/backends/data/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/armorcore/sources/backends/data/android/main/AndroidManifest.xml b/armorcore/sources/backends/data/android/main/AndroidManifest.xml new file mode 100644 index 000000000..3e1b74662 --- /dev/null +++ b/armorcore/sources/backends/data/android/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + +{permissions} + + + + + + + + + + + + \ No newline at end of file diff --git a/armorcore/sources/backends/data/android/main/res/values/strings.xml b/armorcore/sources/backends/data/android/main/res/values/strings.xml new file mode 100644 index 000000000..ade92a1fe --- /dev/null +++ b/armorcore/sources/backends/data/android/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + {name} + \ No newline at end of file diff --git a/armorcore/sources/backends/data/android/settings.gradle.kts b/armorcore/sources/backends/data/android/settings.gradle.kts new file mode 100644 index 000000000..2ef8764d3 --- /dev/null +++ b/armorcore/sources/backends/data/android/settings.gradle.kts @@ -0,0 +1,18 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "{name}" +include(":app") + \ No newline at end of file diff --git a/armorcore/sources/backends/data/wasm/JS-Sources/audio-thread.js b/armorcore/sources/backends/data/wasm/JS-Sources/audio-thread.js new file mode 100644 index 000000000..157b1e9a2 --- /dev/null +++ b/armorcore/sources/backends/data/wasm/JS-Sources/audio-thread.js @@ -0,0 +1,202 @@ +// from https://developer.mozilla.org/en-US/docs/Web/API/AudioWorkletNode + +function create_thread(func) { + +} + +class AudioThread extends AudioWorkletProcessor { + constructor(options) { + super(); + + const self = this; + + this.port.onmessageerror = (e) => { + this.port.postMessage('Error: ' + JSON.stringify(e)); + }; + + const mod = options.processorOptions.mod; + const memory = options.processorOptions.memory; + + const importObject = { + env: { memory }, + imports: { + imported_func: arg => console.log('thread: ' + arg), + create_thread, + glViewport: function(x, y, width, height) { }, + glScissor: function(x, y, width, height) { }, + glGetIntegerv: function(pname, data) { }, + glGetFloatv: function(pname, data) { }, + glGetString: function(name) { }, + glDrawElements: function(mode, count, type, offset) { }, + glDrawElementsInstanced: function(mode, count, type, indices, instancecount) { }, + glVertexAttribDivisor: function(index, divisor) { }, + glBindFramebuffer: function(target, framebuffer) { }, + glFramebufferTexture2D: function(target, attachment, textarget, texture, level) { }, + glGenFramebuffers: function(n, framebuffers) { }, + glGenRenderbuffers: function(n, renderbuffers) { }, + glBindRenderbuffer: function(target, renderbuffer) { }, + glRenderbufferStorage: function(target, internalformat, width, height) { }, + glFramebufferRenderbuffer: function(target, attachment, renderbuffertarget, renderbuffer) { }, + glReadPixels: function(x, y, width, height, format, type, data) { }, + glTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, type, pixels) { }, + glEnable: function(cap) { }, + glDisable: function(cap) { }, + glColorMask: function(red, green, blue, alpha) { }, + glClearColor: function(red, green, blue, alpha) { }, + glDepthMask: function(flag) { }, + glClearDepthf: function(depth) { }, + glStencilMask: function(mask) { }, + glClearStencil: function(s) { }, + glClear: function(mask) { }, + glBindBuffer: function(target, buffer) { }, + glUseProgram: function(program) { }, + glStencilMaskSeparate: function(face, mask) { }, + glStencilOpSeparate: function(face, fail, zfail, zpass) { }, + glStencilFuncSeparate: function(face, func, ref, mask) { }, + glDepthFunc: function(func) { }, + glCullFace: function(mode) { }, + glBlendFuncSeparate: function(src_rgb, dst_rgb, src_alpha, dst_alpha) { }, + glBlendEquationSeparate: function(mode_rgb, mode_alpha) { }, + glGenBuffers: function(n, buffers) { }, + glBufferData: function(target, size, data, usage) { }, + glCreateProgram: function() { }, + glAttachShader: function(program, shader) { }, + glBindAttribLocation: function(program, index, name) { }, + glLinkProgram: function(program) { }, + glGetProgramiv: function(program, pname, params) { }, + glGetProgramInfoLog: function(program) { }, + glCreateShader: function(type) { }, + glShaderSource: function(shader, count, source, length) { }, + glCompileShader: function(shader) { }, + glGetShaderiv: function(shader, pname, params) { }, + glGetShaderInfoLog: function(shader) { }, + glBufferSubData: function(target, offset, size, data) { }, + glEnableVertexAttribArray: function(index) { }, + glVertexAttribPointer: function(index, size, type, normalized, stride, offset) { }, + glDisableVertexAttribArray: function(index) { }, + glGetUniformLocation: function(program, name) { }, + glUniform1i: function(location, v0) { }, + glUniform2i: function(location, v0, v1) { }, + glUniform3i: function(location, v0, v1, v2) { }, + glUniform4i: function(location, v0, v1, v2, v3) { }, + glUniform1iv: function(location, count, value) { }, + glUniform2iv: function(location, count, value) { }, + glUniform3iv: function(location, count, value) { }, + glUniform4iv: function(location, count, value) { }, + glUniform1f: function(location, v0) { }, + glUniform2f: function(location, v0, v1) { }, + glUniform3f: function(location, v0, v1, v2) { }, + glUniform4f: function(location, v0, v1, v2, v3) { }, + glUniform1fv: function(location, count, value) { }, + glUniform2fv: function(location, count, value) { }, + glUniform3fv: function(location, count, value) { }, + glUniform4fv: function(location, count, value) { }, + glUniformMatrix3fv: function(location, count, transpose, value) { }, + glUniformMatrix4fv: function(location, count, transpose, value) { }, + glTexParameterf: function(target, pname, param) { }, + glActiveTexture: function(texture) { }, + glBindTexture: function(target, texture) { }, + glTexParameteri: function(target, pname, param) { }, + glGetActiveUniform: function(program, index, bufSize, length, size, type, name) { }, + glGenTextures: function(n, textures) { }, + glTexImage2D: function(target, level, internalformat, width, height, border, format, type, data) { }, + glPixelStorei: function(pname, param) { }, + glCompressedTexImage2D: function(target, level, internalformat, width, height, border, imageSize, data) { }, + glDrawBuffers: function(n, bufs) { }, + glGenerateMipmap: function(target) { }, + glFlush: function() { }, + glDeleteBuffers: function(n, buffers) { }, + glDeleteTextures: function(n, textures) { }, + glDeleteFramebuffers: function(n, framebuffers) { }, + glDeleteProgram: function(program) { }, + glDeleteShader: function(shader) { }, + js_fprintf: function(format) { + console.log(read_string(format)); + }, + js_fopen: function(filename) { + return 0; + }, + js_ftell: function(stream) { + return 0; + }, + js_fseek: function(stream, offset, origin) { + return 0; + }, + js_fread: function(ptr, size, count, stream) { + return 0; + }, + js_time: function() { + return window.performance.now(); + }, + js_pow: function(x) { + return Math.pow(x); + }, + js_floor: function(x) { + return Math.floor(x); + }, + js_sin: function(x) { + return Math.sin(x); + }, + js_cos: function(x) { + return Math.cos(x); + }, + js_tan: function(x) { + return Math.tan(x); + }, + js_log: function(base, exponent) { + return Math.log(base, exponent); + }, + js_exp: function(x) { + return Math.exp(x); + }, + js_sqrt: function(x) { + return Math.sqrt(x); + }, + js_eval: function(str) { } + } + }; + + WebAssembly.instantiate(mod, importObject).then((instance) => { + this.port.postMessage('Running audio thread'); + self.audio_func = instance.exports.audio_func; + self.audio_pointer = instance.exports.malloc(16 * 1024); + this.port.postMessage('Audio pointer: ' + self.audio_pointer); + this.port.postMessage('Memory byteLength: ' + memory.buffer.byteLength); + self.audio_data = new Float32Array( + memory.buffer, + self.audio_pointer, + 16 * 256 + ); + }); + } + + process(inputs, outputs, parameters) { + const output = outputs[0]; + + const data = output[0]; + + //for (let i = 0; i < data.length; ++i) { + // data[i] = Math.random() * 2 - 1; + //} + + if (this.audio_func) { + let offset = 0; + for (;;) { + const length = Math.min(data.length - offset, this.audio_data.length); + this.audio_func(this.audio_pointer + offset, length); + for (let i = 0; i < length; ++i) { + data[offset + i] = this.audio_data[i]; + } + + if (offset + this.audio_data.length >= data.length) { + break; + } + offset += this.audio_data.length; + } + } + + return true; + } +} + +registerProcessor('audio-thread', AudioThread); diff --git a/armorcore/sources/backends/data/wasm/JS-Sources/index.html b/armorcore/sources/backends/data/wasm/JS-Sources/index.html new file mode 100644 index 000000000..93c963264 --- /dev/null +++ b/armorcore/sources/backends/data/wasm/JS-Sources/index.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/armorcore/sources/backends/data/wasm/JS-Sources/start.js b/armorcore/sources/backends/data/wasm/JS-Sources/start.js new file mode 100644 index 000000000..1ea61de83 --- /dev/null +++ b/armorcore/sources/backends/data/wasm/JS-Sources/start.js @@ -0,0 +1,541 @@ +// Includes snippets from https://surma.dev/things/c-to-webassembly/ and https://developer.mozilla.org/en-US/docs/WebAssembly/Using_the_JavaScript_API --> + +let memory = null; +let heapu8 = null; +let heapu16 = null; +let heapu32 = null; +let heapi32 = null; +let heapf32 = null; +let mod = null; +let instance = null; +let audio_thread_started = false; + +function create_thread(func) { + console.log('Creating thread'); + const thread_starter = new Worker('thread_starter.js'); + const arr = new Uint8Array(memory.buffer, func, 256); + let str = ''; + for (let i = 0; arr[i] != 0; ++i) { + str += String.fromCharCode(arr[i]); + } + thread_starter.postMessage({ mod, memory, func: str }); +} + +async function start_audio_thread() { + const audioContext = new AudioContext(); + await audioContext.audioWorklet.addModule('audio-thread.js'); + const audioThreadNode = new AudioWorkletNode(audioContext, 'audio-thread', { processorOptions: { mod, memory }}); + audioThreadNode.port.onmessage = (message) => { + console.log(message.data); + }; + audioThreadNode.connect(audioContext.destination); +} + +function read_string(ptr) { + let str = ''; + for (let i = 0; heapu8[ptr + i] != 0; ++i) { + str += String.fromCharCode(heapu8[ptr + i]); + } + return str; +} + +function write_string(ptr, str) { + for (let i = 0; i < str.length; ++i) { + heapu8[ptr + i] = str.charCodeAt(i); + } + heapu8[ptr + str.length] = 0; +} + +async function init() { + let wasm_bytes = null; + await fetch("./ShaderTest.wasm").then(res => res.arrayBuffer()).then(buffer => wasm_bytes = new Uint8Array(buffer)); + + // Read memory size from wasm file + let memory_size = 0; + let i = 8; + while (i < wasm_bytes.length) { + function read_leb() { + let result = 0; + let shift = 0; + while (true) { + let byte = wasm_bytes[i++]; + result |= (byte & 0x7f) << shift; + if ((byte & 0x80) == 0) return result; + shift += 7; + } + } + let type = read_leb() + let length = read_leb() + if (type == 6) { + read_leb(); // count + i++; // gtype + i++; // mutable + read_leb(); // opcode + memory_size = read_leb() / 65536 + 1; + break; + } + i += length; + } + + memory = new WebAssembly.Memory({ initial: memory_size, maximum: memory_size, shared: true }); + heapu8 = new Uint8Array(memory.buffer); + heapu16 = new Uint16Array(memory.buffer); + heapu32 = new Uint32Array(memory.buffer); + heapi32 = new Int32Array(memory.buffer); + heapf32 = new Float32Array(memory.buffer); + + const kanvas = document.getElementById('kanvas'); + const gl = kanvas.getContext('webgl2', { antialias: false, alpha: false }); + // gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1); + gl.getExtension("EXT_color_buffer_float"); + gl.getExtension("OES_texture_float_linear"); + gl.getExtension("OES_texture_half_float_linear"); + gl.getExtension("EXT_texture_filter_anisotropic"); + + let file_buffer = null; + let file_buffer_pos = 0; + let gl_programs = [null]; + let gl_shaders = [null]; + let gl_buffers = [null]; + let gl_framebuffers = [null]; + let gl_renderbuffers = [null]; + let gl_textures = [null]; + let gl_locations = [null]; + + const result = await WebAssembly.instantiate( + wasm_bytes, { + env: { memory }, + imports: { + create_thread, + glViewport: function(x, y, width, height) { + gl.viewport(x, y, width, height); + }, + glScissor: function(x, y, width, height) { + gl.scissor(x, y, width, height); + }, + glGetIntegerv: function(pname, data) { + if (pname == 2) { // GL_MAJOR_VERSION + heapu32[data / 4] = 3; + } + else { + heapu32[data / 4] = gl.getParameter(pname); + } + }, + glGetFloatv: function(pname, data) { + heapf32[data / 4] = gl.getParameter(pname); + }, + glGetString: function(name) { + // return gl.getParameter(name); + }, + glDrawElements: function(mode, count, type, offset) { + gl.drawElements(mode, count, type, offset); + }, + glDrawElementsInstanced: function(mode, count, type, indices, instancecount) { + gl.drawElementsInstanced(mode, count, type, indices, instancecount); + }, + glVertexAttribDivisor: function(index, divisor) { + gl.vertexAttribDivisor(index, divisor); + }, + glBindFramebuffer: function(target, framebuffer) { + gl.bindFramebuffer(target, gl_framebuffers[framebuffer]); + }, + glFramebufferTexture2D: function(target, attachment, textarget, texture, level) { + gl.framebufferTexture2D(target, attachment, textarget, gl_textures[texture], level); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + console.log("Incomplete framebuffer"); + } + }, + glGenFramebuffers: function(n, framebuffers) { + for (let i = 0; i < n; ++i) { + gl_framebuffers.push(gl.createFramebuffer()); + heapu32[framebuffers / 4 + i] = gl_framebuffers.length - 1; + } + }, + glGenRenderbuffers: function(n, renderbuffers) { + for (let i = 0; i < n; ++i) { + gl_renderbuffers.push(gl.createRenderbuffer()); + heapu32[renderbuffers / 4 + i] = gl_renderbuffers.length - 1; + } + }, + glBindRenderbuffer: function(target, renderbuffer) { + gl.bindRenderbuffer(target, gl_renderbuffers[renderbuffer]); + }, + glRenderbufferStorage: function(target, internalformat, width, height) { + gl.renderbufferStorage(target, internalformat, width, height) + }, + glFramebufferRenderbuffer: function(target, attachment, renderbuffertarget, renderbuffer) { + gl.framebufferRenderbuffer(target, attachment, renderbuffertarget, gl_renderbuffers[renderbuffer]); + }, + glReadPixels: function(x, y, width, height, format, type, data) { + let pixels = type == gl.FLOAT ? heapf32.subarray(data / 4) : heapu8.subarray(data); + gl.readPixels(x, y, width, height, format, type, pixels); + }, + glTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, type, pixels) { + gl.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, heapu8.subarray(pixels)); + }, + glEnable: function(cap) { + gl.enable(cap); + }, + glDisable: function(cap) { + gl.disable(cap); + }, + glColorMask: function(red, green, blue, alpha) { + gl.colorMask(red, green, blue, alpha); + }, + glClearColor: function(red, green, blue, alpha) { + gl.clearColor(red, green, blue, alpha); + }, + glDepthMask: function(flag) { + gl.depthMask(flag); + }, + glClearDepthf: function(depth) { + gl.clearDepth(depth); + }, + glStencilMask: function(mask) { + gl.stencilMask(mask); + }, + glClearStencil: function(s) { + gl.clearStencil(s); + }, + glClear: function(mask) { + gl.clear(mask); + }, + glBindBuffer: function(target, buffer) { + gl.bindBuffer(target, gl_buffers[buffer]); + }, + glUseProgram: function(program) { + gl.useProgram(gl_programs[program]); + }, + glStencilMaskSeparate: function(face, mask) { + gl.stencilMaskSeparate(face, mask); + }, + glStencilOpSeparate: function(face, fail, zfail, zpass) { + gl.stencilOpSeparate(face, fail, zfail, zpass); + }, + glStencilFuncSeparate: function(face, func, ref, mask) { + gl.stencilFuncSeparate(face, func, ref, mask); + }, + glDepthFunc: function(func) { + gl.depthFunc(func); + }, + glCullFace: function(mode) { + gl.cullFace(mode); + }, + glBlendFuncSeparate: function(src_rgb, dst_rgb, src_alpha, dst_alpha) { + gl.blendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha); + }, + glBlendEquationSeparate: function(mode_rgb, mode_alpha) { + gl.blendEquationSeparate(mode_rgb, mode_alpha); + }, + glGenBuffers: function(n, buffers) { + for (let i = 0; i < n; ++i) { + gl_buffers.push(gl.createBuffer()); + heapu32[buffers / 4 + i] = gl_buffers.length - 1; + } + }, + glBufferData: function(target, size, data, usage) { + gl.bufferData(target, heapu8.subarray(data, data + Number(size)), usage); + }, + glCreateProgram: function() { + gl_programs.push(gl.createProgram()); + return gl_programs.length - 1; + }, + glAttachShader: function(program, shader) { + gl.attachShader(gl_programs[program], gl_shaders[shader]); + }, + glBindAttribLocation: function(program, index, name) { + gl.bindAttribLocation(gl_programs[program], index, read_string(name)); + }, + glLinkProgram: function(program) { + gl.linkProgram(gl_programs[program]); + }, + glGetProgramiv: function(program, pname, params) { + heapu32[params / 4] = gl.getProgramParameter(gl_programs[program], pname); + }, + glGetProgramInfoLog: function(program) { + console.log(gl.getProgramInfoLog(gl_programs[program])); + }, + glCreateShader: function(type) { + gl_shaders.push(gl.createShader(type)); + return gl_shaders.length - 1; + }, + glShaderSource: function(shader, count, source, length) { + gl.shaderSource(gl_shaders[shader], read_string(heapu32[source / 4])); + }, + glCompileShader: function(shader) { + gl.compileShader(gl_shaders[shader]); + }, + glGetShaderiv: function(shader, pname, params) { + heapu32[params / 4] = gl.getShaderParameter(gl_shaders[shader], pname); + }, + glGetShaderInfoLog: function(shader) { + console.log(gl.getShaderInfoLog(gl_shaders[shader])); + }, + glBufferSubData: function(target, offset, size, data) { + gl.bufferSubData(target, Number(offset), heapu8.subarray(data, data + Number(size)), 0); + }, + glEnableVertexAttribArray: function(index) { + gl.enableVertexAttribArray(index); + }, + glVertexAttribPointer: function(index, size, type, normalized, stride, offset) { + gl.vertexAttribPointer(index, size, type, normalized, stride, offset); + }, + glDisableVertexAttribArray: function(index) { + gl.disableVertexAttribArray(index); + }, + glGetUniformLocation: function(program, name) { + gl_locations.push(gl.getUniformLocation(gl_programs[program], read_string(name))); + return gl_locations.length - 1; + }, + glUniform1i: function(location, v0) { + gl.uniform1i(gl_locations[location], v0); + }, + glUniform2i: function(location, v0, v1) { + gl.uniform2i(gl_locations[location], v0, v1); + }, + glUniform3i: function(location, v0, v1, v2) { + gl.uniform3i(gl_locations[location], v0, v1, v2); + }, + glUniform4i: function(location, v0, v1, v2, v3) { + gl.uniform4i(gl_locations[location], v0, v1, v2, v3); + }, + glUniform1iv: function(location, count, value) { + gl.uniform1iv(gl_locations[location], count, heapi32.subarray(value / 4)); + }, + glUniform2iv: function(location, count, value) { + gl.uniform2iv(gl_locations[location], count, heapi32.subarray(value / 4)); + }, + glUniform3iv: function(location, count, value) { + gl.uniform3iv(gl_locations[location], count, heapi32.subarray(value / 4)); + }, + glUniform4iv: function(location, count, value) { + gl.uniform4iv(gl_locations[location], count, heapi32.subarray(value / 4)); + }, + glUniform1f: function(location, v0) { + gl.uniform1f(gl_locations[location], v0); + }, + glUniform2f: function(location, v0, v1) { + gl.uniform2f(gl_locations[location], v0, v1); + }, + glUniform3f: function(location, v0, v1, v2) { + gl.uniform3f(gl_locations[location], v0, v1, v2); + }, + glUniform4f: function(location, v0, v1, v2, v3) { + gl.uniform4f(gl_locations[location], v0, v1, v2, v3); + }, + glUniform1fv: function(location, count, value) { + var f32 = new Float32Array(memory.buffer, value, count); + gl.uniform1fv(gl_locations[location], f32); + }, + glUniform2fv: function(location, count, value) { + var f32 = new Float32Array(memory.buffer, value, count * 2); + gl.uniform2fv(gl_locations[location], f32); + }, + glUniform3fv: function(location, count, value) { + var f32 = new Float32Array(memory.buffer, value, count * 3); + gl.uniform3fv(gl_locations[location], f32); + }, + glUniform4fv: function(location, count, value) { + var f32 = new Float32Array(memory.buffer, value, count * 4); + gl.uniform4fv(gl_locations[location], f32); + }, + glUniformMatrix3fv: function(location, count, transpose, value) { + var f32 = new Float32Array(memory.buffer, value, 3 * 3); + gl.uniformMatrix3fv(gl_locations[location], transpose, f32); + }, + glUniformMatrix4fv: function(location, count, transpose, value) { + var f32 = new Float32Array(memory.buffer, value, 4 * 4); + gl.uniformMatrix4fv(gl_locations[location], transpose, f32); + }, + glTexParameterf: function(target, pname, param) { + gl.texParameterf(target, pname, param); + }, + glActiveTexture: function(texture) { + gl.activeTexture(texture); + }, + glBindTexture: function(target, texture) { + gl.bindTexture(target, gl_textures[texture]); + }, + glTexParameteri: function(target, pname, param) { + gl.texParameteri(target, pname, param); + }, + glGetActiveUniform: function(program, index, bufSize, length, size, type, name) { + let u = gl.getActiveUniform(gl_programs[program], index); + heapu32[size / 4] = u.size; + heapu32[type / 4] = u.type; + write_string(name, u.name); + }, + glGenTextures: function(n, textures) { + for (let i = 0; i < n; ++i) { + gl_textures.push(gl.createTexture()); + heapu32[textures / 4 + i] = gl_textures.length - 1; + } + }, + glTexImage2D: function(target, level, internalformat, width, height, border, format, type, data) { + let pixels = type == gl.FLOAT ? heapf32.subarray(data / 4) : + type == gl.UNSIGNED_INT ? heapu32.subarray(data / 4) : + type == gl.UNSIGNED_SHORT ? heapu16.subarray(data / 2) : + type == gl.HALF_FLOAT ? heapu16.subarray(data / 2) : heapu8.subarray(data); + gl.texImage2D(target, level, internalformat, width, height, border, format, type, pixels); + }, + glPixelStorei: function(pname, param) { + gl.pixelStorei(pname, param); + }, + glCompressedTexImage2D: function(target, level, internalformat, width, height, border, imageSize, data) { + gl.compressedTexImage2D(target, level, internalformat, width, height, border, imageSize, heapu8.subarray(data)); + }, + glDrawBuffers: function(n, bufs) { + let ar = []; + for (let i = 0; i < n; ++i) { + ar.push(gl.COLOR_ATTACHMENT0 + i); + } + gl.drawBuffers(ar); + }, + glGenerateMipmap: function(target) { + gl.generateMipmap(target); + }, + glFlush: function() { + gl.flush(); + }, + glDeleteBuffers: function(n, buffers) { + for (let i = 0; i < n; ++i) { + gl.deleteBuffer(gl_buffers[heapu32[buffers / 4 + i]]); + } + }, + glDeleteTextures: function(n, textures) { + for (let i = 0; i < n; ++i) { + gl.deleteTexture(gl_textures[heapu32[textures / 4 + i]]); + } + }, + glDeleteFramebuffers: function(n, framebuffers) { + for (let i = 0; i < n; ++i) { + gl.deleteFramebuffer(gl_framebuffers[heapu32[framebuffers / 4 + i]]); + } + }, + glDeleteProgram: function(program) { + gl.deleteProgram(gl_programs[program]); + }, + glDeleteShader: function(shader) { + gl.deleteShader(gl_shaders[shader]); + }, + js_fprintf: function(format) { + console.log(read_string(format)); + }, + js_fopen: function(filename) { + const req = new XMLHttpRequest(); + req.open("GET", read_string(filename), false); + req.overrideMimeType("text/plain; charset=x-user-defined"); + req.send(); + let str = req.response; + file_buffer_pos = 0; + file_buffer = new ArrayBuffer(str.length); + let buf_view = new Uint8Array(file_buffer); + for (let i = 0; i < str.length; ++i) { + buf_view[i] = str.charCodeAt(i); + } + return 1; + }, + js_ftell: function(stream) { + return file_buffer_pos; + }, + js_fseek: function(stream, offset, origin) { + file_buffer_pos = offset; + if (origin == 1) file_buffer_pos += file_buffer.byteLength; // SEEK_END + return 0; + }, + js_fread: function(ptr, size, count, stream) { + let buf_view = new Uint8Array(file_buffer); + for (let i = 0; i < count; ++i) { + heapu8[ptr + i] = buf_view[file_buffer_pos++]; + } + return count; + }, + js_time: function() { + return window.performance.now(); + }, + js_pow: function(x) { + return Math.pow(x); + }, + js_floor: function(x) { + return Math.floor(x); + }, + js_sin: function(x) { + return Math.sin(x); + }, + js_cos: function(x) { + return Math.cos(x); + }, + js_tan: function(x) { + return Math.tan(x); + }, + js_log: function(base, exponent) { + return Math.log(base, exponent); + }, + js_exp: function(x) { + return Math.exp(x); + }, + js_sqrt: function(x) { + return Math.sqrt(x); + }, + js_eval: function(str) { + (1, eval)(read_string(str)); + } + } + } + ); + + mod = result.module; + instance = result.instance; + instance.exports._start(); + + function update() { + instance.exports._update(); + window.requestAnimationFrame(update); + } + window.requestAnimationFrame(update); + + kanvas.addEventListener('click', (event) => { + if (!audio_thread_started) { + start_audio_thread(); + audio_thread_started = true; + } + }); + + kanvas.addEventListener('contextmenu', (event) => { + event.preventDefault(); + }); + + kanvas.addEventListener('mousedown', (event) => { + instance.exports._mousedown(event.button, event.clientX, event.clientY); + }); + + kanvas.addEventListener('mouseup', (event) => { + instance.exports._mouseup(event.button, event.clientX, event.clientY); + }); + + kanvas.addEventListener('mousemove', (event) => { + instance.exports._mousemove(event.clientX, event.clientY); + }); + + kanvas.addEventListener('wheel', (event) => { + instance.exports._wheel(event.deltaY); + }); + + kanvas.addEventListener('keydown', (event) => { + if (event.repeat) { + event.preventDefault(); + return; + } + instance.exports._keydown(event.keyCode); + }); + + kanvas.addEventListener('keyup', (event) => { + if (event.repeat) { + event.preventDefault(); + return; + } + instance.exports._keyup(event.keyCode); + }); +} + +init(); diff --git a/armorcore/sources/backends/data/wasm/JS-Sources/thread_starter.js b/armorcore/sources/backends/data/wasm/JS-Sources/thread_starter.js new file mode 100644 index 000000000..f4e50a851 --- /dev/null +++ b/armorcore/sources/backends/data/wasm/JS-Sources/thread_starter.js @@ -0,0 +1,24 @@ +function create_thread(func) { + +} + +onmessage = function(e) { + console.log('onmessage'); + + const mod = e.data.mod; + const memory = e.data.memory; + const func = e.data.func; + + const importObject = { + env: { memory }, + imports: { + imported_func: arg => console.log('thread: ' + arg), + create_thread + } + }; + + WebAssembly.instantiate(mod, importObject).then((instance) => { + console.log('Running thread'); + instance.exports[func](); + }); +}; diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/Direct3D11.c.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/Direct3D11.c.h new file mode 100644 index 000000000..5806596a5 --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/Direct3D11.c.h @@ -0,0 +1,1226 @@ +#include "Direct3D11.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#undef CreateWindow + +#include + +#include +#include + +#ifdef KINC_WINDOWS +#include +#else +int antialiasingSamples(void) { + return 1; +} +#endif + +// MinGW workaround for missing defines +#ifndef D3D11_MIN_DEPTH +#define D3D11_MIN_DEPTH (0.0f) +#endif + +#ifndef D3D11_MAX_DEPTH +#define D3D11_MAX_DEPTH (1.0f) +#endif + +extern kinc_g4_pipeline_t *currentPipeline; +extern float currentBlendFactor[4]; +extern bool needPipelineRebind; +void kinc_internal_set_constants(void); +void kinc_internal_pipeline_rebind(void); + +bool kinc_internal_scissoring = false; + +// static unsigned hz; +// static bool vsync; + +static D3D_FEATURE_LEVEL featureLevel; +// static IDXGISwapChain *swapChain = NULL; +static D3D11_SAMPLER_DESC lastSamplers[16]; + +struct Sampler { + D3D11_SAMPLER_DESC desc; + ID3D11SamplerState *state; +}; + +#define MAX_SAMPLERS 256 +static struct Sampler samplers[MAX_SAMPLERS]; +static uint32_t samplers_size = 0; + +static ID3D11SamplerState *getSamplerState(D3D11_SAMPLER_DESC *desc) { + for (unsigned i = 0; i < samplers_size; ++i) { + if (memcmp(desc, &samplers[i].desc, sizeof(D3D11_SAMPLER_DESC)) == 0) { + return samplers[i].state; + } + } + struct Sampler s; + s.desc = *desc; + dx_ctx.device->lpVtbl->CreateSamplerState(dx_ctx.device, &s.desc, &s.state); + assert(samplers_size < MAX_SAMPLERS); + samplers[samplers_size++] = s; + return s.state; +} + +static void initSamplers(void) { + D3D11_SAMPLER_DESC samplerDesc; + memset(&samplerDesc, 0, sizeof(samplerDesc)); + samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; + + ID3D11SamplerState *state; + dx_ctx.device->lpVtbl->CreateSamplerState(dx_ctx.device, &samplerDesc, &state); + + ID3D11SamplerState *states[D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT]; + for (int i = 0; i < D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT; ++i) { + states[i] = state; + } + + dx_ctx.context->lpVtbl->VSSetSamplers(dx_ctx.context, 0, D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, states); + dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, 0, D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, states); +} + +static ID3D11RenderTargetView *currentRenderTargetViews[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT]; +static int renderTargetCount = 1; +static ID3D11DepthStencilView *currentDepthStencilView; + +void kinc_g4_internal_destroy_window(int window) {} + +void kinc_g4_internal_destroy(void) {} + +static void createBackbuffer(struct dx_window *window, int antialiasingSamples) { + if (window->depthStencil) { + window->depthStencil->lpVtbl->Release(window->depthStencil); + window->depthStencilView->lpVtbl->Release(window->depthStencilView); + window->renderTargetView->lpVtbl->Release(window->renderTargetView); + window->backBuffer->lpVtbl->Release(window->backBuffer); + window->depthStencil = NULL; + window->depthStencilView = NULL; + window->renderTargetView = NULL; + window->backBuffer = NULL; + } + + window->swapChain->lpVtbl->ResizeBuffers(window->swapChain, 1, window->width, window->height, DXGI_FORMAT_B8G8R8A8_UNORM, 0); + + kinc_microsoft_affirm(window->swapChain->lpVtbl->GetBuffer(window->swapChain, 0, &IID_ID3D11Texture2D, (void **)&window->backBuffer)); + + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)window->backBuffer, NULL, &window->renderTargetView)); + + D3D11_TEXTURE2D_DESC backBufferDesc; + window->backBuffer->lpVtbl->GetDesc(window->backBuffer, &backBufferDesc); + window->width = window->new_width = backBufferDesc.Width; + window->height = window->new_height = backBufferDesc.Height; + + // TODO (DK) map depth/stencilBufferBits arguments + D3D11_TEXTURE2D_DESC depth_stencil_desc; + depth_stencil_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; + depth_stencil_desc.Width = backBufferDesc.Width; + depth_stencil_desc.Height = backBufferDesc.Height; + depth_stencil_desc.ArraySize = 1; + depth_stencil_desc.MipLevels = 1; + depth_stencil_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + depth_stencil_desc.Usage = D3D11_USAGE_DEFAULT; + depth_stencil_desc.CPUAccessFlags = 0; + depth_stencil_desc.SampleDesc.Count = antialiasingSamples > 1 ? antialiasingSamples : 1, + depth_stencil_desc.SampleDesc.Quality = antialiasingSamples > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0; + depth_stencil_desc.MiscFlags = 0; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &depth_stencil_desc, NULL, &window->depthStencil)); + + D3D11_DEPTH_STENCIL_VIEW_DESC depth_stencil_view_desc; + depth_stencil_view_desc.ViewDimension = antialiasingSamples > 1 ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D; + depth_stencil_view_desc.Format = DXGI_FORMAT_UNKNOWN; + depth_stencil_view_desc.Flags = 0; + if (antialiasingSamples <= 1) { + depth_stencil_view_desc.Texture2D.MipSlice = 0; + } + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateDepthStencilView(dx_ctx.device, (ID3D11Resource *)window->depthStencil, &depth_stencil_view_desc, + &window->depthStencilView)); +} + +#ifdef KINC_WINDOWS +static bool isWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) { + OSVERSIONINFOEXW osvi = {sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0}; + DWORDLONG const dwlConditionMask = + VerSetConditionMask(VerSetConditionMask(VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL), VER_MINORVERSION, VER_GREATER_EQUAL), + VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + + osvi.dwMajorVersion = wMajorVersion; + osvi.dwMinorVersion = wMinorVersion; + osvi.wServicePackMajor = wServicePackMajor; + + return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; +} + +#ifndef _WIN32_WINNT_WIN8 +#define _WIN32_WINNT_WIN8 0x0602 +#endif + +#ifndef _WIN32_WINNT_WIN10 +#define _WIN32_WINNT_WIN10 0x0A00 +#endif + +static bool isWindows8OrGreater(void) { + return isWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0); +} + +static bool isWindows10OrGreater(void) { + return isWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN10), LOBYTE(_WIN32_WINNT_WIN10), 0); +} +#endif + +void kinc_g4_internal_init(void) { + D3D_FEATURE_LEVEL featureLevels[] = { + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + }; + UINT flags = 0; + +#ifdef _DEBUG + flags = D3D11_CREATE_DEVICE_DEBUG; +#endif + + IDXGIAdapter *adapter = NULL; + HRESULT result = D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, + &dx_ctx.device, &featureLevel, &dx_ctx.context); + +#ifdef _DEBUG + if (result == E_FAIL || result == DXGI_ERROR_SDK_COMPONENT_MISSING) { + kinc_log(KINC_LOG_LEVEL_WARNING, "%s", "Failed to create device with D3D11_CREATE_DEVICE_DEBUG, trying without"); + flags &= ~D3D11_CREATE_DEVICE_DEBUG; + result = D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, &dx_ctx.device, + &featureLevel, &dx_ctx.context); + } +#endif + + if (result != S_OK) { + kinc_log(KINC_LOG_LEVEL_WARNING, "%s", "Falling back to the WARP driver, things will be slow."); + kinc_microsoft_affirm(D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_WARP, NULL, flags, featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION, + &dx_ctx.device, &featureLevel, &dx_ctx.context)); + } + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->QueryInterface(dx_ctx.device, &IID_IDXGIDevice, (void **)&dx_ctx.dxgiDevice)); + kinc_microsoft_affirm(dx_ctx.dxgiDevice->lpVtbl->GetAdapter(dx_ctx.dxgiDevice, &dx_ctx.dxgiAdapter)); + kinc_microsoft_affirm(dx_ctx.dxgiAdapter->lpVtbl->GetParent(dx_ctx.dxgiAdapter, &IID_IDXGIFactory, (void **)&dx_ctx.dxgiFactory)); +} + +void kinc_g4_internal_init_window(int windowId, int depthBufferBits, int stencilBufferBits, bool vSync) { + for (int i = 0; i < 1024 * 4; ++i) + vertexConstants[i] = 0; + for (int i = 0; i < 1024 * 4; ++i) + fragmentConstants[i] = 0; + + struct dx_window *window = &dx_ctx.windows[windowId]; +#ifdef KINC_WINDOWS + window->hwnd = kinc_windows_window_handle(windowId); +#endif + window->depth_bits = depthBufferBits; + window->stencil_bits = stencilBufferBits; + window->vsync = vSync; + + const int _DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL = 3; + const int _DXGI_SWAP_EFFECT_FLIP_DISCARD = 4; + + DXGI_SWAP_CHAIN_DESC swapChainDesc = {0}; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferDesc.RefreshRate.Denominator = 1; // 60Hz + swapChainDesc.BufferDesc.RefreshRate.Numerator = 60; + swapChainDesc.BufferDesc.Width = kinc_window_width(windowId); // use automatic sizing + swapChainDesc.BufferDesc.Height = kinc_window_height(windowId); + swapChainDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // this is the most common swapchain format + // swapChainDesc.Stereo = false; + swapChainDesc.SampleDesc.Count = kinc_g4_antialiasing_samples() > 1 ? kinc_g4_antialiasing_samples() : 1; + swapChainDesc.SampleDesc.Quality = kinc_g4_antialiasing_samples() > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = 2; // use two buffers to enable flip effect + swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; // DXGI_SCALING_NONE; +#ifdef KINC_WINDOWS + if (isWindows10OrGreater()) { + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + //(DXGI_SWAP_EFFECT) _DXGI_SWAP_EFFECT_FLIP_DISCARD; + } + else if (isWindows8OrGreater()) { + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + //(DXGI_SWAP_EFFECT) _DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + } + else +#endif + { + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + } + swapChainDesc.Flags = 0; + swapChainDesc.OutputWindow = window->hwnd; + swapChainDesc.Windowed = true; + + kinc_microsoft_affirm(dx_ctx.dxgiFactory->lpVtbl->CreateSwapChain(dx_ctx.dxgiFactory, (IUnknown *)dx_ctx.dxgiDevice, &swapChainDesc, &window->swapChain)); + + createBackbuffer(window, kinc_g4_antialiasing_samples()); + currentRenderTargetViews[0] = window->renderTargetView; + currentDepthStencilView = window->depthStencilView; + dx_ctx.context->lpVtbl->OMSetRenderTargets(dx_ctx.context, 1, &window->renderTargetView, window->depthStencilView); + + D3D11_VIEWPORT viewPort; + viewPort.TopLeftX = 0.0f; + viewPort.TopLeftY = 0.0f; + viewPort.Width = (float)window->width; + viewPort.Height = (float)window->height; + viewPort.MinDepth = D3D11_MIN_DEPTH; + viewPort.MaxDepth = D3D11_MAX_DEPTH; + dx_ctx.context->lpVtbl->RSSetViewports(dx_ctx.context, 1, &viewPort); + + D3D11_SAMPLER_DESC samplerDesc; + memset(&samplerDesc, 0, sizeof(samplerDesc)); + samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER; + samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + samplerDesc.MaxLOD = D3D11_FLOAT32_MAX; + + for (int i = 0; i < 16; ++i) { + lastSamplers[i] = samplerDesc; + } + + initSamplers(); + + D3D11_BLEND_DESC blendDesc; + memset(&blendDesc, 0, sizeof(blendDesc)); + + D3D11_RENDER_TARGET_BLEND_DESC rtbd; + memset(&rtbd, 0, sizeof(rtbd)); + + rtbd.BlendEnable = true; + rtbd.SrcBlend = D3D11_BLEND_SRC_ALPHA; + rtbd.DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + rtbd.BlendOp = D3D11_BLEND_OP_ADD; + rtbd.SrcBlendAlpha = D3D11_BLEND_ONE; + rtbd.DestBlendAlpha = D3D11_BLEND_ZERO; + rtbd.BlendOpAlpha = D3D11_BLEND_OP_ADD; + rtbd.RenderTargetWriteMask = D3D10_COLOR_WRITE_ENABLE_ALL; + + blendDesc.AlphaToCoverageEnable = false; + blendDesc.RenderTarget[0] = rtbd; + + ID3D11BlendState *blending; + dx_ctx.device->lpVtbl->CreateBlendState(dx_ctx.device, &blendDesc, &blending); + + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBlendState(dx_ctx.device, &blendDesc, &blending)); + dx_ctx.context->lpVtbl->OMSetBlendState(dx_ctx.context, blending, NULL, 0xffffffff); +} + +void kinc_g4_flush() {} + +static ID3D11ShaderResourceView *nullviews[D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT] = {0}; + +static kinc_g4_index_buffer_t *currentIndexBuffer = NULL; + +void kinc_internal_g4_index_buffer_set(kinc_g4_index_buffer_t *buffer) { + currentIndexBuffer = buffer; + dx_ctx.context->lpVtbl->IASetIndexBuffer(dx_ctx.context, buffer->impl.ib, buffer->impl.sixteen ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); +} + +void kinc_g4_draw_indexed_vertices() { + kinc_internal_pipeline_rebind(); + if (currentPipeline->tessellation_control_shader != NULL) { + dx_ctx.context->lpVtbl->IASetPrimitiveTopology(dx_ctx.context, D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST); + } + else { + dx_ctx.context->lpVtbl->IASetPrimitiveTopology(dx_ctx.context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + } + kinc_internal_set_constants(); + dx_ctx.context->lpVtbl->DrawIndexed(dx_ctx.context, currentIndexBuffer->impl.count, 0, 0); +} + +void kinc_g4_draw_indexed_vertices_from_to(int start, int count) { + kinc_g4_draw_indexed_vertices_from_to_from(start, count, 0); +} + +void kinc_g4_draw_indexed_vertices_from_to_from(int start, int count, int vertex_offset) { + kinc_internal_pipeline_rebind(); + if (currentPipeline->tessellation_control_shader != NULL) { + dx_ctx.context->lpVtbl->IASetPrimitiveTopology(dx_ctx.context, D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST); + } + else { + dx_ctx.context->lpVtbl->IASetPrimitiveTopology(dx_ctx.context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + } + kinc_internal_set_constants(); + dx_ctx.context->lpVtbl->DrawIndexed(dx_ctx.context, count, start, vertex_offset); +} + +void kinc_g4_draw_indexed_vertices_instanced(int instanceCount) { + kinc_g4_draw_indexed_vertices_instanced_from_to(instanceCount, 0, currentIndexBuffer->impl.count); +} + +void kinc_g4_draw_indexed_vertices_instanced_from_to(int instanceCount, int start, int count) { + kinc_internal_pipeline_rebind(); + if (currentPipeline->tessellation_control_shader != NULL) { + dx_ctx.context->lpVtbl->IASetPrimitiveTopology(dx_ctx.context, D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST); + } + else { + dx_ctx.context->lpVtbl->IASetPrimitiveTopology(dx_ctx.context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + } + kinc_internal_set_constants(); + dx_ctx.context->lpVtbl->DrawIndexedInstanced(dx_ctx.context, count, instanceCount, start, 0, 0); + + dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, 0, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, nullviews); +} + +static D3D11_TEXTURE_ADDRESS_MODE convertAddressing(kinc_g4_texture_addressing_t addressing) { + switch (addressing) { + default: + case KINC_G4_TEXTURE_ADDRESSING_REPEAT: + return D3D11_TEXTURE_ADDRESS_WRAP; + case KINC_G4_TEXTURE_ADDRESSING_MIRROR: + return D3D11_TEXTURE_ADDRESS_MIRROR; + case KINC_G4_TEXTURE_ADDRESSING_CLAMP: + return D3D11_TEXTURE_ADDRESS_CLAMP; + case KINC_G4_TEXTURE_ADDRESSING_BORDER: + return D3D11_TEXTURE_ADDRESS_BORDER; + } +} + +void kinc_g4_set_texture_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) { + return; + } + + switch (dir) { + case KINC_G4_TEXTURE_DIRECTION_U: + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].AddressU = convertAddressing(addressing); + break; + case KINC_G4_TEXTURE_DIRECTION_V: + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].AddressV = convertAddressing(addressing); + break; + case KINC_G4_TEXTURE_DIRECTION_W: + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].AddressW = convertAddressing(addressing); + break; + } + + ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]); + dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler); +} + +void kinc_g4_set_texture3d_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) { + kinc_g4_set_texture_addressing(unit, dir, addressing); +} + +int kinc_g4_max_bound_textures(void) { + return D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT; +} + +void kinc_g4_clear(unsigned flags, unsigned color, float depth, int stencil) { + const float clearColor[] = {((color & 0x00ff0000) >> 16) / 255.0f, ((color & 0x0000ff00) >> 8) / 255.0f, (color & 0x000000ff) / 255.0f, + ((color & 0xff000000) >> 24) / 255.0f}; + for (int i = 0; i < renderTargetCount; ++i) { + if (currentRenderTargetViews[i] != NULL && flags & KINC_G4_CLEAR_COLOR) { + dx_ctx.context->lpVtbl->ClearRenderTargetView(dx_ctx.context, currentRenderTargetViews[i], clearColor); + } + } + if (currentDepthStencilView != NULL && (flags & KINC_G4_CLEAR_DEPTH) || (flags & KINC_G4_CLEAR_STENCIL)) { + unsigned d3dflags = ((flags & KINC_G4_CLEAR_DEPTH) ? D3D11_CLEAR_DEPTH : 0) | ((flags & KINC_G4_CLEAR_STENCIL) ? D3D11_CLEAR_STENCIL : 0); + dx_ctx.context->lpVtbl->ClearDepthStencilView(dx_ctx.context, currentDepthStencilView, d3dflags, kinc_clamp(depth, 0.0f, 1.0f), stencil); + } +} + +void kinc_g4_begin(int windowId) { + dx_ctx.current_window = windowId; + struct dx_window *window = &dx_ctx.windows[windowId]; + if (window->new_width != window->width || window->new_height != window->height) { + window->width = window->new_width; + window->height = window->new_height; + createBackbuffer(window, kinc_g4_antialiasing_samples()); + } + kinc_g4_restore_render_target(); +} + +void kinc_g4_viewport(int x, int y, int width, int height) { + D3D11_VIEWPORT viewport; + viewport.TopLeftX = (float)x; + viewport.TopLeftY = (float)y; + viewport.Width = (float)width; + viewport.Height = (float)height; + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + dx_ctx.context->lpVtbl->RSSetViewports(dx_ctx.context, 1, &viewport); +} + +void kinc_internal_set_rasterizer_state(kinc_g4_pipeline_t *pipeline, bool scissoring); + +void kinc_g4_scissor(int x, int y, int width, int height) { + D3D11_RECT rect; + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; + dx_ctx.context->lpVtbl->RSSetScissorRects(dx_ctx.context, 1, &rect); + kinc_internal_scissoring = true; + if (currentPipeline != NULL) { + kinc_internal_set_rasterizer_state(currentPipeline, kinc_internal_scissoring); + } +} + +void kinc_g4_disable_scissor() { + dx_ctx.context->lpVtbl->RSSetScissorRects(dx_ctx.context, 0, NULL); + kinc_internal_scissoring = false; + if (currentPipeline != NULL) { + kinc_internal_set_rasterizer_state(currentPipeline, kinc_internal_scissoring); + } +} + +void kinc_g4_set_pipeline(kinc_g4_pipeline_t *pipeline) { + if (pipeline != currentPipeline) { + currentPipeline = pipeline; + needPipelineRebind = true; + } +} + +void kinc_g4_set_stencil_reference_value(int value) { + if (currentPipeline != NULL) { + dx_ctx.context->lpVtbl->OMSetDepthStencilState(dx_ctx.context, currentPipeline->impl.depthStencilState, value); + } +} + +void kinc_g4_set_blend_constant(float r, float g, float b, float a) { + if (currentBlendFactor[0] != r || currentBlendFactor[1] != g || currentBlendFactor[2] != b || currentBlendFactor[3] != a) { + currentBlendFactor[0] = r; + currentBlendFactor[1] = g; + currentBlendFactor[2] = b; + currentBlendFactor[3] = a; + needPipelineRebind = true; + } +} + +void kinc_g4_end(int windowId) {} + +bool kinc_g4_swap_buffers(void) { + bool success = true; + for (int i = 0; i < MAXIMUM_WINDOWS; i++) { + if (dx_ctx.windows[i].swapChain) { + if (!SUCCEEDED(dx_ctx.windows[i].swapChain->lpVtbl->Present(dx_ctx.windows[i].swapChain, dx_ctx.windows[i].vsync, 0))) { + success = false; + } + } + } + // TODO: if (hr == DXGI_STATUS_OCCLUDED)... + // http://www.pouet.net/topic.php?which=10454 + // "Proper handling of DXGI_STATUS_OCCLUDED would be to pause the application, + // and periodically call Present with the TEST flag, and when it returns S_OK, resume rendering." + + // if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { + // Initialize(m_window); + //} + // else { + return success; + //} +} + +static void setInt(uint8_t *constants, uint32_t offset, uint32_t size, int value) { + if (size == 0) + return; + int *ints = (int *)&constants[offset]; + ints[0] = value; +} + +static void setInt2(uint8_t *constants, uint32_t offset, uint32_t size, int value1, int value2) { + if (size == 0) + return; + int *ints = (int *)&constants[offset]; + ints[0] = value1; + ints[1] = value2; +} + +static void setInt3(uint8_t *constants, uint32_t offset, uint32_t size, int value1, int value2, int value3) { + if (size == 0) + return; + int *ints = (int *)&constants[offset]; + ints[0] = value1; + ints[1] = value2; + ints[2] = value3; +} + +static void setInt4(uint8_t *constants, uint32_t offset, uint32_t size, int value1, int value2, int value3, int value4) { + if (size == 0) + return; + int *ints = (int *)&constants[offset]; + ints[0] = value1; + ints[1] = value2; + ints[2] = value3; + ints[3] = value4; +} + +static void setInts(uint8_t *constants, uint32_t offset, uint32_t size, uint8_t columns, uint8_t rows, int *values, int count) { + if (size == 0) + return; + int *ints = (int *)&constants[offset]; + if (columns == 4 && rows == 4) { + for (int i = 0; i < count / 16 && i < (int)size / 4; ++i) { + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 4; ++x) { + ints[i * 16 + x + y * 4] = values[i * 16 + y + x * 4]; + } + } + } + } + else if (columns == 3 && rows == 3) { + for (int i = 0; i < count / 9 && i < (int)size / 3; ++i) { + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 4; ++x) { + ints[i * 12 + x + y * 4] = values[i * 9 + y + x * 3]; + } + } + } + } + else if (columns == 2 && rows == 2) { + for (int i = 0; i < count / 4 && i < (int)size / 2; ++i) { + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 4; ++x) { + ints[i * 8 + x + y * 4] = values[i * 4 + y + x * 2]; + } + } + } + } + else { + for (int i = 0; i < count && i * 4 < (int)size; ++i) { + ints[i] = values[i]; + } + } +} + +static void setFloat(uint8_t *constants, uint32_t offset, uint32_t size, float value) { + if (size == 0) + return; + float *floats = (float *)&constants[offset]; + floats[0] = value; +} + +static void setFloat2(uint8_t *constants, uint32_t offset, uint32_t size, float value1, float value2) { + if (size == 0) + return; + float *floats = (float *)&constants[offset]; + floats[0] = value1; + floats[1] = value2; +} + +static void setFloat3(uint8_t *constants, uint32_t offset, uint32_t size, float value1, float value2, float value3) { + if (size == 0) + return; + float *floats = (float *)&constants[offset]; + floats[0] = value1; + floats[1] = value2; + floats[2] = value3; +} + +static void setFloat4(uint8_t *constants, uint32_t offset, uint32_t size, float value1, float value2, float value3, float value4) { + if (size == 0) + return; + float *floats = (float *)&constants[offset]; + floats[0] = value1; + floats[1] = value2; + floats[2] = value3; + floats[3] = value4; +} + +static void setFloats(uint8_t *constants, uint32_t offset, uint32_t size, uint8_t columns, uint8_t rows, float *values, int count) { + if (size == 0) + return; + float *floats = (float *)&constants[offset]; + if (columns == 4 && rows == 4) { + for (int i = 0; i < count / 16 && i < (int)size / 4; ++i) { + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 4; ++x) { + floats[i * 16 + x + y * 4] = values[i * 16 + y + x * 4]; + } + } + } + } + else if (columns == 3 && rows == 3) { + for (int i = 0; i < count / 9 && i < (int)size / 3; ++i) { + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 4; ++x) { + floats[i * 12 + x + y * 4] = values[i * 9 + y + x * 3]; + } + } + } + } + else if (columns == 2 && rows == 2) { + for (int i = 0; i < count / 4 && i < (int)size / 2; ++i) { + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 4; ++x) { + floats[i * 8 + x + y * 4] = values[i * 4 + y + x * 2]; + } + } + } + } + else { + for (int i = 0; i < count && i * 4 < (int)size; ++i) { + floats[i] = values[i]; + } + } +} + +static void setBool(uint8_t *constants, uint32_t offset, uint32_t size, bool value) { + if (size == 0) + return; + int *ints = (int *)&constants[offset]; + ints[0] = value ? 1 : 0; +} + +static void setMatrix4(uint8_t *constants, uint32_t offset, uint32_t size, kinc_matrix4x4_t *value) { + if (size == 0) + return; + float *floats = (float *)&constants[offset]; + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 4; ++x) { + floats[x + y * 4] = value->m[y + x * 4]; + } + } +} + +static void setMatrix3(uint8_t *constants, uint32_t offset, uint32_t size, kinc_matrix3x3_t *value) { + if (size == 0) + return; + float *floats = (float *)&constants[offset]; + for (int y = 0; y < 3; ++y) { + for (int x = 0; x < 3; ++x) { + floats[x + y * 4] = value->m[y + x * 3]; + } + } +} + +void kinc_g4_set_int(kinc_g4_constant_location_t location, int value) { + setInt(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value); + setInt(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value); + setInt(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value); + setInt(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value); + setInt(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value); + setInt(computeConstants, location.impl.computeOffset, location.impl.computeSize, value); +} + +void kinc_g4_set_int2(kinc_g4_constant_location_t location, int value1, int value2) { + setInt2(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value1, value2); + setInt2(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value1, value2); + setInt2(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value1, value2); + setInt2(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value1, value2); + setInt2(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value1, value2); + setInt2(computeConstants, location.impl.computeOffset, location.impl.computeSize, value1, value2); +} + +void kinc_g4_set_int3(kinc_g4_constant_location_t location, int value1, int value2, int value3) { + setInt3(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value1, value2, value3); + setInt3(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value1, value2, value3); + setInt3(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value1, value2, value3); + setInt3(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value1, value2, value3); + setInt3(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value1, value2, value3); + setInt3(computeConstants, location.impl.computeOffset, location.impl.computeSize, value1, value2, value3); +} + +void kinc_g4_set_int4(kinc_g4_constant_location_t location, int value1, int value2, int value3, int value4) { + setInt4(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value1, value2, value3, value4); + setInt4(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value1, value2, value3, value4); + setInt4(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value1, value2, value3, value4); + setInt4(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value1, value2, value3, value4); + setInt4(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value1, value2, value3, value4); + setInt4(computeConstants, location.impl.computeOffset, location.impl.computeSize, value1, value2, value3, value4); +} + +void kinc_g4_set_ints(kinc_g4_constant_location_t location, int *values, int count) { + setInts(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, location.impl.vertexColumns, location.impl.vertexRows, values, count); + setInts(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, location.impl.fragmentColumns, location.impl.fragmentRows, values, + count); + setInts(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, location.impl.geometryColumns, location.impl.geometryRows, values, + count); + setInts(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, location.impl.tessEvalColumns, location.impl.tessEvalRows, values, + count); + setInts(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, location.impl.tessControlColumns, + location.impl.tessControlRows, values, count); + setInts(computeConstants, location.impl.computeOffset, location.impl.computeSize, location.impl.computeColumns, location.impl.computeRows, values, count); +} + +void kinc_g4_set_float(kinc_g4_constant_location_t location, float value) { + setFloat(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value); + setFloat(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value); + setFloat(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value); + setFloat(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value); + setFloat(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value); + setFloat(computeConstants, location.impl.computeOffset, location.impl.computeSize, value); +} + +void kinc_g4_set_float2(kinc_g4_constant_location_t location, float value1, float value2) { + setFloat2(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value1, value2); + setFloat2(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value1, value2); + setFloat2(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value1, value2); + setFloat2(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value1, value2); + setFloat2(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value1, value2); + setFloat2(computeConstants, location.impl.computeOffset, location.impl.computeSize, value1, value2); +} + +void kinc_g4_set_float3(kinc_g4_constant_location_t location, float value1, float value2, float value3) { + setFloat3(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value1, value2, value3); + setFloat3(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value1, value2, value3); + setFloat3(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value1, value2, value3); + setFloat3(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value1, value2, value3); + setFloat3(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value1, value2, value3); + setFloat3(computeConstants, location.impl.computeOffset, location.impl.computeSize, value1, value2, value3); +} + +void kinc_g4_set_float4(kinc_g4_constant_location_t location, float value1, float value2, float value3, float value4) { + setFloat4(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value1, value2, value3, value4); + setFloat4(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value1, value2, value3, value4); + setFloat4(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value1, value2, value3, value4); + setFloat4(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value1, value2, value3, value4); + setFloat4(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value1, value2, value3, value4); + setFloat4(computeConstants, location.impl.computeOffset, location.impl.computeSize, value1, value2, value3, value4); +} + +void kinc_g4_set_floats(kinc_g4_constant_location_t location, float *values, int count) { + setFloats(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, location.impl.vertexColumns, location.impl.vertexRows, values, count); + setFloats(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, location.impl.fragmentColumns, location.impl.fragmentRows, values, + count); + setFloats(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, location.impl.geometryColumns, location.impl.geometryRows, values, + count); + setFloats(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, location.impl.tessEvalColumns, location.impl.tessEvalRows, values, + count); + setFloats(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, location.impl.tessControlColumns, + location.impl.tessControlRows, values, count); + setFloats(computeConstants, location.impl.computeOffset, location.impl.computeSize, location.impl.computeColumns, location.impl.computeRows, values, count); +} + +void kinc_g4_set_bool(kinc_g4_constant_location_t location, bool value) { + setBool(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value); + setBool(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value); + setBool(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value); + setBool(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value); + setBool(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value); + setBool(computeConstants, location.impl.computeOffset, location.impl.computeSize, value); +} + +void kinc_g4_set_matrix4(kinc_g4_constant_location_t location, kinc_matrix4x4_t *value) { + setMatrix4(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value); + setMatrix4(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value); + setMatrix4(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value); + setMatrix4(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value); + setMatrix4(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value); + setMatrix4(computeConstants, location.impl.computeOffset, location.impl.computeSize, value); +} + +void kinc_g4_set_matrix3(kinc_g4_constant_location_t location, kinc_matrix3x3_t *value) { + setMatrix3(vertexConstants, location.impl.vertexOffset, location.impl.vertexSize, value); + setMatrix3(fragmentConstants, location.impl.fragmentOffset, location.impl.fragmentSize, value); + setMatrix3(geometryConstants, location.impl.geometryOffset, location.impl.geometrySize, value); + setMatrix3(tessEvalConstants, location.impl.tessEvalOffset, location.impl.tessEvalSize, value); + setMatrix3(tessControlConstants, location.impl.tessControlOffset, location.impl.tessControlSize, value); + setMatrix3(computeConstants, location.impl.computeOffset, location.impl.computeSize, value); +} + +void kinc_g4_set_texture_magnification_filter(kinc_g4_texture_unit_t unit, kinc_g4_texture_filter_t filter) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) + return; + + D3D11_FILTER d3d11filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + + switch (filter) { + case KINC_G4_TEXTURE_FILTER_POINT: + switch (lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter) { + case D3D11_FILTER_MIN_MAG_MIP_POINT: + d3d11filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + break; + case D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR: + case D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT: + case D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR: + d3d11filter = D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; + break; + case D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT: + case D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT: + d3d11filter = D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; + break; + case D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR: + case D3D11_FILTER_MIN_MAG_MIP_LINEAR: + case D3D11_FILTER_ANISOTROPIC: + d3d11filter = D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; + break; + default: + break; + } + break; + case KINC_G4_TEXTURE_FILTER_LINEAR: + switch (lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter) { + case D3D11_FILTER_MIN_MAG_MIP_POINT: + case D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT: + d3d11filter = D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; + break; + case D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR: + d3d11filter = D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; + break; + case D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT: + case D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT: + d3d11filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; + break; + case D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR: + case D3D11_FILTER_MIN_MAG_MIP_LINEAR: + case D3D11_FILTER_ANISOTROPIC: + d3d11filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + break; + default: + break; + } + break; + case KINC_G4_TEXTURE_FILTER_ANISOTROPIC: + d3d11filter = D3D11_FILTER_ANISOTROPIC; + break; + } + + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter = d3d11filter; + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].MaxAnisotropy = d3d11filter == D3D11_FILTER_ANISOTROPIC ? D3D11_REQ_MAXANISOTROPY : 0; + + ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]); + dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler); +} + +void kinc_g4_set_texture3d_magnification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) { + kinc_g4_set_texture_magnification_filter(texunit, filter); +} + +void kinc_g4_set_texture_minification_filter(kinc_g4_texture_unit_t unit, kinc_g4_texture_filter_t filter) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) + return; + + D3D11_FILTER d3d11filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + + switch (filter) { + case KINC_G4_TEXTURE_FILTER_POINT: + switch (lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter) { + case D3D11_FILTER_MIN_MAG_MIP_POINT: + case D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT: + d3d11filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + break; + case D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR: + case D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR: + d3d11filter = D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; + break; + case D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT: + case D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT: + d3d11filter = D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; + break; + case D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR: + case D3D11_FILTER_MIN_MAG_MIP_LINEAR: + case D3D11_FILTER_ANISOTROPIC: + d3d11filter = D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; + break; + default: + break; + } + break; + case KINC_G4_TEXTURE_FILTER_LINEAR: + switch (lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter) { + case D3D11_FILTER_MIN_MAG_MIP_POINT: + case D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT: + d3d11filter = D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; + break; + case D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR: + case D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR: + d3d11filter = D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; + break; + case D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT: + case D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT: + d3d11filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; + break; + case D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR: + case D3D11_FILTER_MIN_MAG_MIP_LINEAR: + case D3D11_FILTER_ANISOTROPIC: + d3d11filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + break; + default: + break; + } + break; + case KINC_G4_TEXTURE_FILTER_ANISOTROPIC: + d3d11filter = D3D11_FILTER_ANISOTROPIC; + break; + default: + break; + } + + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter = d3d11filter; + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].MaxAnisotropy = d3d11filter == D3D11_FILTER_ANISOTROPIC ? D3D11_REQ_MAXANISOTROPY : 0; + + ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]); + dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler); +} + +void kinc_g4_set_texture3d_minification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) { + kinc_g4_set_texture_minification_filter(texunit, filter); +} + +void kinc_g4_set_texture_mipmap_filter(kinc_g4_texture_unit_t unit, kinc_g4_mipmap_filter_t filter) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) + return; + + D3D11_FILTER d3d11filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + + switch (filter) { + case KINC_G4_MIPMAP_FILTER_NONE: + case KINC_G4_MIPMAP_FILTER_POINT: + switch (lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter) { + case D3D11_FILTER_MIN_MAG_MIP_POINT: + case D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR: + d3d11filter = D3D11_FILTER_MIN_MAG_MIP_POINT; + break; + case D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT: + case D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR: + d3d11filter = D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; + break; + case D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT: + case D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR: + d3d11filter = D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; + break; + case D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT: + case D3D11_FILTER_MIN_MAG_MIP_LINEAR: + d3d11filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; + break; + case D3D11_FILTER_ANISOTROPIC: + d3d11filter = D3D11_FILTER_ANISOTROPIC; + break; + default: + break; + } + break; + case KINC_G4_MIPMAP_FILTER_LINEAR: + switch (lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter) { + case D3D11_FILTER_MIN_MAG_MIP_POINT: + case D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR: + d3d11filter = D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; + break; + case D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT: + case D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR: + d3d11filter = D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; + break; + case D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT: + case D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR: + d3d11filter = D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; + break; + case D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT: + case D3D11_FILTER_MIN_MAG_MIP_LINEAR: + d3d11filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + break; + case D3D11_FILTER_ANISOTROPIC: + d3d11filter = D3D11_FILTER_ANISOTROPIC; + break; + default: + break; + } + break; + } + + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter = d3d11filter; + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].MaxAnisotropy = d3d11filter == D3D11_FILTER_ANISOTROPIC ? D3D11_REQ_MAXANISOTROPY : 0; + + ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]); + dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler); +} + +void kinc_g4_set_texture3d_mipmap_filter(kinc_g4_texture_unit_t texunit, kinc_g4_mipmap_filter_t filter) { + kinc_g4_set_texture_mipmap_filter(texunit, filter); +} + +void kinc_g4_set_texture_compare_mode(kinc_g4_texture_unit_t unit, bool enabled) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) + return; + + if (enabled) { + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL; + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].Filter = D3D11_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT; + } + else { + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].ComparisonFunc = D3D11_COMPARISON_NEVER; + } + + ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]); + dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler); +} + +void kinc_g4_set_texture_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) + return; + + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].ComparisonFunc = get_comparison(mode); + + ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]); + dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler); +} + +void kinc_g4_set_cubemap_compare_mode(kinc_g4_texture_unit_t unit, bool enabled) { + kinc_g4_set_texture_compare_mode(unit, enabled); +} + +void kinc_g4_set_cubemap_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode) { + kinc_g4_set_texture_compare_func(unit, mode); +} + +void kinc_g4_set_texture_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) + return; + + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].MaxAnisotropy = max_anisotropy; + + ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]); + dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler); +} + +void kinc_g4_set_cubemap_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy) { + kinc_g4_set_texture_max_anisotropy(unit, max_anisotropy); +} + +void kinc_g4_set_texture_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) + return; + + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].MinLOD = lod_min_clamp; + lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]].MaxLOD = lod_max_clamp; + + ID3D11SamplerState *sampler = getSamplerState(&lastSamplers[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]); + dx_ctx.context->lpVtbl->PSSetSamplers(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &sampler); +} + +void kinc_g4_set_cubemap_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp) { + kinc_g4_set_texture_lod(unit, lod_min_clamp, lod_max_clamp); +} + +void kinc_g4_restore_render_target(void) { + struct dx_window *window = &dx_ctx.windows[dx_ctx.current_window]; + currentRenderTargetViews[0] = window->renderTargetView; + currentDepthStencilView = window->depthStencilView; + dx_ctx.context->lpVtbl->OMSetRenderTargets(dx_ctx.context, 1, &window->renderTargetView, window->depthStencilView); + renderTargetCount = 1; + D3D11_VIEWPORT viewPort; + viewPort.TopLeftX = 0.0f; + viewPort.TopLeftY = 0.0f; + viewPort.Width = (float)window->width; + viewPort.Height = (float)window->height; + viewPort.MinDepth = D3D11_MIN_DEPTH; + viewPort.MaxDepth = D3D11_MAX_DEPTH; + dx_ctx.context->lpVtbl->RSSetViewports(dx_ctx.context, 1, &viewPort); +} + +void kinc_g4_set_render_targets(struct kinc_g4_render_target **targets, int count) { + currentDepthStencilView = targets[0]->impl.depthStencilView[0]; + + renderTargetCount = count; + for (int i = 0; i < count; ++i) { + currentRenderTargetViews[i] = targets[i]->impl.renderTargetViewRender[0]; + } + + dx_ctx.context->lpVtbl->OMSetRenderTargets(dx_ctx.context, count, currentRenderTargetViews, currentDepthStencilView); + D3D11_VIEWPORT viewPort; + viewPort.TopLeftX = 0.0f; + viewPort.TopLeftY = 0.0f; + viewPort.Width = (float)targets[0]->width; + viewPort.Height = (float)targets[0]->height; + viewPort.MinDepth = D3D11_MIN_DEPTH; + viewPort.MaxDepth = D3D11_MAX_DEPTH; + dx_ctx.context->lpVtbl->RSSetViewports(dx_ctx.context, 1, &viewPort); +} + +void kinc_g4_set_render_target_face(struct kinc_g4_render_target *texture, int face) { + renderTargetCount = 1; + currentRenderTargetViews[0] = texture->impl.renderTargetViewRender[face]; + currentDepthStencilView = texture->impl.depthStencilView[face]; + dx_ctx.context->lpVtbl->OMSetRenderTargets(dx_ctx.context, 1, currentRenderTargetViews, currentDepthStencilView); + D3D11_VIEWPORT viewPort; + viewPort.TopLeftX = 0.0f; + viewPort.TopLeftY = 0.0f; + viewPort.Width = (float)texture->width; + viewPort.Height = (float)texture->height; + viewPort.MinDepth = D3D11_MIN_DEPTH; + viewPort.MaxDepth = D3D11_MAX_DEPTH; + dx_ctx.context->lpVtbl->RSSetViewports(dx_ctx.context, 1, &viewPort); +} + +void kinc_g4_set_vertex_buffers(kinc_g4_vertex_buffer_t **buffers, int count) { + kinc_internal_g4_vertex_buffer_set(buffers[0], 0); + + ID3D11Buffer **d3dbuffers = (ID3D11Buffer **)alloca(count * sizeof(ID3D11Buffer *)); + for (int i = 0; i < count; ++i) { + d3dbuffers[i] = buffers[i]->impl.vb; + } + + UINT *strides = (UINT *)alloca(count * sizeof(UINT)); + for (int i = 0; i < count; ++i) { + strides[i] = buffers[i]->impl.stride; + } + + UINT *internaloffsets = (UINT *)alloca(count * sizeof(UINT)); + for (int i = 0; i < count; ++i) { + internaloffsets[i] = 0; + } + + dx_ctx.context->lpVtbl->IASetVertexBuffers(dx_ctx.context, 0, count, d3dbuffers, strides, internaloffsets); +} + +void kinc_g4_set_index_buffer(kinc_g4_index_buffer_t *buffer) { + kinc_internal_g4_index_buffer_set(buffer); +} + +void kinc_internal_texture_set(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit); + +void kinc_g4_set_texture(kinc_g4_texture_unit_t unit, kinc_g4_texture_t *texture) { + kinc_internal_texture_set(texture, unit); +} + +void kinc_internal_texture_set_image(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit); + +void kinc_g4_set_image_texture(kinc_g4_texture_unit_t unit, kinc_g4_texture_t *texture) { + kinc_internal_texture_set_image(texture, unit); +} + +void kinc_internal_resize(int windowId, int width, int height) { + struct dx_window *window = &dx_ctx.windows[windowId]; + window->new_width = width; + window->new_height = height; +} + +void kinc_internal_change_framebuffer(int window_index, kinc_framebuffer_options_t *frame) { + struct dx_window *window = &dx_ctx.windows[window_index]; + kinc_g4_set_antialiasing_samples(frame->samples_per_pixel); + window->vsync = frame->vertical_sync; +} + +bool kinc_window_vsynced(int window_index) { + struct dx_window *window = &dx_ctx.windows[window_index]; + return window->vsync; +} + +bool kinc_g4_supports_instanced_rendering(void) { + return true; +} + +bool kinc_g4_supports_compute_shaders(void) { + return true; +} + +bool kinc_g4_supports_blend_constants(void) { + return true; +} + +bool kinc_g4_supports_non_pow2_textures(void) { + return true; // we always request feature level >= 10 +} + +bool kinc_g4_render_targets_inverted_y(void) { + return false; +} diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/Direct3D11.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/Direct3D11.h new file mode 100644 index 000000000..259c8dc21 --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/Direct3D11.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include + +#define MAXIMUM_WINDOWS 16 + +struct dx_window { + HWND hwnd; + IDXGISwapChain *swapChain; + ID3D11Texture2D *backBuffer; + ID3D11RenderTargetView *renderTargetView; + ID3D11Texture2D *depthStencil; + ID3D11DepthStencilView *depthStencilView; + + int width; + int height; + + int new_width; + int new_height; + + bool vsync; + int depth_bits; + int stencil_bits; +}; + +struct dx_context { + ID3D11Device *device; + ID3D11DeviceContext *context; + IDXGIDevice *dxgiDevice; + IDXGIAdapter *dxgiAdapter; + IDXGIFactory *dxgiFactory; + + int current_window; + struct dx_window windows[MAXIMUM_WINDOWS]; +}; + +extern struct dx_context dx_ctx; + +#include diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/ShaderHash.c.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/ShaderHash.c.h new file mode 100644 index 000000000..bfbd458d4 --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/ShaderHash.c.h @@ -0,0 +1,11 @@ +#include "ShaderHash.h" + +// djb2 +uint32_t kinc_internal_hash_name(unsigned char *str) { + unsigned long hash = 5381; + int c; + while ((c = *str++)) { + hash = hash * 33 ^ c; + } + return hash; +} diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/ShaderHash.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/ShaderHash.h new file mode 100644 index 000000000..2963fd345 --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/ShaderHash.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t hash; + uint32_t index; +} kinc_internal_hash_index_t; + +uint32_t kinc_internal_hash_name(unsigned char *str); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/compute.c.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/compute.c.h new file mode 100644 index 000000000..1ce5d2fe6 --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/compute.c.h @@ -0,0 +1,195 @@ +#include "Direct3D11.h" + +#include +#include +#include +#include + +#include + +#include + +static int getMultipleOf16(int value) { + int ret = 16; + while (ret < value) + ret += 16; + return ret; +} + +void kinc_g4_compute_shader_init(kinc_g4_compute_shader *shader, void *_data, int length) { + unsigned index = 0; + uint8_t *data = (uint8_t *)_data; + + memset(&shader->impl.attributes, 0, sizeof(shader->impl.attributes)); + int attributesCount = data[index++]; + for (int i = 0; i < attributesCount; ++i) { + unsigned char name[256]; + for (unsigned i2 = 0; i2 < 255; ++i2) { + name[i2] = data[index++]; + if (name[i2] == 0) + break; + } + shader->impl.attributes[i].hash = kinc_internal_hash_name(name); + shader->impl.attributes[i].index = data[index++]; + } + + memset(&shader->impl.textures, 0, sizeof(shader->impl.textures)); + uint8_t texCount = data[index++]; + for (unsigned i = 0; i < texCount; ++i) { + unsigned char name[256]; + for (unsigned i2 = 0; i2 < 255; ++i2) { + name[i2] = data[index++]; + if (name[i2] == 0) + break; + } + shader->impl.textures[i].hash = kinc_internal_hash_name(name); + shader->impl.textures[i].index = data[index++]; + } + + memset(&shader->impl.constants, 0, sizeof(shader->impl.constants)); + uint8_t constantCount = data[index++]; + shader->impl.constantsSize = 0; + for (unsigned i = 0; i < constantCount; ++i) { + unsigned char name[256]; + for (unsigned i2 = 0; i2 < 255; ++i2) { + name[i2] = data[index++]; + if (name[i2] == 0) + break; + } + kinc_g4_compute_internal_shader_constant constant; + constant.hash = kinc_internal_hash_name(name); + constant.offset = *(uint32_t *)&data[index]; + index += 4; + constant.size = *(uint32_t *)&data[index]; + index += 4; + constant.columns = data[index]; + index += 1; + constant.rows = data[index]; + index += 1; + + shader->impl.constants[i] = constant; + shader->impl.constantsSize = constant.offset + constant.size; + } + + shader->impl.length = (int)(length - index); + shader->impl.data = (uint8_t *)malloc(shader->impl.length); + assert(shader->impl.data != NULL); + memcpy(shader->impl.data, &data[index], shader->impl.length); + + HRESULT hr = + dx_ctx.device->lpVtbl->CreateComputeShader(dx_ctx.device, shader->impl.data, shader->impl.length, NULL, (ID3D11ComputeShader **)&shader->impl.shader); + + if (hr != S_OK) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Could not initialize compute shader."); + return; + } + + D3D11_BUFFER_DESC desc; + desc.ByteWidth = getMultipleOf16(shader->impl.constantsSize); + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + desc.StructureByteStride = 0; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &desc, NULL, &shader->impl.constantBuffer)); +} + +void kinc_g4_compute_shader_destroy(kinc_g4_compute_shader *shader) {} + +static kinc_g4_compute_internal_shader_constant *compute_findConstant(kinc_g4_compute_internal_shader_constant *constants, uint32_t hash) { + for (int i = 0; i < 64; ++i) { + if (constants[i].hash == hash) { + return &constants[i]; + } + } + return NULL; +} + +static kinc_internal_hash_index_t *compute_findTextureUnit(kinc_internal_hash_index_t *units, uint32_t hash) { + for (int i = 0; i < 64; ++i) { + if (units[i].hash == hash) { + return &units[i]; + } + } + return NULL; +} + +kinc_g4_constant_location_t kinc_g4_compute_shader_get_constant_location(kinc_g4_compute_shader *shader, const char *name) { + kinc_g4_constant_location_t location = {0}; + + uint32_t hash = kinc_internal_hash_name((unsigned char *)name); + + kinc_g4_compute_internal_shader_constant *constant = compute_findConstant(shader->impl.constants, hash); + if (constant == NULL) { + location.impl.computeOffset = 0; + location.impl.computeSize = 0; + location.impl.computeColumns = 0; + location.impl.computeRows = 0; + } + else { + location.impl.computeOffset = constant->offset; + location.impl.computeSize = constant->size; + location.impl.computeColumns = constant->columns; + location.impl.computeRows = constant->rows; + } + + if (location.impl.computeSize == 0) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Uniform %s not found.", name); + } + + return location; +} + +kinc_g4_texture_unit_t kinc_g4_compute_shader_get_texture_unit(kinc_g4_compute_shader *shader, const char *name) { + char unitName[64]; + int unitOffset = 0; + size_t len = strlen(name); + if (len > 63) { + len = 63; + } + strncpy(unitName, name, len + 1); + if (unitName[len - 1] == ']') { // Check for array - mySampler[2] + unitOffset = (int)(unitName[len - 2] - '0'); // Array index is unit offset + unitName[len - 3] = 0; // Strip array from name + } + + uint32_t hash = kinc_internal_hash_name((unsigned char *)unitName); + + kinc_g4_texture_unit_t unit; + for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) { + unit.stages[i] = -1; + } + + kinc_internal_hash_index_t *compute_unit = compute_findTextureUnit(shader->impl.textures, hash); + if (compute_unit == NULL) { + unit.stages[KINC_G4_SHADER_TYPE_COMPUTE] = -1; +#ifndef NDEBUG + static int notFoundCount = 0; + if (notFoundCount < 10) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Sampler %s not found.", unitName); + ++notFoundCount; + } + else if (notFoundCount == 10) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Giving up on sampler not found messages.", unitName); + ++notFoundCount; + } +#endif + } + else { + unit.stages[KINC_G4_SHADER_TYPE_COMPUTE] = compute_unit->index + unitOffset; + } + return unit; +} + +void kinc_g4_set_compute_shader(kinc_g4_compute_shader *shader) { + dx_ctx.context->lpVtbl->CSSetShader(dx_ctx.context, (ID3D11ComputeShader *)shader->impl.shader, NULL, 0); + dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)shader->impl.constantBuffer, 0, NULL, computeConstants, 0, 0); + dx_ctx.context->lpVtbl->CSSetConstantBuffers(dx_ctx.context, 0, 1, &shader->impl.constantBuffer); +} + +void kinc_g4_compute(int x, int y, int z) { + dx_ctx.context->lpVtbl->Dispatch(dx_ctx.context, x, y, z); + + ID3D11UnorderedAccessView *nullView = NULL; + dx_ctx.context->lpVtbl->CSSetUnorderedAccessViews(dx_ctx.context, 0, 1, &nullView, NULL); +} diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/compute.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/compute.h new file mode 100644 index 000000000..b85d053cd --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/compute.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ID3D11Buffer; + +typedef struct kinc_g4_compute_constant_location_impl { + uint32_t offset; + uint32_t size; + uint8_t columns; + uint8_t rows; +} kinc_g4_compute_constant_location_impl; + +typedef struct kinc_g4_compute_texture_unit_impl { + int unit; +} kinc_g4_compute_texture_unit_impl; + +typedef struct kinc_g4_compute_internal_shader_constant { + uint32_t hash; + uint32_t offset; + uint32_t size; + uint8_t columns; + uint8_t rows; +} kinc_g4_compute_internal_shader_constant; + +typedef struct kinc_g4_compute_shader_impl { + kinc_g4_compute_internal_shader_constant constants[64]; + int constantsSize; + kinc_internal_hash_index_t attributes[64]; + kinc_internal_hash_index_t textures[64]; + struct ID3D11Buffer *constantBuffer; + void *shader; + uint8_t *data; + int length; +} kinc_g4_compute_shader_impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/d3d11unit.c b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/d3d11unit.c new file mode 100644 index 000000000..424c2c23e --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/d3d11unit.c @@ -0,0 +1,107 @@ +// Windows 7 +#define WINVER 0x0601 +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0601 + +#define NOATOM +#define NOCLIPBOARD +#define NOCOLOR +#define NOCOMM +#define NOCTLMGR +#define NODEFERWINDOWPOS +#define NODRAWTEXT +#define NOGDI +#define NOGDICAPMASKS +#define NOHELP +#define NOICONS +#define NOKANJI +#define NOKEYSTATES +#define NOMB +#define NOMCX +#define NOMEMMGR +#define NOMENUS +#define NOMETAFILE +#define NOMINMAX +// #define NOMSG +#define NONLS +#define NOOPENFILE +#define NOPROFILER +#define NORASTEROPS +#define NOSCROLL +#define NOSERVICE +#define NOSHOWWINDOW +#define NOSOUND +#define NOSYSCOMMANDS +#define NOSYSMETRICS +#define NOTEXTMETRIC +// #define NOUSER +#define NOVIRTUALKEYCODES +#define NOWH +#define NOWINMESSAGES +#define NOWINOFFSETS +#define NOWINSTYLES +#define WIN32_LEAN_AND_MEAN + +#include + +#include + +#pragma warning(disable : 4005) +#include + +#include "Direct3D11.h" + +#include +#include +#include + +struct dx_context dx_ctx = {0}; + +static uint8_t vertexConstants[1024 * 4]; +static uint8_t fragmentConstants[1024 * 4]; +static uint8_t geometryConstants[1024 * 4]; +static uint8_t tessControlConstants[1024 * 4]; +static uint8_t tessEvalConstants[1024 * 4]; +static uint8_t computeConstants[1024 * 4]; + +static D3D11_COMPARISON_FUNC get_comparison(kinc_g4_compare_mode_t compare) { + switch (compare) { + default: + case KINC_G4_COMPARE_ALWAYS: + return D3D11_COMPARISON_ALWAYS; + case KINC_G4_COMPARE_NEVER: + return D3D11_COMPARISON_NEVER; + case KINC_G4_COMPARE_EQUAL: + return D3D11_COMPARISON_EQUAL; + case KINC_G4_COMPARE_NOT_EQUAL: + return D3D11_COMPARISON_NOT_EQUAL; + case KINC_G4_COMPARE_LESS: + return D3D11_COMPARISON_LESS; + case KINC_G4_COMPARE_LESS_EQUAL: + return D3D11_COMPARISON_LESS_EQUAL; + case KINC_G4_COMPARE_GREATER: + return D3D11_COMPARISON_GREATER; + case KINC_G4_COMPARE_GREATER_EQUAL: + return D3D11_COMPARISON_GREATER_EQUAL; + } +} + +static size_t get_multiple_of_16(size_t value) { + size_t ret = 16; + while (ret < value) { + ret += 16; + } + return ret; +} + +#include "Direct3D11.c.h" +#include "ShaderHash.c.h" +#include "compute.c.h" +#include "indexbuffer.c.h" +#include "pipeline.c.h" +#include "rendertarget.c.h" +#include "shader.c.h" +#include "texture.c.h" +#include "vertexbuffer.c.h" diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/indexbuffer.c.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/indexbuffer.c.h new file mode 100644 index 000000000..a3d843c4d --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/indexbuffer.c.h @@ -0,0 +1,89 @@ +#include + +void kinc_g4_index_buffer_init(kinc_g4_index_buffer_t *buffer, int count, kinc_g4_index_buffer_format_t format, kinc_g4_usage_t usage) { + buffer->impl.count = count; + buffer->impl.sixteen = format == KINC_G4_INDEX_BUFFER_FORMAT_16BIT; + buffer->impl.last_start = 0; + buffer->impl.last_count = count; + + uint32_t byte_size = buffer->impl.sixteen ? sizeof(uint16_t) * count : sizeof(uint32_t) * count; + + if (usage == KINC_G4_USAGE_DYNAMIC) { + buffer->impl.indices = NULL; + } + else { + buffer->impl.indices = malloc(byte_size); + } + + D3D11_BUFFER_DESC bufferDesc; + bufferDesc.Usage = D3D11_USAGE_DEFAULT; + bufferDesc.ByteWidth = byte_size; + bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; + bufferDesc.CPUAccessFlags = 0; + bufferDesc.MiscFlags = 0; + bufferDesc.StructureByteStride = 0; + + buffer->impl.usage = usage; + switch (usage) { + case KINC_G4_USAGE_STATIC: + bufferDesc.Usage = D3D11_USAGE_DEFAULT; + break; + case KINC_G4_USAGE_DYNAMIC: + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + break; + case KINC_G4_USAGE_READABLE: + bufferDesc.Usage = D3D11_USAGE_DEFAULT; + break; + } + + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &bufferDesc, NULL, &buffer->impl.ib)); +} + +void kinc_g4_index_buffer_destroy(kinc_g4_index_buffer_t *buffer) { + buffer->impl.ib->lpVtbl->Release(buffer->impl.ib); + free(buffer->impl.indices); + buffer->impl.indices = NULL; +} + +static int kinc_g4_internal_index_buffer_stride(kinc_g4_index_buffer_t *buffer) { + return buffer->impl.sixteen ? 2 : 4; +} + +void *kinc_g4_index_buffer_lock_all(kinc_g4_index_buffer_t *buffer) { + return kinc_g4_index_buffer_lock(buffer, 0, kinc_g4_index_buffer_count(buffer)); +} + +void *kinc_g4_index_buffer_lock(kinc_g4_index_buffer_t *buffer, int start, int count) { + buffer->impl.last_start = start; + buffer->impl.last_count = count; + + if (buffer->impl.usage == KINC_G4_USAGE_DYNAMIC) { + D3D11_MAPPED_SUBRESOURCE mappedResource; + memset(&mappedResource, 0, sizeof(D3D11_MAPPED_SUBRESOURCE)); + dx_ctx.context->lpVtbl->Map(dx_ctx.context, (ID3D11Resource *)buffer->impl.ib, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); + uint8_t *data = (uint8_t *)mappedResource.pData; + return &data[start * kinc_g4_internal_index_buffer_stride(buffer)]; + } + else { + uint8_t *data = (uint8_t *)buffer->impl.indices; + return &data[start * kinc_g4_internal_index_buffer_stride(buffer)]; + } +} + +void kinc_g4_index_buffer_unlock_all(kinc_g4_index_buffer_t *buffer) { + kinc_g4_index_buffer_unlock(buffer, buffer->impl.last_count); +} + +void kinc_g4_index_buffer_unlock(kinc_g4_index_buffer_t *buffer, int count) { + if (buffer->impl.usage == KINC_G4_USAGE_DYNAMIC) { + dx_ctx.context->lpVtbl->Unmap(dx_ctx.context, (ID3D11Resource *)buffer->impl.ib, 0); + } + else { + dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)buffer->impl.ib, 0, NULL, buffer->impl.indices, 0, 0); + } +} + +int kinc_g4_index_buffer_count(kinc_g4_index_buffer_t *buffer) { + return buffer->impl.count; +} diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/indexbuffer.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/indexbuffer.h new file mode 100644 index 000000000..bd6903d28 --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/indexbuffer.h @@ -0,0 +1,13 @@ +#pragma once + +struct ID3D11Buffer; + +typedef struct { + struct ID3D11Buffer *ib; + void *indices; + int count; + int usage; + bool sixteen; + int last_start; + int last_count; +} kinc_g4_index_buffer_impl_t; diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/pipeline.c.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/pipeline.c.h new file mode 100644 index 000000000..bbb84b4fc --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/pipeline.c.h @@ -0,0 +1,866 @@ +#include +#include +#include +#include +#include + +kinc_g4_pipeline_t *currentPipeline = NULL; +float currentBlendFactor[4] = {0, 0, 0, 0}; +bool needPipelineRebind = true; + +static D3D11_CULL_MODE convert_cull_mode(kinc_g4_cull_mode_t cullMode) { + switch (cullMode) { + case KINC_G4_CULL_CLOCKWISE: + return D3D11_CULL_BACK; + case KINC_G4_CULL_COUNTER_CLOCKWISE: + return D3D11_CULL_FRONT; + case KINC_G4_CULL_NOTHING: + return D3D11_CULL_NONE; + default: + assert(false); + return D3D11_CULL_NONE; + } +} + +static D3D11_BLEND convert_blend_factor(kinc_g4_blending_factor_t factor) { + switch (factor) { + case KINC_G4_BLEND_ONE: + return D3D11_BLEND_ONE; + case KINC_G4_BLEND_ZERO: + return D3D11_BLEND_ZERO; + case KINC_G4_BLEND_SOURCE_ALPHA: + return D3D11_BLEND_SRC_ALPHA; + case KINC_G4_BLEND_DEST_ALPHA: + return D3D11_BLEND_DEST_ALPHA; + case KINC_G4_BLEND_INV_SOURCE_ALPHA: + return D3D11_BLEND_INV_SRC_ALPHA; + case KINC_G4_BLEND_INV_DEST_ALPHA: + return D3D11_BLEND_INV_DEST_ALPHA; + case KINC_G4_BLEND_SOURCE_COLOR: + return D3D11_BLEND_SRC_COLOR; + case KINC_G4_BLEND_DEST_COLOR: + return D3D11_BLEND_DEST_COLOR; + case KINC_G4_BLEND_INV_SOURCE_COLOR: + return D3D11_BLEND_INV_SRC_COLOR; + case KINC_G4_BLEND_INV_DEST_COLOR: + return D3D11_BLEND_INV_DEST_COLOR; + default: + assert(false); + return D3D11_BLEND_SRC_ALPHA; + } +} + +static D3D11_BLEND_OP convert_blend_operation(kinc_g4_blending_operation_t operation) { + switch (operation) { + case KINC_G4_BLENDOP_ADD: + return D3D11_BLEND_OP_ADD; + case KINC_G4_BLENDOP_SUBTRACT: + return D3D11_BLEND_OP_SUBTRACT; + case KINC_G4_BLENDOP_REVERSE_SUBTRACT: + return D3D11_BLEND_OP_REV_SUBTRACT; + case KINC_G4_BLENDOP_MIN: + return D3D11_BLEND_OP_MIN; + case KINC_G4_BLENDOP_MAX: + return D3D11_BLEND_OP_MAX; + default: + assert(false); + return D3D11_BLEND_OP_ADD; + } +} + +static D3D11_STENCIL_OP get_stencil_action(kinc_g4_stencil_action_t action) { + switch (action) { + default: + case KINC_G4_STENCIL_KEEP: + return D3D11_STENCIL_OP_KEEP; + case KINC_G4_STENCIL_ZERO: + return D3D11_STENCIL_OP_ZERO; + case KINC_G4_STENCIL_REPLACE: + return D3D11_STENCIL_OP_REPLACE; + case KINC_G4_STENCIL_INCREMENT: + return D3D11_STENCIL_OP_INCR; + case KINC_G4_STENCIL_INCREMENT_WRAP: + return D3D11_STENCIL_OP_INCR_SAT; + case KINC_G4_STENCIL_DECREMENT: + return D3D11_STENCIL_OP_DECR; + case KINC_G4_STENCIL_DECREMENT_WRAP: + return D3D11_STENCIL_OP_DECR_SAT; + case KINC_G4_STENCIL_INVERT: + return D3D11_STENCIL_OP_INVERT; + } +} + +void kinc_internal_set_constants(void) { + if (currentPipeline->vertex_shader->impl.constantsSize > 0) { + dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)currentPipeline->impl.vertexConstantBuffer, 0, NULL, vertexConstants, 0, 0); + dx_ctx.context->lpVtbl->VSSetConstantBuffers(dx_ctx.context, 0, 1, ¤tPipeline->impl.vertexConstantBuffer); + } + if (currentPipeline->fragment_shader->impl.constantsSize > 0) { + dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)currentPipeline->impl.fragmentConstantBuffer, 0, NULL, fragmentConstants, 0, + 0); + dx_ctx.context->lpVtbl->PSSetConstantBuffers(dx_ctx.context, 0, 1, ¤tPipeline->impl.fragmentConstantBuffer); + } + if (currentPipeline->geometry_shader != NULL && currentPipeline->geometry_shader->impl.constantsSize > 0) { + dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)currentPipeline->impl.geometryConstantBuffer, 0, NULL, geometryConstants, 0, + 0); + dx_ctx.context->lpVtbl->GSSetConstantBuffers(dx_ctx.context, 0, 1, ¤tPipeline->impl.geometryConstantBuffer); + } + if (currentPipeline->tessellation_control_shader != NULL && currentPipeline->tessellation_control_shader->impl.constantsSize > 0) { + dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)currentPipeline->impl.tessControlConstantBuffer, 0, NULL, + tessControlConstants, 0, 0); + dx_ctx.context->lpVtbl->HSSetConstantBuffers(dx_ctx.context, 0, 1, ¤tPipeline->impl.tessControlConstantBuffer); + } + if (currentPipeline->tessellation_evaluation_shader != NULL && currentPipeline->tessellation_evaluation_shader->impl.constantsSize > 0) { + dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)currentPipeline->impl.tessEvalConstantBuffer, 0, NULL, tessEvalConstants, 0, + 0); + dx_ctx.context->lpVtbl->DSSetConstantBuffers(dx_ctx.context, 0, 1, ¤tPipeline->impl.tessEvalConstantBuffer); + } +} + +void kinc_g4_pipeline_init(struct kinc_g4_pipeline *state) { + memset(state, 0, sizeof(struct kinc_g4_pipeline)); + kinc_g4_internal_pipeline_set_defaults(state); + state->impl.d3d11inputLayout = NULL; + state->impl.fragmentConstantBuffer = NULL; + state->impl.vertexConstantBuffer = NULL; + state->impl.geometryConstantBuffer = NULL; + state->impl.tessEvalConstantBuffer = NULL; + state->impl.tessControlConstantBuffer = NULL; + state->impl.depthStencilState = NULL; + state->impl.rasterizerState = NULL; + state->impl.rasterizerStateScissor = NULL; + state->impl.blendState = NULL; +} + +void kinc_g4_pipeline_destroy(struct kinc_g4_pipeline *state) { + if (state->impl.d3d11inputLayout != NULL) { + state->impl.d3d11inputLayout->lpVtbl->Release(state->impl.d3d11inputLayout); + state->impl.d3d11inputLayout = NULL; + } + if (state->impl.fragmentConstantBuffer != NULL) { + state->impl.fragmentConstantBuffer->lpVtbl->Release(state->impl.fragmentConstantBuffer); + state->impl.fragmentConstantBuffer = NULL; + } + if (state->impl.vertexConstantBuffer != NULL) { + state->impl.vertexConstantBuffer->lpVtbl->Release(state->impl.vertexConstantBuffer); + state->impl.vertexConstantBuffer = NULL; + } + if (state->impl.geometryConstantBuffer != NULL) { + state->impl.geometryConstantBuffer->lpVtbl->Release(state->impl.geometryConstantBuffer); + state->impl.geometryConstantBuffer = NULL; + } + if (state->impl.tessEvalConstantBuffer != NULL) { + state->impl.tessEvalConstantBuffer->lpVtbl->Release(state->impl.tessEvalConstantBuffer); + state->impl.tessEvalConstantBuffer = NULL; + } + if (state->impl.tessControlConstantBuffer != NULL) { + state->impl.tessControlConstantBuffer->lpVtbl->Release(state->impl.tessControlConstantBuffer); + state->impl.tessControlConstantBuffer = NULL; + } + if (state->impl.depthStencilState != NULL) { + state->impl.depthStencilState->lpVtbl->Release(state->impl.depthStencilState); + state->impl.depthStencilState = NULL; + } + if (state->impl.rasterizerState != NULL) { + state->impl.rasterizerState->lpVtbl->Release(state->impl.rasterizerState); + state->impl.rasterizerState = NULL; + } + if (state->impl.rasterizerStateScissor != NULL) { + state->impl.rasterizerStateScissor->lpVtbl->Release(state->impl.rasterizerStateScissor); + state->impl.rasterizerStateScissor = NULL; + } + if (state->impl.blendState != NULL) { + state->impl.blendState->lpVtbl->Release(state->impl.blendState); + state->impl.blendState = NULL; + } +} + +void kinc_internal_set_rasterizer_state(struct kinc_g4_pipeline *pipeline, bool scissoring) { + if (scissoring && pipeline->impl.rasterizerStateScissor != NULL) + dx_ctx.context->lpVtbl->RSSetState(dx_ctx.context, pipeline->impl.rasterizerStateScissor); + else if (pipeline->impl.rasterizerState != NULL) + dx_ctx.context->lpVtbl->RSSetState(dx_ctx.context, pipeline->impl.rasterizerState); +} + +void kinc_internal_set_pipeline(struct kinc_g4_pipeline *pipeline, bool scissoring) { + dx_ctx.context->lpVtbl->OMSetDepthStencilState(dx_ctx.context, pipeline->impl.depthStencilState, pipeline->stencil_reference_value); + UINT sampleMask = 0xffffffff; + dx_ctx.context->lpVtbl->OMSetBlendState(dx_ctx.context, pipeline->impl.blendState, currentBlendFactor, sampleMask); + kinc_internal_set_rasterizer_state(pipeline, scissoring); + + dx_ctx.context->lpVtbl->VSSetShader(dx_ctx.context, (ID3D11VertexShader *)pipeline->vertex_shader->impl.shader, NULL, 0); + dx_ctx.context->lpVtbl->PSSetShader(dx_ctx.context, (ID3D11PixelShader *)pipeline->fragment_shader->impl.shader, NULL, 0); + + dx_ctx.context->lpVtbl->GSSetShader(dx_ctx.context, + pipeline->geometry_shader != NULL ? (ID3D11GeometryShader *)pipeline->geometry_shader->impl.shader : NULL, NULL, 0); + dx_ctx.context->lpVtbl->HSSetShader( + dx_ctx.context, pipeline->tessellation_control_shader != NULL ? (ID3D11HullShader *)pipeline->tessellation_control_shader->impl.shader : NULL, NULL, 0); + dx_ctx.context->lpVtbl->DSSetShader( + dx_ctx.context, pipeline->tessellation_evaluation_shader != NULL ? (ID3D11DomainShader *)pipeline->tessellation_evaluation_shader->impl.shader : NULL, + NULL, 0); + + dx_ctx.context->lpVtbl->IASetInputLayout(dx_ctx.context, pipeline->impl.d3d11inputLayout); +} + +void kinc_internal_pipeline_rebind() { + if (currentPipeline != NULL && needPipelineRebind) { + kinc_internal_set_pipeline(currentPipeline, kinc_internal_scissoring); + needPipelineRebind = false; + } +} + +static kinc_internal_shader_constant_t *findConstant(kinc_internal_shader_constant_t *constants, uint32_t hash) { + for (int i = 0; i < 64; ++i) { + if (constants[i].hash == hash) { + return &constants[i]; + } + } + return NULL; +} + +static kinc_internal_hash_index_t *findTextureUnit(kinc_internal_hash_index_t *units, uint32_t hash) { + for (int i = 0; i < 64; ++i) { + if (units[i].hash == hash) { + return &units[i]; + } + } + return NULL; +} + +void kinc_g4_pipeline_get_constant_locations(kinc_g4_pipeline_t *state, kinc_g4_constant_location_t *vertex_locations, + kinc_g4_constant_location_t *fragment_locations, int *vertex_sizes, int *fragment_sizes, int *max_vertex, + int *max_fragment) { + + // *max_vertex = state->vertex_shader->impl.constantsSize; + // *max_fragment = state->fragment_shader->impl.constantsSize; + // if (vertex_locations != null && fragment_locations != null) { + // for (int i = 0; i < state->vertex_shader->impl.constantsSize; i++) { + // kinc_internal_shader_constant_t *constant = state->vertex_shader->impl.constants[i]; + // vertex_location[i].impl.vertexOffset = constant->offset; + // vertex_location[i].impl.vertexSize = constant->size; + // vertex_location[i].impl.vertexColumns = constant->columns; + // vertex_location[i].impl.vertexRows = constant->rows; + // vertex_sizes[i] = constant->size; + // } + + // for (int i = 0; i < state->fragment_shader->impl.constantsSize; i++) { + // kinc_internal_shader_constant_t *constant = state->fragment_shader->impl.constants[i]; + // fragment_location[i].impl.vertexOffset = constant->offset; + // fragment_location[i].impl.vertexSize = constant->size; + // fragment_location[i].impl.vertexColumns = constant->columns; + // fragment_location[i].impl.vertexRows = constant->rows; + // fragment_sizes[i] = constant->size; + // } + // } +} + +kinc_g4_constant_location_t kinc_g4_pipeline_get_constant_location(struct kinc_g4_pipeline *state, const char *name) { + kinc_g4_constant_location_t location = {0}; + + uint32_t hash = kinc_internal_hash_name((unsigned char *)name); + + kinc_internal_shader_constant_t *constant = findConstant(state->vertex_shader->impl.constants, hash); + if (constant == NULL) { + location.impl.vertexOffset = 0; + location.impl.vertexSize = 0; + location.impl.vertexColumns = 0; + location.impl.vertexRows = 0; + } + else { + location.impl.vertexOffset = constant->offset; + location.impl.vertexSize = constant->size; + location.impl.vertexColumns = constant->columns; + location.impl.vertexRows = constant->rows; + } + + constant = findConstant(state->fragment_shader->impl.constants, hash); + if (constant == NULL) { + location.impl.fragmentOffset = 0; + location.impl.fragmentSize = 0; + location.impl.fragmentColumns = 0; + location.impl.fragmentRows = 0; + } + else { + location.impl.fragmentOffset = constant->offset; + location.impl.fragmentSize = constant->size; + location.impl.fragmentColumns = constant->columns; + location.impl.fragmentRows = constant->rows; + } + + constant = state->geometry_shader == NULL ? NULL : findConstant(state->geometry_shader->impl.constants, hash); + if (constant == NULL) { + location.impl.geometryOffset = 0; + location.impl.geometrySize = 0; + location.impl.geometryColumns = 0; + location.impl.geometryRows = 0; + } + else { + location.impl.geometryOffset = constant->offset; + location.impl.geometrySize = constant->size; + location.impl.geometryColumns = constant->columns; + location.impl.geometryRows = constant->rows; + } + + constant = state->tessellation_control_shader == NULL ? NULL : findConstant(state->tessellation_control_shader->impl.constants, hash); + if (constant == NULL) { + location.impl.tessControlOffset = 0; + location.impl.tessControlSize = 0; + location.impl.tessControlColumns = 0; + location.impl.tessControlRows = 0; + } + else { + location.impl.tessControlOffset = constant->offset; + location.impl.tessControlSize = constant->size; + location.impl.tessControlColumns = constant->columns; + location.impl.tessControlRows = constant->rows; + } + + constant = state->tessellation_evaluation_shader == NULL ? NULL : findConstant(state->tessellation_evaluation_shader->impl.constants, hash); + if (constant == NULL) { + location.impl.tessEvalOffset = 0; + location.impl.tessEvalSize = 0; + location.impl.tessEvalColumns = 0; + location.impl.tessEvalRows = 0; + } + else { + location.impl.tessEvalOffset = constant->offset; + location.impl.tessEvalSize = constant->size; + location.impl.tessEvalColumns = constant->columns; + location.impl.tessEvalRows = constant->rows; + } + + if (location.impl.vertexSize == 0 && location.impl.fragmentSize == 0 && location.impl.geometrySize == 0 && location.impl.tessControlSize == 0 && + location.impl.tessEvalSize == 0) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Uniform %s not found.", name); + } + + return location; +} + +kinc_g4_texture_unit_t kinc_g4_pipeline_get_texture_unit(struct kinc_g4_pipeline *state, const char *name) { + char unitName[64]; + int unitOffset = 0; + size_t len = strlen(name); + if (len > 63) + len = 63; + strncpy(unitName, name, len + 1); + if (unitName[len - 1] == ']') { // Check for array - mySampler[2] + unitOffset = (int)(unitName[len - 2] - '0'); // Array index is unit offset + unitName[len - 3] = 0; // Strip array from name + } + + uint32_t hash = kinc_internal_hash_name((unsigned char *)unitName); + + kinc_g4_texture_unit_t unit; + for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) { + unit.stages[i] = -1; + } + + kinc_internal_hash_index_t *fragmentUnit = findTextureUnit(state->fragment_shader->impl.textures, hash); + if (fragmentUnit != NULL) { + unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] = fragmentUnit->index + unitOffset; + } + + kinc_internal_hash_index_t *vertexUnit = findTextureUnit(state->vertex_shader->impl.textures, hash); + if (vertexUnit != NULL) { + unit.stages[KINC_G4_SHADER_TYPE_VERTEX] = vertexUnit->index + unitOffset; + } + + return unit; +} + +static char stringCache[1024]; +static int stringCacheIndex = 0; + +static void setVertexDesc(D3D11_INPUT_ELEMENT_DESC *vertexDesc, int attributeIndex, int index, int stream, bool instanced, int subindex) { + if (subindex < 0) { + vertexDesc->SemanticName = "TEXCOORD"; + vertexDesc->SemanticIndex = attributeIndex; + } + else { + // SPIRV_CROSS uses TEXCOORD_0_0,... for split up matrices + int stringStart = stringCacheIndex; + strcpy(&stringCache[stringCacheIndex], "TEXCOORD"); + stringCacheIndex += (int)strlen("TEXCOORD"); + sprintf(&stringCache[stringCacheIndex], "%i", attributeIndex); + stringCacheIndex += (int)strlen(&stringCache[stringCacheIndex]); + strcpy(&stringCache[stringCacheIndex], "_"); + stringCacheIndex += 2; + vertexDesc->SemanticName = &stringCache[stringStart]; + vertexDesc->SemanticIndex = subindex; + } + vertexDesc->InputSlot = stream; + vertexDesc->AlignedByteOffset = (index == 0) ? 0 : D3D11_APPEND_ALIGNED_ELEMENT; + vertexDesc->InputSlotClass = instanced ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA; + vertexDesc->InstanceDataStepRate = instanced ? 1 : 0; +} + +#define usedCount 32 + +static int getAttributeLocation(kinc_internal_hash_index_t *attributes, const char *name, bool *used) { + uint32_t hash = kinc_internal_hash_name((unsigned char *)name); + + for (int i = 0; i < 64; ++i) { + if (attributes[i].hash == hash) { + return attributes[i].index; + } + } + + for (int i = 0; i < usedCount; ++i) { + if (!used[i]) { + used[i] = true; + return i; + } + } + + return 0; +} + +static void createRenderTargetBlendDesc(struct kinc_g4_pipeline *pipe, D3D11_RENDER_TARGET_BLEND_DESC *rtbd, int targetNum) { + rtbd->BlendEnable = pipe->blend_source != KINC_G4_BLEND_ONE || pipe->blend_destination != KINC_G4_BLEND_ZERO || + pipe->alpha_blend_source != KINC_G4_BLEND_ONE || pipe->alpha_blend_destination != KINC_G4_BLEND_ZERO; + rtbd->SrcBlend = convert_blend_factor(pipe->blend_source); + rtbd->DestBlend = convert_blend_factor(pipe->blend_destination); + rtbd->BlendOp = convert_blend_operation(pipe->blend_operation); + rtbd->SrcBlendAlpha = convert_blend_factor(pipe->alpha_blend_source); + rtbd->DestBlendAlpha = convert_blend_factor(pipe->alpha_blend_destination); + rtbd->BlendOpAlpha = convert_blend_operation(pipe->alpha_blend_operation); + rtbd->RenderTargetWriteMask = (((pipe->color_write_mask_red[targetNum] ? D3D11_COLOR_WRITE_ENABLE_RED : 0) | + (pipe->color_write_mask_green[targetNum] ? D3D11_COLOR_WRITE_ENABLE_GREEN : 0)) | + (pipe->color_write_mask_blue[targetNum] ? D3D11_COLOR_WRITE_ENABLE_BLUE : 0)) | + (pipe->color_write_mask_alpha[targetNum] ? D3D11_COLOR_WRITE_ENABLE_ALPHA : 0); +} + +void kinc_g4_pipeline_compile(struct kinc_g4_pipeline *state) { + if (state->vertex_shader->impl.constantsSize > 0) { + D3D11_BUFFER_DESC desc; + desc.ByteWidth = (UINT)get_multiple_of_16(state->vertex_shader->impl.constantsSize); + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + desc.StructureByteStride = 0; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &desc, NULL, &state->impl.vertexConstantBuffer)); + } + if (state->fragment_shader->impl.constantsSize > 0) { + D3D11_BUFFER_DESC desc; + desc.ByteWidth = (UINT)get_multiple_of_16(state->fragment_shader->impl.constantsSize); + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + desc.StructureByteStride = 0; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &desc, NULL, &state->impl.fragmentConstantBuffer)); + } + if (state->geometry_shader != NULL && state->geometry_shader->impl.constantsSize > 0) { + D3D11_BUFFER_DESC desc; + desc.ByteWidth = (UINT)get_multiple_of_16(state->geometry_shader->impl.constantsSize); + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + desc.StructureByteStride = 0; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &desc, NULL, &state->impl.geometryConstantBuffer)); + } + if (state->tessellation_control_shader != NULL && state->tessellation_control_shader->impl.constantsSize > 0) { + D3D11_BUFFER_DESC desc; + desc.ByteWidth = (UINT)get_multiple_of_16(state->tessellation_control_shader->impl.constantsSize); + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + desc.StructureByteStride = 0; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &desc, NULL, &state->impl.tessControlConstantBuffer)); + } + if (state->tessellation_evaluation_shader != NULL && state->tessellation_evaluation_shader->impl.constantsSize > 0) { + D3D11_BUFFER_DESC desc; + desc.ByteWidth = (UINT)get_multiple_of_16(state->tessellation_evaluation_shader->impl.constantsSize); + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + desc.StructureByteStride = 0; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &desc, NULL, &state->impl.tessEvalConstantBuffer)); + } + + int all = 0; + for (int stream = 0; state->input_layout[stream] != NULL; ++stream) { + for (int index = 0; index < state->input_layout[stream]->size; ++index) { + if (state->input_layout[stream]->elements[index].data == KINC_G4_VERTEX_DATA_F32_4X4) { + all += 4; + } + else { + all += 1; + } + } + } + + bool used[usedCount]; + for (int i = 0; i < usedCount; ++i) + used[i] = false; + for (int i = 0; i < 64; ++i) { + used[state->vertex_shader->impl.attributes[i].index] = true; + } + stringCacheIndex = 0; + D3D11_INPUT_ELEMENT_DESC *vertexDesc = (D3D11_INPUT_ELEMENT_DESC *)alloca(sizeof(D3D11_INPUT_ELEMENT_DESC) * all); + + int i = 0; + for (int stream = 0; state->input_layout[stream] != NULL; ++stream) { + for (int index = 0; index < state->input_layout[stream]->size; ++index) { + switch (state->input_layout[stream]->elements[index].data) { + case KINC_G4_VERTEX_DATA_F32_1X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R32_FLOAT; + ++i; + break; + case KINC_G4_VERTEX_DATA_F32_2X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R32G32_FLOAT; + ++i; + break; + case KINC_G4_VERTEX_DATA_F32_3X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R32G32B32_FLOAT; + ++i; + break; + case KINC_G4_VERTEX_DATA_F32_4X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + ++i; + break; + case KINC_G4_VERTEX_DATA_I8_1X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R8_SINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_U8_1X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R8_UINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_I8_1X_NORMALIZED: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R8_SNORM; + ++i; + break; + case KINC_G4_VERTEX_DATA_U8_1X_NORMALIZED: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R8_UNORM; + ++i; + break; + case KINC_G4_VERTEX_DATA_I8_2X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R8G8_SINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_U8_2X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R8G8_UINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_I8_2X_NORMALIZED: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R8G8_SNORM; + ++i; + break; + case KINC_G4_VERTEX_DATA_U8_2X_NORMALIZED: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R8G8_UNORM; + ++i; + break; + case KINC_G4_VERTEX_DATA_I8_4X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R8G8B8A8_SINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_U8_4X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R8G8B8A8_UINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_I8_4X_NORMALIZED: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R8G8B8A8_SNORM; + ++i; + break; + case KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R8G8B8A8_UNORM; + ++i; + break; + case KINC_G4_VERTEX_DATA_I16_1X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R16_SINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_U16_1X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R16_UINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_I16_1X_NORMALIZED: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R16_SNORM; + ++i; + break; + case KINC_G4_VERTEX_DATA_U16_1X_NORMALIZED: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R16_UNORM; + ++i; + break; + case KINC_G4_VERTEX_DATA_I16_2X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R16G16_SINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_U16_2X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R16G16_UINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R16G16_SNORM; + ++i; + break; + case KINC_G4_VERTEX_DATA_U16_2X_NORMALIZED: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R16G16_UNORM; + ++i; + break; + case KINC_G4_VERTEX_DATA_I16_4X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R16G16B16A16_SINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_U16_4X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R16G16B16A16_UINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R16G16B16A16_SNORM; + ++i; + break; + case KINC_G4_VERTEX_DATA_U16_4X_NORMALIZED: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R16G16B16A16_UNORM; + ++i; + break; + case KINC_G4_VERTEX_DATA_I32_1X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R32_SINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_U32_1X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R32_UINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_I32_2X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R32G32_SINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_U32_2X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R32G32_UINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_I32_3X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R32G32B32_SINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_U32_3X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R32G32B32_UINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_I32_4X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R32G32B32A32_SINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_U32_4X: + setVertexDesc(&vertexDesc[i], + getAttributeLocation(state->vertex_shader->impl.attributes, state->input_layout[stream]->elements[index].name, used), index, + stream, state->input_layout[stream]->instanced, -1); + vertexDesc[i].Format = DXGI_FORMAT_R32G32B32A32_UINT; + ++i; + break; + case KINC_G4_VERTEX_DATA_F32_4X4: { + char name[101]; + strcpy(name, state->input_layout[stream]->elements[index].name); + strcat(name, "_"); + size_t length = strlen(name); + sprintf(&name[length], "%i", 0); + name[length + 1] = 0; + int attributeLocation = getAttributeLocation(state->vertex_shader->impl.attributes, name, used); + + for (int i2 = 0; i2 < 4; ++i2) { + setVertexDesc(&vertexDesc[i], attributeLocation, index + i2, stream, state->input_layout[stream]->instanced, i2); + vertexDesc[i].Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + ++i; + } + break; + } + default: + break; + } + } + } + + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateInputLayout(dx_ctx.device, vertexDesc, all, state->vertex_shader->impl.data, + state->vertex_shader->impl.length, &state->impl.d3d11inputLayout)); + { + D3D11_DEPTH_STENCIL_DESC desc; + memset(&desc, 0, sizeof(desc)); + + desc.DepthEnable = state->depth_mode != KINC_G4_COMPARE_ALWAYS; + desc.DepthWriteMask = state->depth_write ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + desc.DepthFunc = get_comparison(state->depth_mode); + + desc.StencilEnable = state->stencil_front_mode != KINC_G4_COMPARE_ALWAYS || state->stencil_back_mode != KINC_G4_COMPARE_ALWAYS || + state->stencil_front_both_pass != KINC_G4_STENCIL_KEEP || state->stencil_back_both_pass != KINC_G4_STENCIL_KEEP || + state->stencil_front_depth_fail != KINC_G4_STENCIL_KEEP || state->stencil_back_depth_fail != KINC_G4_STENCIL_KEEP || + state->stencil_front_fail != KINC_G4_STENCIL_KEEP || state->stencil_back_fail != KINC_G4_STENCIL_KEEP; + desc.StencilReadMask = state->stencil_read_mask; + desc.StencilWriteMask = state->stencil_write_mask; + desc.FrontFace.StencilFunc = desc.BackFace.StencilFunc = get_comparison(state->stencil_front_mode); + desc.FrontFace.StencilDepthFailOp = desc.BackFace.StencilDepthFailOp = get_stencil_action(state->stencil_front_depth_fail); + desc.FrontFace.StencilPassOp = desc.BackFace.StencilPassOp = get_stencil_action(state->stencil_front_both_pass); + desc.FrontFace.StencilFailOp = desc.BackFace.StencilFailOp = get_stencil_action(state->stencil_front_fail); + desc.BackFace.StencilFunc = desc.BackFace.StencilFunc = get_comparison(state->stencil_back_mode); + desc.BackFace.StencilDepthFailOp = desc.BackFace.StencilDepthFailOp = get_stencil_action(state->stencil_back_depth_fail); + desc.BackFace.StencilPassOp = desc.BackFace.StencilPassOp = get_stencil_action(state->stencil_back_both_pass); + desc.BackFace.StencilFailOp = desc.BackFace.StencilFailOp = get_stencil_action(state->stencil_back_fail); + + dx_ctx.device->lpVtbl->CreateDepthStencilState(dx_ctx.device, &desc, &state->impl.depthStencilState); + } + + { + D3D11_RASTERIZER_DESC rasterDesc; + rasterDesc.CullMode = convert_cull_mode(state->cull_mode); + rasterDesc.FillMode = D3D11_FILL_SOLID; + rasterDesc.FrontCounterClockwise = TRUE; + rasterDesc.DepthBias = 0; + rasterDesc.SlopeScaledDepthBias = 0.0f; + rasterDesc.DepthBiasClamp = 0.0f; + rasterDesc.DepthClipEnable = TRUE; + rasterDesc.ScissorEnable = FALSE; + rasterDesc.MultisampleEnable = FALSE; + rasterDesc.AntialiasedLineEnable = FALSE; + + dx_ctx.device->lpVtbl->CreateRasterizerState(dx_ctx.device, &rasterDesc, &state->impl.rasterizerState); + rasterDesc.ScissorEnable = TRUE; + dx_ctx.device->lpVtbl->CreateRasterizerState(dx_ctx.device, &rasterDesc, &state->impl.rasterizerStateScissor); + + // We need d3d11_3 for conservative raster + // D3D11_RASTERIZER_DESC2 rasterDesc; + // rasterDesc.ConservativeRaster = conservativeRasterization ? D3D11_CONSERVATIVE_RASTERIZATION_MODE_ON : D3D11_CONSERVATIVE_RASTERIZATION_MODE_OFF; + // dx_ctx.device->CreateRasterizerState2(&rasterDesc, &rasterizerState); + // rasterDesc.ScissorEnable = TRUE; + // dx_ctx.device->CreateRasterizerState2(&rasterDesc, &rasterizerStateScissor); + } + + { + bool independentBlend = false; + for (int i = 1; i < 8; ++i) { + if (state->color_write_mask_red[0] != state->color_write_mask_red[i] || state->color_write_mask_green[0] != state->color_write_mask_green[i] || + state->color_write_mask_blue[0] != state->color_write_mask_blue[i] || state->color_write_mask_alpha[0] != state->color_write_mask_alpha[i]) { + independentBlend = true; + break; + } + } + + D3D11_BLEND_DESC blendDesc; + memset(&blendDesc, 0, sizeof(blendDesc)); + blendDesc.AlphaToCoverageEnable = false; + blendDesc.IndependentBlendEnable = independentBlend; + + D3D11_RENDER_TARGET_BLEND_DESC rtbd[8]; + memset(&rtbd, 0, sizeof(rtbd)); + createRenderTargetBlendDesc(state, &rtbd[0], 0); + blendDesc.RenderTarget[0] = rtbd[0]; + if (independentBlend) { + for (int i = 1; i < 8; ++i) { + createRenderTargetBlendDesc(state, &rtbd[i], i); + blendDesc.RenderTarget[i] = rtbd[i]; + } + } + + dx_ctx.device->lpVtbl->CreateBlendState(dx_ctx.device, &blendDesc, &state->impl.blendState); + } +} diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/pipeline.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/pipeline.h new file mode 100644 index 000000000..68b9a4f75 --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/pipeline.h @@ -0,0 +1,37 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct ID3D11InputLayout; +struct ID3D11PixelShader; +struct ID3D11VertexShader; +struct ID3D11Buffer; +struct ID3D11DepthStencilState; +struct ID3D11RasterizerState; +struct ID3D11BlendState; + +typedef struct { + // PipelineStateImpl(); + //~PipelineStateImpl(); + struct ID3D11InputLayout *d3d11inputLayout; + struct ID3D11Buffer *fragmentConstantBuffer; + struct ID3D11Buffer *vertexConstantBuffer; + struct ID3D11Buffer *geometryConstantBuffer; + struct ID3D11Buffer *tessEvalConstantBuffer; + struct ID3D11Buffer *tessControlConstantBuffer; + struct ID3D11DepthStencilState *depthStencilState; + struct ID3D11RasterizerState *rasterizerState; + struct ID3D11RasterizerState *rasterizerStateScissor; + struct ID3D11BlendState *blendState; + // void set(Graphics4::PipelineState* pipeline, bool scissoring); + // void setRasterizerState(bool scissoring); + // static void setConstants(); +} kinc_g4_pipeline_impl_t; + +void kinc_internal_set_constants(void); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/rendertarget.c.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/rendertarget.c.h new file mode 100644 index 000000000..34cce521d --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/rendertarget.c.h @@ -0,0 +1,456 @@ +#include +#include +#include + +static DXGI_FORMAT convertRenderTargetFormat(kinc_g4_render_target_format_t format) { + switch (format) { + case KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + case KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT: + return DXGI_FORMAT_R16G16B16A16_FLOAT; + case KINC_G4_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT: + return DXGI_FORMAT_R32_FLOAT; + case KINC_G4_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT: + return DXGI_FORMAT_R16_FLOAT; + case KINC_G4_RENDER_TARGET_FORMAT_8BIT_RED: + return DXGI_FORMAT_R8_UNORM; + case KINC_G4_RENDER_TARGET_FORMAT_32BIT: + default: + return DXGI_FORMAT_R8G8B8A8_UNORM; + } +} + +static int formatRenderTargetByteSize(kinc_g4_render_target_format_t format) { + switch (format) { + case KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT: + return 16; + case KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT: + return 8; + case KINC_G4_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT: + return 4; + case KINC_G4_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT: + return 2; + case KINC_G4_RENDER_TARGET_FORMAT_8BIT_RED: + return 1; + case KINC_G4_RENDER_TARGET_FORMAT_32BIT: + default: + return 4; + } +} + +void kinc_g4_render_target_init_with_multisampling(kinc_g4_render_target_t *renderTarget, int width, int height, kinc_g4_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + renderTarget->isCubeMap = false; + renderTarget->isDepthAttachment = false; + renderTarget->texWidth = renderTarget->width = width; + renderTarget->texHeight = renderTarget->height = height; + renderTarget->impl.format = format; + renderTarget->impl.textureStaging = NULL; + + D3D11_TEXTURE2D_DESC desc; + desc.Width = width; + desc.Height = height; + desc.MipLevels = desc.ArraySize = 1; + desc.Format = convertRenderTargetFormat(format); + if (format == KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH) { + renderTarget->isDepthAttachment = true; + depthBufferBits = 16; + stencilBufferBits = 0; + } + + bool antialiasing = samples_per_pixel > 1; + if (antialiasing) { + desc.SampleDesc.Count = samples_per_pixel; + desc.SampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN; + } + else { + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + } + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; // D3D11_CPU_ACCESS_WRITE; + desc.MiscFlags = 0; + + renderTarget->impl.textureRender = NULL; + renderTarget->impl.textureSample = NULL; + renderTarget->impl.renderTargetSRV = NULL; + for (int i = 0; i < 6; i++) { + renderTarget->impl.renderTargetViewRender[i] = NULL; + renderTarget->impl.renderTargetViewSample[i] = NULL; + } + if (!renderTarget->isDepthAttachment) { + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &renderTarget->impl.textureRender)); + + D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc; + renderTargetViewDesc.Format = desc.Format; + renderTargetViewDesc.ViewDimension = antialiasing ? D3D11_RTV_DIMENSION_TEXTURE2DMS : D3D11_RTV_DIMENSION_TEXTURE2D; + renderTargetViewDesc.Texture2D.MipSlice = 0; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.textureRender, + &renderTargetViewDesc, &renderTarget->impl.renderTargetViewRender[0])); + + if (antialiasing) { + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &renderTarget->impl.textureSample)); + + D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc; + renderTargetViewDesc.Format = desc.Format; + renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + renderTargetViewDesc.Texture2D.MipSlice = 0; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.textureSample, + &renderTargetViewDesc, &renderTarget->impl.renderTargetViewSample[0])); + } + else { + renderTarget->impl.textureSample = renderTarget->impl.textureRender; + renderTarget->impl.renderTargetViewSample[0] = renderTarget->impl.renderTargetViewRender[0]; + } + } + + renderTarget->impl.depthStencil = NULL; + renderTarget->impl.depthStencilSRV = NULL; + for (int i = 0; i < 6; i++) { + renderTarget->impl.depthStencilView[i] = NULL; + } + + DXGI_FORMAT depthFormat; + DXGI_FORMAT depthViewFormat; + DXGI_FORMAT depthResourceFormat; + if (depthBufferBits == 16 && stencilBufferBits == 0) { + depthFormat = DXGI_FORMAT_R16_TYPELESS; + depthViewFormat = DXGI_FORMAT_D16_UNORM; + depthResourceFormat = DXGI_FORMAT_R16_UNORM; + } + else { + depthFormat = DXGI_FORMAT_R24G8_TYPELESS; + depthViewFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; + depthResourceFormat = DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + } + + if (depthBufferBits > 0) { + D3D11_TEXTURE2D_DESC depthStencilDesc; + depthStencilDesc.Format = depthFormat; + depthStencilDesc.Width = width; + depthStencilDesc.Height = height; + depthStencilDesc.ArraySize = 1; + depthStencilDesc.MipLevels = 1; + depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE; + depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; + depthStencilDesc.CPUAccessFlags = 0; + depthStencilDesc.MiscFlags = 0; + if (antialiasing) { + depthStencilDesc.SampleDesc.Count = 4; + depthStencilDesc.SampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN; + } + else { + depthStencilDesc.SampleDesc.Count = 1; + depthStencilDesc.SampleDesc.Quality = 0; + } + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &depthStencilDesc, NULL, &renderTarget->impl.depthStencil)); + D3D11_DEPTH_STENCIL_VIEW_DESC viewDesc; + viewDesc.Format = depthViewFormat; + viewDesc.ViewDimension = antialiasing ? D3D11_DSV_DIMENSION_TEXTURE2DMS : D3D11_DSV_DIMENSION_TEXTURE2D; + viewDesc.Flags = 0; + if (!antialiasing) { + viewDesc.Texture2D.MipSlice = 0; + } + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateDepthStencilView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.depthStencil, &viewDesc, + &renderTarget->impl.depthStencilView[0])); + } + + D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc; + if (!renderTarget->isDepthAttachment) { + shaderResourceViewDesc.Format = desc.Format; + shaderResourceViewDesc.ViewDimension = antialiasing ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D; + shaderResourceViewDesc.Texture2D.MostDetailedMip = 0; + shaderResourceViewDesc.Texture2D.MipLevels = 1; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.textureSample, + &shaderResourceViewDesc, &renderTarget->impl.renderTargetSRV)); + } + + if (depthBufferBits > 0) { + shaderResourceViewDesc.Format = depthResourceFormat; + shaderResourceViewDesc.ViewDimension = antialiasing ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D; + shaderResourceViewDesc.Texture2D.MostDetailedMip = 0; + shaderResourceViewDesc.Texture2D.MipLevels = 1; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.depthStencil, + &shaderResourceViewDesc, &renderTarget->impl.depthStencilSRV)); + } + + if (renderTarget->impl.renderTargetViewRender[0] != NULL) { + FLOAT colors[4] = {0, 0, 0, 0}; + dx_ctx.context->lpVtbl->ClearRenderTargetView(dx_ctx.context, renderTarget->impl.renderTargetViewRender[0], colors); + } +} + +void kinc_g4_render_target_init_cube_with_multisampling(kinc_g4_render_target_t *renderTarget, int cubeMapSize, kinc_g4_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + renderTarget->width = cubeMapSize; + renderTarget->height = cubeMapSize; + renderTarget->isCubeMap = true; + renderTarget->isDepthAttachment = false; + + renderTarget->texWidth = renderTarget->width; + renderTarget->texHeight = renderTarget->height; + renderTarget->impl.format = format; + renderTarget->impl.textureStaging = NULL; + + D3D11_TEXTURE2D_DESC desc; + desc.Width = renderTarget->width; + desc.Height = renderTarget->height; + desc.MipLevels = 1; + desc.ArraySize = 6; + desc.Format = convertRenderTargetFormat(format); + if (format == KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH) { + renderTarget->isDepthAttachment = true; + depthBufferBits = 16; + stencilBufferBits = 0; + } + + bool antialiasing = samples_per_pixel > 1; + if (antialiasing) { + desc.SampleDesc.Count = samples_per_pixel; + desc.SampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN; + } + else { + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + } + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; // D3D11_CPU_ACCESS_WRITE; + desc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE; + + renderTarget->impl.textureRender = NULL; + renderTarget->impl.textureSample = NULL; + renderTarget->impl.renderTargetSRV = NULL; + for (int i = 0; i < 6; i++) { + renderTarget->impl.renderTargetViewRender[i] = NULL; + renderTarget->impl.renderTargetViewSample[i] = NULL; + } + if (!renderTarget->isDepthAttachment) { + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &renderTarget->impl.textureRender)); + + D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc; + renderTargetViewDesc.Format = desc.Format; + renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + renderTargetViewDesc.Texture2DArray.MipSlice = 0; + renderTargetViewDesc.Texture2DArray.ArraySize = 1; + + for (int i = 0; i < 6; i++) { + renderTargetViewDesc.Texture2DArray.FirstArraySlice = i; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.textureRender, + &renderTargetViewDesc, &renderTarget->impl.renderTargetViewRender[i])); + } + + if (antialiasing) { + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &renderTarget->impl.textureSample)); + + D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc; + renderTargetViewDesc.Format = desc.Format; + renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + renderTargetViewDesc.Texture2D.MipSlice = 0; + renderTargetViewDesc.Texture2DArray.ArraySize = 1; + for (int i = 0; i < 6; i++) { + renderTargetViewDesc.Texture2DArray.FirstArraySlice = i; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.textureSample, + &renderTargetViewDesc, &renderTarget->impl.renderTargetViewSample[i])); + } + } + else { + renderTarget->impl.textureSample = renderTarget->impl.textureRender; + for (int i = 0; i < 6; i++) { + renderTarget->impl.renderTargetViewSample[i] = renderTarget->impl.renderTargetViewRender[i]; + } + } + } + + renderTarget->impl.depthStencil = NULL; + renderTarget->impl.depthStencilSRV = NULL; + for (int i = 0; i < 6; i++) { + renderTarget->impl.depthStencilView[i] = NULL; + } + + DXGI_FORMAT depthFormat; + DXGI_FORMAT depthViewFormat; + DXGI_FORMAT depthResourceFormat; + if (depthBufferBits == 16 && stencilBufferBits == 0) { + depthFormat = DXGI_FORMAT_R16_TYPELESS; + depthViewFormat = DXGI_FORMAT_D16_UNORM; + depthResourceFormat = DXGI_FORMAT_R16_UNORM; + } + else { + depthFormat = DXGI_FORMAT_R24G8_TYPELESS; + depthViewFormat = DXGI_FORMAT_D24_UNORM_S8_UINT; + depthResourceFormat = DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + } + + if (depthBufferBits > 0) { + D3D11_TEXTURE2D_DESC depthStencilDesc; + depthStencilDesc.Format = depthFormat; + depthStencilDesc.Width = renderTarget->width; + depthStencilDesc.Height = renderTarget->height; + depthStencilDesc.ArraySize = 1; + depthStencilDesc.MipLevels = 1; + depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE; + depthStencilDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE; + depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; + depthStencilDesc.CPUAccessFlags = 0; + depthStencilDesc.ArraySize = 6; + if (antialiasing) { + depthStencilDesc.SampleDesc.Count = 4; + depthStencilDesc.SampleDesc.Quality = D3D11_STANDARD_MULTISAMPLE_PATTERN; + } + else { + depthStencilDesc.SampleDesc.Count = 1; + depthStencilDesc.SampleDesc.Quality = 0; + } + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &depthStencilDesc, NULL, &renderTarget->impl.depthStencil)); + + D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc; + depthStencilViewDesc.Format = depthViewFormat; + depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY; + depthStencilViewDesc.Texture2DArray.MipSlice = 0; + depthStencilViewDesc.Texture2DArray.ArraySize = 1; + depthStencilViewDesc.Flags = 0; + for (int i = 0; i < 6; i++) { + depthStencilViewDesc.Texture2DArray.FirstArraySlice = i; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateDepthStencilView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.depthStencil, + &depthStencilViewDesc, &renderTarget->impl.depthStencilView[i])); + } + } + + D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc; + if (!renderTarget->isDepthAttachment) { + shaderResourceViewDesc.Format = desc.Format; + shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + shaderResourceViewDesc.TextureCube.MostDetailedMip = 0; + shaderResourceViewDesc.TextureCube.MipLevels = 1; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.textureSample, + &shaderResourceViewDesc, &renderTarget->impl.renderTargetSRV)); + } + + if (depthBufferBits > 0) { + shaderResourceViewDesc.Format = depthResourceFormat; + shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + shaderResourceViewDesc.TextureCube.MostDetailedMip = 0; + shaderResourceViewDesc.TextureCube.MipLevels = 1; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)renderTarget->impl.depthStencil, + &shaderResourceViewDesc, &renderTarget->impl.depthStencilSRV)); + } + + if (!renderTarget->isDepthAttachment) { + FLOAT colors[4] = {0, 0, 0, 0}; + for (int i = 0; i < 6; i++) { + dx_ctx.context->lpVtbl->ClearRenderTargetView(dx_ctx.context, renderTarget->impl.renderTargetViewRender[i], colors); + } + } +} + +void kinc_g4_render_target_destroy(kinc_g4_render_target_t *renderTarget) { + for (int i = 0; i < 6; i++) { + if (renderTarget->impl.renderTargetViewRender[i] != NULL) + renderTarget->impl.renderTargetViewRender[i]->lpVtbl->Release(renderTarget->impl.renderTargetViewRender[i]); + if (renderTarget->impl.renderTargetViewSample[i] != NULL && + renderTarget->impl.renderTargetViewSample[i] != renderTarget->impl.renderTargetViewRender[i]) + renderTarget->impl.renderTargetViewSample[i]->lpVtbl->Release(renderTarget->impl.renderTargetViewSample[i]); + if (renderTarget->impl.depthStencilView[i] != NULL) + renderTarget->impl.depthStencilView[i]->lpVtbl->Release(renderTarget->impl.depthStencilView[i]); + } + if (renderTarget->impl.renderTargetSRV != NULL) + renderTarget->impl.renderTargetSRV->lpVtbl->Release(renderTarget->impl.renderTargetSRV); + if (renderTarget->impl.depthStencilSRV != NULL) + renderTarget->impl.depthStencilSRV->lpVtbl->Release(renderTarget->impl.depthStencilSRV); + if (renderTarget->impl.depthStencil != NULL) + renderTarget->impl.depthStencil->lpVtbl->Release(renderTarget->impl.depthStencil); + if (renderTarget->impl.textureRender != NULL) + renderTarget->impl.textureRender->lpVtbl->Release(renderTarget->impl.textureRender); + if (renderTarget->impl.textureStaging != NULL) + renderTarget->impl.textureStaging->lpVtbl->Release(renderTarget->impl.textureStaging); + if (renderTarget->impl.textureSample != NULL && renderTarget->impl.textureSample != renderTarget->impl.textureRender) + renderTarget->impl.textureSample->lpVtbl->Release(renderTarget->impl.textureSample); +} + +void kinc_g4_render_target_use_color_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0 && unit.stages[KINC_G4_SHADER_TYPE_VERTEX] < 0) + return; + + if (renderTarget->impl.textureSample != renderTarget->impl.textureRender) { + dx_ctx.context->lpVtbl->ResolveSubresource(dx_ctx.context, (ID3D11Resource *)renderTarget->impl.textureSample, 0, + (ID3D11Resource *)renderTarget->impl.textureRender, 0, DXGI_FORMAT_R8G8B8A8_UNORM); + } + + if (unit.stages[KINC_G4_SHADER_TYPE_VERTEX] >= 0) { + dx_ctx.context->lpVtbl->VSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_VERTEX], 1, + renderTarget->isDepthAttachment ? &renderTarget->impl.depthStencilSRV + : &renderTarget->impl.renderTargetSRV); + } + + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] >= 0) { + dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, + renderTarget->isDepthAttachment ? &renderTarget->impl.depthStencilSRV + : &renderTarget->impl.renderTargetSRV); + } +} + +void kinc_g4_render_target_use_depth_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit) { + if (unit.stages[KINC_G4_SHADER_TYPE_VERTEX] >= 0) { + dx_ctx.context->lpVtbl->VSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_VERTEX], 1, &renderTarget->impl.depthStencilSRV); + } + + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] >= 0) { + dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &renderTarget->impl.depthStencilSRV); + } +} + +void kinc_g4_render_target_set_depth_stencil_from(kinc_g4_render_target_t *renderTarget, kinc_g4_render_target_t *source) { + renderTarget->impl.depthStencil = source->impl.depthStencil; + for (int i = 0; i < 6; i++) { + renderTarget->impl.depthStencilView[i] = source->impl.depthStencilView[i]; + } + renderTarget->impl.depthStencilSRV = source->impl.depthStencilSRV; +} + +void kinc_g4_render_target_get_pixels(kinc_g4_render_target_t *renderTarget, uint8_t *data) { + if (renderTarget->impl.textureStaging == NULL) { + D3D11_TEXTURE2D_DESC desc; + desc.Width = renderTarget->texWidth; + desc.Height = renderTarget->texHeight; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = convertRenderTargetFormat((kinc_g4_render_target_format_t)renderTarget->impl.format); + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_STAGING; + desc.BindFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.MiscFlags = 0; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &renderTarget->impl.textureStaging)); + } + + D3D11_BOX sourceRegion; + sourceRegion.left = 0; + sourceRegion.right = renderTarget->texWidth; + sourceRegion.top = 0; + sourceRegion.bottom = renderTarget->texHeight; + sourceRegion.front = 0; + sourceRegion.back = 1; + dx_ctx.context->lpVtbl->CopySubresourceRegion(dx_ctx.context, (ID3D11Resource *)renderTarget->impl.textureStaging, 0, 0, 0, 0, + (ID3D11Resource *)renderTarget->impl.textureRender, 0, &sourceRegion); + + D3D11_MAPPED_SUBRESOURCE mappedResource; + dx_ctx.context->lpVtbl->Map(dx_ctx.context, (ID3D11Resource *)renderTarget->impl.textureStaging, 0, D3D11_MAP_READ, 0, &mappedResource); + int size; + if (mappedResource.RowPitch != 0) { + size = mappedResource.RowPitch * renderTarget->texHeight; + } + else { + size = renderTarget->texWidth * renderTarget->texHeight * formatRenderTargetByteSize((kinc_g4_render_target_format_t)renderTarget->impl.format); + } + memcpy(data, mappedResource.pData, size); + dx_ctx.context->lpVtbl->Unmap(dx_ctx.context, (ID3D11Resource *)renderTarget->impl.textureStaging, 0); +} + +void kinc_g4_render_target_generate_mipmaps(kinc_g4_render_target_t *renderTarget, int levels) {} diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/rendertarget.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/rendertarget.h new file mode 100644 index 000000000..8b92e8201 --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/rendertarget.h @@ -0,0 +1,27 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct ID3D11Texture2D; +struct ID3D11RenderTargetView; +struct ID3D11DepthStencilView; +struct ID3D11ShaderResourceView; + +typedef struct { + struct ID3D11Texture2D *textureRender; + struct ID3D11Texture2D *textureSample; + struct ID3D11Texture2D *textureStaging; + struct ID3D11RenderTargetView *renderTargetViewRender[6]; + struct ID3D11RenderTargetView *renderTargetViewSample[6]; + struct ID3D11Texture2D *depthStencil; + struct ID3D11DepthStencilView *depthStencilView[6]; + struct ID3D11ShaderResourceView *renderTargetSRV; + struct ID3D11ShaderResourceView *depthStencilSRV; + int format; +} kinc_g4_render_target_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/shader.c.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/shader.c.h new file mode 100644 index 000000000..70671d5b1 --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/shader.c.h @@ -0,0 +1,112 @@ +#include + +void kinc_g4_shader_destroy(kinc_g4_shader_t *shader) { + if (shader->impl.shader != NULL) { + ((IUnknown *)shader->impl.shader)->lpVtbl->Release(shader->impl.shader); + free(shader->impl.data); + } +} + +void kinc_g4_shader_init(kinc_g4_shader_t *shader, const void *_data, size_t length, kinc_g4_shader_type_t type) { + unsigned index = 0; + uint8_t *data = (uint8_t *)_data; + shader->impl.type = (int)type; + + memset(&shader->impl.attributes, 0, sizeof(shader->impl.attributes)); + int attributesCount = data[index++]; + for (int i = 0; i < attributesCount; ++i) { + unsigned char name[256]; + for (unsigned i2 = 0; i2 < 255; ++i2) { + name[i2] = data[index++]; + if (name[i2] == 0) + break; + } + shader->impl.attributes[i].hash = kinc_internal_hash_name(name); + shader->impl.attributes[i].index = data[index++]; + } + + memset(&shader->impl.textures, 0, sizeof(shader->impl.textures)); + uint8_t texCount = data[index++]; + for (unsigned i = 0; i < texCount; ++i) { + unsigned char name[256]; + for (unsigned i2 = 0; i2 < 255; ++i2) { + name[i2] = data[index++]; + if (name[i2] == 0) + break; + } + shader->impl.textures[i].hash = kinc_internal_hash_name(name); + shader->impl.textures[i].index = data[index++]; + } + + memset(&shader->impl.constants, 0, sizeof(shader->impl.constants)); + uint8_t constantCount = data[index++]; + shader->impl.constantsSize = 0; + for (unsigned i = 0; i < constantCount; ++i) { + unsigned char name[256]; + for (unsigned i2 = 0; i2 < 255; ++i2) { + name[i2] = data[index++]; + if (name[i2] == 0) + break; + } + kinc_internal_shader_constant_t constant; + constant.hash = kinc_internal_hash_name(name); + constant.offset = *(uint32_t *)&data[index]; + index += 4; + constant.size = *(uint32_t *)&data[index]; + index += 4; + constant.columns = data[index]; + index += 1; + constant.rows = data[index]; + index += 1; + + shader->impl.constants[i] = constant; + shader->impl.constantsSize = constant.offset + constant.size; + } + + shader->impl.length = (int)(length - index); + shader->impl.data = (uint8_t *)malloc(shader->impl.length); + assert(shader->impl.data != NULL); + memcpy(shader->impl.data, &data[index], shader->impl.length); + + switch (type) { + case KINC_G4_SHADER_TYPE_VERTEX: + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateVertexShader(dx_ctx.device, shader->impl.data, shader->impl.length, NULL, + (ID3D11VertexShader **)&shader->impl.shader)); + break; + case KINC_G4_SHADER_TYPE_FRAGMENT: + kinc_microsoft_affirm( + dx_ctx.device->lpVtbl->CreatePixelShader(dx_ctx.device, shader->impl.data, shader->impl.length, NULL, (ID3D11PixelShader **)&shader->impl.shader)); + break; + case KINC_G4_SHADER_TYPE_GEOMETRY: + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateGeometryShader(dx_ctx.device, shader->impl.data, shader->impl.length, NULL, + (ID3D11GeometryShader **)&shader->impl.shader)); + break; + case KINC_G4_SHADER_TYPE_TESSELLATION_CONTROL: + kinc_microsoft_affirm( + dx_ctx.device->lpVtbl->CreateHullShader(dx_ctx.device, shader->impl.data, shader->impl.length, NULL, (ID3D11HullShader **)&shader->impl.shader)); + break; + case KINC_G4_SHADER_TYPE_TESSELLATION_EVALUATION: + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateDomainShader(dx_ctx.device, shader->impl.data, shader->impl.length, NULL, + (ID3D11DomainShader **)&shader->impl.shader)); + break; + } +} + +#ifdef KRAFIX_LIBRARY +extern int krafix_compile(const char *source, char *output, int *length, const char *targetlang, const char *system, const char *shadertype, int version); +#endif + +int kinc_g4_shader_init_from_source(kinc_g4_shader_t *shader, const char *source, kinc_g4_shader_type_t type) { +#ifdef KRAFIX_LIBRARY + char *output = malloc(1024 * 1024); + int length; + int errors = krafix_compile(source, output, &length, "d3d11", "windows", type == KINC_G4_SHADER_TYPE_FRAGMENT ? "frag" : "vert", -1); + if (errors > 0) { + return errors; + } + kinc_g4_shader_init(shader, output, length, type); + return 0; +#else + return 0; +#endif +} diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/shader.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/shader.h new file mode 100644 index 000000000..f082fcdaf --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/shader.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t hash; + uint32_t offset; + uint32_t size; + uint8_t columns; + uint8_t rows; +} kinc_internal_shader_constant_t; + +typedef struct { + kinc_internal_shader_constant_t constants[64]; + int constantsSize; + kinc_internal_hash_index_t attributes[64]; + kinc_internal_hash_index_t textures[64]; + void *shader; + uint8_t *data; + int length; + int type; +} kinc_g4_shader_impl_t; + +typedef struct { + uint32_t vertexOffset; + uint32_t vertexSize; + uint32_t fragmentOffset; + uint32_t fragmentSize; + uint32_t geometryOffset; + uint32_t geometrySize; + uint32_t tessEvalOffset; + uint32_t tessEvalSize; + uint32_t tessControlOffset; + uint32_t tessControlSize; + uint32_t computeOffset; + uint32_t computeSize; + uint8_t vertexColumns; + uint8_t vertexRows; + uint8_t fragmentColumns; + uint8_t fragmentRows; + uint8_t geometryColumns; + uint8_t geometryRows; + uint8_t tessEvalColumns; + uint8_t tessEvalRows; + uint8_t tessControlColumns; + uint8_t tessControlRows; + uint8_t computeColumns; + uint8_t computeRows; +} kinc_g4_constant_location_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/texture.c.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/texture.c.h new file mode 100644 index 000000000..89dc7b96d --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/texture.c.h @@ -0,0 +1,371 @@ +#include +#include + +#include + +static kinc_g4_texture_t *setTextures[16] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + +static DXGI_FORMAT convertFormat(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA128: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + case KINC_IMAGE_FORMAT_RGBA64: + return DXGI_FORMAT_R16G16B16A16_FLOAT; + case KINC_IMAGE_FORMAT_RGB24: + return DXGI_FORMAT_R8G8B8A8_UNORM; + case KINC_IMAGE_FORMAT_A32: + return DXGI_FORMAT_R32_FLOAT; + case KINC_IMAGE_FORMAT_A16: + return DXGI_FORMAT_R16_FLOAT; + case KINC_IMAGE_FORMAT_GREY8: + return DXGI_FORMAT_R8_UNORM; + case KINC_IMAGE_FORMAT_BGRA32: + return DXGI_FORMAT_B8G8R8A8_UNORM; + case KINC_IMAGE_FORMAT_RGBA32: + return DXGI_FORMAT_R8G8B8A8_UNORM; + default: + assert(false); + return DXGI_FORMAT_R8G8B8A8_UNORM; + } +} + +static int formatByteSize(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA128: + return 16; + case KINC_IMAGE_FORMAT_RGBA64: + return 8; + case KINC_IMAGE_FORMAT_RGB24: + return 4; + case KINC_IMAGE_FORMAT_A32: + return 4; + case KINC_IMAGE_FORMAT_A16: + return 2; + case KINC_IMAGE_FORMAT_GREY8: + return 1; + case KINC_IMAGE_FORMAT_BGRA32: + case KINC_IMAGE_FORMAT_RGBA32: + return 4; + default: + assert(false); + return 4; + } +} + +static bool isHdr(kinc_image_format_t format) { + return format == KINC_IMAGE_FORMAT_RGBA128 || format == KINC_IMAGE_FORMAT_RGBA64 || format == KINC_IMAGE_FORMAT_A32 || format == KINC_IMAGE_FORMAT_A16; +} + +void kinc_g4_texture_init_from_image(kinc_g4_texture_t *texture, kinc_image_t *image) { + memset(&texture->impl, 0, sizeof(texture->impl)); + texture->impl.stage = 0; + texture->tex_width = image->width; + texture->tex_height = image->height; + texture->tex_depth = 1; + texture->format = image->format; + texture->impl.rowPitch = 0; + + D3D11_TEXTURE2D_DESC desc; + desc.Width = image->width; + desc.Height = image->height; + desc.MipLevels = desc.ArraySize = 1; + desc.Format = convertFormat(image->format); + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; // D3D11_CPU_ACCESS_WRITE; + desc.MiscFlags = 0; + + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = image->data; + data.SysMemPitch = image->width * formatByteSize(image->format); + data.SysMemSlicePitch = 0; + + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, &data, &texture->impl.texture)); + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture, NULL, &texture->impl.view)); +} + +void kinc_g4_texture_init_from_image3d(kinc_g4_texture_t *texture, kinc_image_t *image) { + memset(&texture->impl, 0, sizeof(texture->impl)); + texture->impl.stage = 0; + texture->tex_width = image->width; + texture->tex_height = image->height; + texture->tex_depth = image->depth; + texture->format = image->format; + texture->impl.rowPitch = 0; + + D3D11_TEXTURE3D_DESC desc; + desc.Width = image->width; + desc.Height = image->height; + desc.Depth = image->depth; + desc.MipLevels = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.MiscFlags = 0; + desc.Format = convertFormat(image->format); + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = image->data; + data.SysMemPitch = image->width * formatByteSize(image->format); + data.SysMemSlicePitch = image->width * image->height * formatByteSize(image->format); + + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture3D(dx_ctx.device, &desc, &data, &texture->impl.texture3D)); + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture3D, NULL, &texture->impl.view)); +} + +void kinc_g4_texture_init(kinc_g4_texture_t *texture, int width, int height, kinc_image_format_t format) { + memset(&texture->impl, 0, sizeof(texture->impl)); + texture->impl.stage = 0; + texture->tex_width = width; + texture->tex_height = height; + texture->tex_depth = 1; + texture->format = format; + + D3D11_TEXTURE2D_DESC desc; + desc.Width = width; + desc.Height = height; + desc.MipLevels = desc.ArraySize = 1; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.MiscFlags = 0; + + if (format == KINC_IMAGE_FORMAT_RGBA128) { // for compute + desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + } + else { + desc.Format = convertFormat(format); + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + } + + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &texture->impl.texture)); + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture, NULL, &texture->impl.view)); + + if (format == KINC_IMAGE_FORMAT_RGBA128) { + D3D11_UNORDERED_ACCESS_VIEW_DESC du; + du.Format = desc.Format; + du.Texture2D.MipSlice = 0; + du.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D; + kinc_microsoft_affirm( + dx_ctx.device->lpVtbl->CreateUnorderedAccessView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture, &du, &texture->impl.computeView)); + } +} + +void kinc_g4_texture_init3d(kinc_g4_texture_t *texture, int width, int height, int depth, kinc_image_format_t format) { + memset(&texture->impl, 0, sizeof(texture->impl)); + texture->impl.stage = 0; + texture->tex_width = width; + texture->tex_height = height; + texture->tex_depth = depth; + texture->format = format; + texture->impl.hasMipmaps = true; + + D3D11_TEXTURE3D_DESC desc; + desc.Width = width; + desc.Height = height; + desc.Depth = depth; + desc.MipLevels = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.MiscFlags = D3D11_RESOURCE_MISC_GENERATE_MIPS; + desc.Format = format == KINC_IMAGE_FORMAT_RGBA32 ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R8_UNORM; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET | D3D11_BIND_UNORDERED_ACCESS; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.CPUAccessFlags = 0; + + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture3D(dx_ctx.device, &desc, NULL, &texture->impl.texture3D)); + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture3D, NULL, &texture->impl.view)); +} + +// TextureImpl::TextureImpl() : hasMipmaps(false), renderView(nullptr), computeView(nullptr) {} + +void kinc_internal_texture_unset(kinc_g4_texture_t *texture); + +void kinc_g4_texture_destroy(kinc_g4_texture_t *texture) { + kinc_internal_texture_unset(texture); + if (texture->impl.view != NULL) { + texture->impl.view->lpVtbl->Release(texture->impl.view); + } + if (texture->impl.texture != NULL) { + texture->impl.texture->lpVtbl->Release(texture->impl.texture); + } + if (texture->impl.texture3D != NULL) { + texture->impl.texture3D->lpVtbl->Release(texture->impl.texture3D); + } + if (texture->impl.computeView != NULL) { + texture->impl.computeView->lpVtbl->Release(texture->impl.computeView); + } +} + +void kinc_internal_texture_unmipmap(kinc_g4_texture_t *texture) { + texture->impl.hasMipmaps = false; +} + +void kinc_internal_texture_set(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0 && unit.stages[KINC_G4_SHADER_TYPE_VERTEX] < 0 && unit.stages[KINC_G4_SHADER_TYPE_COMPUTE] < 0) + return; + + if (unit.stages[KINC_G4_SHADER_TYPE_VERTEX] >= 0) { + dx_ctx.context->lpVtbl->VSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_VERTEX], 1, &texture->impl.view); + } + + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] >= 0) { + dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &texture->impl.view); + } + + if (unit.stages[KINC_G4_SHADER_TYPE_COMPUTE] >= 0) { + dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_COMPUTE], 1, &texture->impl.view); + } + + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] >= 0 || unit.stages[KINC_G4_SHADER_TYPE_VERTEX] >= 0) { + texture->impl.stage = + unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] >= 0 ? unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] : unit.stages[KINC_G4_SHADER_TYPE_VERTEX]; + setTextures[texture->impl.stage] = texture; + } +} + +void kinc_internal_texture_set_image(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0 && unit.stages[KINC_G4_SHADER_TYPE_VERTEX] < 0 && unit.stages[KINC_G4_SHADER_TYPE_COMPUTE] < 0) + return; + + if (texture->impl.computeView == NULL) { + D3D11_UNORDERED_ACCESS_VIEW_DESC du; + du.Format = texture->format == KINC_IMAGE_FORMAT_RGBA32 ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R8_UNORM; + du.Texture3D.MipSlice = 0; + du.Texture3D.FirstWSlice = 0; + du.Texture3D.WSize = -1; + du.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE3D; + kinc_microsoft_affirm( + dx_ctx.device->lpVtbl->CreateUnorderedAccessView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture3D, &du, &texture->impl.computeView)); + } + + dx_ctx.context->lpVtbl->OMSetRenderTargetsAndUnorderedAccessViews(dx_ctx.context, 0, NULL, NULL, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, + &texture->impl.computeView, NULL); + + /*if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] >= 0) { + ID3D11ShaderResourceView *nullView = NULL; + dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, 0, 1, &nullView); + + dx_ctx.context->lpVtbl->CSSetUnorderedAccessViews(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT], 1, &texture->impl.computeView, NULL); + } + if (unit.stages[KINC_G4_SHADER_TYPE_VERTEX] >= 0) { + ID3D11ShaderResourceView *nullView = NULL; + dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, 0, 1, &nullView); + + dx_ctx.context->lpVtbl->CSSetUnorderedAccessViews(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_VERTEX], 1, &texture->impl.computeView, NULL); + } + if (unit.stages[KINC_G4_SHADER_TYPE_COMPUTE] >= 0) { + ID3D11ShaderResourceView *nullView = NULL; + dx_ctx.context->lpVtbl->PSSetShaderResources(dx_ctx.context, 0, 1, &nullView); + + dx_ctx.context->lpVtbl->CSSetUnorderedAccessViews(dx_ctx.context, unit.stages[KINC_G4_SHADER_TYPE_COMPUTE], 1, &texture->impl.computeView, NULL); + }*/ +} + +void kinc_internal_texture_unset(kinc_g4_texture_t *texture) { + if (setTextures[texture->impl.stage] == texture) { + + setTextures[texture->impl.stage] = NULL; + } +} + +uint8_t *kinc_g4_texture_lock(kinc_g4_texture_t *texture) { + D3D11_MAPPED_SUBRESOURCE mappedResource; + kinc_microsoft_affirm(dx_ctx.context->lpVtbl->Map(dx_ctx.context, (ID3D11Resource *)texture->impl.texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource)); + texture->impl.rowPitch = mappedResource.RowPitch; + return (uint8_t *)mappedResource.pData; +} + +void kinc_g4_texture_unlock(kinc_g4_texture_t *texture) { + dx_ctx.context->lpVtbl->Unmap(dx_ctx.context, (ID3D11Resource *)texture->impl.texture, 0); +} + +void kinc_g4_texture_clear(kinc_g4_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color) { + if (texture->impl.renderView == NULL) { + texture->tex_depth > 1 ? kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture3D, + 0, &texture->impl.renderView)) + : kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateRenderTargetView(dx_ctx.device, (ID3D11Resource *)texture->impl.texture, 0, + &texture->impl.renderView)); + } + static float clearColor[4]; + clearColor[0] = ((color & 0x00ff0000) >> 16) / 255.0f; + clearColor[1] = ((color & 0x0000ff00) >> 8) / 255.0f; + clearColor[2] = (color & 0x000000ff) / 255.0f; + clearColor[3] = ((color & 0xff000000) >> 24) / 255.0f; + dx_ctx.context->lpVtbl->ClearRenderTargetView(dx_ctx.context, texture->impl.renderView, clearColor); +} + +int kinc_g4_texture_stride(kinc_g4_texture_t *texture) { + assert(texture->impl.rowPitch != 0); // stride is not yet set, lock and unlock the texture first (or find a good fix for this and send a PR) + return texture->impl.rowPitch; +} + +static void enableMipmaps(kinc_g4_texture_t *texture, int texWidth, int texHeight, int format) { + D3D11_TEXTURE2D_DESC desc; + desc.Width = texWidth; + desc.Height = texHeight; + desc.MipLevels = 0; + desc.ArraySize = 1; + desc.Format = convertFormat((kinc_image_format_t)format); + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; + desc.CPUAccessFlags = 0; + desc.MiscFlags = D3D11_RESOURCE_MISC_GENERATE_MIPS; + + ID3D11Texture2D *mipMappedTexture; + ID3D11ShaderResourceView *mipMappedView; + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateTexture2D(dx_ctx.device, &desc, NULL, &mipMappedTexture)); + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateShaderResourceView(dx_ctx.device, (ID3D11Resource *)mipMappedTexture, NULL, &mipMappedView)); + + D3D11_BOX sourceRegion; + sourceRegion.left = 0; + sourceRegion.right = texWidth; + sourceRegion.top = 0; + sourceRegion.bottom = texHeight; + sourceRegion.front = 0; + sourceRegion.back = 1; + dx_ctx.context->lpVtbl->CopySubresourceRegion(dx_ctx.context, (ID3D11Resource *)mipMappedTexture, 0, 0, 0, 0, (ID3D11Resource *)texture->impl.texture, 0, + &sourceRegion); + + if (texture->impl.texture != NULL) { + texture->impl.texture->lpVtbl->Release(texture->impl.texture); + } + texture->impl.texture = mipMappedTexture; + + if (texture->impl.view != NULL) { + texture->impl.view->lpVtbl->Release(texture->impl.view); + } + texture->impl.view = mipMappedView; + + texture->impl.hasMipmaps = true; +} + +void kinc_g4_texture_generate_mipmaps(kinc_g4_texture_t *texture, int levels) { + if (!texture->impl.hasMipmaps) { + enableMipmaps(texture, texture->tex_width, texture->tex_height, texture->format); + } + dx_ctx.context->lpVtbl->GenerateMips(dx_ctx.context, texture->impl.view); +} + +void kinc_g4_texture_set_mipmap(kinc_g4_texture_t *texture, kinc_image_t *mipmap, int level) { + if (!texture->impl.hasMipmaps) { + enableMipmaps(texture, texture->tex_width, texture->tex_height, texture->format); + } + D3D11_BOX dstRegion; + dstRegion.left = 0; + dstRegion.right = mipmap->width; + dstRegion.top = 0; + dstRegion.bottom = mipmap->height; + dstRegion.front = 0; + dstRegion.back = 1; + dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)texture->impl.texture, level, &dstRegion, mipmap->data, + mipmap->width * formatByteSize(mipmap->format), 0); +} diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/texture.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/texture.h new file mode 100644 index 000000000..ccb947e1d --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/texture.h @@ -0,0 +1,24 @@ +#pragma once + +struct ID3D11Texture2D; +struct ID3D11Texture3D; +struct ID3D11ShaderResourceView; +struct ID3D11UnorderedAccessView; +struct ID3D11RenderTargetView; + +// TextureImpl(); +//~TextureImpl(); +// void enableMipmaps(int texWidth, int texHeight, int format); +// void unmipmap(); +// void unset(); + +typedef struct { + bool hasMipmaps; + int stage; + struct ID3D11Texture2D *texture; + struct ID3D11Texture3D *texture3D; + struct ID3D11ShaderResourceView *view; + struct ID3D11UnorderedAccessView *computeView; + struct ID3D11RenderTargetView *renderView; + int rowPitch; +} kinc_g4_texture_impl_t; diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/vertexbuffer.c.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/vertexbuffer.c.h new file mode 100644 index 000000000..665aebc69 --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/vertexbuffer.c.h @@ -0,0 +1,95 @@ +#include + +void kinc_g4_vertex_buffer_init(kinc_g4_vertex_buffer_t *buffer, int count, kinc_g4_vertex_structure_t *structure, kinc_g4_usage_t usage, + int instance_data_step_rate) { + buffer->impl.count = count; + buffer->impl.stride = 0; + for (int i = 0; i < structure->size; ++i) { + buffer->impl.stride += kinc_g4_vertex_data_size(structure->elements[i].data); + } + + if (usage == KINC_G4_USAGE_DYNAMIC) { + buffer->impl.vertices = NULL; + } + else { + buffer->impl.vertices = (float *)malloc(buffer->impl.stride * count); + } + + D3D11_BUFFER_DESC bufferDesc; + bufferDesc.CPUAccessFlags = 0; + + buffer->impl.usage = usage; + switch (usage) { + case KINC_G4_USAGE_STATIC: + bufferDesc.Usage = D3D11_USAGE_DEFAULT; + break; + case KINC_G4_USAGE_DYNAMIC: + bufferDesc.Usage = D3D11_USAGE_DYNAMIC; + bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + break; + case KINC_G4_USAGE_READABLE: + bufferDesc.Usage = D3D11_USAGE_DEFAULT; + break; + } + + bufferDesc.ByteWidth = buffer->impl.stride * count; + bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + bufferDesc.MiscFlags = 0; + bufferDesc.StructureByteStride = 0; + + kinc_microsoft_affirm(dx_ctx.device->lpVtbl->CreateBuffer(dx_ctx.device, &bufferDesc, NULL, &buffer->impl.vb)); +} + +void kinc_g4_vertex_buffer_destroy(kinc_g4_vertex_buffer_t *buffer) { + buffer->impl.vb->lpVtbl->Release(buffer->impl.vb); + free(buffer->impl.vertices); + buffer->impl.vertices = NULL; +} + +float *kinc_g4_vertex_buffer_lock_all(kinc_g4_vertex_buffer_t *buffer) { + return kinc_g4_vertex_buffer_lock(buffer, 0, buffer->impl.count); +} + +float *kinc_g4_vertex_buffer_lock(kinc_g4_vertex_buffer_t *buffer, int start, int count) { + buffer->impl.lockStart = start; + buffer->impl.lockCount = count; + + if (buffer->impl.usage == KINC_G4_USAGE_DYNAMIC) { + D3D11_MAPPED_SUBRESOURCE mappedResource; + memset(&mappedResource, 0, sizeof(D3D11_MAPPED_SUBRESOURCE)); + dx_ctx.context->lpVtbl->Map(dx_ctx.context, (ID3D11Resource *)buffer->impl.vb, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); + float *data = (float *)mappedResource.pData; + return &data[start * buffer->impl.stride / 4]; + } + else { + return &buffer->impl.vertices[start * buffer->impl.stride / 4]; + } +} + +void kinc_g4_vertex_buffer_unlock_all(kinc_g4_vertex_buffer_t *buffer) { + kinc_g4_vertex_buffer_unlock(buffer, buffer->impl.lockCount); +} + +void kinc_g4_vertex_buffer_unlock(kinc_g4_vertex_buffer_t *buffer, int count) { + if (buffer->impl.usage == KINC_G4_USAGE_DYNAMIC) { + dx_ctx.context->lpVtbl->Unmap(dx_ctx.context, (ID3D11Resource *)buffer->impl.vb, 0); + } + else { + dx_ctx.context->lpVtbl->UpdateSubresource(dx_ctx.context, (ID3D11Resource *)buffer->impl.vb, 0, NULL, buffer->impl.vertices, 0, 0); + } +} + +int kinc_internal_g4_vertex_buffer_set(kinc_g4_vertex_buffer_t *buffer, int offset) { + // UINT stride = myStride; + // UINT internaloffset = 0; + // dx_ctx.context->IASetVertexBuffers(0, 1, &vb, &stride, &internaloffset); + return 0; +} + +int kinc_g4_vertex_buffer_count(kinc_g4_vertex_buffer_t *buffer) { + return buffer->impl.count; +} + +int kinc_g4_vertex_buffer_stride(kinc_g4_vertex_buffer_t *buffer) { + return buffer->impl.stride; +} diff --git a/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/vertexbuffer.h b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/vertexbuffer.h new file mode 100644 index 000000000..6882ac8f2 --- /dev/null +++ b/armorcore/sources/backends/direct3d11/kinc/backend/graphics4/vertexbuffer.h @@ -0,0 +1,13 @@ +#pragma once + +struct ID3D11Buffer; + +typedef struct { + struct ID3D11Buffer *vb; + int stride; + int count; + int lockStart; + int lockCount; + float *vertices; + int usage; +} kinc_g4_vertex_buffer_impl_t; diff --git a/armorcore/sources/backends/direct3d12/d3dx12.h b/armorcore/sources/backends/direct3d12/d3dx12.h new file mode 100644 index 000000000..7d7df77d5 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/d3dx12.h @@ -0,0 +1,2654 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License (MIT). +// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. +// +//********************************************************* + +#ifndef __D3DX12_H__ +#define __D3DX12_H__ + +#include "d3d12.h" + +#if defined( __cplusplus ) + +struct CD3DX12_DEFAULT {}; +extern const DECLSPEC_SELECTANY CD3DX12_DEFAULT D3D12_DEFAULT; + +//------------------------------------------------------------------------------------------------ +inline bool operator==( const D3D12_VIEWPORT& l, const D3D12_VIEWPORT& r ) +{ + return l.TopLeftX == r.TopLeftX && l.TopLeftY == r.TopLeftY && l.Width == r.Width && + l.Height == r.Height && l.MinDepth == r.MinDepth && l.MaxDepth == r.MaxDepth; +} + +//------------------------------------------------------------------------------------------------ +inline bool operator!=( const D3D12_VIEWPORT& l, const D3D12_VIEWPORT& r ) +{ return !( l == r ); } + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RECT : public D3D12_RECT +{ + CD3DX12_RECT() = default; + explicit CD3DX12_RECT( const D3D12_RECT& o ) : + D3D12_RECT( o ) + {} + explicit CD3DX12_RECT( + LONG Left, + LONG Top, + LONG Right, + LONG Bottom ) + { + left = Left; + top = Top; + right = Right; + bottom = Bottom; + } + ~CD3DX12_RECT() {} +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_VIEWPORT : public D3D12_VIEWPORT +{ + CD3DX12_VIEWPORT() = default; + explicit CD3DX12_VIEWPORT( const D3D12_VIEWPORT& o ) : + D3D12_VIEWPORT( o ) + {} + explicit CD3DX12_VIEWPORT( + FLOAT topLeftX, + FLOAT topLeftY, + FLOAT width, + FLOAT height, + FLOAT minDepth = D3D12_MIN_DEPTH, + FLOAT maxDepth = D3D12_MAX_DEPTH ) + { + TopLeftX = topLeftX; + TopLeftY = topLeftY; + Width = width; + Height = height; + MinDepth = minDepth; + MaxDepth = maxDepth; + } + explicit CD3DX12_VIEWPORT( + _In_ ID3D12Resource* pResource, + UINT mipSlice = 0, + FLOAT topLeftX = 0.0f, + FLOAT topLeftY = 0.0f, + FLOAT minDepth = D3D12_MIN_DEPTH, + FLOAT maxDepth = D3D12_MAX_DEPTH ) + { + D3D12_RESOURCE_DESC Desc = pResource->GetDesc(); + const UINT64 SubresourceWidth = Desc.Width >> mipSlice; + const UINT64 SubresourceHeight = Desc.Height >> mipSlice; + switch (Desc.Dimension) + { + case D3D12_RESOURCE_DIMENSION_BUFFER: + TopLeftX = topLeftX; + TopLeftY = 0.0f; + Width = Desc.Width - topLeftX; + Height = 1.0f; + break; + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + TopLeftX = topLeftX; + TopLeftY = 0.0f; + Width = (SubresourceWidth ? SubresourceWidth : 1.0f) - topLeftX; + Height = 1.0f; + break; + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: + TopLeftX = topLeftX; + TopLeftY = topLeftY; + Width = (SubresourceWidth ? SubresourceWidth : 1.0f) - topLeftX; + Height = (SubresourceHeight ? SubresourceHeight: 1.0f) - topLeftY; + break; + default: break; + } + + MinDepth = minDepth; + MaxDepth = maxDepth; + } + ~CD3DX12_VIEWPORT() {} +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_BOX : public D3D12_BOX +{ + CD3DX12_BOX() = default; + explicit CD3DX12_BOX( const D3D12_BOX& o ) : + D3D12_BOX( o ) + {} + explicit CD3DX12_BOX( + LONG Left, + LONG Right ) + { + left = Left; + top = 0; + front = 0; + right = Right; + bottom = 1; + back = 1; + } + explicit CD3DX12_BOX( + LONG Left, + LONG Top, + LONG Right, + LONG Bottom ) + { + left = Left; + top = Top; + front = 0; + right = Right; + bottom = Bottom; + back = 1; + } + explicit CD3DX12_BOX( + LONG Left, + LONG Top, + LONG Front, + LONG Right, + LONG Bottom, + LONG Back ) + { + left = Left; + top = Top; + front = Front; + right = Right; + bottom = Bottom; + back = Back; + } + ~CD3DX12_BOX() {} +}; +inline bool operator==( const D3D12_BOX& l, const D3D12_BOX& r ) +{ + return l.left == r.left && l.top == r.top && l.front == r.front && + l.right == r.right && l.bottom == r.bottom && l.back == r.back; +} +inline bool operator!=( const D3D12_BOX& l, const D3D12_BOX& r ) +{ return !( l == r ); } + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_DEPTH_STENCIL_DESC : public D3D12_DEPTH_STENCIL_DESC +{ + CD3DX12_DEPTH_STENCIL_DESC() = default; + explicit CD3DX12_DEPTH_STENCIL_DESC( const D3D12_DEPTH_STENCIL_DESC& o ) : + D3D12_DEPTH_STENCIL_DESC( o ) + {} + explicit CD3DX12_DEPTH_STENCIL_DESC( CD3DX12_DEFAULT ) + { + DepthEnable = TRUE; + DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + DepthFunc = D3D12_COMPARISON_FUNC_LESS; + StencilEnable = FALSE; + StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; + const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = + { D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS }; + FrontFace = defaultStencilOp; + BackFace = defaultStencilOp; + } + explicit CD3DX12_DEPTH_STENCIL_DESC( + BOOL depthEnable, + D3D12_DEPTH_WRITE_MASK depthWriteMask, + D3D12_COMPARISON_FUNC depthFunc, + BOOL stencilEnable, + UINT8 stencilReadMask, + UINT8 stencilWriteMask, + D3D12_STENCIL_OP frontStencilFailOp, + D3D12_STENCIL_OP frontStencilDepthFailOp, + D3D12_STENCIL_OP frontStencilPassOp, + D3D12_COMPARISON_FUNC frontStencilFunc, + D3D12_STENCIL_OP backStencilFailOp, + D3D12_STENCIL_OP backStencilDepthFailOp, + D3D12_STENCIL_OP backStencilPassOp, + D3D12_COMPARISON_FUNC backStencilFunc ) + { + DepthEnable = depthEnable; + DepthWriteMask = depthWriteMask; + DepthFunc = depthFunc; + StencilEnable = stencilEnable; + StencilReadMask = stencilReadMask; + StencilWriteMask = stencilWriteMask; + FrontFace.StencilFailOp = frontStencilFailOp; + FrontFace.StencilDepthFailOp = frontStencilDepthFailOp; + FrontFace.StencilPassOp = frontStencilPassOp; + FrontFace.StencilFunc = frontStencilFunc; + BackFace.StencilFailOp = backStencilFailOp; + BackFace.StencilDepthFailOp = backStencilDepthFailOp; + BackFace.StencilPassOp = backStencilPassOp; + BackFace.StencilFunc = backStencilFunc; + } + ~CD3DX12_DEPTH_STENCIL_DESC() {} +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_DEPTH_STENCIL_DESC1 : public D3D12_DEPTH_STENCIL_DESC1 +{ + CD3DX12_DEPTH_STENCIL_DESC1() = default; + explicit CD3DX12_DEPTH_STENCIL_DESC1( const D3D12_DEPTH_STENCIL_DESC1& o ) : + D3D12_DEPTH_STENCIL_DESC1( o ) + {} + explicit CD3DX12_DEPTH_STENCIL_DESC1( const D3D12_DEPTH_STENCIL_DESC& o ) + { + DepthEnable = o.DepthEnable; + DepthWriteMask = o.DepthWriteMask; + DepthFunc = o.DepthFunc; + StencilEnable = o.StencilEnable; + StencilReadMask = o.StencilReadMask; + StencilWriteMask = o.StencilWriteMask; + FrontFace.StencilFailOp = o.FrontFace.StencilFailOp; + FrontFace.StencilDepthFailOp = o.FrontFace.StencilDepthFailOp; + FrontFace.StencilPassOp = o.FrontFace.StencilPassOp; + FrontFace.StencilFunc = o.FrontFace.StencilFunc; + BackFace.StencilFailOp = o.BackFace.StencilFailOp; + BackFace.StencilDepthFailOp = o.BackFace.StencilDepthFailOp; + BackFace.StencilPassOp = o.BackFace.StencilPassOp; + BackFace.StencilFunc = o.BackFace.StencilFunc; + DepthBoundsTestEnable = FALSE; + } + explicit CD3DX12_DEPTH_STENCIL_DESC1( CD3DX12_DEFAULT ) + { + DepthEnable = TRUE; + DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + DepthFunc = D3D12_COMPARISON_FUNC_LESS; + StencilEnable = FALSE; + StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; + const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = + { D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS }; + FrontFace = defaultStencilOp; + BackFace = defaultStencilOp; + DepthBoundsTestEnable = FALSE; + } + explicit CD3DX12_DEPTH_STENCIL_DESC1( + BOOL depthEnable, + D3D12_DEPTH_WRITE_MASK depthWriteMask, + D3D12_COMPARISON_FUNC depthFunc, + BOOL stencilEnable, + UINT8 stencilReadMask, + UINT8 stencilWriteMask, + D3D12_STENCIL_OP frontStencilFailOp, + D3D12_STENCIL_OP frontStencilDepthFailOp, + D3D12_STENCIL_OP frontStencilPassOp, + D3D12_COMPARISON_FUNC frontStencilFunc, + D3D12_STENCIL_OP backStencilFailOp, + D3D12_STENCIL_OP backStencilDepthFailOp, + D3D12_STENCIL_OP backStencilPassOp, + D3D12_COMPARISON_FUNC backStencilFunc, + BOOL depthBoundsTestEnable ) + { + DepthEnable = depthEnable; + DepthWriteMask = depthWriteMask; + DepthFunc = depthFunc; + StencilEnable = stencilEnable; + StencilReadMask = stencilReadMask; + StencilWriteMask = stencilWriteMask; + FrontFace.StencilFailOp = frontStencilFailOp; + FrontFace.StencilDepthFailOp = frontStencilDepthFailOp; + FrontFace.StencilPassOp = frontStencilPassOp; + FrontFace.StencilFunc = frontStencilFunc; + BackFace.StencilFailOp = backStencilFailOp; + BackFace.StencilDepthFailOp = backStencilDepthFailOp; + BackFace.StencilPassOp = backStencilPassOp; + BackFace.StencilFunc = backStencilFunc; + DepthBoundsTestEnable = depthBoundsTestEnable; + } + ~CD3DX12_DEPTH_STENCIL_DESC1() {} + operator D3D12_DEPTH_STENCIL_DESC() const + { + D3D12_DEPTH_STENCIL_DESC D; + D.DepthEnable = DepthEnable; + D.DepthWriteMask = DepthWriteMask; + D.DepthFunc = DepthFunc; + D.StencilEnable = StencilEnable; + D.StencilReadMask = StencilReadMask; + D.StencilWriteMask = StencilWriteMask; + D.FrontFace.StencilFailOp = FrontFace.StencilFailOp; + D.FrontFace.StencilDepthFailOp = FrontFace.StencilDepthFailOp; + D.FrontFace.StencilPassOp = FrontFace.StencilPassOp; + D.FrontFace.StencilFunc = FrontFace.StencilFunc; + D.BackFace.StencilFailOp = BackFace.StencilFailOp; + D.BackFace.StencilDepthFailOp = BackFace.StencilDepthFailOp; + D.BackFace.StencilPassOp = BackFace.StencilPassOp; + D.BackFace.StencilFunc = BackFace.StencilFunc; + return D; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_BLEND_DESC : public D3D12_BLEND_DESC +{ + CD3DX12_BLEND_DESC() = default; + explicit CD3DX12_BLEND_DESC( const D3D12_BLEND_DESC& o ) : + D3D12_BLEND_DESC( o ) + {} + explicit CD3DX12_BLEND_DESC( CD3DX12_DEFAULT ) + { + AlphaToCoverageEnable = FALSE; + IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = + { + FALSE,FALSE, + D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) + RenderTarget[ i ] = defaultRenderTargetBlendDesc; + } + ~CD3DX12_BLEND_DESC() {} +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RASTERIZER_DESC : public D3D12_RASTERIZER_DESC +{ + CD3DX12_RASTERIZER_DESC() = default; + explicit CD3DX12_RASTERIZER_DESC( const D3D12_RASTERIZER_DESC& o ) : + D3D12_RASTERIZER_DESC( o ) + {} + explicit CD3DX12_RASTERIZER_DESC( CD3DX12_DEFAULT ) + { + FillMode = D3D12_FILL_MODE_SOLID; + CullMode = D3D12_CULL_MODE_BACK; + FrontCounterClockwise = FALSE; + DepthBias = D3D12_DEFAULT_DEPTH_BIAS; + DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; + SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; + DepthClipEnable = TRUE; + MultisampleEnable = FALSE; + AntialiasedLineEnable = FALSE; + ForcedSampleCount = 0; + ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + } + explicit CD3DX12_RASTERIZER_DESC( + D3D12_FILL_MODE fillMode, + D3D12_CULL_MODE cullMode, + BOOL frontCounterClockwise, + INT depthBias, + FLOAT depthBiasClamp, + FLOAT slopeScaledDepthBias, + BOOL depthClipEnable, + BOOL multisampleEnable, + BOOL antialiasedLineEnable, + UINT forcedSampleCount, + D3D12_CONSERVATIVE_RASTERIZATION_MODE conservativeRaster) + { + FillMode = fillMode; + CullMode = cullMode; + FrontCounterClockwise = frontCounterClockwise; + DepthBias = depthBias; + DepthBiasClamp = depthBiasClamp; + SlopeScaledDepthBias = slopeScaledDepthBias; + DepthClipEnable = depthClipEnable; + MultisampleEnable = multisampleEnable; + AntialiasedLineEnable = antialiasedLineEnable; + ForcedSampleCount = forcedSampleCount; + ConservativeRaster = conservativeRaster; + } + ~CD3DX12_RASTERIZER_DESC() {} +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RESOURCE_ALLOCATION_INFO : public D3D12_RESOURCE_ALLOCATION_INFO +{ + CD3DX12_RESOURCE_ALLOCATION_INFO() = default; + explicit CD3DX12_RESOURCE_ALLOCATION_INFO( const D3D12_RESOURCE_ALLOCATION_INFO& o ) : + D3D12_RESOURCE_ALLOCATION_INFO( o ) + {} + CD3DX12_RESOURCE_ALLOCATION_INFO( + UINT64 size, + UINT64 alignment ) + { + SizeInBytes = size; + Alignment = alignment; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_HEAP_PROPERTIES : public D3D12_HEAP_PROPERTIES +{ + CD3DX12_HEAP_PROPERTIES() = default; + explicit CD3DX12_HEAP_PROPERTIES(const D3D12_HEAP_PROPERTIES &o) : + D3D12_HEAP_PROPERTIES(o) + {} + CD3DX12_HEAP_PROPERTIES( + D3D12_CPU_PAGE_PROPERTY cpuPageProperty, + D3D12_MEMORY_POOL memoryPoolPreference, + UINT creationNodeMask = 1, + UINT nodeMask = 1 ) + { + Type = D3D12_HEAP_TYPE_CUSTOM; + CPUPageProperty = cpuPageProperty; + MemoryPoolPreference = memoryPoolPreference; + CreationNodeMask = creationNodeMask; + VisibleNodeMask = nodeMask; + } + explicit CD3DX12_HEAP_PROPERTIES( + D3D12_HEAP_TYPE type, + UINT creationNodeMask = 1, + UINT nodeMask = 1 ) + { + Type = type; + CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + CreationNodeMask = creationNodeMask; + VisibleNodeMask = nodeMask; + } + bool IsCPUAccessible() const + { + return Type == D3D12_HEAP_TYPE_UPLOAD || Type == D3D12_HEAP_TYPE_READBACK || (Type == D3D12_HEAP_TYPE_CUSTOM && + (CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE || CPUPageProperty == D3D12_CPU_PAGE_PROPERTY_WRITE_BACK)); + } +}; +inline bool operator==( const D3D12_HEAP_PROPERTIES& l, const D3D12_HEAP_PROPERTIES& r ) +{ + return l.Type == r.Type && l.CPUPageProperty == r.CPUPageProperty && + l.MemoryPoolPreference == r.MemoryPoolPreference && + l.CreationNodeMask == r.CreationNodeMask && + l.VisibleNodeMask == r.VisibleNodeMask; +} +inline bool operator!=( const D3D12_HEAP_PROPERTIES& l, const D3D12_HEAP_PROPERTIES& r ) +{ return !( l == r ); } + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_HEAP_DESC : public D3D12_HEAP_DESC +{ + CD3DX12_HEAP_DESC() = default; + explicit CD3DX12_HEAP_DESC(const D3D12_HEAP_DESC &o) : + D3D12_HEAP_DESC(o) + {} + CD3DX12_HEAP_DESC( + UINT64 size, + D3D12_HEAP_PROPERTIES properties, + UINT64 alignment = 0, + D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) + { + SizeInBytes = size; + Properties = properties; + Alignment = alignment; + Flags = flags; + } + CD3DX12_HEAP_DESC( + UINT64 size, + D3D12_HEAP_TYPE type, + UINT64 alignment = 0, + D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) + { + SizeInBytes = size; + Properties = CD3DX12_HEAP_PROPERTIES( type ); + Alignment = alignment; + Flags = flags; + } + CD3DX12_HEAP_DESC( + UINT64 size, + D3D12_CPU_PAGE_PROPERTY cpuPageProperty, + D3D12_MEMORY_POOL memoryPoolPreference, + UINT64 alignment = 0, + D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) + { + SizeInBytes = size; + Properties = CD3DX12_HEAP_PROPERTIES( cpuPageProperty, memoryPoolPreference ); + Alignment = alignment; + Flags = flags; + } + CD3DX12_HEAP_DESC( + const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, + D3D12_HEAP_PROPERTIES properties, + D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) + { + SizeInBytes = resAllocInfo.SizeInBytes; + Properties = properties; + Alignment = resAllocInfo.Alignment; + Flags = flags; + } + CD3DX12_HEAP_DESC( + const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, + D3D12_HEAP_TYPE type, + D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) + { + SizeInBytes = resAllocInfo.SizeInBytes; + Properties = CD3DX12_HEAP_PROPERTIES( type ); + Alignment = resAllocInfo.Alignment; + Flags = flags; + } + CD3DX12_HEAP_DESC( + const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, + D3D12_CPU_PAGE_PROPERTY cpuPageProperty, + D3D12_MEMORY_POOL memoryPoolPreference, + D3D12_HEAP_FLAGS flags = D3D12_HEAP_FLAG_NONE ) + { + SizeInBytes = resAllocInfo.SizeInBytes; + Properties = CD3DX12_HEAP_PROPERTIES( cpuPageProperty, memoryPoolPreference ); + Alignment = resAllocInfo.Alignment; + Flags = flags; + } + bool IsCPUAccessible() const + { return static_cast< const CD3DX12_HEAP_PROPERTIES* >( &Properties )->IsCPUAccessible(); } +}; +inline bool operator==( const D3D12_HEAP_DESC& l, const D3D12_HEAP_DESC& r ) +{ + return l.SizeInBytes == r.SizeInBytes && + l.Properties == r.Properties && + l.Alignment == r.Alignment && + l.Flags == r.Flags; +} +inline bool operator!=( const D3D12_HEAP_DESC& l, const D3D12_HEAP_DESC& r ) +{ return !( l == r ); } + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_CLEAR_VALUE : public D3D12_CLEAR_VALUE +{ + CD3DX12_CLEAR_VALUE() = default; + explicit CD3DX12_CLEAR_VALUE(const D3D12_CLEAR_VALUE &o) : + D3D12_CLEAR_VALUE(o) + {} + CD3DX12_CLEAR_VALUE( + DXGI_FORMAT format, + const FLOAT color[4] ) + { + Format = format; + memcpy( Color, color, sizeof( Color ) ); + } + CD3DX12_CLEAR_VALUE( + DXGI_FORMAT format, + FLOAT depth, + UINT8 stencil ) + { + Format = format; + /* Use memcpy to preserve NAN values */ + memcpy( &DepthStencil.Depth, &depth, sizeof( depth ) ); + DepthStencil.Stencil = stencil; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RANGE : public D3D12_RANGE +{ + CD3DX12_RANGE() = default; + explicit CD3DX12_RANGE(const D3D12_RANGE &o) : + D3D12_RANGE(o) + {} + CD3DX12_RANGE( + SIZE_T begin, + SIZE_T end ) + { + Begin = begin; + End = end; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RANGE_UINT64 : public D3D12_RANGE_UINT64 +{ + CD3DX12_RANGE_UINT64() = default; + explicit CD3DX12_RANGE_UINT64(const D3D12_RANGE_UINT64 &o) : + D3D12_RANGE_UINT64(o) + {} + CD3DX12_RANGE_UINT64( + UINT64 begin, + UINT64 end ) + { + Begin = begin; + End = end; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_SUBRESOURCE_RANGE_UINT64 : public D3D12_SUBRESOURCE_RANGE_UINT64 +{ + CD3DX12_SUBRESOURCE_RANGE_UINT64() = default; + explicit CD3DX12_SUBRESOURCE_RANGE_UINT64(const D3D12_SUBRESOURCE_RANGE_UINT64 &o) : + D3D12_SUBRESOURCE_RANGE_UINT64(o) + {} + CD3DX12_SUBRESOURCE_RANGE_UINT64( + UINT subresource, + const D3D12_RANGE_UINT64& range ) + { + Subresource = subresource; + Range = range; + } + CD3DX12_SUBRESOURCE_RANGE_UINT64( + UINT subresource, + UINT64 begin, + UINT64 end ) + { + Subresource = subresource; + Range.Begin = begin; + Range.End = end; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_SHADER_BYTECODE : public D3D12_SHADER_BYTECODE +{ + CD3DX12_SHADER_BYTECODE() = default; + explicit CD3DX12_SHADER_BYTECODE(const D3D12_SHADER_BYTECODE &o) : + D3D12_SHADER_BYTECODE(o) + {} + CD3DX12_SHADER_BYTECODE( + _In_ ID3DBlob* pShaderBlob ) + { + pShaderBytecode = pShaderBlob->GetBufferPointer(); + BytecodeLength = pShaderBlob->GetBufferSize(); + } + CD3DX12_SHADER_BYTECODE( + const void* _pShaderBytecode, + SIZE_T bytecodeLength ) + { + pShaderBytecode = _pShaderBytecode; + BytecodeLength = bytecodeLength; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_TILED_RESOURCE_COORDINATE : public D3D12_TILED_RESOURCE_COORDINATE +{ + CD3DX12_TILED_RESOURCE_COORDINATE() = default; + explicit CD3DX12_TILED_RESOURCE_COORDINATE(const D3D12_TILED_RESOURCE_COORDINATE &o) : + D3D12_TILED_RESOURCE_COORDINATE(o) + {} + CD3DX12_TILED_RESOURCE_COORDINATE( + UINT x, + UINT y, + UINT z, + UINT subresource ) + { + X = x; + Y = y; + Z = z; + Subresource = subresource; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_TILE_REGION_SIZE : public D3D12_TILE_REGION_SIZE +{ + CD3DX12_TILE_REGION_SIZE() = default; + explicit CD3DX12_TILE_REGION_SIZE(const D3D12_TILE_REGION_SIZE &o) : + D3D12_TILE_REGION_SIZE(o) + {} + CD3DX12_TILE_REGION_SIZE( + UINT numTiles, + BOOL useBox, + UINT width, + UINT16 height, + UINT16 depth ) + { + NumTiles = numTiles; + UseBox = useBox; + Width = width; + Height = height; + Depth = depth; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_SUBRESOURCE_TILING : public D3D12_SUBRESOURCE_TILING +{ + CD3DX12_SUBRESOURCE_TILING() = default; + explicit CD3DX12_SUBRESOURCE_TILING(const D3D12_SUBRESOURCE_TILING &o) : + D3D12_SUBRESOURCE_TILING(o) + {} + CD3DX12_SUBRESOURCE_TILING( + UINT widthInTiles, + UINT16 heightInTiles, + UINT16 depthInTiles, + UINT startTileIndexInOverallResource ) + { + WidthInTiles = widthInTiles; + HeightInTiles = heightInTiles; + DepthInTiles = depthInTiles; + StartTileIndexInOverallResource = startTileIndexInOverallResource; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_TILE_SHAPE : public D3D12_TILE_SHAPE +{ + CD3DX12_TILE_SHAPE() = default; + explicit CD3DX12_TILE_SHAPE(const D3D12_TILE_SHAPE &o) : + D3D12_TILE_SHAPE(o) + {} + CD3DX12_TILE_SHAPE( + UINT widthInTexels, + UINT heightInTexels, + UINT depthInTexels ) + { + WidthInTexels = widthInTexels; + HeightInTexels = heightInTexels; + DepthInTexels = depthInTexels; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RESOURCE_BARRIER : public D3D12_RESOURCE_BARRIER +{ + CD3DX12_RESOURCE_BARRIER() = default; + explicit CD3DX12_RESOURCE_BARRIER(const D3D12_RESOURCE_BARRIER &o) : + D3D12_RESOURCE_BARRIER(o) + {} + static inline CD3DX12_RESOURCE_BARRIER Transition( + _In_ ID3D12Resource* pResource, + D3D12_RESOURCE_STATES stateBefore, + D3D12_RESOURCE_STATES stateAfter, + UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE) + { + CD3DX12_RESOURCE_BARRIER result; + ZeroMemory(&result, sizeof(result)); + D3D12_RESOURCE_BARRIER &barrier = result; + result.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + result.Flags = flags; + barrier.Transition.pResource = pResource; + barrier.Transition.StateBefore = stateBefore; + barrier.Transition.StateAfter = stateAfter; + barrier.Transition.Subresource = subresource; + return result; + } + static inline CD3DX12_RESOURCE_BARRIER Aliasing( + _In_ ID3D12Resource* pResourceBefore, + _In_ ID3D12Resource* pResourceAfter) + { + CD3DX12_RESOURCE_BARRIER result; + ZeroMemory(&result, sizeof(result)); + D3D12_RESOURCE_BARRIER &barrier = result; + result.Type = D3D12_RESOURCE_BARRIER_TYPE_ALIASING; + barrier.Aliasing.pResourceBefore = pResourceBefore; + barrier.Aliasing.pResourceAfter = pResourceAfter; + return result; + } + static inline CD3DX12_RESOURCE_BARRIER UAV( + _In_ ID3D12Resource* pResource) + { + CD3DX12_RESOURCE_BARRIER result; + ZeroMemory(&result, sizeof(result)); + D3D12_RESOURCE_BARRIER &barrier = result; + result.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; + barrier.UAV.pResource = pResource; + return result; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_PACKED_MIP_INFO : public D3D12_PACKED_MIP_INFO +{ + CD3DX12_PACKED_MIP_INFO() = default; + explicit CD3DX12_PACKED_MIP_INFO(const D3D12_PACKED_MIP_INFO &o) : + D3D12_PACKED_MIP_INFO(o) + {} + CD3DX12_PACKED_MIP_INFO( + UINT8 numStandardMips, + UINT8 numPackedMips, + UINT numTilesForPackedMips, + UINT startTileIndexInOverallResource ) + { + NumStandardMips = numStandardMips; + NumPackedMips = numPackedMips; + NumTilesForPackedMips = numTilesForPackedMips; + StartTileIndexInOverallResource = startTileIndexInOverallResource; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_SUBRESOURCE_FOOTPRINT : public D3D12_SUBRESOURCE_FOOTPRINT +{ + CD3DX12_SUBRESOURCE_FOOTPRINT() = default; + explicit CD3DX12_SUBRESOURCE_FOOTPRINT(const D3D12_SUBRESOURCE_FOOTPRINT &o) : + D3D12_SUBRESOURCE_FOOTPRINT(o) + {} + CD3DX12_SUBRESOURCE_FOOTPRINT( + DXGI_FORMAT format, + UINT width, + UINT height, + UINT depth, + UINT rowPitch ) + { + Format = format; + Width = width; + Height = height; + Depth = depth; + RowPitch = rowPitch; + } + explicit CD3DX12_SUBRESOURCE_FOOTPRINT( + const D3D12_RESOURCE_DESC& resDesc, + UINT rowPitch ) + { + Format = resDesc.Format; + Width = UINT( resDesc.Width ); + Height = resDesc.Height; + Depth = (resDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? resDesc.DepthOrArraySize : 1); + RowPitch = rowPitch; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_TEXTURE_COPY_LOCATION : public D3D12_TEXTURE_COPY_LOCATION +{ + CD3DX12_TEXTURE_COPY_LOCATION() = default; + explicit CD3DX12_TEXTURE_COPY_LOCATION(const D3D12_TEXTURE_COPY_LOCATION &o) : + D3D12_TEXTURE_COPY_LOCATION(o) + {} + CD3DX12_TEXTURE_COPY_LOCATION(ID3D12Resource* pRes) { pResource = pRes; } + CD3DX12_TEXTURE_COPY_LOCATION(ID3D12Resource* pRes, D3D12_PLACED_SUBRESOURCE_FOOTPRINT const& Footprint) + { + pResource = pRes; + Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + PlacedFootprint = Footprint; + } + CD3DX12_TEXTURE_COPY_LOCATION(ID3D12Resource* pRes, UINT Sub) + { + pResource = pRes; + Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + SubresourceIndex = Sub; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_DESCRIPTOR_RANGE : public D3D12_DESCRIPTOR_RANGE +{ + CD3DX12_DESCRIPTOR_RANGE() = default; + explicit CD3DX12_DESCRIPTOR_RANGE(const D3D12_DESCRIPTOR_RANGE &o) : + D3D12_DESCRIPTOR_RANGE(o) + {} + CD3DX12_DESCRIPTOR_RANGE( + D3D12_DESCRIPTOR_RANGE_TYPE rangeType, + UINT numDescriptors, + UINT baseShaderRegister, + UINT registerSpace = 0, + UINT offsetInDescriptorsFromTableStart = + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) + { + Init(rangeType, numDescriptors, baseShaderRegister, registerSpace, offsetInDescriptorsFromTableStart); + } + + inline void Init( + D3D12_DESCRIPTOR_RANGE_TYPE rangeType, + UINT numDescriptors, + UINT baseShaderRegister, + UINT registerSpace = 0, + UINT offsetInDescriptorsFromTableStart = + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) + { + Init(*this, rangeType, numDescriptors, baseShaderRegister, registerSpace, offsetInDescriptorsFromTableStart); + } + + static inline void Init( + _Out_ D3D12_DESCRIPTOR_RANGE &range, + D3D12_DESCRIPTOR_RANGE_TYPE rangeType, + UINT numDescriptors, + UINT baseShaderRegister, + UINT registerSpace = 0, + UINT offsetInDescriptorsFromTableStart = + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) + { + range.RangeType = rangeType; + range.NumDescriptors = numDescriptors; + range.BaseShaderRegister = baseShaderRegister; + range.RegisterSpace = registerSpace; + range.OffsetInDescriptorsFromTableStart = offsetInDescriptorsFromTableStart; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_DESCRIPTOR_TABLE : public D3D12_ROOT_DESCRIPTOR_TABLE +{ + CD3DX12_ROOT_DESCRIPTOR_TABLE() = default; + explicit CD3DX12_ROOT_DESCRIPTOR_TABLE(const D3D12_ROOT_DESCRIPTOR_TABLE &o) : + D3D12_ROOT_DESCRIPTOR_TABLE(o) + {} + CD3DX12_ROOT_DESCRIPTOR_TABLE( + UINT numDescriptorRanges, + _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* _pDescriptorRanges) + { + Init(numDescriptorRanges, _pDescriptorRanges); + } + + inline void Init( + UINT numDescriptorRanges, + _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* _pDescriptorRanges) + { + Init(*this, numDescriptorRanges, _pDescriptorRanges); + } + + static inline void Init( + _Out_ D3D12_ROOT_DESCRIPTOR_TABLE &rootDescriptorTable, + UINT numDescriptorRanges, + _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* _pDescriptorRanges) + { + rootDescriptorTable.NumDescriptorRanges = numDescriptorRanges; + rootDescriptorTable.pDescriptorRanges = _pDescriptorRanges; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_CONSTANTS : public D3D12_ROOT_CONSTANTS +{ + CD3DX12_ROOT_CONSTANTS() = default; + explicit CD3DX12_ROOT_CONSTANTS(const D3D12_ROOT_CONSTANTS &o) : + D3D12_ROOT_CONSTANTS(o) + {} + CD3DX12_ROOT_CONSTANTS( + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0) + { + Init(num32BitValues, shaderRegister, registerSpace); + } + + inline void Init( + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0) + { + Init(*this, num32BitValues, shaderRegister, registerSpace); + } + + static inline void Init( + _Out_ D3D12_ROOT_CONSTANTS &rootConstants, + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0) + { + rootConstants.Num32BitValues = num32BitValues; + rootConstants.ShaderRegister = shaderRegister; + rootConstants.RegisterSpace = registerSpace; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_DESCRIPTOR : public D3D12_ROOT_DESCRIPTOR +{ + CD3DX12_ROOT_DESCRIPTOR() = default; + explicit CD3DX12_ROOT_DESCRIPTOR(const D3D12_ROOT_DESCRIPTOR &o) : + D3D12_ROOT_DESCRIPTOR(o) + {} + CD3DX12_ROOT_DESCRIPTOR( + UINT shaderRegister, + UINT registerSpace = 0) + { + Init(shaderRegister, registerSpace); + } + + inline void Init( + UINT shaderRegister, + UINT registerSpace = 0) + { + Init(*this, shaderRegister, registerSpace); + } + + static inline void Init(_Out_ D3D12_ROOT_DESCRIPTOR &table, UINT shaderRegister, UINT registerSpace = 0) + { + table.ShaderRegister = shaderRegister; + table.RegisterSpace = registerSpace; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_PARAMETER : public D3D12_ROOT_PARAMETER +{ + CD3DX12_ROOT_PARAMETER() = default; + explicit CD3DX12_ROOT_PARAMETER(const D3D12_ROOT_PARAMETER &o) : + D3D12_ROOT_PARAMETER(o) + {} + + static inline void InitAsDescriptorTable( + _Out_ D3D12_ROOT_PARAMETER &rootParam, + UINT numDescriptorRanges, + _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* pDescriptorRanges, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR_TABLE::Init(rootParam.DescriptorTable, numDescriptorRanges, pDescriptorRanges); + } + + static inline void InitAsConstants( + _Out_ D3D12_ROOT_PARAMETER &rootParam, + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_CONSTANTS::Init(rootParam.Constants, num32BitValues, shaderRegister, registerSpace); + } + + static inline void InitAsConstantBufferView( + _Out_ D3D12_ROOT_PARAMETER &rootParam, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace); + } + + static inline void InitAsShaderResourceView( + _Out_ D3D12_ROOT_PARAMETER &rootParam, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace); + } + + static inline void InitAsUnorderedAccessView( + _Out_ D3D12_ROOT_PARAMETER &rootParam, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR::Init(rootParam.Descriptor, shaderRegister, registerSpace); + } + + inline void InitAsDescriptorTable( + UINT numDescriptorRanges, + _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE* pDescriptorRanges, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + InitAsDescriptorTable(*this, numDescriptorRanges, pDescriptorRanges, visibility); + } + + inline void InitAsConstants( + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + InitAsConstants(*this, num32BitValues, shaderRegister, registerSpace, visibility); + } + + inline void InitAsConstantBufferView( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + InitAsConstantBufferView(*this, shaderRegister, registerSpace, visibility); + } + + inline void InitAsShaderResourceView( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + InitAsShaderResourceView(*this, shaderRegister, registerSpace, visibility); + } + + inline void InitAsUnorderedAccessView( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + InitAsUnorderedAccessView(*this, shaderRegister, registerSpace, visibility); + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_STATIC_SAMPLER_DESC : public D3D12_STATIC_SAMPLER_DESC +{ + CD3DX12_STATIC_SAMPLER_DESC() = default; + explicit CD3DX12_STATIC_SAMPLER_DESC(const D3D12_STATIC_SAMPLER_DESC &o) : + D3D12_STATIC_SAMPLER_DESC(o) + {} + CD3DX12_STATIC_SAMPLER_DESC( + UINT shaderRegister, + D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC, + D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + FLOAT mipLODBias = 0, + UINT maxAnisotropy = 16, + D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + FLOAT minLOD = 0.f, + FLOAT maxLOD = D3D12_FLOAT32_MAX, + D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, + UINT registerSpace = 0) + { + Init( + shaderRegister, + filter, + addressU, + addressV, + addressW, + mipLODBias, + maxAnisotropy, + comparisonFunc, + borderColor, + minLOD, + maxLOD, + shaderVisibility, + registerSpace); + } + + static inline void Init( + _Out_ D3D12_STATIC_SAMPLER_DESC &samplerDesc, + UINT shaderRegister, + D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC, + D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + FLOAT mipLODBias = 0, + UINT maxAnisotropy = 16, + D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + FLOAT minLOD = 0.f, + FLOAT maxLOD = D3D12_FLOAT32_MAX, + D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, + UINT registerSpace = 0) + { + samplerDesc.ShaderRegister = shaderRegister; + samplerDesc.Filter = filter; + samplerDesc.AddressU = addressU; + samplerDesc.AddressV = addressV; + samplerDesc.AddressW = addressW; + samplerDesc.MipLODBias = mipLODBias; + samplerDesc.MaxAnisotropy = maxAnisotropy; + samplerDesc.ComparisonFunc = comparisonFunc; + samplerDesc.BorderColor = borderColor; + samplerDesc.MinLOD = minLOD; + samplerDesc.MaxLOD = maxLOD; + samplerDesc.ShaderVisibility = shaderVisibility; + samplerDesc.RegisterSpace = registerSpace; + } + inline void Init( + UINT shaderRegister, + D3D12_FILTER filter = D3D12_FILTER_ANISOTROPIC, + D3D12_TEXTURE_ADDRESS_MODE addressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + D3D12_TEXTURE_ADDRESS_MODE addressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP, + FLOAT mipLODBias = 0, + UINT maxAnisotropy = 16, + D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL, + D3D12_STATIC_BORDER_COLOR borderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE, + FLOAT minLOD = 0.f, + FLOAT maxLOD = D3D12_FLOAT32_MAX, + D3D12_SHADER_VISIBILITY shaderVisibility = D3D12_SHADER_VISIBILITY_ALL, + UINT registerSpace = 0) + { + Init( + *this, + shaderRegister, + filter, + addressU, + addressV, + addressW, + mipLODBias, + maxAnisotropy, + comparisonFunc, + borderColor, + minLOD, + maxLOD, + shaderVisibility, + registerSpace); + } + +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_SIGNATURE_DESC : public D3D12_ROOT_SIGNATURE_DESC +{ + CD3DX12_ROOT_SIGNATURE_DESC() = default; + explicit CD3DX12_ROOT_SIGNATURE_DESC(const D3D12_ROOT_SIGNATURE_DESC &o) : + D3D12_ROOT_SIGNATURE_DESC(o) + {} + CD3DX12_ROOT_SIGNATURE_DESC( + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) + { + Init(numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); + } + CD3DX12_ROOT_SIGNATURE_DESC(CD3DX12_DEFAULT) + { + Init(0, NULL, 0, NULL, D3D12_ROOT_SIGNATURE_FLAG_NONE); + } + + inline void Init( + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) + { + Init(*this, numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); + } + + static inline void Init( + _Out_ D3D12_ROOT_SIGNATURE_DESC &desc, + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) + { + desc.NumParameters = numParameters; + desc.pParameters = _pParameters; + desc.NumStaticSamplers = numStaticSamplers; + desc.pStaticSamplers = _pStaticSamplers; + desc.Flags = flags; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_DESCRIPTOR_RANGE1 : public D3D12_DESCRIPTOR_RANGE1 +{ + CD3DX12_DESCRIPTOR_RANGE1() = default; + explicit CD3DX12_DESCRIPTOR_RANGE1(const D3D12_DESCRIPTOR_RANGE1 &o) : + D3D12_DESCRIPTOR_RANGE1(o) + {} + CD3DX12_DESCRIPTOR_RANGE1( + D3D12_DESCRIPTOR_RANGE_TYPE rangeType, + UINT numDescriptors, + UINT baseShaderRegister, + UINT registerSpace = 0, + D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE, + UINT offsetInDescriptorsFromTableStart = + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) + { + Init(rangeType, numDescriptors, baseShaderRegister, registerSpace, flags, offsetInDescriptorsFromTableStart); + } + + inline void Init( + D3D12_DESCRIPTOR_RANGE_TYPE rangeType, + UINT numDescriptors, + UINT baseShaderRegister, + UINT registerSpace = 0, + D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE, + UINT offsetInDescriptorsFromTableStart = + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) + { + Init(*this, rangeType, numDescriptors, baseShaderRegister, registerSpace, flags, offsetInDescriptorsFromTableStart); + } + + static inline void Init( + _Out_ D3D12_DESCRIPTOR_RANGE1 &range, + D3D12_DESCRIPTOR_RANGE_TYPE rangeType, + UINT numDescriptors, + UINT baseShaderRegister, + UINT registerSpace = 0, + D3D12_DESCRIPTOR_RANGE_FLAGS flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE, + UINT offsetInDescriptorsFromTableStart = + D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) + { + range.RangeType = rangeType; + range.NumDescriptors = numDescriptors; + range.BaseShaderRegister = baseShaderRegister; + range.RegisterSpace = registerSpace; + range.Flags = flags; + range.OffsetInDescriptorsFromTableStart = offsetInDescriptorsFromTableStart; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_DESCRIPTOR_TABLE1 : public D3D12_ROOT_DESCRIPTOR_TABLE1 +{ + CD3DX12_ROOT_DESCRIPTOR_TABLE1() = default; + explicit CD3DX12_ROOT_DESCRIPTOR_TABLE1(const D3D12_ROOT_DESCRIPTOR_TABLE1 &o) : + D3D12_ROOT_DESCRIPTOR_TABLE1(o) + {} + CD3DX12_ROOT_DESCRIPTOR_TABLE1( + UINT numDescriptorRanges, + _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* _pDescriptorRanges) + { + Init(numDescriptorRanges, _pDescriptorRanges); + } + + inline void Init( + UINT numDescriptorRanges, + _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* _pDescriptorRanges) + { + Init(*this, numDescriptorRanges, _pDescriptorRanges); + } + + static inline void Init( + _Out_ D3D12_ROOT_DESCRIPTOR_TABLE1 &rootDescriptorTable, + UINT numDescriptorRanges, + _In_reads_opt_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* _pDescriptorRanges) + { + rootDescriptorTable.NumDescriptorRanges = numDescriptorRanges; + rootDescriptorTable.pDescriptorRanges = _pDescriptorRanges; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_DESCRIPTOR1 : public D3D12_ROOT_DESCRIPTOR1 +{ + CD3DX12_ROOT_DESCRIPTOR1() = default; + explicit CD3DX12_ROOT_DESCRIPTOR1(const D3D12_ROOT_DESCRIPTOR1 &o) : + D3D12_ROOT_DESCRIPTOR1(o) + {} + CD3DX12_ROOT_DESCRIPTOR1( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE) + { + Init(shaderRegister, registerSpace, flags); + } + + inline void Init( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE) + { + Init(*this, shaderRegister, registerSpace, flags); + } + + static inline void Init( + _Out_ D3D12_ROOT_DESCRIPTOR1 &table, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE) + { + table.ShaderRegister = shaderRegister; + table.RegisterSpace = registerSpace; + table.Flags = flags; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_ROOT_PARAMETER1 : public D3D12_ROOT_PARAMETER1 +{ + CD3DX12_ROOT_PARAMETER1() = default; + explicit CD3DX12_ROOT_PARAMETER1(const D3D12_ROOT_PARAMETER1 &o) : + D3D12_ROOT_PARAMETER1(o) + {} + + static inline void InitAsDescriptorTable( + _Out_ D3D12_ROOT_PARAMETER1 &rootParam, + UINT numDescriptorRanges, + _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* pDescriptorRanges, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR_TABLE1::Init(rootParam.DescriptorTable, numDescriptorRanges, pDescriptorRanges); + } + + static inline void InitAsConstants( + _Out_ D3D12_ROOT_PARAMETER1 &rootParam, + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_CONSTANTS::Init(rootParam.Constants, num32BitValues, shaderRegister, registerSpace); + } + + static inline void InitAsConstantBufferView( + _Out_ D3D12_ROOT_PARAMETER1 &rootParam, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR1::Init(rootParam.Descriptor, shaderRegister, registerSpace, flags); + } + + static inline void InitAsShaderResourceView( + _Out_ D3D12_ROOT_PARAMETER1 &rootParam, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR1::Init(rootParam.Descriptor, shaderRegister, registerSpace, flags); + } + + static inline void InitAsUnorderedAccessView( + _Out_ D3D12_ROOT_PARAMETER1 &rootParam, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV; + rootParam.ShaderVisibility = visibility; + CD3DX12_ROOT_DESCRIPTOR1::Init(rootParam.Descriptor, shaderRegister, registerSpace, flags); + } + + inline void InitAsDescriptorTable( + UINT numDescriptorRanges, + _In_reads_(numDescriptorRanges) const D3D12_DESCRIPTOR_RANGE1* pDescriptorRanges, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + InitAsDescriptorTable(*this, numDescriptorRanges, pDescriptorRanges, visibility); + } + + inline void InitAsConstants( + UINT num32BitValues, + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + InitAsConstants(*this, num32BitValues, shaderRegister, registerSpace, visibility); + } + + inline void InitAsConstantBufferView( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + InitAsConstantBufferView(*this, shaderRegister, registerSpace, flags, visibility); + } + + inline void InitAsShaderResourceView( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + InitAsShaderResourceView(*this, shaderRegister, registerSpace, flags, visibility); + } + + inline void InitAsUnorderedAccessView( + UINT shaderRegister, + UINT registerSpace = 0, + D3D12_ROOT_DESCRIPTOR_FLAGS flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE, + D3D12_SHADER_VISIBILITY visibility = D3D12_SHADER_VISIBILITY_ALL) + { + InitAsUnorderedAccessView(*this, shaderRegister, registerSpace, flags, visibility); + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC : public D3D12_VERSIONED_ROOT_SIGNATURE_DESC +{ + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC() = default; + explicit CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(const D3D12_VERSIONED_ROOT_SIGNATURE_DESC &o) : + D3D12_VERSIONED_ROOT_SIGNATURE_DESC(o) + {} + explicit CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(const D3D12_ROOT_SIGNATURE_DESC &o) + { + Version = D3D_ROOT_SIGNATURE_VERSION_1_0; + Desc_1_0 = o; + } + explicit CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(const D3D12_ROOT_SIGNATURE_DESC1 &o) + { + Version = D3D_ROOT_SIGNATURE_VERSION_1_1; + Desc_1_1 = o; + } + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC( + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) + { + Init_1_0(numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); + } + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC( + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER1* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) + { + Init_1_1(numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); + } + CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC(CD3DX12_DEFAULT) + { + Init_1_1(0, NULL, 0, NULL, D3D12_ROOT_SIGNATURE_FLAG_NONE); + } + + inline void Init_1_0( + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) + { + Init_1_0(*this, numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); + } + + static inline void Init_1_0( + _Out_ D3D12_VERSIONED_ROOT_SIGNATURE_DESC &desc, + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) + { + desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_0; + desc.Desc_1_0.NumParameters = numParameters; + desc.Desc_1_0.pParameters = _pParameters; + desc.Desc_1_0.NumStaticSamplers = numStaticSamplers; + desc.Desc_1_0.pStaticSamplers = _pStaticSamplers; + desc.Desc_1_0.Flags = flags; + } + + inline void Init_1_1( + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER1* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) + { + Init_1_1(*this, numParameters, _pParameters, numStaticSamplers, _pStaticSamplers, flags); + } + + static inline void Init_1_1( + _Out_ D3D12_VERSIONED_ROOT_SIGNATURE_DESC &desc, + UINT numParameters, + _In_reads_opt_(numParameters) const D3D12_ROOT_PARAMETER1* _pParameters, + UINT numStaticSamplers = 0, + _In_reads_opt_(numStaticSamplers) const D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers = NULL, + D3D12_ROOT_SIGNATURE_FLAGS flags = D3D12_ROOT_SIGNATURE_FLAG_NONE) + { + desc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1; + desc.Desc_1_1.NumParameters = numParameters; + desc.Desc_1_1.pParameters = _pParameters; + desc.Desc_1_1.NumStaticSamplers = numStaticSamplers; + desc.Desc_1_1.pStaticSamplers = _pStaticSamplers; + desc.Desc_1_1.Flags = flags; + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_CPU_DESCRIPTOR_HANDLE : public D3D12_CPU_DESCRIPTOR_HANDLE +{ + CD3DX12_CPU_DESCRIPTOR_HANDLE() = default; + explicit CD3DX12_CPU_DESCRIPTOR_HANDLE(const D3D12_CPU_DESCRIPTOR_HANDLE &o) : + D3D12_CPU_DESCRIPTOR_HANDLE(o) + {} + CD3DX12_CPU_DESCRIPTOR_HANDLE(CD3DX12_DEFAULT) { ptr = 0; } + CD3DX12_CPU_DESCRIPTOR_HANDLE(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &other, INT offsetScaledByIncrementSize) + { + InitOffsetted(other, offsetScaledByIncrementSize); + } + CD3DX12_CPU_DESCRIPTOR_HANDLE(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &other, INT offsetInDescriptors, UINT descriptorIncrementSize) + { + InitOffsetted(other, offsetInDescriptors, descriptorIncrementSize); + } + CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset(INT offsetInDescriptors, UINT descriptorIncrementSize) + { + ptr += INT64(offsetInDescriptors) * UINT64(descriptorIncrementSize); + return *this; + } + CD3DX12_CPU_DESCRIPTOR_HANDLE& Offset(INT offsetScaledByIncrementSize) + { + ptr += offsetScaledByIncrementSize; + return *this; + } + bool operator==(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& other) const + { + return (ptr == other.ptr); + } + bool operator!=(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE& other) const + { + return (ptr != other.ptr); + } + CD3DX12_CPU_DESCRIPTOR_HANDLE &operator=(const D3D12_CPU_DESCRIPTOR_HANDLE &other) + { + ptr = other.ptr; + return *this; + } + + inline void InitOffsetted(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize) + { + InitOffsetted(*this, base, offsetScaledByIncrementSize); + } + + inline void InitOffsetted(_In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize) + { + InitOffsetted(*this, base, offsetInDescriptors, descriptorIncrementSize); + } + + static inline void InitOffsetted(_Out_ D3D12_CPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize) + { + handle.ptr = base.ptr + offsetScaledByIncrementSize; + } + + static inline void InitOffsetted(_Out_ D3D12_CPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_CPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize) + { + handle.ptr = base.ptr + INT64(offsetInDescriptors) * UINT64(descriptorIncrementSize); + } +}; + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_GPU_DESCRIPTOR_HANDLE : public D3D12_GPU_DESCRIPTOR_HANDLE +{ + CD3DX12_GPU_DESCRIPTOR_HANDLE() = default; + explicit CD3DX12_GPU_DESCRIPTOR_HANDLE(const D3D12_GPU_DESCRIPTOR_HANDLE &o) : + D3D12_GPU_DESCRIPTOR_HANDLE(o) + {} + CD3DX12_GPU_DESCRIPTOR_HANDLE(CD3DX12_DEFAULT) { ptr = 0; } + CD3DX12_GPU_DESCRIPTOR_HANDLE(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &other, INT offsetScaledByIncrementSize) + { + InitOffsetted(other, offsetScaledByIncrementSize); + } + CD3DX12_GPU_DESCRIPTOR_HANDLE(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &other, INT offsetInDescriptors, UINT descriptorIncrementSize) + { + InitOffsetted(other, offsetInDescriptors, descriptorIncrementSize); + } + CD3DX12_GPU_DESCRIPTOR_HANDLE& Offset(INT offsetInDescriptors, UINT descriptorIncrementSize) + { + ptr += INT64(offsetInDescriptors) * UINT64(descriptorIncrementSize); + return *this; + } + CD3DX12_GPU_DESCRIPTOR_HANDLE& Offset(INT offsetScaledByIncrementSize) + { + ptr += offsetScaledByIncrementSize; + return *this; + } + inline bool operator==(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE& other) const + { + return (ptr == other.ptr); + } + inline bool operator!=(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE& other) const + { + return (ptr != other.ptr); + } + CD3DX12_GPU_DESCRIPTOR_HANDLE &operator=(const D3D12_GPU_DESCRIPTOR_HANDLE &other) + { + ptr = other.ptr; + return *this; + } + + inline void InitOffsetted(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize) + { + InitOffsetted(*this, base, offsetScaledByIncrementSize); + } + + inline void InitOffsetted(_In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize) + { + InitOffsetted(*this, base, offsetInDescriptors, descriptorIncrementSize); + } + + static inline void InitOffsetted(_Out_ D3D12_GPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetScaledByIncrementSize) + { + handle.ptr = base.ptr + offsetScaledByIncrementSize; + } + + static inline void InitOffsetted(_Out_ D3D12_GPU_DESCRIPTOR_HANDLE &handle, _In_ const D3D12_GPU_DESCRIPTOR_HANDLE &base, INT offsetInDescriptors, UINT descriptorIncrementSize) + { + handle.ptr = base.ptr + INT64(offsetInDescriptors) * UINT64(descriptorIncrementSize); + } +}; + +//------------------------------------------------------------------------------------------------ +inline UINT D3D12CalcSubresource( UINT MipSlice, UINT ArraySlice, UINT PlaneSlice, UINT MipLevels, UINT ArraySize ) +{ + return MipSlice + ArraySlice * MipLevels + PlaneSlice * MipLevels * ArraySize; +} + +//------------------------------------------------------------------------------------------------ +template +inline void D3D12DecomposeSubresource( UINT Subresource, UINT MipLevels, UINT ArraySize, _Out_ T& MipSlice, _Out_ U& ArraySlice, _Out_ V& PlaneSlice ) +{ + MipSlice = static_cast(Subresource % MipLevels); + ArraySlice = static_cast((Subresource / MipLevels) % ArraySize); + PlaneSlice = static_cast(Subresource / (MipLevels * ArraySize)); +} + +//------------------------------------------------------------------------------------------------ +inline UINT8 D3D12GetFormatPlaneCount( + _In_ ID3D12Device* pDevice, + DXGI_FORMAT Format + ) +{ + D3D12_FEATURE_DATA_FORMAT_INFO formatInfo = {Format}; + if (FAILED(pDevice->CheckFeatureSupport(D3D12_FEATURE_FORMAT_INFO, &formatInfo, sizeof(formatInfo)))) + { + return 0; + } + return formatInfo.PlaneCount; +} + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RESOURCE_DESC : public D3D12_RESOURCE_DESC +{ + CD3DX12_RESOURCE_DESC() = default; + explicit CD3DX12_RESOURCE_DESC( const D3D12_RESOURCE_DESC& o ) : + D3D12_RESOURCE_DESC( o ) + {} + CD3DX12_RESOURCE_DESC( + D3D12_RESOURCE_DIMENSION dimension, + UINT64 alignment, + UINT64 width, + UINT height, + UINT16 depthOrArraySize, + UINT16 mipLevels, + DXGI_FORMAT format, + UINT sampleCount, + UINT sampleQuality, + D3D12_TEXTURE_LAYOUT layout, + D3D12_RESOURCE_FLAGS flags ) + { + Dimension = dimension; + Alignment = alignment; + Width = width; + Height = height; + DepthOrArraySize = depthOrArraySize; + MipLevels = mipLevels; + Format = format; + SampleDesc.Count = sampleCount; + SampleDesc.Quality = sampleQuality; + Layout = layout; + Flags = flags; + } + static inline CD3DX12_RESOURCE_DESC Buffer( + const D3D12_RESOURCE_ALLOCATION_INFO& resAllocInfo, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE ) + { + return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_BUFFER, resAllocInfo.Alignment, resAllocInfo.SizeInBytes, + 1, 1, 1, DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags ); + } + static inline CD3DX12_RESOURCE_DESC Buffer( + UINT64 width, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + UINT64 alignment = 0 ) + { + return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_BUFFER, alignment, width, 1, 1, 1, + DXGI_FORMAT_UNKNOWN, 1, 0, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, flags ); + } + static inline CD3DX12_RESOURCE_DESC Tex1D( + DXGI_FORMAT format, + UINT64 width, + UINT16 arraySize = 1, + UINT16 mipLevels = 0, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, + UINT64 alignment = 0 ) + { + return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_TEXTURE1D, alignment, width, 1, arraySize, + mipLevels, format, 1, 0, layout, flags ); + } + static inline CD3DX12_RESOURCE_DESC Tex2D( + DXGI_FORMAT format, + UINT64 width, + UINT height, + UINT16 arraySize = 1, + UINT16 mipLevels = 0, + UINT sampleCount = 1, + UINT sampleQuality = 0, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, + UINT64 alignment = 0 ) + { + return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_TEXTURE2D, alignment, width, height, arraySize, + mipLevels, format, sampleCount, sampleQuality, layout, flags ); + } + static inline CD3DX12_RESOURCE_DESC Tex3D( + DXGI_FORMAT format, + UINT64 width, + UINT height, + UINT16 depth, + UINT16 mipLevels = 0, + D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE, + D3D12_TEXTURE_LAYOUT layout = D3D12_TEXTURE_LAYOUT_UNKNOWN, + UINT64 alignment = 0 ) + { + return CD3DX12_RESOURCE_DESC( D3D12_RESOURCE_DIMENSION_TEXTURE3D, alignment, width, height, depth, + mipLevels, format, 1, 0, layout, flags ); + } + inline UINT16 Depth() const + { return (Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1); } + inline UINT16 ArraySize() const + { return (Dimension != D3D12_RESOURCE_DIMENSION_TEXTURE3D ? DepthOrArraySize : 1); } + inline UINT8 PlaneCount(_In_ ID3D12Device* pDevice) const + { return D3D12GetFormatPlaneCount(pDevice, Format); } + inline UINT Subresources(_In_ ID3D12Device* pDevice) const + { return MipLevels * ArraySize() * PlaneCount(pDevice); } + inline UINT CalcSubresource(UINT MipSlice, UINT ArraySlice, UINT PlaneSlice) + { return D3D12CalcSubresource(MipSlice, ArraySlice, PlaneSlice, MipLevels, ArraySize()); } +}; +inline bool operator==( const D3D12_RESOURCE_DESC& l, const D3D12_RESOURCE_DESC& r ) +{ + return l.Dimension == r.Dimension && + l.Alignment == r.Alignment && + l.Width == r.Width && + l.Height == r.Height && + l.DepthOrArraySize == r.DepthOrArraySize && + l.MipLevels == r.MipLevels && + l.Format == r.Format && + l.SampleDesc.Count == r.SampleDesc.Count && + l.SampleDesc.Quality == r.SampleDesc.Quality && + l.Layout == r.Layout && + l.Flags == r.Flags; +} +inline bool operator!=( const D3D12_RESOURCE_DESC& l, const D3D12_RESOURCE_DESC& r ) +{ return !( l == r ); } + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_VIEW_INSTANCING_DESC : public D3D12_VIEW_INSTANCING_DESC +{ + CD3DX12_VIEW_INSTANCING_DESC() = default; + explicit CD3DX12_VIEW_INSTANCING_DESC( const D3D12_VIEW_INSTANCING_DESC& o ) : + D3D12_VIEW_INSTANCING_DESC( o ) + {} + explicit CD3DX12_VIEW_INSTANCING_DESC( CD3DX12_DEFAULT ) + { + ViewInstanceCount = 0; + pViewInstanceLocations = nullptr; + Flags = D3D12_VIEW_INSTANCING_FLAG_NONE; + } + explicit CD3DX12_VIEW_INSTANCING_DESC( + UINT InViewInstanceCount, + const D3D12_VIEW_INSTANCE_LOCATION* InViewInstanceLocations, + D3D12_VIEW_INSTANCING_FLAGS InFlags) + { + ViewInstanceCount = InViewInstanceCount; + pViewInstanceLocations = InViewInstanceLocations; + Flags = InFlags; + } + ~CD3DX12_VIEW_INSTANCING_DESC() {} +}; + +//------------------------------------------------------------------------------------------------ +// Row-by-row memcpy +inline void MemcpySubresource( + _In_ const D3D12_MEMCPY_DEST* pDest, + _In_ const D3D12_SUBRESOURCE_DATA* pSrc, + SIZE_T RowSizeInBytes, + UINT NumRows, + UINT NumSlices) +{ + for (UINT z = 0; z < NumSlices; ++z) + { + BYTE* pDestSlice = reinterpret_cast(pDest->pData) + pDest->SlicePitch * z; + const BYTE* pSrcSlice = reinterpret_cast(pSrc->pData) + pSrc->SlicePitch * z; + for (UINT y = 0; y < NumRows; ++y) + { + memcpy(pDestSlice + pDest->RowPitch * y, + pSrcSlice + pSrc->RowPitch * y, + RowSizeInBytes); + } + } +} + +//------------------------------------------------------------------------------------------------ +// Returns required size of a buffer to be used for data upload +inline UINT64 GetRequiredIntermediateSize( + _In_ ID3D12Resource* pDestinationResource, + _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, + _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources) +{ + D3D12_RESOURCE_DESC Desc = pDestinationResource->GetDesc(); + UINT64 RequiredSize = 0; + + ID3D12Device* pDevice; + pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast(&pDevice)); + pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, 0, nullptr, nullptr, nullptr, &RequiredSize); + pDevice->Release(); + + return RequiredSize; +} + +//------------------------------------------------------------------------------------------------ +// All arrays must be populated (e.g. by calling GetCopyableFootprints) +inline UINT64 UpdateSubresources( + _In_ ID3D12GraphicsCommandList* pCmdList, + _In_ ID3D12Resource* pDestinationResource, + _In_ ID3D12Resource* pIntermediate, + _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, + _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources, + UINT64 RequiredSize, + _In_reads_(NumSubresources) const D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts, + _In_reads_(NumSubresources) const UINT* pNumRows, + _In_reads_(NumSubresources) const UINT64* pRowSizesInBytes, + _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData) +{ + // Minor validation + D3D12_RESOURCE_DESC IntermediateDesc = pIntermediate->GetDesc(); + D3D12_RESOURCE_DESC DestinationDesc = pDestinationResource->GetDesc(); + if (IntermediateDesc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER || + IntermediateDesc.Width < RequiredSize + pLayouts[0].Offset || + RequiredSize > (SIZE_T)-1 || + (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER && + (FirstSubresource != 0 || NumSubresources != 1))) + { + return 0; + } + + BYTE* pData; + HRESULT hr = pIntermediate->Map(0, NULL, reinterpret_cast(&pData)); + if (FAILED(hr)) + { + return 0; + } + + for (UINT i = 0; i < NumSubresources; ++i) + { + if (pRowSizesInBytes[i] > (SIZE_T)-1) return 0; + D3D12_MEMCPY_DEST DestData = { pData + pLayouts[i].Offset, pLayouts[i].Footprint.RowPitch, SIZE_T(pLayouts[i].Footprint.RowPitch) * SIZE_T(pNumRows[i]) }; + MemcpySubresource(&DestData, &pSrcData[i], (SIZE_T)pRowSizesInBytes[i], pNumRows[i], pLayouts[i].Footprint.Depth); + } + pIntermediate->Unmap(0, NULL); + + if (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) + { + pCmdList->CopyBufferRegion( + pDestinationResource, 0, pIntermediate, pLayouts[0].Offset, pLayouts[0].Footprint.Width); + } + else + { + for (UINT i = 0; i < NumSubresources; ++i) + { + CD3DX12_TEXTURE_COPY_LOCATION Dst(pDestinationResource, i + FirstSubresource); + CD3DX12_TEXTURE_COPY_LOCATION Src(pIntermediate, pLayouts[i]); + pCmdList->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr); + } + } + return RequiredSize; +} + +//------------------------------------------------------------------------------------------------ +// Heap-allocating UpdateSubresources implementation +inline UINT64 UpdateSubresources( + _In_ ID3D12GraphicsCommandList* pCmdList, + _In_ ID3D12Resource* pDestinationResource, + _In_ ID3D12Resource* pIntermediate, + UINT64 IntermediateOffset, + _In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource, + _In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources, + _In_reads_(NumSubresources) D3D12_SUBRESOURCE_DATA* pSrcData) +{ + UINT64 RequiredSize = 0; + UINT64 MemToAlloc = static_cast(sizeof(D3D12_PLACED_SUBRESOURCE_FOOTPRINT) + sizeof(UINT) + sizeof(UINT64)) * NumSubresources; + if (MemToAlloc > SIZE_MAX) + { + return 0; + } + void* pMem = HeapAlloc(GetProcessHeap(), 0, static_cast(MemToAlloc)); + if (pMem == NULL) + { + return 0; + } + D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts = reinterpret_cast(pMem); + UINT64* pRowSizesInBytes = reinterpret_cast(pLayouts + NumSubresources); + UINT* pNumRows = reinterpret_cast(pRowSizesInBytes + NumSubresources); + + D3D12_RESOURCE_DESC Desc = pDestinationResource->GetDesc(); + ID3D12Device* pDevice; + pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast(&pDevice)); + pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, pLayouts, pNumRows, pRowSizesInBytes, &RequiredSize); + pDevice->Release(); + + UINT64 Result = UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, pLayouts, pNumRows, pRowSizesInBytes, pSrcData); + HeapFree(GetProcessHeap(), 0, pMem); + return Result; +} + +//------------------------------------------------------------------------------------------------ +// Stack-allocating UpdateSubresources implementation +template +inline UINT64 UpdateSubresources( + _In_ ID3D12GraphicsCommandList* pCmdList, + _In_ ID3D12Resource* pDestinationResource, + _In_ ID3D12Resource* pIntermediate, + UINT64 IntermediateOffset, + _In_range_(0, MaxSubresources) UINT FirstSubresource, + _In_range_(1, MaxSubresources - FirstSubresource) UINT NumSubresources, + _In_reads_(NumSubresources) D3D12_SUBRESOURCE_DATA* pSrcData) +{ + UINT64 RequiredSize = 0; + D3D12_PLACED_SUBRESOURCE_FOOTPRINT Layouts[MaxSubresources]; + UINT NumRows[MaxSubresources]; + UINT64 RowSizesInBytes[MaxSubresources]; + + D3D12_RESOURCE_DESC Desc = pDestinationResource->GetDesc(); + ID3D12Device* pDevice; + pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast(&pDevice)); + pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, Layouts, NumRows, RowSizesInBytes, &RequiredSize); + pDevice->Release(); + + return UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, Layouts, NumRows, RowSizesInBytes, pSrcData); +} + +//------------------------------------------------------------------------------------------------ +inline bool D3D12IsLayoutOpaque( D3D12_TEXTURE_LAYOUT Layout ) +{ return Layout == D3D12_TEXTURE_LAYOUT_UNKNOWN || Layout == D3D12_TEXTURE_LAYOUT_64KB_UNDEFINED_SWIZZLE; } + +//------------------------------------------------------------------------------------------------ +template +inline ID3D12CommandList * const * CommandListCast(t_CommandListType * const * pp) +{ + // This cast is useful for passing strongly typed command list pointers into + // ExecuteCommandLists. + // This cast is valid as long as the const-ness is respected. D3D12 APIs do + // respect the const-ness of their arguments. + return reinterpret_cast(pp); +} + +//------------------------------------------------------------------------------------------------ +// D3D12 exports a new method for serializing root signatures in the Windows 10 Anniversary Update. +// To help enable root signature 1.1 features when they are available and not require maintaining +// two code paths for building root signatures, this helper method reconstructs a 1.0 signature when +// 1.1 is not supported. +inline HRESULT D3DX12SerializeVersionedRootSignature( + _In_ const D3D12_VERSIONED_ROOT_SIGNATURE_DESC* pRootSignatureDesc, + D3D_ROOT_SIGNATURE_VERSION MaxVersion, + _Outptr_ ID3DBlob** ppBlob, + _Always_(_Outptr_opt_result_maybenull_) ID3DBlob** ppErrorBlob) +{ + if (ppErrorBlob != NULL) + { + *ppErrorBlob = NULL; + } + + switch (MaxVersion) + { + case D3D_ROOT_SIGNATURE_VERSION_1_0: + switch (pRootSignatureDesc->Version) + { + case D3D_ROOT_SIGNATURE_VERSION_1_0: + return D3D12SerializeRootSignature(&pRootSignatureDesc->Desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob); + + case D3D_ROOT_SIGNATURE_VERSION_1_1: + { + HRESULT hr = S_OK; + const D3D12_ROOT_SIGNATURE_DESC1& desc_1_1 = pRootSignatureDesc->Desc_1_1; + + const SIZE_T ParametersSize = sizeof(D3D12_ROOT_PARAMETER) * desc_1_1.NumParameters; + void* pParameters = (ParametersSize > 0) ? HeapAlloc(GetProcessHeap(), 0, ParametersSize) : NULL; + if (ParametersSize > 0 && pParameters == NULL) + { + hr = E_OUTOFMEMORY; + } + D3D12_ROOT_PARAMETER* pParameters_1_0 = reinterpret_cast(pParameters); + + if (SUCCEEDED(hr)) + { + for (UINT n = 0; n < desc_1_1.NumParameters; n++) + { + __analysis_assume(ParametersSize == sizeof(D3D12_ROOT_PARAMETER) * desc_1_1.NumParameters); + pParameters_1_0[n].ParameterType = desc_1_1.pParameters[n].ParameterType; + pParameters_1_0[n].ShaderVisibility = desc_1_1.pParameters[n].ShaderVisibility; + + switch (desc_1_1.pParameters[n].ParameterType) + { + case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS: + pParameters_1_0[n].Constants.Num32BitValues = desc_1_1.pParameters[n].Constants.Num32BitValues; + pParameters_1_0[n].Constants.RegisterSpace = desc_1_1.pParameters[n].Constants.RegisterSpace; + pParameters_1_0[n].Constants.ShaderRegister = desc_1_1.pParameters[n].Constants.ShaderRegister; + break; + + case D3D12_ROOT_PARAMETER_TYPE_CBV: + case D3D12_ROOT_PARAMETER_TYPE_SRV: + case D3D12_ROOT_PARAMETER_TYPE_UAV: + pParameters_1_0[n].Descriptor.RegisterSpace = desc_1_1.pParameters[n].Descriptor.RegisterSpace; + pParameters_1_0[n].Descriptor.ShaderRegister = desc_1_1.pParameters[n].Descriptor.ShaderRegister; + break; + + case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE: + const D3D12_ROOT_DESCRIPTOR_TABLE1& table_1_1 = desc_1_1.pParameters[n].DescriptorTable; + + const SIZE_T DescriptorRangesSize = sizeof(D3D12_DESCRIPTOR_RANGE) * table_1_1.NumDescriptorRanges; + void* pDescriptorRanges = (DescriptorRangesSize > 0 && SUCCEEDED(hr)) ? HeapAlloc(GetProcessHeap(), 0, DescriptorRangesSize) : NULL; + if (DescriptorRangesSize > 0 && pDescriptorRanges == NULL) + { + hr = E_OUTOFMEMORY; + } + D3D12_DESCRIPTOR_RANGE* pDescriptorRanges_1_0 = reinterpret_cast(pDescriptorRanges); + + if (SUCCEEDED(hr)) + { + for (UINT x = 0; x < table_1_1.NumDescriptorRanges; x++) + { + __analysis_assume(DescriptorRangesSize == sizeof(D3D12_DESCRIPTOR_RANGE) * table_1_1.NumDescriptorRanges); + pDescriptorRanges_1_0[x].BaseShaderRegister = table_1_1.pDescriptorRanges[x].BaseShaderRegister; + pDescriptorRanges_1_0[x].NumDescriptors = table_1_1.pDescriptorRanges[x].NumDescriptors; + pDescriptorRanges_1_0[x].OffsetInDescriptorsFromTableStart = table_1_1.pDescriptorRanges[x].OffsetInDescriptorsFromTableStart; + pDescriptorRanges_1_0[x].RangeType = table_1_1.pDescriptorRanges[x].RangeType; + pDescriptorRanges_1_0[x].RegisterSpace = table_1_1.pDescriptorRanges[x].RegisterSpace; + } + } + + D3D12_ROOT_DESCRIPTOR_TABLE& table_1_0 = pParameters_1_0[n].DescriptorTable; + table_1_0.NumDescriptorRanges = table_1_1.NumDescriptorRanges; + table_1_0.pDescriptorRanges = pDescriptorRanges_1_0; + } + } + } + + if (SUCCEEDED(hr)) + { + CD3DX12_ROOT_SIGNATURE_DESC desc_1_0(desc_1_1.NumParameters, pParameters_1_0, desc_1_1.NumStaticSamplers, desc_1_1.pStaticSamplers, desc_1_1.Flags); + hr = D3D12SerializeRootSignature(&desc_1_0, D3D_ROOT_SIGNATURE_VERSION_1, ppBlob, ppErrorBlob); + } + + if (pParameters) + { + for (UINT n = 0; n < desc_1_1.NumParameters; n++) + { + if (desc_1_1.pParameters[n].ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) + { + HeapFree(GetProcessHeap(), 0, reinterpret_cast(const_cast(pParameters_1_0[n].DescriptorTable.pDescriptorRanges))); + } + } + HeapFree(GetProcessHeap(), 0, pParameters); + } + return hr; + } + } + break; + + case D3D_ROOT_SIGNATURE_VERSION_1_1: + return D3D12SerializeVersionedRootSignature(pRootSignatureDesc, ppBlob, ppErrorBlob); + } + + return E_INVALIDARG; +} + +//------------------------------------------------------------------------------------------------ +struct CD3DX12_RT_FORMAT_ARRAY : public D3D12_RT_FORMAT_ARRAY +{ + CD3DX12_RT_FORMAT_ARRAY() = default; + explicit CD3DX12_RT_FORMAT_ARRAY(const D3D12_RT_FORMAT_ARRAY& o) + : D3D12_RT_FORMAT_ARRAY(o) + {} + explicit CD3DX12_RT_FORMAT_ARRAY(const DXGI_FORMAT* pFormats, UINT NumFormats) + { + NumRenderTargets = NumFormats; + memcpy(RTFormats, pFormats, sizeof(RTFormats)); + // assumes ARRAY_SIZE(pFormats) == ARRAY_SIZE(RTFormats) + } +}; + +//------------------------------------------------------------------------------------------------ +// Pipeline State Stream Helpers +//------------------------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------------------------ +// Stream Subobjects, i.e. elements of a stream + +struct DefaultSampleMask { operator UINT() { return UINT_MAX; } }; +struct DefaultSampleDesc { operator DXGI_SAMPLE_DESC() { return DXGI_SAMPLE_DESC{1, 0}; } }; + +template +class alignas(void*) CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT +{ +private: + D3D12_PIPELINE_STATE_SUBOBJECT_TYPE _Type; + InnerStructType _Inner; +public: + CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT() noexcept : _Type(Type), _Inner(DefaultArg()) {} + CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT(InnerStructType const& i) : _Type(Type), _Inner(i) {} + CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT& operator=(InnerStructType const& i) { _Inner = i; return *this; } + operator InnerStructType() const { return _Inner; } + operator InnerStructType&() { return _Inner; } +}; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_PIPELINE_STATE_FLAGS, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS> CD3DX12_PIPELINE_STATE_STREAM_FLAGS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK> CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< ID3D12RootSignature*, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE> CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_INPUT_LAYOUT_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT> CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_INDEX_BUFFER_STRIP_CUT_VALUE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE> CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_PRIMITIVE_TOPOLOGY_TYPE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY> CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS> CD3DX12_PIPELINE_STATE_STREAM_VS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS> CD3DX12_PIPELINE_STATE_STREAM_GS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_STREAM_OUTPUT_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT> CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS> CD3DX12_PIPELINE_STATE_STREAM_HS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS> CD3DX12_PIPELINE_STATE_STREAM_DS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS> CD3DX12_PIPELINE_STATE_STREAM_PS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_SHADER_BYTECODE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS> CD3DX12_PIPELINE_STATE_STREAM_CS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_BLEND_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_DEPTH_STENCIL_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_DEPTH_STENCIL_DESC1, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< DXGI_FORMAT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT> CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_RASTERIZER_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_RT_FORMAT_ARRAY, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS> CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< DXGI_SAMPLE_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC, DefaultSampleDesc> CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< UINT, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK, DefaultSampleMask> CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< D3D12_CACHED_PIPELINE_STATE, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO> CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO; +typedef CD3DX12_PIPELINE_STATE_STREAM_SUBOBJECT< CD3DX12_VIEW_INSTANCING_DESC, D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING, CD3DX12_DEFAULT> CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING; + +//------------------------------------------------------------------------------------------------ +// Stream Parser Helpers + +struct ID3DX12PipelineParserCallbacks +{ + // Subobject Callbacks + virtual void FlagsCb(D3D12_PIPELINE_STATE_FLAGS) {} + virtual void NodeMaskCb(UINT) {} + virtual void RootSignatureCb(ID3D12RootSignature*) {} + virtual void InputLayoutCb(const D3D12_INPUT_LAYOUT_DESC&) {} + virtual void IBStripCutValueCb(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE) {} + virtual void PrimitiveTopologyTypeCb(D3D12_PRIMITIVE_TOPOLOGY_TYPE) {} + virtual void VSCb(const D3D12_SHADER_BYTECODE&) {} + virtual void GSCb(const D3D12_SHADER_BYTECODE&) {} + virtual void StreamOutputCb(const D3D12_STREAM_OUTPUT_DESC&) {} + virtual void HSCb(const D3D12_SHADER_BYTECODE&) {} + virtual void DSCb(const D3D12_SHADER_BYTECODE&) {} + virtual void PSCb(const D3D12_SHADER_BYTECODE&) {} + virtual void CSCb(const D3D12_SHADER_BYTECODE&) {} + virtual void BlendStateCb(const D3D12_BLEND_DESC&) {} + virtual void DepthStencilStateCb(const D3D12_DEPTH_STENCIL_DESC&) {} + virtual void DepthStencilState1Cb(const D3D12_DEPTH_STENCIL_DESC1&) {} + virtual void DSVFormatCb(DXGI_FORMAT) {} + virtual void RasterizerStateCb(const D3D12_RASTERIZER_DESC&) {} + virtual void RTVFormatsCb(const D3D12_RT_FORMAT_ARRAY&) {} + virtual void SampleDescCb(const DXGI_SAMPLE_DESC&) {} + virtual void SampleMaskCb(UINT) {} + virtual void ViewInstancingCb(const D3D12_VIEW_INSTANCING_DESC&) {} + virtual void CachedPSOCb(const D3D12_CACHED_PIPELINE_STATE&) {} + + // Error Callbacks + virtual void ErrorBadInputParameter(UINT /*ParameterIndex*/) {} + virtual void ErrorDuplicateSubobject(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE /*DuplicateType*/) {} + virtual void ErrorUnknownSubobject(UINT /*UnknownTypeValue*/) {} + +}; + +// CD3DX12_PIPELINE_STATE_STREAM1 Works on RS3+ (where there is a new view instancing subobject). +// Use CD3DX12_PIPELINE_STATE_STREAM for RS2+ support. +struct CD3DX12_PIPELINE_STATE_STREAM1 +{ + CD3DX12_PIPELINE_STATE_STREAM1() = default; + CD3DX12_PIPELINE_STATE_STREAM1(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , InputLayout(Desc.InputLayout) + , IBStripCutValue(Desc.IBStripCutValue) + , PrimitiveTopologyType(Desc.PrimitiveTopologyType) + , VS(Desc.VS) + , GS(Desc.GS) + , StreamOutput(Desc.StreamOutput) + , HS(Desc.HS) + , DS(Desc.DS) + , PS(Desc.PS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + , ViewInstancingDesc(CD3DX12_VIEW_INSTANCING_DESC(CD3DX12_DEFAULT())) + {} + CD3DX12_PIPELINE_STATE_STREAM1(const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , CS(CD3DX12_SHADER_BYTECODE(Desc.CS)) + , CachedPSO(Desc.CachedPSO) + { + static_cast(DepthStencilState).DepthEnable = false; + } + CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags; + CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask; + CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature; + CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout; + CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE IBStripCutValue; + CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType; + CD3DX12_PIPELINE_STATE_STREAM_VS VS; + CD3DX12_PIPELINE_STATE_STREAM_GS GS; + CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT StreamOutput; + CD3DX12_PIPELINE_STATE_STREAM_HS HS; + CD3DX12_PIPELINE_STATE_STREAM_DS DS; + CD3DX12_PIPELINE_STATE_STREAM_PS PS; + CD3DX12_PIPELINE_STATE_STREAM_CS CS; + CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1 DepthStencilState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat; + CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER RasterizerState; + CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask; + CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO; + CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING ViewInstancingDesc; + D3D12_GRAPHICS_PIPELINE_STATE_DESC GraphicsDescV0() const + { + D3D12_GRAPHICS_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.InputLayout = this->InputLayout; + D.IBStripCutValue = this->IBStripCutValue; + D.PrimitiveTopologyType = this->PrimitiveTopologyType; + D.VS = this->VS; + D.GS = this->GS; + D.StreamOutput = this->StreamOutput; + D.HS = this->HS; + D.DS = this->DS; + D.PS = this->PS; + D.BlendState = this->BlendState; + D.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(D3D12_DEPTH_STENCIL_DESC1(this->DepthStencilState)); + D.DSVFormat = this->DSVFormat; + D.RasterizerState = this->RasterizerState; + D.NumRenderTargets = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets; + memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats)); + D.SampleDesc = this->SampleDesc; + D.SampleMask = this->SampleMask; + D.CachedPSO = this->CachedPSO; + return D; + } + D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDescV0() const + { + D3D12_COMPUTE_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.CS = this->CS; + D.CachedPSO = this->CachedPSO; + return D; + } +}; + +// CD3DX12_PIPELINE_STATE_STREAM works on RS2+ but does not support new subobject(s) added in RS3+. +// See CD3DX12_PIPELINE_STATE_STREAM1 for instance. +struct CD3DX12_PIPELINE_STATE_STREAM +{ + CD3DX12_PIPELINE_STATE_STREAM() = default; + CD3DX12_PIPELINE_STATE_STREAM(const D3D12_GRAPHICS_PIPELINE_STATE_DESC& Desc) + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , InputLayout(Desc.InputLayout) + , IBStripCutValue(Desc.IBStripCutValue) + , PrimitiveTopologyType(Desc.PrimitiveTopologyType) + , VS(Desc.VS) + , GS(Desc.GS) + , StreamOutput(Desc.StreamOutput) + , HS(Desc.HS) + , DS(Desc.DS) + , PS(Desc.PS) + , BlendState(CD3DX12_BLEND_DESC(Desc.BlendState)) + , DepthStencilState(CD3DX12_DEPTH_STENCIL_DESC1(Desc.DepthStencilState)) + , DSVFormat(Desc.DSVFormat) + , RasterizerState(CD3DX12_RASTERIZER_DESC(Desc.RasterizerState)) + , RTVFormats(CD3DX12_RT_FORMAT_ARRAY(Desc.RTVFormats, Desc.NumRenderTargets)) + , SampleDesc(Desc.SampleDesc) + , SampleMask(Desc.SampleMask) + , CachedPSO(Desc.CachedPSO) + {} + CD3DX12_PIPELINE_STATE_STREAM(const D3D12_COMPUTE_PIPELINE_STATE_DESC& Desc) + : Flags(Desc.Flags) + , NodeMask(Desc.NodeMask) + , pRootSignature(Desc.pRootSignature) + , CS(CD3DX12_SHADER_BYTECODE(Desc.CS)) + , CachedPSO(Desc.CachedPSO) + {} + CD3DX12_PIPELINE_STATE_STREAM_FLAGS Flags; + CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK NodeMask; + CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature; + CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout; + CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE IBStripCutValue; + CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType; + CD3DX12_PIPELINE_STATE_STREAM_VS VS; + CD3DX12_PIPELINE_STATE_STREAM_GS GS; + CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT StreamOutput; + CD3DX12_PIPELINE_STATE_STREAM_HS HS; + CD3DX12_PIPELINE_STATE_STREAM_DS DS; + CD3DX12_PIPELINE_STATE_STREAM_PS PS; + CD3DX12_PIPELINE_STATE_STREAM_CS CS; + CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC BlendState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1 DepthStencilState; + CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat; + CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER RasterizerState; + CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC SampleDesc; + CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK SampleMask; + CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO CachedPSO; + D3D12_GRAPHICS_PIPELINE_STATE_DESC GraphicsDescV0() const + { + D3D12_GRAPHICS_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.InputLayout = this->InputLayout; + D.IBStripCutValue = this->IBStripCutValue; + D.PrimitiveTopologyType = this->PrimitiveTopologyType; + D.VS = this->VS; + D.GS = this->GS; + D.StreamOutput = this->StreamOutput; + D.HS = this->HS; + D.DS = this->DS; + D.PS = this->PS; + D.BlendState = this->BlendState; + D.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(D3D12_DEPTH_STENCIL_DESC1(this->DepthStencilState)); + D.DSVFormat = this->DSVFormat; + D.RasterizerState = this->RasterizerState; + D.NumRenderTargets = D3D12_RT_FORMAT_ARRAY(this->RTVFormats).NumRenderTargets; + memcpy(D.RTVFormats, D3D12_RT_FORMAT_ARRAY(this->RTVFormats).RTFormats, sizeof(D.RTVFormats)); + D.SampleDesc = this->SampleDesc; + D.SampleMask = this->SampleMask; + D.CachedPSO = this->CachedPSO; + return D; + } + D3D12_COMPUTE_PIPELINE_STATE_DESC ComputeDescV0() const + { + D3D12_COMPUTE_PIPELINE_STATE_DESC D; + D.Flags = this->Flags; + D.NodeMask = this->NodeMask; + D.pRootSignature = this->pRootSignature; + D.CS = this->CS; + D.CachedPSO = this->CachedPSO; + return D; + } +}; + +struct CD3DX12_PIPELINE_STATE_STREAM_PARSE_HELPER : public ID3DX12PipelineParserCallbacks +{ + CD3DX12_PIPELINE_STATE_STREAM1 PipelineStream; + CD3DX12_PIPELINE_STATE_STREAM_PARSE_HELPER() noexcept + : SeenDSS(false) + { + // Adjust defaults to account for absent members. + PipelineStream.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + + // Depth disabled if no DSV format specified. + static_cast(PipelineStream.DepthStencilState).DepthEnable = false; + } + virtual ~CD3DX12_PIPELINE_STATE_STREAM_PARSE_HELPER() {} + + // ID3DX12PipelineParserCallbacks + void FlagsCb(D3D12_PIPELINE_STATE_FLAGS Flags) {PipelineStream.Flags = Flags;} + void NodeMaskCb(UINT NodeMask) {PipelineStream.NodeMask = NodeMask;} + void RootSignatureCb(ID3D12RootSignature* pRootSignature) {PipelineStream.pRootSignature = pRootSignature;} + void InputLayoutCb(const D3D12_INPUT_LAYOUT_DESC& InputLayout) {PipelineStream.InputLayout = InputLayout;} + void IBStripCutValueCb(D3D12_INDEX_BUFFER_STRIP_CUT_VALUE IBStripCutValue) {PipelineStream.IBStripCutValue = IBStripCutValue;} + void PrimitiveTopologyTypeCb(D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType) {PipelineStream.PrimitiveTopologyType = PrimitiveTopologyType;} + void VSCb(const D3D12_SHADER_BYTECODE& VS) {PipelineStream.VS = VS;} + void GSCb(const D3D12_SHADER_BYTECODE& GS) {PipelineStream.GS = GS;} + void StreamOutputCb(const D3D12_STREAM_OUTPUT_DESC& StreamOutput) {PipelineStream.StreamOutput = StreamOutput;} + void HSCb(const D3D12_SHADER_BYTECODE& HS) {PipelineStream.HS = HS;} + void DSCb(const D3D12_SHADER_BYTECODE& DS) {PipelineStream.DS = DS;} + void PSCb(const D3D12_SHADER_BYTECODE& PS) {PipelineStream.PS = PS;} + void CSCb(const D3D12_SHADER_BYTECODE& CS) {PipelineStream.CS = CS;} + void BlendStateCb(const D3D12_BLEND_DESC& BlendState) {PipelineStream.BlendState = CD3DX12_BLEND_DESC(BlendState);} + void DepthStencilStateCb(const D3D12_DEPTH_STENCIL_DESC& DepthStencilState) + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(DepthStencilState); + SeenDSS = true; + } + void DepthStencilState1Cb(const D3D12_DEPTH_STENCIL_DESC1& DepthStencilState) + { + PipelineStream.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC1(DepthStencilState); + SeenDSS = true; + } + void DSVFormatCb(DXGI_FORMAT DSVFormat) + { + PipelineStream.DSVFormat = DSVFormat; + if (!SeenDSS && DSVFormat != DXGI_FORMAT_UNKNOWN) + { + // Re-enable depth for the default state. + static_cast(PipelineStream.DepthStencilState).DepthEnable = true; + } + } + void RasterizerStateCb(const D3D12_RASTERIZER_DESC& RasterizerState) {PipelineStream.RasterizerState = CD3DX12_RASTERIZER_DESC(RasterizerState);} + void RTVFormatsCb(const D3D12_RT_FORMAT_ARRAY& RTVFormats) {PipelineStream.RTVFormats = RTVFormats;} + void SampleDescCb(const DXGI_SAMPLE_DESC& SampleDesc) {PipelineStream.SampleDesc = SampleDesc;} + void SampleMaskCb(UINT SampleMask) {PipelineStream.SampleMask = SampleMask;} + void ViewInstancingCb(const D3D12_VIEW_INSTANCING_DESC& ViewInstancingDesc) {PipelineStream.ViewInstancingDesc = CD3DX12_VIEW_INSTANCING_DESC(ViewInstancingDesc);} + void CachedPSOCb(const D3D12_CACHED_PIPELINE_STATE& CachedPSO) {PipelineStream.CachedPSO = CachedPSO;} + void ErrorBadInputParameter(UINT) {} + void ErrorDuplicateSubobject(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE) {} + void ErrorUnknownSubobject(UINT) {} + +private: + bool SeenDSS; +}; + +inline D3D12_PIPELINE_STATE_SUBOBJECT_TYPE D3DX12GetBaseSubobjectType(D3D12_PIPELINE_STATE_SUBOBJECT_TYPE SubobjectType) +{ + switch (SubobjectType) + { + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1: + return D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL; + default: + return SubobjectType; + } +} + +inline HRESULT D3DX12ParsePipelineStream(const D3D12_PIPELINE_STATE_STREAM_DESC& Desc, ID3DX12PipelineParserCallbacks* pCallbacks) +{ + if (pCallbacks == nullptr) + { + return E_INVALIDARG; + } + + if (Desc.SizeInBytes == 0 || Desc.pPipelineStateSubobjectStream == nullptr) + { + pCallbacks->ErrorBadInputParameter(1); // first parameter issue + return E_INVALIDARG; + } + + bool SubobjectSeen[D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MAX_VALID] = {}; + for (SIZE_T CurOffset = 0, SizeOfSubobject = 0; CurOffset < Desc.SizeInBytes; CurOffset += SizeOfSubobject) + { + BYTE* pStream = static_cast(Desc.pPipelineStateSubobjectStream)+CurOffset; + auto SubobjectType = *reinterpret_cast(pStream); + if (SubobjectType >= D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_MAX_VALID) + { + pCallbacks->ErrorUnknownSubobject(SubobjectType); + return E_INVALIDARG; + } + if (SubobjectSeen[D3DX12GetBaseSubobjectType(SubobjectType)]) + { + pCallbacks->ErrorDuplicateSubobject(SubobjectType); + return E_INVALIDARG; // disallow subobject duplicates in a stream + } + SubobjectSeen[SubobjectType] = true; + switch (SubobjectType) + { + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_ROOT_SIGNATURE: + pCallbacks->RootSignatureCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::pRootSignature); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VS: + pCallbacks->VSCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::VS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PS: + pCallbacks->PSCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::PS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DS: + pCallbacks->DSCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::DS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_HS: + pCallbacks->HSCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::HS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_GS: + pCallbacks->GSCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::GS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CS: + pCallbacks->CSCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::CS); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_STREAM_OUTPUT: + pCallbacks->StreamOutputCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::StreamOutput); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_BLEND: + pCallbacks->BlendStateCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::BlendState); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_MASK: + pCallbacks->SampleMaskCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::SampleMask); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RASTERIZER: + pCallbacks->RasterizerStateCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::RasterizerState); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL: + pCallbacks->DepthStencilStateCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL1: + pCallbacks->DepthStencilState1Cb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::DepthStencilState); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_INPUT_LAYOUT: + pCallbacks->InputLayoutCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::InputLayout); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_IB_STRIP_CUT_VALUE: + pCallbacks->IBStripCutValueCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::IBStripCutValue); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_PRIMITIVE_TOPOLOGY: + pCallbacks->PrimitiveTopologyTypeCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::PrimitiveTopologyType); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_RENDER_TARGET_FORMATS: + pCallbacks->RTVFormatsCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::RTVFormats); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_DEPTH_STENCIL_FORMAT: + pCallbacks->DSVFormatCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::DSVFormat); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_SAMPLE_DESC: + pCallbacks->SampleDescCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::SampleDesc); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_NODE_MASK: + pCallbacks->NodeMaskCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::NodeMask); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_CACHED_PSO: + pCallbacks->CachedPSOCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::CachedPSO); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_FLAGS: + pCallbacks->FlagsCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM::Flags); + break; + case D3D12_PIPELINE_STATE_SUBOBJECT_TYPE_VIEW_INSTANCING: + pCallbacks->ViewInstancingCb(*reinterpret_cast(pStream)); + SizeOfSubobject = sizeof(CD3DX12_PIPELINE_STATE_STREAM1::ViewInstancingDesc); + break; + default: + pCallbacks->ErrorUnknownSubobject(SubobjectType); + return E_INVALIDARG; + break; + } + } + + return S_OK; +} + + +#endif // defined( __cplusplus ) + +#endif //__D3DX12_H__ + + + diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/Direct3D12.c.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/Direct3D12.c.h new file mode 100644 index 000000000..2ac40ecbb --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/Direct3D12.c.h @@ -0,0 +1,622 @@ +#include "commandlist.h" +#include "indexbuffer.h" +#include "pipeline.h" +#include "vertexbuffer.h" + +#include +#include +#include +#include +#ifdef KINC_WINDOWS +#include +#undef CreateWindow +#endif +#include +#ifdef KINC_WINDOWS +#include +#endif +#include + +/*IDXGIFactory4* dxgiFactory; +ID3D12Device* device; +ID3D12GraphicsCommandList* commandList; +ID3D12CommandQueue* commandQueue; +ID3D12CommandAllocator* commandAllocators[frameCount]; +unsigned currentFrame = 0; +ID3D12Fence* fence; +UINT64 window->current_fence_value[frameCount]; +HANDLE fenceEvent; +ID3D12Resource* renderTargets[frameCount]; +ID3D12DescriptorHeap* rtvHeap; +unsigned rtvDescriptorSize; +ID3D12CommandAllocator* bundleAllocator; +ID3D12RootSignature* rootSignature; +D3D12_VIEWPORT screenViewport; +D3D12_RECT scissorRect; +ID3D12DescriptorHeap* cbvHeap;*/ +// ID3D12DeviceContext* context; +// ID3D12RenderTargetView* renderTargetView; +// ID3D12DepthStencilView* depthStencilView; + +// ID3D12GraphicsCommandList* commandList; + +extern "C" { +ID3D12CommandQueue *commandQueue; +#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN +ID3D12Resource *swapChainRenderTargets[QUEUE_SLOT_COUNT]; +#else +// IDXGISwapChain *swapChain; +#endif +} + +// int window->width; +// int window->height; +// int window->new_width; +// int window->new_height; + +#ifndef KINC_WINDOWS +#define DXGI_SWAP_CHAIN_DESC DXGI_SWAP_CHAIN_DESC1 +#define IDXGISwapChain IDXGISwapChain1 +#endif + +struct RenderEnvironment { + ID3D12Device *device; + ID3D12CommandQueue *queue; +#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN + ID3D12Resource *renderTargets[QUEUE_SLOT_COUNT]; +#else + IDXGISwapChain *swapChain; +#endif +}; + +#ifndef KINC_WINDOWS +#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN +extern "C" void createSwapChain(struct RenderEnvironment *env, int bufferCount); +#else +extern "C" void createSwapChain(struct RenderEnvironment *env, const DXGI_SWAP_CHAIN_DESC1 *desc); +#endif +#endif + +extern bool bilinearFiltering; + +// ID3D12Resource* renderTarget; +// ID3D12DescriptorHeap* renderTargetDescriptorHeap; + +// static UINT64 window->current_fence_value; +// static UINT64 window->current_fence_value[QUEUE_SLOT_COUNT]; +// static HANDLE window->frame_fence_events[QUEUE_SLOT_COUNT]; +// static ID3D12Fence *window->frame_fences[QUEUE_SLOT_COUNT]; +static ID3D12Fence *uploadFence; +static ID3D12GraphicsCommandList *initCommandList; +static ID3D12CommandAllocator *initCommandAllocator; + +extern "C" struct RenderEnvironment createDeviceAndSwapChainHelper(D3D_FEATURE_LEVEL minimumFeatureLevel, const struct DXGI_SWAP_CHAIN_DESC *swapChainDesc) { + struct RenderEnvironment result = {0}; +#ifdef KINC_WINDOWS + kinc_microsoft_affirm(D3D12CreateDevice(NULL, minimumFeatureLevel, IID_PPV_ARGS(&result.device))); + + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + + kinc_microsoft_affirm(result.device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&result.queue))); + + IDXGIFactory4 *dxgiFactory; + kinc_microsoft_affirm(CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory))); + + DXGI_SWAP_CHAIN_DESC swapChainDescCopy = *swapChainDesc; + kinc_microsoft_affirm(dxgiFactory->CreateSwapChain((IUnknown *)result.queue, &swapChainDescCopy, &result.swapChain)); +#else +#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN + createSwapChain(&result, QUEUE_SLOT_COUNT); +#else + createSwapChain(&result, swapChainDesc); +#endif +#endif + return result; +} + +static void waitForFence(ID3D12Fence *fence, UINT64 completionValue, HANDLE waitEvent) { + if (fence->GetCompletedValue() < completionValue) { + kinc_microsoft_affirm(fence->SetEventOnCompletion(completionValue, waitEvent)); + WaitForSingleObject(waitEvent, INFINITE); + } +} + +extern "C" void setupSwapChain(struct dx_window *window) { + /*D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; + heapDesc.NumDescriptors = 1; + heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + device->CreateDescriptorHeap(&heapDesc, IID_GRAPHICS_PPV_ARGS(&renderTargetDescriptorHeap));*/ + + D3D12_RESOURCE_DESC depthTexture = {}; + depthTexture.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + depthTexture.Alignment = 0; + depthTexture.Width = window->width; + depthTexture.Height = window->height; + depthTexture.DepthOrArraySize = 1; + depthTexture.MipLevels = 1; + depthTexture.Format = DXGI_FORMAT_D32_FLOAT; + depthTexture.SampleDesc.Count = 1; + depthTexture.SampleDesc.Quality = 0; + depthTexture.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + depthTexture.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; + + D3D12_CLEAR_VALUE clearValue = {}; + clearValue.Format = DXGI_FORMAT_D32_FLOAT; + clearValue.DepthStencil.Depth = 1.0f; + clearValue.DepthStencil.Stencil = 0; + + D3D12_HEAP_PROPERTIES heapProperties = {}; + heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProperties.CreationNodeMask = 1; + heapProperties.VisibleNodeMask = 1; + + window->current_fence_value = 0; + + for (int i = 0; i < QUEUE_SLOT_COUNT; ++i) { + window->frame_fence_events[i] = CreateEvent(NULL, FALSE, FALSE, NULL); + window->fence_values[i] = 0; + device->CreateFence(window->current_fence_value, D3D12_FENCE_FLAG_NONE, IID_GRAPHICS_PPV_ARGS(&window->frame_fences[i])); + } + + //**swapChain->GetBuffer(window->current_backbuffer, IID_GRAPHICS_PPV_ARGS(&renderTarget)); + //**createRenderTargetView(); +} + +#ifdef KINC_CONSOLE +extern "C" void createDeviceAndSwapChain(struct dx_window *window); +#else +static void createDeviceAndSwapChain(struct dx_window *window) { +#ifdef _DEBUG + ID3D12Debug *debugController = NULL; + D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)); + debugController->EnableDebugLayer(); +#endif + + struct DXGI_SWAP_CHAIN_DESC swapChainDesc; + ZeroMemory(&swapChainDesc, sizeof(swapChainDesc)); + + swapChainDesc.BufferCount = QUEUE_SLOT_COUNT; + swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferDesc.Width = window->width; + swapChainDesc.BufferDesc.Height = window->height; + swapChainDesc.OutputWindow = kinc_windows_window_handle(window->window_index); + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.Windowed = true; + + struct RenderEnvironment renderEnv = createDeviceAndSwapChainHelper(D3D_FEATURE_LEVEL_11_0, &swapChainDesc); + + device = renderEnv.device; + commandQueue = renderEnv.queue; + + // swapChain = renderEnv.swapChain; + + setupSwapChain(NULL); +} +#endif + +static void createRootSignature() { + ID3DBlob *rootBlob; + ID3DBlob *errorBlob; + + D3D12_ROOT_PARAMETER parameters[4] = {}; + + D3D12_DESCRIPTOR_RANGE range; + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + range.NumDescriptors = (UINT)KINC_INTERNAL_G5_TEXTURE_COUNT; + range.BaseShaderRegister = 0; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + parameters[0].DescriptorTable.NumDescriptorRanges = 1; + parameters[0].DescriptorTable.pDescriptorRanges = ⦥ + + D3D12_DESCRIPTOR_RANGE range2; + range2.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + range2.NumDescriptors = (UINT)KINC_INTERNAL_G5_TEXTURE_COUNT; + range2.BaseShaderRegister = 0; + range2.RegisterSpace = 0; + range2.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + parameters[1].DescriptorTable.NumDescriptorRanges = 1; + parameters[1].DescriptorTable.pDescriptorRanges = &range2; + + parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; + parameters[2].Descriptor.ShaderRegister = 0; + parameters[2].Descriptor.RegisterSpace = 0; + + parameters[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + parameters[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + parameters[3].Descriptor.ShaderRegister = 0; + parameters[3].Descriptor.RegisterSpace = 0; + + D3D12_STATIC_SAMPLER_DESC samplers[KINC_INTERNAL_G5_TEXTURE_COUNT * 2]; + for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) { + samplers[i].ShaderRegister = i; + samplers[i].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + samplers[i].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[i].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[i].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[i].MipLODBias = 0; + samplers[i].MaxAnisotropy = 16; + samplers[i].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; + samplers[i].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE; + samplers[i].MinLOD = 0.0f; + samplers[i].MaxLOD = D3D12_FLOAT32_MAX; + samplers[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + samplers[i].RegisterSpace = 0; + } + for (int i = KINC_INTERNAL_G5_TEXTURE_COUNT; i < KINC_INTERNAL_G5_TEXTURE_COUNT * 2; ++i) { + samplers[i].ShaderRegister = i; + samplers[i].Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT; + samplers[i].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[i].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[i].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[i].MipLODBias = 0; + samplers[i].MaxAnisotropy = 16; + samplers[i].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; + samplers[i].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE; + samplers[i].MinLOD = 0.0f; + samplers[i].MaxLOD = D3D12_FLOAT32_MAX; + samplers[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + samplers[i].RegisterSpace = 0; + } + + D3D12_ROOT_SIGNATURE_DESC descRootSignature; + descRootSignature.NumParameters = 4; + descRootSignature.pParameters = parameters; + descRootSignature.NumStaticSamplers = 0; + descRootSignature.pStaticSamplers = NULL; + descRootSignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; + + kinc_microsoft_affirm(D3D12SerializeRootSignature(&descRootSignature, D3D_ROOT_SIGNATURE_VERSION_1, &rootBlob, &errorBlob)); + device->CreateRootSignature(0, rootBlob->GetBufferPointer(), rootBlob->GetBufferSize(), IID_GRAPHICS_PPV_ARGS(&globalRootSignature)); +} + +static void createComputeRootSignature() { + ID3DBlob *rootBlob; + ID3DBlob *errorBlob; + + D3D12_ROOT_PARAMETER parameters[4] = {}; + + D3D12_DESCRIPTOR_RANGE range; + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + range.NumDescriptors = (UINT)KINC_INTERNAL_G5_TEXTURE_COUNT; + range.BaseShaderRegister = 0; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + parameters[0].DescriptorTable.NumDescriptorRanges = 1; + parameters[0].DescriptorTable.pDescriptorRanges = ⦥ + + D3D12_DESCRIPTOR_RANGE uav_range; + uav_range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + uav_range.NumDescriptors = (UINT)KINC_INTERNAL_G5_TEXTURE_COUNT; + uav_range.BaseShaderRegister = 0; + uav_range.RegisterSpace = 0; + uav_range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + parameters[1].DescriptorTable.NumDescriptorRanges = 1; + parameters[1].DescriptorTable.pDescriptorRanges = &uav_range; + + D3D12_DESCRIPTOR_RANGE range2; + range2.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + range2.NumDescriptors = (UINT)KINC_INTERNAL_G5_TEXTURE_COUNT; + range2.BaseShaderRegister = 0; + range2.RegisterSpace = 0; + range2.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + parameters[2].DescriptorTable.NumDescriptorRanges = 1; + parameters[2].DescriptorTable.pDescriptorRanges = &range2; + + parameters[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + parameters[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + parameters[3].Descriptor.ShaderRegister = 0; + parameters[3].Descriptor.RegisterSpace = 0; + + D3D12_STATIC_SAMPLER_DESC samplers[KINC_INTERNAL_G5_TEXTURE_COUNT * 2]; + for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) { + samplers[i].ShaderRegister = i; + samplers[i].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; + samplers[i].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[i].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[i].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[i].MipLODBias = 0; + samplers[i].MaxAnisotropy = 16; + samplers[i].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; + samplers[i].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE; + samplers[i].MinLOD = 0.0f; + samplers[i].MaxLOD = D3D12_FLOAT32_MAX; + samplers[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + samplers[i].RegisterSpace = 0; + } + for (int i = KINC_INTERNAL_G5_TEXTURE_COUNT; i < KINC_INTERNAL_G5_TEXTURE_COUNT * 2; ++i) { + samplers[i].ShaderRegister = i; + samplers[i].Filter = D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT; + samplers[i].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[i].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[i].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + samplers[i].MipLODBias = 0; + samplers[i].MaxAnisotropy = 16; + samplers[i].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; + samplers[i].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE; + samplers[i].MinLOD = 0.0f; + samplers[i].MaxLOD = D3D12_FLOAT32_MAX; + samplers[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + samplers[i].RegisterSpace = 0; + } + + D3D12_ROOT_SIGNATURE_DESC descRootSignature; + descRootSignature.NumParameters = 4; + descRootSignature.pParameters = parameters; + descRootSignature.NumStaticSamplers = 0; + descRootSignature.pStaticSamplers = NULL; + descRootSignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; + + kinc_microsoft_affirm(D3D12SerializeRootSignature(&descRootSignature, D3D_ROOT_SIGNATURE_VERSION_1, &rootBlob, &errorBlob)); + device->CreateRootSignature(0, rootBlob->GetBufferPointer(), rootBlob->GetBufferSize(), IID_GRAPHICS_PPV_ARGS(&globalComputeRootSignature)); + + // createSamplersAndHeaps(); +} + +static void initialize(struct dx_window *window) { + createDeviceAndSwapChain(window); + createRootSignature(); + createComputeRootSignature(); + + device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_GRAPHICS_PPV_ARGS(&uploadFence)); + + device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_GRAPHICS_PPV_ARGS(&initCommandAllocator)); + device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, initCommandAllocator, NULL, IID_GRAPHICS_PPV_ARGS(&initCommandList)); + + initCommandList->Close(); + + ID3D12CommandList *commandLists[] = {(ID3D12CommandList *)initCommandList}; + commandQueue->ExecuteCommandLists(1, commandLists); + commandQueue->Signal(uploadFence, 1); + + HANDLE waitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + waitForFence(uploadFence, 1, waitEvent); + + initCommandAllocator->Reset(); + initCommandList->Release(); // check me + initCommandAllocator->Release(); // check me + + CloseHandle(waitEvent); +} + +static void shutdown() { + for (int i = 0; i < QUEUE_SLOT_COUNT; ++i) { + // waitForFence(window->frame_fences[i], window->current_fence_value[i], window->frame_fence_events[i]); + } + + for (int i = 0; i < QUEUE_SLOT_COUNT; ++i) { + // CloseHandle(window->frame_fence_events[i]); + } +} + +#ifdef KINC_WINDOWS +static void initWindow(struct dx_window *window, int windowIndex) { + HWND hwnd = kinc_windows_window_handle(windowIndex); + + DXGI_SWAP_CHAIN_DESC swapChainDesc; + ZeroMemory(&swapChainDesc, sizeof(swapChainDesc)); + + swapChainDesc.BufferCount = QUEUE_SLOT_COUNT; + swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferDesc.Width = kinc_window_width(windowIndex); + swapChainDesc.BufferDesc.Height = kinc_window_height(windowIndex); + swapChainDesc.OutputWindow = hwnd; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + swapChainDesc.Windowed = true; + + IDXGIFactory4 *dxgiFactory = NULL; + kinc_microsoft_affirm(CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory))); + + kinc_microsoft_affirm(dxgiFactory->CreateSwapChain((IUnknown *)commandQueue, &swapChainDesc, &window->swapChain)); + + setupSwapChain(window); +} +#endif + +void kinc_g5_internal_destroy_window(int window) {} + +void kinc_g5_internal_destroy() { +#ifdef KINC_WINDOWS + if (device) { + device->Release(); + device = NULL; + } +#endif +} + +void kinc_g5_internal_init() { +#ifdef KINC_WINDOWS +#ifdef _DEBUG + ID3D12Debug *debugController = NULL; + if (D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)) == S_OK) { + debugController->EnableDebugLayer(); + } +#endif + kinc_microsoft_affirm(D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device))); + + createRootSignature(); + createComputeRootSignature(); + + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + + kinc_microsoft_affirm(device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue))); + + device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&uploadFence)); + + device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&initCommandAllocator)); + device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, initCommandAllocator, NULL, IID_PPV_ARGS(&initCommandList)); + + initCommandList->Close(); + + ID3D12CommandList *commandLists[] = {(ID3D12CommandList *)initCommandList}; + commandQueue->ExecuteCommandLists(1, commandLists); + commandQueue->Signal(uploadFence, 1); + + HANDLE waitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + waitForFence(uploadFence, 1, waitEvent); + + initCommandAllocator->Reset(); + initCommandList->Release(); // check me + initCommandAllocator->Release(); // check me + + CloseHandle(waitEvent); +#endif +} + +void kinc_g5_internal_init_window(int windowIndex, int depthBufferBits, int stencilBufferBits, bool verticalSync) { + struct dx_window *window = &dx_ctx.windows[windowIndex]; + window->window_index = windowIndex; + window->vsync = verticalSync; + window->width = window->new_width = kinc_window_width(windowIndex); + window->height = window->new_height = kinc_window_height(windowIndex); +#ifdef KINC_WINDOWS + initWindow(window, windowIndex); +#else + HWND hwnd = NULL; + window->vsync = verticalSync; + window->new_width = window->width = kinc_width(); + window->new_height = window->height = kinc_height(); + initialize(window); +#endif +} + +int kinc_g5_max_bound_textures(void) { + return D3D12_COMMONSHADER_SAMPLER_SLOT_COUNT; +} + +#ifndef KINC_WINDOWS +extern "C" void kinc_internal_wait_for_frame(); +#endif + +static bool began = false; + +void kinc_g5_begin(kinc_g5_render_target_t *renderTarget, int windowId) { + if (began) + return; + began = true; + +#ifndef KINC_WINDOWS + kinc_internal_wait_for_frame(); +#endif + + struct dx_window *window = &dx_ctx.windows[windowId]; + dx_ctx.current_window = windowId; + + window->current_backbuffer = (window->current_backbuffer + 1) % QUEUE_SLOT_COUNT; + + if (window->new_width != window->width || window->new_height != window->height) { +#ifndef KINC_DIRECT3D_HAS_NO_SWAPCHAIN + kinc_microsoft_affirm(window->swapChain->ResizeBuffers(QUEUE_SLOT_COUNT, window->new_width, window->new_height, DXGI_FORMAT_R8G8B8A8_UNORM, 0)); +#endif + setupSwapChain(window); + window->width = window->new_width; + window->height = window->new_height; + window->current_backbuffer = 0; + } + + const UINT64 fenceValue = window->current_fence_value; + commandQueue->Signal(window->frame_fences[window->current_backbuffer], fenceValue); + window->fence_values[window->current_backbuffer] = fenceValue; + ++window->current_fence_value; + + waitForFence(window->frame_fences[window->current_backbuffer], window->fence_values[window->current_backbuffer], + window->frame_fence_events[window->current_backbuffer]); + + // static const float clearColor[] = {0.042f, 0.042f, 0.042f, 1}; + + // commandList->ClearRenderTargetView(GetCPUDescriptorHandle(renderTargetDescriptorHeap), clearColor, 0, nullptr); + + // commandList->ClearDepthStencilView(GetCPUDescriptorHandle(depthStencilDescriptorHeap), D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr); + + static int frameNumber = 0; + frameNumber++; +} + +void kinc_g5_end(int window) { + began = false; +} + +bool kinc_g5_vsynced() { + return true; +} + +bool kinc_window_vsynced(int window) { + return true; +} + +extern "C" void kinc_g4_on_g5_internal_resize(int, int, int); + +extern "C" void kinc_internal_resize(int windowId, int width, int height) { + if (width == 0 || height == 0) + return; + struct dx_window *window = &dx_ctx.windows[windowId]; + window->new_width = width; + window->new_height = height; + kinc_g4_on_g5_internal_resize(windowId, width, height); +} + +extern "C" void kinc_internal_change_framebuffer(int window, kinc_framebuffer_options_t *frame) {} + +#ifndef KINC_DIRECT3D_HAS_NO_SWAPCHAIN +bool kinc_g5_swap_buffers() { + for (int i = 0; i < MAXIMUM_WINDOWS; i++) { + struct dx_window *window = &dx_ctx.windows[i]; + if (window->swapChain) { + kinc_microsoft_affirm(window->swapChain->Present(window->vsync, 0)); + } + } + return true; +} +#endif + +void kinc_g5_flush() {} + +bool kinc_g5_render_targets_inverted_y() { + return false; +} + +bool kinc_g5_supports_raytracing() { + D3D12_FEATURE_DATA_D3D12_OPTIONS5 options; + if (device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &options, sizeof(options)) == S_OK) { + return options.RaytracingTier >= D3D12_RAYTRACING_TIER_1_0; + } + return false; +} + +bool kinc_g5_supports_instanced_rendering() { + return true; +} + +bool kinc_g5_supports_compute_shaders() { + return true; +} + +bool kinc_g5_supports_blend_constants() { + return true; +} + +bool kinc_g5_supports_non_pow2_textures() { + return true; +} \ No newline at end of file diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/ShaderHash.c.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/ShaderHash.c.h new file mode 100644 index 000000000..bfbd458d4 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/ShaderHash.c.h @@ -0,0 +1,11 @@ +#include "ShaderHash.h" + +// djb2 +uint32_t kinc_internal_hash_name(unsigned char *str) { + unsigned long hash = 5381; + int c; + while ((c = *str++)) { + hash = hash * 33 ^ c; + } + return hash; +} diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/ShaderHash.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/ShaderHash.h new file mode 100644 index 000000000..2963fd345 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/ShaderHash.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t hash; + uint32_t index; +} kinc_internal_hash_index_t; + +uint32_t kinc_internal_hash_name(unsigned char *str); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/commandlist.c.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/commandlist.c.h new file mode 100644 index 000000000..db298f5cf --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/commandlist.c.h @@ -0,0 +1,611 @@ +#include +#include +#include +#include +#include +#include +#include + +void createHeaps(kinc_g5_command_list_t *list); + +static int formatSize(DXGI_FORMAT format) { + switch (format) { + case DXGI_FORMAT_R32G32B32A32_FLOAT: + return 16; + case DXGI_FORMAT_R16G16B16A16_FLOAT: + return 8; + case DXGI_FORMAT_R16_FLOAT: + return 2; + case DXGI_FORMAT_R8_UNORM: + return 1; + default: + return 4; + } +} + +void kinc_g5_command_list_init(struct kinc_g5_command_list *list) { +#ifndef NDEBUG + list->impl.open = false; +#endif + + device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_GRAPHICS_PPV_ARGS(&list->impl._commandAllocator)); + device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, list->impl._commandAllocator, NULL, IID_GRAPHICS_PPV_ARGS(&list->impl._commandList)); + + list->impl.fence_value = 0; + list->impl.fence_event = CreateEvent(NULL, FALSE, FALSE, NULL); + device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_GRAPHICS_PPV_ARGS(&list->impl.fence)); + + list->impl._indexCount = 0; + + list->impl.current_full_scissor.left = -1; + + for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) { + list->impl.currentRenderTargets[i] = NULL; + list->impl.currentTextures[i] = NULL; + list->impl.current_samplers[i] = NULL; + } + list->impl.heapIndex = 0; + createHeaps(list); +} + +void kinc_g5_command_list_destroy(struct kinc_g5_command_list *list) {} + +void kinc_g5_internal_reset_textures(struct kinc_g5_command_list *list); + +void kinc_g5_command_list_begin(struct kinc_g5_command_list *list) { + assert(!list->impl.open); + + compute_pipeline_set = false; + + if (list->impl.fence_value > 0) { + waitForFence(list->impl.fence, list->impl.fence_value, list->impl.fence_event); + list->impl._commandAllocator->Reset(); + list->impl._commandList->Reset(list->impl._commandAllocator, NULL); + } + + kinc_g5_internal_reset_textures(list); + +#ifndef NDEBUG + list->impl.open = true; +#endif +} + +void kinc_g5_command_list_end(struct kinc_g5_command_list *list) { + assert(list->impl.open); + + list->impl._commandList->Close(); + +#ifndef NDEBUG + list->impl.open = false; +#endif +} + +void kinc_g5_command_list_clear(struct kinc_g5_command_list *list, kinc_g5_render_target_t *renderTarget, unsigned flags, unsigned color, float depth, + int stencil) { + assert(list->impl.open); + + if (flags & KINC_G5_CLEAR_COLOR) { + float clearColor[] = {((color & 0x00ff0000) >> 16) / 255.0f, ((color & 0x0000ff00) >> 8) / 255.0f, (color & 0x000000ff) / 255.0f, + ((color & 0xff000000) >> 24) / 255.0f}; + list->impl._commandList->ClearRenderTargetView(renderTarget->impl.renderTargetDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), clearColor, 0, + NULL); + } + if ((flags & KINC_G5_CLEAR_DEPTH) || (flags & KINC_G5_CLEAR_STENCIL)) { + D3D12_CLEAR_FLAGS d3dflags = (flags & KINC_G5_CLEAR_DEPTH) && (flags & KINC_G5_CLEAR_STENCIL) ? D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL + : (flags & KINC_G5_CLEAR_DEPTH) ? D3D12_CLEAR_FLAG_DEPTH + : D3D12_CLEAR_FLAG_STENCIL; + + if (renderTarget->impl.depthStencilDescriptorHeap != NULL) { + list->impl._commandList->ClearDepthStencilView(renderTarget->impl.depthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), d3dflags, depth, + stencil, 0, NULL); + } + } +} + +void kinc_g5_command_list_render_target_to_framebuffer_barrier(struct kinc_g5_command_list *list, kinc_g5_render_target_t *renderTarget) { + assert(list->impl.open); + + D3D12_RESOURCE_BARRIER barrier; + barrier.Transition.pResource = renderTarget->impl.renderTarget; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + + list->impl._commandList->ResourceBarrier(1, &barrier); +} + +void kinc_g5_command_list_framebuffer_to_render_target_barrier(struct kinc_g5_command_list *list, kinc_g5_render_target_t *renderTarget) { + assert(list->impl.open); + + D3D12_RESOURCE_BARRIER barrier; + barrier.Transition.pResource = renderTarget->impl.renderTarget; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + + list->impl._commandList->ResourceBarrier(1, &barrier); +} + +void kinc_g5_command_list_texture_to_render_target_barrier(struct kinc_g5_command_list *list, kinc_g5_render_target_t *renderTarget) { + assert(list->impl.open); + + D3D12_RESOURCE_BARRIER barrier; + barrier.Transition.pResource = renderTarget->impl.renderTarget; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + + list->impl._commandList->ResourceBarrier(1, &barrier); +} + +void kinc_g5_command_list_render_target_to_texture_barrier(struct kinc_g5_command_list *list, kinc_g5_render_target_t *renderTarget) { + assert(list->impl.open); + + D3D12_RESOURCE_BARRIER barrier; + barrier.Transition.pResource = renderTarget->impl.renderTarget; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + + list->impl._commandList->ResourceBarrier(1, &barrier); +} + +void kinc_g5_command_list_set_vertex_constant_buffer(struct kinc_g5_command_list *list, kinc_g5_constant_buffer_t *buffer, int offset, size_t size) { + assert(list->impl.open); + +#ifdef KINC_DXC + if (list->impl._currentPipeline->impl.vertexConstantsSize > 0) { + if (list->impl._currentPipeline->impl.textures > 0) { + list->impl._commandList->SetGraphicsRootConstantBufferView(2, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset); + } + else { + list->impl._commandList->SetGraphicsRootConstantBufferView(0, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset); + } + } +#else + list->impl._commandList->SetGraphicsRootConstantBufferView(2, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset); +#endif +} + +void kinc_g5_command_list_set_fragment_constant_buffer(struct kinc_g5_command_list *list, kinc_g5_constant_buffer_t *buffer, int offset, size_t size) { + assert(list->impl.open); + +#ifdef KINC_DXC + if (list->impl._currentPipeline->impl.fragmentConstantsSize > 0) { + list->impl._commandList->SetGraphicsRootConstantBufferView(3, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset); + } +#else + list->impl._commandList->SetGraphicsRootConstantBufferView(3, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset); +#endif +} + +void kinc_g5_command_list_set_compute_constant_buffer(struct kinc_g5_command_list *list, kinc_g5_constant_buffer_t *buffer, int offset, size_t size) { + assert(list->impl.open); + +#ifdef KINC_DXC + if (list->impl._currentPipeline->impl.fragmentConstantsSize > 0) { + list->impl._commandList->SetGraphicsRootConstantBufferView(3, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset); + } +#else + list->impl._commandList->SetComputeRootConstantBufferView(3, buffer->impl.constant_buffer->GetGPUVirtualAddress() + offset); +#endif +} + +void kinc_g5_command_list_draw_indexed_vertices(struct kinc_g5_command_list *list) { + kinc_g5_command_list_draw_indexed_vertices_from_to(list, 0, list->impl._indexCount); +} + +void kinc_g5_command_list_draw_indexed_vertices_from_to(struct kinc_g5_command_list *list, int start, int count) { + assert(list->impl.open); + + list->impl._commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + /*u8* data; + D3D12_RANGE range; + range.Begin = currentConstantBuffer * sizeof(vertexConstants); + range.End = range.Begin + sizeof(vertexConstants); + vertexConstantBuffer->Map(0, &range, (void**)&data); + memcpy(data + currentConstantBuffer * sizeof(vertexConstants), vertexConstants, sizeof(vertexConstants)); + vertexConstantBuffer->Unmap(0, &range); + + range.Begin = currentConstantBuffer * sizeof(fragmentConstants); + range.End = range.Begin + sizeof(fragmentConstants); + fragmentConstantBuffer->Map(0, &range, (void**)&data); + memcpy(data + currentConstantBuffer * sizeof(fragmentConstants), fragmentConstants, sizeof(fragmentConstants)); + fragmentConstantBuffer->Unmap(0, &range); + + _commandList->SetGraphicsRootConstantBufferView(1, vertexConstantBuffer->GetGPUVirtualAddress() + currentConstantBuffer * sizeof(vertexConstants)); + _commandList->SetGraphicsRootConstantBufferView(2, fragmentConstantBuffer->GetGPUVirtualAddress() + currentConstantBuffer * sizeof(fragmentConstants)); + + ++currentConstantBuffer; + if (currentConstantBuffer >= constantBufferMultiply) { + currentConstantBuffer = 0; + }*/ + + list->impl._commandList->DrawIndexedInstanced(count, 1, start, 0, 0); +} + +void kinc_g5_command_list_draw_indexed_vertices_from_to_from(struct kinc_g5_command_list *list, int start, int count, int vertex_offset) { + assert(list->impl.open); + + list->impl._commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + list->impl._commandList->DrawIndexedInstanced(count, 1, start, vertex_offset, 0); +} + +void kinc_g5_command_list_draw_indexed_vertices_instanced(kinc_g5_command_list_t *list, int instanceCount) { + kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(list, instanceCount, 0, list->impl._indexCount); +} +void kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(kinc_g5_command_list_t *list, int instanceCount, int start, int count) { + assert(list->impl.open); + + list->impl._commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + list->impl._commandList->DrawIndexedInstanced(count, instanceCount, start, 0, 0); +} + +void kinc_g5_command_list_execute(kinc_g5_command_list_t *list) { + assert(!list->impl.open); + + ID3D12CommandList *commandLists[] = {(ID3D12CommandList *)list->impl._commandList}; + commandQueue->ExecuteCommandLists(1, commandLists); + + commandQueue->Signal(list->impl.fence, ++list->impl.fence_value); +} + +void kinc_g5_command_list_wait_for_execution_to_finish(kinc_g5_command_list_t *list) { + waitForFence(list->impl.fence, list->impl.fence_value, list->impl.fence_event); +} + +bool kinc_g5_non_pow2_textures_supported(void) { + return true; +} + +void kinc_g5_command_list_viewport(struct kinc_g5_command_list *list, int x, int y, int width, int height) { + assert(list->impl.open); + + D3D12_VIEWPORT viewport; + viewport.TopLeftX = (float)x; + viewport.TopLeftY = (float)y; + viewport.Width = (float)width; + viewport.Height = (float)height; + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + list->impl._commandList->RSSetViewports(1, &viewport); +} + +void kinc_g5_command_list_scissor(struct kinc_g5_command_list *list, int x, int y, int width, int height) { + assert(list->impl.open); + + D3D12_RECT scissor; + scissor.left = x; + scissor.top = y; + scissor.right = x + width; + scissor.bottom = y + height; + list->impl._commandList->RSSetScissorRects(1, &scissor); +} + +void kinc_g5_command_list_disable_scissor(struct kinc_g5_command_list *list) { + assert(list->impl.open); + + if (list->impl.current_full_scissor.left >= 0) { + list->impl._commandList->RSSetScissorRects(1, (D3D12_RECT *)&list->impl.current_full_scissor); + } + else { + D3D12_RECT scissor; + scissor.left = 0; + scissor.top = 0; + scissor.right = kinc_window_width(0); + scissor.bottom = kinc_window_height(0); + list->impl._commandList->RSSetScissorRects(1, &scissor); + } +} + +void kinc_g5_command_list_set_pipeline(struct kinc_g5_command_list *list, kinc_g5_pipeline_t *pipeline) { + assert(list->impl.open); + + list->impl._currentPipeline = pipeline; + list->impl._commandList->SetPipelineState(pipeline->impl.pso); + compute_pipeline_set = false; + + for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) { + list->impl.currentRenderTargets[i] = NULL; + list->impl.currentTextures[i] = NULL; + } + kinc_g5_internal_setConstants(list, list->impl._currentPipeline); +} + +void kinc_g5_command_list_set_blend_constant(kinc_g5_command_list_t *list, float r, float g, float b, float a) { + const FLOAT BlendFactor[4] = {r, g, b, a}; + list->impl._commandList->OMSetBlendFactor(BlendFactor); +} + +void kinc_g5_command_list_set_vertex_buffers(struct kinc_g5_command_list *list, kinc_g5_vertex_buffer_t **buffers, int *offsets, int count) { + assert(list->impl.open); + + D3D12_VERTEX_BUFFER_VIEW *views = (D3D12_VERTEX_BUFFER_VIEW *)alloca(sizeof(D3D12_VERTEX_BUFFER_VIEW) * count); + ZeroMemory(views, sizeof(D3D12_VERTEX_BUFFER_VIEW) * count); + for (int i = 0; i < count; ++i) { + views[i].BufferLocation = buffers[i]->impl.uploadBuffer->GetGPUVirtualAddress() + offsets[i] * kinc_g5_vertex_buffer_stride(buffers[i]); + views[i].SizeInBytes = (kinc_g5_vertex_buffer_count(buffers[i]) - offsets[i]) * kinc_g5_vertex_buffer_stride(buffers[i]); + views[i].StrideInBytes = kinc_g5_vertex_buffer_stride(buffers[i]); // * kinc_g5_vertex_buffer_count(buffers[i]); + } + list->impl._commandList->IASetVertexBuffers(0, count, views); +} + +void kinc_g5_command_list_set_index_buffer(struct kinc_g5_command_list *list, kinc_g5_index_buffer_t *buffer) { + assert(list->impl.open); + + list->impl._indexCount = kinc_g5_index_buffer_count(buffer); + list->impl._commandList->IASetIndexBuffer((D3D12_INDEX_BUFFER_VIEW *)&buffer->impl.index_buffer_view); +} + +void kinc_g5_command_list_set_render_targets(struct kinc_g5_command_list *list, kinc_g5_render_target_t **targets, int count) { + assert(list->impl.open); + + kinc_g5_render_target_t *render_target = targets[0]; + + D3D12_CPU_DESCRIPTOR_HANDLE target_descriptors[16]; + for (int i = 0; i < count; ++i) { + target_descriptors[i] = targets[i]->impl.renderTargetDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + } + + assert(render_target != NULL); + + if (render_target->impl.depthStencilDescriptorHeap != NULL) { + D3D12_CPU_DESCRIPTOR_HANDLE heapStart = render_target->impl.depthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + list->impl._commandList->OMSetRenderTargets(count, &target_descriptors[0], false, &heapStart); + } + else { + list->impl._commandList->OMSetRenderTargets(count, &target_descriptors[0], false, NULL); + } + + list->impl._commandList->RSSetViewports(1, (D3D12_VIEWPORT *)&render_target->impl.viewport); + list->impl._commandList->RSSetScissorRects(1, (D3D12_RECT *)&render_target->impl.scissor); + + list->impl.current_full_scissor = render_target->impl.scissor; +} + +void kinc_g5_command_list_upload_vertex_buffer(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer *buffer) {} + +void kinc_g5_command_list_upload_index_buffer(kinc_g5_command_list_t *list, kinc_g5_index_buffer_t *buffer) { + assert(list->impl.open); + kinc_g5_internal_index_buffer_upload(buffer, list->impl._commandList); +} + +void kinc_g5_command_list_upload_texture(kinc_g5_command_list_t *list, kinc_g5_texture_t *texture) { + assert(list->impl.open); + + { + D3D12_RESOURCE_BARRIER transition = {}; + transition.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + transition.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + transition.Transition.pResource = texture->impl.image; + transition.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + transition.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; + transition.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + + list->impl._commandList->ResourceBarrier(1, &transition); + } + + D3D12_RESOURCE_DESC Desc = texture->impl.image->GetDesc(); + ID3D12Device *device = NULL; + texture->impl.image->GetDevice(IID_GRAPHICS_PPV_ARGS(&device)); + D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint; + device->GetCopyableFootprints(&Desc, 0, 1, 0, &footprint, NULL, NULL, NULL); + device->Release(); + + D3D12_TEXTURE_COPY_LOCATION source = {0}; + source.pResource = texture->impl.uploadImage; + source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + source.PlacedFootprint = footprint; + + D3D12_TEXTURE_COPY_LOCATION destination = {0}; + destination.pResource = texture->impl.image; + destination.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + destination.SubresourceIndex = 0; + + list->impl._commandList->CopyTextureRegion(&destination, 0, 0, 0, &source, NULL); + + { + D3D12_RESOURCE_BARRIER transition = {}; + transition.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + transition.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + transition.Transition.pResource = texture->impl.image; + transition.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + transition.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + transition.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + + list->impl._commandList->ResourceBarrier(1, &transition); + } +} + +#if defined(KINC_WINDOWS) +static int d3d12_textureAlignment() { + return D3D12_TEXTURE_DATA_PITCH_ALIGNMENT; +} +#else +extern "C" int d3d12_textureAlignment(); +#endif + +void kinc_g5_command_list_get_render_target_pixels(kinc_g5_command_list_t *list, kinc_g5_render_target_t *render_target, uint8_t *data) { + assert(list->impl.open); + + DXGI_FORMAT dxgiFormat = render_target->impl.renderTarget->GetDesc().Format; + int formatByteSize = formatSize(dxgiFormat); + int rowPitch = render_target->texWidth * formatByteSize; + int align = rowPitch % d3d12_textureAlignment(); + if (align != 0) + rowPitch = rowPitch + (d3d12_textureAlignment() - align); + + // Create readback buffer + if (render_target->impl.renderTargetReadback == NULL) { + D3D12_HEAP_PROPERTIES heapProperties; + heapProperties.Type = D3D12_HEAP_TYPE_READBACK; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProperties.CreationNodeMask = 1; + heapProperties.VisibleNodeMask = 1; + + D3D12_RESOURCE_DESC resourceDesc; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Alignment = 0; + resourceDesc.Width = rowPitch * render_target->texHeight; + resourceDesc.Height = 1; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_UNKNOWN; + resourceDesc.SampleDesc.Count = 1; + resourceDesc.SampleDesc.Quality = 0; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + + device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, + IID_GRAPHICS_PPV_ARGS(&render_target->impl.renderTargetReadback)); + } + + // Copy render target to readback buffer + D3D12_RESOURCE_STATES sourceState = D3D12_RESOURCE_STATE_RENDER_TARGET; + + { + D3D12_RESOURCE_BARRIER barrier; + barrier.Transition.pResource = render_target->impl.renderTarget; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.StateBefore = sourceState; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + list->impl._commandList->ResourceBarrier(1, &barrier); + } + + D3D12_TEXTURE_COPY_LOCATION source; + source.pResource = render_target->impl.renderTarget; + source.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + source.SubresourceIndex = 0; + + D3D12_TEXTURE_COPY_LOCATION dest; + dest.pResource = render_target->impl.renderTargetReadback; + dest.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + dest.PlacedFootprint.Offset = 0; + dest.PlacedFootprint.Footprint.Format = dxgiFormat; + dest.PlacedFootprint.Footprint.Width = render_target->texWidth; + dest.PlacedFootprint.Footprint.Height = render_target->texHeight; + dest.PlacedFootprint.Footprint.Depth = 1; + dest.PlacedFootprint.Footprint.RowPitch = rowPitch; + + list->impl._commandList->CopyTextureRegion(&dest, 0, 0, 0, &source, NULL); + + { + D3D12_RESOURCE_BARRIER barrier; + barrier.Transition.pResource = render_target->impl.renderTarget; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; + barrier.Transition.StateAfter = sourceState; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + list->impl._commandList->ResourceBarrier(1, &barrier); + } + + kinc_g5_command_list_end(list); + kinc_g5_command_list_execute(list); + kinc_g5_command_list_wait_for_execution_to_finish(list); + kinc_g5_command_list_begin(list); + + // Read buffer + void *p; + render_target->impl.renderTargetReadback->Map(0, NULL, &p); + memcpy(data, p, render_target->texWidth * render_target->texHeight * formatByteSize); + render_target->impl.renderTargetReadback->Unmap(0, NULL); +} + +void kinc_g5_internal_set_compute_constants(kinc_g5_command_list_t *commandList); + +void kinc_g5_command_list_set_compute_shader(kinc_g5_command_list_t *list, kinc_g5_compute_shader *shader) { + list->impl._commandList->SetPipelineState(shader->impl.pso); + compute_pipeline_set = true; + + for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) { + list->impl.currentRenderTargets[i] = NULL; + list->impl.currentTextures[i] = NULL; + } + kinc_g5_internal_set_compute_constants(list); +} + +void kinc_g5_command_list_compute(kinc_g5_command_list_t *list, int x, int y, int z) { + assert(list->impl.open); + list->impl._commandList->Dispatch(x, y, z); +} + +void kinc_g5_command_list_set_render_target_face(kinc_g5_command_list_t *list, kinc_g5_render_target_t *texture, int face) {} + +void kinc_g5_command_list_set_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture) { + if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) { + kinc_g5_internal_texture_set(list, texture, unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]); + } + else if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) { + kinc_g5_internal_texture_set(list, texture, unit.stages[KINC_G5_SHADER_TYPE_VERTEX]); + } + else if (unit.stages[KINC_G5_SHADER_TYPE_COMPUTE] >= 0) { + kinc_g5_internal_texture_set(list, texture, unit.stages[KINC_G5_SHADER_TYPE_COMPUTE]); + } + kinc_g5_internal_set_textures(list); +} + +void kinc_g5_internal_sampler_set(kinc_g5_command_list_t *list, kinc_g5_sampler_t *sampler, int unit); + +void kinc_g5_command_list_set_sampler(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_sampler_t *sampler) { + if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) { + kinc_g5_internal_sampler_set(list, sampler, unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]); + } + else if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) { + kinc_g5_internal_sampler_set(list, sampler, unit.stages[KINC_G5_SHADER_TYPE_VERTEX]); + } + kinc_g5_internal_set_textures(list); +} + +void kinc_g5_command_list_set_image_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture) { + if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) { + kinc_g5_internal_texture_set(list, texture, unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]); + } + else if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) { + kinc_g5_internal_texture_set(list, texture, unit.stages[KINC_G5_SHADER_TYPE_VERTEX]); + } + else if (unit.stages[KINC_G5_SHADER_TYPE_COMPUTE] >= 0) { + kinc_g5_internal_texture_set(list, texture, unit.stages[KINC_G5_SHADER_TYPE_COMPUTE]); + } + kinc_g5_internal_set_textures(list); +} + +void kinc_g5_command_list_set_texture_from_render_target(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_render_target_t *target) { + if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) { + target->impl.stage = unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]; + list->impl.currentRenderTargets[target->impl.stage] = target; + } + else if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) { + target->impl.stage = unit.stages[KINC_G5_SHADER_TYPE_VERTEX]; + list->impl.currentRenderTargets[target->impl.stage] = target; + } + list->impl.currentTextures[target->impl.stage] = NULL; + + kinc_g5_internal_set_textures(list); +} + +void kinc_g5_command_list_set_texture_from_render_target_depth(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_render_target_t *target) { + if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) { + target->impl.stage_depth = unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]; + list->impl.currentRenderTargets[target->impl.stage_depth] = target; + } + else if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) { + target->impl.stage_depth = unit.stages[KINC_G5_SHADER_TYPE_VERTEX]; + list->impl.currentRenderTargets[target->impl.stage_depth] = target; + } + list->impl.currentTextures[target->impl.stage_depth] = NULL; + + kinc_g5_internal_set_textures(list); +} diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/commandlist.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/commandlist.h new file mode 100644 index 000000000..49a9a5740 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/commandlist.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include "d3d12mini.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct kinc_g5_pipeline; +struct kinc_g5_render_target; +struct kinc_g5_texture; +struct kinc_g5_sampler; + +#define KINC_INTERNAL_G5_TEXTURE_COUNT 16 + +typedef struct { + struct ID3D12CommandAllocator *_commandAllocator; + struct ID3D12GraphicsCommandList *_commandList; + struct kinc_g5_pipeline *_currentPipeline; + int _indexCount; + +#ifndef NDEBUG + bool open; +#endif + + struct D3D12Rect current_full_scissor; + + // keep track of when a command-list is done + uint64_t fence_value; + struct ID3D12Fence *fence; + HANDLE fence_event; + + struct kinc_g5_render_target *currentRenderTargets[KINC_INTERNAL_G5_TEXTURE_COUNT]; + struct kinc_g5_texture *currentTextures[KINC_INTERNAL_G5_TEXTURE_COUNT]; + struct kinc_g5_sampler *current_samplers[KINC_INTERNAL_G5_TEXTURE_COUNT]; + + int heapIndex; + struct ID3D12DescriptorHeap *srvHeap; + struct ID3D12DescriptorHeap *samplerHeap; +} CommandList5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/compute.c.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/compute.c.h new file mode 100644 index 000000000..a92d7c8bb --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/compute.c.h @@ -0,0 +1,167 @@ +#include + +#include +#include +#include + +#include + +void kinc_g5_compute_shader_init(kinc_g5_compute_shader *shader, void *_data, int length) { + unsigned index = 0; + uint8_t *data = (uint8_t *)_data; + + memset(&shader->impl.attributes, 0, sizeof(shader->impl.attributes)); + int attributesCount = data[index++]; + for (int i = 0; i < attributesCount; ++i) { + unsigned char name[256]; + for (unsigned i2 = 0; i2 < 255; ++i2) { + name[i2] = data[index++]; + if (name[i2] == 0) + break; + } + shader->impl.attributes[i].hash = kinc_internal_hash_name(name); + shader->impl.attributes[i].index = data[index++]; + } + + memset(&shader->impl.textures, 0, sizeof(shader->impl.textures)); + uint8_t texCount = data[index++]; + for (unsigned i = 0; i < texCount; ++i) { + unsigned char name[256]; + for (unsigned i2 = 0; i2 < 255; ++i2) { + name[i2] = data[index++]; + if (name[i2] == 0) + break; + } + shader->impl.textures[i].hash = kinc_internal_hash_name(name); + shader->impl.textures[i].index = data[index++]; + } + + memset(&shader->impl.constants, 0, sizeof(shader->impl.constants)); + uint8_t constantCount = data[index++]; + shader->impl.constantsSize = 0; + for (unsigned i = 0; i < constantCount; ++i) { + unsigned char name[256]; + for (unsigned i2 = 0; i2 < 255; ++i2) { + name[i2] = data[index++]; + if (name[i2] == 0) + break; + } + kinc_compute_internal_shader_constant_t constant; + constant.hash = kinc_internal_hash_name(name); + memcpy(&constant.offset, &data[index], sizeof(constant.offset)); + index += 4; + memcpy(&constant.size, &data[index], sizeof(constant.size)); + index += 4; + constant.columns = data[index]; + index += 1; + constant.rows = data[index]; + index += 1; + + shader->impl.constants[i] = constant; + shader->impl.constantsSize = constant.offset + constant.size; + } + + shader->impl.length = (int)(length - index); + shader->impl.data = (uint8_t *)malloc(shader->impl.length); + assert(shader->impl.data != NULL); + memcpy(shader->impl.data, &data[index], shader->impl.length); + + D3D12_COMPUTE_PIPELINE_STATE_DESC desc = {0}; + desc.CS.BytecodeLength = shader->impl.length; + desc.CS.pShaderBytecode = shader->impl.data; + desc.pRootSignature = globalComputeRootSignature; + HRESULT hr = device->CreateComputePipelineState(&desc, IID_GRAPHICS_PPV_ARGS(&shader->impl.pso)); + + if (hr != S_OK) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Could not initialize compute shader."); + return; + } + + // kinc_microsoft_affirm(device->CreateBuffer(&CD3D11_BUFFER_DESC(getMultipleOf16(shader->impl.constantsSize), D3D11_BIND_CONSTANT_BUFFER), nullptr, + // &shader->impl.constantBuffer)); +} + +void kinc_g5_compute_shader_destroy(kinc_g5_compute_shader *shader) { + if (shader->impl.pso != NULL) { + shader->impl.pso->Release(); + shader->impl.pso = NULL; + } +} + +static kinc_compute_internal_shader_constant_t *findComputeConstant(kinc_compute_internal_shader_constant_t *constants, uint32_t hash) { + for (int i = 0; i < 64; ++i) { + if (constants[i].hash == hash) { + return &constants[i]; + } + } + return NULL; +} + +static kinc_internal_hash_index_t *findComputeTextureUnit(kinc_internal_hash_index_t *units, uint32_t hash) { + for (int i = 0; i < 64; ++i) { + if (units[i].hash == hash) { + return &units[i]; + } + } + return NULL; +} + +kinc_g5_constant_location_t kinc_g5_compute_shader_get_constant_location(kinc_g5_compute_shader *shader, const char *name) { + kinc_g5_constant_location_t location = {0}; + + uint32_t hash = kinc_internal_hash_name((unsigned char *)name); + + kinc_compute_internal_shader_constant_t *constant = findComputeConstant(shader->impl.constants, hash); + if (constant == NULL) { + location.impl.computeOffset = 0; + location.impl.computeSize = 0; + } + else { + location.impl.computeOffset = constant->offset; + location.impl.computeSize = constant->size; + } + + if (location.impl.computeSize == 0) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Uniform %s not found.", name); + } + + return location; +} + +kinc_g5_texture_unit_t kinc_g5_compute_shader_get_texture_unit(kinc_g5_compute_shader *shader, const char *name) { + char unitName[64]; + int unitOffset = 0; + size_t len = strlen(name); + if (len > 63) + len = 63; + strncpy(unitName, name, len + 1); + if (unitName[len - 1] == ']') { // Check for array - mySampler[2] + unitOffset = (int)(unitName[len - 2] - '0'); // Array index is unit offset + unitName[len - 3] = 0; // Strip array from name + } + + uint32_t hash = kinc_internal_hash_name((unsigned char *)unitName); + + kinc_g5_texture_unit_t unit; + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + unit.stages[i] = -1; + } + kinc_internal_hash_index_t *computeUnit = findComputeTextureUnit(shader->impl.textures, hash); + if (computeUnit == NULL) { +#ifndef NDEBUG + static int notFoundCount = 0; + if (notFoundCount < 10) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Sampler %s not found.", unitName); + ++notFoundCount; + } + else if (notFoundCount == 10) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Giving up on sampler not found messages.", unitName); + ++notFoundCount; + } +#endif + } + else { + unit.stages[KINC_G5_SHADER_TYPE_COMPUTE] = computeUnit->index + unitOffset; + } + return unit; +} diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/compute.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/compute.h new file mode 100644 index 000000000..8e3f1e207 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/compute.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t hash; + uint32_t offset; + uint32_t size; + uint8_t columns; + uint8_t rows; +} kinc_compute_internal_shader_constant_t; + +typedef struct kinc_g5_compute_shader_impl { + kinc_compute_internal_shader_constant_t constants[64]; + int constantsSize; + kinc_internal_hash_index_t attributes[64]; + kinc_internal_hash_index_t textures[64]; + uint8_t *data; + int length; + struct ID3D12Buffer *constantBuffer; + struct ID3D12PipelineState *pso; +} kinc_g5_compute_shader_impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/constantbuffer.c.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/constantbuffer.c.h new file mode 100644 index 000000000..f483383df --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/constantbuffer.c.h @@ -0,0 +1,69 @@ + +#include + +bool kinc_g5_transposeMat3 = false; +bool kinc_g5_transposeMat4 = false; + +void kinc_g5_constant_buffer_init(kinc_g5_constant_buffer_t *buffer, int size) { + buffer->impl.mySize = size; + buffer->data = NULL; + + D3D12_HEAP_PROPERTIES heapProperties; + heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProperties.CreationNodeMask = 1; + heapProperties.VisibleNodeMask = 1; + + D3D12_RESOURCE_DESC resourceDesc; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Alignment = 0; + resourceDesc.Width = size; + resourceDesc.Height = 1; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_UNKNOWN; + resourceDesc.SampleDesc.Count = 1; + resourceDesc.SampleDesc.Quality = 0; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + + kinc_microsoft_affirm(device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + IID_GRAPHICS_PPV_ARGS(&buffer->impl.constant_buffer))); + + void *p; + buffer->impl.constant_buffer->Map(0, NULL, &p); + ZeroMemory(p, size); + buffer->impl.constant_buffer->Unmap(0, NULL); +} + +void kinc_g5_constant_buffer_destroy(kinc_g5_constant_buffer_t *buffer) { + buffer->impl.constant_buffer->Release(); +} + +void kinc_g5_constant_buffer_lock_all(kinc_g5_constant_buffer_t *buffer) { + kinc_g5_constant_buffer_lock(buffer, 0, kinc_g5_constant_buffer_size(buffer)); +} + +void kinc_g5_constant_buffer_lock(kinc_g5_constant_buffer_t *buffer, int start, int count) { + buffer->impl.lastStart = start; + buffer->impl.lastCount = count; + D3D12_RANGE range; + range.Begin = start; + range.End = range.Begin + count; + uint8_t *p; + buffer->impl.constant_buffer->Map(0, &range, (void **)&p); + buffer->data = &p[start]; +} + +void kinc_g5_constant_buffer_unlock(kinc_g5_constant_buffer_t *buffer) { + D3D12_RANGE range; + range.Begin = buffer->impl.lastStart; + range.End = range.Begin + buffer->impl.lastCount; + buffer->impl.constant_buffer->Unmap(0, &range); + buffer->data = NULL; +} + +int kinc_g5_constant_buffer_size(kinc_g5_constant_buffer_t *buffer) { + return buffer->impl.mySize; +} diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/constantbuffer.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/constantbuffer.h new file mode 100644 index 000000000..3638bc480 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/constantbuffer.h @@ -0,0 +1,21 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct ID3D12Resource; + +typedef struct { + struct ID3D12Resource *constant_buffer; + int lastStart; + int lastCount; + int mySize; +} ConstantBuffer5Impl; + +extern bool kinc_g5_transposeMat3; +extern bool kinc_g5_transposeMat4; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/d3d12mini.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/d3d12mini.h new file mode 100644 index 000000000..916a72459 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/d3d12mini.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct ID3D12CommandAllocator; +struct ID3D12GraphicsCommandList; +struct ID3D12Fence; +struct ID3D12Resource; +struct ID3D12DescriptorHeap; +struct IDXGISwapChain; + +typedef void *HANDLE; +typedef unsigned __int64 UINT64; + +struct D3D12Viewport { + float TopLeftX; + float TopLeftY; + float Width; + float Height; + float MinDepth; + float MaxDepth; +}; + +struct D3D12Rect { + long left; + long top; + long right; + long bottom; +}; + +#define QUEUE_SLOT_COUNT 2 + +struct dx_window { +#ifndef KINC_DIRECT3D_HAS_NO_SWAPCHAIN + struct IDXGISwapChain *swapChain; +#endif + UINT64 current_fence_value; + UINT64 fence_values[QUEUE_SLOT_COUNT]; + HANDLE frame_fence_events[QUEUE_SLOT_COUNT]; + struct ID3D12Fence *frame_fences[QUEUE_SLOT_COUNT]; + int width; + int height; + int new_width; + int new_height; + int current_backbuffer; + bool vsync; + int window_index; +}; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/d3d12unit.cpp b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/d3d12unit.cpp new file mode 100644 index 000000000..2c53f12f4 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/d3d12unit.cpp @@ -0,0 +1,106 @@ +#include + +// Windows 7 +#define WINVER 0x0601 +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0601 + +#define NOATOM +#define NOCLIPBOARD +#define NOCOLOR +#define NOCOMM +#define NOCTLMGR +#define NODEFERWINDOWPOS +#define NODRAWTEXT +#define NOGDI +#define NOGDICAPMASKS +#define NOHELP +#define NOICONS +#define NOKANJI +#define NOKEYSTATES +#define NOMB +#define NOMCX +#define NOMEMMGR +#define NOMENUS +#define NOMETAFILE +#define NOMINMAX +// #define NOMSG +#define NONLS +#define NOOPENFILE +#define NOPROFILER +#define NORASTEROPS +#define NOSCROLL +#define NOSERVICE +#define NOSHOWWINDOW +#define NOSOUND +#define NOSYSCOMMANDS +#define NOSYSMETRICS +#define NOTEXTMETRIC +// #define NOUSER +#define NOVIRTUALKEYCODES +#define NOWH +#define NOWINMESSAGES +#define NOWINOFFSETS +#define NOWINSTYLES +#define WIN32_LEAN_AND_MEAN + +#ifdef KINC_WINDOWS +#include +#else +#include +#endif +#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN +struct DXGI_SWAP_CHAIN_DESC1; +#else +#include +#endif + +#include "d3d12mini.h" + +#ifndef IID_GRAPHICS_PPV_ARGS +#define IID_GRAPHICS_PPV_ARGS(x) IID_PPV_ARGS(x) +#endif + +extern "C" { +ID3D12Device *device = NULL; +} +static ID3D12RootSignature *globalRootSignature = NULL; +static ID3D12RootSignature *globalComputeRootSignature = NULL; +// extern ID3D12GraphicsCommandList* commandList; + +#include + +#define MAXIMUM_WINDOWS 16 + +struct dx_ctx { + int current_window; + struct dx_window windows[MAXIMUM_WINDOWS]; +}; + +static struct dx_ctx dx_ctx = {0}; + +inline struct dx_window *kinc_dx_current_window() { + return &dx_ctx.windows[dx_ctx.current_window]; +} + +static bool compute_pipeline_set = false; + +#include +#include +#include + +#include "Direct3D12.c.h" +#include "ShaderHash.c.h" +#include "commandlist.c.h" +#include "compute.c.h" +#include "constantbuffer.c.h" +#include "indexbuffer.c.h" +#include "pipeline.c.h" +#include "raytrace.c.h" +#include "rendertarget.c.h" +#include "sampler.c.h" +#include "shader.c.h" +#include "texture.c.h" +#include "vertexbuffer.c.h" diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/graphics.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/graphics.h new file mode 100644 index 000000000..056f2a534 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/graphics.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/indexbuffer.c.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/indexbuffer.c.h new file mode 100644 index 000000000..07c22625e --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/indexbuffer.c.h @@ -0,0 +1,130 @@ +#include "indexbuffer.h" + +#include +#include + +void kinc_g5_index_buffer_init(kinc_g5_index_buffer_t *buffer, int count, kinc_g5_index_buffer_format_t format, bool gpuMemory) { + buffer->impl.count = count; + buffer->impl.gpu_memory = gpuMemory; + buffer->impl.format = format; + + // static_assert(sizeof(D3D12IindexBufferView) == sizeof(D3D12_INDEX_BUFFER_VIEW), "Something is wrong with D3D12IindexBufferView"); + int uploadBufferSize = format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? sizeof(uint16_t) * count : sizeof(uint32_t) * count; + + D3D12_HEAP_PROPERTIES heapProperties; + heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProperties.CreationNodeMask = 1; + heapProperties.VisibleNodeMask = 1; + + D3D12_RESOURCE_DESC resourceDesc; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Alignment = 0; + resourceDesc.Width = uploadBufferSize; + resourceDesc.Height = 1; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_UNKNOWN; + resourceDesc.SampleDesc.Count = 1; + resourceDesc.SampleDesc.Quality = 0; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + + kinc_microsoft_affirm(device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + IID_GRAPHICS_PPV_ARGS(&buffer->impl.upload_buffer))); + + if (gpuMemory) { + D3D12_HEAP_PROPERTIES heapProperties; + heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProperties.CreationNodeMask = 1; + heapProperties.VisibleNodeMask = 1; + + kinc_microsoft_affirm(device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, + IID_GRAPHICS_PPV_ARGS(&buffer->impl.index_buffer))); + + buffer->impl.index_buffer_view.BufferLocation = buffer->impl.index_buffer->GetGPUVirtualAddress(); + } + else { + buffer->impl.index_buffer_view.BufferLocation = buffer->impl.upload_buffer->GetGPUVirtualAddress(); + } + buffer->impl.index_buffer_view.SizeInBytes = uploadBufferSize; + buffer->impl.index_buffer_view.Format = format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT; + + buffer->impl.last_start = 0; + buffer->impl.last_count = kinc_g5_index_buffer_count(buffer); +} + +void kinc_g5_index_buffer_destroy(kinc_g5_index_buffer_t *buffer) { + if (buffer->impl.index_buffer != NULL) { + buffer->impl.index_buffer->Release(); + buffer->impl.index_buffer = NULL; + } + + buffer->impl.upload_buffer->Release(); + buffer->impl.upload_buffer = NULL; +} + +static int kinc_g5_internal_index_buffer_stride(kinc_g5_index_buffer_t *buffer) { + return buffer->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_32BIT ? 4 : 2; +} + +void *kinc_g5_index_buffer_lock_all(kinc_g5_index_buffer_t *buffer) { + return kinc_g5_index_buffer_lock(buffer, 0, kinc_g5_index_buffer_count(buffer)); +} + +void *kinc_g5_index_buffer_lock(kinc_g5_index_buffer_t *buffer, int start, int count) { + buffer->impl.last_start = start; + buffer->impl.last_count = count; + + D3D12_RANGE range; + range.Begin = start * kinc_g5_internal_index_buffer_stride(buffer); + range.End = (start + count) * kinc_g5_internal_index_buffer_stride(buffer); + + void *p; + buffer->impl.upload_buffer->Map(0, &range, &p); + byte *bytes = (byte *)p; + bytes += start * kinc_g5_internal_index_buffer_stride(buffer); + return bytes; +} + +void kinc_g5_index_buffer_unlock_all(kinc_g5_index_buffer_t *buffer) { + D3D12_RANGE range; + range.Begin = buffer->impl.last_start * kinc_g5_internal_index_buffer_stride(buffer); + range.End = (buffer->impl.last_start + buffer->impl.last_count) * kinc_g5_internal_index_buffer_stride(buffer); + + buffer->impl.upload_buffer->Unmap(0, &range); +} + +void kinc_g5_index_buffer_unlock(kinc_g5_index_buffer_t *buffer, int count) { + D3D12_RANGE range; + range.Begin = buffer->impl.last_start * kinc_g5_internal_index_buffer_stride(buffer); + range.End = (buffer->impl.last_start + count) * kinc_g5_internal_index_buffer_stride(buffer); + + buffer->impl.upload_buffer->Unmap(0, &range); +} + +void kinc_g5_internal_index_buffer_upload(kinc_g5_index_buffer_t *buffer, ID3D12GraphicsCommandList *commandList) { + if (!buffer->impl.gpu_memory) + return; + + commandList->CopyBufferRegion(buffer->impl.index_buffer, 0, buffer->impl.upload_buffer, 0, + buffer->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? sizeof(uint16_t) * buffer->impl.count + : sizeof(uint32_t) * buffer->impl.count); + + D3D12_RESOURCE_BARRIER barriers[1] = {}; + barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[0].Transition.pResource = buffer->impl.index_buffer; + barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_INDEX_BUFFER; + barriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + + commandList->ResourceBarrier(1, barriers); +} + +int kinc_g5_index_buffer_count(kinc_g5_index_buffer_t *buffer) { + return buffer->impl.count; +} diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/indexbuffer.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/indexbuffer.h new file mode 100644 index 000000000..90c7252ff --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/indexbuffer.h @@ -0,0 +1,32 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct ID3D12Resource; + +struct D3D12IindexBufferView { + __int64 BufferLocation; + unsigned int SizeInBytes; + int Format; +}; + +typedef struct { + struct ID3D12Resource *index_buffer; + struct D3D12IindexBufferView index_buffer_view; + struct ID3D12Resource *upload_buffer; + int count; + bool gpu_memory; + int format; + int last_start; + int last_count; +} IndexBuffer5Impl; + +struct kinc_g5_index_buffer; + +void kinc_g5_internal_index_buffer_upload(struct kinc_g5_index_buffer *buffer, struct ID3D12GraphicsCommandList *commandList); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/pipeline.c.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/pipeline.c.h new file mode 100644 index 000000000..2cd81b97d --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/pipeline.c.h @@ -0,0 +1,559 @@ +#include "pipeline.h" + +#include +#include +#include +#include + +#include + +void kinc_g5_internal_setConstants(kinc_g5_command_list_t *commandList, kinc_g5_pipeline_t *pipeline) { + /*if (currentProgram->vertexShader->constantsSize > 0) { + context->UpdateSubresource(currentProgram->vertexConstantBuffer, 0, nullptr, vertexConstants, 0, 0); + context->VSSetConstantBuffers(0, 1, ¤tProgram->vertexConstantBuffer); + } + if (currentProgram->fragmentShader->constantsSize > 0) { + context->UpdateSubresource(currentProgram->fragmentConstantBuffer, 0, nullptr, fragmentConstants, 0, 0); + context->PSSetConstantBuffers(0, 1, ¤tProgram->fragmentConstantBuffer); + } + if (currentProgram->geometryShader != nullptr && currentProgram->geometryShader->constantsSize > 0) { + context->UpdateSubresource(currentProgram->geometryConstantBuffer, 0, nullptr, geometryConstants, 0, 0); + context->GSSetConstantBuffers(0, 1, ¤tProgram->geometryConstantBuffer); + } + if (currentProgram->tessControlShader != nullptr && currentProgram->tessControlShader->constantsSize > 0) { + context->UpdateSubresource(currentProgram->tessControlConstantBuffer, 0, nullptr, tessControlConstants, 0, 0); + context->HSSetConstantBuffers(0, 1, ¤tProgram->tessControlConstantBuffer); + } + if (currentProgram->tessEvalShader != nullptr && currentProgram->tessEvalShader->constantsSize > 0) { + context->UpdateSubresource(currentProgram->tessEvalConstantBuffer, 0, nullptr, tessEvalConstants, 0, 0); + context->DSSetConstantBuffers(0, 1, ¤tProgram->tessEvalConstantBuffer); + } + */ + +#ifdef KINC_DXC + // commandList->SetGraphicsRootSignature(pipeline->impl.rootSignature); + commandList->impl._commandList->SetGraphicsRootSignature(globalRootSignature); +#else + commandList->impl._commandList->SetGraphicsRootSignature(globalRootSignature); +#endif + + if (pipeline->impl.textures > 0) { + kinc_g5_internal_set_textures(commandList); + } +} + +void kinc_g5_internal_set_compute_constants(kinc_g5_command_list_t *commandList) { + /*if (currentProgram->vertexShader->constantsSize > 0) { + context->UpdateSubresource(currentProgram->vertexConstantBuffer, 0, nullptr, vertexConstants, 0, 0); + context->VSSetConstantBuffers(0, 1, ¤tProgram->vertexConstantBuffer); + } + if (currentProgram->fragmentShader->constantsSize > 0) { + context->UpdateSubresource(currentProgram->fragmentConstantBuffer, 0, nullptr, fragmentConstants, 0, 0); + context->PSSetConstantBuffers(0, 1, ¤tProgram->fragmentConstantBuffer); + } + if (currentProgram->geometryShader != nullptr && currentProgram->geometryShader->constantsSize > 0) { + context->UpdateSubresource(currentProgram->geometryConstantBuffer, 0, nullptr, geometryConstants, 0, 0); + context->GSSetConstantBuffers(0, 1, ¤tProgram->geometryConstantBuffer); + } + if (currentProgram->tessControlShader != nullptr && currentProgram->tessControlShader->constantsSize > 0) { + context->UpdateSubresource(currentProgram->tessControlConstantBuffer, 0, nullptr, tessControlConstants, 0, 0); + context->HSSetConstantBuffers(0, 1, ¤tProgram->tessControlConstantBuffer); + } + if (currentProgram->tessEvalShader != nullptr && currentProgram->tessEvalShader->constantsSize > 0) { + context->UpdateSubresource(currentProgram->tessEvalConstantBuffer, 0, nullptr, tessEvalConstants, 0, 0); + context->DSSetConstantBuffers(0, 1, ¤tProgram->tessEvalConstantBuffer); + } + */ + + commandList->impl._commandList->SetComputeRootSignature(globalComputeRootSignature); + + //if (pipeline->impl.textures > 0) { + kinc_g5_internal_set_textures(commandList); + //} +} + +void kinc_g5_pipeline_init(kinc_g5_pipeline_t *pipe) { + kinc_g5_internal_pipeline_init(pipe); +} + +void kinc_g5_pipeline_destroy(kinc_g5_pipeline_t *pipe) { + if (pipe->impl.pso != NULL) { + pipe->impl.pso->Release(); + pipe->impl.pso = NULL; + } +} + +// void PipelineState5Impl::set(Graphics5::PipelineState* pipeline) { +//_current = this; +// context->VSSetShader((ID3D11VertexShader*)vertexShader->shader, nullptr, 0); +// context->PSSetShader((ID3D11PixelShader*)fragmentShader->shader, nullptr, 0); + +// if (geometryShader != nullptr) context->GSSetShader((ID3D11GeometryShader*)geometryShader->shader, nullptr, 0); +// if (tessControlShader != nullptr) context->HSSetShader((ID3D11HullShader*)tessControlShader->shader, nullptr, 0); +// if (tessEvalShader != nullptr) context->DSSetShader((ID3D11DomainShader*)tessEvalShader->shader, nullptr, 0); + +// context->IASetInputLayout(inputLayout); +//} + +#define MAX_SHADER_THING 32 + +static ShaderConstant findConstant(kinc_g5_shader_t *shader, const char *name) { + if (shader != NULL) { + for (int i = 0; i < MAX_SHADER_THING; ++i) { + if (strcmp(shader->impl.constants[i].name, name) == 0) { + return shader->impl.constants[i]; + } + } + } + + ShaderConstant constant; + constant.name[0] = 0; + constant.offset = -1; + constant.size = 0; + return constant; +} + +static ShaderTexture findTexture(kinc_g5_shader_t *shader, const char *name) { + for (int i = 0; i < MAX_SHADER_THING; ++i) { + if (strcmp(shader->impl.textures[i].name, name) == 0) { + return shader->impl.textures[i]; + } + } + + ShaderTexture texture; + texture.name[0] = 0; + texture.texture = -1; + return texture; +} + +static ShaderAttribute findAttribute(kinc_g5_shader_t *shader, const char *name) { + for (int i = 0; i < MAX_SHADER_THING; ++i) { + if (strcmp(shader->impl.attributes[i].name, name) == 0) { + return shader->impl.attributes[i]; + } + } + + ShaderAttribute attribute; + attribute.name[0] = 0; + attribute.attribute = -1; + return attribute; +} + +kinc_g5_constant_location_t kinc_g5_pipeline_get_constant_location(struct kinc_g5_pipeline *pipe, const char *name) { + kinc_g5_constant_location_t location; + + { + ShaderConstant constant = findConstant(pipe->vertexShader, name); + location.impl.vertexOffset = constant.offset; + location.impl.vertexSize = constant.size; + } + + { + ShaderConstant constant = findConstant(pipe->fragmentShader, name); + location.impl.fragmentOffset = constant.offset; + location.impl.fragmentSize = constant.size; + } + + location.impl.computeOffset = 0; + location.impl.computeSize = 0; + + { + ShaderConstant constant = findConstant(pipe->geometryShader, name); + location.impl.geometryOffset = constant.offset; + location.impl.geometrySize = constant.size; + } + + { + ShaderConstant constant = findConstant(pipe->tessellationControlShader, name); + location.impl.tessControlOffset = constant.offset; + location.impl.tessControlSize = constant.size; + } + + { + ShaderConstant constant = findConstant(pipe->tessellationEvaluationShader, name); + location.impl.tessEvalOffset = constant.offset; + location.impl.tessEvalSize = constant.size; + } + + return location; +} + +kinc_g5_texture_unit_t kinc_g5_pipeline_get_texture_unit(kinc_g5_pipeline_t *pipe, const char *name) { + kinc_g5_texture_unit_t unit; + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + unit.stages[i] = -1; + } + + ShaderTexture vertexTexture = findTexture(pipe->vertexShader, name); + if (vertexTexture.texture != -1) { + unit.stages[KINC_G5_SHADER_TYPE_VERTEX] = vertexTexture.texture; + } + else { + ShaderTexture fragmentTexture = findTexture(pipe->fragmentShader, name); + unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] = fragmentTexture.texture; + } + return unit; +} + +static D3D12_BLEND convert_blend_factor(kinc_g5_blending_factor_t factor) { + switch (factor) { + case KINC_G5_BLEND_ONE: + return D3D12_BLEND_ONE; + case KINC_G5_BLEND_ZERO: + return D3D12_BLEND_ZERO; + case KINC_G5_BLEND_SOURCE_ALPHA: + return D3D12_BLEND_SRC_ALPHA; + case KINC_G5_BLEND_DEST_ALPHA: + return D3D12_BLEND_DEST_ALPHA; + case KINC_G5_BLEND_INV_SOURCE_ALPHA: + return D3D12_BLEND_INV_SRC_ALPHA; + case KINC_G5_BLEND_INV_DEST_ALPHA: + return D3D12_BLEND_INV_DEST_ALPHA; + case KINC_G5_BLEND_SOURCE_COLOR: + return D3D12_BLEND_SRC_COLOR; + case KINC_G5_BLEND_DEST_COLOR: + return D3D12_BLEND_DEST_COLOR; + case KINC_G5_BLEND_INV_SOURCE_COLOR: + return D3D12_BLEND_INV_SRC_COLOR; + case KINC_G5_BLEND_INV_DEST_COLOR: + return D3D12_BLEND_INV_DEST_COLOR; + case KINC_G5_BLEND_CONSTANT: + return D3D12_BLEND_BLEND_FACTOR; + case KINC_G5_BLEND_INV_CONSTANT: + return D3D12_BLEND_INV_BLEND_FACTOR; + default: + assert(false); + return D3D12_BLEND_ONE; + } +} + +static D3D12_BLEND_OP convert_blend_operation(kinc_g5_blending_operation_t op) { + switch (op) { + case KINC_G5_BLENDOP_ADD: + return D3D12_BLEND_OP_ADD; + case KINC_G5_BLENDOP_SUBTRACT: + return D3D12_BLEND_OP_SUBTRACT; + case KINC_G5_BLENDOP_REVERSE_SUBTRACT: + return D3D12_BLEND_OP_REV_SUBTRACT; + case KINC_G5_BLENDOP_MIN: + return D3D12_BLEND_OP_MIN; + case KINC_G5_BLENDOP_MAX: + return D3D12_BLEND_OP_MAX; + default: + assert(false); + return D3D12_BLEND_OP_ADD; + } +} + +static D3D12_CULL_MODE convert_cull_mode(kinc_g5_cull_mode_t cullMode) { + switch (cullMode) { + case KINC_G5_CULL_MODE_CLOCKWISE: + return D3D12_CULL_MODE_FRONT; + case KINC_G5_CULL_MODE_COUNTERCLOCKWISE: + return D3D12_CULL_MODE_BACK; + case KINC_G5_CULL_MODE_NEVER: + default: + return D3D12_CULL_MODE_NONE; + } +} + +static D3D12_COMPARISON_FUNC convert_compare_mode(kinc_g5_compare_mode_t compare) { + switch (compare) { + default: + case KINC_G5_COMPARE_MODE_ALWAYS: + return D3D12_COMPARISON_FUNC_ALWAYS; + case KINC_G5_COMPARE_MODE_NEVER: + return D3D12_COMPARISON_FUNC_NEVER; + case KINC_G5_COMPARE_MODE_EQUAL: + return D3D12_COMPARISON_FUNC_EQUAL; + case KINC_G5_COMPARE_MODE_NOT_EQUAL: + return D3D12_COMPARISON_FUNC_NOT_EQUAL; + case KINC_G5_COMPARE_MODE_LESS: + return D3D12_COMPARISON_FUNC_LESS; + case KINC_G5_COMPARE_MODE_LESS_EQUAL: + return D3D12_COMPARISON_FUNC_LESS_EQUAL; + case KINC_G5_COMPARE_MODE_GREATER: + return D3D12_COMPARISON_FUNC_GREATER; + case KINC_G5_COMPARE_MODE_GREATER_EQUAL: + return D3D12_COMPARISON_FUNC_GREATER_EQUAL; + } +} + +static DXGI_FORMAT convert_format(kinc_g5_render_target_format_t format) { + switch (format) { + case KINC_G5_RENDER_TARGET_FORMAT_128BIT_FLOAT: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_64BIT_FLOAT: + return DXGI_FORMAT_R16G16B16A16_FLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT: + return DXGI_FORMAT_R32_FLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT: + return DXGI_FORMAT_R16_FLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_8BIT_RED: + return DXGI_FORMAT_R8_UNORM; + case KINC_G5_RENDER_TARGET_FORMAT_32BIT: + default: +#ifdef KINC_WINDOWS + return DXGI_FORMAT_R8G8B8A8_UNORM; +#else + return DXGI_FORMAT_B8G8R8A8_UNORM; +#endif + } +} + +static void set_blend_state(D3D12_BLEND_DESC *blend_desc, kinc_g5_pipeline_t *pipe, int target) { + blend_desc->RenderTarget[target].BlendEnable = pipe->blend_source != KINC_G5_BLEND_ONE || pipe->blend_destination != KINC_G5_BLEND_ZERO || + pipe->alpha_blend_source != KINC_G5_BLEND_ONE || pipe->alpha_blend_destination != KINC_G5_BLEND_ZERO; + blend_desc->RenderTarget[target].SrcBlend = convert_blend_factor(pipe->blend_source); + blend_desc->RenderTarget[target].DestBlend = convert_blend_factor(pipe->blend_destination); + blend_desc->RenderTarget[target].BlendOp = convert_blend_operation(pipe->blend_operation); + blend_desc->RenderTarget[target].SrcBlendAlpha = convert_blend_factor(pipe->alpha_blend_source); + blend_desc->RenderTarget[target].DestBlendAlpha = convert_blend_factor(pipe->alpha_blend_destination); + blend_desc->RenderTarget[target].BlendOpAlpha = convert_blend_operation(pipe->alpha_blend_operation); + blend_desc->RenderTarget[target].RenderTargetWriteMask = + (((pipe->colorWriteMaskRed[target] ? D3D12_COLOR_WRITE_ENABLE_RED : 0) | (pipe->colorWriteMaskGreen[target] ? D3D12_COLOR_WRITE_ENABLE_GREEN : 0)) | + (pipe->colorWriteMaskBlue[target] ? D3D12_COLOR_WRITE_ENABLE_BLUE : 0)) | + (pipe->colorWriteMaskAlpha[target] ? D3D12_COLOR_WRITE_ENABLE_ALPHA : 0); +} + +void kinc_g5_pipeline_compile(kinc_g5_pipeline_t *pipe) { + // TODO FLOAT4x4 + int vertexAttributeCount = 0; + for (int i = 0; i < 16; ++i) { + if (pipe->inputLayout[i] == NULL) { + break; + } + vertexAttributeCount += pipe->inputLayout[i]->size; + } + D3D12_INPUT_ELEMENT_DESC *vertexDesc = (D3D12_INPUT_ELEMENT_DESC *)alloca(sizeof(D3D12_INPUT_ELEMENT_DESC) * vertexAttributeCount); + ZeroMemory(vertexDesc, sizeof(D3D12_INPUT_ELEMENT_DESC) * vertexAttributeCount); + int curAttr = 0; + for (int stream = 0; pipe->inputLayout[stream] != NULL; ++stream) { + for (int i = 0; i < pipe->inputLayout[stream]->size; ++i) { + vertexDesc[curAttr].SemanticName = "TEXCOORD"; + vertexDesc[curAttr].SemanticIndex = findAttribute(pipe->vertexShader, pipe->inputLayout[stream]->elements[i].name).attribute; + vertexDesc[curAttr].InputSlot = stream; + vertexDesc[curAttr].AlignedByteOffset = (i == 0) ? 0 : D3D12_APPEND_ALIGNED_ELEMENT; + vertexDesc[curAttr].InputSlotClass = + pipe->inputLayout[stream]->instanced ? D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA : D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; + vertexDesc[curAttr].InstanceDataStepRate = pipe->inputLayout[stream]->instanced ? 1 : 0; + + switch (pipe->inputLayout[stream]->elements[i].data) { + case KINC_G4_VERTEX_DATA_F32_1X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R32_FLOAT; + break; + case KINC_G4_VERTEX_DATA_F32_2X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32_FLOAT; + break; + case KINC_G4_VERTEX_DATA_F32_3X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32B32_FLOAT; + break; + case KINC_G4_VERTEX_DATA_F32_4X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + break; + case KINC_G4_VERTEX_DATA_I8_1X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R8_SINT; + break; + case KINC_G4_VERTEX_DATA_U8_1X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R8_UINT; + break; + case KINC_G4_VERTEX_DATA_I8_1X_NORMALIZED: + vertexDesc[curAttr].Format = DXGI_FORMAT_R8_SNORM; + break; + case KINC_G4_VERTEX_DATA_U8_1X_NORMALIZED: + vertexDesc[curAttr].Format = DXGI_FORMAT_R8_UNORM; + break; + case KINC_G4_VERTEX_DATA_I8_2X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8_SINT; + break; + case KINC_G4_VERTEX_DATA_U8_2X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8_UINT; + break; + case KINC_G4_VERTEX_DATA_I8_2X_NORMALIZED: + vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8_SNORM; + break; + case KINC_G4_VERTEX_DATA_U8_2X_NORMALIZED: + vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8_UNORM; + break; + case KINC_G4_VERTEX_DATA_I8_4X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8B8A8_SINT; + break; + case KINC_G4_VERTEX_DATA_U8_4X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8B8A8_UINT; + break; + case KINC_G4_VERTEX_DATA_I8_4X_NORMALIZED: + vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8B8A8_SNORM; + break; + case KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED: + vertexDesc[curAttr].Format = DXGI_FORMAT_R8G8B8A8_UNORM; + break; + case KINC_G4_VERTEX_DATA_I16_1X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R16_SINT; + break; + case KINC_G4_VERTEX_DATA_U16_1X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R16_UINT; + break; + case KINC_G4_VERTEX_DATA_I16_1X_NORMALIZED: + vertexDesc[curAttr].Format = DXGI_FORMAT_R16_SNORM; + break; + case KINC_G4_VERTEX_DATA_U16_1X_NORMALIZED: + vertexDesc[curAttr].Format = DXGI_FORMAT_R16_UNORM; + break; + case KINC_G4_VERTEX_DATA_I16_2X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16_SINT; + break; + case KINC_G4_VERTEX_DATA_U16_2X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16_UINT; + break; + case KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED: + vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16_SNORM; + break; + case KINC_G4_VERTEX_DATA_U16_2X_NORMALIZED: + vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16_UNORM; + break; + case KINC_G4_VERTEX_DATA_I16_4X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16B16A16_SINT; + break; + case KINC_G4_VERTEX_DATA_U16_4X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16B16A16_UINT; + break; + case KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED: + vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16B16A16_SNORM; + break; + case KINC_G4_VERTEX_DATA_U16_4X_NORMALIZED: + vertexDesc[curAttr].Format = DXGI_FORMAT_R16G16B16A16_UNORM; + break; + case KINC_G4_VERTEX_DATA_I32_1X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R32_SINT; + break; + case KINC_G4_VERTEX_DATA_U32_1X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R32_UINT; + break; + case KINC_G4_VERTEX_DATA_I32_2X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32_SINT; + break; + case KINC_G4_VERTEX_DATA_U32_2X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32_UINT; + break; + case KINC_G4_VERTEX_DATA_I32_3X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32B32_SINT; + break; + case KINC_G4_VERTEX_DATA_U32_3X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32B32_UINT; + break; + case KINC_G4_VERTEX_DATA_I32_4X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32B32A32_SINT; + break; + case KINC_G4_VERTEX_DATA_U32_4X: + vertexDesc[curAttr].Format = DXGI_FORMAT_R32G32B32A32_UINT; + break; + default: + break; + } + curAttr++; + } + } + + HRESULT hr = S_OK; +#ifdef KINC_DXC + // hr = device->CreateRootSignature(0, pipe->vertexShader->impl.data, pipe->vertexShader->impl.length, IID_GRAPHICS_PPV_ARGS(&pipe->impl.rootSignature)); + if (hr != S_OK) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Could not create root signature."); + } + pipe->impl.vertexConstantsSize = pipe->vertexShader->impl.constantsSize; + pipe->impl.fragmentConstantsSize = pipe->fragmentShader->impl.constantsSize; +#endif + + pipe->impl.textures = pipe->fragmentShader->impl.texturesCount; + + D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {0}; + psoDesc.VS.BytecodeLength = pipe->vertexShader->impl.length; + psoDesc.VS.pShaderBytecode = pipe->vertexShader->impl.data; + psoDesc.PS.BytecodeLength = pipe->fragmentShader->impl.length; + psoDesc.PS.pShaderBytecode = pipe->fragmentShader->impl.data; +#ifdef KINC_DXC + // psoDesc.pRootSignature = pipe->impl.rootSignature; + psoDesc.pRootSignature = globalRootSignature; +#else + psoDesc.pRootSignature = globalRootSignature; +#endif + psoDesc.NumRenderTargets = pipe->colorAttachmentCount; + for (int i = 0; i < pipe->colorAttachmentCount; ++i) { + psoDesc.RTVFormats[i] = convert_format(pipe->colorAttachment[i]); + } + psoDesc.DSVFormat = DXGI_FORMAT_UNKNOWN; + + psoDesc.InputLayout.NumElements = vertexAttributeCount; + psoDesc.InputLayout.pInputElementDescs = vertexDesc; + + psoDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; + psoDesc.RasterizerState.CullMode = convert_cull_mode(pipe->cullMode); + psoDesc.RasterizerState.FrontCounterClockwise = FALSE; + psoDesc.RasterizerState.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; + psoDesc.RasterizerState.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; + psoDesc.RasterizerState.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; + psoDesc.RasterizerState.DepthClipEnable = TRUE; + psoDesc.RasterizerState.MultisampleEnable = FALSE; + psoDesc.RasterizerState.AntialiasedLineEnable = FALSE; + psoDesc.RasterizerState.ForcedSampleCount = 0; + psoDesc.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; + + psoDesc.BlendState.AlphaToCoverageEnable = FALSE; + psoDesc.BlendState.IndependentBlendEnable = FALSE; + const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { + FALSE, + FALSE, + D3D12_BLEND_ONE, + D3D12_BLEND_ZERO, + D3D12_BLEND_OP_ADD, + D3D12_BLEND_ONE, + D3D12_BLEND_ZERO, + D3D12_BLEND_OP_ADD, + D3D12_LOGIC_OP_NOOP, + D3D12_COLOR_WRITE_ENABLE_ALL, + }; + for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) { + psoDesc.BlendState.RenderTarget[i] = defaultRenderTargetBlendDesc; + } + + bool independentBlend = false; + for (int i = 1; i < 8; ++i) { + if (pipe->colorWriteMaskRed[0] != pipe->colorWriteMaskRed[i] || pipe->colorWriteMaskGreen[0] != pipe->colorWriteMaskGreen[i] || + pipe->colorWriteMaskBlue[0] != pipe->colorWriteMaskBlue[i] || pipe->colorWriteMaskAlpha[0] != pipe->colorWriteMaskAlpha[i]) { + independentBlend = true; + break; + } + } + + set_blend_state(&psoDesc.BlendState, pipe, 0); + if (independentBlend) { + psoDesc.BlendState.IndependentBlendEnable = true; + for (int i = 1; i < 8; ++i) { + set_blend_state(&psoDesc.BlendState, pipe, i); + } + } + + psoDesc.DepthStencilState.DepthEnable = TRUE; + psoDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; + psoDesc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS; + psoDesc.DepthStencilState.StencilEnable = FALSE; + psoDesc.DepthStencilState.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK; + psoDesc.DepthStencilState.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK; + const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = {D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS}; + psoDesc.DepthStencilState.FrontFace = defaultStencilOp; + psoDesc.DepthStencilState.BackFace = defaultStencilOp; + + psoDesc.DepthStencilState.DepthEnable = pipe->depthMode != KINC_G5_COMPARE_MODE_ALWAYS; + psoDesc.DepthStencilState.DepthWriteMask = pipe->depthWrite ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO; + psoDesc.DepthStencilState.DepthFunc = convert_compare_mode(pipe->depthMode); + psoDesc.DepthStencilState.StencilEnable = false; + psoDesc.DSVFormat = DXGI_FORMAT_D32_FLOAT; + psoDesc.SampleDesc.Count = 1; + psoDesc.SampleMask = 0xFFFFFFFF; + psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + + hr = device->CreateGraphicsPipelineState(&psoDesc, IID_GRAPHICS_PPV_ARGS(&pipe->impl.pso)); + if (hr != S_OK) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Could not create pipeline."); + } +} diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/pipeline.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/pipeline.h new file mode 100644 index 000000000..1fbea864e --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/pipeline.h @@ -0,0 +1,77 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct kinc_g5_shader; + +struct ID3D12PipelineState; +struct ID3D12GraphicsCommandList; +struct ID3D12RootSignature; + +typedef struct { + struct ID3D12PipelineState *pso; +#ifdef KINC_DXC + // struct ID3D12RootSignature *rootSignature; + int vertexConstantsSize; + int fragmentConstantsSize; +#endif + int textures; + // ID3D11InputLayout* inputLayout; + // ID3D11Buffer* fragmentConstantBuffer; + // ID3D11Buffer* vertexConstantBuffer; + // ID3D11Buffer* geometryConstantBuffer; + // ID3D11Buffer* tessEvalConstantBuffer; + // ID3D11Buffer* tessControlConstantBuffer; + + // static void setConstants(ID3D12GraphicsCommandList *commandList, Graphics5::PipelineState *pipeline); +} PipelineState5Impl; + +typedef struct { + struct ID3D12PipelineState *pso; +#ifdef KINC_DXC + struct ID3D12RootSignature *rootSignature; + int vertexConstantsSize; + int fragmentConstantsSize; +#endif + int textures; + // ID3D11InputLayout* inputLayout; + // ID3D11Buffer* fragmentConstantBuffer; + // ID3D11Buffer* vertexConstantBuffer; + // ID3D11Buffer* geometryConstantBuffer; + // ID3D11Buffer* tessEvalConstantBuffer; + // ID3D11Buffer* tessControlConstantBuffer; + + // static void setConstants(ID3D12GraphicsCommandList *commandList, Graphics5::PipelineState *pipeline); +} ComputePipelineState5Impl; + +typedef struct { + int vertexOffset; + uint32_t vertexSize; + int fragmentOffset; + uint32_t fragmentSize; + int computeOffset; + uint32_t computeSize; + int geometryOffset; + uint32_t geometrySize; + int tessEvalOffset; + uint32_t tessEvalSize; + int tessControlOffset; + uint32_t tessControlSize; +} ConstantLocation5Impl; + +typedef struct { + int nothing; +} AttributeLocation5Impl; + +struct kinc_g5_pipeline; +struct kinc_g5_command_list; + +void kinc_g5_internal_setConstants(struct kinc_g5_command_list *commandList, struct kinc_g5_pipeline *pipeline); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/raytrace.c.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/raytrace.c.h new file mode 100644 index 000000000..a9302eb53 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/raytrace.c.h @@ -0,0 +1,752 @@ +#ifndef KINC_XBOX_ONE + +#include + +#include +#include +#include +#include +#include + +static const wchar_t *hit_group_name = L"hitgroup"; +static const wchar_t *raygen_shader_name = L"raygeneration"; +static const wchar_t *closesthit_shader_name = L"closesthit"; +static const wchar_t *miss_shader_name = L"miss"; + +static ID3D12Device5 *dxrDevice = NULL; +static ID3D12GraphicsCommandList4 *dxrCommandList = NULL; +static ID3D12RootSignature *dxrRootSignature = NULL; +static ID3D12DescriptorHeap *descriptorHeap = NULL; +static kinc_raytrace_acceleration_structure_t *accel; +static kinc_raytrace_pipeline_t *pipeline; +static kinc_g5_render_target_t *output = NULL; +static D3D12_CPU_DESCRIPTOR_HANDLE outputCpuDescriptor; +static D3D12_GPU_DESCRIPTOR_HANDLE outputDescriptorHandle; +static D3D12_GPU_DESCRIPTOR_HANDLE vbgpuDescriptorHandle; +static D3D12_GPU_DESCRIPTOR_HANDLE ibgpuDescriptorHandle; +static D3D12_GPU_DESCRIPTOR_HANDLE tex0gpuDescriptorHandle; +static D3D12_GPU_DESCRIPTOR_HANDLE tex1gpuDescriptorHandle; +static D3D12_GPU_DESCRIPTOR_HANDLE tex2gpuDescriptorHandle; +static D3D12_GPU_DESCRIPTOR_HANDLE texenvgpuDescriptorHandle; +static D3D12_GPU_DESCRIPTOR_HANDLE texsobolgpuDescriptorHandle; +static D3D12_GPU_DESCRIPTOR_HANDLE texscramblegpuDescriptorHandle; +static D3D12_GPU_DESCRIPTOR_HANDLE texrankgpuDescriptorHandle; +static int descriptorsAllocated = 0; +static UINT descriptorSize; + +void kinc_raytrace_pipeline_init(kinc_raytrace_pipeline_t *pipeline, kinc_g5_command_list_t *command_list, void *ray_shader, int ray_shader_size, + kinc_g5_constant_buffer_t *constant_buffer) { + output = NULL; + descriptorsAllocated = 0; + pipeline->_constant_buffer = constant_buffer; + // Descriptor heap + D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {}; + // Allocate a heap for 3 descriptors: + // 2 - bottom and top level acceleration structure + // 1 - raytracing output texture SRV + descriptorHeapDesc.NumDescriptors = 12; + descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + descriptorHeapDesc.NodeMask = 0; + if (descriptorHeap != NULL) descriptorHeap->Release(); + device->CreateDescriptorHeap(&descriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(&descriptorHeap)); + descriptorSize = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + + // Device + if (dxrDevice != NULL) dxrDevice->Release(); + device->QueryInterface(IID_GRAPHICS_PPV_ARGS(&dxrDevice)); + if (dxrCommandList != NULL) dxrCommandList->Release(); + command_list->impl._commandList->QueryInterface(IID_GRAPHICS_PPV_ARGS(&dxrCommandList)); + + // Root signatures + // This is a root signature that is shared across all raytracing shaders invoked during a DispatchRays() call. + D3D12_DESCRIPTOR_RANGE UAVDescriptor = {}; + UAVDescriptor.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + UAVDescriptor.NumDescriptors = 1; + UAVDescriptor.BaseShaderRegister = 0; + UAVDescriptor.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_DESCRIPTOR_RANGE SRVDescriptorA = {}; + SRVDescriptorA.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + SRVDescriptorA.NumDescriptors = 1; + SRVDescriptorA.BaseShaderRegister = 1; + SRVDescriptorA.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_DESCRIPTOR_RANGE SRVDescriptorB = {}; + SRVDescriptorB.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + SRVDescriptorB.NumDescriptors = 1; + SRVDescriptorB.BaseShaderRegister = 2; + SRVDescriptorB.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_DESCRIPTOR_RANGE SRVDescriptor0 = {}; + SRVDescriptor0.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + SRVDescriptor0.NumDescriptors = 1; + SRVDescriptor0.BaseShaderRegister = 3; + SRVDescriptor0.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_DESCRIPTOR_RANGE SRVDescriptor1 = {}; + SRVDescriptor1.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + SRVDescriptor1.NumDescriptors = 1; + SRVDescriptor1.BaseShaderRegister = 4; + SRVDescriptor1.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_DESCRIPTOR_RANGE SRVDescriptor2 = {}; + SRVDescriptor2.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + SRVDescriptor2.NumDescriptors = 1; + SRVDescriptor2.BaseShaderRegister = 5; + SRVDescriptor2.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_DESCRIPTOR_RANGE SRVDescriptorEnv = {}; + SRVDescriptorEnv.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + SRVDescriptorEnv.NumDescriptors = 1; + SRVDescriptorEnv.BaseShaderRegister = 6; + SRVDescriptorEnv.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_DESCRIPTOR_RANGE SRVDescriptorSobol = {}; + SRVDescriptorSobol.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + SRVDescriptorSobol.NumDescriptors = 1; + SRVDescriptorSobol.BaseShaderRegister = 7; + SRVDescriptorSobol.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_DESCRIPTOR_RANGE SRVDescriptorScramble = {}; + SRVDescriptorScramble.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + SRVDescriptorScramble.NumDescriptors = 1; + SRVDescriptorScramble.BaseShaderRegister = 8; + SRVDescriptorScramble.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_DESCRIPTOR_RANGE SRVDescriptorRank = {}; + SRVDescriptorRank.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + SRVDescriptorRank.NumDescriptors = 1; + SRVDescriptorRank.BaseShaderRegister = 9; + SRVDescriptorRank.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + + D3D12_ROOT_PARAMETER rootParameters[12] = {}; + // Output view + rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + rootParameters[0].DescriptorTable.NumDescriptorRanges = 1; + rootParameters[0].DescriptorTable.pDescriptorRanges = &UAVDescriptor; + // Acceleration structure + rootParameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV; + rootParameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + rootParameters[1].Descriptor.ShaderRegister = 0; + // Constant buffer + rootParameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + rootParameters[2].DescriptorTable.NumDescriptorRanges = 1; + rootParameters[2].DescriptorTable.pDescriptorRanges = &SRVDescriptorA; + rootParameters[3].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParameters[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + rootParameters[3].DescriptorTable.NumDescriptorRanges = 1; + rootParameters[3].DescriptorTable.pDescriptorRanges = &SRVDescriptorB; + rootParameters[4].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + rootParameters[4].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + rootParameters[4].Descriptor.ShaderRegister = 0; + rootParameters[5].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParameters[5].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + rootParameters[5].DescriptorTable.NumDescriptorRanges = 1; + rootParameters[5].DescriptorTable.pDescriptorRanges = &SRVDescriptor0; + rootParameters[6].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParameters[6].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + rootParameters[6].DescriptorTable.NumDescriptorRanges = 1; + rootParameters[6].DescriptorTable.pDescriptorRanges = &SRVDescriptor1; + rootParameters[7].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParameters[7].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + rootParameters[7].DescriptorTable.NumDescriptorRanges = 1; + rootParameters[7].DescriptorTable.pDescriptorRanges = &SRVDescriptor2; + rootParameters[8].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParameters[8].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + rootParameters[8].DescriptorTable.NumDescriptorRanges = 1; + rootParameters[8].DescriptorTable.pDescriptorRanges = &SRVDescriptorEnv; + rootParameters[9].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParameters[9].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + rootParameters[9].DescriptorTable.NumDescriptorRanges = 1; + rootParameters[9].DescriptorTable.pDescriptorRanges = &SRVDescriptorSobol; + rootParameters[10].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParameters[10].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + rootParameters[10].DescriptorTable.NumDescriptorRanges = 1; + rootParameters[10].DescriptorTable.pDescriptorRanges = &SRVDescriptorScramble; + rootParameters[11].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + rootParameters[11].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + rootParameters[11].DescriptorTable.NumDescriptorRanges = 1; + rootParameters[11].DescriptorTable.pDescriptorRanges = &SRVDescriptorRank; + + D3D12_ROOT_SIGNATURE_DESC dxrRootSignatureDesc = {0}; + dxrRootSignatureDesc.NumParameters = ARRAYSIZE(rootParameters); + dxrRootSignatureDesc.pParameters = rootParameters; + ID3DBlob *blob = NULL; + ID3DBlob *error = NULL; + D3D12SerializeRootSignature(&dxrRootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, &error); + if (dxrRootSignature != NULL) dxrRootSignature->Release(); + device->CreateRootSignature(1, blob->GetBufferPointer(), blob->GetBufferSize(), IID_GRAPHICS_PPV_ARGS(&dxrRootSignature)); + + // Pipeline + D3D12_STATE_OBJECT_DESC raytracingPipeline = {}; + raytracingPipeline.Type = D3D12_STATE_OBJECT_TYPE_RAYTRACING_PIPELINE; + + D3D12_SHADER_BYTECODE shaderBytecode = {}; + shaderBytecode.pShaderBytecode = ray_shader; + shaderBytecode.BytecodeLength = ray_shader_size; + + D3D12_DXIL_LIBRARY_DESC dxilLibrary = {}; + dxilLibrary.DXILLibrary = shaderBytecode; + D3D12_EXPORT_DESC exports[3] = {}; + exports[0].Name = raygen_shader_name; + exports[1].Name = closesthit_shader_name; + exports[2].Name = miss_shader_name; + dxilLibrary.pExports = exports; + dxilLibrary.NumExports = 3; + + D3D12_HIT_GROUP_DESC hitGroup = {}; + hitGroup.ClosestHitShaderImport = closesthit_shader_name; + hitGroup.HitGroupExport = hit_group_name; + hitGroup.Type = D3D12_HIT_GROUP_TYPE_TRIANGLES; + + D3D12_RAYTRACING_SHADER_CONFIG shaderConfig = {}; + shaderConfig.MaxPayloadSizeInBytes = 10 * sizeof(float); // float4 color + shaderConfig.MaxAttributeSizeInBytes = 8 * sizeof(float); // float2 barycentrics + + D3D12_RAYTRACING_PIPELINE_CONFIG pipelineConfig = {}; + pipelineConfig.MaxTraceRecursionDepth = 1; // ~ primary rays only + + D3D12_STATE_SUBOBJECT subobjects[5] = {}; + subobjects[0].Type = D3D12_STATE_SUBOBJECT_TYPE_DXIL_LIBRARY; + subobjects[0].pDesc = &dxilLibrary; + subobjects[1].Type = D3D12_STATE_SUBOBJECT_TYPE_HIT_GROUP; + subobjects[1].pDesc = &hitGroup; + subobjects[2].Type = D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_SHADER_CONFIG; + subobjects[2].pDesc = &shaderConfig; + subobjects[3].Type = D3D12_STATE_SUBOBJECT_TYPE_GLOBAL_ROOT_SIGNATURE; + subobjects[3].pDesc = &dxrRootSignature; + subobjects[4].Type = D3D12_STATE_SUBOBJECT_TYPE_RAYTRACING_PIPELINE_CONFIG; + subobjects[4].pDesc = &pipelineConfig; + raytracingPipeline.NumSubobjects = 5; + raytracingPipeline.pSubobjects = subobjects; + + dxrDevice->CreateStateObject(&raytracingPipeline, IID_GRAPHICS_PPV_ARGS(&pipeline->impl.dxr_state)); + + // Shader tables + // Get shader identifiers + ID3D12StateObjectProperties *stateObjectProps = NULL; + pipeline->impl.dxr_state->QueryInterface(IID_GRAPHICS_PPV_ARGS(&stateObjectProps)); + const void *rayGenShaderId = stateObjectProps->GetShaderIdentifier(raygen_shader_name); + const void *missShaderId = stateObjectProps->GetShaderIdentifier(miss_shader_name); + const void *hitGroupShaderId = stateObjectProps->GetShaderIdentifier(hit_group_name); + UINT shaderIdSize = D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES; + int align = D3D12_RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT; + + // Ray gen shader table + { + UINT size = shaderIdSize + constant_buffer->impl.mySize; + UINT shaderRecordSize = (size + (align - 1)) & ~(align - 1); + D3D12_RESOURCE_DESC bufferDesc = {}; + bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + bufferDesc.Width = shaderRecordSize; + bufferDesc.Height = 1; + bufferDesc.DepthOrArraySize = 1; + bufferDesc.MipLevels = 1; + bufferDesc.Format = DXGI_FORMAT_UNKNOWN; + bufferDesc.SampleDesc.Count = 1; + bufferDesc.SampleDesc.Quality = 0; + bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + D3D12_HEAP_PROPERTIES uploadHeapProperties = {}; + uploadHeapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + uploadHeapProperties.CreationNodeMask = 1; + uploadHeapProperties.VisibleNodeMask = 1; + + device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + IID_GRAPHICS_PPV_ARGS(&pipeline->impl.raygen_shader_table)); + + D3D12_RANGE rstRange = {}; + rstRange.Begin = 0; + rstRange.End = 0; + uint8_t *byteDest; + pipeline->impl.raygen_shader_table->Map(0, &rstRange, (void **)(&byteDest)); + + D3D12_RANGE cbRange = {}; + cbRange.Begin = 0; + cbRange.End = constant_buffer->impl.mySize; + void *constantBufferData; + constant_buffer->impl.constant_buffer->Map(0, &cbRange, (void **)&constantBufferData); + memcpy(byteDest, rayGenShaderId, size); + memcpy(byteDest + size, constantBufferData, constant_buffer->impl.mySize); + pipeline->impl.raygen_shader_table->Unmap(0, NULL); + } + + // Miss shader table + { + UINT size = shaderIdSize; + UINT shaderRecordSize = (size + (align - 1)) & ~(align - 1); + D3D12_RESOURCE_DESC bufferDesc = {}; + bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + bufferDesc.Width = shaderRecordSize; + bufferDesc.Height = 1; + bufferDesc.DepthOrArraySize = 1; + bufferDesc.MipLevels = 1; + bufferDesc.Format = DXGI_FORMAT_UNKNOWN; + bufferDesc.SampleDesc.Count = 1; + bufferDesc.SampleDesc.Quality = 0; + bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + D3D12_HEAP_PROPERTIES uploadHeapProperties = {}; + uploadHeapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + uploadHeapProperties.CreationNodeMask = 1; + uploadHeapProperties.VisibleNodeMask = 1; + + device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + IID_GRAPHICS_PPV_ARGS(&pipeline->impl.miss_shader_table)); + + D3D12_RANGE mstRange = {}; + mstRange.Begin = 0; + mstRange.End = 0; + uint8_t *byteDest; + pipeline->impl.miss_shader_table->Map(0, &mstRange, (void **)(&byteDest)); + memcpy(byteDest, missShaderId, size); + pipeline->impl.miss_shader_table->Unmap(0, NULL); + } + + // Hit group shader table + { + UINT size = shaderIdSize; + UINT shaderRecordSize = (size + (align - 1)) & ~(align - 1); + D3D12_RESOURCE_DESC bufferDesc = {}; + bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + bufferDesc.Width = shaderRecordSize; + bufferDesc.Height = 1; + bufferDesc.DepthOrArraySize = 1; + bufferDesc.MipLevels = 1; + bufferDesc.Format = DXGI_FORMAT_UNKNOWN; + bufferDesc.SampleDesc.Count = 1; + bufferDesc.SampleDesc.Quality = 0; + bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + D3D12_HEAP_PROPERTIES uploadHeapProperties = {}; + uploadHeapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + uploadHeapProperties.CreationNodeMask = 1; + uploadHeapProperties.VisibleNodeMask = 1; + + device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + IID_GRAPHICS_PPV_ARGS(&pipeline->impl.hitgroup_shader_table)); + + D3D12_RANGE hstRange = {}; + hstRange.Begin = 0; + hstRange.End = 0; + uint8_t *byteDest; + pipeline->impl.hitgroup_shader_table->Map(0, &hstRange, (void **)(&byteDest)); + memcpy(byteDest, hitGroupShaderId, size); + pipeline->impl.hitgroup_shader_table->Unmap(0, NULL); + } + + // Output descriptor + outputCpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + (INT64)(descriptorsAllocated) * (UINT64)(descriptorSize); + + int descriptorHeapIndex = descriptorsAllocated++; + outputDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + (INT64)(descriptorHeapIndex) * (UINT64)(descriptorSize); +} + +void kinc_raytrace_pipeline_destroy(kinc_raytrace_pipeline_t *pipeline) { + pipeline->impl.dxr_state->Release(); + pipeline->impl.raygen_shader_table->Release(); + pipeline->impl.miss_shader_table->Release(); + pipeline->impl.hitgroup_shader_table->Release(); +} + +UINT create_srv_vb(kinc_g5_vertex_buffer_t *vb, UINT numElements, UINT elementSize) { + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc.Buffer.NumElements = numElements; + if (elementSize == 0) { + srvDesc.Format = DXGI_FORMAT_R32_TYPELESS; + srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; + srvDesc.Buffer.StructureByteStride = 0; + } + else { + srvDesc.Format = DXGI_FORMAT_UNKNOWN; + srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; + srvDesc.Buffer.StructureByteStride = elementSize; + } + + D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptor = {0}; + cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + (INT64)(descriptorsAllocated) * (UINT64)(descriptorSize); + UINT descriptorIndex = descriptorsAllocated++; + + device->CreateShaderResourceView(vb->impl.uploadBuffer, &srvDesc, cpuDescriptor); + vbgpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + (INT64)(descriptorIndex) * (UINT64)(descriptorSize); + + return descriptorIndex; +} + +UINT create_srv_ib(kinc_g5_index_buffer_t *ib, UINT numElements, UINT elementSize) { + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDesc.Buffer.NumElements = numElements; + if (elementSize == 0) { + srvDesc.Format = DXGI_FORMAT_R32_TYPELESS; + srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; + srvDesc.Buffer.StructureByteStride = 0; + } + else { + srvDesc.Format = DXGI_FORMAT_UNKNOWN; + srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; + srvDesc.Buffer.StructureByteStride = elementSize; + } + + D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptor = {0}; + cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + (INT64)(descriptorsAllocated) * (UINT64)(descriptorSize); + UINT descriptorIndex = descriptorsAllocated++; + + device->CreateShaderResourceView(ib->impl.upload_buffer, &srvDesc, cpuDescriptor); + ibgpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + (INT64)(descriptorIndex) * (UINT64)(descriptorSize); + + return descriptorIndex; +} + +void kinc_raytrace_acceleration_structure_init(kinc_raytrace_acceleration_structure_t *accel, kinc_g5_command_list_t *command_list, kinc_g5_vertex_buffer_t *vb, + kinc_g5_index_buffer_t *ib, float scale) { + create_srv_ib(ib, ib->impl.count, 0); + create_srv_vb(vb, vb->impl.myCount, 8 * 2); + + // Reset the command list for the acceleration structure construction + command_list->impl._commandList->Reset(command_list->impl._commandAllocator, NULL); + + D3D12_RAYTRACING_GEOMETRY_DESC geometryDesc = {}; + geometryDesc.Type = D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES; + geometryDesc.Triangles.IndexBuffer = ib->impl.upload_buffer->GetGPUVirtualAddress(); + geometryDesc.Triangles.IndexCount = ib->impl.count; + geometryDesc.Triangles.IndexFormat = ib->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT; + geometryDesc.Triangles.Transform3x4 = 0; + geometryDesc.Triangles.VertexFormat = DXGI_FORMAT_R16G16B16A16_SNORM; + geometryDesc.Triangles.VertexCount = vb->impl.myCount; + geometryDesc.Triangles.VertexBuffer.StartAddress = vb->impl.uploadBuffer->GetGPUVirtualAddress(); + geometryDesc.Triangles.VertexBuffer.StrideInBytes = vb->impl.uploadBuffer->GetDesc().Width / vb->impl.myCount; + geometryDesc.Flags = D3D12_RAYTRACING_GEOMETRY_FLAG_OPAQUE; + + // Get required sizes for an acceleration structure + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS topLevelInputs = {}; + topLevelInputs.DescsLayout = D3D12_ELEMENTS_LAYOUT_ARRAY; + topLevelInputs.Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE; + topLevelInputs.NumDescs = 1; + topLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL; + + D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO topLevelPrebuildInfo = {0}; + dxrDevice->GetRaytracingAccelerationStructurePrebuildInfo(&topLevelInputs, &topLevelPrebuildInfo); + + D3D12_RAYTRACING_ACCELERATION_STRUCTURE_PREBUILD_INFO bottomLevelPrebuildInfo = {0}; + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS bottomLevelInputs = topLevelInputs; + bottomLevelInputs.Type = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; + bottomLevelInputs.pGeometryDescs = &geometryDesc; + bottomLevelInputs.Flags = D3D12_RAYTRACING_ACCELERATION_STRUCTURE_BUILD_FLAG_PREFER_FAST_TRACE; + dxrDevice->GetRaytracingAccelerationStructurePrebuildInfo(&bottomLevelInputs, &bottomLevelPrebuildInfo); + + ID3D12Resource *scratchResource; + { + UINT64 tlSize = topLevelPrebuildInfo.ScratchDataSizeInBytes; + UINT64 blSize = bottomLevelPrebuildInfo.ScratchDataSizeInBytes; + D3D12_RESOURCE_DESC bufferDesc = {}; + bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + bufferDesc.Width = tlSize > blSize ? tlSize : blSize; + bufferDesc.Height = 1; + bufferDesc.DepthOrArraySize = 1; + bufferDesc.MipLevels = 1; + bufferDesc.Format = DXGI_FORMAT_UNKNOWN; + bufferDesc.SampleDesc.Count = 1; + bufferDesc.SampleDesc.Quality = 0; + bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + bufferDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + D3D12_HEAP_PROPERTIES uploadHeapProperties = {}; + uploadHeapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + uploadHeapProperties.CreationNodeMask = 1; + uploadHeapProperties.VisibleNodeMask = 1; + + device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, NULL, + IID_GRAPHICS_PPV_ARGS(&scratchResource)); + } + + // Allocate resources for acceleration structures + // The resources that will contain acceleration structures must be created in the state D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, + // and must have resource flag D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS. + { + D3D12_RESOURCE_DESC bufferDesc = {}; + bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + bufferDesc.Width = bottomLevelPrebuildInfo.ResultDataMaxSizeInBytes; + bufferDesc.Height = 1; + bufferDesc.DepthOrArraySize = 1; + bufferDesc.MipLevels = 1; + bufferDesc.Format = DXGI_FORMAT_UNKNOWN; + bufferDesc.SampleDesc.Count = 1; + bufferDesc.SampleDesc.Quality = 0; + bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + bufferDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + D3D12_HEAP_PROPERTIES uploadHeapProperties = {}; + uploadHeapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + uploadHeapProperties.CreationNodeMask = 1; + uploadHeapProperties.VisibleNodeMask = 1; + + device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, NULL, + IID_GRAPHICS_PPV_ARGS(&accel->impl.bottom_level_accel)); + } + { + D3D12_RESOURCE_DESC bufferDesc = {}; + bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + bufferDesc.Width = topLevelPrebuildInfo.ResultDataMaxSizeInBytes; + bufferDesc.Height = 1; + bufferDesc.DepthOrArraySize = 1; + bufferDesc.MipLevels = 1; + bufferDesc.Format = DXGI_FORMAT_UNKNOWN; + bufferDesc.SampleDesc.Count = 1; + bufferDesc.SampleDesc.Quality = 0; + bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + bufferDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + D3D12_HEAP_PROPERTIES uploadHeapProperties = {}; + uploadHeapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + uploadHeapProperties.CreationNodeMask = 1; + uploadHeapProperties.VisibleNodeMask = 1; + + device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_RAYTRACING_ACCELERATION_STRUCTURE, NULL, + IID_GRAPHICS_PPV_ARGS(&accel->impl.top_level_accel)); + } + + // Create an instance desc for the bottom-level acceleration structure + ID3D12Resource *instanceDescs; + D3D12_RAYTRACING_INSTANCE_DESC instanceDesc = {}; + instanceDesc.Transform[0][0] = instanceDesc.Transform[1][1] = instanceDesc.Transform[2][2] = scale; + instanceDesc.InstanceMask = 1; + instanceDesc.AccelerationStructure = accel->impl.bottom_level_accel->GetGPUVirtualAddress(); + + D3D12_RESOURCE_DESC bufferDesc = {}; + bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + bufferDesc.Width = sizeof(instanceDesc); + bufferDesc.Height = 1; + bufferDesc.DepthOrArraySize = 1; + bufferDesc.MipLevels = 1; + bufferDesc.Format = DXGI_FORMAT_UNKNOWN; + bufferDesc.SampleDesc.Count = 1; + bufferDesc.SampleDesc.Quality = 0; + bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + D3D12_HEAP_PROPERTIES uploadHeapProperties = {}; + uploadHeapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + uploadHeapProperties.CreationNodeMask = 1; + uploadHeapProperties.VisibleNodeMask = 1; + + device->CreateCommittedResource(&uploadHeapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + IID_GRAPHICS_PPV_ARGS(&instanceDescs)); + void *mappedData; + instanceDescs->Map(0, NULL, &mappedData); + memcpy(mappedData, &instanceDesc, sizeof(instanceDesc)); + instanceDescs->Unmap(0, NULL); + + // Bottom Level Acceleration Structure desc + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC bottomLevelBuildDesc = {0}; + bottomLevelBuildDesc.Inputs = bottomLevelInputs; + bottomLevelBuildDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress(); + bottomLevelBuildDesc.DestAccelerationStructureData = accel->impl.bottom_level_accel->GetGPUVirtualAddress(); + + // Top Level Acceleration Structure desc + D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC topLevelBuildDesc = bottomLevelBuildDesc; + topLevelInputs.InstanceDescs = instanceDescs->GetGPUVirtualAddress(); + topLevelBuildDesc.Inputs = topLevelInputs; + topLevelBuildDesc.DestAccelerationStructureData = accel->impl.top_level_accel->GetGPUVirtualAddress(); + topLevelBuildDesc.ScratchAccelerationStructureData = scratchResource->GetGPUVirtualAddress(); + + // Build acceleration structure + dxrCommandList->BuildRaytracingAccelerationStructure(&bottomLevelBuildDesc, 0, NULL); + D3D12_RESOURCE_BARRIER barrier = {}; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; + barrier.UAV.pResource = accel->impl.bottom_level_accel; + command_list->impl._commandList->ResourceBarrier(1, &barrier); + dxrCommandList->BuildRaytracingAccelerationStructure(&topLevelBuildDesc, 0, NULL); + + kinc_g5_command_list_end(command_list); + kinc_g5_command_list_execute(command_list); + kinc_g5_command_list_wait_for_execution_to_finish(command_list); + kinc_g5_command_list_begin(command_list); + + scratchResource->Release(); + instanceDescs->Release(); +} + +void kinc_raytrace_acceleration_structure_destroy(kinc_raytrace_acceleration_structure_t *accel) { + accel->impl.bottom_level_accel->Release(); + accel->impl.top_level_accel->Release(); +} + +void kinc_raytrace_set_textures(kinc_g5_render_target_t *texpaint0, kinc_g5_render_target_t *texpaint1, kinc_g5_render_target_t *texpaint2, kinc_g5_texture_t *texenv, kinc_g5_texture_t *texsobol, kinc_g5_texture_t *texscramble, kinc_g5_texture_t *texrank) { + D3D12_CPU_DESCRIPTOR_HANDLE cpuDescriptor = {0}; + cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 5 * (UINT64)(descriptorSize); + D3D12_CPU_DESCRIPTOR_HANDLE sourceCpu = texpaint0->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + tex0gpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 5 * (UINT64)(descriptorSize); + + cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 6 * (UINT64)(descriptorSize); + sourceCpu = texpaint1->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + tex1gpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 6 * (UINT64)(descriptorSize); + + cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 7 * (UINT64)(descriptorSize); + sourceCpu = texpaint2->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + tex2gpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 7 * (UINT64)(descriptorSize); + + cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 8 * (UINT64)(descriptorSize); + sourceCpu = texenv->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + texenvgpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 8 * (UINT64)(descriptorSize); + + cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 9 * (UINT64)(descriptorSize); + sourceCpu = texsobol->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + texsobolgpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 9 * (UINT64)(descriptorSize); + + cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 10 * (UINT64)(descriptorSize); + sourceCpu = texscramble->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + texscramblegpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 10 * (UINT64)(descriptorSize); + + cpuDescriptor.ptr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr + 11 * (UINT64)(descriptorSize); + sourceCpu = texrank->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + device->CopyDescriptorsSimple(1, cpuDescriptor, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + texrankgpuDescriptorHandle.ptr = descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + 11 * (UINT64)(descriptorSize); +} + +void kinc_raytrace_set_acceleration_structure(kinc_raytrace_acceleration_structure_t *_accel) { + accel = _accel; +} + +void kinc_raytrace_set_pipeline(kinc_raytrace_pipeline_t *_pipeline) { + pipeline = _pipeline; +} + +void kinc_raytrace_set_target(kinc_g5_render_target_t *_output) { + if (_output != output) { + _output->impl.renderTarget->Release(); + _output->impl.renderTargetDescriptorHeap->Release(); + _output->impl.srvDescriptorHeap->Release(); + + D3D12_HEAP_PROPERTIES heapProperties = {}; + heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProperties.CreationNodeMask = 1; + heapProperties.VisibleNodeMask = 1; + D3D12_RESOURCE_DESC desc = {}; + desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + desc.Width = _output->texWidth; + desc.Height = _output->texHeight; + desc.DepthOrArraySize = 1; + desc.MipLevels = 1; + desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + D3D12_CLEAR_VALUE clearValue; + clearValue.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; + clearValue.Color[0] = 0.0f; + clearValue.Color[1] = 0.0f; + clearValue.Color[2] = 0.0f; + clearValue.Color[3] = 1.0f; + + device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &desc, + D3D12_RESOURCE_STATE_COMMON, &clearValue, IID_GRAPHICS_PPV_ARGS(&_output->impl.renderTarget)); + + D3D12_RENDER_TARGET_VIEW_DESC view; + view.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; + view.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + view.Texture2D.MipSlice = 0; + view.Texture2D.PlaneSlice = 0; + D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; + heapDesc.NumDescriptors = 1; + heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + device->CreateDescriptorHeap(&heapDesc, IID_GRAPHICS_PPV_ARGS(&_output->impl.renderTargetDescriptorHeap)); + device->CreateRenderTargetView(_output->impl.renderTarget, &view, + _output->impl.renderTargetDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); + + D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {}; + descriptorHeapDesc.NumDescriptors = 1; + descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + descriptorHeapDesc.NodeMask = 0; + descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + device->CreateDescriptorHeap(&descriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(&_output->impl.srvDescriptorHeap)); + + D3D12_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc = {}; + shaderResourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + shaderResourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + shaderResourceViewDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; + shaderResourceViewDesc.Texture2D.MipLevels = 1; + shaderResourceViewDesc.Texture2D.MostDetailedMip = 0; + shaderResourceViewDesc.Texture2D.ResourceMinLODClamp = 0.0f; + device->CreateShaderResourceView(_output->impl.renderTarget, &shaderResourceViewDesc, + _output->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); + + D3D12_UNORDERED_ACCESS_VIEW_DESC UAVDesc = {}; + UAVDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D; + device->CreateUnorderedAccessView(_output->impl.renderTarget, NULL, &UAVDesc, outputCpuDescriptor); + } + output = _output; +} + +void kinc_raytrace_dispatch_rays(kinc_g5_command_list_t *command_list) { + command_list->impl._commandList->SetComputeRootSignature(dxrRootSignature); + + // Bind the heaps, acceleration structure and dispatch rays + command_list->impl._commandList->SetDescriptorHeaps(1, &descriptorHeap); + command_list->impl._commandList->SetComputeRootDescriptorTable(0, outputDescriptorHandle); + command_list->impl._commandList->SetComputeRootShaderResourceView(1, accel->impl.top_level_accel->GetGPUVirtualAddress()); + command_list->impl._commandList->SetComputeRootDescriptorTable(2, ibgpuDescriptorHandle); + command_list->impl._commandList->SetComputeRootDescriptorTable(3, vbgpuDescriptorHandle); + command_list->impl._commandList->SetComputeRootConstantBufferView(4, pipeline->_constant_buffer->impl.constant_buffer->GetGPUVirtualAddress()); + command_list->impl._commandList->SetComputeRootDescriptorTable(5, tex0gpuDescriptorHandle); + command_list->impl._commandList->SetComputeRootDescriptorTable(6, tex1gpuDescriptorHandle); + command_list->impl._commandList->SetComputeRootDescriptorTable(7, tex2gpuDescriptorHandle); + command_list->impl._commandList->SetComputeRootDescriptorTable(8, texenvgpuDescriptorHandle); + command_list->impl._commandList->SetComputeRootDescriptorTable(9, texsobolgpuDescriptorHandle); + command_list->impl._commandList->SetComputeRootDescriptorTable(10, texscramblegpuDescriptorHandle); + command_list->impl._commandList->SetComputeRootDescriptorTable(11, texrankgpuDescriptorHandle); + + // Since each shader table has only one shader record, the stride is same as the size. + D3D12_DISPATCH_RAYS_DESC dispatchDesc = {0}; + dispatchDesc.HitGroupTable.StartAddress = pipeline->impl.hitgroup_shader_table->GetGPUVirtualAddress(); + dispatchDesc.HitGroupTable.SizeInBytes = pipeline->impl.hitgroup_shader_table->GetDesc().Width; + dispatchDesc.HitGroupTable.StrideInBytes = dispatchDesc.HitGroupTable.SizeInBytes; + dispatchDesc.MissShaderTable.StartAddress = pipeline->impl.miss_shader_table->GetGPUVirtualAddress(); + dispatchDesc.MissShaderTable.SizeInBytes = pipeline->impl.miss_shader_table->GetDesc().Width; + dispatchDesc.MissShaderTable.StrideInBytes = dispatchDesc.MissShaderTable.SizeInBytes; + dispatchDesc.RayGenerationShaderRecord.StartAddress = pipeline->impl.raygen_shader_table->GetGPUVirtualAddress(); + dispatchDesc.RayGenerationShaderRecord.SizeInBytes = pipeline->impl.raygen_shader_table->GetDesc().Width; + dispatchDesc.Width = output->texWidth; + dispatchDesc.Height = output->texHeight; + dispatchDesc.Depth = 1; + dxrCommandList->SetPipelineState1(pipeline->impl.dxr_state); + dxrCommandList->DispatchRays(&dispatchDesc); +} + +void kinc_raytrace_copy(kinc_g5_command_list_t *command_list, kinc_g5_render_target_t *target, kinc_g5_texture_t *source) { + D3D12_RESOURCE_BARRIER preCopyBarriers[2] = {}; + preCopyBarriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + preCopyBarriers[0].Transition.pResource = target->impl.renderTarget; + preCopyBarriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; + preCopyBarriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; + preCopyBarriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + preCopyBarriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + preCopyBarriers[1].Transition.pResource = source->impl.image; + preCopyBarriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + preCopyBarriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE; + preCopyBarriers[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + command_list->impl._commandList->ResourceBarrier(ARRAYSIZE(preCopyBarriers), preCopyBarriers); + + command_list->impl._commandList->CopyResource(target->impl.renderTarget, source->impl.image); + + D3D12_RESOURCE_BARRIER postCopyBarriers[2] = {}; + postCopyBarriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + postCopyBarriers[0].Transition.pResource = target->impl.renderTarget; + postCopyBarriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST; + postCopyBarriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; + postCopyBarriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + postCopyBarriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + postCopyBarriers[1].Transition.pResource = source->impl.image; + postCopyBarriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE; + postCopyBarriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + postCopyBarriers[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + command_list->impl._commandList->ResourceBarrier(ARRAYSIZE(postCopyBarriers), postCopyBarriers); +} + +#endif diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/raytrace.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/raytrace.h new file mode 100644 index 000000000..25ccebd83 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/raytrace.h @@ -0,0 +1,24 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct ID3D12StateObject; +struct ID3D12Resource; + +typedef struct { + struct ID3D12StateObject *dxr_state; + struct ID3D12Resource *raygen_shader_table; + struct ID3D12Resource *miss_shader_table; + struct ID3D12Resource *hitgroup_shader_table; +} kinc_raytrace_pipeline_impl_t; + +typedef struct { + struct ID3D12Resource *bottom_level_accel; + struct ID3D12Resource *top_level_accel; +} kinc_raytrace_acceleration_structure_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/rendertarget.c.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/rendertarget.c.h new file mode 100644 index 000000000..ad3661fca --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/rendertarget.c.h @@ -0,0 +1,287 @@ +#include "rendertarget.h" + +#include +#include +#include + +#ifdef KINC_WINDOWS +#include +#endif + +#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN +extern ID3D12Resource *swapChainRenderTargets[QUEUE_SLOT_COUNT]; +#endif + +static void WaitForFence(ID3D12Fence *fence, UINT64 completionValue, HANDLE waitEvent) { + if (fence->GetCompletedValue() < completionValue) { + fence->SetEventOnCompletion(completionValue, waitEvent); + WaitForSingleObject(waitEvent, INFINITE); + } +} + +static void createRenderTargetView(ID3D12Resource *renderTarget, ID3D12DescriptorHeap *renderTargetDescriptorHeap, DXGI_FORMAT format) { + // const D3D12_RESOURCE_DESC resourceDesc = renderTarget->lpVtbl->GetDesc(renderTarget); + + D3D12_RENDER_TARGET_VIEW_DESC viewDesc; + viewDesc.Format = format; + viewDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + viewDesc.Texture2D.MipSlice = 0; + viewDesc.Texture2D.PlaneSlice = 0; + + device->CreateRenderTargetView(renderTarget, &viewDesc, renderTargetDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); +} + +static DXGI_FORMAT convertFormat(kinc_g5_render_target_format_t format) { + switch (format) { + case KINC_G5_RENDER_TARGET_FORMAT_128BIT_FLOAT: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_64BIT_FLOAT: + return DXGI_FORMAT_R16G16B16A16_FLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT: + return DXGI_FORMAT_R32_FLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT: + return DXGI_FORMAT_R16_FLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_8BIT_RED: + return DXGI_FORMAT_R8_UNORM; + case KINC_G5_RENDER_TARGET_FORMAT_32BIT: + default: +#ifdef KINC_WINDOWS + return DXGI_FORMAT_R8G8B8A8_UNORM; +#else + return DXGI_FORMAT_B8G8R8A8_UNORM; +#endif + } +} + +extern "C" void kinc_memory_emergency(); + +static void render_target_init(kinc_g5_render_target_t *render_target, int width, int height, kinc_g5_render_target_format_t format, int depthBufferBits, + int stencilBufferBits, int samples_per_pixel, int framebuffer_index) { + render_target->texWidth = render_target->width = width; + render_target->texHeight = render_target->height = height; + render_target->impl.stage = 0; + render_target->impl.stage_depth = -1; + render_target->impl.renderTargetReadback = NULL; + render_target->impl.framebuffer_index = framebuffer_index; + + DXGI_FORMAT dxgiFormat = convertFormat(format); + + D3D12_CLEAR_VALUE clearValue; + clearValue.Format = dxgiFormat; + clearValue.Color[0] = 0.0f; + clearValue.Color[1] = 0.0f; + clearValue.Color[2] = 0.0f; + clearValue.Color[3] = 1.0f; + + D3D12_HEAP_PROPERTIES heapProperties; + heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProperties.CreationNodeMask = 1; + heapProperties.VisibleNodeMask = 1; + + D3D12_RESOURCE_DESC texResourceDesc; + texResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + texResourceDesc.Alignment = 0; + texResourceDesc.Width = render_target->texWidth; + texResourceDesc.Height = render_target->texHeight; + texResourceDesc.DepthOrArraySize = 1; + texResourceDesc.MipLevels = 1; + texResourceDesc.Format = dxgiFormat; + texResourceDesc.SampleDesc.Count = 1; + texResourceDesc.SampleDesc.Quality = 0; + texResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + texResourceDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; + + D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; + heapDesc.NumDescriptors = 1; + heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + kinc_microsoft_affirm(device->CreateDescriptorHeap(&heapDesc, IID_GRAPHICS_PPV_ARGS(&render_target->impl.renderTargetDescriptorHeap))); + + if (framebuffer_index >= 0) { +#ifdef KINC_DIRECT3D_HAS_NO_SWAPCHAIN + render_target->impl.renderTarget = swapChainRenderTargets[framebuffer_index]; +#else + IDXGISwapChain *swapChain = kinc_dx_current_window()->swapChain; + kinc_microsoft_affirm(swapChain->GetBuffer(framebuffer_index, IID_PPV_ARGS(&render_target->impl.renderTarget))); + wchar_t buffer[128]; + wsprintf(buffer, L"Backbuffer (index %i)", framebuffer_index); + render_target->impl.renderTarget->SetName(buffer); +#endif + createRenderTargetView(render_target->impl.renderTarget, render_target->impl.renderTargetDescriptorHeap, dxgiFormat); + } + else { + HRESULT result = device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &texResourceDesc, D3D12_RESOURCE_STATE_RENDER_TARGET, + &clearValue, IID_GRAPHICS_PPV_ARGS(&render_target->impl.renderTarget)); + if (result != S_OK) { + for (int i = 0; i < 10; ++i) { + kinc_memory_emergency(); + result = device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &texResourceDesc, D3D12_RESOURCE_STATE_RENDER_TARGET, + &clearValue, IID_GRAPHICS_PPV_ARGS(&render_target->impl.renderTarget)); + if (result == S_OK) { + break; + } + } + } + + D3D12_RENDER_TARGET_VIEW_DESC view; + // const D3D12_RESOURCE_DESC resourceDesc = render_target->impl.renderTarget->lpVtbl->GetDesc(render_target->impl.renderTarget); + view.Format = dxgiFormat; + view.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + view.Texture2D.MipSlice = 0; + view.Texture2D.PlaneSlice = 0; + + device->CreateRenderTargetView(render_target->impl.renderTarget, &view, + render_target->impl.renderTargetDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); + } + + D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {}; + descriptorHeapDesc.NumDescriptors = 1; + + descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + descriptorHeapDesc.NodeMask = 0; + descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + + kinc_microsoft_affirm(device->CreateDescriptorHeap(&descriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(&render_target->impl.srvDescriptorHeap))); + + D3D12_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc; + ZeroMemory(&shaderResourceViewDesc, sizeof(shaderResourceViewDesc)); + shaderResourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + shaderResourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + shaderResourceViewDesc.Format = dxgiFormat; + shaderResourceViewDesc.Texture2D.MipLevels = 1; + shaderResourceViewDesc.Texture2D.MostDetailedMip = 0; + shaderResourceViewDesc.Texture2D.ResourceMinLODClamp = 0.0f; + + device->CreateShaderResourceView(render_target->impl.renderTarget, &shaderResourceViewDesc, + render_target->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); + + if (depthBufferBits > 0) { + D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc = {}; + dsvHeapDesc.NumDescriptors = 1; + dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; + dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + kinc_microsoft_affirm(device->CreateDescriptorHeap(&dsvHeapDesc, IID_GRAPHICS_PPV_ARGS(&render_target->impl.depthStencilDescriptorHeap))); + + D3D12_RESOURCE_DESC depthTexture; + depthTexture.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + depthTexture.Alignment = 0; + depthTexture.Width = width; + depthTexture.Height = height; + depthTexture.DepthOrArraySize = 1; + depthTexture.MipLevels = 1; + depthTexture.Format = DXGI_FORMAT_D32_FLOAT; + depthTexture.SampleDesc.Count = 1; + depthTexture.SampleDesc.Quality = 0; + depthTexture.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + depthTexture.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + + D3D12_CLEAR_VALUE clearValue; + clearValue.Format = DXGI_FORMAT_D32_FLOAT; + clearValue.DepthStencil.Depth = 1.0f; + clearValue.DepthStencil.Stencil = 0; + + D3D12_HEAP_PROPERTIES heapProperties; + heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProperties.CreationNodeMask = 1; + heapProperties.VisibleNodeMask = 1; + + HRESULT result = device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &depthTexture, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clearValue, + IID_GRAPHICS_PPV_ARGS(&render_target->impl.depthStencilTexture)); + if (result != S_OK) { + for (int i = 0; i < 10; ++i) { + kinc_memory_emergency(); + result = device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &depthTexture, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clearValue, + IID_GRAPHICS_PPV_ARGS(&render_target->impl.depthStencilTexture)); + if (result == S_OK) { + break; + } + } + } + + device->CreateDepthStencilView(render_target->impl.depthStencilTexture, NULL, + render_target->impl.depthStencilDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); + + // Reading depth texture as a shader resource + D3D12_DESCRIPTOR_HEAP_DESC srvDepthHeapDesc = {}; + srvDepthHeapDesc.NumDescriptors = 1; + srvDepthHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + srvDepthHeapDesc.NodeMask = 0; + srvDepthHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + kinc_microsoft_affirm(device->CreateDescriptorHeap(&srvDepthHeapDesc, IID_GRAPHICS_PPV_ARGS(&render_target->impl.srvDepthDescriptorHeap))); + + D3D12_SHADER_RESOURCE_VIEW_DESC srvDepthViewDesc; + ZeroMemory(&srvDepthViewDesc, sizeof(srvDepthViewDesc)); + srvDepthViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srvDepthViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + srvDepthViewDesc.Format = DXGI_FORMAT_R32_FLOAT; + srvDepthViewDesc.Texture2D.MipLevels = 1; + srvDepthViewDesc.Texture2D.MostDetailedMip = 0; + srvDepthViewDesc.Texture2D.ResourceMinLODClamp = 0.0f; + device->CreateShaderResourceView(render_target->impl.depthStencilTexture, &srvDepthViewDesc, + render_target->impl.srvDepthDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); + } + else { + render_target->impl.depthStencilDescriptorHeap = NULL; + render_target->impl.depthStencilTexture = NULL; + render_target->impl.srvDepthDescriptorHeap = NULL; + } + + render_target->impl.scissor.top = 0; + render_target->impl.scissor.left = 0; + render_target->impl.scissor.right = width; + render_target->impl.scissor.bottom = height; + + render_target->impl.viewport.TopLeftX = 0.0f; + render_target->impl.viewport.TopLeftY = 0.0f; + render_target->impl.viewport.Width = (float)width; + render_target->impl.viewport.Height = (float)height; + render_target->impl.viewport.MinDepth = 0.0f; + render_target->impl.viewport.MaxDepth = 1.0f; +} + +void kinc_g5_render_target_init_with_multisampling(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + render_target_init(target, width, height, format, depthBufferBits, stencilBufferBits, samples_per_pixel, -1); +} + +static int framebuffer_count = 0; + +void kinc_g5_render_target_init_framebuffer_with_multisampling(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + render_target_init(target, width, height, format, depthBufferBits, stencilBufferBits, samples_per_pixel, framebuffer_count); + framebuffer_count += 1; +} + +void kinc_g5_render_target_init_cube_with_multisampling(kinc_g5_render_target_t *render_target, int cubeMapSize, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + render_target->impl.stage = 0; + render_target->impl.stage_depth = -1; +} + +void kinc_g5_render_target_destroy(kinc_g5_render_target_t *render_target) { + if (render_target->impl.framebuffer_index >= 0) { + framebuffer_count -= 1; + } + + render_target->impl.renderTarget->Release(); + render_target->impl.renderTargetDescriptorHeap->Release(); + render_target->impl.srvDescriptorHeap->Release(); + if (render_target->impl.depthStencilTexture != NULL) { + render_target->impl.depthStencilTexture->Release(); + render_target->impl.depthStencilDescriptorHeap->Release(); + render_target->impl.srvDepthDescriptorHeap->Release(); + } + if (render_target->impl.renderTargetReadback != NULL) { + render_target->impl.renderTargetReadback->Release(); + } +} + +void kinc_g5_render_target_set_depth_stencil_from(kinc_g5_render_target_t *render_target, kinc_g5_render_target_t *source) { + render_target->impl.depthStencilDescriptorHeap = source->impl.depthStencilDescriptorHeap; + render_target->impl.srvDepthDescriptorHeap = source->impl.srvDepthDescriptorHeap; + render_target->impl.depthStencilTexture = source->impl.depthStencilTexture; +} diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/rendertarget.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/rendertarget.h new file mode 100644 index 000000000..569ffebf1 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/rendertarget.h @@ -0,0 +1,28 @@ +#pragma once + +#include "d3d12mini.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum RenderTargetResourceState { RenderTargetResourceStateUndefined, RenderTargetResourceStateRenderTarget, RenderTargetResourceStateTexture }; + +typedef struct { + struct ID3D12Resource *renderTarget; + struct ID3D12Resource *renderTargetReadback; + struct ID3D12DescriptorHeap *renderTargetDescriptorHeap; + struct ID3D12DescriptorHeap *srvDescriptorHeap; + struct ID3D12DescriptorHeap *depthStencilDescriptorHeap; + struct ID3D12DescriptorHeap *srvDepthDescriptorHeap; + struct ID3D12Resource *depthStencilTexture; + struct D3D12Viewport viewport; + struct D3D12Rect scissor; + int stage; + int stage_depth; + int framebuffer_index; +} RenderTarget5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/sampler.c.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/sampler.c.h new file mode 100644 index 000000000..cf50bb001 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/sampler.c.h @@ -0,0 +1,145 @@ +#include + +static D3D12_TEXTURE_ADDRESS_MODE convert_texture_addressing(kinc_g5_texture_addressing_t addressing) { + switch (addressing) { + case KINC_G5_TEXTURE_ADDRESSING_REPEAT: + return D3D12_TEXTURE_ADDRESS_MODE_WRAP; + case KINC_G5_TEXTURE_ADDRESSING_MIRROR: + return D3D12_TEXTURE_ADDRESS_MODE_MIRROR; + case KINC_G5_TEXTURE_ADDRESSING_CLAMP: + return D3D12_TEXTURE_ADDRESS_MODE_CLAMP; + case KINC_G5_TEXTURE_ADDRESSING_BORDER: + return D3D12_TEXTURE_ADDRESS_MODE_BORDER; + default: + assert(false); + return D3D12_TEXTURE_ADDRESS_MODE_WRAP; + } +} + +static D3D12_FILTER convert_filter(kinc_g5_texture_filter_t minification, kinc_g5_texture_filter_t magnification, kinc_g5_mipmap_filter_t mipmap) { + switch (minification) { + case KINC_G5_TEXTURE_FILTER_POINT: + switch (magnification) { + case KINC_G5_TEXTURE_FILTER_POINT: + switch (mipmap) { + case KINC_G5_MIPMAP_FILTER_NONE: + case KINC_G5_MIPMAP_FILTER_POINT: + return D3D12_FILTER_MIN_MAG_MIP_POINT; + case KINC_G5_MIPMAP_FILTER_LINEAR: + return D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR; + } + case KINC_G5_TEXTURE_FILTER_LINEAR: + switch (mipmap) { + case KINC_G5_MIPMAP_FILTER_NONE: + case KINC_G5_MIPMAP_FILTER_POINT: + return D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; + case KINC_G5_MIPMAP_FILTER_LINEAR: + return D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR; + } + case KINC_G5_TEXTURE_FILTER_ANISOTROPIC: + return D3D12_FILTER_ANISOTROPIC; + } + case KINC_G5_TEXTURE_FILTER_LINEAR: + switch (magnification) { + case KINC_G5_TEXTURE_FILTER_POINT: + switch (mipmap) { + case KINC_G5_MIPMAP_FILTER_NONE: + case KINC_G5_MIPMAP_FILTER_POINT: + return D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT; + case KINC_G5_MIPMAP_FILTER_LINEAR: + return D3D12_FILTER_MIN_MAG_MIP_LINEAR; + } + case KINC_G5_TEXTURE_FILTER_LINEAR: + switch (mipmap) { + case KINC_G5_MIPMAP_FILTER_NONE: + case KINC_G5_MIPMAP_FILTER_POINT: + return D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT; + case KINC_G5_MIPMAP_FILTER_LINEAR: + return D3D12_FILTER_MIN_MAG_MIP_LINEAR; + } + case KINC_G5_TEXTURE_FILTER_ANISOTROPIC: + return D3D12_FILTER_ANISOTROPIC; + } + case KINC_G5_TEXTURE_FILTER_ANISOTROPIC: + return D3D12_FILTER_ANISOTROPIC; + } + + assert(false); + return D3D12_FILTER_MIN_MAG_MIP_POINT; +} + +static D3D12_FILTER convert_comparison_filter(kinc_g5_texture_filter_t minification, kinc_g5_texture_filter_t magnification, kinc_g5_mipmap_filter_t mipmap) { + switch (minification) { + case KINC_G5_TEXTURE_FILTER_POINT: + switch (magnification) { + case KINC_G5_TEXTURE_FILTER_POINT: + switch (mipmap) { + case KINC_G5_MIPMAP_FILTER_NONE: + case KINC_G5_MIPMAP_FILTER_POINT: + return D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT; + case KINC_G5_MIPMAP_FILTER_LINEAR: + return D3D12_FILTER_COMPARISON_MIN_MAG_POINT_MIP_LINEAR; + } + case KINC_G5_TEXTURE_FILTER_LINEAR: + switch (mipmap) { + case KINC_G5_MIPMAP_FILTER_NONE: + case KINC_G5_MIPMAP_FILTER_POINT: + return D3D12_FILTER_COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT; + case KINC_G5_MIPMAP_FILTER_LINEAR: + return D3D12_FILTER_COMPARISON_MIN_POINT_MAG_MIP_LINEAR; + } + case KINC_G5_TEXTURE_FILTER_ANISOTROPIC: + return D3D12_FILTER_COMPARISON_ANISOTROPIC; + } + case KINC_G5_TEXTURE_FILTER_LINEAR: + switch (magnification) { + case KINC_G5_TEXTURE_FILTER_POINT: + switch (mipmap) { + case KINC_G5_MIPMAP_FILTER_NONE: + case KINC_G5_MIPMAP_FILTER_POINT: + return D3D12_FILTER_COMPARISON_MIN_LINEAR_MAG_MIP_POINT; + case KINC_G5_MIPMAP_FILTER_LINEAR: + return D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR; + } + case KINC_G5_TEXTURE_FILTER_LINEAR: + switch (mipmap) { + case KINC_G5_MIPMAP_FILTER_NONE: + case KINC_G5_MIPMAP_FILTER_POINT: + return D3D12_FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT; + case KINC_G5_MIPMAP_FILTER_LINEAR: + return D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR; + } + case KINC_G5_TEXTURE_FILTER_ANISOTROPIC: + return D3D12_FILTER_COMPARISON_ANISOTROPIC; + } + case KINC_G5_TEXTURE_FILTER_ANISOTROPIC: + return D3D12_FILTER_COMPARISON_ANISOTROPIC; + } + + assert(false); + return D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT; +} + +void kinc_g5_sampler_init(kinc_g5_sampler_t *sampler, const kinc_g5_sampler_options_t *options) { + D3D12_DESCRIPTOR_HEAP_DESC descHeapSampler = {}; + descHeapSampler.NumDescriptors = 2; + descHeapSampler.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; + descHeapSampler.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + device->CreateDescriptorHeap(&descHeapSampler, IID_GRAPHICS_PPV_ARGS(&sampler->impl.sampler_heap)); + + D3D12_SAMPLER_DESC samplerDesc; + ZeroMemory(&samplerDesc, sizeof(D3D12_SAMPLER_DESC)); + samplerDesc.Filter = options->is_comparison ? convert_comparison_filter(options->minification_filter, options->magnification_filter, options->mipmap_filter) + : convert_filter(options->minification_filter, options->magnification_filter, options->mipmap_filter); + samplerDesc.AddressU = convert_texture_addressing(options->u_addressing); + samplerDesc.AddressV = convert_texture_addressing(options->v_addressing); + samplerDesc.AddressW = convert_texture_addressing(options->w_addressing); + samplerDesc.MinLOD = options->lod_min_clamp; + samplerDesc.MaxLOD = options->lod_max_clamp; + samplerDesc.MipLODBias = 0.0f; + samplerDesc.MaxAnisotropy = options->max_anisotropy; + samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; + device->CreateSampler(&samplerDesc, sampler->impl.sampler_heap->GetCPUDescriptorHandleForHeapStart()); +} + +void kinc_g5_sampler_destroy(kinc_g5_sampler_t *sampler) {} diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/sampler.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/sampler.h new file mode 100644 index 000000000..320dbcc4e --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/sampler.h @@ -0,0 +1,7 @@ +#pragma once + +struct ID3D12DescriptorHeap; + +typedef struct kinc_g5_sampler_impl { + struct ID3D12DescriptorHeap *sampler_heap; +} kinc_g5_sampler_impl_t; diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/shader.c.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/shader.c.h new file mode 100644 index 000000000..d5856141c --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/shader.c.h @@ -0,0 +1,87 @@ +#include +#include +#include + +void kinc_g5_shader_init(kinc_g5_shader_t *shader, const void *_data, size_t length, kinc_g5_shader_type_t type) { + memset(shader->impl.constants, 0, sizeof(shader->impl.constants)); + memset(shader->impl.attributes, 0, sizeof(shader->impl.attributes)); + memset(shader->impl.textures, 0, sizeof(shader->impl.textures)); + + unsigned index = 0; + uint8_t *data = (uint8_t *)_data; + + int attributesCount = data[index++]; + for (int i = 0; i < attributesCount; ++i) { + char name[64]; + for (unsigned i2 = 0; i2 < 63; ++i2) { + name[i2] = data[index++]; + if (name[i2] == 0) + break; + } + strcpy(shader->impl.attributes[i].name, name); + shader->impl.attributes[i].attribute = data[index++]; + } + + uint8_t texCount = data[index++]; + for (unsigned i = 0; i < texCount; ++i) { + char name[64]; + for (unsigned i2 = 0; i2 < 63; ++i2) { + name[i2] = data[index++]; + if (name[i2] == 0) + break; + } + strcpy(shader->impl.textures[i].name, name); + shader->impl.textures[i].texture = data[index++]; + } + shader->impl.texturesCount = texCount; + + uint8_t constantCount = data[index++]; + shader->impl.constantsSize = 0; + for (unsigned i = 0; i < constantCount; ++i) { + char name[64]; + for (unsigned i2 = 0; i2 < 63; ++i2) { + name[i2] = data[index++]; + if (name[i2] == 0) + break; + } + ShaderConstant constant; + memcpy(&constant.offset, &data[index], sizeof(constant.offset)); + index += 4; + memcpy(&constant.size, &data[index], sizeof(constant.size)); + index += 4; +#ifdef KINC_WINDOWS + index += 2; // columns and rows +#endif + strcpy(constant.name, name); + shader->impl.constants[i] = constant; + shader->impl.constantsSize = constant.offset + constant.size; + } + + shader->impl.length = (int)length - index; + shader->impl.data = (uint8_t *)malloc(shader->impl.length); + memcpy(shader->impl.data, &data[index], shader->impl.length); + + switch (type) { + case KINC_G5_SHADER_TYPE_VERTEX: + // Microsoft::affirm(device->CreateVertexShader(this->data, this->length, nullptr, (ID3D11VertexShader**)&shader)); + break; + case KINC_G5_SHADER_TYPE_FRAGMENT: + // Microsoft::affirm(device->CreatePixelShader(this->data, this->length, nullptr, (ID3D11PixelShader**)&shader)); + break; + case KINC_G5_SHADER_TYPE_GEOMETRY: + // Microsoft::affirm(device->CreateGeometryShader(this->data, this->length, nullptr, (ID3D11GeometryShader**)&shader)); + break; + case KINC_G5_SHADER_TYPE_TESSELLATION_CONTROL: + // Microsoft::affirm(device->CreateHullShader(this->data, this->length, nullptr, (ID3D11HullShader**)&shader)); + break; + case KINC_G5_SHADER_TYPE_TESSELLATION_EVALUATION: + // Microsoft::affirm(device->CreateDomainShader(this->data, this->length, nullptr, (ID3D11DomainShader**)&shader)); + break; + default: + break; + } +} + +void kinc_g5_shader_destroy(kinc_g5_shader_t *shader) { + free(shader->impl.data); +} diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/shader.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/shader.h new file mode 100644 index 000000000..9f29f628b --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/shader.h @@ -0,0 +1,36 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char name[64]; + uint32_t offset; + uint32_t size; +} ShaderConstant; + +typedef struct { + char name[64]; + int attribute; +} ShaderAttribute; + +typedef struct { + char name[64]; + int texture; +} ShaderTexture; + +typedef struct { + ShaderConstant constants[32]; + int constantsSize; + ShaderAttribute attributes[32]; + ShaderTexture textures[32]; + int texturesCount; + void *shader; + uint8_t *data; + int length; +} Shader5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/texture.c.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/texture.c.h new file mode 100644 index 000000000..6b406d63c --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/texture.c.h @@ -0,0 +1,425 @@ +#include "texture.h" + +#include +#include +#include + +#include + +#include + +static const int heapSize = 1024; + +#if defined(KINC_WINDOWS) +/*static int d3d12_textureAlignment() { + return D3D12_TEXTURE_DATA_PITCH_ALIGNMENT; +}*/ +#else +int d3d12_textureAlignment(); +#endif + +void kinc_g5_internal_reset_textures(struct kinc_g5_command_list *list) { + for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) { + list->impl.currentRenderTargets[i] = NULL; + list->impl.currentTextures[i] = NULL; + list->impl.current_samplers[i] = NULL; + } +} + +static inline UINT64 GetRequiredIntermediateSize(ID3D12Resource *destinationResource, UINT FirstSubresource, UINT NumSubresources) { + D3D12_RESOURCE_DESC desc = destinationResource->GetDesc(); + UINT64 requiredSize = 0; + device->GetCopyableFootprints(&desc, FirstSubresource, NumSubresources, 0, NULL, NULL, NULL, &requiredSize); + device->Release(); + return requiredSize; +} + +static DXGI_FORMAT convertImageFormat(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA128: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + case KINC_IMAGE_FORMAT_RGBA64: + return DXGI_FORMAT_R16G16B16A16_FLOAT; + case KINC_IMAGE_FORMAT_RGB24: + return DXGI_FORMAT_R8G8B8A8_UNORM; + case KINC_IMAGE_FORMAT_A32: + return DXGI_FORMAT_R32_FLOAT; + case KINC_IMAGE_FORMAT_A16: + return DXGI_FORMAT_R16_FLOAT; + case KINC_IMAGE_FORMAT_GREY8: + return DXGI_FORMAT_R8_UNORM; + case KINC_IMAGE_FORMAT_BGRA32: + return DXGI_FORMAT_B8G8R8A8_UNORM; + case KINC_IMAGE_FORMAT_RGBA32: + return DXGI_FORMAT_R8G8B8A8_UNORM; + default: + return DXGI_FORMAT_R8G8B8A8_UNORM; + } +} + +static int formatByteSize(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA128: + return 16; + case KINC_IMAGE_FORMAT_RGBA64: + return 8; + case KINC_IMAGE_FORMAT_RGB24: + return 4; + case KINC_IMAGE_FORMAT_A32: + return 4; + case KINC_IMAGE_FORMAT_A16: + return 2; + case KINC_IMAGE_FORMAT_GREY8: + return 1; + case KINC_IMAGE_FORMAT_BGRA32: + case KINC_IMAGE_FORMAT_RGBA32: + return 4; + default: + return 4; + } +} + +void kinc_g5_internal_set_textures(kinc_g5_command_list_t *list) { + if (list->impl.currentRenderTargets[0] != NULL || list->impl.currentTextures[0] != NULL) { + int srvStep = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + int samplerStep = device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + + if (list->impl.heapIndex + KINC_INTERNAL_G5_TEXTURE_COUNT >= heapSize) { + list->impl.heapIndex = 0; + } + + D3D12_GPU_DESCRIPTOR_HANDLE srvGpu = list->impl.srvHeap->GetGPUDescriptorHandleForHeapStart(); + D3D12_GPU_DESCRIPTOR_HANDLE samplerGpu = list->impl.samplerHeap->GetGPUDescriptorHandleForHeapStart(); + srvGpu.ptr += list->impl.heapIndex * srvStep; + samplerGpu.ptr += list->impl.heapIndex * samplerStep; + + for (int i = 0; i < KINC_INTERNAL_G5_TEXTURE_COUNT; ++i) { + if ((list->impl.currentRenderTargets[i] != NULL || list->impl.currentTextures[i] != NULL) && list->impl.current_samplers[i] != NULL) { + ID3D12DescriptorHeap *samplerDescriptorHeap = list->impl.current_samplers[i]->impl.sampler_heap; + + D3D12_CPU_DESCRIPTOR_HANDLE srvCpu = list->impl.srvHeap->GetCPUDescriptorHandleForHeapStart(); + D3D12_CPU_DESCRIPTOR_HANDLE samplerCpu = list->impl.samplerHeap->GetCPUDescriptorHandleForHeapStart(); + srvCpu.ptr += list->impl.heapIndex * srvStep; + samplerCpu.ptr += list->impl.heapIndex * samplerStep; + ++list->impl.heapIndex; + + if (list->impl.currentRenderTargets[i] != NULL) { + bool is_depth = list->impl.currentRenderTargets[i]->impl.stage_depth == i; + D3D12_CPU_DESCRIPTOR_HANDLE sourceCpu = + is_depth ? list->impl.currentRenderTargets[i]->impl.srvDepthDescriptorHeap->GetCPUDescriptorHandleForHeapStart() + : list->impl.currentRenderTargets[i]->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + device->CopyDescriptorsSimple(1, srvCpu, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + device->CopyDescriptorsSimple(1, samplerCpu, samplerDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + } + else { + D3D12_CPU_DESCRIPTOR_HANDLE sourceCpu = list->impl.currentTextures[i]->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart(); + device->CopyDescriptorsSimple(1, srvCpu, sourceCpu, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + device->CopyDescriptorsSimple(1, samplerCpu, samplerDescriptorHeap->GetCPUDescriptorHandleForHeapStart(), + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + } + } + } + + ID3D12DescriptorHeap *heaps[2] = {list->impl.srvHeap, list->impl.samplerHeap}; + list->impl._commandList->SetDescriptorHeaps(2, heaps); + if (compute_pipeline_set) { + list->impl._commandList->SetComputeRootDescriptorTable(0, srvGpu); + list->impl._commandList->SetComputeRootDescriptorTable(1, srvGpu); + list->impl._commandList->SetComputeRootDescriptorTable(2, samplerGpu); + } + else { + list->impl._commandList->SetGraphicsRootDescriptorTable(0, srvGpu); + list->impl._commandList->SetGraphicsRootDescriptorTable(1, samplerGpu); + } + } +} + +void createHeaps(kinc_g5_command_list_t *list) { + D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; + heapDesc.NumDescriptors = heapSize; + heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + device->CreateDescriptorHeap(&heapDesc, IID_GRAPHICS_PPV_ARGS(&list->impl.srvHeap)); + + D3D12_DESCRIPTOR_HEAP_DESC samplerHeapDesc = {}; + samplerHeapDesc.NumDescriptors = heapSize; + samplerHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; + samplerHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + device->CreateDescriptorHeap(&samplerHeapDesc, IID_GRAPHICS_PPV_ARGS(&list->impl.samplerHeap)); +} + +extern "C" void kinc_memory_emergency(); + +void kinc_g5_texture_init_from_image(kinc_g5_texture_t *texture, kinc_image_t *image) { + memset(&texture->impl, 0, sizeof(texture->impl)); + texture->impl.stage = 0; + texture->impl.mipmap = true; + texture->texWidth = image->width; + texture->texHeight = image->height; + + DXGI_FORMAT d3dformat = convertImageFormat(image->format); + int formatSize = formatByteSize(image->format); + + D3D12_HEAP_PROPERTIES heapPropertiesDefault; + heapPropertiesDefault.Type = D3D12_HEAP_TYPE_DEFAULT; + heapPropertiesDefault.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapPropertiesDefault.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapPropertiesDefault.CreationNodeMask = 1; + heapPropertiesDefault.VisibleNodeMask = 1; + + D3D12_RESOURCE_DESC resourceDescTex; + resourceDescTex.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + resourceDescTex.Alignment = 0; + resourceDescTex.Width = texture->texWidth; + resourceDescTex.Height = texture->texHeight; + resourceDescTex.DepthOrArraySize = 1; + resourceDescTex.MipLevels = 1; + resourceDescTex.Format = d3dformat; + resourceDescTex.SampleDesc.Count = 1; + resourceDescTex.SampleDesc.Quality = 0; + resourceDescTex.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + resourceDescTex.Flags = D3D12_RESOURCE_FLAG_NONE; + + HRESULT result = device->CreateCommittedResource(&heapPropertiesDefault, D3D12_HEAP_FLAG_NONE, &resourceDescTex, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + NULL, IID_GRAPHICS_PPV_ARGS(&texture->impl.image)); + if (result != S_OK) { + for (int i = 0; i < 10; ++i) { + kinc_memory_emergency(); + result = device->CreateCommittedResource(&heapPropertiesDefault, D3D12_HEAP_FLAG_NONE, &resourceDescTex, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + NULL, IID_GRAPHICS_PPV_ARGS(&texture->impl.image)); + if (result == S_OK) { + break; + } + } + } + + D3D12_HEAP_PROPERTIES heapPropertiesUpload; + heapPropertiesUpload.Type = D3D12_HEAP_TYPE_UPLOAD; + heapPropertiesUpload.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapPropertiesUpload.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapPropertiesUpload.CreationNodeMask = 1; + heapPropertiesUpload.VisibleNodeMask = 1; + + const UINT64 uploadBufferSize = GetRequiredIntermediateSize(texture->impl.image, 0, 1); + D3D12_RESOURCE_DESC resourceDescBuffer; + resourceDescBuffer.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDescBuffer.Alignment = 0; + resourceDescBuffer.Width = uploadBufferSize; + resourceDescBuffer.Height = 1; + resourceDescBuffer.DepthOrArraySize = 1; + resourceDescBuffer.MipLevels = 1; + resourceDescBuffer.Format = DXGI_FORMAT_UNKNOWN; + resourceDescBuffer.SampleDesc.Count = 1; + resourceDescBuffer.SampleDesc.Quality = 0; + resourceDescBuffer.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + resourceDescBuffer.Flags = D3D12_RESOURCE_FLAG_NONE; + + result = device->CreateCommittedResource(&heapPropertiesUpload, D3D12_HEAP_FLAG_NONE, &resourceDescBuffer, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + IID_GRAPHICS_PPV_ARGS(&texture->impl.uploadImage)); + if (result != S_OK) { + for (int i = 0; i < 10; ++i) { + kinc_memory_emergency(); + result = device->CreateCommittedResource(&heapPropertiesUpload, D3D12_HEAP_FLAG_NONE, &resourceDescBuffer, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + IID_GRAPHICS_PPV_ARGS(&texture->impl.uploadImage)); + if (result == S_OK) { + break; + } + } + } + + texture->impl.stride = (int)ceilf(uploadBufferSize / (float)(image->height * d3d12_textureAlignment())) * d3d12_textureAlignment(); + + BYTE *pixel; + texture->impl.uploadImage->Map(0, NULL, (void **)&pixel); + int pitch = kinc_g5_texture_stride(texture); + for (int y = 0; y < texture->texHeight; ++y) { + memcpy(&pixel[y * pitch], &((uint8_t *)image->data)[y * texture->texWidth * formatSize], texture->texWidth * formatSize); + } + texture->impl.uploadImage->Unmap(0, NULL); + + D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc = {}; + descriptorHeapDesc.NumDescriptors = 1; + + descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + descriptorHeapDesc.NodeMask = 0; + descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + + device->CreateDescriptorHeap(&descriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(&texture->impl.srvDescriptorHeap)); + + D3D12_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc; + ZeroMemory(&shaderResourceViewDesc, sizeof(shaderResourceViewDesc)); + shaderResourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + shaderResourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + shaderResourceViewDesc.Format = d3dformat; + shaderResourceViewDesc.Texture2D.MipLevels = 1; + shaderResourceViewDesc.Texture2D.MostDetailedMip = 0; + shaderResourceViewDesc.Texture2D.ResourceMinLODClamp = 0.0f; + + device->CreateShaderResourceView(texture->impl.image, &shaderResourceViewDesc, texture->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); +} + +void create_texture(struct kinc_g5_texture *texture, int width, int height, kinc_image_format_t format, D3D12_RESOURCE_FLAGS flags) { + // kinc_image_init(&texture->image, width, height, format, readable); + memset(&texture->impl, 0, sizeof(texture->impl)); + texture->impl.stage = 0; + texture->impl.mipmap = true; + texture->texWidth = width; + texture->texHeight = height; + + DXGI_FORMAT d3dformat = convertImageFormat(format); + + D3D12_HEAP_PROPERTIES heapPropertiesDefault; + heapPropertiesDefault.Type = D3D12_HEAP_TYPE_DEFAULT; + heapPropertiesDefault.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapPropertiesDefault.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapPropertiesDefault.CreationNodeMask = 1; + heapPropertiesDefault.VisibleNodeMask = 1; + + D3D12_RESOURCE_DESC resourceDescTex; + resourceDescTex.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + resourceDescTex.Alignment = 0; + resourceDescTex.Width = texture->texWidth; + resourceDescTex.Height = texture->texHeight; + resourceDescTex.DepthOrArraySize = 1; + resourceDescTex.MipLevels = 1; + resourceDescTex.Format = d3dformat; + resourceDescTex.SampleDesc.Count = 1; + resourceDescTex.SampleDesc.Quality = 0; + resourceDescTex.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + resourceDescTex.Flags = flags; + + HRESULT result = device->CreateCommittedResource(&heapPropertiesDefault, D3D12_HEAP_FLAG_NONE, &resourceDescTex, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + NULL, IID_GRAPHICS_PPV_ARGS(&texture->impl.image)); + if (result != S_OK) { + for (int i = 0; i < 10; ++i) { + kinc_memory_emergency(); + result = device->CreateCommittedResource(&heapPropertiesDefault, D3D12_HEAP_FLAG_NONE, &resourceDescTex, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + NULL, IID_GRAPHICS_PPV_ARGS(&texture->impl.image)); + if (result == S_OK) { + break; + } + } + } + + D3D12_HEAP_PROPERTIES heapPropertiesUpload; + heapPropertiesUpload.Type = D3D12_HEAP_TYPE_UPLOAD; + heapPropertiesUpload.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapPropertiesUpload.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapPropertiesUpload.CreationNodeMask = 1; + heapPropertiesUpload.VisibleNodeMask = 1; + + const UINT64 uploadBufferSize = GetRequiredIntermediateSize(texture->impl.image, 0, 1); + D3D12_RESOURCE_DESC resourceDescBuffer; + resourceDescBuffer.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDescBuffer.Alignment = 0; + resourceDescBuffer.Width = uploadBufferSize; + resourceDescBuffer.Height = 1; + resourceDescBuffer.DepthOrArraySize = 1; + resourceDescBuffer.MipLevels = 1; + resourceDescBuffer.Format = DXGI_FORMAT_UNKNOWN; + resourceDescBuffer.SampleDesc.Count = 1; + resourceDescBuffer.SampleDesc.Quality = 0; + resourceDescBuffer.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + resourceDescBuffer.Flags = D3D12_RESOURCE_FLAG_NONE; + + result = device->CreateCommittedResource(&heapPropertiesUpload, D3D12_HEAP_FLAG_NONE, &resourceDescBuffer, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + IID_GRAPHICS_PPV_ARGS(&texture->impl.uploadImage)); + if (result != S_OK) { + for (int i = 0; i < 10; ++i) { + kinc_memory_emergency(); + result = device->CreateCommittedResource(&heapPropertiesUpload, D3D12_HEAP_FLAG_NONE, &resourceDescBuffer, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + IID_GRAPHICS_PPV_ARGS(&texture->impl.uploadImage)); + if (result == S_OK) { + break; + } + } + } + + texture->impl.stride = (int)ceilf(uploadBufferSize / (float)(height * d3d12_textureAlignment())) * d3d12_textureAlignment(); + + D3D12_DESCRIPTOR_HEAP_DESC descriptorHeapDesc; + ZeroMemory(&descriptorHeapDesc, sizeof(descriptorHeapDesc)); + descriptorHeapDesc.NumDescriptors = 1; + + descriptorHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + descriptorHeapDesc.NodeMask = 0; + descriptorHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + + device->CreateDescriptorHeap(&descriptorHeapDesc, IID_GRAPHICS_PPV_ARGS(&texture->impl.srvDescriptorHeap)); + + D3D12_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc = {}; + shaderResourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + shaderResourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + shaderResourceViewDesc.Format = d3dformat; + shaderResourceViewDesc.Texture2D.MipLevels = 1; + shaderResourceViewDesc.Texture2D.MostDetailedMip = 0; + shaderResourceViewDesc.Texture2D.ResourceMinLODClamp = 0.0f; + + device->CreateShaderResourceView(texture->impl.image, &shaderResourceViewDesc, texture->impl.srvDescriptorHeap->GetCPUDescriptorHandleForHeapStart()); +} + +void kinc_g5_texture_init(struct kinc_g5_texture *texture, int width, int height, kinc_image_format_t format) { + create_texture(texture, width, height, format, D3D12_RESOURCE_FLAG_NONE); +} + +void kinc_g5_texture_init3d(kinc_g5_texture_t *texture, int width, int height, int depth, kinc_image_format_t format) { + // kinc_image_init3d(&texture->image, width, height, depth, format, readable); +} + +void kinc_g5_texture_init_non_sampled_access(struct kinc_g5_texture *texture, int width, int height, kinc_image_format_t format) { + create_texture(texture, width, height, format, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS); +} + +void kinc_g5_texture_destroy(struct kinc_g5_texture *texture) { + texture->impl.image->Release(); + texture->impl.uploadImage->Release(); + texture->impl.srvDescriptorHeap->Release(); +} + +void kinc_g5_internal_texture_unmipmap(struct kinc_g5_texture *texture) { + texture->impl.mipmap = false; +} + +void kinc_g5_internal_texture_set(kinc_g5_command_list_t *list, struct kinc_g5_texture *texture, int unit) { + if (unit < 0) + return; + // context->PSSetShaderResources(unit.unit, 1, &view); + texture->impl.stage = unit; + list->impl.currentTextures[texture->impl.stage] = texture; + list->impl.currentRenderTargets[texture->impl.stage] = NULL; +} + +void kinc_g5_internal_sampler_set(kinc_g5_command_list_t *list, kinc_g5_sampler_t *sampler, int unit) { + if (unit < 0) + return; + + list->impl.current_samplers[unit] = sampler; +} + +uint8_t *kinc_g5_texture_lock(struct kinc_g5_texture *texture) { + BYTE *pixel; + texture->impl.uploadImage->Map(0, NULL, (void **)&pixel); + return pixel; +} + +void kinc_g5_texture_unlock(struct kinc_g5_texture *texture) { + texture->impl.uploadImage->Unmap(0, NULL); +} + +void kinc_g5_texture_clear(kinc_g5_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color) {} + +int kinc_g5_texture_stride(struct kinc_g5_texture *texture) { + /*int baseStride = texture->format == KINC_IMAGE_FORMAT_RGBA32 ? (texture->texWidth * 4) : texture->texWidth; + if (texture->format == KINC_IMAGE_FORMAT_GREY8) return texture->texWidth; // please investigate further + for (int i = 0;; ++i) { + if (d3d12_textureAlignment() * i >= baseStride) { + return d3d12_textureAlignment() * i; + } + }*/ + return texture->impl.stride; +} + +void kinc_g5_texture_generate_mipmaps(struct kinc_g5_texture *texture, int levels) {} + +void kinc_g5_texture_set_mipmap(struct kinc_g5_texture *texture, kinc_image_t *mipmap, int level) {} diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/texture.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/texture.h new file mode 100644 index 000000000..d12506247 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/texture.h @@ -0,0 +1,32 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct ID3D12Resource; +struct ID3D12DescriptorHeap; +struct ID3D12GraphicsCommandList; + +typedef struct { + int unit; +} TextureUnit5Impl; + +typedef struct { + bool mipmap; + int stage; + int stride; + struct ID3D12Resource *image; + struct ID3D12Resource *uploadImage; + struct ID3D12DescriptorHeap *srvDescriptorHeap; +} Texture5Impl; + +struct kinc_g5_texture; +struct kinc_g5_command_list; + +void kinc_g5_internal_set_textures(struct kinc_g5_command_list *commandList); +void kinc_g5_internal_texture_set(struct kinc_g5_command_list *commandList, struct kinc_g5_texture *texture, int unit); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/vertexbuffer.c.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/vertexbuffer.c.h new file mode 100644 index 000000000..ed3930c57 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/vertexbuffer.c.h @@ -0,0 +1,123 @@ +#include "vertexbuffer.h" + +#include + +#include +#include + +kinc_g5_vertex_buffer_t *_current_vertex_buffer = NULL; + +void kinc_g5_vertex_buffer_init(kinc_g5_vertex_buffer_t *buffer, int count, kinc_g5_vertex_structure_t *structure, bool gpuMemory, int instanceDataStepRate) { + buffer->impl.myCount = count; + + // static_assert(sizeof(D3D12VertexBufferView) == sizeof(D3D12_VERTEX_BUFFER_VIEW), "Something is wrong with D3D12IVertexBufferView"); + + buffer->impl.myStride = 0; + for (int i = 0; i < structure->size; ++i) { + buffer->impl.myStride += kinc_g4_vertex_data_size(structure->elements[i].data); + } + + int uploadBufferSize = buffer->impl.myStride * buffer->impl.myCount; + + D3D12_HEAP_PROPERTIES heapProperties; + heapProperties.Type = D3D12_HEAP_TYPE_UPLOAD; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProperties.CreationNodeMask = 1; + heapProperties.VisibleNodeMask = 1; + + D3D12_RESOURCE_DESC resourceDesc; + resourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + resourceDesc.Alignment = 0; + resourceDesc.Width = uploadBufferSize; + resourceDesc.Height = 1; + resourceDesc.DepthOrArraySize = 1; + resourceDesc.MipLevels = 1; + resourceDesc.Format = DXGI_FORMAT_UNKNOWN; + resourceDesc.SampleDesc.Count = 1; + resourceDesc.SampleDesc.Quality = 0; + resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + + device->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + IID_GRAPHICS_PPV_ARGS(&buffer->impl.uploadBuffer)); + + // device_->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES (D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, + // &CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize), + // D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&vertexBuffer)); + + buffer->impl.view.BufferLocation = buffer->impl.uploadBuffer->GetGPUVirtualAddress(); + buffer->impl.view.SizeInBytes = uploadBufferSize; + buffer->impl.view.StrideInBytes = buffer->impl.myStride; + + buffer->impl.lastStart = 0; + buffer->impl.lastCount = kinc_g5_vertex_buffer_count(buffer); +} + +void kinc_g5_vertex_buffer_destroy(kinc_g5_vertex_buffer_t *buffer) { + // buffer->impl.vertexBuffer->Release(); + buffer->impl.uploadBuffer->Release(); +} + +float *kinc_g5_vertex_buffer_lock_all(kinc_g5_vertex_buffer_t *buffer) { + return kinc_g5_vertex_buffer_lock(buffer, 0, kinc_g5_vertex_buffer_count(buffer)); +} + +float *kinc_g5_vertex_buffer_lock(kinc_g5_vertex_buffer_t *buffer, int start, int count) { + buffer->impl.lastStart = start; + buffer->impl.lastCount = count; + + D3D12_RANGE range; + range.Begin = start * buffer->impl.myStride; + range.End = (start + count) * buffer->impl.myStride; + + void *p; + buffer->impl.uploadBuffer->Map(0, &range, &p); + byte *bytes = (byte *)p; + bytes += start * buffer->impl.myStride; + return (float *)bytes; +} + +void kinc_g5_vertex_buffer_unlock_all(kinc_g5_vertex_buffer_t *buffer) { + D3D12_RANGE range; + range.Begin = buffer->impl.lastStart * buffer->impl.myStride; + range.End = (buffer->impl.lastStart + buffer->impl.lastCount) * buffer->impl.myStride; + buffer->impl.uploadBuffer->Unmap(0, &range); + + // view.BufferLocation = uploadBuffer->GetGPUVirtualAddress() + myStart * myStride; + + // commandList->CopyBufferRegion(vertexBuffer, 0, uploadBuffer, 0, count() * stride()); + // CD3DX12_RESOURCE_BARRIER barriers[1] = { CD3DX12_RESOURCE_BARRIER::Transition(vertexBuffer, D3D12_RESOURCE_STATE_COPY_DEST, + // D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER) }; + // commandList->ResourceBarrier(1, barriers); +} + +void kinc_g5_vertex_buffer_unlock(kinc_g5_vertex_buffer_t *buffer, int count) { + D3D12_RANGE range; + range.Begin = buffer->impl.lastStart * buffer->impl.myStride; + range.End = (buffer->impl.lastStart + count) * buffer->impl.myStride; + buffer->impl.uploadBuffer->Unmap(0, &range); + + // view.BufferLocation = uploadBuffer->GetGPUVirtualAddress() + myStart * myStride; + + // commandList->CopyBufferRegion(vertexBuffer, 0, uploadBuffer, 0, count() * stride()); + // CD3DX12_RESOURCE_BARRIER barriers[1] = { CD3DX12_RESOURCE_BARRIER::Transition(vertexBuffer, D3D12_RESOURCE_STATE_COPY_DEST, + // D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER) }; + // commandList->ResourceBarrier(1, barriers); +} + +int kinc_g5_internal_vertex_buffer_set(kinc_g5_vertex_buffer_t *buffer, int offset) { + // UINT stride = myStride; + // UINT offset = 0; + // context->IASetVertexBuffers(0, 1, &vb, &stride, &offset); + _current_vertex_buffer = buffer; + return 0; +} + +int kinc_g5_vertex_buffer_count(kinc_g5_vertex_buffer_t *buffer) { + return buffer->impl.myCount; +} + +int kinc_g5_vertex_buffer_stride(kinc_g5_vertex_buffer_t *buffer) { + return buffer->impl.myStride; +} diff --git a/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/vertexbuffer.h b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/vertexbuffer.h new file mode 100644 index 000000000..f99191193 --- /dev/null +++ b/armorcore/sources/backends/direct3d12/kinc/backend/graphics5/vertexbuffer.h @@ -0,0 +1,30 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct ID3D12Resource; + +struct D3D12VertexBufferView { + __int64 BufferLocation; + unsigned int SizeInBytes; + unsigned int StrideInBytes; +}; + +typedef struct { + // ID3D12Resource* vertexBuffer; + struct ID3D12Resource *uploadBuffer; + struct D3D12VertexBufferView view; + + int myCount; + int myStride; + int lastStart; + int lastCount; + // float* vertices; + // static VertexBuffer5Impl* _current; +} VertexBuffer5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/G4.c.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/G4.c.h new file mode 100644 index 000000000..ecfc8d91f --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/G4.c.h @@ -0,0 +1,852 @@ +#include "G4.h" +#include "kinc/graphics4/graphics.h" +#include "kinc/window.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +kinc_g5_command_list_t commandList; + +uint64_t frameNumber = 0; +bool waitAfterNextDraw = false; + +static kinc_g5_constant_buffer_t vertexConstantBuffer; +static kinc_g5_constant_buffer_t fragmentConstantBuffer; +static kinc_g5_constant_buffer_t computeConstantBuffer; +#define constantBufferSize 4096 +#define constantBufferMultiply 100 +static int constantBufferIndex = 0; + +void kinc_g4_internal_init(void) { + kinc_g5_internal_init(); +} + +void kinc_g4_internal_destroy(void) { + kinc_g5_internal_destroy(); +} + +void kinc_g4_internal_destroy_window(int window) { + kinc_g5_internal_destroy_window(window); +} + +#define bufferCount 2 +#define renderTargetCount 8 + +static struct { + int currentBuffer; + kinc_g5_render_target_t framebuffers[bufferCount]; + + kinc_g4_render_target_t *current_render_targets[renderTargetCount]; + int current_render_target_count; + + bool resized; +} windows[16] = {0}; + +static int current_window; + +#define MAX_VERTEX_BUFFERS 16 +#define MAX_TEXTURES 16 + +typedef struct render_state { + kinc_g5_pipeline_t *pipeline; + kinc_g5_compute_shader *compute_shader; + + kinc_g5_index_buffer_t *index_buffer; + + kinc_g5_vertex_buffer_t *vertex_buffers[MAX_VERTEX_BUFFERS]; + int vertex_buffer_offsets[MAX_VERTEX_BUFFERS]; + int vertex_buffer_count; + + bool blend_constant_set; + float blend_constant_r; + float blend_constant_g; + float blend_constant_b; + float blend_constant_a; + + bool viewport_set; + int viewport_x; + int viewport_y; + int viewport_width; + int viewport_height; + + bool scissor_set; + int scissor_x; + int scissor_y; + int scissor_width; + int scissor_height; + + kinc_g5_texture_t *textures[MAX_TEXTURES]; + kinc_g5_texture_unit_t texture_units[MAX_TEXTURES]; + int texture_count; + + kinc_g5_render_target_t *render_targets[MAX_TEXTURES]; + kinc_g5_texture_unit_t render_target_units[MAX_TEXTURES]; + int render_target_count; + + kinc_g5_render_target_t *depth_render_targets[MAX_TEXTURES]; + kinc_g5_texture_unit_t depth_render_target_units[MAX_TEXTURES]; + int depth_render_target_count; + + uint8_t vertex_constant_data[constantBufferSize]; + uint8_t fragment_constant_data[constantBufferSize]; + uint8_t compute_constant_data[constantBufferSize]; +} render_state; + +static render_state current_state; + +void kinc_g4_on_g5_internal_resize(int window, int width, int height) { + windows[window].resized = true; +} + +void kinc_g4_on_g5_internal_restore_render_target(void) { + windows[current_window].current_render_targets[0] = NULL; + kinc_g5_render_target_t *render_target = &windows[current_window].framebuffers[windows[current_window].currentBuffer]; + kinc_g5_command_list_set_render_targets(&commandList, &render_target, 1); + windows[current_window].current_render_target_count = 1; +} + +void kinc_g4_internal_init_window(int window, int depthBufferBits, int stencilBufferBits, bool vsync) { + kinc_g5_internal_init_window(window, depthBufferBits, stencilBufferBits, vsync); + + kinc_g5_command_list_init(&commandList); + windows[window].currentBuffer = -1; + for (int i = 0; i < bufferCount; ++i) { + kinc_g5_render_target_init_framebuffer(&windows[window].framebuffers[i], kinc_window_width(window), kinc_window_height(window), + KINC_G5_RENDER_TARGET_FORMAT_32BIT, depthBufferBits, 0); + } + kinc_g5_constant_buffer_init(&vertexConstantBuffer, constantBufferSize * constantBufferMultiply); + kinc_g5_constant_buffer_init(&fragmentConstantBuffer, constantBufferSize * constantBufferMultiply); + kinc_g5_constant_buffer_init(&computeConstantBuffer, constantBufferSize * constantBufferMultiply); + + // to support doing work after kinc_g4_end and before kinc_g4_begin + kinc_g5_command_list_begin(&commandList); +} + +void kinc_g4_on_g5_internal_set_samplers(int count, kinc_g5_texture_unit_t *texture_units) { + for (int i = 0; i < count; ++i) { + for (int j = 0; j < KINC_G5_SHADER_TYPE_COUNT; ++j) { + if (texture_units[i].stages[j] >= 0) { + kinc_g5_sampler_t *sampler = get_current_sampler(j, texture_units[i].stages[j]); + kinc_g5_texture_unit_t unit; + for (int k = 0; k < KINC_G5_SHADER_TYPE_COUNT; ++k) { + unit.stages[k] = -1; + } + unit.stages[j] = texture_units[i].stages[j]; + kinc_g5_command_list_set_sampler(&commandList, unit, sampler); + } + } + } +} + +static void startDraw(bool compute) { + if ((constantBufferIndex + 1) >= constantBufferMultiply || waitAfterNextDraw) { + memcpy(current_state.vertex_constant_data, vertexConstantBuffer.data, constantBufferSize); + memcpy(current_state.fragment_constant_data, fragmentConstantBuffer.data, constantBufferSize); + memcpy(current_state.compute_constant_data, computeConstantBuffer.data, constantBufferSize); + } + kinc_g5_constant_buffer_unlock(&vertexConstantBuffer); + kinc_g5_constant_buffer_unlock(&fragmentConstantBuffer); + kinc_g5_constant_buffer_unlock(&computeConstantBuffer); + + kinc_g4_on_g5_internal_set_samplers(current_state.texture_count, current_state.texture_units); + kinc_g4_on_g5_internal_set_samplers(current_state.render_target_count, current_state.render_target_units); + kinc_g4_on_g5_internal_set_samplers(current_state.depth_render_target_count, current_state.depth_render_target_units); + + if (compute) { + kinc_g5_command_list_set_compute_constant_buffer(&commandList, &computeConstantBuffer, constantBufferIndex * constantBufferSize, constantBufferSize); + } + else { + kinc_g5_command_list_set_vertex_constant_buffer(&commandList, &vertexConstantBuffer, constantBufferIndex * constantBufferSize, constantBufferSize); + kinc_g5_command_list_set_fragment_constant_buffer(&commandList, &fragmentConstantBuffer, constantBufferIndex * constantBufferSize, constantBufferSize); + } +} + +static void endDraw(bool compute) { + ++constantBufferIndex; + if (constantBufferIndex >= constantBufferMultiply || waitAfterNextDraw) { + kinc_g5_command_list_end(&commandList); + kinc_g5_command_list_execute(&commandList); + kinc_g5_command_list_wait_for_execution_to_finish(&commandList); + kinc_g5_command_list_begin(&commandList); + if (windows[current_window].current_render_targets[0] == NULL) { + kinc_g4_on_g5_internal_restore_render_target(); + } + else { + const int count = windows[current_window].current_render_target_count; + kinc_g5_render_target_t *render_targets[16]; + for (int i = 0; i < count; ++i) { + render_targets[i] = &windows[current_window].current_render_targets[i]->impl._renderTarget; + } + kinc_g5_command_list_set_render_targets(&commandList, render_targets, count); + } + + if (current_state.pipeline != NULL) { + kinc_g5_command_list_set_pipeline(&commandList, current_state.pipeline); + } + if (current_state.compute_shader != NULL) { +#ifndef KINC_METAL + // Metal still has some trouble switching between graphics and compute encoders + kinc_g5_command_list_set_compute_shader(&commandList, current_state.compute_shader); +#endif + } + if (current_state.index_buffer != NULL) { + kinc_g5_command_list_set_index_buffer(&commandList, current_state.index_buffer); + } + if (current_state.vertex_buffer_count > 0) { + kinc_g5_command_list_set_vertex_buffers(&commandList, current_state.vertex_buffers, current_state.vertex_buffer_offsets, + current_state.vertex_buffer_count); + } + if (current_state.blend_constant_set) { + kinc_g5_command_list_set_blend_constant(&commandList, current_state.blend_constant_r, current_state.blend_constant_g, + current_state.blend_constant_b, current_state.blend_constant_a); + } + if (current_state.viewport_set) { + kinc_g5_command_list_viewport(&commandList, current_state.viewport_x, current_state.viewport_y, current_state.viewport_width, + current_state.viewport_height); + } + if (current_state.scissor_set) { + kinc_g5_command_list_scissor(&commandList, current_state.scissor_x, current_state.scissor_y, current_state.scissor_width, + current_state.scissor_height); + } + for (int i = 0; i < current_state.texture_count; ++i) { + kinc_g5_command_list_set_texture(&commandList, current_state.texture_units[i], current_state.textures[i]); + } + for (int i = 0; i < current_state.render_target_count; ++i) { + kinc_g5_command_list_set_texture_from_render_target(&commandList, current_state.render_target_units[i], current_state.render_targets[i]); + } + for (int i = 0; i < current_state.depth_render_target_count; ++i) { + kinc_g5_command_list_set_texture_from_render_target_depth(&commandList, current_state.depth_render_target_units[i], + current_state.depth_render_targets[i]); + } + constantBufferIndex = 0; + waitAfterNextDraw = false; + + kinc_g5_constant_buffer_lock(&vertexConstantBuffer, 0, constantBufferSize); + kinc_g5_constant_buffer_lock(&fragmentConstantBuffer, 0, constantBufferSize); + kinc_g5_constant_buffer_lock(&computeConstantBuffer, 0, constantBufferSize); + + memcpy(vertexConstantBuffer.data, current_state.vertex_constant_data, constantBufferSize); + memcpy(fragmentConstantBuffer.data, current_state.fragment_constant_data, constantBufferSize); + memcpy(computeConstantBuffer.data, current_state.compute_constant_data, constantBufferSize); + } + else { + kinc_g5_constant_buffer_lock(&vertexConstantBuffer, constantBufferIndex * constantBufferSize, constantBufferSize); + kinc_g5_constant_buffer_lock(&fragmentConstantBuffer, constantBufferIndex * constantBufferSize, constantBufferSize); + kinc_g5_constant_buffer_lock(&computeConstantBuffer, constantBufferIndex * constantBufferSize, constantBufferSize); + } +} + +void kinc_g4_draw_indexed_vertices(void) { + startDraw(false); + kinc_g5_command_list_draw_indexed_vertices(&commandList); + endDraw(false); +} + +void kinc_g4_draw_indexed_vertices_from_to(int start, int count) { + startDraw(false); + kinc_g5_command_list_draw_indexed_vertices_from_to(&commandList, start, count); + endDraw(false); +} + +void kinc_g4_draw_indexed_vertices_from_to_from(int start, int count, int vertex_offset) { + startDraw(false); + kinc_g5_command_list_draw_indexed_vertices_from_to_from(&commandList, start, count, vertex_offset); + endDraw(false); +} + +void kinc_g4_draw_indexed_vertices_instanced(int instanceCount) { + startDraw(false); + kinc_g5_command_list_draw_indexed_vertices_instanced(&commandList, instanceCount); + endDraw(false); +} + +void kinc_g4_draw_indexed_vertices_instanced_from_to(int instanceCount, int start, int count) { + startDraw(false); + kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(&commandList, instanceCount, start, count); + endDraw(false); +} + +void kinc_g4_clear(unsigned flags, unsigned color, float depth, int stencil) { + if (windows[current_window].current_render_target_count > 0) { + if (windows[current_window].current_render_targets[0] == NULL) { + kinc_g5_command_list_clear(&commandList, &windows[current_window].framebuffers[windows[current_window].currentBuffer], flags, color, depth, + stencil); + } + else { + if (windows[current_window].current_render_targets[0]->impl.state != KINC_INTERNAL_RENDER_TARGET_STATE_RENDER_TARGET) { + kinc_g5_command_list_texture_to_render_target_barrier(&commandList, &windows[current_window].current_render_targets[0]->impl._renderTarget); + windows[current_window].current_render_targets[0]->impl.state = KINC_INTERNAL_RENDER_TARGET_STATE_RENDER_TARGET; + } + kinc_g5_command_list_clear(&commandList, &windows[current_window].current_render_targets[0]->impl._renderTarget, flags, color, depth, stencil); + } + } +} + +bool first_run = true; + +void kinc_g4_begin(int window) { + // to support doing work after kinc_g4_end and before kinc_g4_begin + kinc_g5_command_list_end(&commandList); + kinc_g5_command_list_execute(&commandList); + + current_window = window; + + windows[current_window].currentBuffer = (windows[current_window].currentBuffer + 1) % bufferCount; + + bool resized = windows[window].resized; + if (resized) { + for (int i = 0; i < bufferCount; ++i) { + kinc_g5_render_target_destroy(&windows[current_window].framebuffers[i]); + } + windows[current_window].currentBuffer = 0; + } + + kinc_g5_begin(&windows[current_window].framebuffers[windows[current_window].currentBuffer], window); + + if (resized) { + for (int i = 0; i < bufferCount; ++i) { + kinc_g5_render_target_init_framebuffer(&windows[current_window].framebuffers[i], kinc_window_width(window), kinc_window_height(window), + KINC_G5_RENDER_TARGET_FORMAT_32BIT, 16, 0); + } + windows[window].resized = false; + } + + windows[current_window].current_render_targets[0] = NULL; + windows[current_window].current_render_target_count = 1; + + current_state.pipeline = NULL; + current_state.compute_shader = NULL; + current_state.index_buffer = NULL; + for (int i = 0; i < MAX_VERTEX_BUFFERS; ++i) { + current_state.vertex_buffers[i] = NULL; + current_state.vertex_buffer_offsets[i] = 0; + } + current_state.vertex_buffer_count = 0; + current_state.blend_constant_set = false; + current_state.viewport_set = false; + current_state.scissor_set = false; + current_state.texture_count = 0; + current_state.render_target_count = 0; + current_state.depth_render_target_count = 0; + + samplers_reset(); + + // commandList = new Graphics5::CommandList; + kinc_g5_command_list_begin(&commandList); + + // Currently we do not necessarily wait at the end of a frame so for now it's endDraw + // constantBufferIndex = 0; + // kinc_g5_constant_buffer_lock(&vertexConstantBuffer, 0, constantBufferSize); + // kinc_g5_constant_buffer_lock(&fragmentConstantBuffer, 0, constantBufferSize); + endDraw(false); + + kinc_g5_command_list_framebuffer_to_render_target_barrier(&commandList, &windows[current_window].framebuffers[windows[current_window].currentBuffer]); + kinc_g4_restore_render_target(); + + ++frameNumber; +} + +void kinc_g4_viewport(int x, int y, int width, int height) { + current_state.viewport_x = x; + current_state.viewport_y = y; + current_state.viewport_width = width; + current_state.viewport_height = height; + current_state.viewport_set = true; + kinc_g5_command_list_viewport(&commandList, x, y, width, height); +} + +void kinc_g4_scissor(int x, int y, int width, int height) { + current_state.scissor_x = x; + current_state.scissor_y = y; + current_state.scissor_width = width; + current_state.scissor_height = height; + current_state.scissor_set = true; + kinc_g5_command_list_scissor(&commandList, x, y, width, height); +} + +void kinc_g4_disable_scissor(void) { + current_state.scissor_set = false; + kinc_g5_command_list_disable_scissor(&commandList); +} + +void kinc_g4_end(int window) { + kinc_g5_constant_buffer_unlock(&vertexConstantBuffer); + kinc_g5_constant_buffer_unlock(&fragmentConstantBuffer); + kinc_g5_constant_buffer_unlock(&computeConstantBuffer); + + kinc_g5_command_list_render_target_to_framebuffer_barrier(&commandList, &windows[current_window].framebuffers[windows[current_window].currentBuffer]); + kinc_g5_command_list_end(&commandList); + kinc_g5_command_list_execute(&commandList); + + // delete commandList; + // commandList = nullptr; + kinc_g5_end(window); + + // to support doing work after kinc_g4_end and before kinc_g4_begin + kinc_g5_command_list_begin(&commandList); +} + +/*void Graphics4::_changeFramebuffer(int window, Kore::FramebufferOptions* frame) { + +}*/ + +bool kinc_g4_swap_buffers(void) { + return kinc_g5_swap_buffers(); +} + +void kinc_g4_flush(void) { + kinc_g5_flush(); +} + +void kinc_g4_set_stencil_reference_value(int value) {} + +void kinc_g4_set_int(kinc_g4_constant_location_t location, int value) { + if (location.impl._location.impl.vertexOffset >= 0) + kinc_g5_constant_buffer_set_int(&vertexConstantBuffer, location.impl._location.impl.vertexOffset, value); + if (location.impl._location.impl.fragmentOffset >= 0) + kinc_g5_constant_buffer_set_int(&fragmentConstantBuffer, location.impl._location.impl.fragmentOffset, value); + if (location.impl._location.impl.computeOffset >= 0) + kinc_g5_constant_buffer_set_int(&computeConstantBuffer, location.impl._location.impl.computeOffset, value); +} + +void kinc_g4_set_int2(kinc_g4_constant_location_t location, int value1, int value2) { + if (location.impl._location.impl.vertexOffset >= 0) + kinc_g5_constant_buffer_set_int2(&vertexConstantBuffer, location.impl._location.impl.vertexOffset, value1, value2); + if (location.impl._location.impl.fragmentOffset >= 0) + kinc_g5_constant_buffer_set_int2(&fragmentConstantBuffer, location.impl._location.impl.fragmentOffset, value1, value2); + if (location.impl._location.impl.computeOffset >= 0) + kinc_g5_constant_buffer_set_int2(&computeConstantBuffer, location.impl._location.impl.computeOffset, value1, value2); +} + +void kinc_g4_set_int3(kinc_g4_constant_location_t location, int value1, int value2, int value3) { + if (location.impl._location.impl.vertexOffset >= 0) + kinc_g5_constant_buffer_set_int3(&vertexConstantBuffer, location.impl._location.impl.vertexOffset, value1, value2, value3); + if (location.impl._location.impl.fragmentOffset >= 0) + kinc_g5_constant_buffer_set_int3(&fragmentConstantBuffer, location.impl._location.impl.fragmentOffset, value1, value2, value3); + if (location.impl._location.impl.computeOffset >= 0) + kinc_g5_constant_buffer_set_int3(&computeConstantBuffer, location.impl._location.impl.computeOffset, value1, value2, value3); +} + +void kinc_g4_set_int4(kinc_g4_constant_location_t location, int value1, int value2, int value3, int value4) { + if (location.impl._location.impl.vertexOffset >= 0) + kinc_g5_constant_buffer_set_int4(&vertexConstantBuffer, location.impl._location.impl.vertexOffset, value1, value2, value3, value4); + if (location.impl._location.impl.fragmentOffset >= 0) + kinc_g5_constant_buffer_set_int4(&fragmentConstantBuffer, location.impl._location.impl.fragmentOffset, value1, value2, value3, value4); + if (location.impl._location.impl.computeOffset >= 0) + kinc_g5_constant_buffer_set_int4(&computeConstantBuffer, location.impl._location.impl.computeOffset, value1, value2, value3, value4); +} + +void kinc_g4_set_ints(kinc_g4_constant_location_t location, int *values, int count) { + if (location.impl._location.impl.vertexOffset >= 0) + kinc_g5_constant_buffer_set_ints(&vertexConstantBuffer, location.impl._location.impl.vertexOffset, values, count); + if (location.impl._location.impl.fragmentOffset >= 0) + kinc_g5_constant_buffer_set_ints(&fragmentConstantBuffer, location.impl._location.impl.fragmentOffset, values, count); + if (location.impl._location.impl.computeOffset >= 0) + kinc_g5_constant_buffer_set_ints(&computeConstantBuffer, location.impl._location.impl.computeOffset, values, count); +} + +void kinc_g4_set_float(kinc_g4_constant_location_t location, float value) { + if (location.impl._location.impl.vertexOffset >= 0) + kinc_g5_constant_buffer_set_float(&vertexConstantBuffer, location.impl._location.impl.vertexOffset, value); + if (location.impl._location.impl.fragmentOffset >= 0) + kinc_g5_constant_buffer_set_float(&fragmentConstantBuffer, location.impl._location.impl.fragmentOffset, value); + if (location.impl._location.impl.computeOffset >= 0) + kinc_g5_constant_buffer_set_float(&computeConstantBuffer, location.impl._location.impl.computeOffset, value); +} + +void kinc_g4_set_float2(kinc_g4_constant_location_t location, float value1, float value2) { + if (location.impl._location.impl.vertexOffset >= 0) + kinc_g5_constant_buffer_set_float2(&vertexConstantBuffer, location.impl._location.impl.vertexOffset, value1, value2); + if (location.impl._location.impl.fragmentOffset >= 0) + kinc_g5_constant_buffer_set_float2(&fragmentConstantBuffer, location.impl._location.impl.fragmentOffset, value1, value2); + if (location.impl._location.impl.computeOffset >= 0) + kinc_g5_constant_buffer_set_float2(&computeConstantBuffer, location.impl._location.impl.computeOffset, value1, value2); +} + +void kinc_g4_set_float3(kinc_g4_constant_location_t location, float value1, float value2, float value3) { + if (location.impl._location.impl.vertexOffset >= 0) + kinc_g5_constant_buffer_set_float3(&vertexConstantBuffer, location.impl._location.impl.vertexOffset, value1, value2, value3); + if (location.impl._location.impl.fragmentOffset >= 0) + kinc_g5_constant_buffer_set_float3(&fragmentConstantBuffer, location.impl._location.impl.fragmentOffset, value1, value2, value3); + if (location.impl._location.impl.computeOffset >= 0) + kinc_g5_constant_buffer_set_float3(&computeConstantBuffer, location.impl._location.impl.computeOffset, value1, value2, value3); +} + +void kinc_g4_set_float4(kinc_g4_constant_location_t location, float value1, float value2, float value3, float value4) { + if (location.impl._location.impl.vertexOffset >= 0) + kinc_g5_constant_buffer_set_float4(&vertexConstantBuffer, location.impl._location.impl.vertexOffset, value1, value2, value3, value4); + if (location.impl._location.impl.fragmentOffset >= 0) + kinc_g5_constant_buffer_set_float4(&fragmentConstantBuffer, location.impl._location.impl.fragmentOffset, value1, value2, value3, value4); + if (location.impl._location.impl.computeOffset >= 0) + kinc_g5_constant_buffer_set_float4(&computeConstantBuffer, location.impl._location.impl.computeOffset, value1, value2, value3, value4); +} + +void kinc_g4_set_floats(kinc_g4_constant_location_t location, float *values, int count) { + if (location.impl._location.impl.vertexOffset >= 0) + kinc_g5_constant_buffer_set_floats(&vertexConstantBuffer, location.impl._location.impl.vertexOffset, values, count); + if (location.impl._location.impl.fragmentOffset >= 0) + kinc_g5_constant_buffer_set_floats(&fragmentConstantBuffer, location.impl._location.impl.fragmentOffset, values, count); + if (location.impl._location.impl.computeOffset >= 0) + kinc_g5_constant_buffer_set_floats(&computeConstantBuffer, location.impl._location.impl.computeOffset, values, count); +} + +void kinc_g4_set_bool(kinc_g4_constant_location_t location, bool value) { + if (location.impl._location.impl.vertexOffset >= 0) + kinc_g5_constant_buffer_set_bool(&vertexConstantBuffer, location.impl._location.impl.vertexOffset, value); + if (location.impl._location.impl.fragmentOffset >= 0) + kinc_g5_constant_buffer_set_bool(&fragmentConstantBuffer, location.impl._location.impl.fragmentOffset, value); + if (location.impl._location.impl.computeOffset >= 0) + kinc_g5_constant_buffer_set_bool(&computeConstantBuffer, location.impl._location.impl.computeOffset, value); +} + +void kinc_g4_set_matrix4(kinc_g4_constant_location_t location, kinc_matrix4x4_t *value) { + if (location.impl._location.impl.vertexOffset >= 0) + kinc_g5_constant_buffer_set_matrix4(&vertexConstantBuffer, location.impl._location.impl.vertexOffset, value); + if (location.impl._location.impl.fragmentOffset >= 0) + kinc_g5_constant_buffer_set_matrix4(&fragmentConstantBuffer, location.impl._location.impl.fragmentOffset, value); + if (location.impl._location.impl.computeOffset >= 0) + kinc_g5_constant_buffer_set_matrix4(&computeConstantBuffer, location.impl._location.impl.computeOffset, value); +} + +void kinc_g4_set_matrix3(kinc_g4_constant_location_t location, kinc_matrix3x3_t *value) { + if (location.impl._location.impl.vertexOffset >= 0) + kinc_g5_constant_buffer_set_matrix3(&vertexConstantBuffer, location.impl._location.impl.vertexOffset, value); + if (location.impl._location.impl.fragmentOffset >= 0) + kinc_g5_constant_buffer_set_matrix3(&fragmentConstantBuffer, location.impl._location.impl.fragmentOffset, value); + if (location.impl._location.impl.computeOffset >= 0) + kinc_g5_constant_buffer_set_matrix3(&computeConstantBuffer, location.impl._location.impl.computeOffset, value); +} + +void kinc_g4_set_texture_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) { + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + if (unit.stages[i] >= 0) { + if (dir == KINC_G4_TEXTURE_DIRECTION_U) { + sampler_options[i][unit.stages[i]].u_addressing = (kinc_g5_texture_addressing_t)addressing; + } + if (dir == KINC_G4_TEXTURE_DIRECTION_V) { + sampler_options[i][unit.stages[i]].v_addressing = (kinc_g5_texture_addressing_t)addressing; + } + if (dir == KINC_G4_TEXTURE_DIRECTION_W) { + sampler_options[i][unit.stages[i]].w_addressing = (kinc_g5_texture_addressing_t)addressing; + } + } + } +} + +void kinc_g4_set_texture3d_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) { + kinc_g4_set_texture_addressing(unit, dir, addressing); +} + +void kinc_g4_set_texture_magnification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) { + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + if (texunit.stages[i] >= 0) { + sampler_options[i][texunit.stages[i]].magnification_filter = (kinc_g5_texture_filter_t)filter; + } + } +} + +void kinc_g4_set_texture3d_magnification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) { + kinc_g4_set_texture_magnification_filter(texunit, filter); +} + +void kinc_g4_set_texture_minification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) { + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + if (texunit.stages[i] >= 0) { + sampler_options[i][texunit.stages[i]].minification_filter = (kinc_g5_texture_filter_t)filter; + } + } +} + +void kinc_g4_set_texture3d_minification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) { + kinc_g4_set_texture_minification_filter(texunit, filter); +} + +void kinc_g4_set_texture_mipmap_filter(kinc_g4_texture_unit_t texunit, kinc_g4_mipmap_filter_t filter) { + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + if (texunit.stages[i] >= 0) { + sampler_options[i][texunit.stages[i]].mipmap_filter = (kinc_g5_mipmap_filter_t)filter; + } + } +} + +void kinc_g4_set_texture3d_mipmap_filter(kinc_g4_texture_unit_t texunit, kinc_g4_mipmap_filter_t filter) { + kinc_g4_set_texture_mipmap_filter(texunit, filter); +} + +void kinc_g4_set_texture_compare_mode(kinc_g4_texture_unit_t unit, bool enabled) { + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + if (unit.stages[i] >= 0) { + sampler_options[i][unit.stages[i]].is_comparison = enabled; + } + } +} + +void kinc_g4_set_texture_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode) { + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + if (unit.stages[i] >= 0) { + sampler_options[i][unit.stages[i]].compare_mode = (kinc_g5_compare_mode_t)mode; + } + } +} + +void kinc_g4_set_cubemap_compare_mode(kinc_g4_texture_unit_t unit, bool enabled) {} +void kinc_g4_set_cubemap_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode) {} + +void kinc_g4_set_texture_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy) { + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + if (unit.stages[i] >= 0) { + sampler_options[i][unit.stages[i]].max_anisotropy = max_anisotropy; + } + } +} + +void kinc_g4_set_cubemap_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy) {} + +void kinc_g4_set_texture_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp) { + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + if (unit.stages[i] >= 0) { + sampler_options[i][unit.stages[i]].lod_min_clamp = lod_min_clamp; + sampler_options[i][unit.stages[i]].lod_max_clamp = lod_max_clamp; + } + } +} + +void kinc_g4_set_cubemap_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp) {} + +void kinc_g4_restore_render_target(void) { + kinc_g4_on_g5_internal_restore_render_target(); + current_state.viewport_set = false; + current_state.scissor_set = false; +} + +void kinc_g4_set_render_targets(kinc_g4_render_target_t **targets, int count) { + for (int i = 0; i < count; ++i) { + windows[current_window].current_render_targets[i] = targets[i]; + if (windows[current_window].current_render_targets[i]->impl.state != KINC_INTERNAL_RENDER_TARGET_STATE_RENDER_TARGET) { + kinc_g5_command_list_texture_to_render_target_barrier(&commandList, &windows[current_window].current_render_targets[i]->impl._renderTarget); + windows[current_window].current_render_targets[i]->impl.state = KINC_INTERNAL_RENDER_TARGET_STATE_RENDER_TARGET; + } + } + windows[current_window].current_render_target_count = count; + kinc_g5_render_target_t *render_targets[16]; + assert(count <= 16); + for (int i = 0; i < count; ++i) { + render_targets[i] = &targets[i]->impl._renderTarget; + } + kinc_g5_command_list_set_render_targets(&commandList, render_targets, count); + current_state.viewport_set = false; + current_state.scissor_set = false; +} + +void kinc_g4_set_render_target_face(kinc_g4_render_target_t *texture, int face) { + kinc_g5_command_list_set_render_target_face(&commandList, &texture->impl._renderTarget, face); + current_state.viewport_set = false; + current_state.scissor_set = false; +} + +void kinc_g4_set_vertex_buffers(kinc_g4_vertex_buffer_t **buffers, int count) { + assert(count <= MAX_VERTEX_BUFFERS); + for (int i = 0; i < count; ++i) { + current_state.vertex_buffers[i] = &buffers[i]->impl._buffer; + int index = buffers[i]->impl._currentIndex; + current_state.vertex_buffer_offsets[i] = index * kinc_g4_vertex_buffer_count(buffers[i]); + } + current_state.vertex_buffer_count = count; + kinc_g5_command_list_set_vertex_buffers(&commandList, current_state.vertex_buffers, current_state.vertex_buffer_offsets, count); +} + +int kinc_internal_g4_vertex_buffer_set(kinc_g4_vertex_buffer_t *buffer, int offset) { + kinc_g4_vertex_buffer_t *buffers[1]; + buffers[0] = buffer; + kinc_g4_set_vertex_buffers(buffers, 1); + return 0; +} + +void kinc_g4_set_index_buffer(kinc_g4_index_buffer_t *buffer) { + kinc_g5_index_buffer_t *g5_index_buffer = &buffer->impl._buffer; + current_state.index_buffer = g5_index_buffer; + kinc_g5_command_list_set_index_buffer(&commandList, g5_index_buffer); +} + +void kinc_g4_set_texture(kinc_g4_texture_unit_t unit, kinc_g4_texture_t *texture) { + if (!texture->impl._uploaded) { + kinc_g5_command_list_upload_texture(&commandList, &texture->impl._texture); + texture->impl._uploaded = true; + } + + assert(KINC_G4_SHADER_TYPE_COUNT == KINC_G5_SHADER_TYPE_COUNT); + kinc_g5_texture_unit_t g5_unit; + memcpy(&g5_unit.stages[0], &unit.stages[0], KINC_G5_SHADER_TYPE_COUNT * sizeof(int)); + + bool found = false; + for (int i = 0; i < current_state.texture_count; ++i) { + if (kinc_g5_texture_unit_equals(¤t_state.texture_units[i], &g5_unit)) { + current_state.textures[i] = &texture->impl._texture; + current_state.texture_units[i] = g5_unit; + found = true; + break; + } + } + if (!found) { + assert(current_state.texture_count < MAX_TEXTURES); + current_state.textures[current_state.texture_count] = &texture->impl._texture; + current_state.texture_units[current_state.texture_count] = g5_unit; + current_state.texture_count += 1; + } + + kinc_g5_command_list_set_texture(&commandList, g5_unit, &texture->impl._texture); +} + +void kinc_g4_set_image_texture(kinc_g4_texture_unit_t unit, kinc_g4_texture_t *texture) { + assert(KINC_G4_SHADER_TYPE_COUNT == KINC_G5_SHADER_TYPE_COUNT); + kinc_g5_texture_unit_t g5_unit; + memcpy(&g5_unit.stages[0], &unit.stages[0], KINC_G5_SHADER_TYPE_COUNT * sizeof(int)); + + bool found = false; + for (int i = 0; i < current_state.texture_count; ++i) { + if (kinc_g5_texture_unit_equals(¤t_state.texture_units[i], &g5_unit)) { + current_state.textures[i] = &texture->impl._texture; + current_state.texture_units[i] = g5_unit; + found = true; + break; + } + } + if (!found) { + assert(current_state.texture_count < MAX_TEXTURES); + current_state.textures[current_state.texture_count] = &texture->impl._texture; + current_state.texture_units[current_state.texture_count] = g5_unit; + current_state.texture_count += 1; + } + + kinc_g5_command_list_set_image_texture(&commandList, g5_unit, &texture->impl._texture); +} + +int kinc_g4_max_bound_textures(void) { + return kinc_g5_max_bound_textures(); +} + +void kinc_g4_set_pipeline(kinc_g4_pipeline_t *pipeline) { + kinc_g5_pipeline_t *g5_pipeline = &pipeline->impl._pipeline; + current_state.pipeline = g5_pipeline; + kinc_g5_command_list_set_pipeline(&commandList, g5_pipeline); +} + +void kinc_g4_set_blend_constant(float r, float g, float b, float a) { + current_state.blend_constant_set = true; + current_state.blend_constant_r = r; + current_state.blend_constant_g = g; + current_state.blend_constant_b = b; + current_state.blend_constant_a = a; + kinc_g5_command_list_set_blend_constant(&commandList, r, g, b, a); +} + +bool kinc_g4_supports_instanced_rendering(void) { + return kinc_g5_supports_instanced_rendering(); +} + +bool kinc_g4_supports_compute_shaders(void) { + return kinc_g5_supports_compute_shaders(); +} + +bool kinc_g4_supports_blend_constants(void) { + return kinc_g5_supports_blend_constants(); +} + +bool kinc_g4_supports_non_pow2_textures(void) { + return kinc_g5_supports_non_pow2_textures(); +} + +bool kinc_g4_render_targets_inverted_y(void) { + return kinc_g5_render_targets_inverted_y(); +} + +void kinc_g4_render_target_use_color_as_texture(kinc_g4_render_target_t *render_target, kinc_g4_texture_unit_t unit) { + if (render_target->impl.state != KINC_INTERNAL_RENDER_TARGET_STATE_TEXTURE) { + kinc_g5_command_list_render_target_to_texture_barrier(&commandList, &render_target->impl._renderTarget); + render_target->impl.state = KINC_INTERNAL_RENDER_TARGET_STATE_TEXTURE; + } + + assert(KINC_G4_SHADER_TYPE_COUNT == KINC_G5_SHADER_TYPE_COUNT); + kinc_g5_texture_unit_t g5_unit; + memcpy(&g5_unit.stages[0], &unit.stages[0], KINC_G5_SHADER_TYPE_COUNT * sizeof(int)); + + bool found = false; + for (int i = 0; i < current_state.render_target_count; ++i) { + if (kinc_g5_texture_unit_equals(¤t_state.render_target_units[i], &g5_unit)) { + current_state.render_targets[i] = &render_target->impl._renderTarget; + current_state.render_target_units[i] = g5_unit; + found = true; + break; + } + } + if (!found) { + assert(current_state.render_target_count < MAX_TEXTURES); + current_state.render_targets[current_state.render_target_count] = &render_target->impl._renderTarget; + current_state.render_target_units[current_state.render_target_count] = g5_unit; + current_state.render_target_count += 1; + } + + kinc_g5_command_list_set_texture_from_render_target(&commandList, g5_unit, &render_target->impl._renderTarget); +} + +void kinc_g4_render_target_use_depth_as_texture(kinc_g4_render_target_t *render_target, kinc_g4_texture_unit_t unit) { + if (render_target->impl.state != KINC_INTERNAL_RENDER_TARGET_STATE_TEXTURE) { + kinc_g5_command_list_render_target_to_texture_barrier(&commandList, &render_target->impl._renderTarget); + render_target->impl.state = KINC_INTERNAL_RENDER_TARGET_STATE_TEXTURE; + } + + assert(KINC_G4_SHADER_TYPE_COUNT == KINC_G5_SHADER_TYPE_COUNT); + kinc_g5_texture_unit_t g5_unit; + memcpy(&g5_unit.stages[0], &unit.stages[0], KINC_G5_SHADER_TYPE_COUNT * sizeof(int)); + + bool found = false; + for (int i = 0; i < current_state.depth_render_target_count; ++i) { + if (kinc_g5_texture_unit_equals(¤t_state.depth_render_target_units[i], &g5_unit)) { + current_state.depth_render_targets[i] = &render_target->impl._renderTarget; + current_state.depth_render_target_units[i] = g5_unit; + found = true; + break; + } + } + if (!found) { + assert(current_state.depth_render_target_count < MAX_TEXTURES); + current_state.depth_render_targets[current_state.depth_render_target_count] = &render_target->impl._renderTarget; + current_state.depth_render_target_units[current_state.depth_render_target_count] = g5_unit; + current_state.depth_render_target_count += 1; + } + + kinc_g5_command_list_set_texture_from_render_target_depth(&commandList, g5_unit, &render_target->impl._renderTarget); +} + +void kinc_g4_set_compute_shader(kinc_g4_compute_shader *shader) { + kinc_g5_compute_shader *g5_shader = &shader->impl.shader; + current_state.compute_shader = g5_shader; + kinc_g5_command_list_set_compute_shader(&commandList, g5_shader); +} + +void kinc_g4_compute(int x, int y, int z) { + startDraw(true); + kinc_g5_command_list_compute(&commandList, x, y, z); + endDraw(true); +} diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/G4.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/G4.h new file mode 100644 index 000000000..3cd03a891 --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/G4.h @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/compute.c.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/compute.c.h new file mode 100644 index 000000000..bbab09bd3 --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/compute.c.h @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +extern kinc_g5_command_list_t commandList; + +void kinc_g4_compute_shader_init(kinc_g4_compute_shader *shader, void *source, int length) { + kinc_g5_compute_shader_init(&shader->impl.shader, source, length); +} + +void kinc_g4_compute_shader_destroy(kinc_g4_compute_shader *shader) { + kinc_g5_compute_shader_destroy(&shader->impl.shader); +} + +kinc_g4_constant_location_t kinc_g4_compute_shader_get_constant_location(kinc_g4_compute_shader *shader, const char *name) { + kinc_g4_constant_location_t location; + location.impl._location = kinc_g5_compute_shader_get_constant_location(&shader->impl.shader, name); + return location; +} + +kinc_g4_texture_unit_t kinc_g4_compute_shader_get_texture_unit(kinc_g4_compute_shader *shader, const char *name) { + kinc_g5_texture_unit_t g5_unit = kinc_g5_compute_shader_get_texture_unit(&shader->impl.shader, name); + kinc_g4_texture_unit_t g4_unit; + for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) { + g4_unit.stages[i] = g5_unit.stages[i]; + } + return g4_unit; +} diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/compute.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/compute.h new file mode 100644 index 000000000..3911d0e4e --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/compute.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +typedef struct kinc_g4_compute_shader_impl { + kinc_g5_compute_shader shader; +} kinc_g4_compute_shader_impl; diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/g4ong5unit.c b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/g4ong5unit.c new file mode 100644 index 000000000..a1a72e27a --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/g4ong5unit.c @@ -0,0 +1,10 @@ +#include "samplers.c.h" + +#include "G4.c.h" +#include "compute.c.h" +#include "indexbuffer.c.h" +#include "pipeline.c.h" +#include "rendertarget.c.h" +#include "shader.c.h" +#include "texture.c.h" +#include "vertexbuffer.c.h" \ No newline at end of file diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/graphics.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/graphics.h new file mode 100644 index 000000000..53f1df391 --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/graphics.h @@ -0,0 +1,6 @@ +#pragma once + +#include "IndexBufferImpl.h" +#include "RenderTargetImpl.h" +#include "TextureImpl.h" +#include "VertexBufferImpl.h" diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/indexbuffer.c.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/indexbuffer.c.h new file mode 100644 index 000000000..a7c19f5c5 --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/indexbuffer.c.h @@ -0,0 +1,35 @@ +#include + +#include + +extern kinc_g5_command_list_t commandList; + +void kinc_g4_index_buffer_init(kinc_g4_index_buffer_t *buffer, int count, kinc_g4_index_buffer_format_t format, kinc_g4_usage_t usage) { + kinc_g5_index_buffer_init(&buffer->impl._buffer, count, (kinc_g5_index_buffer_format_t)format, usage == KINC_G4_USAGE_STATIC); +} + +void kinc_g4_index_buffer_destroy(kinc_g4_index_buffer_t *buffer) { + kinc_g5_index_buffer_destroy(&buffer->impl._buffer); +} + +void *kinc_g4_index_buffer_lock_all(kinc_g4_index_buffer_t *buffer) { + return kinc_g5_index_buffer_lock_all(&buffer->impl._buffer); +} + +void *kinc_g4_index_buffer_lock(kinc_g4_index_buffer_t *buffer, int start, int count) { + return kinc_g5_index_buffer_lock(&buffer->impl._buffer, start, count); +} + +void kinc_g4_index_buffer_unlock_all(kinc_g4_index_buffer_t *buffer) { + kinc_g5_index_buffer_unlock_all(&buffer->impl._buffer); + kinc_g5_command_list_upload_index_buffer(&commandList, &buffer->impl._buffer); +} + +void kinc_g4_index_buffer_unlock(kinc_g4_index_buffer_t *buffer, int count) { + kinc_g5_index_buffer_unlock(&buffer->impl._buffer, count); + kinc_g5_command_list_upload_index_buffer(&commandList, &buffer->impl._buffer); +} + +int kinc_g4_index_buffer_count(kinc_g4_index_buffer_t *buffer) { + return kinc_g5_index_buffer_count(&buffer->impl._buffer); +} diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/indexbuffer.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/indexbuffer.h new file mode 100644 index 000000000..02efa3199 --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/indexbuffer.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + kinc_g5_index_buffer_t _buffer; +} kinc_g4_index_buffer_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/pipeline.c.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/pipeline.c.h new file mode 100644 index 000000000..d73a6503f --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/pipeline.c.h @@ -0,0 +1,62 @@ +#include + +#include +#include +#include + +void kinc_g4_pipeline_init(kinc_g4_pipeline_t *pipe) { + kinc_g4_internal_pipeline_set_defaults(pipe); + kinc_g5_pipeline_init(&pipe->impl._pipeline); +} + +void kinc_g4_pipeline_destroy(kinc_g4_pipeline_t *pipe) { + kinc_g5_pipeline_destroy(&pipe->impl._pipeline); +} + +kinc_g4_constant_location_t kinc_g4_pipeline_get_constant_location(kinc_g4_pipeline_t *pipe, const char *name) { + kinc_g4_constant_location_t location; + location.impl._location = kinc_g5_pipeline_get_constant_location(&pipe->impl._pipeline, name); + return location; +} + +kinc_g4_texture_unit_t kinc_g4_pipeline_get_texture_unit(kinc_g4_pipeline_t *pipe, const char *name) { + kinc_g5_texture_unit_t g5_unit = kinc_g5_pipeline_get_texture_unit(&pipe->impl._pipeline, name); + + assert(KINC_G4_SHADER_TYPE_COUNT == KINC_G5_SHADER_TYPE_COUNT); + kinc_g4_texture_unit_t g4_unit; + memcpy(&g4_unit.stages[0], &g5_unit.stages[0], KINC_G4_SHADER_TYPE_COUNT * sizeof(int)); + + return g4_unit; +} + +void kinc_g4_pipeline_compile(kinc_g4_pipeline_t *pipe) { + for (int i = 0; i < 16; ++i) { + pipe->impl._pipeline.inputLayout[i] = pipe->input_layout[i]; + } + pipe->impl._pipeline.vertexShader = &pipe->vertex_shader->impl._shader; + pipe->impl._pipeline.fragmentShader = &pipe->fragment_shader->impl._shader; + pipe->impl._pipeline.geometryShader = pipe->geometry_shader != NULL ? &pipe->geometry_shader->impl._shader : NULL; + pipe->impl._pipeline.tessellationControlShader = pipe->tessellation_control_shader != NULL ? &pipe->tessellation_control_shader->impl._shader : NULL; + pipe->impl._pipeline.tessellationEvaluationShader = + pipe->tessellation_evaluation_shader != NULL ? &pipe->tessellation_evaluation_shader->impl._shader : NULL; + pipe->impl._pipeline.blend_source = (kinc_g5_blending_factor_t)pipe->blend_source; + pipe->impl._pipeline.blend_destination = (kinc_g5_blending_factor_t)pipe->blend_destination; + pipe->impl._pipeline.blend_operation = (kinc_g5_blending_operation_t)pipe->blend_operation; + pipe->impl._pipeline.alpha_blend_source = (kinc_g5_blending_factor_t)pipe->alpha_blend_source; + pipe->impl._pipeline.alpha_blend_destination = (kinc_g5_blending_factor_t)pipe->alpha_blend_destination; + pipe->impl._pipeline.alpha_blend_operation = (kinc_g5_blending_operation_t)pipe->alpha_blend_operation; + pipe->impl._pipeline.cullMode = (kinc_g5_cull_mode_t)pipe->cull_mode; + pipe->impl._pipeline.depthMode = (kinc_g5_compare_mode_t)pipe->depth_mode; + pipe->impl._pipeline.depthWrite = pipe->depth_write; + pipe->impl._pipeline.colorAttachmentCount = pipe->color_attachment_count; + for (int i = 0; i < 8; ++i) { + pipe->impl._pipeline.colorWriteMaskRed[i] = pipe->color_write_mask_red[i]; + pipe->impl._pipeline.colorWriteMaskGreen[i] = pipe->color_write_mask_green[i]; + pipe->impl._pipeline.colorWriteMaskBlue[i] = pipe->color_write_mask_blue[i]; + pipe->impl._pipeline.colorWriteMaskAlpha[i] = pipe->color_write_mask_alpha[i]; + pipe->impl._pipeline.colorAttachment[i] = (kinc_g5_render_target_format_t)pipe->color_attachment[i]; + } + pipe->impl._pipeline.depthAttachmentBits = pipe->depth_attachment_bits; + pipe->impl._pipeline.stencilAttachmentBits = pipe->stencil_attachment_bits; + kinc_g5_pipeline_compile(&pipe->impl._pipeline); +} diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/pipeline.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/pipeline.h new file mode 100644 index 000000000..bfac31416 --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/pipeline.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + // PipelineStateImpl(); + kinc_g5_pipeline_t _pipeline; +} kinc_g4_pipeline_impl_t; + +typedef struct { + kinc_g5_constant_location_t _location; +} kinc_g4_constant_location_impl_t; + +typedef struct { + int nothing; +} Kinc_G4_AttributeLocationImpl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/rendertarget.c.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/rendertarget.c.h new file mode 100644 index 000000000..404e2bee2 --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/rendertarget.c.h @@ -0,0 +1,36 @@ +#include + +#include +#include +#include + +extern kinc_g5_command_list_t commandList; + +void kinc_g4_render_target_init_with_multisampling(kinc_g4_render_target_t *render_target, int width, int height, kinc_g4_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + kinc_g5_render_target_init_with_multisampling(&render_target->impl._renderTarget, width, height, (kinc_g5_render_target_format_t)format, depthBufferBits, + stencilBufferBits, samples_per_pixel); + render_target->texWidth = render_target->width = width; + render_target->texHeight = render_target->height = height; + render_target->impl.state = KINC_INTERNAL_RENDER_TARGET_STATE_RENDER_TARGET; +} + +void kinc_g4_render_target_init_cube_with_multisampling(kinc_g4_render_target_t *render_target, int cubeMapSize, kinc_g4_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + kinc_g5_render_target_init_cube_with_multisampling(&render_target->impl._renderTarget, cubeMapSize, (kinc_g5_render_target_format_t)format, depthBufferBits, + stencilBufferBits, samples_per_pixel); +} + +void kinc_g4_render_target_destroy(kinc_g4_render_target_t *render_target) { + kinc_g5_render_target_destroy(&render_target->impl._renderTarget); +} + +void kinc_g4_render_target_set_depth_stencil_from(kinc_g4_render_target_t *render_target, kinc_g4_render_target_t *source) { + kinc_g5_render_target_set_depth_stencil_from(&render_target->impl._renderTarget, &source->impl._renderTarget); +} + +void kinc_g4_render_target_get_pixels(kinc_g4_render_target_t *render_target, uint8_t *data) { + kinc_g5_command_list_get_render_target_pixels(&commandList, &render_target->impl._renderTarget, data); +} + +void kinc_g4_render_target_generate_mipmaps(kinc_g4_render_target_t *render_target, int levels) {} diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/rendertarget.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/rendertarget.h new file mode 100644 index 000000000..4d951c40e --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/rendertarget.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum kinc_internal_render_target_state { KINC_INTERNAL_RENDER_TARGET_STATE_RENDER_TARGET, KINC_INTERNAL_RENDER_TARGET_STATE_TEXTURE }; + +typedef struct { + // RenderTargetImpl(int width, int height, int depthBufferBits, bool antialiasing, Graphics5::RenderTargetFormat format, int stencilBufferBits, + // int contextId); + // RenderTargetImpl(int cubeMapSize, int depthBufferBits, bool antialiasing, Graphics5::RenderTargetFormat format, int stencilBufferBits, int contextId); + kinc_g5_render_target_t _renderTarget; + enum kinc_internal_render_target_state state; +} kinc_g4_render_target_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/samplers.c.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/samplers.c.h new file mode 100644 index 000000000..aba829b90 --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/samplers.c.h @@ -0,0 +1,50 @@ +#include + +#include + +#define MAX_SAMPLERS_PER_STAGE 16 + +static kinc_g5_sampler_options_t sampler_options[KINC_G5_SHADER_TYPE_COUNT][MAX_SAMPLERS_PER_STAGE]; + +static void samplers_reset(void) { + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + for (int j = 0; j < MAX_SAMPLERS_PER_STAGE; ++j) { + kinc_g5_sampler_options_set_defaults(&sampler_options[i][j]); + } + } +} + +bool sampler_options_equals(kinc_g5_sampler_options_t *options1, kinc_g5_sampler_options_t *options2) { + return options1->u_addressing == options2->u_addressing && options1->v_addressing == options2->v_addressing && + options1->w_addressing == options2->w_addressing && options1->minification_filter == options2->minification_filter && + options1->magnification_filter == options2->magnification_filter && options1->mipmap_filter == options2->mipmap_filter && + options1->lod_min_clamp == options2->lod_min_clamp && options1->lod_max_clamp == options2->lod_max_clamp && + options1->max_anisotropy == options2->max_anisotropy && options1->is_comparison == options2->is_comparison && + options1->compare_mode == options2->compare_mode; +} + +struct sampler_cache_entry { + kinc_g5_sampler_options_t options; + kinc_g5_sampler_t sampler; +}; + +#define MAX_SAMPLER_CACHE_SIZE 256 +static struct sampler_cache_entry sampler_cache[MAX_SAMPLER_CACHE_SIZE]; +static int sampler_cache_size = 0; + +// TODO: Please make this much faster +static kinc_g5_sampler_t *get_current_sampler(int stage, int unit) { + for (int i = 0; i < sampler_cache_size; ++i) { + if (sampler_options_equals(&sampler_cache[i].options, &sampler_options[stage][unit])) { + return &sampler_cache[i].sampler; + } + } + + assert(sampler_cache_size < MAX_SAMPLER_CACHE_SIZE); + kinc_g5_sampler_t *sampler = &sampler_cache[sampler_cache_size].sampler; + kinc_g5_sampler_init(sampler, &sampler_options[stage][unit]); + sampler_cache[sampler_cache_size].options = sampler_options[stage][unit]; + sampler_cache_size += 1; + + return sampler; +} diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/shader.c.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/shader.c.h new file mode 100644 index 000000000..20d8969b3 --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/shader.c.h @@ -0,0 +1,49 @@ +#include + +void kinc_g4_shader_init(kinc_g4_shader_t *shader, const void *_data, size_t length, kinc_g4_shader_type_t type) { + kinc_g5_shader_init(&shader->impl._shader, _data, length, (kinc_g5_shader_type_t)type); +} + +#ifdef KRAFIX_LIBRARY +extern int krafix_compile(const char *source, char *output, int *length, const char *targetlang, const char *system, const char *shadertype, int version); +#endif + +int kinc_g4_shader_init_from_source(kinc_g4_shader_t *shader, const char *source, kinc_g4_shader_type_t type) { +#ifdef KRAFIX_LIBRARY + char *output = malloc(1024 * 1024); + int length; + +#ifdef KINC_WINDOWS + const char *system = "windows"; +#elif defined(KINC_MACOS) + const char *system = "macos"; +#elif defined(KINC_LINUX) + const char *system = "linux"; +#elif defined(KINC_ANDROID) + const char *system = "android"; +#elif defined(KINC_IOS) + const char *system = "ios"; +#endif + +#ifdef KINC_VULKAN + const char *target = "spirv"; +#elif defined(KINC_METAL) + const char *target = "metal"; +#else + const char *target = "d3d11"; +#endif + + int errors = krafix_compile(source, output, &length, target, system, type == KINC_G4_SHADER_TYPE_FRAGMENT ? "frag" : "vert", -1); + if (errors > 0) { + return errors; + } + kinc_g4_shader_init(shader, output, length, type); + return 0; +#else + return 0; +#endif +} + +void kinc_g4_shader_destroy(kinc_g4_shader_t *shader) { + kinc_g5_shader_destroy(&shader->impl._shader); +} diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/shader.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/shader.h new file mode 100644 index 000000000..ce79f4bdd --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/shader.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + // ShaderImpl(void *data, int length, Graphics5::ShaderType type); + kinc_g5_shader_t _shader; +} kinc_g4_shader_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/texture.c.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/texture.c.h new file mode 100644 index 000000000..3e762274d --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/texture.c.h @@ -0,0 +1,70 @@ +#include +#include +#include +#include + +#include + +void kinc_g4_texture_init_from_image(kinc_g4_texture_t *texture, kinc_image_t *image) { + texture->impl._uploaded = false; + kinc_g5_texture_init_from_image(&texture->impl._texture, image); + texture->tex_width = texture->impl._texture.texWidth; + texture->tex_height = texture->impl._texture.texHeight; + texture->tex_depth = 1; + texture->format = image->format; +} + +void kinc_g4_texture_init_from_image3d(kinc_g4_texture_t *texture, kinc_image_t *image) {} + +void kinc_g4_texture_init(kinc_g4_texture_t *texture, int width, int height, kinc_image_format_t format) { + texture->impl._uploaded = true; + kinc_g5_texture_init(&texture->impl._texture, width, height, format); + texture->tex_width = texture->impl._texture.texWidth; + texture->tex_height = texture->impl._texture.texHeight; + texture->tex_depth = 1; + texture->format = format; +} + +void kinc_g4_texture_init3d(kinc_g4_texture_t *texture, int width, int height, int depth, kinc_image_format_t format) {} + +void kinc_g4_texture_init_from_bytes(kinc_g4_texture_t *texture, void *data, int size, const char *format) {} + +void kinc_g4_texture_init_from_bytes3d(kinc_g4_texture_t *texture, void *data, int width, int height, int depth, int format, bool readable) {} + +void kinc_g4_texture_destroy(kinc_g4_texture_t *texture) { + // kinc_g4_internal_texture_unset(texture); + kinc_g5_texture_destroy(&texture->impl._texture); +} + +void kinc_g4_internal_texture_unset(kinc_g4_texture_t *texture) { + // TODO +} + +void kinc_g4_internal_texture_unmipmap(kinc_g4_texture_t *texture) { + // TODO +} + +uint8_t *kinc_g4_texture_lock(kinc_g4_texture_t *texture) { + return kinc_g5_texture_lock(&texture->impl._texture); +} + +void kinc_g4_texture_unlock(kinc_g4_texture_t *texture) { + kinc_g5_texture_unlock(&texture->impl._texture); + texture->impl._uploaded = false; +} + +void kinc_g4_texture_clear(kinc_g4_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color) { + kinc_g5_texture_clear(&texture->impl._texture, x, y, z, width, height, depth, color); +} + +int kinc_g4_texture_stride(kinc_g4_texture_t *texture) { + return kinc_g5_texture_stride(&texture->impl._texture); +} + +void kinc_g4_texture_generate_mipmaps(kinc_g4_texture_t *texture, int levels) { + kinc_g5_texture_generate_mipmaps(&texture->impl._texture, levels); +} + +void kinc_g4_texture_set_mipmap(kinc_g4_texture_t *texture, kinc_image_t *mipmap, int level) { + kinc_g5_texture_set_mipmap(&texture->impl._texture, mipmap, level); +} diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/texture.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/texture.h new file mode 100644 index 000000000..512a83472 --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/texture.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + kinc_g5_texture_unit_t _unit; +} kinc_g4_texture_unit_impl_t; + +typedef struct { + /*TextureImpl(); + TextureImpl(int width, int height, Image::Format format, bool readable); + TextureImpl(int width, int height, int depth, Image::Format format, bool readable); + ~TextureImpl(); + void unmipmap(); + void unset();*/ + + kinc_g5_texture_t _texture; + bool _uploaded; +} kinc_g4_texture_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/vertexbuffer.c.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/vertexbuffer.c.h new file mode 100644 index 000000000..023e982c0 --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/vertexbuffer.c.h @@ -0,0 +1,64 @@ +#include + +#include +#include + +extern uint64_t frameNumber; +extern bool waitAfterNextDraw; + +void kinc_g4_vertex_buffer_init(kinc_g4_vertex_buffer_t *buffer, int count, kinc_g4_vertex_structure_t *structure, kinc_g4_usage_t usage, + int instanceDataStepRate) { + int multiple = usage == KINC_G4_USAGE_STATIC ? 1 : 500; + kinc_g5_vertex_buffer_init(&buffer->impl._buffer, count * multiple, structure, usage == KINC_G4_USAGE_STATIC, instanceDataStepRate); + buffer->impl._multiple = multiple; + buffer->impl._lastFrameNumber = 0; + buffer->impl._currentIndex = 0; + buffer->impl.myCount = count; +} + +void kinc_g4_vertex_buffer_destroy(kinc_g4_vertex_buffer_t *buffer) { + kinc_g5_vertex_buffer_destroy(&buffer->impl._buffer); +} + +static void prepareLock(kinc_g4_vertex_buffer_t *buffer) { + /*if (frameNumber > _lastFrameNumber) { + _lastFrameNumber = frameNumber; + _currentIndex = 0; + } + else {*/ + ++buffer->impl._currentIndex; + if (buffer->impl._currentIndex >= buffer->impl._multiple - 1) { + waitAfterNextDraw = true; + } + if (buffer->impl._currentIndex >= buffer->impl._multiple) { + buffer->impl._currentIndex = 0; + } + //} +} + +float *kinc_g4_vertex_buffer_lock_all(kinc_g4_vertex_buffer_t *buffer) { + prepareLock(buffer); + return kinc_g5_vertex_buffer_lock(&buffer->impl._buffer, buffer->impl._currentIndex * kinc_g4_vertex_buffer_count(buffer), + kinc_g4_vertex_buffer_count(buffer)); +} + +float *kinc_g4_vertex_buffer_lock(kinc_g4_vertex_buffer_t *buffer, int start, int count) { + prepareLock(buffer); + return kinc_g5_vertex_buffer_lock(&buffer->impl._buffer, start + buffer->impl._currentIndex * kinc_g4_vertex_buffer_count(buffer), count); +} + +void kinc_g4_vertex_buffer_unlock_all(kinc_g4_vertex_buffer_t *buffer) { + kinc_g5_vertex_buffer_unlock_all(&buffer->impl._buffer); +} + +void kinc_g4_vertex_buffer_unlock(kinc_g4_vertex_buffer_t *buffer, int count) { + kinc_g5_vertex_buffer_unlock(&buffer->impl._buffer, count); +} + +int kinc_g4_vertex_buffer_count(kinc_g4_vertex_buffer_t *buffer) { + return buffer->impl.myCount; +} + +int kinc_g4_vertex_buffer_stride(kinc_g4_vertex_buffer_t *buffer) { + return kinc_g5_vertex_buffer_stride(&buffer->impl._buffer); +} diff --git a/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/vertexbuffer.h b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/vertexbuffer.h new file mode 100644 index 000000000..17da62692 --- /dev/null +++ b/armorcore/sources/backends/g4ong5/kinc/backend/graphics4/vertexbuffer.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int myCount; + // void prepareLock(); + kinc_g5_vertex_buffer_t _buffer; + int _currentIndex; + int _multiple; + uint64_t _lastFrameNumber; +} kinc_g4_vertex_buffer_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/G5onG4.c.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/G5onG4.c.h new file mode 100644 index 000000000..7daf9e731 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/G5onG4.c.h @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +// extern kinc_g5_pipeline_t *currentProgram; + +void kinc_g5_internal_destroy_window(int window) { + kinc_g4_internal_destroy_window(window); +} + +void kinc_g5_internal_destroy() { + kinc_g4_internal_destroy(); +} + +void kinc_g5_internal_init() { + kinc_g4_internal_init(); +} + +void kinc_g5_internal_init_window(int window, int depthBufferBits, int stencilBufferBits, bool vsync) { + kinc_g4_internal_init_window(window, depthBufferBits, stencilBufferBits, vsync); +} + +// void kinc_g5_draw_indexed_vertices_instanced(int instanceCount) {} + +// void kinc_g5_draw_indexed_vertices_instanced_from_to(int instanceCount, int start, int count) {} + +// void kinc_g5_draw_indexed_vertices_instanced_from_to_from(int instanceCount, int start, int count, int vertex_offset) {} + +void kinc_g5_begin(kinc_g5_render_target_t *renderTarget, int window) {} + +void kinc_g5_end(int window) {} + +bool kinc_g5_swap_buffers() { + return kinc_g4_swap_buffers(); +} + +void kinc_g5_flush() {} + +int kinc_g5_max_bound_textures(void) { + return kinc_g4_max_bound_textures(); +} + +bool kinc_g5_supports_raytracing() { + return false; +} + +bool kinc_g5_supports_instanced_rendering() { + return kinc_g4_supports_instanced_rendering(); +} + +bool kinc_g5_supports_compute_shaders() { + return kinc_g4_supports_compute_shaders(); +} + +bool kinc_g5_supports_blend_constants() { + return kinc_g4_supports_blend_constants(); +} + +bool kinc_g5_supports_non_pow2_textures() { + return kinc_g4_supports_non_pow2_textures(); +} + +bool kinc_g5_render_targets_inverted_y() { + return kinc_g4_render_targets_inverted_y(); +} \ No newline at end of file diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/G5onG4.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/G5onG4.h new file mode 100644 index 000000000..654181c14 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/G5onG4.h @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/commandlist.c.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/commandlist.c.h new file mode 100644 index 000000000..81d706063 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/commandlist.c.h @@ -0,0 +1,384 @@ +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#ifdef KINC_MICROSOFT +#include +#endif + +#define WRITE(type, value) \ + if (list->impl.commandIndex + sizeof(type) > KINC_G5ONG4_COMMANDS_SIZE) { \ + kinc_log(KINC_LOG_LEVEL_ERROR, "Trying to write too many commands to the command list."); \ + return; \ + } \ + *(type *)(&list->impl.commands[list->impl.commandIndex]) = value; \ + list->impl.commandIndex += sizeof(type); +#define READ(type, var) \ + if (index + sizeof(type) > KINC_G5ONG4_COMMANDS_SIZE) { \ + kinc_log(KINC_LOG_LEVEL_ERROR, "Trying to read beyond the end of the command list?"); \ + return; \ + } \ + type var = *(type *)(&list->impl.commands[index]); \ + index += sizeof(type); + +typedef enum command { + Clear, + Draw, + SetViewport, + SetScissor, + SetPipeline, + SetVertexBuffers, + SetIndexBuffer, + SetRenderTargets, + SetRenderTargetFace, + DrawInstanced, + SetSampler, + SetTexture, + SetImageTexture, + SetTextureFromRenderTarget, + SetTextureFromRenderTargetDepth, + SetVertexConstantBuffer, + SetFragmentConstantBuffer, + SetBlendConstant, +} command_t; + +void kinc_g4_pipeline_get_constant_locations(kinc_g4_pipeline_t *state, kinc_g4_constant_location_t *vertex_locations, + kinc_g4_constant_location_t *fragment_locations, int *vertex_sizes, int *fragment_sizes, int *max_vertex, + int *max_fragment); + +void kinc_g5_command_list_init(kinc_g5_command_list_t *list) {} + +void kinc_g5_command_list_destroy(kinc_g5_command_list_t *list) {} + +void kinc_g5_command_list_begin(kinc_g5_command_list_t *list) { + list->impl.commandIndex = 0; +} + +void kinc_g5_command_list_end(kinc_g5_command_list_t *list) {} + +void kinc_g5_command_list_clear(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget, unsigned flags, unsigned color, float depth, + int stencil) { + WRITE(command_t, Clear); + WRITE(unsigned, flags); + WRITE(unsigned, color); + WRITE(float, depth); + WRITE(int, stencil); +} + +void kinc_g5_command_list_render_target_to_framebuffer_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) {} +void kinc_g5_command_list_framebuffer_to_render_target_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) {} +void kinc_g5_command_list_texture_to_render_target_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) {} +void kinc_g5_command_list_render_target_to_texture_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) {} + +void kinc_g5_command_list_draw_indexed_vertices(kinc_g5_command_list_t *list) { + kinc_g5_command_list_draw_indexed_vertices_from_to(list, 0, list->impl._indexCount); +} + +void kinc_g5_command_list_draw_indexed_vertices_from_to(kinc_g5_command_list_t *list, int start, int count) { + WRITE(command_t, Draw); + WRITE(int, start); + WRITE(int, count); +} + +void kinc_g5_command_list_draw_indexed_vertices_instanced(kinc_g5_command_list_t *list, int instanceCount) { + kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(list, instanceCount, 0, list->impl._indexCount); +} +void kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(kinc_g5_command_list_t *list, int instanceCount, int start, int count) { + WRITE(command_t, DrawInstanced); + WRITE(int, instanceCount); + WRITE(int, start); + WRITE(int, count); +} + +void kinc_g5_command_list_viewport(kinc_g5_command_list_t *list, int x, int y, int width, int height) { + WRITE(command_t, SetViewport); + WRITE(int, x); + WRITE(int, y); + WRITE(int, width); + WRITE(int, height); +} + +void kinc_g5_command_list_scissor(kinc_g5_command_list_t *list, int x, int y, int width, int height) { + WRITE(command_t, SetScissor); + WRITE(int, x); + WRITE(int, y); + WRITE(int, width); + WRITE(int, height); +} + +void kinc_g5_command_list_disable_scissor(kinc_g5_command_list_t *list) {} + +void kinc_g5_command_list_set_pipeline(kinc_g5_command_list_t *list, struct kinc_g5_pipeline *pipeline) { + WRITE(command_t, SetPipeline); + WRITE(kinc_g5_pipeline_t *, pipeline); +} + +void kinc_g5_command_list_set_blend_constant(kinc_g5_command_list_t *list, float r, float g, float b, float a) { + WRITE(command_t, SetBlendConstant); + WRITE(float, r); + WRITE(float, g); + WRITE(float, b); + WRITE(float, a); +} + +void kinc_g5_command_list_set_vertex_buffers(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer **buffers, int *offsets, int count) { + WRITE(command_t, SetVertexBuffers); + WRITE(int, count); + for (int i = 0; i < count; ++i) { + WRITE(kinc_g5_vertex_buffer_t *, buffers[i]); + if (offsets[i] != 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "kinc_g5_command_list_set_vertex_buffers: offsets not supported"); + } + } +} + +void kinc_g5_command_list_set_index_buffer(kinc_g5_command_list_t *list, struct kinc_g5_index_buffer *buffer) { + WRITE(command_t, SetIndexBuffer); + WRITE(kinc_g5_index_buffer_t *, buffer); + list->impl._indexCount = buffer->impl.myCount; +} + +void kinc_g5_command_list_set_render_targets(kinc_g5_command_list_t *list, struct kinc_g5_render_target **targets, int count) { + WRITE(command_t, SetRenderTargets); + WRITE(int, count); + for (int i = 0; i < count; ++i) { + WRITE(kinc_g5_render_target_t *, targets[i]); + } +} + +void kinc_g5_command_list_upload_index_buffer(kinc_g5_command_list_t *list, struct kinc_g5_index_buffer *buffer) {} +void kinc_g5_command_list_upload_vertex_buffer(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer *buffer) {} +void kinc_g5_command_list_upload_texture(kinc_g5_command_list_t *list, struct kinc_g5_texture *texture) {} + +void kinc_g5_command_list_set_vertex_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size) { + WRITE(command_t, SetVertexConstantBuffer); + WRITE(kinc_g5_constant_buffer_t *, buffer); +} + +void kinc_g5_command_list_set_fragment_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size) { + WRITE(command_t, SetFragmentConstantBuffer); + WRITE(kinc_g5_constant_buffer_t *, buffer); +} + +void kinc_g5_command_list_execute(kinc_g5_command_list_t *list) { + kinc_g5_pipeline_t *current_pipeline = NULL; + int index = 0; + while (index < list->impl.commandIndex) { + READ(command_t, command); + switch (command) { + case Clear: { + READ(unsigned, flags); + READ(unsigned, color); + READ(float, depth); + READ(int, stencil); + kinc_g4_clear(flags, color, depth, stencil); + break; + } + case Draw: { + READ(int, start); + READ(int, count); + kinc_g4_draw_indexed_vertices_from_to(start, count); + break; + } + case SetViewport: { + READ(int, x); + READ(int, y); + READ(int, width); + READ(int, height); + kinc_g4_viewport(x, y, width, height); + break; + } + case SetScissor: { + READ(int, x); + READ(int, y); + READ(int, width); + READ(int, height); + kinc_g4_scissor(x, y, width, height); + break; + } + case SetPipeline: { + READ(kinc_g5_pipeline_t *, pipeline); + current_pipeline = pipeline; + kinc_g4_set_pipeline(&pipeline->impl.pipe); + break; + } + case SetVertexBuffers: { + READ(int, count); +#ifdef KINC_MICROSOFT + kinc_g4_vertex_buffer_t **buffers = (kinc_g4_vertex_buffer_t **)alloca(sizeof(kinc_g4_vertex_buffer_t *) * count); +#else + kinc_g4_vertex_buffer_t *buffers[count]; +#endif + for (int i = 0; i < count; ++i) { + READ(kinc_g5_vertex_buffer_t *, buffer); + buffers[i] = &buffer->impl.buffer; + } + kinc_g4_set_vertex_buffers(buffers, count); + break; + } + case SetIndexBuffer: { + READ(kinc_g5_index_buffer_t *, buffer); + kinc_g4_set_index_buffer(&buffer->impl.buffer); + break; + } + case SetRenderTargets: { + READ(int, count); +#ifdef KINC_MICROSOFT + kinc_g4_render_target_t **buffers = (kinc_g4_render_target_t **)alloca(sizeof(kinc_g4_render_target_t *) * count); +#else + kinc_g4_render_target_t *buffers[count]; +#endif + int first_framebuffer_index = -1; + for (int i = 0; i < count; ++i) { + READ(kinc_g5_render_target_t *, buffer); + if (i == 0) { + first_framebuffer_index = buffer->framebuffer_index; + } + buffers[i] = &buffer->impl.target; + } + if (first_framebuffer_index >= 0) { + if (count > 1) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Rendering to backbuffer and render targets at the same time is not supported"); + } + kinc_g4_restore_render_target(); + } + else { + kinc_g4_set_render_targets(buffers, count); + } + break; + } + case SetRenderTargetFace: { + READ(kinc_g5_render_target_t *, target); + READ(int, face); + kinc_g4_set_render_target_face(&target->impl.target, face); + break; + } + case DrawInstanced: { + READ(int, instanceCount); + READ(int, start); + READ(int, count); + kinc_g4_draw_indexed_vertices_instanced_from_to(instanceCount, start, count); + break; + } + case SetSampler: { + assert(false); + // TODO + break; + } + case SetTexture: { + READ(kinc_g5_texture_unit_t, unit); + READ(kinc_g5_texture_t *, texture); + assert(KINC_G4_SHADER_TYPE_COUNT == KINC_G5_SHADER_TYPE_COUNT); + kinc_g4_texture_unit_t g4_unit; + memcpy(&g4_unit.stages[0], &unit.stages[0], KINC_G4_SHADER_TYPE_COUNT * sizeof(int)); + kinc_g4_set_texture(g4_unit, &texture->impl.texture); + break; + } + case SetImageTexture: { + READ(kinc_g5_texture_unit_t, unit); + READ(kinc_g5_texture_t *, texture); + assert(KINC_G4_SHADER_TYPE_COUNT == KINC_G5_SHADER_TYPE_COUNT); + kinc_g4_texture_unit_t g4_unit; + memcpy(&g4_unit.stages[0], &unit.stages[0], KINC_G4_SHADER_TYPE_COUNT * sizeof(int)); + kinc_g4_set_image_texture(g4_unit, &texture->impl.texture); + break; + } + case SetTextureFromRenderTarget: { + assert(false); + // TODO + break; + } + case SetTextureFromRenderTargetDepth: { + assert(false); + // TODO + break; + } + case SetVertexConstantBuffer: { + READ(kinc_g5_constant_buffer_t *, buffer); + (void)buffer; + (void)current_pipeline; + kinc_log(KINC_LOG_LEVEL_ERROR, "Constant buffers are not supported on G5onG4 at the moment."); + // if(current_pipeline == NULL) { + // kinc_log(KINC_LOG_LEVEL_ERROR, "Please set the pipeline before setting constant buffers."); + // } else { + // kinc_g4_constant_location_t *constant_locations = current_pipeline->impl.pipe.vertex_locations; + // int *sizes = current_pipeline->impl.pipe.vertex_sizes; + // char *data = buffer->data; + // for(int i = 0; i < current_pipeline->impl.pipe.vertex_count; ++i) { + // // kinc_g4_set + // // kinc_g4_set_vertex_constant_buffer(constant_locations[i], sizes[i], data); + // data += sizes[i]; + // } + // } + break; + } + case SetFragmentConstantBuffer: { + READ(kinc_g5_constant_buffer_t *, buffer); + (void)buffer; + kinc_log(KINC_LOG_LEVEL_ERROR, "Constant buffers are not supported on G5onG4 at the moment."); + break; + } + case SetBlendConstant: { + READ(float, r); + READ(float, g); + READ(float, b); + READ(float, a); + kinc_g4_set_blend_constant(r, g, b, a); + } + default: + kinc_log(KINC_LOG_LEVEL_ERROR, "Unknown command %i\n", command); + return; + } + } +} + +void kinc_g5_command_list_wait_for_execution_to_finish(kinc_g5_command_list_t *list) { + kinc_g4_flush(); +} + +void kinc_g5_command_list_get_render_target_pixels(kinc_g5_command_list_t *list, struct kinc_g5_render_target *render_target, uint8_t *data) { + kinc_log(KINC_LOG_LEVEL_ERROR, "kinc_g5_command_list_get_render_target_pixels not implemented"); +} + +void kinc_g5_command_list_set_render_target_face(kinc_g5_command_list_t *list, kinc_g5_render_target_t *texture, int face) { + WRITE(command_t, SetRenderTargetFace); + WRITE(kinc_g5_render_target_t *, texture); + WRITE(int, face); +} + +void kinc_g5_command_list_set_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture) { + WRITE(command_t, SetTexture); + WRITE(kinc_g5_texture_unit_t, unit); + WRITE(kinc_g5_texture_t *, texture); +} + +void kinc_g5_command_list_set_sampler(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_sampler_t *sampler) { + WRITE(command_t, SetSampler); +} + +void kinc_g5_command_list_set_texture_from_render_target(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_render_target_t *render_target) { + WRITE(command_t, SetTextureFromRenderTarget); +} + +void kinc_g5_command_list_set_texture_from_render_target_depth(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, + kinc_g5_render_target_t *render_target) { + WRITE(command_t, SetTextureFromRenderTargetDepth); +} + +void kinc_g5_command_list_set_image_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture) { + WRITE(command_t, SetImageTexture); + WRITE(kinc_g5_texture_unit_t, unit); + WRITE(kinc_g5_texture_t *, texture); +} + +void kinc_g5_command_list_set_compute_shader(kinc_g5_command_list_t *list, struct kinc_g5_compute_shader *shader) {} + +void kinc_g5_command_list_compute(kinc_g5_command_list_t *list, int x, int y, int z) {} diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/commandlist.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/commandlist.h new file mode 100644 index 000000000..f8c26e6c0 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/commandlist.h @@ -0,0 +1,21 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct kinc_g5_pipeline; + +#define KINC_G5ONG4_COMMANDS_SIZE 1024 + +typedef struct { + struct kinc_g5_pipeline *_currentPipeline; + int _indexCount; + char commands[KINC_G5ONG4_COMMANDS_SIZE]; + int commandIndex; + bool closed; +} CommandList5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/compute.c b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/compute.c new file mode 100644 index 000000000..3d13a3b0f --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/compute.c @@ -0,0 +1,28 @@ +#include +#include + +#include + +void kinc_g5_compute_shader_init(kinc_g5_compute_shader *shader, void *source, int length) { + kinc_g4_compute_shader_init(&shader->impl.g4, source, length); +} + +void kinc_g5_compute_shader_destroy(kinc_g5_compute_shader *shader) { + kinc_g4_compute_shader_destroy(&shader->impl.g4); +} + +kinc_g5_constant_location_t kinc_g5_compute_shader_get_constant_location(kinc_g5_compute_shader *shader, const char *name) { + kinc_g5_constant_location_t location = {0}; + location.impl.location = kinc_g4_compute_shader_get_constant_location(&shader->impl.g4, name); + return location; +} + +kinc_g5_texture_unit_t kinc_g5_compute_shader_get_texture_unit(kinc_g5_compute_shader *shader, const char *name) { + kinc_g5_texture_unit_t g5_unit = {0}; + kinc_g4_texture_unit_t g4_unit = kinc_g4_compute_shader_get_texture_unit(&shader->impl.g4, name); + assert(KINC_G4_SHADER_TYPE_COUNT == KINC_G5_SHADER_TYPE_COUNT); + for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) { + g5_unit.stages[i] = g4_unit.stages[i]; + } + return g5_unit; +} diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/compute.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/compute.h new file mode 100644 index 000000000..59a278980 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/compute.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +typedef struct kinc_g5_compute_shader_impl { + kinc_g4_compute_shader g4; +} kinc_g5_compute_shader_impl; diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/constantbuffer.c.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/constantbuffer.c.h new file mode 100644 index 000000000..043dc19a1 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/constantbuffer.c.h @@ -0,0 +1,33 @@ +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool kinc_g5_transposeMat3 = false; +bool kinc_g5_transposeMat4 = false; + +void kinc_g5_constant_buffer_init(kinc_g5_constant_buffer_t *buffer, int size) { + buffer->impl.mySize = size; + buffer->data = malloc(size); +} + +void kinc_g5_constant_buffer_destroy(kinc_g5_constant_buffer_t *buffer) { + free(buffer->data); +} + +void kinc_g5_constant_buffer_lock_all(kinc_g5_constant_buffer_t *buffer) {} + +void kinc_g5_constant_buffer_lock(kinc_g5_constant_buffer_t *buffer, int start, int count) {} + +void kinc_g5_constant_buffer_unlock(kinc_g5_constant_buffer_t *buffer) {} + +int kinc_g5_constant_buffer_size(kinc_g5_constant_buffer_t *buffer) { + return buffer->impl.mySize; +} + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/constantbuffer.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/constantbuffer.h new file mode 100644 index 000000000..e812d9e6e --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/constantbuffer.h @@ -0,0 +1,15 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int lastStart; + int lastCount; + int mySize; +} ConstantBuffer5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/g5ong4unit.c b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/g5ong4unit.c new file mode 100644 index 000000000..85bcd6999 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/g5ong4unit.c @@ -0,0 +1,10 @@ +#include "G5onG4.c.h" +#include "commandlist.c.h" +#include "constantbuffer.c.h" +#include "indexbuffer.c.h" +#include "pipeline.c.h" +#include "rendertarget.c.h" +#include "sampler.c.h" +#include "shader.c.h" +#include "texture.c.h" +#include "vertexbuffer.c.h" diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/graphics.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/graphics.h new file mode 100644 index 000000000..056f2a534 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/graphics.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/indexbuffer.c.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/indexbuffer.c.h new file mode 100644 index 000000000..f68a278c5 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/indexbuffer.c.h @@ -0,0 +1,34 @@ +#include "indexbuffer.h" + +#include + +#include + +void kinc_g5_index_buffer_init(kinc_g5_index_buffer_t *buffer, int count, kinc_g5_index_buffer_format_t format, bool gpuMemory) { + buffer->impl.myCount = count; + kinc_g4_index_buffer_init(&buffer->impl.buffer, count, (kinc_g4_index_buffer_format_t)format, gpuMemory ? KINC_G4_USAGE_STATIC : KINC_G4_USAGE_DYNAMIC); +} + +void kinc_g5_index_buffer_destroy(kinc_g5_index_buffer_t *buffer) { + kinc_g4_index_buffer_destroy(&buffer->impl.buffer); +} + +void *kinc_g5_index_buffer_lock_all(kinc_g5_index_buffer_t *buffer) { + return kinc_g4_index_buffer_lock_all(&buffer->impl.buffer); +} + +void *kinc_g5_index_buffer_lock(kinc_g5_index_buffer_t *buffer, int start, int count) { + return kinc_g4_index_buffer_lock(&buffer->impl.buffer, start, count); +} + +void kinc_g5_index_buffer_unlock_all(kinc_g5_index_buffer_t *buffer) { + kinc_g4_index_buffer_unlock_all(&buffer->impl.buffer); +} + +void kinc_g5_index_buffer_unlock(kinc_g5_index_buffer_t *buffer, int count) { + kinc_g4_index_buffer_unlock(&buffer->impl.buffer, count); +} + +int kinc_g5_index_buffer_count(kinc_g5_index_buffer_t *buffer) { + return buffer->impl.myCount; +} diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/indexbuffer.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/indexbuffer.h new file mode 100644 index 000000000..a08bd929b --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/indexbuffer.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + kinc_g4_index_buffer_t buffer; + int myCount; +} IndexBuffer5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/pipeline.c.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/pipeline.c.h new file mode 100644 index 000000000..dcc51c88e --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/pipeline.c.h @@ -0,0 +1,63 @@ +#include +#include +#include +#include + +#include + +#include + +void kinc_g4_pipeline_get_constant_locations(kinc_g4_pipeline_t *state, kinc_g4_constant_location_t *vertex_locations, + kinc_g4_constant_location_t *fragment_locations, int *vertex_sizes, int *fragment_sizes, int *max_vertex, + int *max_fragment); + +void kinc_g5_pipeline_init(kinc_g5_pipeline_t *pipeline) { + kinc_g4_pipeline_init(&pipeline->impl.pipe); + // int vertex_count = 0; + // int fragment_count = 0; + // kinc_g4_pipeline_get_constant_locations(&pipeline->impl.pipe, NULL, NULL, NULL, NULL, &vertex_count, &fragment_count); + // pipeline->impl.vertex_locations = malloc(vertex_count * sizeof(kinc_g4_constant_location_t)); + // pipeline->impl.fragment_locations = malloc(fragment_count * sizeof(kinc_g4_constant_location_t)); + // pipeline->impl.vertex_sizes = malloc(vertex_count * sizeof(int)); + // pipeline->impl.fragment_sizes = malloc(fragment_count * sizeof(int)); + // if (pipeline->impl.vertex_locations == NULL || pipeline->impl.fragment_locations == NULL || pipeline->impl.vertex_sizes == NULL || + // pipeline->impl.fragment_sizes == NULL) { + // kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to allocate pipeline reflection data."); + // kinc_stop(); + // } + // else { + // pipeline->impl.vertex_location_count = vertex_count; + // pipeline->impl.fragment_location_count = fragment_count; + // kinc_g4_pipeline_get_constant_locations(&pipeline->impl.pipe, pipeline->impl.vertex_locations, pipeline->impl.fragment_locations, + // pipeline->impl.vertex_sizes, pipeline->impl.fragment_sizes, &vertex_count, &fragment_count); + // } +} + +void kinc_g5_pipeline_destroy(kinc_g5_pipeline_t *pipe) { + kinc_g4_pipeline_destroy(&pipe->impl.pipe); +} + +kinc_g5_constant_location_t kinc_g5_pipeline_get_constant_location(kinc_g5_pipeline_t *pipe, const char *name) { + kinc_g5_constant_location_t location; + location.impl.location = kinc_g4_pipeline_get_constant_location(&pipe->impl.pipe, name); + return location; +} + +kinc_g5_texture_unit_t kinc_g5_pipeline_get_texture_unit(kinc_g5_pipeline_t *pipe, const char *name) { + kinc_g4_texture_unit_t g4_unit = kinc_g4_pipeline_get_texture_unit(&pipe->impl.pipe, name); + + assert(KINC_G4_SHADER_TYPE_COUNT == KINC_G5_SHADER_TYPE_COUNT); + kinc_g5_texture_unit_t g5_unit; + memcpy(&g5_unit.stages[0], &g4_unit.stages[0], KINC_G5_SHADER_TYPE_COUNT * sizeof(int)); + + return g5_unit; +} + +void kinc_g5_pipeline_compile(kinc_g5_pipeline_t *pipe) { + for (int i = 0; i < 16; ++i) { + pipe->impl.pipe.input_layout[i] = pipe->inputLayout[i]; + } + pipe->impl.pipe.vertex_shader = &pipe->vertexShader->impl.shader; + pipe->impl.pipe.fragment_shader = &pipe->fragmentShader->impl.shader; + kinc_g4_pipeline_compile(&pipe->impl.pipe); +} diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/pipeline.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/pipeline.h new file mode 100644 index 000000000..ae3f46c8f --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/pipeline.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + kinc_g4_pipeline_t pipe; + // int vertex_location_count; + // int fragment_location_count; + // kinc_g4_constant_location_t *vertex_locations; + // kinc_g4_constant_location_t *fragment_locations; + // int *vertex_sizes; + // int *fragment_sizes; +} PipelineState5Impl; + +typedef struct { + int a; +} ComputePipelineState5Impl; + +typedef struct { + kinc_g4_constant_location_t location; +} ConstantLocation5Impl; + +typedef struct { + int nothing; +} AttributeLocation5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/rendertarget.c.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/rendertarget.c.h new file mode 100644 index 000000000..3a25f6dae --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/rendertarget.c.h @@ -0,0 +1,45 @@ +#include +#include +#include + +void kinc_g5_render_target_init_with_multisampling(kinc_g5_render_target_t *renderTarget, int width, int height, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + renderTarget->texWidth = renderTarget->width = width; + renderTarget->texHeight = renderTarget->height = height; + renderTarget->framebuffer_index = -1; + kinc_g4_render_target_init_with_multisampling(&renderTarget->impl.target, width, height, (kinc_g4_render_target_format_t)format, depthBufferBits, + stencilBufferBits, samples_per_pixel); +} + +void kinc_g5_render_target_init_framebuffer_with_multisampling(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + target->framebuffer_index = 0; + target->width = target->texWidth = width; + target->height = target->texHeight = height; +} + +void kinc_g5_render_target_init_cube_with_multisampling(kinc_g5_render_target_t *target, int cubeMapSize, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + target->texWidth = target->width = cubeMapSize; + target->texHeight = target->height = cubeMapSize; + target->isCubeMap = true; + target->framebuffer_index = -1; + kinc_g4_render_target_init_cube_with_multisampling(&target->impl.target, cubeMapSize, (kinc_g4_render_target_format_t)format, depthBufferBits, + stencilBufferBits, samples_per_pixel); +} + +void kinc_g5_render_target_destroy(kinc_g5_render_target_t *renderTarget) { + kinc_g4_render_target_destroy(&renderTarget->impl.target); +} + +void kinc_g5_render_target_set_depth_stencil_from(kinc_g5_render_target_t *renderTarget, kinc_g5_render_target_t *source) { + kinc_g4_render_target_set_depth_stencil_from(&renderTarget->impl.target, &source->impl.target); +} + +// void kinc_g5_render_target_get_pixels(kinc_g5_render_target_t *renderTarget, uint8_t *data) { +// kinc_g4_render_target_get_pixels(&renderTarget->impl, data); +// } + +// void kinc_g5_render_target_generate_mipmaps(kinc_g5_render_target_t *renderTarget, int levels) { +// kinc_g4_render_target_generate_mipmaps(&renderTarget->impl, levels); +// } diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/rendertarget.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/rendertarget.h new file mode 100644 index 000000000..e4e866fd1 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/rendertarget.h @@ -0,0 +1,14 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + kinc_g4_render_target_t target; +} RenderTarget5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/sampler.c.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/sampler.c.h new file mode 100644 index 000000000..c3d6c4006 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/sampler.c.h @@ -0,0 +1,5 @@ +#include + +void kinc_g5_sampler_init(kinc_g5_sampler_t *sampler, const kinc_g5_sampler_options_t *options) {} + +void kinc_g5_sampler_destroy(kinc_g5_sampler_t *sampler) {} diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/sampler.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/sampler.h new file mode 100644 index 000000000..01d504802 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/sampler.h @@ -0,0 +1,5 @@ +#pragma once + +typedef struct kinc_g5_sampler_impl { + int a; +} kinc_g5_sampler_impl_t; diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/shader.c.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/shader.c.h new file mode 100644 index 000000000..2a61a346e --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/shader.c.h @@ -0,0 +1,10 @@ +#include +#include + +void kinc_g5_shader_init(kinc_g5_shader_t *shader, const void *source, size_t length, kinc_g5_shader_type_t type) { + kinc_g4_shader_init(&shader->impl.shader, source, length, (kinc_g4_shader_type_t)type); +} + +void kinc_g5_shader_destroy(kinc_g5_shader_t *shader) { + kinc_g4_shader_destroy(&shader->impl.shader); +} diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/shader.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/shader.h new file mode 100644 index 000000000..61b260b35 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/shader.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + kinc_g4_shader_t shader; +} Shader5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/texture.c.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/texture.c.h new file mode 100644 index 000000000..d3c86fdba --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/texture.c.h @@ -0,0 +1,50 @@ +#include "texture.h" + +#include +#include + +void kinc_g5_texture_init(kinc_g5_texture_t *texture, int width, int height, kinc_image_format_t format) { + kinc_g4_texture_init(&texture->impl.texture, width, height, format); +} +void kinc_g5_texture_init3d(kinc_g5_texture_t *texture, int width, int height, int depth, kinc_image_format_t format) { + kinc_g4_texture_init3d(&texture->impl.texture, width, height, depth, format); +} +void kinc_g5_texture_init_from_image(kinc_g5_texture_t *texture, kinc_image_t *image) { + kinc_g4_texture_init_from_image(&texture->impl.texture, image); +} +void kinc_g5_texture_init_from_encoded_data(kinc_g5_texture_t *texture, void *data, int size, const char *format, bool readable) { + kinc_log(KINC_LOG_LEVEL_ERROR, "kinc_g5_texture_init_from_encoded_data not implemented"); +} +void kinc_g5_texture_init_from_data(kinc_g5_texture_t *texture, void *data, int width, int height, int format, bool readable) { + kinc_log(KINC_LOG_LEVEL_ERROR, "kinc_g5_texture_init_from_data not implemented"); +} +void kinc_g5_texture_init_non_sampled_access(kinc_g5_texture_t *texture, int width, int height, kinc_image_format_t format) { + kinc_log(KINC_LOG_LEVEL_ERROR, "kinc_g5_texture_init_non_sampled_access not implemented"); +} +void kinc_g5_texture_destroy(kinc_g5_texture_t *texture) { + kinc_g4_texture_destroy(&texture->impl.texture); +} + +uint8_t *kinc_g5_texture_lock(kinc_g5_texture_t *texture) { + return kinc_g4_texture_lock(&texture->impl.texture); +} + +void kinc_g5_texture_unlock(kinc_g5_texture_t *texture) { + kinc_g4_texture_unlock(&texture->impl.texture); +} + +void kinc_g5_texture_clear(kinc_g5_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color) { + kinc_g4_texture_clear(&texture->impl.texture, x, y, z, width, height, depth, color); +} + +int kinc_g5_texture_stride(kinc_g5_texture_t *texture) { + return kinc_g4_texture_stride(&texture->impl.texture); +} + +void kinc_g5_texture_generate_mipmaps(kinc_g5_texture_t *texture, int levels) { + kinc_g4_texture_generate_mipmaps(&texture->impl.texture, levels); +} + +void kinc_g5_texture_set_mipmap(kinc_g5_texture_t *texture, kinc_image_t *mipmap, int level) { + kinc_g4_texture_set_mipmap(&texture->impl.texture, mipmap, level); +} diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/texture.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/texture.h new file mode 100644 index 000000000..d25ecbeb6 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/texture.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + kinc_g4_texture_unit_t unit; +} TextureUnit5Impl; + +typedef struct { + kinc_g4_texture_t texture; +} Texture5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/vertexbuffer.c.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/vertexbuffer.c.h new file mode 100644 index 000000000..280d1a5b0 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/vertexbuffer.c.h @@ -0,0 +1,39 @@ +#include + +#include + +kinc_g5_vertex_buffer_t *kinc_g5_internal_current_vertex_buffer = NULL; + +void kinc_g5_vertex_buffer_init(kinc_g5_vertex_buffer_t *buffer, int count, kinc_g5_vertex_structure_t *structure, bool gpuMemory, int instanceDataStepRate) { + buffer->impl.myCount = count; + buffer->impl.myStart = 0; + kinc_g4_vertex_buffer_init(&buffer->impl.buffer, count, structure, KINC_G4_USAGE_STATIC, instanceDataStepRate); +} + +void kinc_g5_vertex_buffer_destroy(kinc_g5_vertex_buffer_t *buffer) { + kinc_g4_vertex_buffer_destroy(&buffer->impl.buffer); +} + +float *kinc_g5_vertex_buffer_lock_all(kinc_g5_vertex_buffer_t *buffer) { + return kinc_g4_vertex_buffer_lock_all(&buffer->impl.buffer); +} + +float *kinc_g5_vertex_buffer_lock(kinc_g5_vertex_buffer_t *buffer, int start, int count) { + return kinc_g4_vertex_buffer_lock(&buffer->impl.buffer, start, count); +} + +void kinc_g5_vertex_buffer_unlock_all(kinc_g5_vertex_buffer_t *buffer) { + kinc_g4_vertex_buffer_unlock_all(&buffer->impl.buffer); +} + +void kinc_g5_vertex_buffer_unlock(kinc_g5_vertex_buffer_t *buffer, int count) { + kinc_g4_vertex_buffer_unlock(&buffer->impl.buffer, count); +} + +int kinc_g5_vertex_buffer_count(kinc_g5_vertex_buffer_t *buffer) { + return buffer->impl.myCount; +} + +int kinc_g5_vertex_buffer_stride(kinc_g5_vertex_buffer_t *buffer) { + return kinc_g4_vertex_buffer_stride(&buffer->impl.buffer); +} diff --git a/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/vertexbuffer.h b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/vertexbuffer.h new file mode 100644 index 000000000..1351f05d8 --- /dev/null +++ b/armorcore/sources/backends/g5ong4/kinc/backend/graphics5/vertexbuffer.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + kinc_g4_vertex_buffer_t buffer; + int myCount; + int myStart; +} VertexBuffer5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/ios/kinc/backend/GLView.h b/armorcore/sources/backends/ios/kinc/backend/GLView.h new file mode 100644 index 000000000..bb5f70a79 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/GLView.h @@ -0,0 +1,50 @@ +#import +#import +#ifdef KINC_METAL +#import +#import +#else +#import +#import +#endif +#ifndef KINC_TVOS +#import +#endif + +struct kinc_g5_render_target; + +@interface GLView : UIView { +@private +#ifdef KINC_METAL + id device; + id commandQueue; + id commandBuffer; + id commandEncoder; + id drawable; + id library; + MTLRenderPassDescriptor *renderPassDescriptor; + id depthTexture; +#else + EAGLContext *context; + GLuint defaultFramebuffer, colorRenderbuffer, depthStencilRenderbuffer; +#endif + +#ifndef KINC_TVOS + CMMotionManager *motionManager; +#endif + bool hasAccelerometer; + float lastAccelerometerX, lastAccelerometerY, lastAccelerometerZ; +} + +- (void)begin; +- (void)end; +- (void)showKeyboard; +- (void)hideKeyboard; +#ifdef KINC_METAL +- (CAMetalLayer *)metalLayer; +- (id)metalDevice; +- (id)metalLibrary; +- (id)metalQueue; +#endif + +@end diff --git a/armorcore/sources/backends/ios/kinc/backend/GLView.m.h b/armorcore/sources/backends/ios/kinc/backend/GLView.m.h new file mode 100644 index 000000000..af56a5282 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/GLView.m.h @@ -0,0 +1,460 @@ +#import "GLView.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef KINC_OPENGL +#include +#endif + +static const int touchmaxcount = 20; +static void *touches[touchmaxcount]; + +static void initTouches(void) { + for (int i = 0; i < touchmaxcount; ++i) { + touches[i] = NULL; + } +} + +static int getTouchIndex(void *touch) { + for (int i = 0; i < touchmaxcount; ++i) { + if (touches[i] == touch) + return i; + } + return -1; +} + +static int addTouch(void *touch) { + for (int i = 0; i < touchmaxcount; ++i) { + if (touches[i] == NULL) { + touches[i] = touch; + return i; + } + } + return -1; +} + +static int removeTouch(void *touch) { + for (int i = 0; i < touchmaxcount; ++i) { + if (touches[i] == touch) { + touches[i] = NULL; + return i; + } + } + return -1; +} + +static GLint backingWidth, backingHeight; + +int kinc_window_width(int window) { + return backingWidth; +} + +int kinc_window_height(int window) { + return backingHeight; +} + +@implementation GLView + +#ifdef KINC_METAL ++ (Class)layerClass { + return [CAMetalLayer class]; +} +#else ++ (Class)layerClass { + return [CAEAGLLayer class]; +} +#endif + +#ifdef KINC_OPENGL +extern int kinc_ios_gl_framebuffer; +#endif + +#ifdef KINC_METAL +- (void)hoverGesture:(UIHoverGestureRecognizer *)recognizer { + CGPoint point = [recognizer locationInView:self]; + float x = point.x * self.contentScaleFactor; + float y = point.y * self.contentScaleFactor; + // Pencil hover + kinc_internal_pen_trigger_move(0, x, y, 0.0); +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:(CGRect)frame]; + self.contentScaleFactor = [UIScreen mainScreen].scale; + + backingWidth = frame.size.width * self.contentScaleFactor; + backingHeight = frame.size.height * self.contentScaleFactor; + + initTouches(); + + device = MTLCreateSystemDefaultDevice(); + commandQueue = [device newCommandQueue]; + library = [device newDefaultLibrary]; + + CAMetalLayer *metalLayer = (CAMetalLayer *)self.layer; + + metalLayer.device = device; + metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; + metalLayer.framebufferOnly = YES; + // metalLayer.presentsWithTransaction = YES; + + metalLayer.opaque = YES; + metalLayer.backgroundColor = nil; + + [self addGestureRecognizer:[[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(hoverGesture:)]]; + + return self; +} +#else +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:(CGRect)frame]; + self.contentScaleFactor = [UIScreen mainScreen].scale; + + backingWidth = frame.size.width * self.contentScaleFactor; + backingHeight = frame.size.height * self.contentScaleFactor; + + initTouches(); + + CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer; + + eaglLayer.opaque = YES; + eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking, + kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; + + context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + + if (!context || ![EAGLContext setCurrentContext:context]) { + //[self release]; + return nil; + } + + glGenFramebuffersOES(1, &defaultFramebuffer); + glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer); + kinc_ios_gl_framebuffer = defaultFramebuffer; + + glGenRenderbuffersOES(1, &colorRenderbuffer); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer); + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, colorRenderbuffer); + + glGenRenderbuffersOES(1, &depthStencilRenderbuffer); + glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthStencilRenderbuffer); + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthStencilRenderbuffer); + glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_STENCIL_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthStencilRenderbuffer); + + // Start acceletometer + hasAccelerometer = false; +#ifndef KINC_TVOS + motionManager = [[CMMotionManager alloc] init]; + if ([motionManager isAccelerometerAvailable]) { + motionManager.accelerometerUpdateInterval = 0.033; + [motionManager startAccelerometerUpdates]; + hasAccelerometer = true; + } +#endif + +#ifndef KINC_TVOS + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onKeyboardHide:) name:UIKeyboardWillHideNotification object:nil]; +#endif + + return self; +} +#endif + +#ifdef KINC_METAL +- (void)begin { +} +#else +- (void)begin { + [EAGLContext setCurrentContext:context]; + // glBindFramebufferOES(GL_FRAMEBUFFER_OES, defaultFramebuffer); + // glViewport(0, 0, backingWidth, backingHeight); + +#ifndef KINC_TVOS + // Accelerometer updates + if (hasAccelerometer) { + + CMAcceleration acc = motionManager.accelerometerData.acceleration; + + if (acc.x != lastAccelerometerX || acc.y != lastAccelerometerY || acc.z != lastAccelerometerZ) { + + kinc_internal_on_acceleration(acc.x, acc.y, acc.z); + + lastAccelerometerX = acc.x; + lastAccelerometerY = acc.y; + lastAccelerometerZ = acc.z; + } + } +#endif +} +#endif + +#ifdef KINC_METAL +- (void)end { +} +#else +- (void)end { + glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer); + [context presentRenderbuffer:GL_RENDERBUFFER_OES]; // crash at end +} +#endif + +void kinc_internal_call_resize_callback(int window, int width, int height); + +#ifdef KINC_METAL +- (void)layoutSubviews { + backingWidth = self.frame.size.width * self.contentScaleFactor; + backingHeight = self.frame.size.height * self.contentScaleFactor; + + CAMetalLayer *metalLayer = (CAMetalLayer *)self.layer; + metalLayer.drawableSize = CGSizeMake(backingWidth, backingHeight); + + kinc_internal_call_resize_callback(0, backingWidth, backingHeight); +} +#else +- (void)layoutSubviews { + glBindRenderbufferOES(GL_RENDERBUFFER_OES, colorRenderbuffer); + [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer *)self.layer]; + + glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); + glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); + + printf("backingWitdh/Height: %i, %i\n", backingWidth, backingHeight); + + glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthStencilRenderbuffer); + glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH24_STENCIL8_OES, backingWidth, backingHeight); + + if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) { + NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); + } + + kinc_internal_call_resize_callback(0, backingWidth, backingHeight); +} +#endif + +#ifdef KINC_METAL +- (void)dealloc { +} +#else +- (void)dealloc { + if (defaultFramebuffer) { + glDeleteFramebuffersOES(1, &defaultFramebuffer); + defaultFramebuffer = 0; + } + + if (colorRenderbuffer) { + glDeleteRenderbuffersOES(1, &colorRenderbuffer); + colorRenderbuffer = 0; + } + + if (depthStencilRenderbuffer) { + glDeleteRenderbuffersOES(1, &depthStencilRenderbuffer); + depthStencilRenderbuffer = 0; + } + + if ([EAGLContext currentContext] == context) + [EAGLContext setCurrentContext:nil]; + + //[context release]; + context = nil; + + //[super dealloc]; +} +#endif + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + for (UITouch *touch in touches) { + int index = getTouchIndex((__bridge void *)touch); + if (index == -1) + index = addTouch((__bridge void *)touch); + if (index >= 0) { + CGPoint point = [touch locationInView:self]; + float x = point.x * self.contentScaleFactor; + float y = point.y * self.contentScaleFactor; + if (index == 0) { + kinc_internal_mouse_trigger_press(0, event.buttonMask == UIEventButtonMaskSecondary ? 1 : 0, x, y); + } + kinc_internal_surface_trigger_touch_start(index, x, y); + + if (touch.type == UITouchTypePencil) { + kinc_internal_pen_trigger_press(0, x, y, 0.0); + } + } + } +} + +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { + for (UITouch *touch in touches) { + int index = getTouchIndex((__bridge void *)touch); + if (index >= 0) { + CGPoint point = [touch locationInView:self]; + float x = point.x * self.contentScaleFactor; + float y = point.y * self.contentScaleFactor; + if (index == 0) { + kinc_internal_mouse_trigger_move(0, x, y); + } + kinc_internal_surface_trigger_move(index, x, y); + } + } +} + +- (void)touchesEstimatedPropertiesUpdated:(NSSet *)touches { + for (UITouch *touch in touches) { + if (touch.type == UITouchTypePencil) { + CGPoint point = [touch locationInView:self]; + float x = point.x * self.contentScaleFactor; + float y = point.y * self.contentScaleFactor; + kinc_internal_pen_trigger_move(0, x, y, touch.force); + } + } +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + for (UITouch *touch in touches) { + int index = removeTouch((__bridge void *)touch); + if (index >= 0) { + CGPoint point = [touch locationInView:self]; + float x = point.x * self.contentScaleFactor; + float y = point.y * self.contentScaleFactor; + if (index == 0) { + kinc_internal_mouse_trigger_release(0, event.buttonMask == UIEventButtonMaskSecondary ? 1 : 0, x, y); + } + kinc_internal_surface_trigger_touch_end(index, x, y); + + if (touch.type == UITouchTypePencil) { + kinc_internal_pen_trigger_release(0, x, y, 0.0); + } + } + } +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { + for (UITouch *touch in touches) { + int index = removeTouch((__bridge void *)touch); + if (index >= 0) { + CGPoint point = [touch locationInView:self]; + float x = point.x * self.contentScaleFactor; + float y = point.y * self.contentScaleFactor; + if (index == 0) { + kinc_internal_mouse_trigger_release(0, event.buttonMask == UIEventButtonMaskSecondary ? 1 : 0, x, y); + } + kinc_internal_surface_trigger_touch_end(index, x, y); + + if (touch.type == UITouchTypePencil) { + kinc_internal_pen_trigger_release(0, x, y, 0.0); + } + } + } +} + +static NSString *keyboardstring; +static UITextField *myTextField = NULL; +static bool shiftDown = false; + +- (void)showKeyboard { + [self becomeFirstResponder]; +} + +- (void)hideKeyboard { + [self resignFirstResponder]; +} + +- (BOOL)hasText { + return YES; +} + +- (void)insertText:(NSString *)text { + if ([text length] == 1) { + unichar ch = [text characterAtIndex:[text length] - 1]; + if (ch == 8212) + ch = '_'; + if (ch == L'\n') { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_RETURN); + kinc_internal_keyboard_trigger_key_up(KINC_KEY_RETURN); + return; + } + + if (ch == L'.') { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_PERIOD); + kinc_internal_keyboard_trigger_key_up(KINC_KEY_PERIOD); + } + else if (ch == L'%') { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_PERCENT); + kinc_internal_keyboard_trigger_key_up(KINC_KEY_PERCENT); + } + else if (ch == L'(') { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_OPEN_PAREN); + kinc_internal_keyboard_trigger_key_up(KINC_KEY_OPEN_PAREN); + } + else if (ch == L'&') { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_AMPERSAND); + kinc_internal_keyboard_trigger_key_up(KINC_KEY_AMPERSAND); + } + else if (ch == L'$') { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_DOLLAR); + kinc_internal_keyboard_trigger_key_up(KINC_KEY_DOLLAR); + } + else if (ch == L'#') { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_HASH); + kinc_internal_keyboard_trigger_key_up(KINC_KEY_HASH); + } + else if (ch >= L'a' && ch <= L'z') { + if (shiftDown) { + kinc_internal_keyboard_trigger_key_up(KINC_KEY_SHIFT); + shiftDown = false; + } + kinc_internal_keyboard_trigger_key_down(ch + KINC_KEY_A - L'a'); + kinc_internal_keyboard_trigger_key_up(ch + KINC_KEY_A - L'a'); + } + else { + if (!shiftDown) { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_SHIFT); + shiftDown = true; + } + kinc_internal_keyboard_trigger_key_down(ch + KINC_KEY_A - L'A'); + kinc_internal_keyboard_trigger_key_up(ch + KINC_KEY_A - L'A'); + } + + kinc_internal_keyboard_trigger_key_press(ch); + } +} + +- (void)deleteBackward { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_BACKSPACE); + kinc_internal_keyboard_trigger_key_up(KINC_KEY_BACKSPACE); +} + +- (BOOL)canBecomeFirstResponder { + return YES; +} + +- (void)onKeyboardHide:(NSNotification *)notification { + kinc_keyboard_hide(); +} + +#ifdef KINC_METAL +- (CAMetalLayer *)metalLayer { + return (CAMetalLayer *)self.layer; +} + +- (id)metalDevice { + return device; +} + +- (id)metalLibrary { + return library; +} + +- (id)metalQueue { + return commandQueue; +} +#endif + +@end diff --git a/armorcore/sources/backends/ios/kinc/backend/GLViewController.h b/armorcore/sources/backends/ios/kinc/backend/GLViewController.h new file mode 100644 index 000000000..17cad8337 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/GLViewController.h @@ -0,0 +1,17 @@ +#import +#import +#import +#import +#ifndef KINC_TVOS +#import +#endif + +@interface GLViewController : UIViewController { +@private +} + + - (void)loadView; + + - (void)setVisible:(BOOL)value; + + @end diff --git a/armorcore/sources/backends/ios/kinc/backend/GLViewController.m.h b/armorcore/sources/backends/ios/kinc/backend/GLViewController.m.h new file mode 100644 index 000000000..0138c6fa6 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/GLViewController.m.h @@ -0,0 +1,130 @@ +#import "GLView.h" +#import "GLViewController.h" + +#import + +#include +#include + +#include + +static GLView *glView; + +static bool visible; + +void beginGL(void) { +#ifdef KINC_METAL + if (!visible) { + return; + } +#endif + [glView begin]; +} + +void endGL(void) { +#ifdef KINC_METAL + if (!visible) { + return; + } +#endif + [glView end]; +} + +void showKeyboard(void) { + [glView showKeyboard]; +} + +void hideKeyboard(void) { + [glView hideKeyboard]; +} + +#ifdef KINC_METAL + +CAMetalLayer *getMetalLayer(void) { + return [glView metalLayer]; +} + +id getMetalDevice(void) { + return [glView metalDevice]; +} + +id getMetalLibrary(void) { + return [glView metalLibrary]; +} + +id getMetalQueue(void) { + return [glView metalQueue]; +} + +#endif + +@implementation GLViewController + +- (void)loadView { + visible = true; + self.view = glView = [[GLView alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + + [self.view addInteraction: [[UIDropInteraction alloc] initWithDelegate: self]]; + [self setNeedsUpdateOfScreenEdgesDeferringSystemGestures]; +} + +- (void)setVisible:(BOOL)value { + visible = value; +} + +#include +#include +#include + +extern char mobile_title[1024]; + +void importFile(NSURL *url) { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *folderName = [NSString stringWithUTF8String:mobile_title]; + NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:folderName]; + if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) { + [[NSFileManager defaultManager] createDirectoryAtPath:filePath withIntermediateDirectories:NO attributes:nil error:nil]; + } + NSString *suggestedName = url.path.lastPathComponent; + filePath = [filePath stringByAppendingPathComponent:suggestedName]; + CFURLRef cfurl = (__bridge CFURLRef)url; + CFURLStartAccessingSecurityScopedResource(cfurl); + [[NSFileManager defaultManager] copyItemAtPath:url.path toPath:filePath error:nil]; + CFURLStopAccessingSecurityScopedResource(cfurl); + wchar_t *wpath = (wchar_t *)[filePath cStringUsingEncoding:NSUTF32LittleEndianStringEncoding]; + kinc_internal_drop_files_callback(wpath); +} + +- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url { + // wchar_t *filePath = (wchar_t *)[url.path cStringUsingEncoding:NSUTF32LittleEndianStringEncoding]; + // CFURLRef cfurl = (__bridge CFURLRef)url; + // CFURLStartAccessingSecurityScopedResource(cfurl); + // kinc_internal_drop_files_callback(filePath); + // CFURLStopAccessingSecurityScopedResource(cfurl); + importFile(url); +} + +- (void)dropInteraction:(UIDropInteraction *)interaction performDrop:(id)session { + CGPoint point = [session locationInView:self.view]; + float x = point.x * glView.contentScaleFactor; + float y = point.y * glView.contentScaleFactor; + kinc_internal_mouse_trigger_move(0, x, y); + kinc_internal_surface_trigger_move(0, x, y); + + for (UIDragItem *item in session.items) { + [item.itemProvider loadInPlaceFileRepresentationForTypeIdentifier:item.itemProvider.registeredTypeIdentifiers[0] completionHandler:^(NSURL * _Nullable url, BOOL isInPlace, NSError * _Nullable error) { + importFile(url); + }]; + } +} + +- (UIDropProposal *)dropInteraction:(UIDropInteraction *)interaction sessionDidUpdate:(id)session { + return [[UIDropProposal alloc] initWithDropOperation: UIDropOperationCopy]; +} + +- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures +{ + return UIRectEdgeAll; +} + +@end diff --git a/armorcore/sources/backends/ios/kinc/backend/KoreAppDelegate.h b/armorcore/sources/backends/ios/kinc/backend/KoreAppDelegate.h new file mode 100644 index 000000000..c8bc62b21 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/KoreAppDelegate.h @@ -0,0 +1,8 @@ +#import + +@class GLView; + +@interface KoreAppDelegate : NSObject { +} + +@end \ No newline at end of file diff --git a/armorcore/sources/backends/ios/kinc/backend/KoreAppDelegate.m.h b/armorcore/sources/backends/ios/kinc/backend/KoreAppDelegate.m.h new file mode 100644 index 000000000..8862c3ebf --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/KoreAppDelegate.m.h @@ -0,0 +1,142 @@ +#import "GLView.h" +#import "GLViewController.h" +#import "KoreAppDelegate.h" +#import + +#include +#include + +@implementation KoreAppDelegate + +static UIWindow *window; +static GLViewController *glViewController; + +void loadURL(const char *url) { + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithUTF8String:url]]]; +} + +- (void)mainLoop { + // NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + // try { + @autoreleasepool { + kickstart(0, NULL); + } + //} + // catch (Kt::Exception& ex) { + // printf("Exception\n"); + // printf("%s", ex.what()); + //} + + //[pool drain]; +} + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + AVAudioSession *sessionInstance = [AVAudioSession sharedInstance]; + NSError *error; + + // set the session category + NSString *category = AVAudioSessionCategoryAmbient; + bool success = [sessionInstance setCategory:category error:&error]; + if (!success) + NSLog(@"Error setting AVAudioSession category! %@\n", [error localizedDescription]); + // CGRect rect = [[UIScreen mainScreen] applicationFrame]; + // CGRect screenBounds = [[UIScreen mainScreen] bounds]; + + // window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, Kore::max(screenBounds.size.width, screenBounds.size.height), + // Kore::max(screenBounds.size.width, screenBounds.size.height))]; + // CGRect bounds = [[UIScreen mainScreen] bounds]; + window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + [window setBackgroundColor:[UIColor blackColor]]; + + // glView = [[GLView alloc] initWithFrame:CGRectMake(0, 0, Kore::max(screenBounds.size.width, screenBounds.size.height), Kore::max(screenBounds.size.width, + // screenBounds.size.height))]; + glViewController = [[GLViewController alloc] init]; +#ifndef KINC_TVOS + glViewController.view.multipleTouchEnabled = YES; +#endif + // glViewController.view = glView; + //[glViewController ] + //[window addSubview:glView]; + [window setRootViewController:glViewController]; + [window makeKeyAndVisible]; + + [self performSelectorOnMainThread:@selector(mainLoop) withObject:nil waitUntilDone:NO]; + + return YES; +} + +#ifndef KINC_TVOS +// static Kore::Orientation convertOrientation(UIDeviceOrientation orientation) { +// switch (orientation) { +// case UIDeviceOrientationLandscapeLeft: +// return Kore::OrientationLandscapeRight; +// case UIDeviceOrientationLandscapeRight: +// return Kore::OrientationLandscapeLeft; +// case UIDeviceOrientationPortrait: +// return Kore::OrientationPortrait; +// case UIDeviceOrientationPortraitUpsideDown: +// return Kore::OrientationPortraitUpsideDown; +// default: +// return Kore::OrientationUnknown; +// } +//} + +// static UIInterfaceOrientation convertAppleOrientation(UIDeviceOrientation orientation, UIInterfaceOrientation lastOrientation) { +// switch (orientation) { +// case UIDeviceOrientationLandscapeLeft: +// return UIInterfaceOrientationLandscapeRight; +// case UIDeviceOrientationLandscapeRight: +// return UIInterfaceOrientationLandscapeLeft; +// case UIDeviceOrientationPortrait: +// return UIInterfaceOrientationPortrait; +// case UIDeviceOrientationPortraitUpsideDown: +// return UIInterfaceOrientationPortraitUpsideDown; +// default: +// return lastOrientation; +// } +//} +#endif + +void KoreUpdateKeyboard(void); +/* +- (void)didRotate:(NSNotification*)notification { + if (Kore::Application::the() != nullptr && Kore::Application::the()->orientationCallback != nullptr) +Kore::Application::the()->orientationCallback(convertOrientation([[UIDevice currentDevice] orientation])); + [UIApplication sharedApplication].statusBarOrientation = convertAppleOrientation([[UIDevice currentDevice] orientation], [UIApplication +sharedApplication].statusBarOrientation); + KoreUpdateKeyboard(); +} +*/ +- (void)applicationWillEnterForeground:(UIApplication *)application { + [glViewController setVisible:YES]; + kinc_internal_foreground_callback(); +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + kinc_internal_resume_callback(); + //[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; + //[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil]; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + kinc_internal_pause_callback(); + //[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil]; +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + [glViewController setVisible:NO]; + kinc_internal_background_callback(); +} + +- (void)applicationWillTerminate:(UIApplication *)application { + kinc_internal_shutdown_callback(); +} + +//- (void)dealloc { +// [window release]; +// [glView release]; +// [super dealloc]; +//} + +@end diff --git a/armorcore/sources/backends/ios/kinc/backend/LaunchScreen.storyboard b/armorcore/sources/backends/ios/kinc/backend/LaunchScreen.storyboard new file mode 100644 index 000000000..542ecd9e4 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/armorcore/sources/backends/ios/kinc/backend/audio.m.h b/armorcore/sources/backends/ios/kinc/backend/audio.m.h new file mode 100644 index 000000000..ab1a03a68 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/audio.m.h @@ -0,0 +1,223 @@ +#import +#import + +#include +#include +#include + +#include + +#define kOutputBus 0 + +static kinc_internal_video_sound_stream_t *video = NULL; + +void iosPlayVideoSoundStream(kinc_internal_video_sound_stream_t *v) { + video = v; +} + +void iosStopVideoSoundStream(void) { + video = NULL; +} + +static void affirm(OSStatus err) { + if (err) { + fprintf(stderr, "Error: %i\n", (int)err); + } +} + +static bool initialized; +static bool soundPlaying; +static AudioStreamBasicDescription deviceFormat; +static AudioComponentInstance audioUnit; +static bool isFloat = false; +static bool isInterleaved = true; + +static kinc_a2_buffer_t a2_buffer; + +static void copySample(void *buffer, void *secondary_buffer) { + float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location]; + float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location]; + a2_buffer.read_location += 1; + if (a2_buffer.read_location >= a2_buffer.data_size) { + a2_buffer.read_location = 0; + } + + if (video != NULL) { + float *frame = kinc_internal_video_sound_stream_next_frame(video); + left_value += frame[0]; + left_value = kinc_max(kinc_min(left_value, 1.0f), -1.0f); + right_value += frame[1]; + right_value = kinc_max(kinc_min(right_value, 1.0f), -1.0f); + if (kinc_internal_video_sound_stream_ended(video)) { + video = NULL; + } + } + + if (secondary_buffer == NULL) { + if (isFloat) { + ((float *)buffer)[0] = left_value; + ((float *)buffer)[1] = right_value; + } + else { + ((int16_t *)buffer)[0] = (int16_t)(left_value * 32767); + ((int16_t *)buffer)[1] = (int16_t)(right_value * 32767); + } + } + else { + if (isFloat) { + *(float *)buffer = left_value; + *(float *)secondary_buffer = right_value; + } + else { + *(int16_t *)buffer = (int16_t)(left_value * 32767); + *(int16_t *)secondary_buffer = (int16_t)(right_value * 32767); + } + } +} + +static OSStatus renderInput(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, + UInt32 inNumberFrames, AudioBufferList *outOutputData) { + kinc_a2_internal_callback(&a2_buffer, inNumberFrames); + if (isInterleaved) { + if (isFloat) { + float *output = (float *)outOutputData->mBuffers[0].mData; + for (int i = 0; i < inNumberFrames; ++i) { + copySample(output, NULL); + output += 2; + } + } + else { + int16_t *output = (int16_t *)outOutputData->mBuffers[0].mData; + for (int i = 0; i < inNumberFrames; ++i) { + copySample(output, NULL); + output += 2; + } + } + } + else { + if (isFloat) { + float *out1 = (float *)outOutputData->mBuffers[0].mData; + float *out2 = (float *)outOutputData->mBuffers[1].mData; + for (int i = 0; i < inNumberFrames; ++i) { + copySample(out1++, out2++); + } + } + else { + int16_t *out1 = (int16_t *)outOutputData->mBuffers[0].mData; + int16_t *out2 = (int16_t *)outOutputData->mBuffers[1].mData; + for (int i = 0; i < inNumberFrames; ++i) { + copySample(out1++, out2++); + } + } + } + return noErr; +} + +static uint32_t samples_per_second = 44100; + +static void sampleRateListener(void *inRefCon, AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement) { + Float64 sampleRate; + UInt32 size = sizeof(sampleRate); + affirm(AudioUnitGetProperty(inUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRate, &size)); + + if (samples_per_second != (uint32_t)sampleRate) { + samples_per_second = (uint32_t)sampleRate; + kinc_a2_internal_sample_rate_callback(); + } +} + +static bool initialized = false; + +void kinc_a2_init(void) { + if (initialized) { + return; + } + + kinc_a2_internal_init(); + initialized = true; + + a2_buffer.read_location = 0; + a2_buffer.write_location = 0; + a2_buffer.data_size = 128 * 1024; + a2_buffer.channel_count = 2; + a2_buffer.channels[0] = (float *)malloc(a2_buffer.data_size * sizeof(float)); + a2_buffer.channels[1] = (float *)malloc(a2_buffer.data_size * sizeof(float)); + + initialized = false; + + AudioComponentDescription desc; + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_RemoteIO; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + + AudioComponent comp = AudioComponentFindNext(NULL, &desc); + + // Get audio units + affirm(AudioComponentInstanceNew(comp, &audioUnit)); + UInt32 flag = 1; + affirm(AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(UInt32))); + + if (soundPlaying) + return; + + affirm(AudioOutputUnitStart(audioUnit)); + + UInt32 size = sizeof(AudioStreamBasicDescription); + affirm(AudioUnitGetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &deviceFormat, &size)); + + if (deviceFormat.mFormatID != kAudioFormatLinearPCM) { + fprintf(stderr, "mFormatID != kAudioFormatLinearPCM\n"); + return; + } + + if (deviceFormat.mFormatFlags & kLinearPCMFormatFlagIsFloat) { + isFloat = true; + } + + if (deviceFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) { + isInterleaved = false; + } + + AudioUnitAddPropertyListener(audioUnit, kAudioUnitProperty_StreamFormat, sampleRateListener, nil); + + initialized = true; + + printf("mSampleRate = %g\n", deviceFormat.mSampleRate); + printf("mFormatFlags = %08X\n", (unsigned int)deviceFormat.mFormatFlags); + printf("mBytesPerPacket = %d\n", (unsigned int)deviceFormat.mBytesPerPacket); + printf("mFramesPerPacket = %d\n", (unsigned int)deviceFormat.mFramesPerPacket); + printf("mChannelsPerFrame = %d\n", (unsigned int)deviceFormat.mChannelsPerFrame); + printf("mBytesPerFrame = %d\n", (unsigned int)deviceFormat.mBytesPerFrame); + printf("mBitsPerChannel = %d\n", (unsigned int)deviceFormat.mBitsPerChannel); + + if (samples_per_second != (uint32_t)deviceFormat.mSampleRate) { + samples_per_second = (uint32_t)deviceFormat.mSampleRate; + kinc_a2_internal_sample_rate_callback(); + } + + AURenderCallbackStruct callbackStruct; + callbackStruct.inputProc = renderInput; + callbackStruct.inputProcRefCon = NULL; + affirm(AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &callbackStruct, sizeof(callbackStruct))); + + soundPlaying = true; +} + +void kinc_a2_update(void) {} + +void kinc_a2_shutdown(void) { + if (!initialized) + return; + if (!soundPlaying) + return; + + affirm(AudioOutputUnitStop(audioUnit)); + + soundPlaying = false; +} + +uint32_t kinc_a2_samples_per_second(void) { + return samples_per_second; +} diff --git a/armorcore/sources/backends/ios/kinc/backend/display.h b/armorcore/sources/backends/ios/kinc/backend/display.h new file mode 100644 index 000000000..857c8c0df --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/display.h @@ -0,0 +1,12 @@ +#pragma once + +namespace Kore { + namespace Display { + int count(); + int x(int index); + int y(int index); + int height(int index); + int width(int index); + bool isPrimary(int index); + } +} diff --git a/armorcore/sources/backends/ios/kinc/backend/display.m.h b/armorcore/sources/backends/ios/kinc/backend/display.m.h new file mode 100644 index 000000000..bf798bdf1 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/display.m.h @@ -0,0 +1,44 @@ +#import + +#include +#include + +void kinc_display_init(void) {} + +kinc_display_mode_t kinc_display_available_mode(int display, int mode) { + kinc_display_mode_t dm; + dm.width = kinc_window_width(0); + dm.height = kinc_window_height(0); + dm.frequency = 60; + dm.bits_per_pixel = 32; + return dm; +} + +int kinc_display_count_available_modes(int display) { + return 1; +} + +bool kinc_display_available(int display) { + return true; +} + +const char *kinc_display_name(int display) { + return "Display"; +} + +kinc_display_mode_t kinc_display_current_mode(int display) { + kinc_display_mode_t dm; + dm.width = kinc_window_width(0); + dm.height = kinc_window_height(0); + dm.frequency = (int)[[UIScreen mainScreen] maximumFramesPerSecond]; + dm.bits_per_pixel = 32; + return dm; +} + +int kinc_count_displays(void) { + return 1; +} + +int kinc_primary_display(void) { + return 0; +} diff --git a/armorcore/sources/backends/ios/kinc/backend/displaydata.h b/armorcore/sources/backends/ios/kinc/backend/displaydata.h new file mode 100644 index 000000000..184681291 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/displaydata.h @@ -0,0 +1,5 @@ +#pragma once + +struct DisplayData { + DisplayData(); +}; diff --git a/armorcore/sources/backends/ios/kinc/backend/ios.plist b/armorcore/sources/backends/ios/kinc/backend/ios.plist new file mode 100644 index 000000000..2a4a67295 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/ios.plist @@ -0,0 +1,53 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(BUNDLE_VERSION) + CFBundleSignature + ???? + CFBundleVersion + $(BUILD_VERSION) + LSRequiresIPhoneOS + + LSSupportsOpeningDocumentsInPlace + + UIFileSharingEnabled + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/armorcore/sources/backends/ios/kinc/backend/ios_file_dialog.h b/armorcore/sources/backends/ios/kinc/backend/ios_file_dialog.h new file mode 100644 index 000000000..399f40fd8 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/ios_file_dialog.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +void IOSFileDialogOpen(); +wchar_t *IOSFileDialogSave(); +void IOSDeleteFile(const char *path); diff --git a/armorcore/sources/backends/ios/kinc/backend/ios_file_dialog.m b/armorcore/sources/backends/ios/kinc/backend/ios_file_dialog.m new file mode 100644 index 000000000..a68053942 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/ios_file_dialog.m @@ -0,0 +1,25 @@ +#import "ios_file_dialog.h" + +#import + +void IOSFileDialogOpen() { + UIViewController *glViewController = [UIApplication sharedApplication].keyWindow.rootViewController; + UIDocumentPickerViewController *documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.data"] inMode:UIDocumentPickerModeOpen]; + documentPicker.delegate = glViewController; + documentPicker.modalPresentationStyle = UIModalPresentationFormSheet; + [glViewController presentViewController:documentPicker animated:YES completion:nil]; +} + +wchar_t* IOSFileDialogSave() { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + NSString *fileName = @"/untitled"; + NSString *filePath = [documentsDirectory stringByAppendingString:fileName]; + return (wchar_t*)[filePath cStringUsingEncoding:NSUTF32LittleEndianStringEncoding]; +} + +void IOSDeleteFile(const char *path) { + NSError *error = nil; + NSString *nspath = [NSString stringWithUTF8String:path]; + [[NSFileManager defaultManager] removeItemAtPath:nspath error:&error]; +} diff --git a/armorcore/sources/backends/ios/kinc/backend/iosunit.m b/armorcore/sources/backends/ios/kinc/backend/iosunit.m new file mode 100644 index 000000000..bad9d2245 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/iosunit.m @@ -0,0 +1,9 @@ +#include "GLView.m.h" +#include "GLViewController.m.h" +#include "KoreAppDelegate.m.h" +#include "audio.m.h" +#include "display.m.h" +#include "motion.m.h" +#include "mouse.c.h" +#include "system.m.h" +#include "window.c.h" diff --git a/armorcore/sources/backends/ios/kinc/backend/motion.h b/armorcore/sources/backends/ios/kinc/backend/motion.h new file mode 100644 index 000000000..cd13daceb --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/motion.h @@ -0,0 +1,11 @@ +#pragma once +#import +// #include + +@interface Motion : NSObject { + // Kt::GyroHandler* gyroHandler; +} + +// + (Motion*)the; + +@end diff --git a/armorcore/sources/backends/ios/kinc/backend/motion.m.h b/armorcore/sources/backends/ios/kinc/backend/motion.m.h new file mode 100644 index 000000000..f50015556 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/motion.m.h @@ -0,0 +1,26 @@ +#import "motion.h" + +@implementation Motion +/* +Motion* the = [[Motion alloc]init]; ++ (Motion*) the {return the;}; + +-(void)configureAccelerometerWithGyroHandler: (Kt::GyroHandler*) gyroHandler + AndUpdateInterval: (int) updateInterval +{ + UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer]; + accelerometer.updateInterval = 1 / updateInterval; + + accelerometer.delegate = self; + self->gyroHandler = gyroHandler; +} + +- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration{ + //gyroHandler->updateAccelerometerData(acceleration.x, acceleration.y, acceleration.z); +} + +void Kt::GyroHandler::configAccelerometer(int updateInterval){ + [[Motion the]configureAccelerometerWithGyroHandler:this AndUpdateInterval:updateInterval]; +}; +*/ +@end diff --git a/armorcore/sources/backends/ios/kinc/backend/mouse.c.h b/armorcore/sources/backends/ios/kinc/backend/mouse.c.h new file mode 100644 index 000000000..7b75836d4 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/mouse.c.h @@ -0,0 +1,19 @@ +#include + +void kinc_internal_mouse_lock(int window) {} + +void kinc_internal_mouse_unlock(void) {} + +bool kinc_mouse_can_lock(void) { + return false; +} + +void kinc_mouse_show(void) {} + +void kinc_mouse_hide(void) {} + +void kinc_mouse_set_position(int window, int x, int y) {} + +void kinc_mouse_get_position(int window, int *x, int *y) {} + +void kinc_mouse_set_cursor(int cursor_index) {} diff --git a/armorcore/sources/backends/ios/kinc/backend/system.m.h b/armorcore/sources/backends/ios/kinc/backend/system.m.h new file mode 100644 index 000000000..c675cea66 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/system.m.h @@ -0,0 +1,184 @@ +#import "KoreAppDelegate.h" + +#include +#include +#include +#include +#include +#include + +#import +#import + +bool withAutoreleasepool(bool (*f)(void)) { + @autoreleasepool { + return f(); + } +} + +static bool keyboardshown = false; + +const char *iphonegetresourcepath(void) { + return [[[NSBundle mainBundle] resourcePath] cStringUsingEncoding:1]; +} + +bool kinc_internal_handle_messages(void) { + SInt32 result; + do { + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, TRUE); + } while (result == kCFRunLoopRunHandledSource); + return true; +} + +void kinc_set_keep_screen_on(bool on) {} + +void showKeyboard(void); +void hideKeyboard(void); + +void kinc_keyboard_show(void) { + keyboardshown = true; + showKeyboard(); +} + +void kinc_keyboard_hide(void) { + keyboardshown = false; + hideKeyboard(); +} + +bool kinc_keyboard_active(void) { + return keyboardshown; +} + +void loadURL(const char *url); + +void kinc_load_url(const char *url) { + loadURL(url); +} + +// On iOS you can't set the length of the vibration. +void kinc_vibrate(int ms) { + AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); +}; + +static char language[3]; + +const char *kinc_language(void) { + NSString *nsstr = [[NSLocale preferredLanguages] objectAtIndex:0]; + const char *lang = [nsstr UTF8String]; + language[0] = lang[0]; + language[1] = lang[1]; + language[2] = 0; + return language; +} + +// called on rotation event +void KoreUpdateKeyboard(void) { + if (keyboardshown) { + hideKeyboard(); + showKeyboard(); + } + else { + hideKeyboard(); + } +} + +void kinc_internal_shutdown(void) {} + +int kinc_init(const char *name, int width, int height, struct kinc_window_options *win, struct kinc_framebuffer_options *frame) { + kinc_window_options_t defaultWin; + if (win == NULL) { + kinc_window_options_set_defaults(&defaultWin); + win = &defaultWin; + } + kinc_framebuffer_options_t defaultFrame; + if (frame == NULL) { + kinc_framebuffer_options_set_defaults(&defaultFrame); + frame = &defaultFrame; + } + kinc_g4_internal_init(); + kinc_g4_internal_init_window(0, frame->depth_bits, frame->stencil_bits, true); + + return 0; +} + +void endGL(void); + +void swapBuffersiOS(void) { + endGL(); +} + +static char sysid[512]; + +const char *kinc_system_id(void) { + const char *name = [[[UIDevice currentDevice] name] UTF8String]; + const char *vendorId = [[[[UIDevice currentDevice] identifierForVendor] UUIDString] UTF8String]; + strcpy(sysid, name); + strcat(sysid, "-"); + strcat(sysid, vendorId); + return sysid; +} + +static const char *getSavePath(void) { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); + NSString *resolvedPath = [paths objectAtIndex:0]; + NSString *appName = [NSString stringWithUTF8String:kinc_application_name()]; + resolvedPath = [resolvedPath stringByAppendingPathComponent:appName]; + + NSFileManager *fileMgr = [[NSFileManager alloc] init]; + + NSError *error; + [fileMgr createDirectoryAtPath:resolvedPath withIntermediateDirectories:YES attributes:nil error:&error]; + + resolvedPath = [resolvedPath stringByAppendingString:@"/"]; + return [resolvedPath cStringUsingEncoding:1]; +} + +const char *kinc_internal_save_path(void) { + return getSavePath(); +} + +static const char *videoFormats[] = {"mp4", NULL}; + +const char **kinc_video_formats(void) { + return videoFormats; +} + +#include + +double kinc_frequency(void) { + mach_timebase_info_data_t info; + mach_timebase_info(&info); + return (double)info.denom / (double)info.numer / 1e-9; +} + +kinc_ticks_t kinc_timestamp(void) { + kinc_ticks_t time = mach_absolute_time(); + return time; +} + +void kinc_login(void) {} + +void kinc_unlock_achievement(int id) {} + +const char *kinc_gamepad_vendor(int gamepad) { + return "nobody"; +} + +const char *kinc_gamepad_product_name(int gamepad) { + return "none"; +} + +bool kinc_gamepad_connected(int num) { + return true; +} + +void kinc_gamepad_rumble(int gamepad, float left, float right) {} + +int main(int argc, char *argv[]) { + int retVal = 0; + @autoreleasepool { + [KoreAppDelegate description]; // otherwise removed by the linker + retVal = UIApplicationMain(argc, argv, nil, @"KoreAppDelegate"); + } + return retVal; +} diff --git a/armorcore/sources/backends/ios/kinc/backend/window.c.h b/armorcore/sources/backends/ios/kinc/backend/window.c.h new file mode 100644 index 000000000..f564d95aa --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/window.c.h @@ -0,0 +1,71 @@ +#include +#include +#include + +static void (*resizeCallback)(int x, int y, void *data) = NULL; +static void *resizeCallbackData = NULL; + +int kinc_window_x(int window) { + return 0; +} + +int kinc_window_y(int window) { + return 0; +} + +int kinc_count_windows(void) { + return 1; +} + +void kinc_window_resize(int window, int width, int height) {} + +void kinc_window_move(int window, int x, int y) {} + +void kinc_internal_change_framebuffer(int window, struct kinc_framebuffer_options *frame); + +void kinc_window_change_framebuffer(int window, struct kinc_framebuffer_options *frame) { + kinc_internal_change_framebuffer(0, frame); +} + +#ifdef KINC_METAL +void kinc_internal_change_framebuffer(int window, struct kinc_framebuffer_options *frame) {} +#endif + +void kinc_window_change_features(int window, int features) {} + +void kinc_window_change_mode(int window, kinc_window_mode_t mode) {} + +void kinc_window_destroy(int window) {} + +void kinc_window_show(int window) {} + +void kinc_window_hide(int window) {} + +void kinc_window_set_title(int window, const char *title) {} + +int kinc_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) { + return 0; +} + +void kinc_window_set_resize_callback(int window, void (*callback)(int x, int y, void *data), void *data) { + resizeCallback = callback; + resizeCallbackData = data; +} + +void kinc_internal_call_resize_callback(int window, int width, int height) { + if (resizeCallback != NULL) { + resizeCallback(width, height, resizeCallbackData); + } +} + +void kinc_window_set_ppi_changed_callback(int window, void (*callback)(int ppi, void *data), void *data) {} + +void kinc_window_set_close_callback(int window, bool (*callback)(void *), void *data) {} + +kinc_window_mode_t kinc_window_get_mode(int window) { + return KINC_WINDOW_MODE_FULLSCREEN; +} + +int kinc_window_display(int window) { + return 0; +} diff --git a/armorcore/sources/backends/ios/kinc/backend/windowdata.h b/armorcore/sources/backends/ios/kinc/backend/windowdata.h new file mode 100644 index 000000000..191fba2c6 --- /dev/null +++ b/armorcore/sources/backends/ios/kinc/backend/windowdata.h @@ -0,0 +1,5 @@ +#pragma once + +struct WindowData { + WindowData(); +}; diff --git a/armorcore/sources/backends/license.txt b/armorcore/sources/backends/license.txt new file mode 100644 index 000000000..4d2ae2a58 --- /dev/null +++ b/armorcore/sources/backends/license.txt @@ -0,0 +1,19 @@ +Copyright (c) 2024 the Kinc Development Team + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. diff --git a/armorcore/sources/backends/linux/kinc/backend/display.c.h b/armorcore/sources/backends/linux/kinc/backend/display.c.h new file mode 100644 index 000000000..9f98e41c3 --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/display.c.h @@ -0,0 +1,43 @@ +#include "funcs.h" +#include + +void kinc_display_init() { + static bool display_initialized = false; + if (display_initialized) { + return; + } + + kinc_linux_init_procs(); + if (procs.display_init != NULL) { + procs.display_init(); + display_initialized = true; + } +} + +kinc_display_mode_t kinc_display_available_mode(int display, int mode) { + return procs.display_available_mode(display, mode); +} + +int kinc_display_count_available_modes(int display) { + return procs.display_count_available_modes(display); +} + +bool kinc_display_available(int display) { + return procs.display_available(display); +} + +const char *kinc_display_name(int display) { + return procs.display_name(display); +} + +kinc_display_mode_t kinc_display_current_mode(int display) { + return procs.display_current_mode(display); +} + +int kinc_primary_display(void) { + return procs.display_primary(); +} + +int kinc_count_displays(void) { + return procs.count_displays(); +} diff --git a/armorcore/sources/backends/linux/kinc/backend/funcs.h b/armorcore/sources/backends/linux/kinc/backend/funcs.h new file mode 100644 index 000000000..cf66fb8c8 --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/funcs.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#ifdef KINC_EGL +#define EGL_NO_PLATFORM_SPECIFIC_TYPES +#include +#endif +#ifdef KINC_VULKAN +#include +#endif + +struct linux_procs { + bool (*handle_messages)(void); + void (*shutdown)(void); + + void (*display_init)(void); + kinc_display_mode_t (*display_available_mode)(int display, int mode); + int (*display_count_available_modes)(int display); + bool (*display_available)(int display_index); + const char *(*display_name)(int display_index); + kinc_display_mode_t (*display_current_mode)(int display_index); + int (*display_primary)(void); + int (*count_displays)(void); + + int (*window_create)(kinc_window_options_t *window_options, kinc_framebuffer_options_t *framebuffer_options); + void (*window_destroy)(int window_index); + int (*window_display)(int window_index); + void (*window_show)(int window_index); + void (*window_hide)(int window_index); + void (*window_set_title)(int window_index, const char *title); + void (*window_change_mode)(int window_index, kinc_window_mode_t mode); + kinc_window_mode_t (*window_get_mode)(int window_index); + void (*window_move)(int window_index, int x, int y); + void (*window_resize)(int window_index, int width, int height); + int (*window_x)(int window_index); + int (*window_y)(int window_index); + int (*window_width)(int window_index); + int (*window_height)(int window_index); + int (*count_windows)(void); + + bool (*mouse_can_lock)(void); + bool (*mouse_is_locked)(void); + void (*mouse_lock)(int window); + void (*mouse_unlock)(void); + void (*mouse_show)(void); + void (*mouse_hide)(void); + void (*mouse_set_position)(int window, int x, int y); + void (*mouse_get_position)(int window, int *x, int *y); + void (*mouse_set_cursor)(int cursor); + + void (*copy_to_clipboard)(const char *text); +#ifdef KINC_EGL + EGLDisplay (*egl_get_display)(void); + EGLNativeWindowType (*egl_get_native_window)(EGLDisplay display, EGLConfig config, int window_index); +#endif +#ifdef KINC_VULKAN + void (*vulkan_get_instance_extensions)(const char **extensions, int *count, int max); + VkResult (*vulkan_create_surface)(VkInstance instance, int window_index, VkSurfaceKHR *surface); + VkBool32 (*vulkan_get_physical_device_presentation_support)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex); +#endif +}; + +extern struct linux_procs procs; + +void kinc_linux_init_procs(); \ No newline at end of file diff --git a/armorcore/sources/backends/linux/kinc/backend/gamepad.c.h b/armorcore/sources/backends/linux/kinc/backend/gamepad.c.h new file mode 100644 index 000000000..0b3a7405a --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/gamepad.c.h @@ -0,0 +1,199 @@ +#include "gamepad.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +struct HIDGamepad { + int idx; + char gamepad_dev_name[256]; + char name[385]; + int file_descriptor; + bool connected; + struct js_event gamepadEvent; +}; + +static void HIDGamepad_open(struct HIDGamepad *pad) { + pad->file_descriptor = open(pad->gamepad_dev_name, O_RDONLY | O_NONBLOCK); + if (pad->file_descriptor < 0) { + pad->connected = false; + } + else { + pad->connected = true; + + char buf[128]; + if (ioctl(pad->file_descriptor, JSIOCGNAME(sizeof(buf)), buf) < 0) { + strncpy(buf, "Unknown", sizeof(buf)); + } + pad->name[0] = 0; + // snprintf(pad->name, sizeof(pad->name), "%s(%s)", buf, pad->gamepad_dev_name); // TODO: valgrind error + kinc_internal_gamepad_trigger_connect(pad->idx); + } +} + +static void HIDGamepad_init(struct HIDGamepad *pad, int index) { + pad->file_descriptor = -1; + pad->connected = false; + pad->gamepad_dev_name[0] = 0; + if (index >= 0 && index < 12) { + pad->idx = index; + snprintf(pad->gamepad_dev_name, sizeof(pad->gamepad_dev_name), "/dev/input/js%d", pad->idx); + HIDGamepad_open(pad); + } +} + +static void HIDGamepad_close(struct HIDGamepad *pad) { + if (pad->connected) { + kinc_internal_gamepad_trigger_disconnect(pad->idx); + close(pad->file_descriptor); + pad->file_descriptor = -1; + pad->connected = false; + } +} + +void HIDGamepad_processEvent(struct HIDGamepad *pad, struct js_event e) { + switch (e.type) { + case JS_EVENT_BUTTON: + kinc_internal_gamepad_trigger_button(pad->idx, e.number, e.value); + break; + case JS_EVENT_AXIS: { + float value = e.number % 2 == 0 ? e.value : -e.value; + kinc_internal_gamepad_trigger_axis(pad->idx, e.number, value / 32767.0f); + break; + } + default: + break; + } +} + +void HIDGamepad_update(struct HIDGamepad *pad) { + if (pad->connected) { + while (read(pad->file_descriptor, &pad->gamepadEvent, sizeof(pad->gamepadEvent)) > 0) { + HIDGamepad_processEvent(pad, pad->gamepadEvent); + } + } +} + +struct HIDGamepadUdevHelper { + struct udev *udevPtr; + struct udev_monitor *udevMonitorPtr; + int udevMonitorFD; +}; + +static struct HIDGamepadUdevHelper udev_helper; + +static struct HIDGamepad gamepads[KINC_GAMEPAD_MAX_COUNT]; + +static void HIDGamepadUdevHelper_openOrCloseGamepad(struct HIDGamepadUdevHelper *helper, struct udev_device *dev) { + const char *action = udev_device_get_action(dev); + if (!action) + action = "add"; + + const char *joystickDevnodeName = strstr(udev_device_get_devnode(dev), "js"); + + if (joystickDevnodeName) { + int joystickDevnodeIndex; + sscanf(joystickDevnodeName, "js%d", &joystickDevnodeIndex); + + if (!strcmp(action, "add")) { + HIDGamepad_open(&gamepads[joystickDevnodeIndex]); + } + + if (!strcmp(action, "remove")) { + HIDGamepad_close(&gamepads[joystickDevnodeIndex]); + } + } +} + +static void HIDGamepadUdevHelper_processDevice(struct HIDGamepadUdevHelper *helper, struct udev_device *dev) { + if (dev) { + if (udev_device_get_devnode(dev)) + HIDGamepadUdevHelper_openOrCloseGamepad(helper, dev); + + udev_device_unref(dev); + } +} + +static void HIDGamepadUdevHelper_init(struct HIDGamepadUdevHelper *helper) { + struct udev *udevPtrNew = udev_new(); + + // enumerate + struct udev_enumerate *enumerate = udev_enumerate_new(udevPtrNew); + + udev_enumerate_add_match_subsystem(enumerate, "input"); + udev_enumerate_scan_devices(enumerate); + + struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate); + struct udev_list_entry *entry; + + udev_list_entry_foreach(entry, devices) { + const char *path = udev_list_entry_get_name(entry); + struct udev_device *dev = udev_device_new_from_syspath(udevPtrNew, path); + HIDGamepadUdevHelper_processDevice(helper, dev); + } + + udev_enumerate_unref(enumerate); + + // setup mon + helper->udevMonitorPtr = udev_monitor_new_from_netlink(udevPtrNew, "udev"); + + udev_monitor_filter_add_match_subsystem_devtype(helper->udevMonitorPtr, "input", NULL); + udev_monitor_enable_receiving(helper->udevMonitorPtr); + + helper->udevMonitorFD = udev_monitor_get_fd(helper->udevMonitorPtr); + + helper->udevPtr = udevPtrNew; +} + +static void HIDGamepadUdevHelper_update(struct HIDGamepadUdevHelper *helper) { + fd_set fds; + FD_ZERO(&fds); + FD_SET(helper->udevMonitorFD, &fds); + + if (FD_ISSET(helper->udevMonitorFD, &fds)) { + struct udev_device *dev = udev_monitor_receive_device(helper->udevMonitorPtr); + HIDGamepadUdevHelper_processDevice(helper, dev); + } +} + +static void HIDGamepadUdevHelper_close(struct HIDGamepadUdevHelper *helper) { + udev_unref(helper->udevPtr); +} + +void kinc_linux_initHIDGamepads() { + for (int i = 0; i < KINC_GAMEPAD_MAX_COUNT; ++i) { + HIDGamepad_init(&gamepads[i], i); + } + HIDGamepadUdevHelper_init(&udev_helper); +} + +void kinc_linux_updateHIDGamepads() { + HIDGamepadUdevHelper_update(&udev_helper); + for (int i = 0; i < KINC_GAMEPAD_MAX_COUNT; ++i) { + HIDGamepad_update(&gamepads[i]); + } +} + +void kinc_linux_closeHIDGamepads() { + HIDGamepadUdevHelper_close(&udev_helper); +} + +const char *kinc_gamepad_vendor(int gamepad) { + return "Linux gamepad"; +} + +const char *kinc_gamepad_product_name(int gamepad) { + return gamepad >= 0 && gamepad < KINC_GAMEPAD_MAX_COUNT ? gamepads[gamepad].name : ""; +} + +bool kinc_gamepad_connected(int gamepad) { + return gamepad >= 0 && gamepad < KINC_GAMEPAD_MAX_COUNT && gamepads[gamepad].connected; +} + +void kinc_gamepad_rumble(int gamepad, float left, float right) {} diff --git a/armorcore/sources/backends/linux/kinc/backend/gamepad.h b/armorcore/sources/backends/linux/kinc/backend/gamepad.h new file mode 100644 index 000000000..ea5c66e14 --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/gamepad.h @@ -0,0 +1,5 @@ +#pragma once + +void kinc_linux_initHIDGamepads(); +void kinc_linux_updateHIDGamepads(); +void kinc_linux_closeHIDGamepads(); diff --git a/armorcore/sources/backends/linux/kinc/backend/linuxunit.c b/armorcore/sources/backends/linux/kinc/backend/linuxunit.c new file mode 100644 index 000000000..73b7e89fb --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/linuxunit.c @@ -0,0 +1,160 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // memfd_create and mkostemp +#endif +#include "funcs.h" +#include +#include + +static void load_lib(void **lib, const char *name) { + char libname[64]; + sprintf(libname, "lib%s.so", name); + *lib = dlopen(libname, RTLD_LAZY); + if (*lib != NULL) { + return; + } + // Ubuntu and Fedora only ship libFoo.so.major by default, so look for those. + for (int i = 0; i < 10; i++) { + sprintf(libname, "lib%s.so.%i", name, i); + *lib = dlopen(libname, RTLD_LAZY); + if (*lib != NULL) { + return; + } + } +} + +#ifndef KINC_NO_WAYLAND +#include "wayland/display.c.h" +#include "wayland/system.c.h" +#include "wayland/window.c.h" +#endif + +#ifndef KINC_NO_X11 +#include "x11/display.c.h" +#include "x11/system.c.h" +#include "x11/window.c.h" +#endif + +struct linux_procs procs = {0}; + +void kinc_linux_init_procs() { + if (procs.window_create != NULL) { + return; + } +#ifndef KINC_NO_WAYLAND + if (kinc_wayland_init()) { + procs.handle_messages = kinc_wayland_handle_messages; + procs.shutdown = kinc_wayland_shutdown; + + procs.window_create = kinc_wayland_window_create; + procs.window_width = kinc_wayland_window_width; + procs.window_height = kinc_wayland_window_height; + procs.window_x = kinc_wayland_window_x; + procs.window_y = kinc_wayland_window_y; + procs.window_destroy = kinc_wayland_window_destroy; + procs.window_change_mode = kinc_wayland_window_change_mode; + procs.window_get_mode = kinc_wayland_window_get_mode; + procs.window_set_title = kinc_wayland_window_set_title; + procs.window_display = kinc_wayland_window_display; + procs.window_move = kinc_wayland_window_move; + procs.window_resize = kinc_wayland_window_resize; + procs.window_show = kinc_wayland_window_show; + procs.window_hide = kinc_wayland_window_hide; + procs.count_windows = kinc_wayland_count_windows; + + procs.mouse_can_lock = kinc_wl_mouse_can_lock; + procs.mouse_lock = kinc_wl_mouse_lock; + procs.mouse_unlock = kinc_wl_mouse_unlock; + procs.mouse_show = kinc_wl_mouse_show; + procs.mouse_hide = kinc_wl_mouse_hide; + procs.mouse_set_position = kinc_wl_mouse_set_position; + procs.mouse_get_position = kinc_wl_mouse_get_position; + procs.mouse_set_cursor = kinc_wl_mouse_set_cursor; + + procs.display_init = kinc_wayland_display_init; + procs.display_available = kinc_wayland_display_available; + procs.display_available_mode = kinc_wayland_display_available_mode; + procs.display_count_available_modes = kinc_wayland_display_count_available_modes; + procs.display_current_mode = kinc_wayland_display_current_mode; + procs.display_name = kinc_wayland_display_name; + procs.display_primary = kinc_wayland_display_primary; + procs.count_displays = kinc_wayland_count_displays; + + procs.copy_to_clipboard = kinc_wayland_copy_to_clipboard; +#ifdef KINC_EGL + procs.egl_get_display = kinc_wayland_egl_get_display; + procs.egl_get_native_window = kinc_wayland_egl_get_native_window; +#endif +#ifdef KINC_VULKAN + procs.vulkan_create_surface = kinc_wayland_vulkan_create_surface; + procs.vulkan_get_instance_extensions = kinc_wayland_vulkan_get_instance_extensions; + procs.vulkan_get_physical_device_presentation_support = kinc_wayland_vulkan_get_physical_device_presentation_support; +#endif + } + else +#endif +#ifndef KINC_NO_X11 + if (kinc_x11_init()) { + procs.handle_messages = kinc_x11_handle_messages; + procs.shutdown = kinc_x11_shutdown; + + procs.window_create = kinc_x11_window_create; + procs.window_width = kinc_x11_window_width; + procs.window_height = kinc_x11_window_height; + procs.window_x = kinc_x11_window_x; + procs.window_y = kinc_x11_window_y; + procs.window_destroy = kinc_x11_window_destroy; + procs.window_change_mode = kinc_x11_window_change_mode; + procs.window_get_mode = kinc_x11_window_get_mode; + procs.window_set_title = kinc_x11_window_set_title; + procs.window_display = kinc_x11_window_display; + procs.window_move = kinc_x11_window_move; + procs.window_resize = kinc_x11_window_resize; + procs.window_show = kinc_x11_window_show; + procs.window_hide = kinc_x11_window_hide; + procs.count_windows = kinc_x11_count_windows; + + procs.display_init = kinc_x11_display_init; + procs.display_available = kinc_x11_display_available; + procs.display_available_mode = kinc_x11_display_available_mode; + procs.display_count_available_modes = kinc_x11_display_count_available_modes; + procs.display_current_mode = kinc_x11_display_current_mode; + procs.display_name = kinc_x11_display_name; + procs.display_primary = kinc_x11_display_primary; + procs.count_displays = kinc_x11_count_displays; + + procs.mouse_can_lock = kinc_x11_mouse_can_lock; + procs.mouse_lock = kinc_x11_mouse_lock; + procs.mouse_unlock = kinc_x11_mouse_unlock; + procs.mouse_show = kinc_x11_mouse_show; + procs.mouse_hide = kinc_x11_mouse_hide; + procs.mouse_set_position = kinc_x11_mouse_set_position; + procs.mouse_get_position = kinc_x11_mouse_get_position; + procs.mouse_set_cursor = kinc_x11_mouse_set_cursor; + + procs.copy_to_clipboard = kinc_x11_copy_to_clipboard; +#ifdef KINC_EGL + procs.egl_get_display = kinc_x11_egl_get_display; + procs.egl_get_native_window = kinc_x11_egl_get_native_window; +#endif +#ifdef KINC_VULKAN + procs.vulkan_create_surface = kinc_x11_vulkan_create_surface; + procs.vulkan_get_instance_extensions = kinc_x11_vulkan_get_instance_extensions; + procs.vulkan_get_physical_device_presentation_support = kinc_x11_vulkan_get_physical_device_presentation_support; +#endif + } + else +#endif + { + kinc_log(KINC_LOG_LEVEL_ERROR, "Neither wayland nor X11 found."); + } +} + +#include "display.c.h" +#ifndef __FreeBSD__ +#include "gamepad.c.h" +#endif +#include "mouse.c.h" +#include "sound.c.h" +#include "system.c.h" +#include "video.c.h" +#include "window.c.h" diff --git a/armorcore/sources/backends/linux/kinc/backend/mouse.c.h b/armorcore/sources/backends/linux/kinc/backend/mouse.c.h new file mode 100644 index 000000000..c854d17b7 --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/mouse.c.h @@ -0,0 +1,36 @@ +#include "funcs.h" +#include + +void kinc_internal_mouse_lock(int window) { + procs.mouse_lock(window); +} + +void kinc_internal_mouse_unlock() { + procs.mouse_unlock(); +} + +bool kinc_mouse_can_lock(void) { + return true; +} + +bool _mouseHidden = false; + +void kinc_mouse_show() { + procs.mouse_show(); +} + +void kinc_mouse_hide() { + procs.mouse_hide(); +} + +void kinc_mouse_set_cursor(int cursorIndex) { + procs.mouse_set_cursor(cursorIndex); +} + +void kinc_mouse_set_position(int window, int x, int y) { + procs.mouse_set_position(window, x, y); +} + +void kinc_mouse_get_position(int window, int *x, int *y) { + procs.mouse_get_position(window, x, y); +} diff --git a/armorcore/sources/backends/linux/kinc/backend/sound.c.h b/armorcore/sources/backends/linux/kinc/backend/sound.c.h new file mode 100644 index 000000000..6a26044a3 --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/sound.c.h @@ -0,0 +1,233 @@ +#include + +#include +#include +#include +#include +#include +#include + +// apt-get install libasound2-dev + +kinc_a2_buffer_t a2_buffer; + +pthread_t threadid; +bool audioRunning = false; +snd_pcm_t *playback_handle; +short buf[4096 * 4]; + +static unsigned int samples_per_second = 44100; + +uint32_t kinc_a2_samples_per_second(void) { + return samples_per_second; +} + +void copySample(void *buffer) { + float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location]; + float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location]; + a2_buffer.read_location += 1; + if (a2_buffer.read_location >= a2_buffer.data_size) { + a2_buffer.read_location = 0; + } + ((int16_t *)buffer)[0] = (int16_t)(left_value * 32767); + ((int16_t *)buffer)[1] = (int16_t)(right_value * 32767); +} + +int playback_callback(snd_pcm_sframes_t nframes) { + int err = 0; + if (kinc_a2_internal_callback(&a2_buffer, nframes)) { + int ni = 0; + while (ni < nframes) { + int i = 0; + for (; ni < nframes && i < 4096 * 2; ++i, ++ni) { + copySample(&buf[i * 2]); + } + int err2; + if ((err2 = snd_pcm_writei(playback_handle, buf, i)) < 0) { + fprintf(stderr, "write failed (%s)\n", snd_strerror(err2)); + } + err += err2; + } + } + return err; +} + +bool tryToRecover(snd_pcm_t *handle, int errorCode) { + switch (-errorCode) { + case EINTR: + case EPIPE: + case ESPIPE: +#if !defined(__FreeBSD__) + case ESTRPIPE: +#endif + { + int recovered = snd_pcm_recover(playback_handle, errorCode, 1); + + if (recovered != 0) { + fprintf(stderr, "unable to recover from ALSA error code=%i\n", errorCode); + return false; + } + else { + fprintf(stdout, "recovered from ALSA error code=%i\n", errorCode); + return true; + } + } + default: + fprintf(stderr, "unhandled ALSA error code=%i\n", errorCode); + return false; + } +} + +void *doAudio(void *arg) { + snd_pcm_hw_params_t *hw_params; + snd_pcm_sw_params_t *sw_params; + snd_pcm_sframes_t frames_to_deliver; + int err; + + if ((err = snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) { + fprintf(stderr, "cannot open audio device default (%s)\n", snd_strerror(err)); + return NULL; + } + + if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) { + fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n", snd_strerror(err)); + return NULL; + } + + if ((err = snd_pcm_hw_params_any(playback_handle, hw_params)) < 0) { + fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n", snd_strerror(err)); + return NULL; + } + + if ((err = snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + fprintf(stderr, "cannot set access type (%s)\n", snd_strerror(err)); + return NULL; + } + + if ((err = snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { + fprintf(stderr, "cannot set sample format (%s)\n", snd_strerror(err)); + return NULL; + } + + int dir = 0; + if ((err = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &samples_per_second, &dir)) < 0) { + fprintf(stderr, "cannot set sample rate (%s)\n", snd_strerror(err)); + return NULL; + } + + if ((err = snd_pcm_hw_params_set_channels(playback_handle, hw_params, 2)) < 0) { + fprintf(stderr, "cannot set channel count (%s)\n", snd_strerror(err)); + return NULL; + } + + snd_pcm_uframes_t bufferSize = samples_per_second / 8; + if (((err = snd_pcm_hw_params_set_buffer_size(playback_handle, hw_params, bufferSize)) < 0 && + (snd_pcm_hw_params_set_buffer_size_near(playback_handle, hw_params, &bufferSize)) < 0)) { + fprintf(stderr, "cannot set buffer size (%s)\n", snd_strerror(err)); + return NULL; + } + + if ((err = snd_pcm_hw_params(playback_handle, hw_params)) < 0) { + fprintf(stderr, "cannot set parameters (%s)\n", snd_strerror(err)); + return NULL; + } + + snd_pcm_hw_params_free(hw_params); + + /* tell ALSA to wake us up whenever 4096 or more frames + of playback data can be delivered. Also, tell + ALSA that we'll start the device ourselves. + */ + + if ((err = snd_pcm_sw_params_malloc(&sw_params)) < 0) { + fprintf(stderr, "cannot allocate software parameters structure (%s)\n", snd_strerror(err)); + return NULL; + } + if ((err = snd_pcm_sw_params_current(playback_handle, sw_params)) < 0) { + fprintf(stderr, "cannot initialize software parameters structure (%s)\n", snd_strerror(err)); + return NULL; + } + if ((err = snd_pcm_sw_params_set_avail_min(playback_handle, sw_params, 4096)) < 0) { + fprintf(stderr, "cannot set minimum available count (%s)\n", snd_strerror(err)); + return NULL; + } + if ((err = snd_pcm_sw_params_set_start_threshold(playback_handle, sw_params, 0U)) < 0) { + fprintf(stderr, "cannot set start mode (%s)\n", snd_strerror(err)); + return NULL; + } + if ((err = snd_pcm_sw_params(playback_handle, sw_params)) < 0) { + fprintf(stderr, "cannot set software parameters (%s)\n", snd_strerror(err)); + return NULL; + } + + /* the interface will interrupt the kernel every 4096 frames, and ALSA + will wake up this program very soon after that. + */ + + if ((err = snd_pcm_prepare(playback_handle)) < 0) { + fprintf(stderr, "cannot prepare audio interface for use (%s)\n", snd_strerror(err)); + return NULL; + } + + while (audioRunning) { + + /* wait till the interface is ready for data, or 1 second + has elapsed. + */ + + if ((err = snd_pcm_wait(playback_handle, 1000)) < 0) { + fprintf(stderr, "poll failed (%s)\n", strerror(errno)); + break; + } + + /* find out how much space is available for playback data */ + + if ((frames_to_deliver = snd_pcm_avail_update(playback_handle)) < 0) { + if (!tryToRecover(playback_handle, frames_to_deliver)) { + // break; + } + } + else { + // frames_to_deliver = frames_to_deliver > 4096 ? 4096 : frames_to_deliver; + + /* deliver the data */ + + int delivered = playback_callback(frames_to_deliver); + + if (delivered != frames_to_deliver) { + fprintf(stderr, "playback callback failed (delivered %i / %ld frames)\n", delivered, frames_to_deliver); + // break; + } + } + } + + snd_pcm_close(playback_handle); + return NULL; +} + +static bool initialized = false; + +void kinc_a2_init() { + if (initialized) { + return; + } + + kinc_a2_internal_init(); + initialized = true; + + a2_buffer.read_location = 0; + a2_buffer.write_location = 0; + a2_buffer.data_size = 128 * 1024; + a2_buffer.channel_count = 2; + a2_buffer.channels[0] = (float *)malloc(a2_buffer.data_size * sizeof(float)); + a2_buffer.channels[1] = (float *)malloc(a2_buffer.data_size * sizeof(float)); + + audioRunning = true; + pthread_create(&threadid, NULL, &doAudio, NULL); +} + +void kinc_a2_update() {} + +void kinc_a2_shutdown() { + audioRunning = false; +} diff --git a/armorcore/sources/backends/linux/kinc/backend/system.c.h b/armorcore/sources/backends/linux/kinc/backend/system.c.h new file mode 100644 index 000000000..1c2e7aa71 --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/system.c.h @@ -0,0 +1,427 @@ +#include "kinc/graphics4/graphics.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __FreeBSD__ +#include "gamepad.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "funcs.h" + +bool kinc_internal_handle_messages() { + if (!procs.handle_messages()) { + return false; + } +#ifndef __FreeBSD__ + kinc_linux_updateHIDGamepads(); +#endif // TODO: add #else with proper call to FreeBSD backend impl + return true; +} + +const char *kinc_system_id() { + return "Linux"; +} + +void kinc_set_keep_screen_on(bool on) {} + +void kinc_keyboard_show() {} + +void kinc_keyboard_hide() {} + +bool kinc_keyboard_active() { + return true; +} + +void kinc_load_url(const char *url) { +#define MAX_COMMAND_BUFFER_SIZE 256 +#define HTTP "http://" +#define HTTPS "https://" + if (strncmp(url, HTTP, sizeof(HTTP) - 1) == 0 || strncmp(url, HTTPS, sizeof(HTTPS) - 1) == 0) { + char openUrlCommand[MAX_COMMAND_BUFFER_SIZE]; + snprintf(openUrlCommand, MAX_COMMAND_BUFFER_SIZE, "xdg-open %s", url); + int err = system(openUrlCommand); + if (err != 0) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Error opening url %s", url); + } + } +#undef HTTPS +#undef HTTP +#undef MAX_COMMAND_BUFFER_SIZE +} + +void kinc_vibrate(int ms) {} + +const char *kinc_language() { + return "en"; +} + +static char save[2000]; +static bool saveInitialized = false; + +const char *kinc_internal_save_path() { + // first check for an existing directory in $HOME + // if one exists, use it, else create one in $XDG_DATA_HOME + // See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + if (!saveInitialized) { + const char *homedir; + + if ((homedir = getenv("HOME")) == NULL) { + homedir = getpwuid(getuid())->pw_dir; + } + + strcpy(save, homedir); + strcat(save, "/."); + strcat(save, kinc_application_name()); + strcat(save, "/"); + struct stat st; + if (stat(save, &st) == 0) { + // use existing folder in $HOME + } + else { + // use XDG folder + const char *data_home; + if ((data_home = getenv("XDG_DATA_HOME")) == NULL) { + // $XDG_DATA_HOME is not defined, fall back to the default, $HOME/.local/share + strcpy(save, homedir); + strcat(save, "/.local/share/"); + } + else { + // use $XDG_DATA_HOME + strcpy(save, data_home); + if (data_home[strlen(data_home) - 1] != '/') { + strcat(save, "/"); + } + } + strcat(save, kinc_application_name()); + strcat(save, "/"); + int res = mkdir(save, 0700); + if (res != 0 && errno != EEXIST) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not create save directory '%s'. Error %d", save, errno); + } + } + + saveInitialized = true; + } + return save; +} + +static const char *videoFormats[] = {"ogv", NULL}; + +const char **kinc_video_formats() { + return videoFormats; +} + +#include +#include + +double kinc_frequency(void) { + return 1000000.0; +} + +static struct timeval start; + +kinc_ticks_t kinc_timestamp(void) { + struct timeval now; + gettimeofday(&now, NULL); + now.tv_sec -= start.tv_sec; + now.tv_usec -= start.tv_usec; + return (kinc_ticks_t)now.tv_sec * 1000000 + (kinc_ticks_t)now.tv_usec; +} + +void kinc_login() {} + +void kinc_unlock_achievement(int id) {} + +void kinc_linux_init_procs(); + +int kinc_init(const char *name, int width, int height, kinc_window_options_t *win, kinc_framebuffer_options_t *frame) { +#ifndef __FreeBSD__ + kinc_linux_initHIDGamepads(); +#endif // TODO: add #else with proper call to FreeBSD backend impl + + gettimeofday(&start, NULL); + kinc_linux_init_procs(); + kinc_display_init(); + + kinc_set_application_name(name); + + kinc_g4_internal_init(); + + kinc_window_options_t defaultWin; + if (win == NULL) { + kinc_window_options_set_defaults(&defaultWin); + win = &defaultWin; + } + kinc_framebuffer_options_t defaultFrame; + if (frame == NULL) { + kinc_framebuffer_options_set_defaults(&defaultFrame); + frame = &defaultFrame; + } + win->width = width; + win->height = height; + if (win->title == NULL) { + win->title = name; + } + + int window = kinc_window_create(win, frame); + + return window; +} + +void kinc_internal_shutdown() { + kinc_g4_internal_destroy(); +#ifndef __FreeBSD__ + kinc_linux_closeHIDGamepads(); +#endif // TODO: add #else with proper call to FreeBSD backend impl + procs.shutdown(); + kinc_internal_shutdown_callback(); +} + +#ifndef KINC_NO_MAIN +int main(int argc, char **argv) { + return kickstart(argc, argv); +} +#endif + +void kinc_copy_to_clipboard(const char *text) { + procs.copy_to_clipboard(text); +} + +static int parse_number_at_end_of_line(char *line) { + char *end = &line[strlen(line) - 2]; + int num = 0; + int multi = 1; + while (*end >= '0' && *end <= '9') { + num += (*end - '0') * multi; + multi *= 10; + --end; + } + return num; +} + +int kinc_cpu_cores(void) { + char line[1024]; + FILE *file = fopen("/proc/cpuinfo", "r"); + + if (file != NULL) { + int cores[1024]; + memset(cores, 0, sizeof(cores)); + + int cpu_count = 0; + int physical_id = -1; + int per_cpu_cores = -1; + int processor_count = 0; + + while (fgets(line, sizeof(line), file)) { + if (strncmp(line, "processor", 9) == 0) { + ++processor_count; + if (physical_id >= 0 && per_cpu_cores > 0) { + if (physical_id + 1 > cpu_count) { + cpu_count = physical_id + 1; + } + cores[physical_id] = per_cpu_cores; + physical_id = -1; + per_cpu_cores = -1; + } + } + else if (strncmp(line, "physical id", 11) == 0) { + physical_id = parse_number_at_end_of_line(line); + } + else if (strncmp(line, "cpu cores", 9) == 0) { + per_cpu_cores = parse_number_at_end_of_line(line); + } + } + fclose(file); + + if (physical_id >= 0 && per_cpu_cores > 0) { + if (physical_id + 1 > cpu_count) { + cpu_count = physical_id + 1; + } + cores[physical_id] = per_cpu_cores; + } + + int proper_cpu_count = 0; + for (int i = 0; i < cpu_count; ++i) { + proper_cpu_count += cores[i]; + } + + if (proper_cpu_count > 0) { + return proper_cpu_count; + } + else { + return processor_count == 0 ? 1 : processor_count; + } + } + else { + return 1; + }; +} + +int kinc_hardware_threads(void) { +#ifndef __FreeBSD__ + return sysconf(_SC_NPROCESSORS_ONLN); +#else + return kinc_cpu_cores(); +#endif +} + +#include + +int xkb_to_kinc(xkb_keysym_t symbol) { +#define KEY(xkb, kinc) \ + case xkb: \ + return kinc; + switch (symbol) { + KEY(XKB_KEY_Right, KINC_KEY_RIGHT) + KEY(XKB_KEY_Left, KINC_KEY_LEFT) + KEY(XKB_KEY_Up, KINC_KEY_UP) + KEY(XKB_KEY_Down, KINC_KEY_DOWN) + KEY(XKB_KEY_space, KINC_KEY_SPACE) + KEY(XKB_KEY_BackSpace, KINC_KEY_BACKSPACE) + KEY(XKB_KEY_Tab, KINC_KEY_TAB) + KEY(XKB_KEY_Return, KINC_KEY_RETURN) + KEY(XKB_KEY_Shift_L, KINC_KEY_SHIFT) + KEY(XKB_KEY_Shift_R, KINC_KEY_SHIFT) + KEY(XKB_KEY_Control_L, KINC_KEY_CONTROL) + KEY(XKB_KEY_Control_R, KINC_KEY_CONTROL) + KEY(XKB_KEY_Alt_L, KINC_KEY_ALT) + KEY(XKB_KEY_Alt_R, KINC_KEY_ALT) + KEY(XKB_KEY_Delete, KINC_KEY_DELETE) + KEY(XKB_KEY_comma, KINC_KEY_COMMA) + KEY(XKB_KEY_period, KINC_KEY_PERIOD) + KEY(XKB_KEY_bracketleft, KINC_KEY_OPEN_BRACKET) + KEY(XKB_KEY_bracketright, KINC_KEY_CLOSE_BRACKET) + KEY(XKB_KEY_braceleft, KINC_KEY_OPEN_CURLY_BRACKET) + KEY(XKB_KEY_braceright, KINC_KEY_CLOSE_CURLY_BRACKET) + KEY(XKB_KEY_parenleft, KINC_KEY_OPEN_PAREN) + KEY(XKB_KEY_parenright, KINC_KEY_CLOSE_PAREN) + KEY(XKB_KEY_backslash, KINC_KEY_BACK_SLASH) + KEY(XKB_KEY_apostrophe, KINC_KEY_QUOTE) + KEY(XKB_KEY_colon, KINC_KEY_COLON) + KEY(XKB_KEY_semicolon, KINC_KEY_SEMICOLON) + KEY(XKB_KEY_minus, KINC_KEY_HYPHEN_MINUS) + KEY(XKB_KEY_underscore, KINC_KEY_UNDERSCORE) + KEY(XKB_KEY_slash, KINC_KEY_SLASH) + KEY(XKB_KEY_bar, KINC_KEY_PIPE) + KEY(XKB_KEY_question, KINC_KEY_QUESTIONMARK) + KEY(XKB_KEY_less, KINC_KEY_LESS_THAN) + KEY(XKB_KEY_greater, KINC_KEY_GREATER_THAN) + KEY(XKB_KEY_asterisk, KINC_KEY_ASTERISK) + KEY(XKB_KEY_ampersand, KINC_KEY_AMPERSAND) + KEY(XKB_KEY_asciicircum, KINC_KEY_CIRCUMFLEX) + KEY(XKB_KEY_percent, KINC_KEY_PERCENT) + KEY(XKB_KEY_dollar, KINC_KEY_DOLLAR) + KEY(XKB_KEY_numbersign, KINC_KEY_HASH) + KEY(XKB_KEY_at, KINC_KEY_AT) + KEY(XKB_KEY_exclam, KINC_KEY_EXCLAMATION) + KEY(XKB_KEY_equal, KINC_KEY_EQUALS) + KEY(XKB_KEY_plus, KINC_KEY_ADD) + KEY(XKB_KEY_quoteleft, KINC_KEY_BACK_QUOTE) + KEY(XKB_KEY_quotedbl, KINC_KEY_DOUBLE_QUOTE) + KEY(XKB_KEY_asciitilde, KINC_KEY_TILDE) + KEY(XKB_KEY_Pause, KINC_KEY_PAUSE) + KEY(XKB_KEY_Scroll_Lock, KINC_KEY_SCROLL_LOCK) + KEY(XKB_KEY_Home, KINC_KEY_HOME) + KEY(XKB_KEY_Page_Up, KINC_KEY_PAGE_UP) + KEY(XKB_KEY_Page_Down, KINC_KEY_PAGE_DOWN) + KEY(XKB_KEY_End, KINC_KEY_END) + KEY(XKB_KEY_Insert, KINC_KEY_INSERT) + KEY(XKB_KEY_KP_Enter, KINC_KEY_RETURN) + KEY(XKB_KEY_KP_Multiply, KINC_KEY_MULTIPLY) + KEY(XKB_KEY_KP_Add, KINC_KEY_ADD) + KEY(XKB_KEY_KP_Subtract, KINC_KEY_SUBTRACT) + KEY(XKB_KEY_KP_Decimal, KINC_KEY_DECIMAL) + KEY(XKB_KEY_KP_Divide, KINC_KEY_DIVIDE) + KEY(XKB_KEY_KP_0, KINC_KEY_NUMPAD_0) + KEY(XKB_KEY_KP_1, KINC_KEY_NUMPAD_1) + KEY(XKB_KEY_KP_2, KINC_KEY_NUMPAD_2) + KEY(XKB_KEY_KP_3, KINC_KEY_NUMPAD_3) + KEY(XKB_KEY_KP_4, KINC_KEY_NUMPAD_4) + KEY(XKB_KEY_KP_5, KINC_KEY_NUMPAD_5) + KEY(XKB_KEY_KP_6, KINC_KEY_NUMPAD_6) + KEY(XKB_KEY_KP_7, KINC_KEY_NUMPAD_7) + KEY(XKB_KEY_KP_8, KINC_KEY_NUMPAD_8) + KEY(XKB_KEY_KP_9, KINC_KEY_NUMPAD_9) + KEY(XKB_KEY_KP_Insert, KINC_KEY_INSERT) + KEY(XKB_KEY_KP_Delete, KINC_KEY_DELETE) + KEY(XKB_KEY_KP_End, KINC_KEY_END) + KEY(XKB_KEY_KP_Home, KINC_KEY_HOME) + KEY(XKB_KEY_KP_Left, KINC_KEY_LEFT) + KEY(XKB_KEY_KP_Up, KINC_KEY_UP) + KEY(XKB_KEY_KP_Right, KINC_KEY_RIGHT) + KEY(XKB_KEY_KP_Down, KINC_KEY_DOWN) + KEY(XKB_KEY_KP_Page_Up, KINC_KEY_PAGE_UP) + KEY(XKB_KEY_KP_Page_Down, KINC_KEY_PAGE_DOWN) + KEY(XKB_KEY_Menu, KINC_KEY_CONTEXT_MENU) + KEY(XKB_KEY_a, KINC_KEY_A) + KEY(XKB_KEY_b, KINC_KEY_B) + KEY(XKB_KEY_c, KINC_KEY_C) + KEY(XKB_KEY_d, KINC_KEY_D) + KEY(XKB_KEY_e, KINC_KEY_E) + KEY(XKB_KEY_f, KINC_KEY_F) + KEY(XKB_KEY_g, KINC_KEY_G) + KEY(XKB_KEY_h, KINC_KEY_H) + KEY(XKB_KEY_i, KINC_KEY_I) + KEY(XKB_KEY_j, KINC_KEY_J) + KEY(XKB_KEY_k, KINC_KEY_K) + KEY(XKB_KEY_l, KINC_KEY_L) + KEY(XKB_KEY_m, KINC_KEY_M) + KEY(XKB_KEY_n, KINC_KEY_N) + KEY(XKB_KEY_o, KINC_KEY_O) + KEY(XKB_KEY_p, KINC_KEY_P) + KEY(XKB_KEY_q, KINC_KEY_Q) + KEY(XKB_KEY_r, KINC_KEY_R) + KEY(XKB_KEY_s, KINC_KEY_S) + KEY(XKB_KEY_t, KINC_KEY_T) + KEY(XKB_KEY_u, KINC_KEY_U) + KEY(XKB_KEY_v, KINC_KEY_V) + KEY(XKB_KEY_w, KINC_KEY_W) + KEY(XKB_KEY_x, KINC_KEY_X) + KEY(XKB_KEY_y, KINC_KEY_Y) + KEY(XKB_KEY_z, KINC_KEY_Z) + KEY(XKB_KEY_1, KINC_KEY_1) + KEY(XKB_KEY_2, KINC_KEY_2) + KEY(XKB_KEY_3, KINC_KEY_3) + KEY(XKB_KEY_4, KINC_KEY_4) + KEY(XKB_KEY_5, KINC_KEY_5) + KEY(XKB_KEY_6, KINC_KEY_6) + KEY(XKB_KEY_7, KINC_KEY_7) + KEY(XKB_KEY_8, KINC_KEY_8) + KEY(XKB_KEY_9, KINC_KEY_9) + KEY(XKB_KEY_0, KINC_KEY_0) + KEY(XKB_KEY_Escape, KINC_KEY_ESCAPE) + KEY(XKB_KEY_F1, KINC_KEY_F1) + KEY(XKB_KEY_F2, KINC_KEY_F2) + KEY(XKB_KEY_F3, KINC_KEY_F3) + KEY(XKB_KEY_F4, KINC_KEY_F4) + KEY(XKB_KEY_F5, KINC_KEY_F5) + KEY(XKB_KEY_F6, KINC_KEY_F6) + KEY(XKB_KEY_F7, KINC_KEY_F7) + KEY(XKB_KEY_F8, KINC_KEY_F8) + KEY(XKB_KEY_F9, KINC_KEY_F9) + KEY(XKB_KEY_F10, KINC_KEY_F10) + KEY(XKB_KEY_F11, KINC_KEY_F11) + KEY(XKB_KEY_F12, KINC_KEY_F12) + default: + return KINC_KEY_UNKNOWN; + } +#undef KEY +} \ No newline at end of file diff --git a/armorcore/sources/backends/linux/kinc/backend/video.c.h b/armorcore/sources/backends/linux/kinc/backend/video.c.h new file mode 100644 index 000000000..adf52f71e --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/video.c.h @@ -0,0 +1,59 @@ +#include + +#if !defined(KINC_VIDEO_GSTREAMER) +void kinc_video_init(kinc_video_t *video, const char *filename) {} + +void kinc_video_destroy(kinc_video_t *video) {} + +void kinc_video_play(kinc_video_t *video, bool loop) {} + +void kinc_video_pause(kinc_video_t *video) {} + +void kinc_video_stop(kinc_video_t *video) {} + +int kinc_video_width(kinc_video_t *video) { + return 256; +} + +int kinc_video_height(kinc_video_t *video) { + return 256; +} + +kinc_g4_texture_t *kinc_video_current_image(kinc_video_t *video) { + return NULL; +} + +double kinc_video_duration(kinc_video_t *video) { + return 0.0; +} + +double kinc_video_position(kinc_video_t *video) { + return 0.0; +} + +bool kinc_video_finished(kinc_video_t *video) { + return false; +} + +bool kinc_video_paused(kinc_video_t *video) { + return false; +} + +void kinc_video_update(kinc_video_t *video, double time) {} + +void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency) {} + +void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream) {} + +void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count) {} + +static float samples[2] = {0}; + +float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream) { + return samples; +} + +bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream) { + return true; +} +#endif diff --git a/armorcore/sources/backends/linux/kinc/backend/video.h b/armorcore/sources/backends/linux/kinc/backend/video.h new file mode 100644 index 000000000..22c464e6b --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/video.h @@ -0,0 +1,31 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(KINC_VIDEO_GSTREAMER) +#include +#else +typedef struct { + int nothing; +} kinc_video_impl_t; + +typedef struct kinc_internal_video_sound_stream { + int nothing; +} kinc_internal_video_sound_stream_t; + +void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency); + +void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream); + +void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count); + +float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream); + +bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/linux/kinc/backend/wayland/display.c.h b/armorcore/sources/backends/linux/kinc/backend/wayland/display.c.h new file mode 100644 index 000000000..3ecd0ac8e --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/wayland/display.c.h @@ -0,0 +1,52 @@ +#include "wayland.h" + +void kinc_wayland_display_init(void) { + // This is a no-op because displays are already registered in kinc_wayland_init, + // which should be called before this function is ever invoked +} + +int kinc_wayland_display_primary(void) { + return 0; // TODO +} + +int kinc_wayland_count_displays(void) { + return wl_ctx.num_displays; +} + +bool kinc_wayland_display_available(int display_index) { + if (display_index >= MAXIMUM_DISPLAYS) { + return false; + } + struct kinc_wl_display *display = &wl_ctx.displays[display_index]; + return display->output != NULL; +} + +const char *kinc_wayland_display_name(int display_index) { + if (display_index >= MAXIMUM_DISPLAYS) + display_index = 0; + struct kinc_wl_display *display = &wl_ctx.displays[display_index]; + return display->name; +} + +kinc_display_mode_t kinc_wayland_display_current_mode(int display_index) { + if (display_index >= MAXIMUM_DISPLAYS) + display_index = 0; + struct kinc_wl_display *display = &wl_ctx.displays[display_index]; + return display->modes[display->current_mode]; +} + +int kinc_wayland_display_count_available_modes(int display_index) { + if (display_index >= MAXIMUM_DISPLAYS) + display_index = 0; + struct kinc_wl_display *display = &wl_ctx.displays[display_index]; + return display->num_modes; +} + +kinc_display_mode_t kinc_wayland_display_available_mode(int display_index, int mode_index) { + if (display_index >= MAXIMUM_DISPLAYS) + display_index = 0; + struct kinc_wl_display *display = &wl_ctx.displays[display_index]; + if (mode_index >= display->num_modes) + mode_index = 0; + return display->modes[mode_index]; +} \ No newline at end of file diff --git a/armorcore/sources/backends/linux/kinc/backend/wayland/system.c.h b/armorcore/sources/backends/linux/kinc/backend/wayland/system.c.h new file mode 100644 index 000000000..1498cfd64 --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/wayland/system.c.h @@ -0,0 +1,1327 @@ +#include "wayland.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef KINC_EGL +#define EGL_NO_PLATFORM_SPECIFIC_TYPES +#include +#endif +#include +#include +#include +#include +#include + +static void load_lib(void **lib, const char *name); + +struct kinc_wl_procs wl = {0}; +struct kinc_xkb_procs wl_xkb = {0}; + +bool kinc_wayland_load_procs() { + void *wayland_client = NULL; + load_lib(&wayland_client, "wayland-client"); + if (wayland_client == NULL) { + return false; + } + bool has_missing_symbol = false; +#undef LOAD_FUN +#define LOAD_FUN(lib, symbol, name) \ + wl.symbol = dlsym(lib, name); \ + if (wl.symbol == NULL) { \ + has_missing_symbol = true; \ + kinc_log(KINC_LOG_LEVEL_ERROR, "Did not find symbol %s.", name); \ + } +#define KINC_WL_FUN(ret, name, args) LOAD_FUN(wayland_client, _##name, #name) +#include "wayland-funs.h" +#undef KINC_WL_FN + + void *wayland_cursor = NULL; + load_lib(&wayland_cursor, "wayland-cursor"); + if (wayland_cursor == NULL) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to find libwayland-cursor.so"); + return false; + } + LOAD_FUN(wayland_cursor, _wl_cursor_theme_load, "wl_cursor_theme_load") + LOAD_FUN(wayland_cursor, _wl_cursor_theme_destroy, "wl_cursor_theme_destroy") + LOAD_FUN(wayland_cursor, _wl_cursor_theme_get_cursor, "wl_cursor_theme_get_cursor") + LOAD_FUN(wayland_cursor, _wl_cursor_image_get_buffer, "wl_cursor_image_get_buffer") + LOAD_FUN(wayland_cursor, _wl_cursor_frame, "wl_cursor_frame") + LOAD_FUN(wayland_cursor, _wl_cursor_frame_and_duration, "wl_cursor_frame_and_duration") + +#ifdef KINC_EGL + void *wayland_egl = NULL; + load_lib(&wayland_egl, "wayland-egl"); + + if (wayland_egl == NULL) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to find libwayland-egl.so"); + return false; + } + LOAD_FUN(wayland_egl, _wl_egl_window_create, "wl_egl_window_create") + LOAD_FUN(wayland_egl, _wl_egl_window_destroy, "wl_egl_window_destroy") + LOAD_FUN(wayland_egl, _wl_egl_window_resize, "wl_egl_window_resize") + LOAD_FUN(wayland_egl, _wl_egl_window_get_attached_size, "wl_egl_window_get_attached_size") +#endif + +#undef LOAD_FUN +#define LOAD_FUN(symbol) \ + wl_xkb.symbol = dlsym(xkb, #symbol); \ + if (wl_xkb.symbol == NULL) { \ + has_missing_symbol = true; \ + kinc_log(KINC_LOG_LEVEL_ERROR, "Did not find symbol %s.", #symbol); \ + } + void *xkb = NULL; + load_lib(&xkb, "xkbcommon"); + + if (xkb == NULL) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to find libxkb_common.so"); + return false; + } + LOAD_FUN(xkb_context_new) + LOAD_FUN(xkb_context_unref) + LOAD_FUN(xkb_state_new) + LOAD_FUN(xkb_keymap_new_from_string) + LOAD_FUN(xkb_state_key_get_one_sym) + LOAD_FUN(xkb_state_key_get_utf32) + LOAD_FUN(xkb_state_serialize_mods) + LOAD_FUN(xkb_state_update_mask) + LOAD_FUN(xkb_state_mod_name_is_active) + LOAD_FUN(xkb_keymap_key_repeats) +#undef LOAD_FUN + + if (has_missing_symbol) { + return false; + } + + return true; +} + +struct wayland_context wl_ctx = {0}; + +static void xdg_wm_base_handle_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) { + xdg_wm_base_pong(shell, serial); +}; + +static const struct xdg_wm_base_listener xdg_wm_base_listener = { + xdg_wm_base_handle_ping, +}; + +static void wl_output_handle_geometry(void *data, struct wl_output *wl_output, int x, int y, int physical_width, int physical_height, int subpixel, + const char *make, const char *model, int transform) { + struct kinc_wl_display *display = data; + snprintf(display->name, sizeof(display->name), "%s %s", make, model); + display->x = x; + display->y = y; + display->physical_width = physical_width; + display->physical_height = physical_height; + display->subpixel = subpixel; + display->transform = transform; +} + +static void wl_output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { + struct kinc_wl_display *display = data; + if (display->num_modes < MAXIMUM_DISPLAY_MODES) { + int mode_index = display->num_modes++; + kinc_display_mode_t *mode = &display->modes[mode_index]; + mode->x = 0; + mode->y = 0; + mode->width = width; + mode->height = height; + mode->bits_per_pixel = 32; + mode->pixels_per_inch = 96; + mode->frequency = (refresh / 1000); + if (flags & WL_OUTPUT_MODE_CURRENT) + display->current_mode = mode_index; + } +} +static void wl_output_handle_done(void *data, struct wl_output *wl_output) { + // struct kinc_wl_display *display = data; +} +static void wl_output_handle_scale(void *data, struct wl_output *wl_output, int32_t factor) { + struct kinc_wl_display *display = data; + display->scale = factor; +} + +static const struct wl_output_listener wl_output_listener = { + wl_output_handle_geometry, + wl_output_handle_mode, + wl_output_handle_done, + wl_output_handle_scale, +}; + +struct kinc_wl_window *kinc_wayland_window_from_surface(struct wl_surface *surface, enum kinc_wl_decoration_focus *focus) { + struct kinc_wl_window *window = wl_surface_get_user_data(surface); + if (window == NULL) { + for (int i = 0; i < MAXIMUM_WINDOWS; i++) { + struct kinc_wl_window *_window = &wl_ctx.windows[i]; + if (_window->surface == surface) { + *focus = KINC_WL_DECORATION_FOCUS_MAIN; + window = _window; + } + else if (surface == _window->decorations.top.surface) { + *focus = KINC_WL_DECORATION_FOCUS_TOP; + window = _window; + } + else if (surface == _window->decorations.left.surface) { + *focus = KINC_WL_DECORATION_FOCUS_LEFT; + window = _window; + } + else if (surface == _window->decorations.right.surface) { + *focus = KINC_WL_DECORATION_FOCUS_RIGHT; + window = _window; + } + else if (surface == _window->decorations.bottom.surface) { + *focus = KINC_WL_DECORATION_FOCUS_BOTTOM; + window = _window; + } + else if (surface == _window->decorations.close.surface) { + *focus = KINC_WL_DECORATION_FOCUS_CLOSE_BUTTON; + window = _window; + } + } + } + return window; +} + +void wl_pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, + wl_fixed_t surface_y) { + enum kinc_wl_decoration_focus focus = KINC_WL_DECORATION_FOCUS_MAIN; + struct kinc_wl_window *window = kinc_wayland_window_from_surface(surface, &focus); + struct kinc_wl_mouse *mouse = data; + mouse->enter_serial = serial; + window->decorations.focus = focus; + if (window != NULL) { + mouse->current_window = window->window_id; + kinc_internal_mouse_trigger_enter_window(window->window_id); + } +} + +void wl_pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { + enum kinc_wl_decoration_focus focus = KINC_WL_DECORATION_FOCUS_MAIN; + struct kinc_wl_window *window = kinc_wayland_window_from_surface(surface, &focus); + + if (window != NULL) { + kinc_internal_mouse_trigger_leave_window(window->window_id); + } +} + +#include + +void kinc_wayland_set_cursor(struct kinc_wl_mouse *mouse, const char *name) { + if (!name) + return; + struct wl_cursor *cursor = wl_cursor_theme_get_cursor(wl_ctx.cursor_theme, name); + if (!cursor) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland: No cursor found '%'.", name); + return; + } + struct wl_cursor_image *image = cursor->images[0]; + if (!image) + return; + struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); + if (!buffer) + return; + + wl_pointer_set_cursor(mouse->pointer, mouse->enter_serial, mouse->surface, image->hotspot_x, image->hotspot_y); + wl_surface_attach(mouse->surface, buffer, 0, 0); + wl_surface_damage(mouse->surface, 0, 0, image->width, image->height); + wl_surface_commit(mouse->surface); + mouse->previous_cursor_name = name; +} + +void wl_pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { + struct kinc_wl_mouse *mouse = data; + struct kinc_wl_window *window = &wl_ctx.windows[mouse->current_window]; + + int x = wl_fixed_to_int(surface_x); + int y = wl_fixed_to_int(surface_y); + + mouse->x = x; + mouse->y = y; + + if (!window->decorations.server_side) { + const char *cursor_name = "default"; + + switch (window->decorations.focus) { + case KINC_WL_DECORATION_FOCUS_MAIN: + kinc_internal_mouse_trigger_move(mouse->current_window, x, y); + break; + case KINC_WL_DECORATION_FOCUS_TOP: + if (y < KINC_WL_DECORATION_TOP_HEIGHT / 2) + cursor_name = "n-resize"; + else + cursor_name = "left_ptr"; + break; + case KINC_WL_DECORATION_FOCUS_LEFT: + if (y < KINC_WL_DECORATION_WIDTH) + cursor_name = "nw-resize"; + else if (mouse->y > KINC_WL_DECORATION_TOP_HEIGHT - KINC_WL_DECORATION_WIDTH) + cursor_name = "sw-resize"; + else + cursor_name = "w-resize"; + break; + case KINC_WL_DECORATION_FOCUS_RIGHT: + if (y < KINC_WL_DECORATION_WIDTH) + cursor_name = "ne-resize"; + else if (mouse->y > KINC_WL_DECORATION_RIGHT_HEIGHT - KINC_WL_DECORATION_WIDTH) + cursor_name = "se-resize"; + else + cursor_name = "e-resize"; + break; + case KINC_WL_DECORATION_FOCUS_BOTTOM: + if (x < 10) + cursor_name = "sw-resize"; + else if (x > window->width + 10) + cursor_name = "se-resize"; + else + cursor_name = "s-resize"; + break; + case KINC_WL_DECORATION_FOCUS_CLOSE_BUTTON: + break; + default: + break; + } + + if (mouse->previous_cursor_name != cursor_name) { + kinc_wayland_set_cursor(mouse, cursor_name); + } + } + else { + kinc_internal_mouse_trigger_move(mouse->current_window, x, y); + } +} + +#include + +void wl_pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { + struct kinc_wl_mouse *mouse = data; + struct kinc_wl_window *window = &wl_ctx.windows[mouse->current_window]; + int kinc_button = button - BTN_MOUSE; // evdev codes should have the same order as Kinc buttons + if (!window->decorations.server_side) { + if (kinc_button == 0) { + enum xdg_toplevel_resize_edge edges = XDG_TOPLEVEL_RESIZE_EDGE_NONE; + switch (window->decorations.focus) { + case KINC_WL_DECORATION_FOCUS_MAIN: + break; + case KINC_WL_DECORATION_FOCUS_TOP: + if (mouse->y > KINC_WL_DECORATION_TOP_HEIGHT / 2) + edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP; + else { + xdg_toplevel_move(window->toplevel, wl_ctx.seat.seat, serial); + } + break; + case KINC_WL_DECORATION_FOCUS_LEFT: + if (mouse->y < KINC_WL_DECORATION_TOP_HEIGHT / 2) + edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; + else if (mouse->y > KINC_WL_DECORATION_TOP_HEIGHT - (KINC_WL_DECORATION_TOP_HEIGHT / 2)) + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; + else + edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; + break; + case KINC_WL_DECORATION_FOCUS_RIGHT: + if (mouse->y < KINC_WL_DECORATION_TOP_HEIGHT / 2) + edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; + else if (mouse->y > KINC_WL_DECORATION_RIGHT_HEIGHT - (KINC_WL_DECORATION_TOP_HEIGHT / 2)) + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; + else + edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; + break; + case KINC_WL_DECORATION_FOCUS_BOTTOM: + edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; + break; + case KINC_WL_DECORATION_FOCUS_CLOSE_BUTTON: + if (kinc_button == 0) { + if (kinc_internal_call_close_callback(window->window_id)) { + kinc_window_destroy(window->window_id); + if (wl_ctx.num_windows <= 0) { + // no windows left, stop + kinc_stop(); + } + } + } + break; + default: + break; + } + if (edges != XDG_TOPLEVEL_RESIZE_EDGE_NONE) { + xdg_toplevel_resize(window->toplevel, wl_ctx.seat.seat, serial, edges); + } + } + else if (kinc_button == 1) { + if (window->decorations.focus == KINC_WL_DECORATION_FOCUS_TOP) { + xdg_toplevel_show_window_menu(window->toplevel, mouse->seat->seat, serial, mouse->x, mouse->y); + } + } + } + + if (window->decorations.focus == KINC_WL_DECORATION_FOCUS_MAIN) { + if (state == WL_POINTER_BUTTON_STATE_PRESSED) { + kinc_internal_mouse_trigger_press(mouse->current_window, kinc_button, mouse->x, mouse->y); + } + if (state == WL_POINTER_BUTTON_STATE_RELEASED) { + kinc_internal_mouse_trigger_release(mouse->current_window, kinc_button, mouse->x, mouse->y); + } + } +} + +void wl_pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { + struct kinc_wl_mouse *mouse = data; + // FIXME: figure out what the other backends give as deltas + int delta = wl_fixed_to_int(value); + kinc_internal_mouse_trigger_scroll(mouse->current_window, delta); +} + +void wl_pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {} + +void wl_pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) {} + +void wl_pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) {} + +void wl_pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {} + +void wl_pointer_handle_axis_value120(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120) {} + +static const struct wl_pointer_listener wl_pointer_listener = {wl_pointer_handle_enter, wl_pointer_handle_leave, wl_pointer_handle_motion, + wl_pointer_handle_button, wl_pointer_handle_axis, +#ifdef WL_POINTER_FRAME_SINCE_VERSION + wl_pointer_handle_frame, +#endif +#ifdef WL_POINTER_AXIS_SOURCE_SINCE_VERSION + wl_pointer_handle_axis_source, +#endif +#ifdef WL_POINTER_AXIS_STOP_SINCE_VERSION + wl_pointer_handle_axis_stop, +#endif +#ifdef WL_POINTER_AXIS_DISCRETE_SINCE_VERSION + wl_pointer_handle_axis_discrete, +#endif +#ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION + wl_pointer_handle_axis_value120 +#endif +}; + +void zwp_relative_pointer_v1_handle_relative_motion(void *data, struct zwp_relative_pointer_v1 *zwp_relative_pointer_v1, uint32_t utime_hi, uint32_t utime_lo, + wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel) { + struct kinc_wl_mouse *mouse = data; + if (mouse->locked) { + mouse->x += wl_fixed_to_int(dx); + mouse->y += wl_fixed_to_int(dy); + kinc_internal_mouse_trigger_move(mouse->current_window, mouse->x, mouse->y); + } +} + +static const struct zwp_relative_pointer_v1_listener zwp_relative_pointer_v1_listener = { + zwp_relative_pointer_v1_handle_relative_motion, +}; + +#include +#include + +void wl_keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) { + struct kinc_wl_keyboard *keyboard = wl_keyboard_get_user_data(wl_keyboard); + switch (format) { + case WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1: { + char *mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (mapStr == MAP_FAILED) { + mapStr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (mapStr == MAP_FAILED) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to map wayland keymap."); + close(fd); + return; + } + } + keyboard->keymap = wl_xkb.xkb_keymap_new_from_string(wl_ctx.xkb_context, mapStr, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(mapStr, size); + close(fd); + keyboard->state = wl_xkb.xkb_state_new(keyboard->keymap); + keyboard->ctrlDown = false; + break; + } + default: + close(fd); + kinc_log(KINC_LOG_LEVEL_WARNING, "Unsupported wayland keymap format %i", format); + } +} + +void wl_keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { + // struct kinc_wl_seat *seat = data; + // struct kinc_wl_keyboard *keyboard = wl_keyboard_get_user_data(wl_keyboard); +} + +void wl_keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { + // struct kinc_wl_seat *seat = data; + // struct kinc_wl_keyboard *keyboard = wl_keyboard_get_user_data(wl_keyboard); +} + +int xkb_to_kinc(xkb_keysym_t symbol); + +void handle_paste(void *data, size_t data_size, void *user_data) { + kinc_internal_paste_callback(data); +} + +#include + +void wl_keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { + struct kinc_wl_keyboard *keyboard = wl_keyboard_get_user_data(wl_keyboard); + if (keyboard->keymap && keyboard->state) { + xkb_keysym_t symbol = wl_xkb.xkb_state_key_get_one_sym(keyboard->state, key + 8); + uint32_t character = wl_xkb.xkb_state_key_get_utf32(keyboard->state, key + 8); + int kinc_key = xkb_to_kinc(symbol); + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { + if (keyboard->ctrlDown && (symbol == XKB_KEY_c || symbol == XKB_KEY_C)) { + char *text = kinc_internal_copy_callback(); + if (text != NULL) { + kinc_wayland_set_selection(keyboard->seat, text, serial); + } + } + else if (keyboard->ctrlDown && (symbol == XKB_KEY_x || symbol == XKB_KEY_X)) { + char *text = kinc_internal_copy_callback(); + if (text != NULL) { + kinc_wayland_set_selection(keyboard->seat, text, serial); + } + } + else if (keyboard->ctrlDown && (symbol == XKB_KEY_v || symbol == XKB_KEY_V)) { + if (keyboard->seat->current_selection_offer != NULL) { + kinc_wl_data_offer_accept(keyboard->seat->current_selection_offer, handle_paste, NULL); + } + } + kinc_internal_keyboard_trigger_key_down(kinc_key); + if (character != 0) { + kinc_internal_keyboard_trigger_key_press(character); + if (wl_xkb.xkb_keymap_key_repeats(keyboard->keymap, key + 8) && keyboard->repeat_rate > 0) { + struct itimerspec timer = {}; + keyboard->last_character = character; + keyboard->last_key_code = key + 8; + if (keyboard->repeat_rate > 1) { + timer.it_interval.tv_nsec = 1000000000 / keyboard->repeat_rate; + } + else { + timer.it_interval.tv_sec = 1; + } + + timer.it_value.tv_sec = keyboard->repeat_delay / 1000; + timer.it_value.tv_nsec = (keyboard->repeat_delay % 1000) * 1000000; + + timerfd_settime(keyboard->timerfd, 0, &timer, NULL); + } + } + } + if (state == WL_KEYBOARD_KEY_STATE_RELEASED) { + if (key + 8 == keyboard->last_key_code) { + keyboard->last_key_code = keyboard->last_character = -1; + } + + kinc_internal_keyboard_trigger_key_up(kinc_key); + } + } +} + +void wl_keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) { + struct kinc_wl_keyboard *keyboard = wl_keyboard_get_user_data(wl_keyboard); + if (keyboard->keymap && keyboard->state) { + wl_xkb.xkb_state_update_mask(keyboard->state, mods_depressed, mods_latched, mods_locked, 0, 0, group); + wl_xkb.xkb_state_serialize_mods(keyboard->state, + XKB_STATE_MODS_DEPRESSED | XKB_STATE_LAYOUT_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_LAYOUT_LATCHED); + keyboard->ctrlDown = wl_xkb.xkb_state_mod_name_is_active(keyboard->state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0; + } +} + +void wl_keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) { + struct kinc_wl_keyboard *keyboard = wl_keyboard_get_user_data(wl_keyboard); + keyboard->repeat_rate = rate; + keyboard->repeat_delay = delay; + kinc_log(KINC_LOG_LEVEL_INFO, "Keyboard repeat rate: %i, delay: %i", rate, delay); +} + +static const struct wl_keyboard_listener wl_keyboard_listener = { + wl_keyboard_handle_keymap, wl_keyboard_handle_enter, wl_keyboard_handle_leave, wl_keyboard_handle_key, wl_keyboard_handle_modifiers, +#ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION + wl_keyboard_handle_repeat_info, +#endif +}; + +void wl_seat_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities) { + struct kinc_wl_seat *seat = data; + seat->capabilities = capabilities; + if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { + seat->keyboard.keyboard = wl_seat_get_keyboard(wl_seat); + seat->keyboard.seat = seat; + seat->keyboard.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); + seat->keyboard.repeat_delay = 0; + seat->keyboard.repeat_rate = 0; + seat->keyboard.last_key_code = -1; + wl_keyboard_add_listener(seat->keyboard.keyboard, &wl_keyboard_listener, &seat->keyboard); + } + if (capabilities & WL_SEAT_CAPABILITY_POINTER) { + seat->mouse.pointer = wl_seat_get_pointer(wl_seat); + seat->mouse.surface = wl_compositor_create_surface(wl_ctx.compositor); + seat->mouse.seat = seat; + wl_pointer_add_listener(seat->mouse.pointer, &wl_pointer_listener, &seat->mouse); + if (wl_ctx.relative_pointer_manager) { + seat->mouse.relative = zwp_relative_pointer_manager_v1_get_relative_pointer(wl_ctx.relative_pointer_manager, seat->mouse.pointer); + zwp_relative_pointer_v1_add_listener(seat->mouse.relative, &zwp_relative_pointer_v1_listener, &seat->mouse); + } + } + if (capabilities & WL_SEAT_CAPABILITY_TOUCH) { + seat->touch = wl_seat_get_touch(wl_seat); + } +} + +void wl_seat_name(void *data, struct wl_seat *wl_seat, const char *name) { + struct kinc_wl_seat *seat = data; + snprintf(seat->name, sizeof(seat->name), "%s", name); +} + +static const struct wl_seat_listener wl_seat_listener = { + wl_seat_capabilities, + wl_seat_name, +}; + +void wl_data_source_handle_target(void *data, struct wl_data_source *wl_data_source, const char *mime_type) {} + +void wl_data_source_handle_send(void *data, struct wl_data_source *wl_data_source, const char *mime_type, int32_t fd) { + struct kinc_wl_data_source *data_source = wl_data_source_get_user_data(wl_data_source); + if (write(fd, data_source->data, data_source->data_size) < data_source->data_size) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to write all data for data source"); + } + close(fd); +} + +void wl_data_source_handle_cancelled(void *data, struct wl_data_source *wl_data_source) {} + +void wl_data_source_handle_dnd_drop_performed(void *data, struct wl_data_source *wl_data_source) {} + +void wl_data_source_handle_dnd_finished(void *data, struct wl_data_source *wl_data_source) {} + +void wl_data_source_handle_action(void *data, struct wl_data_source *wl_data_source, uint32_t dnd_action) {} + +static const struct wl_data_source_listener wl_data_source_listener = { + wl_data_source_handle_target, wl_data_source_handle_send, wl_data_source_handle_cancelled, wl_data_source_handle_dnd_drop_performed, + wl_data_source_handle_dnd_finished, wl_data_source_handle_action, +}; + +struct kinc_wl_data_source *kinc_wl_create_data_source(struct kinc_wl_seat *seat, const char *mime_types[], int num_mime_types, void *data, size_t data_size) { + struct kinc_wl_data_source *data_source = malloc(sizeof *data_source); + data_source->source = wl_data_device_manager_create_data_source(wl_ctx.data_device_manager); + data_source->data = data; + data_source->data_size = data_size; + data_source->mime_types = mime_types; + data_source->num_mime_types = num_mime_types; + + for (int i = 0; i < num_mime_types; i++) { + wl_data_source_offer(data_source->source, mime_types[i]); + } + // wl_data_source_set_actions(data_source->source, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); + wl_data_source_set_user_data(data_source->source, data_source); + wl_data_source_add_listener(data_source->source, &wl_data_source_listener, data_source); + return data_source; +} + +void kinc_wl_data_source_destroy(struct kinc_wl_data_source *data_source) {} + +void wl_data_offer_handle_offer(void *data, struct wl_data_offer *wl_data_offer, const char *mime_type) { + struct kinc_wl_data_offer *offer = wl_data_offer_get_user_data(wl_data_offer); + if (offer != NULL) { + offer->mime_type_count++; + offer->mime_types = realloc(offer->mime_types, offer->mime_type_count * sizeof(const char *)); + char *copy = malloc(strlen(mime_type) + 1); + strcpy(copy, mime_type); + offer->mime_types[offer->mime_type_count - 1] = copy; + } +} + +void wl_data_offer_handle_source_actions(void *data, struct wl_data_offer *wl_data_offer, uint32_t source_actions) { + struct kinc_wl_data_offer *offer = wl_data_offer_get_user_data(wl_data_offer); + offer->source_actions = source_actions; +} + +void wl_data_offer_handle_action(void *data, struct wl_data_offer *wl_data_offer, uint32_t dnd_action) { + struct kinc_wl_data_offer *offer = wl_data_offer_get_user_data(wl_data_offer); + offer->dnd_action = dnd_action; +} + +static const struct wl_data_offer_listener wl_data_offer_listener = { + wl_data_offer_handle_offer, + wl_data_offer_handle_source_actions, + wl_data_offer_handle_action, +}; + +void kinc_wl_init_data_offer(struct wl_data_offer *id) { + struct kinc_wl_data_offer *offer = malloc(sizeof *offer); + memset(offer, 0, sizeof *offer); + offer->id = id; + offer->mime_type_count = 0; + offer->mime_types = NULL; + + wl_data_offer_set_user_data(id, offer); + wl_data_offer_add_listener(id, &wl_data_offer_listener, offer); +} + +void kinc_wl_data_offer_accept(struct kinc_wl_data_offer *offer, void (*callback)(void *data, size_t data_size, void *user_data), void *user_data) { + offer->callback = callback; + offer->user_data = user_data; + + int fds[2]; + if (pipe(fds) != 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to create pipe to accept wayland data offer (errno=%i)", errno); + return; + } + wl_data_offer_receive(offer->id, "text/plain", fds[1]); + close(fds[1]); + + wl_display_roundtrip(wl_ctx.display); + + offer->read_fd = fds[0]; + + struct kinc_wl_data_offer **queue = &wl_ctx.data_offer_queue; + + while (*queue != NULL) + queue = &(*queue)->next; + *queue = offer; +} + +void kinc_wl_destroy_data_offer(struct kinc_wl_data_offer *offer) { + wl_data_offer_destroy(offer->id); + if (offer->buffer != NULL) { + free(offer->buffer); + } + for (int i = 0; i < offer->mime_type_count; i++) { + free(offer->mime_types[i]); + } + free(offer->mime_types); + free(offer); +} + +void wl_data_device_handle_data_offer(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *id) { + // struct kinc_wl_seat *seat = data; + kinc_wl_init_data_offer(id); +} + +void wl_data_device_handle_enter(void *data, struct wl_data_device *wl_data_device, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y, + struct wl_data_offer *id) { + struct kinc_wl_seat *seat = data; + seat->current_dnd_offer = wl_data_offer_get_user_data(id); + wl_data_offer_set_actions(id, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY); +} + +void wl_data_device_handle_leave(void *data, struct wl_data_device *wl_data_device) { + struct kinc_wl_seat *seat = data; + kinc_wl_destroy_data_offer(seat->current_dnd_offer); + seat->current_dnd_offer = NULL; +} + +void wl_data_device_handle_motion(void *data, struct wl_data_device *wl_data_device, uint32_t time, wl_fixed_t x, wl_fixed_t y) { + // struct kinc_wl_seat *seat = data; +} + +static void dnd_callback(void *data, size_t data_size, void *user_data) { + char *str = data; + if (strncmp(data, "file://", strlen("file://")) == 0) { + str += strlen("file://"); + } + size_t wide_size = mbstowcs(NULL, str, 0) + 1; + wchar_t *dest = malloc(wide_size * sizeof(wchar_t)); + mbstowcs(dest, str, wide_size); + kinc_internal_drop_files_callback(dest); + free(dest); +} + +void wl_data_device_handle_drop(void *data, struct wl_data_device *wl_data_device) { + struct kinc_wl_seat *seat = data; + if (seat->current_dnd_offer != NULL) { + kinc_wl_data_offer_accept(seat->current_dnd_offer, dnd_callback, NULL); + } +} + +void wl_data_device_handle_selection(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *id) { + struct kinc_wl_seat *seat = data; + if (seat->current_selection_offer != NULL && seat->current_selection_offer->id != id) { + kinc_wl_destroy_data_offer(seat->current_selection_offer); + seat->current_selection_offer = NULL; + } + + if (id != NULL) { + seat->current_selection_offer = wl_data_offer_get_user_data(id); + } +} + +static const struct wl_data_device_listener wl_data_device_listener = { + wl_data_device_handle_data_offer, wl_data_device_handle_enter, wl_data_device_handle_leave, + wl_data_device_handle_motion, wl_data_device_handle_drop, wl_data_device_handle_selection, +}; + +void kinc_wl_tablet_tool_destroy(struct kinc_wl_tablet_tool *tool) { + zwp_tablet_tool_v2_destroy(tool->id); + free(tool); +} + +void zwp_tablet_tool_v2_handle_type(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t tool_type) { + struct kinc_wl_tablet_tool *tool = data; + tool->type = tool_type; +} + +#ifdef KINC_LITTLE_ENDIAN +#define HI_LO_TO_64(hi, lo) (uint64_t) lo | ((uint64_t)hi << 32) +#else +#define HI_LO_TO_64(hi, lo) (uint64_t) hi | ((uint64_t)lo << 32) +#endif + +void zwp_tablet_tool_v2_handle_hardware_serial(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_serial_hi, + uint32_t hardware_serial_lo) { + struct kinc_wl_tablet_tool *tool = data; + tool->hardware_serial = HI_LO_TO_64(hardware_serial_hi, hardware_serial_lo); +} + +void zwp_tablet_tool_v2_handle_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo) { + struct kinc_wl_tablet_tool *tool = data; + tool->hardware_id_wacom = HI_LO_TO_64(hardware_id_hi, hardware_id_lo); +} + +void zwp_tablet_tool_v2_handle_capability(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t capability) { + struct kinc_wl_tablet_tool *tool = data; + tool->capabilities |= capability; +} + +void zwp_tablet_tool_v2_handle_done(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { + struct kinc_wl_tablet_tool *tool = data; + switch (tool->type) { + case ZWP_TABLET_TOOL_V2_TYPE_PEN: + tool->press = kinc_internal_pen_trigger_press; + tool->move = kinc_internal_pen_trigger_move; + tool->release = kinc_internal_pen_trigger_release; + break; + case ZWP_TABLET_TOOL_V2_TYPE_ERASER: + tool->press = kinc_internal_eraser_trigger_press; + tool->move = kinc_internal_eraser_trigger_move; + tool->release = kinc_internal_eraser_trigger_release; + break; + default: + break; + } +} + +void zwp_tablet_tool_v2_handle_removed(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { + struct kinc_wl_tablet_tool *tool = data; + struct kinc_wl_tablet_seat *seat = tool->seat; + struct kinc_wl_tablet_tool **tools = &seat->tablet_tools; + while (*tools != NULL) { + struct kinc_wl_tablet_tool *current = *tools; + struct kinc_wl_tablet_tool **next = ¤t->next; + + if (current == tool) { + *tools = *next; + + break; + } + else { + tools = next; + } + } + + kinc_wl_tablet_tool_destroy(tool); +} + +void zwp_tablet_tool_v2_handle_proximity_in(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, + struct wl_surface *surface) { + struct kinc_wl_tablet_tool *tool = data; + enum kinc_wl_decoration_focus focus; + struct kinc_wl_window *window = kinc_wayland_window_from_surface(surface, &focus); + tool->current_window = window->window_id; +} + +void zwp_tablet_tool_v2_handle_proximity_out(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { + struct kinc_wl_tablet_tool *tool = data; + tool->current_window = -1; +} + +void zwp_tablet_tool_v2_handle_down(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial) { + struct kinc_wl_tablet_tool *tool = data; + if (tool->current_window >= 0 && tool->press) { + tool->press(tool->current_window, tool->x, tool->y, tool->current_pressure); + } +} + +void zwp_tablet_tool_v2_handle_up(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2) { + struct kinc_wl_tablet_tool *tool = data; + if (tool->current_window >= 0 && tool->release) { + tool->release(tool->current_window, tool->x, tool->y, tool->current_pressure); + } +} + +void zwp_tablet_tool_v2_handle_motion(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y) { + struct kinc_wl_tablet_tool *tool = data; + tool->x = wl_fixed_to_int(x); + tool->y = wl_fixed_to_int(y); + if (tool->current_window >= 0 && tool->move) { + tool->move(tool->current_window, tool->x, tool->y, tool->current_pressure); + } +} + +void zwp_tablet_tool_v2_handle_pressure(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t pressure) { + struct kinc_wl_tablet_tool *tool = data; + // TODO: verify what the other backends give + tool->current_pressure = (float)pressure / 65535.f; +} + +void zwp_tablet_tool_v2_handle_distance(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t distance) { + struct kinc_wl_tablet_tool *tool = data; + tool->current_distance = (float)distance / 65535.f; +} + +void zwp_tablet_tool_v2_handle_tilt(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y) {} + +void zwp_tablet_tool_v2_handle_rotation(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees) {} + +void zwp_tablet_tool_v2_handle_slider(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, int32_t position) {} + +void zwp_tablet_tool_v2_handle_wheel(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks) {} + +void zwp_tablet_tool_v2_handle_button(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state) {} + +void zwp_tablet_tool_v2_handle_frame(void *data, struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2, uint32_t time) {} + +static const struct zwp_tablet_tool_v2_listener zwp_tablet_tool_v2_listener = { + zwp_tablet_tool_v2_handle_type, + zwp_tablet_tool_v2_handle_hardware_serial, + zwp_tablet_tool_v2_handle_hardware_id_wacom, + zwp_tablet_tool_v2_handle_capability, + zwp_tablet_tool_v2_handle_done, + zwp_tablet_tool_v2_handle_removed, + zwp_tablet_tool_v2_handle_proximity_in, + zwp_tablet_tool_v2_handle_proximity_out, + zwp_tablet_tool_v2_handle_down, + zwp_tablet_tool_v2_handle_up, + zwp_tablet_tool_v2_handle_motion, + zwp_tablet_tool_v2_handle_pressure, + zwp_tablet_tool_v2_handle_distance, + zwp_tablet_tool_v2_handle_tilt, + zwp_tablet_tool_v2_handle_rotation, + zwp_tablet_tool_v2_handle_slider, + zwp_tablet_tool_v2_handle_wheel, + zwp_tablet_tool_v2_handle_button, + zwp_tablet_tool_v2_handle_frame, +}; + +void zwp_tablet_seat_v2_handle_tablet_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_v2 *id) { + struct kinc_wl_tablet *tablet = malloc(sizeof *tablet); + tablet->id = id; + tablet->seat = zwp_tablet_seat_v2_get_user_data(zwp_tablet_seat_v2); + tablet->next = tablet->seat->tablets; + tablet->seat->tablets = tablet; + + // zwp_tablet_v2_add_listener(tablet->id, NULL, tablet); +} + +void zwp_tablet_seat_v2_handle_tool_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id) { + struct kinc_wl_tablet_tool *tool = malloc(sizeof *tool); + tool->id = id; + tool->seat = zwp_tablet_seat_v2_get_user_data(zwp_tablet_seat_v2); + tool->next = tool->seat->tablet_tools; + tool->seat->tablet_tools = tool; + + zwp_tablet_tool_v2_add_listener(tool->id, &zwp_tablet_tool_v2_listener, tool); +} + +void zwp_tablet_seat_v2_handle_pad_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id) {} + +static const struct zwp_tablet_seat_v2_listener zwp_tablet_seat_v2_listener = { + zwp_tablet_seat_v2_handle_tablet_added, + zwp_tablet_seat_v2_handle_tool_added, + zwp_tablet_seat_v2_handle_pad_added, +}; + +static void wl_registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { + if (strcmp(interface, wl_compositor_interface.name) == 0) { + wl_ctx.compositor = wl_registry_bind(wl_ctx.registry, name, &wl_compositor_interface, 4); + } + else if (strcmp(interface, wl_shm_interface.name) == 0) { + wl_ctx.shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + } + else if (strcmp(interface, wl_subcompositor_interface.name) == 0) { + wl_ctx.subcompositor = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); + } + else if (strcmp(interface, wp_viewporter_interface.name) == 0) { + wl_ctx.viewporter = wl_registry_bind(registry, name, &wp_viewporter_interface, 1); + } + else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + wl_ctx.xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); + xdg_wm_base_add_listener(wl_ctx.xdg_wm_base, &xdg_wm_base_listener, NULL); + } + else if (strcmp(interface, wl_seat_interface.name) == 0) { + if (wl_ctx.seat.seat) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Multi-seat configurations not supported"); + return; + } + wl_ctx.seat.seat = wl_registry_bind(registry, name, &wl_seat_interface, version); + + wl_seat_add_listener(wl_ctx.seat.seat, &wl_seat_listener, &wl_ctx.seat); + if (wl_ctx.data_device_manager != NULL) { + wl_ctx.seat.data_device = wl_data_device_manager_get_data_device(wl_ctx.data_device_manager, wl_ctx.seat.seat); + wl_data_device_add_listener(wl_ctx.seat.data_device, &wl_data_device_listener, &wl_ctx.seat); + } + } + else if (strcmp(interface, wl_output_interface.name) == 0) { + int display_index = -1; + for (int i = 0; i < MAXIMUM_WINDOWS; i++) { + if (wl_ctx.displays[i].output == NULL) { + display_index = i; + break; + } + } + if (display_index == -1) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Too much displays (maximum is %i)", MAXIMUM_DISPLAYS); + } + else { + struct kinc_wl_display *display = &wl_ctx.displays[display_index]; + display->output = wl_registry_bind(registry, name, &wl_output_interface, 2); + display->scale = 1; + wl_output_set_user_data(display->output, display); + wl_output_add_listener(display->output, &wl_output_listener, display); + wl_ctx.num_displays++; + } + } + else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { + wl_ctx.decoration_manager = wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, 1); + } + else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) { + wl_ctx.data_device_manager = wl_registry_bind(registry, name, &wl_data_device_manager_interface, 3); + if (wl_ctx.seat.seat != NULL) { + wl_ctx.seat.data_device = wl_data_device_manager_get_data_device(wl_ctx.data_device_manager, wl_ctx.seat.seat); + wl_data_device_add_listener(wl_ctx.seat.data_device, &wl_data_device_listener, &wl_ctx.seat); + } + } + else if (strcmp(interface, zwp_tablet_manager_v2_interface.name) == 0) { + wl_ctx.tablet_manager = wl_registry_bind(registry, name, &zwp_tablet_manager_v2_interface, 1); + if (wl_ctx.seat.seat != NULL) { + wl_ctx.seat.tablet_seat.seat = zwp_tablet_manager_v2_get_tablet_seat(wl_ctx.tablet_manager, wl_ctx.seat.seat); + zwp_tablet_seat_v2_add_listener(wl_ctx.seat.tablet_seat.seat, &zwp_tablet_seat_v2_listener, &wl_ctx.seat.tablet_seat); + } + } + else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0) { + wl_ctx.pointer_constraints = wl_registry_bind(registry, name, &zwp_pointer_constraints_v1_interface, 1); + } + else if (strcmp(interface, zwp_relative_pointer_manager_v1_interface.name) == 0) { + wl_ctx.relative_pointer_manager = wl_registry_bind(registry, name, &zwp_relative_pointer_manager_v1_interface, 1); + } +} + +static void wl_registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { + // TODO: handle output removal +} + +static const struct wl_registry_listener registry_listener = { + wl_registry_handle_global, + wl_registry_handle_global_remove, +}; + +bool kinc_wayland_init() { + if (!kinc_wayland_load_procs()) { + return false; + } + + wl_ctx.xkb_context = wl_xkb.xkb_context_new(XKB_CONTEXT_NO_FLAGS); + + wl_ctx.display = wl_display_connect(NULL); + if (!wl_ctx.display) { + return false; + } + wl_ctx.registry = wl_display_get_registry(wl_ctx.display); + wl_registry_add_listener(wl_ctx.registry, ®istry_listener, NULL); + wl_display_dispatch(wl_ctx.display); + wl_display_roundtrip(wl_ctx.display); + wl_display_roundtrip(wl_ctx.display); + + if (wl_ctx.seat.mouse.pointer && wl_ctx.shm) { + const char *cursor_theme = getenv("XCURSOR_THEME"); + const char *cursor_size_str = getenv("XCURSOR_SIZE"); + int cursor_size = 32; + + if (cursor_size_str) { + char *end_ptr; + long size = strtol(cursor_size_str, &end_ptr, 10); + if (!(*end_ptr) && size > 0 && size < INT32_MAX) { + cursor_size = (int)size; + } + } + + wl_ctx.cursor_theme = wl_cursor_theme_load(cursor_theme, cursor_size, wl_ctx.shm); + } + + return true; +} + +void kinc_wayland_shutdown() { + wl_display_disconnect(wl_ctx.display); + wl_xkb.xkb_context_unref(wl_ctx.xkb_context); +} + +void kinc_wayland_set_selection(struct kinc_wl_seat *seat, const char *text, int serial) { + static const char *mime_types[] = {"text/plain"}; + char *copy = malloc(strlen(text) + 1); + strcpy(copy, text); + struct kinc_wl_data_source *data_source = kinc_wl_create_data_source(seat, mime_types, sizeof mime_types / sizeof mime_types[0], copy, strlen(text)); + wl_data_device_set_selection(seat->data_device, data_source->source, serial); +} + +void kinc_wayland_copy_to_clipboard(const char *text) {} + +#define READ_SIZE 64 + +static bool flush_display(void) { + while (wl_display_flush(wl_ctx.display) == -1) { + if (errno != EAGAIN) + return false; + + struct pollfd fd = {wl_display_get_fd(wl_ctx.display), POLLOUT}; + + while (poll(&fd, 1, 10) == -1) { + if (errno != EINTR && errno != EAGAIN) + return false; + } + } + + return true; +} + +bool kinc_wayland_handle_messages() { + while (wl_display_prepare_read(wl_ctx.display) != 0) + wl_display_dispatch_pending(wl_ctx.display); + if (!flush_display()) { + // Something went wrong, abort. + wl_display_cancel_read(wl_ctx.display); + + for (int i = 0; i < wl_ctx.num_windows; i++) { + kinc_window_destroy(i); + } + + return true; + } + + struct pollfd fds[] = { + {wl_display_get_fd(wl_ctx.display), POLLIN}, + {wl_ctx.seat.keyboard.timerfd, POLLIN}, + }; + + if (poll(fds, sizeof(fds) / sizeof(struct pollfd), 10) <= 0) { + wl_display_cancel_read(wl_ctx.display); + return false; + } + + if (fds[0].revents & POLLIN) { + wl_display_read_events(wl_ctx.display); + wl_display_dispatch_pending(wl_ctx.display); + } + else { + wl_display_cancel_read(wl_ctx.display); + } + + if (fds[1].revents & POLLIN) { + uint64_t repeats; + + if (read(wl_ctx.seat.keyboard.timerfd, &repeats, sizeof(repeats)) == 8) { + if (wl_ctx.seat.keyboard.last_key_code != -1) { + for (uint64_t i = 0; i < repeats; i++) { + kinc_internal_keyboard_trigger_key_press(wl_ctx.seat.keyboard.last_character); + } + } + } + } + + struct kinc_wl_data_offer **offer = &wl_ctx.data_offer_queue; + while (*offer != NULL) { + struct kinc_wl_data_offer *current = *offer; + struct kinc_wl_data_offer **next = ¤t->next; + if (current->buf_pos + READ_SIZE > current->buf_size) { + current->buffer = realloc(current->buffer, current->buf_size + READ_SIZE); + current->buf_size += READ_SIZE; + } + + ssize_t n = read(current->read_fd, current->buffer + current->buf_pos, READ_SIZE); + if (n <= 0) { + *offer = *next; + close(current->read_fd); + + current->callback(current->buffer, current->buf_pos, current->user_data); + + free(current->buffer); + current->buffer = NULL; + current->buf_pos = 0; + current->buf_size = 0; + current->read_fd = 0; + current->next = NULL; + } + else { + current->buf_pos += n; + offer = next; + } + } + return false; +} + +#undef READ_SIZE + +#ifdef KINC_EGL +EGLDisplay kinc_wayland_egl_get_display() { + return eglGetDisplay((EGLNativeDisplayType)wl_ctx.display); +} + +EGLNativeWindowType kinc_wayland_egl_get_native_window(EGLDisplay display, EGLConfig config, int window_index) { + return (EGLNativeWindowType)wl_ctx.windows[window_index].egl_window; +} +#endif + +#ifdef KINC_VULKAN +#include +#include +VkResult kinc_wayland_vulkan_create_surface(VkInstance instance, int window_index, VkSurfaceKHR *surface) { + VkWaylandSurfaceCreateInfoKHR info = {0}; + info.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + info.pNext = NULL; + info.flags = 0; + info.display = wl_ctx.display; + info.surface = wl_ctx.windows[window_index].surface; + return vkCreateWaylandSurfaceKHR(instance, &info, NULL, surface); +} + +#include + +void kinc_wayland_vulkan_get_instance_extensions(const char **names, int *index, int max) { + assert(*index + 1 < max); + names[(*index)++] = VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; +} + +VkBool32 kinc_wayland_vulkan_get_physical_device_presentation_support(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex) { + return vkGetPhysicalDeviceWaylandPresentationSupportKHR(physicalDevice, queueFamilyIndex, wl_ctx.display); +} +#endif + +void zwp_locked_pointer_v1_handle_locked(void *data, struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) { + struct kinc_wl_mouse *mouse = data; + mouse->locked = true; +} + +void zwp_locked_pointer_v1_handle_unlocked(void *data, struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) { + struct kinc_wl_mouse *mouse = data; + mouse->locked = false; +} + +static const struct zwp_locked_pointer_v1_listener zwp_locked_pointer_v1_listener = { + zwp_locked_pointer_v1_handle_locked, + zwp_locked_pointer_v1_handle_unlocked, +}; + +void kinc_wl_mouse_show() { + kinc_wayland_set_cursor(&wl_ctx.seat.mouse, "default"); // TODO: should use the last set cursor instead +} + +void kinc_wl_mouse_hide() { + wl_pointer_set_cursor(wl_ctx.seat.mouse.pointer, wl_ctx.seat.mouse.serial, NULL, 0, 0); +} + +void kinc_wl_mouse_lock(int window) { + struct kinc_wl_mouse *mouse = &wl_ctx.seat.mouse; + struct wl_region *region = wl_compositor_create_region(wl_ctx.compositor); + wl_region_add(region, mouse->x, mouse->y, 0, 0); + mouse->lock = zwp_pointer_constraints_v1_lock_pointer(wl_ctx.pointer_constraints, wl_ctx.windows[window].surface, wl_ctx.seat.mouse.pointer, region, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + zwp_locked_pointer_v1_add_listener(mouse->lock, &zwp_locked_pointer_v1_listener, mouse); +} + +void kinc_wl_mouse_unlock(void) { + zwp_locked_pointer_v1_destroy(wl_ctx.seat.mouse.lock); + wl_ctx.seat.mouse.lock = NULL; + wl_ctx.seat.mouse.locked = false; + kinc_wl_mouse_show(); +} + +bool kinc_wl_mouse_can_lock(void) { + return true; +} + +void kinc_wl_mouse_set_cursor(int cursorIndex) { + const char *name; + switch (cursorIndex) { + case 0: { + name = "arrow"; + break; + } + case 1: { + name = "hand1"; + break; + } + case 2: { + name = "xterm"; + break; + } + case 3: { + name = "sb_h_double_arrow"; + break; + } + case 4: { + name = "sb_v_double_arrow"; + break; + } + case 5: { + name = "top_right_corner"; + break; + } + case 6: { + name = "bottom_right_corner"; + break; + } + case 7: { + name = "top_left_corner"; + break; + } + case 8: { + name = "bottom_left_corner"; + break; + } + case 9: { + name = "grab"; + break; + } + case 10: { + name = "grabbing"; + break; + } + case 11: { + name = "not-allowed"; + break; + } + case 12: { + name = "watch"; + break; + } + case 13: { + name = "crosshair"; + break; + } + default: { + name = "arrow"; + break; + } + } + if (!wl_ctx.seat.mouse.hidden) { + kinc_wayland_set_cursor(&wl_ctx.seat.mouse, name); + } +} + +void kinc_wl_mouse_set_position(int window_index, int x, int y) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland: cannot set the mouse position."); +} + +void kinc_wl_mouse_get_position(int window_index, int *x, int *y) { + *x = wl_ctx.seat.mouse.x; + *y = wl_ctx.seat.mouse.y; +} diff --git a/armorcore/sources/backends/linux/kinc/backend/wayland/wayland-funs.h b/armorcore/sources/backends/linux/kinc/backend/wayland/wayland-funs.h new file mode 100644 index 000000000..608288f59 --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/wayland/wayland-funs.h @@ -0,0 +1,54 @@ +#ifndef KINC_WL_FN +#define KINC_WL_FN(ret, name, args) +#endif + +KINC_WL_FUN(void, wl_event_queue_destroy, (struct wl_event_queue * queue)) +#if KINC_WL_CHECK_VERSION(1, 20, 0) +KINC_WL_FUN(struct wl_proxy *, wl_proxy_marshal_flags, + (struct wl_proxy * proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, uint32_t flags, ...)) +KINC_WL_FUN(struct wl_proxy *, wl_proxy_marshal_array_flags, + (struct wl_proxy * proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, uint32_t flags, union wl_argument *args)) +#endif +KINC_WL_FUN(void, wl_proxy_marshal, (struct wl_proxy * p, uint32_t opcode, ...)) +KINC_WL_FUN(void, wl_proxy_marshal_array, (struct wl_proxy * p, uint32_t opcode, union wl_argument *args)) +KINC_WL_FUN(struct wl_proxy *, wl_proxy_create, (struct wl_proxy * factory, const struct wl_interface *interface)) +KINC_WL_FUN(void *, wl_proxy_create_wrapper, (void *proxy)) +KINC_WL_FUN(void, wl_proxy_wrapper_destroy, (void *proxy_wrapper)) +KINC_WL_FUN(struct wl_proxy *, wl_proxy_marshal_constructor, (struct wl_proxy * proxy, uint32_t opcode, const struct wl_interface *interface, ...)) +KINC_WL_FUN(struct wl_proxy *, wl_proxy_marshal_constructor_versioned, + (struct wl_proxy * proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, ...)) +KINC_WL_FUN(struct wl_proxy *, wl_proxy_marshal_array_constructor, + (struct wl_proxy * proxy, uint32_t opcode, union wl_argument *args, const struct wl_interface *interface)) +KINC_WL_FUN(struct wl_proxy *, wl_proxy_marshal_array_constructor_versioned, + (struct wl_proxy * proxy, uint32_t opcode, union wl_argument *args, const struct wl_interface *interface, uint32_t version)) +KINC_WL_FUN(void, wl_proxy_destroy, (struct wl_proxy * proxy)) +KINC_WL_FUN(int, wl_proxy_add_listener, (struct wl_proxy * proxy, void (**implementation)(void), void *data)) +KINC_WL_FUN(const void *, wl_proxy_get_listener, (struct wl_proxy * proxy)) +KINC_WL_FUN(int, wl_proxy_add_dispatcher, (struct wl_proxy * proxy, wl_dispatcher_func_t dispatcher_func, const void *dispatcher_data, void *data)) +KINC_WL_FUN(void, wl_proxy_set_user_data, (struct wl_proxy * proxy, void *user_data)) +KINC_WL_FUN(void *, wl_proxy_get_user_data, (struct wl_proxy * proxy)) +KINC_WL_FUN(uint32_t, wl_proxy_get_version, (struct wl_proxy * proxy)) +KINC_WL_FUN(uint32_t, wl_proxy_get_id, (struct wl_proxy * proxy)) +KINC_WL_FUN(void, wl_proxy_set_tag, (struct wl_proxy * proxy, const char *const *tag)) +KINC_WL_FUN(const char *const *, wl_proxy_get_tag, (struct wl_proxy * proxy)) +KINC_WL_FUN(const char *, wl_proxy_get_class, (struct wl_proxy * proxy)) +KINC_WL_FUN(void, wl_proxy_set_queue, (struct wl_proxy * proxy, struct wl_event_queue *queue)) +KINC_WL_FUN(struct wl_display *, wl_display_connect, (const char *name)) +KINC_WL_FUN(struct wl_display *, wl_display_connect_to_fd, (int fd)) +KINC_WL_FUN(void, wl_display_disconnect, (struct wl_display * display)) +KINC_WL_FUN(int, wl_display_get_fd, (struct wl_display * display)) +KINC_WL_FUN(int, wl_display_dispatch, (struct wl_display * display)) +KINC_WL_FUN(int, wl_display_dispatch_queue, (struct wl_display * display, struct wl_event_queue *queue)) +KINC_WL_FUN(int, wl_display_dispatch_queue_pending, (struct wl_display * display, struct wl_event_queue *queue)) +KINC_WL_FUN(int, wl_display_dispatch_pending, (struct wl_display * display)) +KINC_WL_FUN(int, wl_display_get_error, (struct wl_display * display)) +KINC_WL_FUN(uint32_t, wl_display_get_protocol_error, (struct wl_display * display, const struct wl_interface **interface, uint32_t *id)) +KINC_WL_FUN(int, wl_display_flush, (struct wl_display * display)) +KINC_WL_FUN(int, wl_display_roundtrip_queue, (struct wl_display * display, struct wl_event_queue *queue)) +KINC_WL_FUN(int, wl_display_roundtrip, (struct wl_display * display)) +KINC_WL_FUN(struct wl_event_queue *, wl_display_create_queue, (struct wl_display * display)) +KINC_WL_FUN(int, wl_display_prepare_read_queue, (struct wl_display * display, struct wl_event_queue *queue)) +KINC_WL_FUN(int, wl_display_prepare_read, (struct wl_display * display)) +KINC_WL_FUN(void, wl_display_cancel_read, (struct wl_display * display)) +KINC_WL_FUN(int, wl_display_read_events, (struct wl_display * display)) +KINC_WL_FUN(void, wl_log_set_handler_client, (wl_log_func_t handler)) diff --git a/armorcore/sources/backends/linux/kinc/backend/wayland/wayland.h b/armorcore/sources/backends/linux/kinc/backend/wayland/wayland.h new file mode 100644 index 000000000..83498c086 --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/wayland/wayland.h @@ -0,0 +1,367 @@ +#pragma once +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // put this here too, to make clangd happy +#endif +#include +#include +#include + +#include +#include + +#define KINC_WL_CHECK_VERSION(x, y, z) \ + (WAYLAND_VERSION_MAJOR > x || (WAYLAND_VERSION_MAJOR == x && WAYLAND_VERSION_MINOR > y) || \ + (WAYLAND_VERSION_MAJOR == x && WAYLAND_VERSION_MINOR == y && WAYLAND_VERSION_MICRO >= z)) + +struct wl_surface; + +struct kinc_wl_procs { +#define KINC_WL_FUN(ret, name, args) ret(*_##name) args; +#include "wayland-funs.h" +#undef KINC_WL_FUN + + struct wl_cursor_theme *(*_wl_cursor_theme_load)(const char *name, int size, struct wl_shm *shm); + void (*_wl_cursor_theme_destroy)(struct wl_cursor_theme *theme); + struct wl_cursor *(*_wl_cursor_theme_get_cursor)(struct wl_cursor_theme *theme, const char *name); + struct wl_buffer *(*_wl_cursor_image_get_buffer)(struct wl_cursor_image *image); + int (*_wl_cursor_frame)(struct wl_cursor *cursor, uint32_t time); + int (*_wl_cursor_frame_and_duration)(struct wl_cursor *cursor, uint32_t time, uint32_t *duration); + +#ifdef KINC_EGL + struct wl_egl_window *(*_wl_egl_window_create)(struct wl_surface *surface, int width, int height); + void (*_wl_egl_window_destroy)(struct wl_egl_window *egl_window); + void (*_wl_egl_window_resize)(struct wl_egl_window *egl_window, int width, int height, int dx, int dy); + void (*_wl_egl_window_get_attached_size)(struct wl_egl_window *egl_window, int *width, int *height); +#endif +}; + +#define wl_event_queue_destroy wl._wl_event_queue_destroy +#define wl_proxy_marshal_flags wl._wl_proxy_marshal_flags +#define wl_proxy_marshal_array_flags wl._wl_proxy_marshal_array_flags +#define wl_proxy_marshal wl._wl_proxy_marshal +#define wl_proxy_marshal_array wl._wl_proxy_marshal_array +#define wl_proxy_create wl._wl_proxy_create +#define wl_proxy_create_wrapper wl._wl_proxy_create_wrapper +#define wl_proxy_wrapper_destroy wl._wl_proxy_wrapper_destroy +#define wl_proxy_marshal_constructor wl._wl_proxy_marshal_constructor +#define wl_proxy_marshal_constructor_versioned wl._wl_proxy_marshal_constructor_versioned +#define wl_proxy_marshal_array_constructor wl._wl_proxy_marshal_array_constructor +#define wl_proxy_marshal_array_constructor_versioned wl._wl_proxy_marshal_array_constructor_versioned +#define wl_proxy_destroy wl._wl_proxy_destroy +#define wl_proxy_add_listener wl._wl_proxy_add_listener +#define wl_proxy_get_listener wl._wl_proxy_get_listener +#define wl_proxy_add_dispatcher wl._wl_proxy_add_dispatcher +#define wl_proxy_set_user_data wl._wl_proxy_set_user_data +#define wl_proxy_get_user_data wl._wl_proxy_get_user_data +#define wl_proxy_get_version wl._wl_proxy_get_version +#define wl_proxy_get_id wl._wl_proxy_get_id +#define wl_proxy_set_tag wl._wl_proxy_set_tag +#define wl_proxy_get_tag wl._wl_proxy_get_tag +#define wl_proxy_get_class wl._wl_proxy_get_class +#define wl_proxy_set_queue wl._wl_proxy_set_queue +#define wl_display_connect wl._wl_display_connect +#define wl_display_connect_to_fd wl._wl_display_connect_to_fd +#define wl_display_disconnect wl._wl_display_disconnect +#define wl_display_get_fd wl._wl_display_get_fd +#define wl_display_dispatch wl._wl_display_dispatch +#define wl_display_dispatch_queue wl._wl_display_dispatch_queue +#define wl_display_dispatch_queue_pending wl._wl_display_dispatch_queue_pending +#define wl_display_dispatch_pending wl._wl_display_dispatch_pending +#define wl_display_get_error wl._wl_display_get_error +#define wl_display_get_protocol_error wl._wl_display_get_protocol_error +#define wl_display_flush wl._wl_display_flush +#define wl_display_roundtrip_queue wl._wl_display_roundtrip_queue +#define wl_display_roundtrip wl._wl_display_roundtrip +#define wl_display_create_queue wl._wl_display_create_queue +#define wl_display_prepare_read_queue wl._wl_display_prepare_read_queue +#define wl_display_prepare_read wl._wl_display_prepare_read +#define wl_display_cancel_read wl._wl_display_cancel_read +#define wl_display_read_events wl._wl_display_read_events +#define wl_log_set_handler_client wl._wl_log_set_handler_client + +#define wl_cursor_theme_load wl._wl_cursor_theme_load +#define wl_cursor_theme_destroy wl._wl_cursor_theme_destroy +#define wl_cursor_theme_get_cursor wl._wl_cursor_theme_get_cursor +#define wl_cursor_image_get_buffer wl._wl_cursor_image_get_buffer +#define wl_cursor_frame wl._wl_cursor_frame +#define wl_cursor_frame_and_duration wl._wl_cursor_frame_and_duration + +#define wl_egl_window_create wl._wl_egl_window_create +#define wl_egl_window_destroy wl._wl_egl_window_destroy +#define wl_egl_window_resize wl._wl_egl_window_resize + +extern struct kinc_wl_procs wl; + +#include +#include +#include +#include +#include +#include +#include + +#include + +struct kinc_xkb_procs { + struct xkb_context *(*xkb_context_new)(enum xkb_context_flags flags); + void (*xkb_context_unref)(struct xkb_context *context); + struct xkb_keymap *(*xkb_keymap_new_from_string)(struct xkb_context *context, const char *string, enum xkb_keymap_format format, + enum xkb_keymap_compile_flags flags); + struct xkb_state *(*xkb_state_new)(struct xkb_keymap *keymap); + xkb_keysym_t (*xkb_state_key_get_one_sym)(struct xkb_state *state, xkb_keycode_t key); + uint32_t (*xkb_state_key_get_utf32)(struct xkb_state *state, xkb_keycode_t key); + xkb_mod_mask_t (*xkb_state_serialize_mods)(struct xkb_state *state, enum xkb_state_component components); + enum xkb_state_component (*xkb_state_update_mask)(struct xkb_state *state, xkb_mod_mask_t depressed_mods, xkb_mod_mask_t latched_mods, + xkb_mod_mask_t locked_mods, xkb_layout_index_t depressed_layout, xkb_layout_index_t latched_layout, + xkb_layout_index_t locked_layout); + int (*xkb_state_mod_name_is_active)(struct xkb_state *state, const char *name, enum xkb_state_component type); + int (*xkb_keymap_key_repeats)(struct xkb_keymap *keymap, xkb_keycode_t key); +}; + +extern struct kinc_xkb_procs wl_xkb; + +#include +#include +#include +#include + +#define MAXIMUM_WINDOWS 16 +#define MAXIMUM_DISPLAYS 16 +#define MAXIMUM_DISPLAY_MODES 16 + +struct kinc_wl_decoration { + struct wl_surface *surface; + struct wl_subsurface *subsurface; + struct wp_viewport *viewport; +}; + +enum kinc_wl_decoration_focus { + KINC_WL_DECORATION_FOCUS_MAIN, + KINC_WL_DECORATION_FOCUS_TOP, + KINC_WL_DECORATION_FOCUS_LEFT, + KINC_WL_DECORATION_FOCUS_RIGHT, + KINC_WL_DECORATION_FOCUS_BOTTOM, + KINC_WL_DECORATION_FOCUS_CLOSE_BUTTON, + KINC_WL_DECORATION_FOCUS_MAX_BUTTON, + KINC_WL_DECORATION_FOCUS_MIN_BUTTON +}; + +#define KINC_WL_DECORATION_WIDTH 10 + +#define KINC_WL_DECORATION_TOP_X 0 +#define KINC_WL_DECORATION_TOP_Y -(KINC_WL_DECORATION_TOP_HEIGHT) +#define KINC_WL_DECORATION_TOP_WIDTH window->width +#define KINC_WL_DECORATION_TOP_HEIGHT KINC_WL_DECORATION_WIDTH * 3 + +#define KINC_WL_DECORATION_LEFT_X -10 +#define KINC_WL_DECORATION_LEFT_Y -(KINC_WL_DECORATION_TOP_HEIGHT) +#define KINC_WL_DECORATION_LEFT_WIDTH KINC_WL_DECORATION_WIDTH +#define KINC_WL_DECORATION_LEFT_HEIGHT window->height + KINC_WL_DECORATION_TOP_HEIGHT + KINC_WL_DECORATION_BOTTOM_HEIGHT + +#define KINC_WL_DECORATION_RIGHT_X window->width +#define KINC_WL_DECORATION_RIGHT_Y -(KINC_WL_DECORATION_TOP_HEIGHT) +#define KINC_WL_DECORATION_RIGHT_WIDTH 10 +#define KINC_WL_DECORATION_RIGHT_HEIGHT window->height + KINC_WL_DECORATION_TOP_HEIGHT + KINC_WL_DECORATION_BOTTOM_HEIGHT + +#define KINC_WL_DECORATION_BOTTOM_X 0 +#define KINC_WL_DECORATION_BOTTOM_Y window->height +#define KINC_WL_DECORATION_BOTTOM_WIDTH window->width +#define KINC_WL_DECORATION_BOTTOM_HEIGHT KINC_WL_DECORATION_WIDTH + +#define KINC_WL_DECORATION_CLOSE_X window->width - 10 +#define KINC_WL_DECORATION_CLOSE_Y -20 +#define KINC_WL_DECORATION_CLOSE_WIDTH 9 +#define KINC_WL_DECORATION_CLOSE_HEIGHT 9 + +struct kinc_wl_window { + int display_index; + int window_id; + int width; + int height; + kinc_window_mode_t mode; + struct wl_surface *surface; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *toplevel; + struct zxdg_toplevel_decoration_v1 *xdg_decoration; + + bool configured; + + struct { + bool server_side; + enum kinc_wl_decoration_focus focus; + struct kinc_wl_decoration top; + struct kinc_wl_decoration left; + struct kinc_wl_decoration right; + struct kinc_wl_decoration bottom; + + struct kinc_wl_decoration close; + struct kinc_wl_decoration max; + struct kinc_wl_decoration min; + + struct wl_buffer *dec_buffer; + struct wl_buffer *close_buffer; + struct wl_buffer *max_buffer; + struct wl_buffer *min_buffer; + } decorations; +#ifdef KINC_EGL + struct wl_egl_window *egl_window; +#endif +}; + +struct kinc_wl_display { + struct wl_output *output; + int index; + int current_mode; + int num_modes; + char name[64]; + int x; + int y; + int physical_width; + int physical_height; + int subpixel; + enum wl_output_transform transform; + enum wl_output_subpixel scale; + kinc_display_mode_t modes[MAXIMUM_DISPLAY_MODES]; +}; + +struct kinc_wl_mouse { + struct kinc_wl_seat *seat; + int current_window; + int x; + int y; + int enter_serial; + const char *previous_cursor_name; + struct wl_pointer *pointer; + struct wl_surface *surface; + bool hidden; + bool locked; + struct zwp_locked_pointer_v1 *lock; + struct zwp_relative_pointer_v1 *relative; + int serial; +}; + +struct kinc_wl_keyboard { + struct kinc_wl_seat *seat; + struct wl_keyboard *keyboard; + struct xkb_keymap *keymap; + struct xkb_state *state; + bool ctrlDown; + + int repeat_delay; + int repeat_rate; + + int timerfd; + + int last_key_code; + int last_character; +}; + +struct kinc_wl_data_offer { + struct wl_data_offer *id; + + int mime_type_count; + char **mime_types; + + uint32_t source_actions; + uint32_t dnd_action; + + void (*callback)(void *data, size_t data_size, void *user_data); + void *user_data; + int read_fd; + + void *buffer; + size_t buf_size; + size_t buf_pos; + + struct kinc_wl_data_offer *next; +}; + +struct kinc_wl_data_source { + struct wl_data_source *source; + const char **mime_types; + int num_mime_types; + void *data; + size_t data_size; +}; + +struct kinc_wl_tablet_tool { + struct zwp_tablet_tool_v2 *id; + enum zwp_tablet_tool_v2_type type; + uint32_t capabilities; + uint64_t hardware_serial; + uint64_t hardware_id_wacom; + + int current_window; + int x; + int y; + float current_pressure; + float current_distance; + + void (*press)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/); + void (*move)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/); + void (*release)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/); + + struct kinc_wl_tablet_seat *seat; + struct kinc_wl_tablet_tool *next; +}; + +struct kinc_wl_tablet { + struct zwp_tablet_v2 *id; + struct kinc_wl_tablet_seat *seat; + struct kinc_wl_tablet *next; +}; + +struct kinc_wl_tablet_seat { + struct zwp_tablet_seat_v2 *seat; + struct kinc_wl_tablet *tablets; + struct kinc_wl_tablet_tool *tablet_tools; +}; + +struct kinc_wl_seat { + struct wl_seat *seat; + struct kinc_wl_keyboard keyboard; + struct kinc_wl_mouse mouse; + struct wl_touch *touch; + struct kinc_wl_tablet_seat tablet_seat; + struct wl_data_device *data_device; + struct kinc_wl_data_offer *current_selection_offer; + struct kinc_wl_data_offer *current_dnd_offer; + int current_serial; + uint32_t capabilities; + char name[64]; +}; + +struct wayland_context { + struct xkb_context *xkb_context; + + struct wl_display *display; + struct wl_shm *shm; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_subcompositor *subcompositor; + struct wp_viewporter *viewporter; + struct kinc_wl_seat seat; + struct xdg_wm_base *xdg_wm_base; + struct zxdg_decoration_manager_v1 *decoration_manager; + struct wl_data_device_manager *data_device_manager; + struct zwp_tablet_manager_v2 *tablet_manager; + struct zwp_pointer_constraints_v1 *pointer_constraints; + struct zwp_relative_pointer_manager_v1 *relative_pointer_manager; + struct wl_cursor_theme *cursor_theme; + int cursor_size; + int num_windows; + struct kinc_wl_window windows[MAXIMUM_WINDOWS]; + int num_displays; + struct kinc_wl_display displays[MAXIMUM_DISPLAYS]; + + struct kinc_wl_data_offer *data_offer_queue; +}; + +extern struct wayland_context wl_ctx; + +struct kinc_wl_data_source *kinc_wl_create_data_source(struct kinc_wl_seat *seat, const char *mime_types[], int num_mime_types, void *data, size_t data_size); +void kinc_wl_data_source_destroy(struct kinc_wl_data_source *data_source); +void kinc_wl_data_offer_accept(struct kinc_wl_data_offer *offer, void (*callback)(void *data, size_t data_size, void *user_data), void *user_data); +void kinc_wl_destroy_data_offer(struct kinc_wl_data_offer *offer); +void kinc_wayland_set_selection(struct kinc_wl_seat *seat, const char *text, int serial); +void kinc_wayland_window_destroy(int window_index); diff --git a/armorcore/sources/backends/linux/kinc/backend/wayland/window.c.h b/armorcore/sources/backends/linux/kinc/backend/wayland/window.c.h new file mode 100644 index 000000000..f63ec099a --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/wayland/window.c.h @@ -0,0 +1,469 @@ +#include "wayland.h" + +#include +#include + +// for all that shared memory stuff later on +#include +#include +#include +#include +#include +#include + +#ifdef KINC_EGL +#define EGL_NO_PLATFORM_SPECIFIC_TYPES +#include +#endif + +static void xdg_surface_handle_configure(void *data, struct xdg_surface *surface, uint32_t serial) { + xdg_surface_ack_configure(surface, serial); + struct kinc_wl_window *window = data; + window->configured = true; +} + +void kinc_internal_resize(int, int, int); +void kinc_wayland_destroy_decoration(struct kinc_wl_decoration *); +void kinc_wayland_resize_decoration(struct kinc_wl_decoration *, int x, int y, int width, int height); +static void xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states) { + struct kinc_wl_window *window = data; + if ((width <= 0 || height <= 0) || (width == window->width + (KINC_WL_DECORATION_WIDTH * 2) && + height == window->height + KINC_WL_DECORATION_TOP_HEIGHT + KINC_WL_DECORATION_BOTTOM_HEIGHT)) { + return; + } + if (window->decorations.server_side) { + window->width = width; + window->height = height; + } + else { + window->width = width - (KINC_WL_DECORATION_WIDTH * 2); + window->height = height - KINC_WL_DECORATION_TOP_HEIGHT + KINC_WL_DECORATION_BOTTOM_HEIGHT; + } + + enum xdg_toplevel_state *state; + wl_array_for_each(state, states) { + switch (*state) { + case XDG_TOPLEVEL_STATE_ACTIVATED: + kinc_internal_foreground_callback(); + break; + case XDG_TOPLEVEL_STATE_RESIZING: + break; + case XDG_TOPLEVEL_STATE_MAXIMIZED: + break; + default: + break; + } + } + kinc_internal_resize(window->window_id, window->width, window->height); + kinc_internal_call_resize_callback(window->window_id, window->width, window->height); + if (window->decorations.server_side) { + xdg_surface_set_window_geometry(window->xdg_surface, 0, 0, window->width, window->height); + } + else { + xdg_surface_set_window_geometry(window->xdg_surface, KINC_WL_DECORATION_LEFT_X, KINC_WL_DECORATION_TOP_Y, + window->width + (KINC_WL_DECORATION_WIDTH * 2), + window->height + KINC_WL_DECORATION_TOP_HEIGHT + KINC_WL_DECORATION_BOTTOM_HEIGHT); + } +#ifdef KINC_EGL + wl_egl_window_resize(window->egl_window, window->width, window->height, 0, 0); +#endif + + kinc_wayland_resize_decoration(&window->decorations.top, KINC_WL_DECORATION_TOP_X, KINC_WL_DECORATION_TOP_Y, KINC_WL_DECORATION_TOP_WIDTH, + KINC_WL_DECORATION_TOP_HEIGHT); + kinc_wayland_resize_decoration(&window->decorations.left, KINC_WL_DECORATION_LEFT_X, KINC_WL_DECORATION_LEFT_Y, KINC_WL_DECORATION_LEFT_WIDTH, + KINC_WL_DECORATION_LEFT_HEIGHT); + kinc_wayland_resize_decoration(&window->decorations.right, KINC_WL_DECORATION_RIGHT_X, KINC_WL_DECORATION_RIGHT_Y, KINC_WL_DECORATION_RIGHT_WIDTH, + KINC_WL_DECORATION_RIGHT_HEIGHT); + kinc_wayland_resize_decoration(&window->decorations.bottom, KINC_WL_DECORATION_BOTTOM_X, KINC_WL_DECORATION_BOTTOM_Y, KINC_WL_DECORATION_BOTTOM_WIDTH, + KINC_WL_DECORATION_BOTTOM_HEIGHT); + kinc_wayland_resize_decoration(&window->decorations.close, KINC_WL_DECORATION_CLOSE_X, KINC_WL_DECORATION_CLOSE_Y, KINC_WL_DECORATION_CLOSE_WIDTH, + KINC_WL_DECORATION_CLOSE_HEIGHT); +} + +void kinc_wayland_window_destroy(int window_index); + +static void xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel) { + struct kinc_wl_window *window = data; + if (kinc_internal_call_close_callback(window->window_id)) { + kinc_window_destroy(window->window_id); + if (wl_ctx.num_windows <= 0) { + // no windows left, stop + kinc_stop(); + } + } +} + +static int create_shm_fd(off_t size) { + int fd = -1; +#if defined(__linux__) +#if defined(__GLIBC__) && !__GLIBC_PREREQ(2, 27) +#else + // memfd_create is available since glibc 2.27 and musl 1.1.20 + // the syscall is available since linux 3.17 + // at the time of writing (04/02/2022) these requirements are fullfilled for the "LTS" versions of the following distributions + // Ubuntu 18.04 + // Debian Stretch + // Alpine 3.12 + // Fedora 34 + + fd = memfd_create("kinc-wayland-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd >= 0) { + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + int ret = posix_fallocate(fd, 0, size); + if (ret != 0) { + close(fd); + errno = ret; + return -1; + } + } + else // fall back to a temp file +#endif +#endif + { + + static const char template[] = "/kinc-shared-XXXXXX"; + + const char *path = getenv("XDG_RUNTIME_DIR"); + if (!path) { + errno = ENOENT; + return -1; + } + + char *name = malloc(strlen(path) + sizeof(template)); + strcpy(name, path); + strcat(name, template); + + fd = mkostemp(name, O_CLOEXEC); + if (fd >= 0) + unlink(name); + + free(name); + if (fd < 0) + return -1; + + int ret = ftruncate(fd, size); + if (ret != 0) { + close(fd); + errno = ret; + return -1; + } + } + + return fd; +} + +struct wl_buffer *kinc_wayland_create_shm_buffer(const kinc_image_t *image) { + int stride = image->width * 4; + int length = image->width * image->height * 4; + + const int fd = create_shm_fd(length); + if (fd < 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland: Creating a buffer file for %d B failed: %s", length, strerror(errno)); + return NULL; + } + + void *data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland: mmap failed: %s", strerror(errno)); + close(fd); + return NULL; + } + + struct wl_shm_pool *pool = wl_shm_create_pool(wl_ctx.shm, fd, length); + + close(fd); + memcpy(data, image->data, image->width * image->height * 4); + + struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, image->width, image->height, stride, WL_SHM_FORMAT_ARGB8888); + munmap(data, length); + wl_shm_pool_destroy(pool); + + return buffer; +} + +static int grey_data[] = {0xFF333333}; + +static int close_data[] = { + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, +}; + +// image format is argb32, but kinc does not have that, so let's lie to it + +static kinc_image_t grey_image = { + 1, 1, 0, KINC_IMAGE_FORMAT_RGBA32, 0, KINC_IMAGE_COMPRESSION_NONE, grey_data, sizeof(grey_data), +}; +static kinc_image_t close_image = { + 9, 9, 0, KINC_IMAGE_FORMAT_RGBA32, 0, KINC_IMAGE_COMPRESSION_NONE, close_data, sizeof(close_data), +}; + +void kinc_wayland_create_decoration(struct kinc_wl_decoration *decoration, struct wl_surface *parent, struct wl_buffer *buffer, bool opaque, int x, int y, + int width, int height) { + decoration->surface = wl_compositor_create_surface(wl_ctx.compositor); + decoration->subsurface = wl_subcompositor_get_subsurface(wl_ctx.subcompositor, decoration->surface, parent); + wl_subsurface_set_position(decoration->subsurface, x, y); + decoration->viewport = wp_viewporter_get_viewport(wl_ctx.viewporter, decoration->surface); + wp_viewport_set_destination(decoration->viewport, width, height); + if (buffer) + wl_surface_attach(decoration->surface, buffer, 0, 0); + + if (opaque) { + struct wl_region *region = wl_compositor_create_region(wl_ctx.compositor); + wl_region_add(region, 0, 0, width, height); + wl_surface_set_opaque_region(decoration->surface, region); + wl_surface_commit(decoration->surface); + wl_region_destroy(region); + } + else + wl_surface_commit(decoration->surface); +} + +void kinc_wayland_resize_decoration(struct kinc_wl_decoration *decoration, int x, int y, int width, int height) { + if (decoration->surface) { + wl_subsurface_set_position(decoration->subsurface, x, y); + wp_viewport_set_destination(decoration->viewport, width, height); + wl_surface_commit(decoration->surface); + } +} + +void kinc_wayland_destroy_decoration(struct kinc_wl_decoration *decoration) { + if (decoration->subsurface) + wl_subsurface_destroy(decoration->subsurface); + if (decoration->surface) + wl_surface_destroy(decoration->surface); + if (decoration->viewport) + wp_viewport_destroy(decoration->viewport); + decoration->surface = NULL; + decoration->subsurface = NULL; + decoration->viewport = NULL; +} + +void kinc_wayland_destroy_decorations(struct kinc_wl_window *window) { + kinc_wayland_destroy_decoration(&window->decorations.top); + kinc_wayland_destroy_decoration(&window->decorations.left); + kinc_wayland_destroy_decoration(&window->decorations.right); + kinc_wayland_destroy_decoration(&window->decorations.bottom); + kinc_wayland_destroy_decoration(&window->decorations.close); +} + +void kinc_wayland_create_decorations(struct kinc_wl_window *window) { + if (!window->decorations.dec_buffer) { + window->decorations.dec_buffer = kinc_wayland_create_shm_buffer(&grey_image); + window->decorations.close_buffer = kinc_wayland_create_shm_buffer(&close_image); + window->decorations.max_buffer = kinc_wayland_create_shm_buffer(&grey_image); + window->decorations.min_buffer = kinc_wayland_create_shm_buffer(&grey_image); + } + kinc_wayland_create_decoration(&window->decorations.top, window->surface, window->decorations.dec_buffer, true, KINC_WL_DECORATION_TOP_X, + KINC_WL_DECORATION_TOP_Y, KINC_WL_DECORATION_TOP_WIDTH, KINC_WL_DECORATION_TOP_HEIGHT); + kinc_wayland_create_decoration(&window->decorations.left, window->surface, window->decorations.dec_buffer, true, KINC_WL_DECORATION_LEFT_X, + KINC_WL_DECORATION_LEFT_Y, KINC_WL_DECORATION_LEFT_WIDTH, KINC_WL_DECORATION_LEFT_HEIGHT); + kinc_wayland_create_decoration(&window->decorations.right, window->surface, window->decorations.dec_buffer, true, KINC_WL_DECORATION_RIGHT_X, + KINC_WL_DECORATION_RIGHT_Y, KINC_WL_DECORATION_RIGHT_WIDTH, KINC_WL_DECORATION_RIGHT_HEIGHT); + kinc_wayland_create_decoration(&window->decorations.bottom, window->surface, window->decorations.dec_buffer, true, KINC_WL_DECORATION_BOTTOM_X, + KINC_WL_DECORATION_BOTTOM_Y, KINC_WL_DECORATION_BOTTOM_WIDTH, KINC_WL_DECORATION_BOTTOM_HEIGHT); + kinc_wayland_create_decoration(&window->decorations.close, window->surface, window->decorations.close_buffer, true, KINC_WL_DECORATION_CLOSE_X, + KINC_WL_DECORATION_CLOSE_Y, KINC_WL_DECORATION_CLOSE_WIDTH, KINC_WL_DECORATION_CLOSE_HEIGHT); +} + +void xdg_toplevel_decoration_configure(void *data, struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1, uint32_t mode) { + struct kinc_wl_window *window = data; + + if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) { + window->decorations.server_side = false; + if (window->decorations.top.surface) { + kinc_wayland_destroy_decorations(window); + } + if (window->mode == KINC_WINDOW_MODE_WINDOW) { + kinc_wayland_create_decorations(window); + } + } + else if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) { + window->decorations.server_side = true; + if (window->decorations.top.surface) { + kinc_wayland_destroy_decorations(window); + } + } +} + +void wl_surface_handle_enter(void *data, struct wl_surface *wl_surface, struct wl_output *output) { + struct kinc_wl_window *window = wl_surface_get_user_data(wl_surface); + struct kinc_wl_display *display = wl_output_get_user_data(output); + + if (display && window) { + window->display_index = display->index; + } +} + +void wl_surface_handle_leave(void *data, struct wl_surface *wl_surface, struct wl_output *output) {} + +static const struct wl_surface_listener wl_surface_listener = { + wl_surface_handle_enter, + wl_surface_handle_leave, +}; + +static const struct xdg_surface_listener xdg_surface_listener = { + xdg_surface_handle_configure, +}; + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + xdg_toplevel_handle_configure, + xdg_toplevel_handle_close, +}; + +static const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_listener = { + xdg_toplevel_decoration_configure, +}; + +void kinc_wayland_window_set_title(int window_index, const char *title); +void kinc_wayland_window_change_mode(int window_index, kinc_window_mode_t mode); + +int kinc_wayland_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) { + int window_index = -1; + for (int i = 0; i < MAXIMUM_WINDOWS; i++) { + if (wl_ctx.windows[i].surface == NULL) { + window_index = i; + break; + } + } + if (window_index == -1) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Too much windows (maximum is %i)", MAXIMUM_WINDOWS); + exit(1); + } + struct kinc_wl_window *window = &wl_ctx.windows[window_index]; + window->window_id = window_index; + window->width = win->width; + window->height = win->height; + window->mode = KINC_WINDOW_MODE_WINDOW; + window->surface = wl_compositor_create_surface(wl_ctx.compositor); + wl_surface_set_user_data(window->surface, window); + wl_surface_add_listener(window->surface, &wl_surface_listener, NULL); + + window->xdg_surface = xdg_wm_base_get_xdg_surface(wl_ctx.xdg_wm_base, window->surface); + xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window); + + window->toplevel = xdg_surface_get_toplevel(window->xdg_surface); + xdg_toplevel_add_listener(window->toplevel, &xdg_toplevel_listener, window); + + kinc_wayland_window_set_title(window_index, win->title); + + if (wl_ctx.decoration_manager) { + window->xdg_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(wl_ctx.decoration_manager, window->toplevel); +#ifdef KINC_WAYLAND_FORCE_CSD + zxdg_toplevel_decoration_v1_set_mode(window->xdg_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); +#endif + zxdg_toplevel_decoration_v1_add_listener(window->xdg_decoration, &xdg_toplevel_decoration_listener, window); + } + else { + window->decorations.server_side = false; + kinc_wayland_create_decorations(window); + } + +#ifdef KINC_EGL + window->egl_window = wl_egl_window_create(window->surface, window->width, window->height); +#endif + wl_surface_commit(window->surface); + kinc_wayland_window_change_mode(window_index, win->mode); + wl_ctx.num_windows++; + + while (!window->configured) { + wl_display_roundtrip(wl_ctx.display); + } + + return window_index; +} + +void kinc_wayland_window_destroy(int window_index) { + struct kinc_wl_window *window = &wl_ctx.windows[window_index]; +#ifdef KINC_EGL + wl_egl_window_destroy(window->egl_window); +#endif + if (window->xdg_decoration) { + zxdg_toplevel_decoration_v1_destroy(window->xdg_decoration); + } + + xdg_toplevel_destroy(window->toplevel); + xdg_surface_destroy(window->xdg_surface); + wl_surface_destroy(window->surface); + *window = (struct kinc_wl_window){0}; + wl_ctx.num_windows--; +} + +void kinc_wayland_window_set_title(int window_index, const char *title) { + struct kinc_wl_window *window = &wl_ctx.windows[window_index]; + xdg_toplevel_set_title(window->toplevel, title == NULL ? "" : title); +} + +int kinc_wayland_window_x(int window_index) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland does not support getting the window position."); + return 0; +} + +int kinc_wayland_window_y(int window_index) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland does not support getting the window position."); + return 0; +} + +void kinc_wayland_window_move(int window_index, int x, int y) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland does not support setting the window position."); +} + +int kinc_wayland_window_width(int window_index) { + return wl_ctx.windows[window_index].width; +} + +int kinc_wayland_window_height(int window_index) { + return wl_ctx.windows[window_index].height; +} + +void kinc_wayland_window_resize(int window_index, int width, int height) { + kinc_log(KINC_LOG_LEVEL_WARNING, "TODO: resizing windows"); +} + +void kinc_wayland_window_show(int window_index) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland does not support unhiding windows."); +} + +void kinc_wayland_window_hide(int window_index) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Wayland does not support hiding windows."); +} + +kinc_window_mode_t kinc_wayland_window_get_mode(int window_index) { + return wl_ctx.windows[window_index].mode; +} + +void kinc_wayland_window_change_mode(int window_index, kinc_window_mode_t mode) { + struct kinc_wl_window *window = &wl_ctx.windows[window_index]; + if (mode == window->mode) { + return; + } + switch (mode) { + case KINC_WINDOW_MODE_WINDOW: + if (window->mode == KINC_WINDOW_MODE_FULLSCREEN || window->mode == KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { + window->mode = KINC_WINDOW_MODE_WINDOW; + xdg_toplevel_unset_fullscreen(window->toplevel); + } + break; + case KINC_WINDOW_MODE_FULLSCREEN: + case KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN: + if (window->mode == KINC_WINDOW_MODE_WINDOW) { + window->mode = mode; + struct kinc_wl_display *display = &wl_ctx.displays[window->display_index]; + xdg_toplevel_set_fullscreen(window->toplevel, display->output); + } + break; + } +} + +int kinc_wayland_window_display(int window_index) { + struct kinc_wl_window *window = &wl_ctx.windows[window_index]; + return window->display_index; +} + +int kinc_wayland_count_windows() { + return wl_ctx.num_windows; +} \ No newline at end of file diff --git a/armorcore/sources/backends/linux/kinc/backend/window.c.h b/armorcore/sources/backends/linux/kinc/backend/window.c.h new file mode 100644 index 000000000..47df1afff --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/window.c.h @@ -0,0 +1,140 @@ +#include "funcs.h" +#include +#include +#include + +#include + +#ifdef KINC_EGL +EGLDisplay kinc_egl_get_display() { + return procs.egl_get_display(); +} +EGLNativeWindowType kinc_egl_get_native_window(EGLDisplay display, EGLConfig config, int window_index) { + return procs.egl_get_native_window(display, config, window_index); +} +#endif + +#ifdef KINC_VULKAN +void kinc_vulkan_get_instance_extensions(const char **extensions, int *count, int max) { + procs.vulkan_get_instance_extensions(extensions, count, max); +} + +VkBool32 kinc_vulkan_get_physical_device_presentation_support(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex) { + return procs.vulkan_get_physical_device_presentation_support(physicalDevice, queueFamilyIndex); +} +VkResult kinc_vulkan_create_surface(VkInstance instance, int window_index, VkSurfaceKHR *surface) { + return procs.vulkan_create_surface(instance, window_index, surface); +} +#endif + +int kinc_count_windows(void) { + return procs.count_windows(); +} + +int kinc_window_x(int window_index) { + return procs.window_x(window_index); +} + +int kinc_window_y(int window_index) { + return procs.window_y(window_index); +} + +int kinc_window_width(int window_index) { + return procs.window_width(window_index); +} + +int kinc_window_height(int window_index) { + return procs.window_height(window_index); +} + +void kinc_window_resize(int window_index, int width, int height) { + procs.window_resize(window_index, width, height); +} + +void kinc_window_move(int window_index, int x, int y) { + procs.window_move(window_index, x, y); +} + +void kinc_window_change_framebuffer(int window_index, kinc_framebuffer_options_t *frame) {} + +void kinc_window_change_features(int window_index, int features) {} + +void kinc_window_change_mode(int window_index, kinc_window_mode_t mode) { + procs.window_change_mode(window_index, mode); +} + +int kinc_window_display(int window_index) { + return procs.window_display(window_index); +} + +void kinc_window_destroy(int window_index) { + kinc_g4_internal_destroy_window(window_index); + procs.window_destroy(window_index); +} + +void kinc_window_show(int window_index) { + procs.window_show(window_index); +} + +void kinc_window_hide(int window_index) { + procs.window_hide(window_index); +} + +void kinc_window_set_title(int window_index, const char *title) { + procs.window_set_title(window_index, title); +} + +int kinc_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) { + int index = procs.window_create(win, frame); + kinc_g4_internal_init_window(index, frame->depth_bits, frame->stencil_bits, frame->vertical_sync); + return index; +} + +static struct { + void (*resize_callback)(int width, int height, void *data); + void *resize_data; + void (*ppi_callback)(int ppi, void *data); + void *ppi_data; + bool (*close_callback)(void *data); + void *close_data; +} kinc_internal_window_callbacks[16]; + +void kinc_window_set_resize_callback(int window_index, void (*callback)(int width, int height, void *data), void *data) { + kinc_internal_window_callbacks[window_index].resize_callback = callback; + kinc_internal_window_callbacks[window_index].resize_data = data; +} + +void kinc_internal_call_resize_callback(int window_index, int width, int height) { + if (kinc_internal_window_callbacks[window_index].resize_callback != NULL) { + kinc_internal_window_callbacks[window_index].resize_callback(width, height, kinc_internal_window_callbacks[window_index].resize_data); + } +} + +void kinc_window_set_ppi_changed_callback(int window_index, void (*callback)(int ppi, void *data), void *data) { + kinc_internal_window_callbacks[window_index].ppi_callback = callback; + kinc_internal_window_callbacks[window_index].ppi_data = data; +} + +void kinc_internal_call_ppi_changed_callback(int window_index, int ppi) { + if (kinc_internal_window_callbacks[window_index].ppi_callback != NULL) { + kinc_internal_window_callbacks[window_index].ppi_callback(ppi, kinc_internal_window_callbacks[window_index].resize_data); + } +} + +void kinc_window_set_close_callback(int window_index, bool (*callback)(void *data), void *data) { + kinc_internal_window_callbacks[window_index].close_callback = callback; + kinc_internal_window_callbacks[window_index].close_data = data; +} + +bool kinc_internal_call_close_callback(int window_index) { + if (kinc_internal_window_callbacks[window_index].close_callback != NULL) { + return kinc_internal_window_callbacks[window_index].close_callback(kinc_internal_window_callbacks[window_index].close_data); + } + else { + return true; + } +} + +kinc_window_mode_t kinc_window_get_mode(int window_index) { + return procs.window_get_mode(window_index); +} diff --git a/armorcore/sources/backends/linux/kinc/backend/x11/display.c.h b/armorcore/sources/backends/linux/kinc/backend/x11/display.c.h new file mode 100644 index 000000000..e531ead5a --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/x11/display.c.h @@ -0,0 +1,221 @@ + +#include "x11.h" +#include + +// TODO: deal with monitor hotplugging and such + +void kinc_x11_display_init(void) { + int eventBase; + int errorBase; + + bool hasXinerama = (xlib.XineramaQueryExtension(x11_ctx.display, &eventBase, &errorBase) && xlib.XineramaIsActive(x11_ctx.display)); + XineramaScreenInfo *xinerama_screens = NULL; + int xinerama_screen_count = 0; + if (hasXinerama) { + xinerama_screens = xlib.XineramaQueryScreens(x11_ctx.display, &xinerama_screen_count); + } + + Window root_window = RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display)); + XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window); + RROutput primary_output = xlib.XRRGetOutputPrimary(x11_ctx.display, root_window); + + for (int i = 0; i < screen_resources->noutput; i++) { + if (i >= MAXIMUM_DISPLAYS) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Too many screens (maximum %i)", MAXIMUM_DISPLAYS); + break; + } + + XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[i]); + if (output_info->connection != RR_Connected || output_info->crtc == None) { + xlib.XRRFreeOutputInfo(output_info); + continue; + } + + XRRCrtcInfo *crtc_info = xlib.XRRGetCrtcInfo(x11_ctx.display, screen_resources, output_info->crtc); + + struct kinc_x11_display *display = &x11_ctx.displays[x11_ctx.num_displays++]; + display->index = i; + strncpy(display->name, output_info->name, sizeof(display->name)); + display->x = crtc_info->x; + display->y = crtc_info->y; + display->width = crtc_info->width; + display->height = crtc_info->height; + display->primary = screen_resources->outputs[i] == primary_output; + display->crtc = output_info->crtc; + display->output = screen_resources->outputs[i]; + + xlib.XRRFreeOutputInfo(output_info); + xlib.XRRFreeCrtcInfo(crtc_info); + } + + xlib.XRRFreeScreenResources(screen_resources); + if (hasXinerama) { + xlib.XFree(xinerama_screens); + } +} + +int kinc_x11_display_primary(void) { + for (int i = 0; i < x11_ctx.num_displays; i++) { + if (x11_ctx.displays[i].primary) { + return i; + } + } + return 0; +} + +int kinc_x11_count_displays(void) { + return x11_ctx.num_displays; +} + +bool kinc_x11_display_available(int display_index) { + if (display_index >= MAXIMUM_DISPLAYS) { + return false; + } + + return x11_ctx.displays[display_index].output != None; +} + +const char *kinc_x11_display_name(int display_index) { + if (display_index >= MAXIMUM_DISPLAYS) { + return ""; + } + + return x11_ctx.displays[display_index].name; +} + +kinc_display_mode_t kinc_x11_display_current_mode(int display_index) { + if (display_index >= MAXIMUM_DISPLAYS) + display_index = 0; + struct kinc_x11_display *display = &x11_ctx.displays[display_index]; + kinc_display_mode_t mode; + mode.x = 0; + mode.y = 0; + mode.width = display->width; + mode.height = display->height; + mode.frequency = 60; + mode.bits_per_pixel = 32; + mode.pixels_per_inch = 96; + + Window root_window = DefaultRootWindow(x11_ctx.display); + XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window); + + XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[display->index]); + if (output_info->connection != RR_Connected || output_info->crtc == None) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Display %i not connected.", display_index); + xlib.XRRFreeOutputInfo(output_info); + xlib.XRRFreeScreenResources(screen_resources); + return mode; + } + + XRRCrtcInfo *crtc_info = xlib.XRRGetCrtcInfo(x11_ctx.display, screen_resources, output_info->crtc); + for (int j = 0; j < output_info->nmode; j++) { + RRMode rr_mode = crtc_info->mode; + XRRModeInfo *mode_info = NULL; + for (int k = 0; k < screen_resources->nmode; k++) { + if (screen_resources->modes[k].id == rr_mode) { + mode_info = &screen_resources->modes[k]; + break; + } + } + + if (mode_info == NULL) { + continue; + } + mode.x = display->x; + mode.y = display->y; + mode.width = mode_info->width; + mode.height = mode_info->height; + mode.pixels_per_inch = 96; + mode.bits_per_pixel = 32; + if (mode_info->hTotal && mode_info->vTotal) { + mode.frequency = (mode_info->dotClock / (mode_info->hTotal * mode_info->vTotal)); + } + else { + mode.frequency = 60; + } + } + xlib.XRRFreeOutputInfo(output_info); + xlib.XRRFreeCrtcInfo(crtc_info); + xlib.XRRFreeScreenResources(screen_resources); + return mode; +} + +int kinc_x11_display_count_available_modes(int display_index) { + if (display_index >= MAXIMUM_DISPLAYS) + display_index = 0; + struct kinc_x11_display *display = &x11_ctx.displays[display_index]; + + Window root_window = RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display)); + XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window); + + XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[display->index]); + if (output_info->connection != RR_Connected || output_info->crtc == None) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Display %i not connected.", display_index); + xlib.XRRFreeOutputInfo(output_info); + xlib.XRRFreeScreenResources(screen_resources); + return 0; + } + + int num_modes = output_info->nmode; + xlib.XRRFreeOutputInfo(output_info); + xlib.XRRFreeScreenResources(screen_resources); + return num_modes; +} + +kinc_display_mode_t kinc_x11_display_available_mode(int display_index, int mode_index) { + if (display_index >= MAXIMUM_DISPLAYS) + display_index = 0; + struct kinc_x11_display *display = &x11_ctx.displays[display_index]; + kinc_display_mode_t mode; + mode.x = 0; + mode.y = 0; + mode.width = display->width; + mode.height = display->height; + mode.frequency = 60; + mode.bits_per_pixel = 32; + mode.pixels_per_inch = 96; + + Window root_window = RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display)); + XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window); + + XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[display->index]); + if (output_info->connection != RR_Connected || output_info->crtc == None) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Display %i not connected.", display_index); + xlib.XRRFreeOutputInfo(output_info); + xlib.XRRFreeScreenResources(screen_resources); + return mode; + } + + if (mode_index >= output_info->nmode) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Invalid mode index %i.", mode_index); + } + + RRMode rr_mode = output_info->modes[mode_index]; + XRRModeInfo *mode_info = NULL; + + for (int k = 0; k < screen_resources->nmode; k++) { + if (screen_resources->modes[k].id == rr_mode) { + mode_info = &screen_resources->modes[k]; + break; + } + } + + if (mode_info != NULL) { + mode.x = display->x; + mode.y = display->y; + mode.width = mode_info->width; + mode.height = mode_info->height; + mode.pixels_per_inch = 96; + mode.bits_per_pixel = 32; + if (mode_info->hTotal && mode_info->vTotal) { + mode.frequency = (mode_info->dotClock / (mode_info->hTotal * mode_info->vTotal)); + } + else { + mode.frequency = 60; + } + } + + xlib.XRRFreeOutputInfo(output_info); + xlib.XRRFreeScreenResources(screen_resources); + return mode; +} diff --git a/armorcore/sources/backends/linux/kinc/backend/x11/system.c.h b/armorcore/sources/backends/linux/kinc/backend/x11/system.c.h new file mode 100644 index 000000000..50ef1f7d0 --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/x11/system.c.h @@ -0,0 +1,1057 @@ +#include "x11.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define Button6 6 +#define Button7 7 + +struct kinc_x11_procs xlib = {0}; +struct x11_context x11_ctx = {0}; + +static size_t clipboardStringSize = 1024; +static char *clipboardString = NULL; + +char buffer[1024]; + +int kinc_x11_error_handler(Display *display, XErrorEvent *error_event) { + xlib.XGetErrorText(display, error_event->error_code, buffer, 1024); + kinc_log(KINC_LOG_LEVEL_ERROR, "X Error: %s", buffer); + kinc_debug_break(); + return 0; +} + +struct kinc_x11_window *window_from_window(Window window) { + for (int i = 0; i < MAXIMUM_WINDOWS; i++) { + if (x11_ctx.windows[i].window == window) { + return &x11_ctx.windows[i]; + } + } + + return NULL; +} + +void kinc_internal_resize(int window_index, int width, int height); +static void init_pen_device(XDeviceInfo *info, struct x11_pen_device *pen, bool eraser); + +static void load_lib(void **lib, const char *name); + +bool kinc_x11_init() { + +#undef LOAD_LIB +#undef LOAD_FUN +#define LOAD_LIB(name) \ + { \ + load_lib(&x11_ctx.libs.name, #name); \ + \ + if (x11_ctx.libs.name == NULL) { \ + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to load lib%s.so", #name); \ + return false; \ + } \ + } \ + // manually check for libX11, and return false if not present + // only error for further libs + + load_lib(&x11_ctx.libs.X11, "X11"); + if (x11_ctx.libs.X11 == NULL) { + return false; + } + + LOAD_LIB(Xi); + LOAD_LIB(Xcursor); + LOAD_LIB(Xinerama); + LOAD_LIB(Xrandr); +#define LOAD_FUN(lib, symbol) \ + xlib.symbol = dlsym(x11_ctx.libs.lib, #symbol); \ + if (xlib.symbol == NULL) { \ + kinc_log(KINC_LOG_LEVEL_ERROR, "Did not find symbol %s in library %s.", #symbol, #lib); \ + } + LOAD_FUN(X11, XOpenDisplay) + LOAD_FUN(X11, XCloseDisplay) + LOAD_FUN(X11, XSetErrorHandler) + LOAD_FUN(X11, XGetErrorText) + LOAD_FUN(X11, XInternAtoms) + LOAD_FUN(X11, XPending) + LOAD_FUN(X11, XFlush) + LOAD_FUN(X11, XNextEvent) + LOAD_FUN(X11, XRefreshKeyboardMapping) + LOAD_FUN(X11, XwcLookupString) + LOAD_FUN(X11, XFilterEvent) + LOAD_FUN(X11, XConvertSelection) + LOAD_FUN(X11, XSetSelectionOwner) + LOAD_FUN(X11, XLookupString) + LOAD_FUN(X11, XkbKeycodeToKeysym) + LOAD_FUN(X11, XSendEvent) + LOAD_FUN(X11, XGetWindowProperty) + LOAD_FUN(X11, XFree) + LOAD_FUN(X11, XChangeProperty) + LOAD_FUN(X11, XDefineCursor) + LOAD_FUN(X11, XUndefineCursor) + LOAD_FUN(X11, XCreateBitmapFromData) + LOAD_FUN(X11, XCreatePixmapCursor) + LOAD_FUN(X11, XFreePixmap) + LOAD_FUN(Xcursor, XcursorLibraryLoadCursor) + LOAD_FUN(X11, XWarpPointer) + LOAD_FUN(X11, XQueryPointer) + LOAD_FUN(X11, XCreateColormap) + LOAD_FUN(X11, XCreateWindow) + LOAD_FUN(X11, XMoveWindow) + LOAD_FUN(X11, XResizeWindow) + LOAD_FUN(X11, XDestroyWindow) + LOAD_FUN(X11, XSetClassHint) + LOAD_FUN(X11, XSetLocaleModifiers) + LOAD_FUN(X11, XOpenIM) + LOAD_FUN(X11, XCloseIM) + LOAD_FUN(X11, XCreateIC) + LOAD_FUN(X11, XDestroyIC) + LOAD_FUN(X11, XSetICFocus) + LOAD_FUN(X11, XMapWindow) + LOAD_FUN(X11, XUnmapWindow) + LOAD_FUN(X11, XSetWMProtocols) + LOAD_FUN(X11, XPeekEvent) + + LOAD_FUN(Xi, XListInputDevices) + LOAD_FUN(Xi, XFreeDeviceList) + LOAD_FUN(Xi, XOpenDevice) + LOAD_FUN(Xi, XCloseDevice) + LOAD_FUN(Xi, XSelectExtensionEvent) + + LOAD_FUN(Xinerama, XineramaQueryExtension) + LOAD_FUN(Xinerama, XineramaIsActive) + LOAD_FUN(Xinerama, XineramaQueryScreens) + LOAD_FUN(Xrandr, XRRGetScreenResourcesCurrent) + LOAD_FUN(Xrandr, XRRGetOutputPrimary) + LOAD_FUN(Xrandr, XRRGetOutputInfo) + LOAD_FUN(Xrandr, XRRFreeOutputInfo) + LOAD_FUN(Xrandr, XRRGetCrtcInfo) + LOAD_FUN(Xrandr, XRRFreeCrtcInfo) + LOAD_FUN(Xrandr, XRRFreeScreenResources) + +#undef LOAD_FUN +#undef LOAD_LIB + + x11_ctx.display = xlib.XOpenDisplay(NULL); + if (!x11_ctx.display) { + return false; + } + + xlib.XSetErrorHandler(kinc_x11_error_handler); + + // this should be kept in sync with the x11_atoms struct + static char *atom_names[] = { + "XdndAware", + "XdndDrop", + "XdndEnter", + "text/uri-list", + "XdndStatus", + "XdndActionCopy", + "XdndSelection", + "CLIPBOARD", + "UTF8_STRING", + "XSEL_DATA", + "TARGETS", + "MULTIPLE", + "text/plain;charset=utf-8", + "WM_DELETE_WINDOW", + "_MOTIF_WM_HINTS", + "_NET_WM_NAME", + "_NET_WM_ICON_NAME", + "_NET_WM_STATE", + "_NET_WM_STATE_FULLSCREEN", + XI_MOUSE, + XI_TABLET, + XI_KEYBOARD, + XI_TOUCHSCREEN, + XI_TOUCHPAD, + XI_BUTTONBOX, + XI_BARCODE, + XI_TRACKBALL, + XI_QUADRATURE, + XI_ID_MODULE, + XI_ONE_KNOB, + XI_NINE_KNOB, + XI_KNOB_BOX, + XI_SPACEBALL, + XI_DATAGLOVE, + XI_EYETRACKER, + XI_CURSORKEYS, + XI_FOOTMOUSE, + XI_JOYSTICK, + }; + + assert((sizeof atom_names / sizeof atom_names[0]) == (sizeof(struct kinc_x11_atoms) / sizeof(Atom))); + xlib.XInternAtoms(x11_ctx.display, atom_names, sizeof atom_names / sizeof atom_names[0], False, (Atom *)&x11_ctx.atoms); + clipboardString = (char *)malloc(clipboardStringSize); + + x11_ctx.pen.id = -1; + x11_ctx.eraser.id = -1; + + int count; + XDeviceInfoPtr devices = (XDeviceInfoPtr)xlib.XListInputDevices(x11_ctx.display, &count); + for (int i = 0; i < count; i++) { + strncpy(buffer, devices[i].name, 1023); + buffer[1023] = 0; + for (int j = 0; buffer[j]; j++) { + buffer[j] = tolower(buffer[j]); + } + + if (strstr(buffer, "stylus") || strstr(buffer, "pen") || strstr(buffer, "wacom")) { + init_pen_device(&devices[i], &x11_ctx.pen, false); + } + if (strstr(buffer, "eraser")) { + init_pen_device(&devices[i], &x11_ctx.eraser, true); + } + } + + if (devices != NULL) { + xlib.XFreeDeviceList(devices); + } + + return true; +} + +void kinc_x11_shutdown() { + free(clipboardString); + xlib.XCloseDisplay(x11_ctx.display); +} + +#include + +static void init_pen_device(XDeviceInfo *info, struct x11_pen_device *pen, bool eraser) { + XDevice *device = xlib.XOpenDevice(x11_ctx.display, info->id); + XAnyClassPtr c = info->inputclassinfo; + for (int j = 0; j < device->num_classes; j++) { + if (c->class == ValuatorClass) { + XValuatorInfo *valuator_info = (XValuatorInfo *)c; + if (valuator_info->num_axes > 2) { + pen->maxPressure = valuator_info->axes[2].max_value; + } + pen->id = info->id; + DeviceMotionNotify(device, pen->motionEvent, pen->motionClass); + if (eraser) { + pen->press = kinc_internal_eraser_trigger_press; + pen->move = kinc_internal_eraser_trigger_move; + pen->release = kinc_internal_eraser_trigger_release; + } + else { + pen->press = kinc_internal_pen_trigger_press; + pen->move = kinc_internal_pen_trigger_move; + pen->release = kinc_internal_pen_trigger_release; + } + return; + } + c = (XAnyClassPtr)((uint8_t *)c + c->length); + } + xlib.XCloseDevice(x11_ctx.display, device); +} + +static void check_pen_device(struct kinc_x11_window *window, XEvent *event, struct x11_pen_device *pen) { + if (event->type == pen->motionEvent) { + XDeviceMotionEvent *motion = (XDeviceMotionEvent *)(event); + if (motion->deviceid == pen->id) { + float p = (float)motion->axis_data[2] / (float)pen->maxPressure; + if (p > 0 && x11_ctx.pen.current_pressure == 0) { + pen->press(window->window_index, motion->x, motion->y, p); + } + else if (p == 0 && pen->current_pressure > 0) { + pen->release(window->window_index, motion->x, motion->y, p); + } + else if (p > 0) { + pen->move(window->window_index, motion->x, motion->y, p); + } + pen->current_pressure = p; + } + } +} + +bool kinc_x11_handle_messages() { + static bool controlDown = false; + static int ignoreKeycode = 0; + static bool preventNextKeyDownEvent = false; + + while (xlib.XPending(x11_ctx.display)) { + XEvent event; + xlib.XNextEvent(x11_ctx.display, &event); + Window window = event.xclient.window; + struct kinc_x11_window *k_window = window_from_window(window); + if (k_window == NULL) { + continue; + } + check_pen_device(k_window, &event, &x11_ctx.pen); + check_pen_device(k_window, &event, &x11_ctx.eraser); + switch (event.type) { + case MappingNotify: { + xlib.XRefreshKeyboardMapping(&event.xmapping); + } break; + case KeyPress: { + + XKeyEvent *key = (XKeyEvent *)&event; + KeySym keysym; + + wchar_t wchar; + + bool wcConverted = xlib.XwcLookupString(k_window->xInputContext, key, &wchar, 1, &keysym, NULL); + + bool isIgnoredKeySym = keysym == XK_Escape || keysym == XK_BackSpace || keysym == XK_Delete; + if (!controlDown && !xlib.XFilterEvent(&event, window) && !isIgnoredKeySym) { + + if (wcConverted) { + kinc_internal_keyboard_trigger_key_press(wchar); + } + } + + if (preventNextKeyDownEvent) { + // this keypress is a repeated keystroke and should not lead to a keydown-event + preventNextKeyDownEvent = false; + continue; + } + +#define KEY(xkey, korekey) \ + case xkey: \ + kinc_internal_keyboard_trigger_key_down(korekey); \ + break; + + KeySym ksKey = xlib.XkbKeycodeToKeysym(x11_ctx.display, event.xkey.keycode, 0, 0); + + if (ksKey == XK_Control_L || ksKey == XK_Control_R) { + controlDown = true; + } + else if (controlDown && (ksKey == XK_v || ksKey == XK_V)) { + xlib.XConvertSelection(x11_ctx.display, x11_ctx.atoms.CLIPBOARD, x11_ctx.atoms.UTF8_STRING, x11_ctx.atoms.XSEL_DATA, window, CurrentTime); + } + else if (controlDown && (ksKey == XK_c || ksKey == XK_C)) { + xlib.XSetSelectionOwner(x11_ctx.display, x11_ctx.atoms.CLIPBOARD, window, CurrentTime); + char *text = kinc_internal_copy_callback(); + if (text != NULL) + kinc_x11_copy_to_clipboard(text); + } + else if (controlDown && (ksKey == XK_x || ksKey == XK_X)) { + xlib.XSetSelectionOwner(x11_ctx.display, x11_ctx.atoms.CLIPBOARD, window, CurrentTime); + char *text = kinc_internal_cut_callback(); + if (text != NULL) + kinc_x11_copy_to_clipboard(text); + } + + if (event.xkey.keycode == ignoreKeycode) { + break; + } + else { + ignoreKeycode = event.xkey.keycode; + } + + if (ksKey < 97 || ksKey > 122) { + ksKey = keysym; + } + + switch (ksKey) { + KEY(XK_Right, KINC_KEY_RIGHT) + KEY(XK_Left, KINC_KEY_LEFT) + KEY(XK_Up, KINC_KEY_UP) + KEY(XK_Down, KINC_KEY_DOWN) + KEY(XK_space, KINC_KEY_SPACE) + KEY(XK_BackSpace, KINC_KEY_BACKSPACE) + KEY(XK_Tab, KINC_KEY_TAB) + KEY(XK_Return, KINC_KEY_RETURN) + KEY(XK_Shift_L, KINC_KEY_SHIFT) + KEY(XK_Shift_R, KINC_KEY_SHIFT) + KEY(XK_Control_L, KINC_KEY_CONTROL) + KEY(XK_Control_R, KINC_KEY_CONTROL) + KEY(XK_Alt_L, KINC_KEY_ALT) + KEY(XK_Alt_R, KINC_KEY_ALT) + KEY(XK_Delete, KINC_KEY_DELETE) + KEY(XK_comma, KINC_KEY_COMMA) + KEY(XK_period, KINC_KEY_PERIOD) + KEY(XK_bracketleft, KINC_KEY_OPEN_BRACKET) + KEY(XK_bracketright, KINC_KEY_CLOSE_BRACKET) + KEY(XK_braceleft, KINC_KEY_OPEN_CURLY_BRACKET) + KEY(XK_braceright, KINC_KEY_CLOSE_CURLY_BRACKET) + KEY(XK_parenleft, KINC_KEY_OPEN_PAREN) + KEY(XK_parenright, KINC_KEY_CLOSE_PAREN) + KEY(XK_backslash, KINC_KEY_BACK_SLASH) + KEY(XK_apostrophe, KINC_KEY_QUOTE) + KEY(XK_colon, KINC_KEY_COLON) + KEY(XK_semicolon, KINC_KEY_SEMICOLON) + KEY(XK_minus, KINC_KEY_HYPHEN_MINUS) + KEY(XK_underscore, KINC_KEY_UNDERSCORE) + KEY(XK_slash, KINC_KEY_SLASH) + KEY(XK_bar, KINC_KEY_PIPE) + KEY(XK_question, KINC_KEY_QUESTIONMARK) + KEY(XK_less, KINC_KEY_LESS_THAN) + KEY(XK_greater, KINC_KEY_GREATER_THAN) + KEY(XK_asterisk, KINC_KEY_ASTERISK) + KEY(XK_ampersand, KINC_KEY_AMPERSAND) + KEY(XK_asciicircum, KINC_KEY_CIRCUMFLEX) + KEY(XK_percent, KINC_KEY_PERCENT) + KEY(XK_dollar, KINC_KEY_DOLLAR) + KEY(XK_numbersign, KINC_KEY_HASH) + KEY(XK_at, KINC_KEY_AT) + KEY(XK_exclam, KINC_KEY_EXCLAMATION) + KEY(XK_equal, KINC_KEY_EQUALS) + KEY(XK_plus, KINC_KEY_ADD) + KEY(XK_quoteleft, KINC_KEY_BACK_QUOTE) + KEY(XK_quotedbl, KINC_KEY_DOUBLE_QUOTE) + KEY(XK_asciitilde, KINC_KEY_TILDE) + KEY(XK_Pause, KINC_KEY_PAUSE) + KEY(XK_Scroll_Lock, KINC_KEY_SCROLL_LOCK) + KEY(XK_Home, KINC_KEY_HOME) + KEY(XK_Page_Up, KINC_KEY_PAGE_UP) + KEY(XK_Page_Down, KINC_KEY_PAGE_DOWN) + KEY(XK_End, KINC_KEY_END) + KEY(XK_Insert, KINC_KEY_INSERT) + KEY(XK_KP_Enter, KINC_KEY_RETURN) + KEY(XK_KP_Multiply, KINC_KEY_MULTIPLY) + KEY(XK_KP_Add, KINC_KEY_ADD) + KEY(XK_KP_Subtract, KINC_KEY_SUBTRACT) + KEY(XK_KP_Decimal, KINC_KEY_DECIMAL) + KEY(XK_KP_Divide, KINC_KEY_DIVIDE) + KEY(XK_KP_0, KINC_KEY_NUMPAD_0) + KEY(XK_KP_1, KINC_KEY_NUMPAD_1) + KEY(XK_KP_2, KINC_KEY_NUMPAD_2) + KEY(XK_KP_3, KINC_KEY_NUMPAD_3) + KEY(XK_KP_4, KINC_KEY_NUMPAD_4) + KEY(XK_KP_5, KINC_KEY_NUMPAD_5) + KEY(XK_KP_6, KINC_KEY_NUMPAD_6) + KEY(XK_KP_7, KINC_KEY_NUMPAD_7) + KEY(XK_KP_8, KINC_KEY_NUMPAD_8) + KEY(XK_KP_9, KINC_KEY_NUMPAD_9) + KEY(XK_KP_Insert, KINC_KEY_INSERT) + KEY(XK_KP_Delete, KINC_KEY_DELETE) + KEY(XK_KP_End, KINC_KEY_END) + KEY(XK_KP_Home, KINC_KEY_HOME) + KEY(XK_KP_Left, KINC_KEY_LEFT) + KEY(XK_KP_Up, KINC_KEY_UP) + KEY(XK_KP_Right, KINC_KEY_RIGHT) + KEY(XK_KP_Down, KINC_KEY_DOWN) + KEY(XK_KP_Page_Up, KINC_KEY_PAGE_UP) + KEY(XK_KP_Page_Down, KINC_KEY_PAGE_DOWN) + KEY(XK_Menu, KINC_KEY_CONTEXT_MENU) + KEY(XK_a, KINC_KEY_A) + KEY(XK_b, KINC_KEY_B) + KEY(XK_c, KINC_KEY_C) + KEY(XK_d, KINC_KEY_D) + KEY(XK_e, KINC_KEY_E) + KEY(XK_f, KINC_KEY_F) + KEY(XK_g, KINC_KEY_G) + KEY(XK_h, KINC_KEY_H) + KEY(XK_i, KINC_KEY_I) + KEY(XK_j, KINC_KEY_J) + KEY(XK_k, KINC_KEY_K) + KEY(XK_l, KINC_KEY_L) + KEY(XK_m, KINC_KEY_M) + KEY(XK_n, KINC_KEY_N) + KEY(XK_o, KINC_KEY_O) + KEY(XK_p, KINC_KEY_P) + KEY(XK_q, KINC_KEY_Q) + KEY(XK_r, KINC_KEY_R) + KEY(XK_s, KINC_KEY_S) + KEY(XK_t, KINC_KEY_T) + KEY(XK_u, KINC_KEY_U) + KEY(XK_v, KINC_KEY_V) + KEY(XK_w, KINC_KEY_W) + KEY(XK_x, KINC_KEY_X) + KEY(XK_y, KINC_KEY_Y) + KEY(XK_z, KINC_KEY_Z) + KEY(XK_1, KINC_KEY_1) + KEY(XK_2, KINC_KEY_2) + KEY(XK_3, KINC_KEY_3) + KEY(XK_4, KINC_KEY_4) + KEY(XK_5, KINC_KEY_5) + KEY(XK_6, KINC_KEY_6) + KEY(XK_7, KINC_KEY_7) + KEY(XK_8, KINC_KEY_8) + KEY(XK_9, KINC_KEY_9) + KEY(XK_0, KINC_KEY_0) + KEY(XK_Escape, KINC_KEY_ESCAPE) + KEY(XK_F1, KINC_KEY_F1) + KEY(XK_F2, KINC_KEY_F2) + KEY(XK_F3, KINC_KEY_F3) + KEY(XK_F4, KINC_KEY_F4) + KEY(XK_F5, KINC_KEY_F5) + KEY(XK_F6, KINC_KEY_F6) + KEY(XK_F7, KINC_KEY_F7) + KEY(XK_F8, KINC_KEY_F8) + KEY(XK_F9, KINC_KEY_F9) + KEY(XK_F10, KINC_KEY_F10) + KEY(XK_F11, KINC_KEY_F11) + KEY(XK_F12, KINC_KEY_F12) + } + break; +#undef KEY + } + case KeyRelease: { + XKeyEvent *key = (XKeyEvent *)&event; + + // peek next-event to determine if this a repeated-keystroke + XEvent nev; + if (xlib.XPending(x11_ctx.display)) { + xlib.XPeekEvent(x11_ctx.display, &nev); + + if (nev.type == KeyPress && nev.xkey.time == event.xkey.time && nev.xkey.keycode == event.xkey.keycode) { + // repeated keystroke! prevent this keyup-event and next keydown-event from being fired + preventNextKeyDownEvent = true; + continue; + } + } + KeySym keysym; + + char c; + xlib.XLookupString(key, &c, 1, &keysym, NULL); + +#define KEY(xkey, korekey) \ + case xkey: \ + kinc_internal_keyboard_trigger_key_up(korekey); \ + break; + + KeySym ksKey = xlib.XkbKeycodeToKeysym(x11_ctx.display, event.xkey.keycode, 0, 0); + + if (ksKey == XK_Control_L || ksKey == XK_Control_R) { + controlDown = false; + } + + if (event.xkey.keycode == ignoreKeycode) { + ignoreKeycode = 0; + } + + if (ksKey < 97 || ksKey > 122) { + ksKey = keysym; + } + + switch (ksKey) { + KEY(XK_Right, KINC_KEY_RIGHT) + KEY(XK_Left, KINC_KEY_LEFT) + KEY(XK_Up, KINC_KEY_UP) + KEY(XK_Down, KINC_KEY_DOWN) + KEY(XK_space, KINC_KEY_SPACE) + KEY(XK_BackSpace, KINC_KEY_BACKSPACE) + KEY(XK_Tab, KINC_KEY_TAB) + KEY(XK_Return, KINC_KEY_RETURN) + KEY(XK_Shift_L, KINC_KEY_SHIFT) + KEY(XK_Shift_R, KINC_KEY_SHIFT) + KEY(XK_Control_L, KINC_KEY_CONTROL) + KEY(XK_Control_R, KINC_KEY_CONTROL) + KEY(XK_Alt_L, KINC_KEY_ALT) + KEY(XK_ISO_Prev_Group, KINC_KEY_ALT) //// XK_ISO_Prev_Group received instead of XK_Alt_L? + KEY(XK_Alt_R, KINC_KEY_ALT) + KEY(XK_Delete, KINC_KEY_DELETE) + KEY(XK_comma, KINC_KEY_COMMA) + KEY(XK_period, KINC_KEY_PERIOD) + KEY(XK_bracketleft, KINC_KEY_OPEN_BRACKET) + KEY(XK_bracketright, KINC_KEY_CLOSE_BRACKET) + KEY(XK_braceleft, KINC_KEY_OPEN_CURLY_BRACKET) + KEY(XK_braceright, KINC_KEY_CLOSE_CURLY_BRACKET) + KEY(XK_parenleft, KINC_KEY_OPEN_PAREN) + KEY(XK_parenright, KINC_KEY_CLOSE_PAREN) + KEY(XK_backslash, KINC_KEY_BACK_SLASH) + KEY(XK_apostrophe, KINC_KEY_QUOTE) + KEY(XK_colon, KINC_KEY_COLON) + KEY(XK_semicolon, KINC_KEY_SEMICOLON) + KEY(XK_minus, KINC_KEY_HYPHEN_MINUS) + KEY(XK_underscore, KINC_KEY_UNDERSCORE) + KEY(XK_slash, KINC_KEY_SLASH) + KEY(XK_bar, KINC_KEY_PIPE) + KEY(XK_question, KINC_KEY_QUESTIONMARK) + KEY(XK_less, KINC_KEY_LESS_THAN) + KEY(XK_greater, KINC_KEY_GREATER_THAN) + KEY(XK_asterisk, KINC_KEY_ASTERISK) + KEY(XK_ampersand, KINC_KEY_AMPERSAND) + KEY(XK_asciicircum, KINC_KEY_CIRCUMFLEX) + KEY(XK_percent, KINC_KEY_PERCENT) + KEY(XK_dollar, KINC_KEY_DOLLAR) + KEY(XK_numbersign, KINC_KEY_HASH) + KEY(XK_at, KINC_KEY_AT) + KEY(XK_exclam, KINC_KEY_EXCLAMATION) + KEY(XK_equal, KINC_KEY_EQUALS) + KEY(XK_plus, KINC_KEY_ADD) + KEY(XK_quoteleft, KINC_KEY_BACK_QUOTE) + KEY(XK_quotedbl, KINC_KEY_DOUBLE_QUOTE) + KEY(XK_asciitilde, KINC_KEY_TILDE) + KEY(XK_Pause, KINC_KEY_PAUSE) + KEY(XK_Scroll_Lock, KINC_KEY_SCROLL_LOCK) + KEY(XK_Home, KINC_KEY_HOME) + KEY(XK_Page_Up, KINC_KEY_PAGE_UP) + KEY(XK_Page_Down, KINC_KEY_PAGE_DOWN) + KEY(XK_End, KINC_KEY_END) + KEY(XK_Insert, KINC_KEY_INSERT) + KEY(XK_KP_Enter, KINC_KEY_RETURN) + KEY(XK_KP_Multiply, KINC_KEY_MULTIPLY) + KEY(XK_KP_Add, KINC_KEY_ADD) + KEY(XK_KP_Subtract, KINC_KEY_SUBTRACT) + KEY(XK_KP_Decimal, KINC_KEY_DECIMAL) + KEY(XK_KP_Divide, KINC_KEY_DIVIDE) + KEY(XK_KP_0, KINC_KEY_NUMPAD_0) + KEY(XK_KP_1, KINC_KEY_NUMPAD_1) + KEY(XK_KP_2, KINC_KEY_NUMPAD_2) + KEY(XK_KP_3, KINC_KEY_NUMPAD_3) + KEY(XK_KP_4, KINC_KEY_NUMPAD_4) + KEY(XK_KP_5, KINC_KEY_NUMPAD_5) + KEY(XK_KP_6, KINC_KEY_NUMPAD_6) + KEY(XK_KP_7, KINC_KEY_NUMPAD_7) + KEY(XK_KP_8, KINC_KEY_NUMPAD_8) + KEY(XK_KP_9, KINC_KEY_NUMPAD_9) + KEY(XK_KP_Insert, KINC_KEY_INSERT) + KEY(XK_KP_Delete, KINC_KEY_DELETE) + KEY(XK_KP_End, KINC_KEY_END) + KEY(XK_KP_Home, KINC_KEY_HOME) + KEY(XK_KP_Left, KINC_KEY_LEFT) + KEY(XK_KP_Up, KINC_KEY_UP) + KEY(XK_KP_Right, KINC_KEY_RIGHT) + KEY(XK_KP_Down, KINC_KEY_DOWN) + KEY(XK_KP_Page_Up, KINC_KEY_PAGE_UP) + KEY(XK_KP_Page_Down, KINC_KEY_PAGE_DOWN) + KEY(XK_Menu, KINC_KEY_CONTEXT_MENU) + KEY(XK_a, KINC_KEY_A) + KEY(XK_b, KINC_KEY_B) + KEY(XK_c, KINC_KEY_C) + KEY(XK_d, KINC_KEY_D) + KEY(XK_e, KINC_KEY_E) + KEY(XK_f, KINC_KEY_F) + KEY(XK_g, KINC_KEY_G) + KEY(XK_h, KINC_KEY_H) + KEY(XK_i, KINC_KEY_I) + KEY(XK_j, KINC_KEY_J) + KEY(XK_k, KINC_KEY_K) + KEY(XK_l, KINC_KEY_L) + KEY(XK_m, KINC_KEY_M) + KEY(XK_n, KINC_KEY_N) + KEY(XK_o, KINC_KEY_O) + KEY(XK_p, KINC_KEY_P) + KEY(XK_q, KINC_KEY_Q) + KEY(XK_r, KINC_KEY_R) + KEY(XK_s, KINC_KEY_S) + KEY(XK_t, KINC_KEY_T) + KEY(XK_u, KINC_KEY_U) + KEY(XK_v, KINC_KEY_V) + KEY(XK_w, KINC_KEY_W) + KEY(XK_x, KINC_KEY_X) + KEY(XK_y, KINC_KEY_Y) + KEY(XK_z, KINC_KEY_Z) + KEY(XK_1, KINC_KEY_1) + KEY(XK_2, KINC_KEY_2) + KEY(XK_3, KINC_KEY_3) + KEY(XK_4, KINC_KEY_4) + KEY(XK_5, KINC_KEY_5) + KEY(XK_6, KINC_KEY_6) + KEY(XK_7, KINC_KEY_7) + KEY(XK_8, KINC_KEY_8) + KEY(XK_9, KINC_KEY_9) + KEY(XK_0, KINC_KEY_0) + KEY(XK_Escape, KINC_KEY_ESCAPE) + KEY(XK_F1, KINC_KEY_F1) + KEY(XK_F2, KINC_KEY_F2) + KEY(XK_F3, KINC_KEY_F3) + KEY(XK_F4, KINC_KEY_F4) + KEY(XK_F5, KINC_KEY_F5) + KEY(XK_F6, KINC_KEY_F6) + KEY(XK_F7, KINC_KEY_F7) + KEY(XK_F8, KINC_KEY_F8) + KEY(XK_F9, KINC_KEY_F9) + KEY(XK_F10, KINC_KEY_F10) + KEY(XK_F11, KINC_KEY_F11) + KEY(XK_F12, KINC_KEY_F12) + } + break; +#undef KEY + } + case ButtonPress: { + XButtonEvent *button = (XButtonEvent *)&event; + int window_index = k_window->window_index; + + switch (button->button) { + case Button1: + kinc_internal_mouse_trigger_press(window_index, 0, button->x, button->y); + break; + case Button2: + kinc_internal_mouse_trigger_press(window_index, 2, button->x, button->y); + break; + case Button3: + kinc_internal_mouse_trigger_press(window_index, 1, button->x, button->y); + break; + // buttons 4-7 are for mouse wheel events because why not + case Button4: + case Button5: + case Button6: + case Button7: + break; + default: + kinc_internal_mouse_trigger_press(window_index, button->button - Button1 - 4, button->x, button->y); + break; + } + break; + } + case ButtonRelease: { + XButtonEvent *button = (XButtonEvent *)&event; + int window_index = k_window->window_index; + + switch (button->button) { + case Button1: + kinc_internal_mouse_trigger_release(window_index, 0, button->x, button->y); + break; + case Button2: + kinc_internal_mouse_trigger_release(window_index, 2, button->x, button->y); + break; + case Button3: + kinc_internal_mouse_trigger_release(window_index, 1, button->x, button->y); + break; + // Button4 and Button5 provide mouse wheel events because why not + case Button4: + kinc_internal_mouse_trigger_scroll(window_index, -1); + break; + case Button5: + kinc_internal_mouse_trigger_scroll(window_index, 1); + break; + // button 6 and 7 seem to be horizontal scrolling, which is not exposed in Kinc's api at the moment + case Button6: + case Button7: + break; + default: + kinc_internal_mouse_trigger_release(window_index, button->button - Button1 - 4, button->x, button->y); + break; + } + break; + } + case MotionNotify: { + XMotionEvent *motion = (XMotionEvent *)&event; + kinc_internal_mouse_trigger_move(k_window->window_index, motion->x, motion->y); + break; + } + case ConfigureNotify: { + if (event.xconfigure.width != k_window->width || event.xconfigure.height != k_window->height) { + k_window->width = event.xconfigure.width; + k_window->height = event.xconfigure.height; + kinc_internal_resize(k_window->window_index, event.xconfigure.width, event.xconfigure.height); + kinc_internal_call_resize_callback(k_window->window_index, event.xconfigure.width, event.xconfigure.height); + } + break; + } + case ClientMessage: { + if (event.xclient.message_type == x11_ctx.atoms.XdndEnter) { + Window source_window = event.xclient.data.l[0]; + XEvent m; + memset(&m, 0, sizeof(m)); + m.type = ClientMessage; + m.xclient.window = event.xclient.data.l[0]; + m.xclient.message_type = x11_ctx.atoms.XdndStatus; + m.xclient.format = 32; + m.xclient.data.l[0] = window; + m.xclient.data.l[2] = 0; + m.xclient.data.l[3] = 0; + m.xclient.data.l[1] = 1; + m.xclient.data.l[4] = x11_ctx.atoms.XdndActionCopy; + xlib.XSendEvent(x11_ctx.display, source_window, false, NoEventMask, (XEvent *)&m); + xlib.XFlush(x11_ctx.display); + } + else if (event.xclient.message_type == x11_ctx.atoms.XdndDrop) { + xlib.XConvertSelection(x11_ctx.display, x11_ctx.atoms.XdndSelection, x11_ctx.atoms.XdndTextUriList, x11_ctx.atoms.XdndSelection, window, + event.xclient.data.l[2]); + } + else if (event.xclient.data.l[0] == x11_ctx.atoms.WM_DELETE_WINDOW) { + if (kinc_internal_call_close_callback(k_window->window_index)) { + kinc_window_destroy(k_window->window_index); + if (x11_ctx.num_windows <= 0) { + // no windows left, stop + kinc_stop(); + } + } + } + }; break; + case SelectionNotify: { + if (event.xselection.selection == x11_ctx.atoms.CLIPBOARD) { + char *result; + unsigned long ressize, restail; + int resbits; + xlib.XGetWindowProperty(x11_ctx.display, window, x11_ctx.atoms.XSEL_DATA, 0, LONG_MAX / 4, False, AnyPropertyType, &x11_ctx.atoms.UTF8_STRING, + &resbits, &ressize, &restail, (unsigned char **)&result); + kinc_internal_paste_callback(result); + xlib.XFree(result); + } + else if (event.xselection.property == x11_ctx.atoms.XdndSelection) { + Atom type; + int format; + unsigned long numItems; + unsigned long bytesAfter = 1; + unsigned char *data = 0; + xlib.XGetWindowProperty(x11_ctx.display, event.xselection.requestor, event.xselection.property, 0, LONG_MAX, False, event.xselection.target, + &type, &format, &numItems, &bytesAfter, &data); + size_t pos = 0; + size_t len = 0; + while (pos < numItems) { + if (data[pos] == '\r') { // Found a file + wchar_t filePath[len + 1]; + mbstowcs(filePath, buffer, len); + filePath[len] = 0; + kinc_internal_drop_files_callback(filePath + 7); // Strip file:// + pos += 2; // Avoid \n + len = 0; + } + buffer[len++] = data[pos++]; + } + xlib.XFree(data); + } + break; + } + case SelectionRequest: { + if (event.xselectionrequest.target == x11_ctx.atoms.TARGETS) { + XEvent send; + send.xselection.type = SelectionNotify; + send.xselection.requestor = event.xselectionrequest.requestor; + send.xselection.selection = event.xselectionrequest.selection; + send.xselection.target = event.xselectionrequest.target; + send.xselection.property = event.xselectionrequest.property; + send.xselection.time = event.xselectionrequest.time; + Atom available[] = {x11_ctx.atoms.TARGETS, x11_ctx.atoms.MULTIPLE, x11_ctx.atoms.TEXT_PLAIN, x11_ctx.atoms.UTF8_STRING}; + xlib.XChangeProperty(x11_ctx.display, send.xselection.requestor, send.xselection.property, XA_ATOM, 32, PropModeReplace, + (unsigned char *)&available[0], 4); + xlib.XSendEvent(x11_ctx.display, send.xselection.requestor, True, 0, &send); + } + if (event.xselectionrequest.target == x11_ctx.atoms.TEXT_PLAIN || event.xselectionrequest.target == x11_ctx.atoms.UTF8_STRING) { + XEvent send; + send.xselection.type = SelectionNotify; + send.xselection.requestor = event.xselectionrequest.requestor; + send.xselection.selection = event.xselectionrequest.selection; + send.xselection.target = event.xselectionrequest.target; + send.xselection.property = event.xselectionrequest.property; + send.xselection.time = event.xselectionrequest.time; + xlib.XChangeProperty(x11_ctx.display, send.xselection.requestor, send.xselection.property, send.xselection.target, 8, PropModeReplace, + (const unsigned char *)clipboardString, strlen(clipboardString)); + xlib.XSendEvent(x11_ctx.display, send.xselection.requestor, True, 0, &send); + } + break; + } + case Expose: + break; + case FocusIn: { + kinc_internal_foreground_callback(); + break; + } + case FocusOut: { + controlDown = false; + ignoreKeycode = 0; + kinc_internal_background_callback(); + break; + } + case LeaveNotify: + kinc_internal_mouse_trigger_leave_window(k_window->window_index); + break; + case EnterNotify: + x11_ctx.mouse.current_window = k_window->window_index; + kinc_internal_mouse_trigger_enter_window(k_window->window_index); + break; + } + } + + return true; +} + +void kinc_x11_copy_to_clipboard(const char *text) { + size_t textLength = strlen(text); + if (textLength >= clipboardStringSize) { + free(clipboardString); + clipboardStringSize = textLength + 1; + clipboardString = (char *)malloc(clipboardStringSize); + } + strcpy(clipboardString, text); +} + +#ifdef KINC_EGL +EGLDisplay kinc_x11_egl_get_display() { + return eglGetDisplay(x11_ctx.display); +} + +EGLNativeWindowType kinc_x11_egl_get_native_window(EGLDisplay display, EGLConfig config, int window_index) { + return (EGLNativeWindowType)x11_ctx.windows[window_index].window; +} +#endif + +#ifdef KINC_VULKAN +#include +#include +VkResult kinc_x11_vulkan_create_surface(VkInstance instance, int window_index, VkSurfaceKHR *surface) { + VkXlibSurfaceCreateInfoKHR info = {0}; + info.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + info.pNext = NULL; + info.flags = 0; + info.dpy = x11_ctx.display; + info.window = x11_ctx.windows[window_index].window; + return vkCreateXlibSurfaceKHR(instance, &info, NULL, surface); +} + +#include + +void kinc_x11_vulkan_get_instance_extensions(const char **names, int *index, int max) { + assert(*index + 1 < max); + names[(*index)++] = VK_KHR_XLIB_SURFACE_EXTENSION_NAME; +} + +VkBool32 kinc_x11_vulkan_get_physical_device_presentation_support(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex) { + return vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice, queueFamilyIndex, x11_ctx.display, + DefaultVisual(x11_ctx.display, DefaultScreen(x11_ctx.display))->visualid); +} +#endif + +void kinc_x11_mouse_lock(int window) { + kinc_mouse_hide(); + int width = kinc_window_width(window); + int height = kinc_window_height(window); + + int x, y; + kinc_mouse_get_position(window, &x, &y); + + // Guess the new position of X and Y + int newX = x; + int newY = y; + + // Correct the position of the X coordinate + // if the mouse is out the window + if (x < 0) { + newX -= x; + } + else if (x > width) { + newX -= x - width; + } + + // Correct the position of the Y coordinate + // if the mouse is out the window + if (y < 0) { + newY -= y; + } + else if (y > height) { + newY -= y - height; + } + + // Force the mouse to stay inside the window + kinc_mouse_set_position(window, newX, newY); +} + +void kinc_x11_mouse_unlock(void) { + kinc_mouse_show(); +} + +bool kinc_x11_mouse_can_lock(void) { + return true; +} + +static bool mouseHidden = false; + +void kinc_x11_mouse_show() { + struct kinc_x11_window *window = &x11_ctx.windows[x11_ctx.mouse.current_window]; + if (mouseHidden) { + xlib.XUndefineCursor(x11_ctx.display, window->window); + mouseHidden = false; + } +} + +void kinc_x11_mouse_hide() { + struct kinc_x11_window *window = &x11_ctx.windows[x11_ctx.mouse.current_window]; + if (!mouseHidden) { + XColor col; + col.pixel = 0; + col.red = 0; + col.green = 0; + col.blue = 0; + col.flags = DoRed | DoGreen | DoBlue; + col.pad = 0; + char data[1] = {'\0'}; + Pixmap blank = xlib.XCreateBitmapFromData(x11_ctx.display, window->window, data, 1, 1); + Cursor cursor = xlib.XCreatePixmapCursor(x11_ctx.display, blank, blank, &col, &col, 0, 0); + xlib.XDefineCursor(x11_ctx.display, window->window, cursor); + xlib.XFreePixmap(x11_ctx.display, blank); + mouseHidden = true; + } +} + +void kinc_x11_mouse_set_cursor(int cursorIndex) { + struct kinc_x11_window *window = &x11_ctx.windows[x11_ctx.mouse.current_window]; + if (!mouseHidden) { + Cursor cursor; + switch (cursorIndex) { + case 0: { + // cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "arrow"); + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "left_ptr"); + break; + } + case 1: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "hand1"); + break; + } + case 2: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "xterm"); + break; + } + case 3: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "sb_h_double_arrow"); + break; + } + case 4: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "sb_v_double_arrow"); + break; + } + case 5: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "top_right_corner"); + break; + } + case 6: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "bottom_right_corner"); + break; + } + case 7: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "top_left_corner"); + break; + } + case 8: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "bottom_left_corner"); + break; + } + case 9: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "grab"); + break; + } + case 10: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "grabbing"); + break; + } + case 11: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "not-allowed"); + break; + } + case 12: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "watch"); + break; + } + case 13: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "crosshair"); + break; + } + default: { + cursor = xlib.XcursorLibraryLoadCursor(x11_ctx.display, "arrow"); + break; + } + } + xlib.XDefineCursor(x11_ctx.display, window->window, cursor); + } +} + +void kinc_x11_mouse_set_position(int window_index, int x, int y) { + struct kinc_x11_window *window = &x11_ctx.windows[window_index]; + + xlib.XWarpPointer(x11_ctx.display, None, window->window, 0, 0, 0, 0, x, y); + xlib.XFlush(x11_ctx.display); // Flushes the output buffer, therefore updates the cursor's position. +} + +void kinc_x11_mouse_get_position(int window_index, int *x, int *y) { + struct kinc_x11_window *window = &x11_ctx.windows[window_index]; + Window inwin; + Window inchildwin; + int rootx, rooty; + unsigned int mask; + + xlib.XQueryPointer(x11_ctx.display, window->window, &inwin, &inchildwin, &rootx, &rooty, x, y, &mask); +} diff --git a/armorcore/sources/backends/linux/kinc/backend/x11/window.c.h b/armorcore/sources/backends/linux/kinc/backend/x11/window.c.h new file mode 100644 index 000000000..fb8ada005 --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/x11/window.c.h @@ -0,0 +1,206 @@ +#include "x11.h" + +#include + +struct MwmHints { + // These correspond to XmRInt resources. (VendorSE.c) + int flags; + int functions; + int decorations; + int input_mode; + int status; +}; +#define MWM_HINTS_DECORATIONS (1L << 1) + +void kinc_x11_window_set_title(int window_index, const char *title); +void kinc_x11_window_change_mode(int window_index, kinc_window_mode_t mode); + +int kinc_x11_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) { + int window_index = -1; + for (int i = 0; i < MAXIMUM_WINDOWS; i++) { + if (x11_ctx.windows[i].window == None) { + window_index = i; + break; + } + } + + if (window_index == -1) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Too much windows (maximum is %i)", MAXIMUM_WINDOWS); + exit(1); + } + + struct kinc_x11_window *window = &x11_ctx.windows[window_index]; + window->window_index = window_index; + window->width = win->width; + window->height = win->height; + + Visual *visual = NULL; + XSetWindowAttributes set_window_attribs = {0}; + + set_window_attribs.border_pixel = 0; + set_window_attribs.event_mask = + KeyPressMask | KeyReleaseMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask | FocusChangeMask; + int screen = DefaultScreen(x11_ctx.display); + visual = DefaultVisual(x11_ctx.display, screen); + int depth = DefaultDepth(x11_ctx.display, screen); + set_window_attribs.colormap = xlib.XCreateColormap(x11_ctx.display, RootWindow(x11_ctx.display, screen), visual, AllocNone); + window->window = xlib.XCreateWindow(x11_ctx.display, RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display)), 0, 0, win->width, win->height, 0, depth, + InputOutput, visual, CWBorderPixel | CWColormap | CWEventMask, &set_window_attribs); + + static char nameClass[256]; + static const char *nameClassAddendum = "_KincApplication"; + strncpy(nameClass, kinc_application_name(), sizeof(nameClass) - strlen(nameClassAddendum) - 1); + strcat(nameClass, nameClassAddendum); + char resNameBuffer[256]; + strncpy(resNameBuffer, kinc_application_name(), 256); + XClassHint classHint = {.res_name = resNameBuffer, .res_class = nameClass}; + xlib.XSetClassHint(x11_ctx.display, window->window, &classHint); + + xlib.XSetLocaleModifiers("@im=none"); + window->xInputMethod = xlib.XOpenIM(x11_ctx.display, NULL, NULL, NULL); + window->xInputContext = xlib.XCreateIC(window->xInputMethod, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, window->window, NULL); + xlib.XSetICFocus(window->xInputContext); + + window->mode = KINC_WINDOW_MODE_WINDOW; + kinc_x11_window_change_mode(window_index, win->mode); + + xlib.XMapWindow(x11_ctx.display, window->window); + + Atom XdndVersion = 5; + xlib.XChangeProperty(x11_ctx.display, window->window, x11_ctx.atoms.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&XdndVersion, 1); + xlib.XSetWMProtocols(x11_ctx.display, window->window, &x11_ctx.atoms.WM_DELETE_WINDOW, 1); + + kinc_x11_window_set_title(window_index, win->title); + + if (x11_ctx.pen.id != -1) { + xlib.XSelectExtensionEvent(x11_ctx.display, window->window, &x11_ctx.pen.motionClass, 1); + } + + if (x11_ctx.eraser.id != -1) { + xlib.XSelectExtensionEvent(x11_ctx.display, window->window, &x11_ctx.eraser.motionClass, 1); + } + + x11_ctx.num_windows++; + return window_index; +} + +void kinc_x11_window_destroy(int window_index) { + xlib.XFlush(x11_ctx.display); + struct kinc_x11_window *window = &x11_ctx.windows[window_index]; + xlib.XDestroyIC(window->xInputContext); + xlib.XCloseIM(window->xInputMethod); + xlib.XDestroyWindow(x11_ctx.display, window->window); + xlib.XFlush(x11_ctx.display); + *window = (struct kinc_x11_window){0}; + x11_ctx.num_windows--; +} + +void kinc_x11_window_set_title(int window_index, const char *_title) { + const char *title = _title == NULL ? "" : _title; + struct kinc_x11_window *window = &x11_ctx.windows[window_index]; + xlib.XChangeProperty(x11_ctx.display, window->window, x11_ctx.atoms.NET_WM_NAME, x11_ctx.atoms.UTF8_STRING, 8, PropModeReplace, (unsigned char *)title, + strlen(title)); + + xlib.XChangeProperty(x11_ctx.display, window->window, x11_ctx.atoms.NET_WM_ICON_NAME, x11_ctx.atoms.UTF8_STRING, 8, PropModeReplace, (unsigned char *)title, + strlen(title)); + + xlib.XFlush(x11_ctx.display); +} + +int kinc_x11_window_x(int window_index) { + kinc_log(KINC_LOG_LEVEL_ERROR, "x11 does not support getting the window position."); + return 0; +} + +int kinc_x11_window_y(int window_index) { + kinc_log(KINC_LOG_LEVEL_ERROR, "x11 does not support getting the window position."); + return 0; +} + +void kinc_x11_window_move(int window_index, int x, int y) { + struct kinc_x11_window *window = &x11_ctx.windows[window_index]; + xlib.XMoveWindow(x11_ctx.display, window->window, x, y); +} + +int kinc_x11_window_width(int window_index) { + return x11_ctx.windows[window_index].width; +} + +int kinc_x11_window_height(int window_index) { + return x11_ctx.windows[window_index].height; +} + +void kinc_x11_window_resize(int window_index, int width, int height) { + struct kinc_x11_window *window = &x11_ctx.windows[window_index]; + xlib.XResizeWindow(x11_ctx.display, window->window, width, height); +} + +void kinc_x11_window_show(int window_index) { + struct kinc_x11_window *window = &x11_ctx.windows[window_index]; + xlib.XMapWindow(x11_ctx.display, window->window); +} + +void kinc_x11_window_hide(int window_index) { + struct kinc_x11_window *window = &x11_ctx.windows[window_index]; + xlib.XUnmapWindow(x11_ctx.display, window->window); +} + +kinc_window_mode_t kinc_x11_window_get_mode(int window_index) { + return x11_ctx.windows[window_index].mode; +} + +void kinc_x11_window_change_mode(int window_index, kinc_window_mode_t mode) { + struct kinc_x11_window *window = &x11_ctx.windows[window_index]; + if (mode == window->mode) { + return; + } + + bool fullscreen = false; + + switch (mode) { + case KINC_WINDOW_MODE_WINDOW: + if (window->mode == KINC_WINDOW_MODE_FULLSCREEN) { + window->mode = KINC_WINDOW_MODE_WINDOW; + fullscreen = false; + } + else { + return; + } + break; + case KINC_WINDOW_MODE_FULLSCREEN: + case KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN: + if (window->mode == KINC_WINDOW_MODE_WINDOW) { + window->mode = KINC_WINDOW_MODE_FULLSCREEN; + fullscreen = true; + } + else { + return; + } + break; + } + + XEvent xev; + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = window->window; + xev.xclient.message_type = x11_ctx.atoms.NET_WM_STATE; + xev.xclient.format = 32; + xev.xclient.data.l[0] = fullscreen ? 1 : 0; + xev.xclient.data.l[1] = x11_ctx.atoms.NET_WM_STATE_FULLSCREEN; + xev.xclient.data.l[2] = 0; + + xlib.XMapWindow(x11_ctx.display, window->window); + + xlib.XSendEvent(x11_ctx.display, DefaultRootWindow(x11_ctx.display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + + xlib.XFlush(x11_ctx.display); +} + +int kinc_x11_window_display(int window_index) { + struct kinc_x11_window *window = &x11_ctx.windows[window_index]; + return window->display_index; +} + +int kinc_x11_count_windows() { + return x11_ctx.num_windows; +} \ No newline at end of file diff --git a/armorcore/sources/backends/linux/kinc/backend/x11/x11.h b/armorcore/sources/backends/linux/kinc/backend/x11/x11.h new file mode 100644 index 000000000..b462ec0d8 --- /dev/null +++ b/armorcore/sources/backends/linux/kinc/backend/x11/x11.h @@ -0,0 +1,200 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef KINC_EGL +#define EGL_NO_PLATFORM_SPECIFIC_TYPES +#include +#endif + +#define MAXIMUM_WINDOWS 16 +#define MAXIMUM_DISPLAYS 16 + +struct kinc_x11_window { + int display_index; + int window_index; + int width; + int height; + kinc_window_mode_t mode; + Window window; + XIM xInputMethod; + XIC xInputContext; +}; + +struct kinc_x11_display { + int index; + int current_mode; + int num_modes; + int x; + int y; + int width; + int height; + bool primary; + char name[64]; + + RROutput output; + RRCrtc crtc; +}; + +struct kinc_x11_mouse { + int current_window; + int x; + int y; +}; + +struct kinc_x11_atoms { + Atom XdndAware; + Atom XdndDrop; + Atom XdndEnter; + Atom XdndTextUriList; + Atom XdndStatus; + Atom XdndActionCopy; + Atom XdndSelection; + Atom CLIPBOARD; + Atom UTF8_STRING; + Atom XSEL_DATA; + Atom TARGETS; + Atom MULTIPLE; + Atom TEXT_PLAIN; + Atom WM_DELETE_WINDOW; + Atom MOTIF_WM_HINTS; + Atom NET_WM_NAME; + Atom NET_WM_ICON_NAME; + Atom NET_WM_STATE; + Atom NET_WM_STATE_FULLSCREEN; + + Atom MOUSE; + Atom TABLET; + Atom KEYBOARD; + Atom TOUCHSCREEN; + Atom TOUCHPAD; + Atom BUTTONBOX; + Atom BARCODE; + Atom TRACKBALL; + Atom QUADRATURE; + Atom ID_MODULE; + Atom ONE_KNOB; + Atom NINE_KNOB; + Atom KNOB_BOX; + Atom SPACEBALL; + Atom DATAGLOVE; + Atom EYETRACKER; + Atom CURSORKEYS; + Atom FOOTMOUSE; + Atom JOYSTICK; +}; + +struct kinc_x11_libs { + void *X11; + void *Xcursor; + void *Xi; + void *Xinerama; + void *Xrandr; +}; + +struct kinc_x11_procs { + Display *(*XOpenDisplay)(const char *name); + Status (*XInternAtoms)(Display *display, char **names, int count, Bool only_if_exists, Atom *atoms_return); + int (*XCloseDisplay)(Display *display); + XErrorHandler (*XSetErrorHandler)(XErrorHandler handler); + int (*XGetErrorText)(Display *, int, char *, int); + int (*XPending)(Display *display); + int (*XFlush)(Display *display); + int (*XNextEvent)(Display *display, XEvent *event_return); + int (*XPeekEvent)(Display *display, XEvent *event_return); + int (*XRefreshKeyboardMapping)(XMappingEvent *event_map); + int (*XwcLookupString)(XIC, XKeyPressedEvent *, wchar_t *, int, KeySym *, int *); + int (*XFilterEvent)(XEvent *, Window); + int (*XConvertSelection)(Display *, Atom, Atom, Atom, Window, Time); + int (*XSetSelectionOwner)(Display *, Atom, Window, Time); + int (*XLookupString)(XKeyEvent *, char *, int, KeySym *, XComposeStatus *); + KeySym (*XkbKeycodeToKeysym)(Display *, KeyCode, int, int); + int (*XSendEvent)(Display *, Window, int, long, XEvent *); + int (*XGetWindowProperty)(Display *, Window, Atom, long, long, int, Atom, Atom *, int *, unsigned long *, unsigned long *, unsigned char **); + int (*XFree)(void *); + int (*XChangeProperty)(Display *, Window, Atom, Atom, int, int, const unsigned char *, int); + int (*XDefineCursor)(Display *, Window, Cursor); + int (*XUndefineCursor)(Display *, Window); + Pixmap (*XCreateBitmapFromData)(Display *, Drawable, const char *, unsigned int, unsigned int); + Cursor (*XCreatePixmapCursor)(Display *, Pixmap, Pixmap, XColor *, XColor *, unsigned int, unsigned int); + int (*XFreePixmap)(Display *, Pixmap); + Cursor (*XcursorLibraryLoadCursor)(Display *, const char *); + int (*XWarpPointer)(Display *, Window, Window, int, int, unsigned int, unsigned int, int, int); + int (*XQueryPointer)(Display *, Window, Window *, Window *, int *, int *, int *, int *, unsigned int *); + Colormap (*XCreateColormap)(Display *, Window, Visual *, int); + Window (*XCreateWindow)(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, + unsigned int class, Visual *visual, unsigned long valuemask, XSetWindowAttributes *attributes); + int (*XMoveWindow)(Display *, Window, int, int); + int (*XResizeWindow)(Display *, Window, unsigned int, unsigned int); + int (*XDestroyWindow)(Display *, Window); + int (*XSetClassHint)(Display *, Window, XClassHint *); + char *(*XSetLocaleModifiers)(const char *); + XIM (*XOpenIM)(Display *, struct _XrmHashBucketRec *, char *, char *); + int (*XCloseIM)(XIM); + XIC (*XCreateIC)(XIM, ...); + void (*XDestroyIC)(XIC); + void (*XSetICFocus)(XIC); + int (*XMapWindow)(Display *, Window); + int (*XUnmapWindow)(Display *, Window); + int (*XSetWMProtocols)(Display *, Window, Atom *, int); + + XDeviceInfo *(*XListInputDevices)(Display *, int *); + void (*XFreeDeviceList)(XDeviceInfo *); + XDevice *(*XOpenDevice)(Display *display, XID device_id); + int (*XCloseDevice)(Display *display, XDevice *device); + int (*XSelectExtensionEvent)(Display *, Window, XEventClass *, int); + + int (*XineramaQueryExtension)(Display *dpy, int *event_base, int *error_base); + int (*XineramaIsActive)(Display *dpy); + XineramaScreenInfo *(*XineramaQueryScreens)(Display *dpy, int *number); + + XRRScreenResources *(*XRRGetScreenResourcesCurrent)(Display *dpy, Window window); + RROutput (*XRRGetOutputPrimary)(Display *dpy, Window window); + XRROutputInfo *(*XRRGetOutputInfo)(Display *dpy, XRRScreenResources *resources, RROutput output); + void (*XRRFreeOutputInfo)(XRROutputInfo *outputInfo); + XRRCrtcInfo *(*XRRGetCrtcInfo)(Display *dpy, XRRScreenResources *resources, RRCrtc crtc); + void (*XRRFreeCrtcInfo)(XRRCrtcInfo *crtcInfo); + void (*XRRFreeScreenResources)(XRRScreenResources *resources); +}; + +struct x11_pen_device { + XID id; + uint32_t motionEvent; + XEventClass motionClass; + uint32_t maxPressure; + float current_pressure; + + void (*press)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/); + void (*move)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/); + void (*release)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/); +}; + +struct x11_context { + Display *display; + struct kinc_x11_libs libs; + struct kinc_x11_atoms atoms; + struct kinc_x11_mouse mouse; + + struct x11_pen_device pen; + struct x11_pen_device eraser; + + int num_windows; + struct kinc_x11_window windows[MAXIMUM_WINDOWS]; + int num_displays; + struct kinc_x11_display displays[MAXIMUM_DISPLAYS]; +}; + +struct kinc_x11_procs xlib; +struct x11_context x11_ctx; + +void kinc_x11_copy_to_clipboard(const char *text); \ No newline at end of file diff --git a/armorcore/sources/backends/macos/kinc/backend/BasicOpenGLView.h b/armorcore/sources/backends/macos/kinc/backend/BasicOpenGLView.h new file mode 100644 index 000000000..741a1167c --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/BasicOpenGLView.h @@ -0,0 +1,69 @@ +#ifdef KINC_METAL +#import +#else +#import +#import +#import +#import +#import +#import +#endif + +#ifdef KINC_METAL + +struct kinc_g5_render_target; + +@interface BasicOpenGLView : MTKView { +@private + id device; + id commandQueue; + id library; +} + +#else + +// (DK) context sharing +// www.cocoabuilder.com/archive/cocoa/29573-sharing-opengl-context.html +// basically: +// -don't use NSOpenGLView, but implement all that by hand +// -use -initWithFormat:shareContext: (NSOpenGLContext) to setup the shared contexts +@interface BasicOpenGLView : NSOpenGLView { +} + +#endif + +#ifdef KINC_METAL +- (CAMetalLayer *)metalLayer; +- (id)metalDevice; +- (id)metalLibrary; +- (id)metalQueue; +#else +- (void)prepareOpenGL; +- (void)switchBuffers; ++ (NSOpenGLPixelFormat *)basicPixelFormat; +#endif + +- (void)keyDown:(NSEvent *)theEvent; +- (void)keyUp:(NSEvent *)theEvent; + +- (void)mouseDown:(NSEvent *)theEvent; +- (void)mouseUp:(NSEvent *)theEvent; +- (void)mouseMoved:(NSEvent *)theEvent; +- (void)mouseDragged:(NSEvent *)theEvent; +- (void)rightMouseDown:(NSEvent *)theEvent; +- (void)rightMouseUp:(NSEvent *)theEvent; +- (void)rightMouseDragged:(NSEvent *)theEvent; +- (void)scrollWheel:(NSEvent *)theEvent; +- (NSDragOperation)draggingEntered:(id)sender; +- (BOOL)performDragOperation:(id)sender; + +- (void)update; // moved or resized + +- (BOOL)acceptsFirstResponder; +- (BOOL)becomeFirstResponder; +- (BOOL)resignFirstResponder; + +- (id)initWithFrame:(NSRect)frameRect; +- (void)resize:(NSSize)size; + +@end diff --git a/armorcore/sources/backends/macos/kinc/backend/BasicOpenGLView.m.h b/armorcore/sources/backends/macos/kinc/backend/BasicOpenGLView.m.h new file mode 100644 index 000000000..04d4fc2dc --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/BasicOpenGLView.m.h @@ -0,0 +1,483 @@ +#import "BasicOpenGLView.h" + +#include +#include +#include +#include + +#ifdef KINC_METAL +#include +#endif + +@implementation BasicOpenGLView + +static bool shift = false; +static bool ctrl = false; +static bool alt = false; +static bool cmd = false; + +#ifndef KINC_METAL ++ (NSOpenGLPixelFormat *)basicPixelFormat { + // TODO (DK) pass via argument in + int aa = 1; // Kore::Application::the()->antialiasing(); + if (aa > 0) { + NSOpenGLPixelFormatAttribute attributes[] = {NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, + (NSOpenGLPixelFormatAttribute)24, // 16 bit depth buffer + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, + NSOpenGLPFASupersample, NSOpenGLPFASampleBuffers, + (NSOpenGLPixelFormatAttribute)1, NSOpenGLPFASamples, + (NSOpenGLPixelFormatAttribute)aa, NSOpenGLPFAStencilSize, + (NSOpenGLPixelFormatAttribute)8, (NSOpenGLPixelFormatAttribute)0}; + return [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + } + else { + NSOpenGLPixelFormatAttribute attributes[] = { + NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)24, // 16 bit depth buffer + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, NSOpenGLPFAStencilSize, + (NSOpenGLPixelFormatAttribute)8, (NSOpenGLPixelFormatAttribute)0}; + return [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + } +} + +- (void)switchBuffers { + [[self openGLContext] flushBuffer]; +} +#endif + +- (void)flagsChanged:(NSEvent *)theEvent { + if (shift) { + kinc_internal_keyboard_trigger_key_up(KINC_KEY_SHIFT); + shift = false; + } + if (ctrl) { + kinc_internal_keyboard_trigger_key_up(KINC_KEY_CONTROL); + ctrl = false; + } + if (alt) { + kinc_internal_keyboard_trigger_key_up(KINC_KEY_ALT); + alt = false; + } + if (cmd) { + kinc_internal_keyboard_trigger_key_up(KINC_KEY_META); + cmd = false; + } + + if ([theEvent modifierFlags] & NSShiftKeyMask) { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_SHIFT); + shift = true; + } + if ([theEvent modifierFlags] & NSControlKeyMask) { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_CONTROL); + ctrl = true; + } + if ([theEvent modifierFlags] & NSAlternateKeyMask) { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_ALT); + alt = true; + } + if ([theEvent modifierFlags] & NSCommandKeyMask) { + kinc_internal_keyboard_trigger_key_down(KINC_KEY_META); + cmd = true; + } +} + +- (void)keyDown:(NSEvent *)theEvent { + if ([theEvent isARepeat]) + return; + NSString *characters = [theEvent charactersIgnoringModifiers]; + if ([characters length]) { + unichar ch = [characters characterAtIndex:0]; + switch (ch) { // keys that exist in keydown and keypress events + case 59: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_SEMICOLON); + break; + case 91: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_OPEN_BRACKET); + break; + case 93: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_CLOSE_BRACKET); + break; + case 39: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_QUOTE); + break; + case 92: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_BACK_SLASH); + break; + case 44: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_COMMA); + break; + case 46: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_PERIOD); + break; + case 47: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_SLASH); + break; + case 96: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_BACK_QUOTE); + break; + case 32: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_SPACE); + break; + case 45: // we need breaks because EQUALS triggered too for some reason + kinc_internal_keyboard_trigger_key_down(KINC_KEY_HYPHEN_MINUS); + break; + case 61: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_EQUALS); + break; + } + switch (ch) { + case NSRightArrowFunctionKey: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_RIGHT); + break; + case NSLeftArrowFunctionKey: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_LEFT); + break; + case NSUpArrowFunctionKey: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_UP); + break; + case NSDownArrowFunctionKey: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_DOWN); + break; + case 27: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_ESCAPE); + break; + case NSEnterCharacter: + case NSNewlineCharacter: + case NSCarriageReturnCharacter: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_RETURN); + kinc_internal_keyboard_trigger_key_press('\n'); + break; + case 0x7f: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_BACKSPACE); + kinc_internal_keyboard_trigger_key_press('\x08'); + break; + case 9: + kinc_internal_keyboard_trigger_key_down(KINC_KEY_TAB); + kinc_internal_keyboard_trigger_key_press('\t'); + break; + default: + if (ch == 'x' && [theEvent modifierFlags] & NSCommandKeyMask) { + char *text = kinc_internal_cut_callback(); + if (text != NULL) { + NSPasteboard *board = [NSPasteboard generalPasteboard]; + [board clearContents]; + [board setString:[NSString stringWithUTF8String:text] forType:NSStringPboardType]; + } + } + if (ch == 'c' && [theEvent modifierFlags] & NSCommandKeyMask) { + char *text = kinc_internal_copy_callback(); + if (text != NULL) { + NSPasteboard *board = [NSPasteboard generalPasteboard]; + [board clearContents]; + [board setString:[NSString stringWithUTF8String:text] forType:NSStringPboardType]; + } + } + if (ch == 'v' && [theEvent modifierFlags] & NSCommandKeyMask) { + NSPasteboard *board = [NSPasteboard generalPasteboard]; + NSString *data = [board stringForType:NSStringPboardType]; + if (data != nil) { + char charData[4096]; + strcpy(charData, [data UTF8String]); + kinc_internal_paste_callback(charData); + } + } + if (ch >= L'a' && ch <= L'z') { + kinc_internal_keyboard_trigger_key_down(ch - L'a' + KINC_KEY_A); + } + else if (ch >= L'A' && ch <= L'Z') { + kinc_internal_keyboard_trigger_key_down(ch - L'A' + KINC_KEY_A); + } + else if (ch >= L'0' && ch <= L'9') { + kinc_internal_keyboard_trigger_key_down(ch - L'0' + KINC_KEY_0); + } + kinc_internal_keyboard_trigger_key_press(ch); + break; + } + } +} + +- (void)keyUp:(NSEvent *)theEvent { + NSString *characters = [theEvent charactersIgnoringModifiers]; + if ([characters length]) { + unichar ch = [characters characterAtIndex:0]; + switch (ch) { + case 59: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_SEMICOLON); + break; + case 91: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_OPEN_BRACKET); + break; + case 93: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_CLOSE_BRACKET); + break; + case 39: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_QUOTE); + break; + case 92: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_BACK_SLASH); + break; + case 44: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_COMMA); + break; + case 46: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_PERIOD); + break; + case 47: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_SLASH); + break; + case 96: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_BACK_QUOTE); + break; + case 45: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_HYPHEN_MINUS); + break; + case 61: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_EQUALS); + break; + case NSRightArrowFunctionKey: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_RIGHT); + break; + case NSLeftArrowFunctionKey: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_LEFT); + break; + case NSUpArrowFunctionKey: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_UP); + break; + case NSDownArrowFunctionKey: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_DOWN); + break; + case 27: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_ESCAPE); + break; + case NSEnterCharacter: + case NSNewlineCharacter: + case NSCarriageReturnCharacter: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_RETURN); + break; + case 0x7f: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_BACKSPACE); + break; + case 9: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_TAB); + break; + case 32: + kinc_internal_keyboard_trigger_key_up(KINC_KEY_SPACE); + break; + default: + if (ch >= L'a' && ch <= L'z') { + kinc_internal_keyboard_trigger_key_up(ch - L'a' + KINC_KEY_A); + } + else if (ch >= L'A' && ch <= L'Z') { + kinc_internal_keyboard_trigger_key_up(ch - L'A' + KINC_KEY_A); + } + else if (ch >= L'0' && ch <= L'9') { + kinc_internal_keyboard_trigger_key_up(ch - L'0' + KINC_KEY_0); + } + break; + } + } +} + +static int getMouseX(NSEvent *event) { + // TODO (DK) map [theEvent window] to window id instead of 0 + NSWindow *window = [[NSApplication sharedApplication] mainWindow]; + float scale = [window backingScaleFactor]; + return (int)([event locationInWindow].x * scale); +} + +static int getMouseY(NSEvent *event) { + // TODO (DK) map [theEvent window] to window id instead of 0 + NSWindow *window = [[NSApplication sharedApplication] mainWindow]; + float scale = [window backingScaleFactor]; + return (int)(kinc_height() - [event locationInWindow].y * scale); +} + +static bool controlKeyMouseButton = false; + +- (void)mouseDown:(NSEvent *)theEvent { + // TODO (DK) map [theEvent window] to window id instead of 0 + if ([theEvent modifierFlags] & NSControlKeyMask) { + controlKeyMouseButton = true; + kinc_internal_mouse_trigger_press(0, 1, getMouseX(theEvent), getMouseY(theEvent)); + } + else { + controlKeyMouseButton = false; + kinc_internal_mouse_trigger_press(0, 0, getMouseX(theEvent), getMouseY(theEvent)); + } + + if ([theEvent subtype] == NSTabletPointEventSubtype) { + kinc_internal_pen_trigger_press(0, getMouseX(theEvent), getMouseY(theEvent), theEvent.pressure); + } +} + +- (void)mouseUp:(NSEvent *)theEvent { + // TODO (DK) map [theEvent window] to window id instead of 0 + if (controlKeyMouseButton) { + kinc_internal_mouse_trigger_release(0, 1, getMouseX(theEvent), getMouseY(theEvent)); + } + else { + kinc_internal_mouse_trigger_release(0, 0, getMouseX(theEvent), getMouseY(theEvent)); + } + controlKeyMouseButton = false; + + if ([theEvent subtype] == NSTabletPointEventSubtype) { + kinc_internal_pen_trigger_release(0, getMouseX(theEvent), getMouseY(theEvent), theEvent.pressure); + } +} + +- (void)mouseMoved:(NSEvent *)theEvent { + // TODO (DK) map [theEvent window] to window id instead of 0 + kinc_internal_mouse_trigger_move(0, getMouseX(theEvent), getMouseY(theEvent)); +} + +- (void)mouseDragged:(NSEvent *)theEvent { + // TODO (DK) map [theEvent window] to window id instead of 0 + kinc_internal_mouse_trigger_move(0, getMouseX(theEvent), getMouseY(theEvent)); + + if ([theEvent subtype] == NSTabletPointEventSubtype) { + kinc_internal_pen_trigger_move(0, getMouseX(theEvent), getMouseY(theEvent), theEvent.pressure); + } +} + +- (void)rightMouseDown:(NSEvent *)theEvent { + // TODO (DK) map [theEvent window] to window id instead of 0 + kinc_internal_mouse_trigger_press(0, 1, getMouseX(theEvent), getMouseY(theEvent)); +} + +- (void)rightMouseUp:(NSEvent *)theEvent { + // TODO (DK) map [theEvent window] to window id instead of 0 + kinc_internal_mouse_trigger_release(0, 1, getMouseX(theEvent), getMouseY(theEvent)); +} + +- (void)rightMouseDragged:(NSEvent *)theEvent { + // TODO (DK) map [theEvent window] to window id instead of 0 + kinc_internal_mouse_trigger_move(0, getMouseX(theEvent), getMouseY(theEvent)); +} + +- (void)otherMouseDown:(NSEvent *)theEvent { + kinc_internal_mouse_trigger_press(0, 2, getMouseX(theEvent), getMouseY(theEvent)); +} + +- (void)otherMouseUp:(NSEvent *)theEvent { + kinc_internal_mouse_trigger_release(0, 2, getMouseX(theEvent), getMouseY(theEvent)); +} + +- (void)otherMouseDragged:(NSEvent *)theEvent { + kinc_internal_mouse_trigger_move(0, getMouseX(theEvent), getMouseY(theEvent)); +} + +- (void)scrollWheel:(NSEvent *)theEvent { + // TODO (DK) map [theEvent window] to window id instead of 0 + int delta = [theEvent deltaY]; + kinc_internal_mouse_trigger_scroll(0, -delta); +} + +- (NSDragOperation)draggingEntered:(id)sender { + NSPasteboard *pboard = [sender draggingPasteboard]; + NSDragOperation sourceDragMask = [sender draggingSourceOperationMask]; + if ([[pboard types] containsObject:NSURLPboardType]) { + if (sourceDragMask & NSDragOperationLink) { + return NSDragOperationLink; + } + } + return NSDragOperationNone; +} + +- (BOOL)performDragOperation:(id)sender { + NSPasteboard *pboard = [sender draggingPasteboard]; + // NSDragOperation sourceDragMask = [sender draggingSourceOperationMask]; + if ([[pboard types] containsObject:NSURLPboardType]) { + NSURL *fileURL = [NSURL URLFromPasteboard:pboard]; + wchar_t *filePath = (wchar_t *)[fileURL.path cStringUsingEncoding:NSUTF32LittleEndianStringEncoding]; + kinc_internal_drop_files_callback(filePath); + } + return YES; +} + +#ifndef KINC_METAL +- (void)prepareOpenGL { + const GLint swapInt = 1; + [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + [super prepareOpenGL]; +} +#endif + +- (void)update { // window resizes, moves and display changes (resize, depth and display config change) +#ifdef KINC_OPENGL + [super update]; +#endif +} + +#ifndef KINC_METAL +- (id)initWithFrame:(NSRect)frameRect { + NSOpenGLPixelFormat *pf = [BasicOpenGLView basicPixelFormat]; + self = [super initWithFrame:frameRect pixelFormat:pf]; + + [self prepareOpenGL]; + [[self openGLContext] makeCurrentContext]; + [self setWantsBestResolutionOpenGLSurface:YES]; + return self; +} +#else + +- (id)initWithFrame:(NSRect)frameRect { + self = [super initWithFrame:frameRect]; + + device = MTLCreateSystemDefaultDevice(); + commandQueue = [device newCommandQueue]; + library = [device newDefaultLibrary]; + + CAMetalLayer *metalLayer = (CAMetalLayer *)self.layer; + + metalLayer.device = device; + metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; + metalLayer.framebufferOnly = YES; + // metalLayer.presentsWithTransaction = YES; + + metalLayer.opaque = YES; + metalLayer.backgroundColor = nil; + + return self; +} +#endif + +- (BOOL)acceptsFirstResponder { + return YES; +} + +- (BOOL)becomeFirstResponder { + return YES; +} + +- (BOOL)resignFirstResponder { + return YES; +} + +- (void)resize:(NSSize)size { + [self setFrameSize:size]; +} + +#ifdef KINC_METAL +- (CAMetalLayer *)metalLayer { + return (CAMetalLayer *)self.layer; +} + +- (id)metalDevice { + return device; +} + +- (id)metalLibrary { + return library; +} + +- (id)metalQueue { + return commandQueue; +} +#endif + +@end + +void kinc_copy_to_clipboard(const char *text) { + NSPasteboard *board = [NSPasteboard generalPasteboard]; + [board clearContents]; + [board setString:[NSString stringWithUTF8String:text] forType:NSStringPboardType]; +} diff --git a/armorcore/sources/backends/macos/kinc/backend/HIDGamepad.c.h b/armorcore/sources/backends/macos/kinc/backend/HIDGamepad.c.h new file mode 100644 index 000000000..c18c75158 --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/HIDGamepad.c.h @@ -0,0 +1,384 @@ +#include "HIDGamepad.h" +#include "HIDManager.h" + +#include +#include +#include +#include + +static void inputValueCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef inIOHIDValueRef); +static void valueAvailableCallback(void *inContext, IOReturn inResult, void *inSender); + +static void reset(struct HIDGamepad *gamepad); + +static void initDeviceElements(struct HIDGamepad *gamepad, CFArrayRef elements); + +static void buttonChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int buttonIndex); +static void axisChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int axisIndex); + +static bool debugButtonInput = false; + +static void logButton(int buttonIndex, bool pressed) { + switch (buttonIndex) { + case 0: + kinc_log(KINC_LOG_LEVEL_INFO, "A Pressed %i", pressed); + break; + + case 1: + kinc_log(KINC_LOG_LEVEL_INFO, "B Pressed %i", pressed); + break; + + case 2: + kinc_log(KINC_LOG_LEVEL_INFO, "X Pressed %i", pressed); + break; + + case 3: + kinc_log(KINC_LOG_LEVEL_INFO, "Y Pressed %i", pressed); + break; + + case 4: + kinc_log(KINC_LOG_LEVEL_INFO, "Lb Pressed %i", pressed); + break; + + case 5: + kinc_log(KINC_LOG_LEVEL_INFO, "Rb Pressed %i", pressed); + break; + + case 6: + kinc_log(KINC_LOG_LEVEL_INFO, "Left Stick Pressed %i", pressed); + break; + + case 7: + kinc_log(KINC_LOG_LEVEL_INFO, "Right Stick Pressed %i", pressed); + break; + + case 8: + kinc_log(KINC_LOG_LEVEL_INFO, "Start Pressed %i", pressed); + break; + + case 9: + kinc_log(KINC_LOG_LEVEL_INFO, "Back Pressed %i", pressed); + break; + + case 10: + kinc_log(KINC_LOG_LEVEL_INFO, "Home Pressed %i", pressed); + break; + + case 11: + kinc_log(KINC_LOG_LEVEL_INFO, "Up Pressed %i", pressed); + break; + + case 12: + kinc_log(KINC_LOG_LEVEL_INFO, "Down Pressed %i", pressed); + break; + + case 13: + kinc_log(KINC_LOG_LEVEL_INFO, "Left Pressed %i", pressed); + break; + + case 14: + kinc_log(KINC_LOG_LEVEL_INFO, "Right Pressed %i", pressed); + break; + + default: + break; + } +} + +static bool debugAxisInput = false; + +static void logAxis(int axisIndex) { + switch (axisIndex) { + case 0: + kinc_log(KINC_LOG_LEVEL_INFO, "Left stick X"); + break; + + case 1: + kinc_log(KINC_LOG_LEVEL_INFO, "Left stick Y"); + break; + + case 2: + kinc_log(KINC_LOG_LEVEL_INFO, "Right stick X"); + break; + + case 3: + kinc_log(KINC_LOG_LEVEL_INFO, "Right stick Y"); + break; + + case 4: + kinc_log(KINC_LOG_LEVEL_INFO, "Left trigger"); + break; + + case 5: + kinc_log(KINC_LOG_LEVEL_INFO, "Right trigger"); + break; + + default: + break; + } +} + +// Helper function to copy a CFStringRef to a cstring buffer. +// CFStringRef is converted to UTF8 and as many characters as possible are +// placed into the buffer followed by a null terminator. +// The buffer is set to an empty string if the conversion fails. +static void cstringFromCFStringRef(CFStringRef string, char *cstr, size_t clen) { + cstr[0] = '\0'; + if (string != NULL) { + char temp[256]; + if (CFStringGetCString(string, temp, 256, kCFStringEncodingUTF8)) { + temp[kinc_mini(255, (int)(clen - 1))] = '\0'; + strncpy(cstr, temp, clen); + } + } +} + +void HIDGamepad_init(struct HIDGamepad *gamepad) { + reset(gamepad); +} + +void HIDGamepad_destroy(struct HIDGamepad *gamepad) { + HIDGamepad_unbind(gamepad); +} + +void HIDGamepad_bind(struct HIDGamepad *gamepad, IOHIDDeviceRef inDeviceRef, int inPadIndex) { + kinc_affirm(inDeviceRef != NULL); + kinc_affirm(inPadIndex >= 0); + kinc_affirm(gamepad->hidDeviceRef == NULL); + kinc_affirm(gamepad->hidQueueRef == NULL); + kinc_affirm(gamepad->padIndex == -1); + + // Set device and device index + gamepad->hidDeviceRef = inDeviceRef; + gamepad->padIndex = inPadIndex; + + // Initialise HID Device + // ...open device + IOHIDDeviceOpen(gamepad->hidDeviceRef, kIOHIDOptionsTypeSeizeDevice); + + // ..register callbacks + IOHIDDeviceRegisterInputValueCallback(gamepad->hidDeviceRef, inputValueCallback, gamepad); + IOHIDDeviceScheduleWithRunLoop(gamepad->hidDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + + // ...create a queue to access element values + gamepad->hidQueueRef = IOHIDQueueCreate(kCFAllocatorDefault, gamepad->hidDeviceRef, 32, kIOHIDOptionsTypeNone); + if (CFGetTypeID(gamepad->hidQueueRef) == IOHIDQueueGetTypeID()) { + IOHIDQueueStart(gamepad->hidQueueRef); + IOHIDQueueRegisterValueAvailableCallback(gamepad->hidQueueRef, valueAvailableCallback, gamepad); + IOHIDQueueScheduleWithRunLoop(gamepad->hidQueueRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + } + + // ...get all elements (buttons, axes) + CFArrayRef elementCFArrayRef = IOHIDDeviceCopyMatchingElements(gamepad->hidDeviceRef, NULL, kIOHIDOptionsTypeNone); + initDeviceElements(gamepad, elementCFArrayRef); + + // ...get device manufacturer and product details + { + CFNumberRef vendorIdRef = (CFNumberRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDVendorIDKey)); + CFNumberGetValue(vendorIdRef, kCFNumberIntType, &gamepad->hidDeviceVendorID); + + CFNumberRef productIdRef = (CFNumberRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDProductIDKey)); + CFNumberGetValue(productIdRef, kCFNumberIntType, &gamepad->hidDeviceProductID); + + CFStringRef vendorRef = (CFStringRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDManufacturerKey)); + cstringFromCFStringRef(vendorRef, gamepad->hidDeviceVendor, sizeof(gamepad->hidDeviceVendor)); + + CFStringRef productRef = (CFStringRef)IOHIDDeviceGetProperty(gamepad->hidDeviceRef, CFSTR(kIOHIDProductKey)); + cstringFromCFStringRef(productRef, gamepad->hidDeviceProduct, sizeof(gamepad->hidDeviceProduct)); + } + + // Initialise Kore::Gamepad for this HID Device + //** + /*Gamepad *gamepad = Gamepad::get(padIndex); + gamepad->vendor = hidDeviceVendor; + gamepad->productName = hidDeviceProduct;*/ + + kinc_log(KINC_LOG_LEVEL_INFO, "HIDGamepad.bind: <%p> idx:%d [0x%x:0x%x] [%s] [%s]", inDeviceRef, gamepad->padIndex, gamepad->hidDeviceVendorID, + gamepad->hidDeviceProductID, gamepad->hidDeviceVendor, gamepad->hidDeviceProduct); +} + +static void initDeviceElements(struct HIDGamepad *gamepad, CFArrayRef elements) { + kinc_affirm(elements != NULL); + + for (CFIndex i = 0, count = CFArrayGetCount(elements); i < count; ++i) { + IOHIDElementRef elementRef = (IOHIDElementRef)CFArrayGetValueAtIndex(elements, i); + IOHIDElementType elemType = IOHIDElementGetType(elementRef); + + IOHIDElementCookie cookie = IOHIDElementGetCookie(elementRef); + + uint32_t usagePage = IOHIDElementGetUsagePage(elementRef); + uint32_t usage = IOHIDElementGetUsage(elementRef); + + // Match up items + switch (usagePage) { + case kHIDPage_GenericDesktop: + switch (usage) { + case kHIDUsage_GD_X: // Left stick X + // log(Info, "Left stick X axis[0] = %i", cookie); + gamepad->axis[0] = cookie; + break; + case kHIDUsage_GD_Y: // Left stick Y + // log(Info, "Left stick Y axis[1] = %i", cookie); + gamepad->axis[1] = cookie; + break; + case kHIDUsage_GD_Z: // Left trigger + // log(Info, "Left trigger axis[4] = %i", cookie); + gamepad->axis[4] = cookie; + break; + case kHIDUsage_GD_Rx: // Right stick X + // log(Info, "Right stick X axis[2] = %i", cookie); + gamepad->axis[2] = cookie; + break; + case kHIDUsage_GD_Ry: // Right stick Y + // log(Info, "Right stick Y axis[3] = %i", cookie); + gamepad->axis[3] = cookie; + break; + case kHIDUsage_GD_Rz: // Right trigger + // log(Info, "Right trigger axis[5] = %i", cookie); + gamepad->axis[5] = cookie; + break; + case kHIDUsage_GD_Hatswitch: + break; + default: + break; + } + break; + case kHIDPage_Button: + if ((usage >= 1) && (usage <= 15)) { + // Button 1-11 + gamepad->buttons[usage - 1] = cookie; + // log(Info, "Button %i = %i", usage-1, cookie); + } + break; + default: + break; + } + + if (elemType == kIOHIDElementTypeInput_Misc || elemType == kIOHIDElementTypeInput_Button || elemType == kIOHIDElementTypeInput_Axis) { + if (!IOHIDQueueContainsElement(gamepad->hidQueueRef, elementRef)) + IOHIDQueueAddElement(gamepad->hidQueueRef, elementRef); + } + } +} + +void HIDGamepad_unbind(struct HIDGamepad *gamepad) { + kinc_log(KINC_LOG_LEVEL_INFO, "HIDGamepad.unbind: idx:%d [0x%x:0x%x] [%s] [%s]", gamepad->padIndex, gamepad->hidDeviceVendorID, gamepad->hidDeviceProductID, + gamepad->hidDeviceVendor, gamepad->hidDeviceProduct); + + if (gamepad->hidQueueRef) { + IOHIDQueueStop(gamepad->hidQueueRef); + IOHIDQueueUnscheduleFromRunLoop(gamepad->hidQueueRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + } + + if (gamepad->hidDeviceRef) { + IOHIDDeviceUnscheduleFromRunLoop(gamepad->hidDeviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOHIDDeviceClose(gamepad->hidDeviceRef, kIOHIDOptionsTypeSeizeDevice); + } + + if (gamepad->padIndex >= 0) { + //** + /*Gamepad *gamepad = Gamepad::get(padIndex); + gamepad->vendor = nullptr; + gamepad->productName = nullptr;*/ + } + + reset(gamepad); +} + +static void reset(struct HIDGamepad *gamepad) { + gamepad->padIndex = -1; + gamepad->hidDeviceRef = NULL; + gamepad->hidQueueRef = NULL; + gamepad->hidDeviceVendor[0] = '\0'; + gamepad->hidDeviceProduct[0] = '\0'; + gamepad->hidDeviceVendorID = 0; + gamepad->hidDeviceProductID = 0; + + memset(gamepad->axis, 0, sizeof(gamepad->axis)); + memset(gamepad->buttons, 0, sizeof(gamepad->buttons)); +} + +static void buttonChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int buttonIndex) { + // double rawValue = IOHIDValueGetIntegerValue(valueRef); + double rawValue = IOHIDValueGetScaledValue(valueRef, kIOHIDValueScaleTypePhysical); + + // Normalize button value to the range [0.0, 1.0] (0 - release, 1 - pressed) + double min = IOHIDElementGetLogicalMin(elementRef); + double max = IOHIDElementGetLogicalMax(elementRef); + double normalize = (rawValue - min) / (max - min); + + // log(Info, "%f %f %f %f", rawValue, min, max, normalize); + + kinc_internal_gamepad_trigger_button(gamepad->padIndex, buttonIndex, normalize); + + if (debugButtonInput) + logButton(buttonIndex, (normalize != 0)); +} + +static void axisChanged(struct HIDGamepad *gamepad, IOHIDElementRef elementRef, IOHIDValueRef valueRef, int axisIndex) { + // double rawValue = IOHIDValueGetIntegerValue(valueRef); + double rawValue = IOHIDValueGetScaledValue(valueRef, kIOHIDValueScaleTypePhysical); + + // Normalize axis value to the range [-1.0, 1.0] (e.g. -1 - left, 0 - release, 1 - right) + double min = IOHIDElementGetPhysicalMin(elementRef); + double max = IOHIDElementGetPhysicalMax(elementRef); + double normalize = normalize = (((rawValue - min) / (max - min)) * 2) - 1; + + // Invert Y axis + if (axisIndex % 2 == 1) + normalize = -normalize; + + // log(Info, "%f %f %f %f", rawValue, min, max, normalize); + + kinc_internal_gamepad_trigger_axis(gamepad->padIndex, axisIndex, normalize); + + if (debugAxisInput) + logAxis(axisIndex); +} + +static void inputValueCallback(void *inContext, IOReturn inResult, void *inSender, IOHIDValueRef inIOHIDValueRef) {} + +static void valueAvailableCallback(void *inContext, IOReturn inResult, void *inSender) { + struct HIDGamepad *pad = (struct HIDGamepad *)inContext; + do { + IOHIDValueRef valueRef = IOHIDQueueCopyNextValueWithTimeout((IOHIDQueueRef)inSender, 0.); + if (!valueRef) + break; + + // process the HID value reference + IOHIDElementRef elementRef = IOHIDValueGetElement(valueRef); + // IOHIDElementType elemType = IOHIDElementGetType(elementRef); + // log(Info, "Type %d %d\n", elemType, elementRef); + + IOHIDElementCookie cookie = IOHIDElementGetCookie(elementRef); + // uint32_t page = IOHIDElementGetUsagePage(elementRef); + // uint32_t usage = IOHIDElementGetUsage(elementRef); + // log(Info, "page %i, usage %i cookie %i", page, usage, cookie); + + // Check button + for (int i = 0, c = sizeof(pad->buttons); i < c; ++i) { + if (cookie == pad->buttons[i]) { + buttonChanged(pad, elementRef, valueRef, i); + break; + } + } + + // Check axes + for (int i = 0, c = sizeof(pad->axis); i < c; ++i) { + if (cookie == pad->axis[i]) { + axisChanged(pad, elementRef, valueRef, i); + break; + } + } + + CFRelease(valueRef); + } while (1); +} + +const char *kinc_gamepad_vendor(int gamepad) { + return "unknown"; +} + +const char *kinc_gamepad_product_name(int gamepad) { + return "unknown"; +} diff --git a/armorcore/sources/backends/macos/kinc/backend/HIDGamepad.h b/armorcore/sources/backends/macos/kinc/backend/HIDGamepad.h new file mode 100644 index 000000000..3fc310c04 --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/HIDGamepad.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include + +struct HIDGamepad { + int padIndex; + IOHIDDeviceRef hidDeviceRef; + IOHIDQueueRef hidQueueRef; + int hidDeviceVendorID; + int hidDeviceProductID; + char hidDeviceVendor[64]; + char hidDeviceProduct[64]; + + IOHIDElementCookie axis[6]; + IOHIDElementCookie buttons[15]; +}; + +void HIDGamepad_init(struct HIDGamepad *gamepad); +void HIDGamepad_destroy(struct HIDGamepad *gamepad); +void HIDGamepad_bind(struct HIDGamepad *gamepad, IOHIDDeviceRef deviceRef, int padIndex); +void HIDGamepad_unbind(struct HIDGamepad *gamepad); diff --git a/armorcore/sources/backends/macos/kinc/backend/HIDManager.c.h b/armorcore/sources/backends/macos/kinc/backend/HIDManager.c.h new file mode 100644 index 000000000..4a112fb30 --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/HIDManager.c.h @@ -0,0 +1,140 @@ +#include +#include + +static int initHIDManager(struct HIDManager *manager); +static bool addMatchingArray(struct HIDManager *manager, CFMutableArrayRef matchingCFArrayRef, CFDictionaryRef matchingCFDictRef); +static CFMutableDictionaryRef createDeviceMatchingDictionary(struct HIDManager *manager, uint32_t inUsagePage, uint32_t inUsage); + +static void deviceConnected(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef); +static void deviceRemoved(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef); + +void HIDManager_init(struct HIDManager *manager) { + manager->managerRef = 0x0; + initHIDManager(manager); +} + +void HIDManager_destroy(struct HIDManager *manager) { + if (manager->managerRef) { + IOHIDManagerUnscheduleFromRunLoop(manager->managerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOHIDManagerClose(manager->managerRef, kIOHIDOptionsTypeNone); + } +} + +static int initHIDManager(struct HIDManager *manager) { + // Initialize the IOHIDManager + manager->managerRef = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); + if (CFGetTypeID(manager->managerRef) == IOHIDManagerGetTypeID()) { + + // Create a matching dictionary for gamepads and joysticks + CFMutableArrayRef matchingCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); + if (matchingCFArrayRef) { + // Create a device matching dictionary for joysticks + CFDictionaryRef matchingCFDictRef = createDeviceMatchingDictionary(manager, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick); + addMatchingArray(manager, matchingCFArrayRef, matchingCFDictRef); + + // Create a device matching dictionary for game pads + matchingCFDictRef = createDeviceMatchingDictionary(manager, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad); + addMatchingArray(manager, matchingCFArrayRef, matchingCFDictRef); + } + else { + kinc_log(KINC_LOG_LEVEL_ERROR, "%s: CFArrayCreateMutable failed.", __PRETTY_FUNCTION__); + return -1; + } + + // Set the HID device matching array + IOHIDManagerSetDeviceMatchingMultiple(manager->managerRef, matchingCFArrayRef); + CFRelease(matchingCFArrayRef); + + // Open manager + IOHIDManagerOpen(manager->managerRef, kIOHIDOptionsTypeNone); + + // Register routines to be called when (matching) devices are connected or disconnected + IOHIDManagerRegisterDeviceMatchingCallback(manager->managerRef, deviceConnected, manager); + IOHIDManagerRegisterDeviceRemovalCallback(manager->managerRef, deviceRemoved, manager); + + IOHIDManagerScheduleWithRunLoop(manager->managerRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + + return 0; + } + return -1; +} + +bool addMatchingArray(struct HIDManager *manager, CFMutableArrayRef matchingCFArrayRef, CFDictionaryRef matchingCFDictRef) { + if (matchingCFDictRef) { + // Add it to the matching array + CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef); + CFRelease(matchingCFDictRef); // and release it + return true; + } + return false; +} + +CFMutableDictionaryRef createDeviceMatchingDictionary(struct HIDManager *manager, uint32_t inUsagePage, uint32_t inUsage) { + // Create a dictionary to add usage page/usages to + CFMutableDictionaryRef result = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (result) { + if (inUsagePage) { + // Add key for device type to refine the matching dictionary. + CFNumberRef pageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsagePage); + if (pageCFNumberRef) { + CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsagePageKey), pageCFNumberRef); + CFRelease(pageCFNumberRef); + + // note: the usage is only valid if the usage page is also defined + if (inUsage) { + CFNumberRef usageCFNumberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &inUsage); + if (usageCFNumberRef) { + CFDictionarySetValue(result, CFSTR(kIOHIDDeviceUsageKey), usageCFNumberRef); + CFRelease(usageCFNumberRef); + } + else { + kinc_log(KINC_LOG_LEVEL_ERROR, "%s: CFNumberCreate(usage) failed.", __PRETTY_FUNCTION__); + } + } + } + else { + kinc_log(KINC_LOG_LEVEL_ERROR, "%s: CFNumberCreate(usage page) failed.", __PRETTY_FUNCTION__); + } + } + } + else { + kinc_log(KINC_LOG_LEVEL_ERROR, "%s: CFDictionaryCreateMutable failed.", __PRETTY_FUNCTION__); + } + return result; +} + +// HID device plugged callback +void deviceConnected(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) { + // Reference manager + struct HIDManager *manager = (struct HIDManager *)inContext; + + // Find an empty slot in the devices list and add the new device there + // TODO: does this need to be made thread safe? + struct HIDManagerDeviceRecord *device = &manager->devices[0]; + for (int i = 0; i < KINC_MAX_HID_DEVICES; ++i, ++device) { + if (!device->connected) { + device->connected = true; + device->device = inIOHIDDeviceRef; + HIDGamepad_bind(&device->pad, inIOHIDDeviceRef, i); + break; + } + } +} + +// HID device unplugged callback +void deviceRemoved(void *inContext, IOReturn inResult, void *inSender, IOHIDDeviceRef inIOHIDDeviceRef) { + // Reference manager + struct HIDManager *manager = (struct HIDManager *)inContext; + + // TODO: does this need to be made thread safe? + struct HIDManagerDeviceRecord *device = &manager->devices[0]; + for (int i = 0; i < KINC_MAX_HID_DEVICES; ++i, ++device) { + // TODO: is comparing IOHIDDeviceRef to match devices safe? Is there a better way? + if (device->connected && device->device == inIOHIDDeviceRef) { + device->connected = false; + device->device = NULL; + HIDGamepad_unbind(&device->pad); + break; + } + } +} diff --git a/armorcore/sources/backends/macos/kinc/backend/HIDManager.h b/armorcore/sources/backends/macos/kinc/backend/HIDManager.h new file mode 100644 index 000000000..0e7420430 --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/HIDManager.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include +#include +#include + +#include "HIDGamepad.h" + +// Maximum number of devices supported +// Corresponds to size of Kore::Gamepad array +static const int KINC_MAX_HID_DEVICES = 12; + +// Slots to hold details on connected devices +struct HIDManagerDeviceRecord { + bool connected; // = false; + IOHIDDeviceRef device; // = NULL; + struct HIDGamepad pad; +}; + +struct HIDManager { + IOHIDManagerRef managerRef; + struct HIDManagerDeviceRecord devices[KINC_MAX_HID_DEVICES]; +}; + +void HIDManager_init(struct HIDManager *manager); +void HIDManager_destroy(struct HIDManager *manager); diff --git a/armorcore/sources/backends/macos/kinc/backend/audio.c.h b/armorcore/sources/backends/macos/kinc/backend/audio.c.h new file mode 100644 index 000000000..59d5a84cc --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/audio.c.h @@ -0,0 +1,158 @@ +#include +#include + +#include +#include +#include + +#include + +static kinc_internal_video_sound_stream_t *video = NULL; + +void macPlayVideoSoundStream(kinc_internal_video_sound_stream_t *v) { + video = v; +} + +void macStopVideoSoundStream(void) { + video = NULL; +} + +static void affirm(OSStatus err) { + if (err != kAudioHardwareNoError) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Error: %i\n", err); + } +} + +static bool initialized; +static bool soundPlaying; +static AudioDeviceID device; +static UInt32 deviceBufferSize; +static UInt32 size; +static AudioStreamBasicDescription deviceFormat; +static AudioObjectPropertyAddress address; + +static AudioDeviceIOProcID theIOProcID = NULL; + +static kinc_a2_buffer_t a2_buffer; + +static uint32_t samples_per_second = 44100; + +uint32_t kinc_a2_samples_per_second(void) { + return samples_per_second; +} + +static void copySample(void *buffer) { + float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location]; + float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location]; + a2_buffer.read_location += 1; + if (a2_buffer.read_location >= a2_buffer.data_size) { + a2_buffer.read_location = 0; + } + ((float *)buffer)[0] = left_value; + ((float *)buffer)[1] = right_value; +} + +static OSStatus appIOProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, + AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *userdata) { + affirm(AudioObjectGetPropertyData(device, &address, 0, NULL, &size, &deviceFormat)); + if (samples_per_second != (int)deviceFormat.mSampleRate) { + samples_per_second = (int)deviceFormat.mSampleRate; + kinc_a2_internal_sample_rate_callback(); + } + int num_frames = deviceBufferSize / deviceFormat.mBytesPerFrame; + kinc_a2_internal_callback(&a2_buffer, num_frames); + float *output = (float *)outOutputData->mBuffers[0].mData; + for (int i = 0; i < num_frames; ++i) { + copySample(output); + output += 2; + } + return kAudioHardwareNoError; +} + +static bool initialized = false; + +void kinc_a2_init(void) { + if (initialized) { + return; + } + + kinc_a2_internal_init(); + initialized = true; + + a2_buffer.read_location = 0; + a2_buffer.write_location = 0; + a2_buffer.data_size = 128 * 1024; + a2_buffer.channel_count = 2; + a2_buffer.channels[0] = (float *)malloc(a2_buffer.data_size * sizeof(float)); + a2_buffer.channels[1] = (float *)malloc(a2_buffer.data_size * sizeof(float)); + + device = kAudioDeviceUnknown; + + initialized = false; + + size = sizeof(AudioDeviceID); + address.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + address.mScope = kAudioObjectPropertyScopeGlobal; + address.mElement = kAudioObjectPropertyElementMaster; + affirm(AudioObjectGetPropertyData(kAudioObjectSystemObject, &address, 0, NULL, &size, &device)); + + size = sizeof(UInt32); + address.mSelector = kAudioDevicePropertyBufferSize; + address.mScope = kAudioDevicePropertyScopeOutput; + affirm(AudioObjectGetPropertyData(device, &address, 0, NULL, &size, &deviceBufferSize)); + + kinc_log(KINC_LOG_LEVEL_INFO, "deviceBufferSize = %i\n", deviceBufferSize); + + size = sizeof(AudioStreamBasicDescription); + address.mSelector = kAudioDevicePropertyStreamFormat; + address.mScope = kAudioDevicePropertyScopeOutput; + + affirm(AudioObjectGetPropertyData(device, &address, 0, NULL, &size, &deviceFormat)); + + if (deviceFormat.mFormatID != kAudioFormatLinearPCM) { + kinc_log(KINC_LOG_LEVEL_ERROR, "mFormatID != kAudioFormatLinearPCM\n"); + return; + } + + if (!(deviceFormat.mFormatFlags & kLinearPCMFormatFlagIsFloat)) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Only works with float format.\n"); + return; + } + + if (samples_per_second != (int)deviceFormat.mSampleRate) { + samples_per_second = (int)deviceFormat.mSampleRate; + kinc_a2_internal_sample_rate_callback(); + } + + initialized = true; + + kinc_log(KINC_LOG_LEVEL_INFO, "mSampleRate = %g\n", deviceFormat.mSampleRate); + kinc_log(KINC_LOG_LEVEL_INFO, "mFormatFlags = %08X\n", (unsigned int)deviceFormat.mFormatFlags); + kinc_log(KINC_LOG_LEVEL_INFO, "mBytesPerPacket = %d\n", (unsigned int)deviceFormat.mBytesPerPacket); + kinc_log(KINC_LOG_LEVEL_INFO, "mFramesPerPacket = %d\n", (unsigned int)deviceFormat.mFramesPerPacket); + kinc_log(KINC_LOG_LEVEL_INFO, "mChannelsPerFrame = %d\n", (unsigned int)deviceFormat.mChannelsPerFrame); + kinc_log(KINC_LOG_LEVEL_INFO, "mBytesPerFrame = %d\n", (unsigned int)deviceFormat.mBytesPerFrame); + kinc_log(KINC_LOG_LEVEL_INFO, "mBitsPerChannel = %d\n", (unsigned int)deviceFormat.mBitsPerChannel); + + if (soundPlaying) + return; + + affirm(AudioDeviceCreateIOProcID(device, appIOProc, NULL, &theIOProcID)); + affirm(AudioDeviceStart(device, theIOProcID)); + + soundPlaying = true; +} + +void kinc_a2_update(void) {} + +void kinc_a2_shutdown(void) { + if (!initialized) + return; + if (!soundPlaying) + return; + + affirm(AudioDeviceStop(device, theIOProcID)); + affirm(AudioDeviceDestroyIOProcID(device, theIOProcID)); + + soundPlaying = false; +} diff --git a/armorcore/sources/backends/macos/kinc/backend/display.m.h b/armorcore/sources/backends/macos/kinc/backend/display.m.h new file mode 100644 index 000000000..344cd181a --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/display.m.h @@ -0,0 +1,112 @@ +#import + +#include +#include + +#define maxDisplays 10 +// Display displays[maxDisplays]; + +/*void initMacDisplays() { + for (int i = 0; i < maxDisplays; ++i) { + displays[i]._data.index = i; + } +}*/ + +int kinc_count_displays(void) { + NSArray *screens = [NSScreen screens]; + return (int)[screens count]; +} + +int kinc_primary_display(void) { + NSArray *screens = [NSScreen screens]; + NSScreen *mainScreen = [NSScreen mainScreen]; + for (int i = 0; i < maxDisplays; ++i) { + if (mainScreen == screens[i]) { + return i; + } + } + return -1; +} + +void kinc_display_init(void) {} + +kinc_display_mode_t kinc_display_available_mode(int display, int mode) { + kinc_display_mode_t dm; + dm.width = 800; + dm.height = 600; + dm.frequency = 60; + dm.bits_per_pixel = 32; + return dm; +} + +int kinc_display_count_available_modes(int display) { + return 1; +} + +bool kinc_display_available(int display) { + return true; +} + +const char *kinc_display_name(int display) { + return "Display"; +} + +kinc_display_mode_t kinc_display_current_mode(int display) { + NSArray *screens = [NSScreen screens]; + NSScreen *screen = screens[display]; + NSRect screenRect = [screen frame]; + kinc_display_mode_t dm; + dm.width = screenRect.size.width; + dm.height = screenRect.size.height; + dm.frequency = 60; + dm.bits_per_pixel = 32; + + NSDictionary *description = [screen deviceDescription]; + NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; + NSNumber *screenNumber = [description objectForKey:@"NSScreenNumber"]; + CGSize displayPhysicalSize = CGDisplayScreenSize([screenNumber unsignedIntValue]); // in millimeters + double ppi = displayPixelSize.width / (displayPhysicalSize.width * 0.039370); // Convert MM to INCH + dm.pixels_per_inch = round(ppi); + + return dm; +} + +//** +/* +int Display::x() { + NSArray* screens = [NSScreen screens]; + NSScreen* screen = screens[_data.index]; + NSRect rect = [screen frame]; + return rect.origin.x; +} + +int Display::y() { + NSArray* screens = [NSScreen screens]; + NSScreen* screen = screens[_data.index]; + NSRect rect = [screen frame]; + return rect.origin.y; +} + +int Display::width() { + NSArray* screenArray = [NSScreen screens]; + NSScreen* screen = [screenArray objectAtIndex:_data.index]; + NSRect screenRect = [screen visibleFrame]; + return screenRect.size.width; +} + +int Display::height() { + NSArray* screenArray = [NSScreen screens]; + // unsigned screenCount = [screenArray count]; + NSScreen* screen = [screenArray objectAtIndex:_data.index]; + NSRect screenRect = [screen visibleFrame]; + return screenRect.size.height; +} + +int Display::frequency() { + return 60; +} + +int Display::pixelsPerInch() { + return 96; +} +*/ diff --git a/armorcore/sources/backends/macos/kinc/backend/displaydata.h b/armorcore/sources/backends/macos/kinc/backend/displaydata.h new file mode 100644 index 000000000..55aba2e36 --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/displaydata.h @@ -0,0 +1,6 @@ +#pragma once + +struct DisplayData { + int index; + DisplayData(); +}; diff --git a/armorcore/sources/backends/macos/kinc/backend/mac.plist b/armorcore/sources/backends/macos/kinc/backend/mac.plist new file mode 100644 index 000000000..56c0fe3d3 --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/mac.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2018 the Kore Development Team. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/armorcore/sources/backends/macos/kinc/backend/macosunit.m b/armorcore/sources/backends/macos/kinc/backend/macosunit.m new file mode 100644 index 000000000..cf2dddd91 --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/macosunit.m @@ -0,0 +1,24 @@ +#include + +struct WindowData { + id handle; + id view; + bool fullscreen; + void (*resizeCallback)(int width, int height, void *data); + void *resizeCallbackData; + bool (*closeCallback)(void *data); + void *closeCallbackData; +}; + +static struct WindowData windows[10] = {}; +static int windowCounter = 0; + +#include "BasicOpenGLView.m.h" +#include "HIDGamepad.c.h" +#include "HIDManager.c.h" +#include "audio.c.h" +#include "display.m.h" +#include "mouse.m.h" +#include "system.c.h" +#include "system.m.h" +#include "window.c.h" diff --git a/armorcore/sources/backends/macos/kinc/backend/mouse.m.h b/armorcore/sources/backends/macos/kinc/backend/mouse.m.h new file mode 100644 index 000000000..4c13b876f --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/mouse.m.h @@ -0,0 +1,49 @@ +#import + +#include +#include +#include + +void kinc_internal_mouse_lock(int window) { + kinc_mouse_hide(); +} + +void kinc_internal_mouse_unlock(void) { + kinc_mouse_show(); +} + +bool kinc_mouse_can_lock(void) { + return true; +} + +void kinc_mouse_show(void) { + CGDisplayShowCursor(kCGDirectMainDisplay); +} + +void kinc_mouse_hide(void) { + CGDisplayHideCursor(kCGDirectMainDisplay); +} + +void kinc_mouse_set_position(int windowId, int x, int y) { + + NSWindow *window = kinc_get_mac_window_handle(windowId); + float scale = [window backingScaleFactor]; + NSRect rect = [[NSScreen mainScreen] frame]; + + CGPoint point; + point.x = window.frame.origin.x + (x / scale); + point.y = rect.size.height - (window.frame.origin.y + (y / scale)); + + CGDisplayMoveCursorToPoint(windowId, point); + CGAssociateMouseAndMouseCursorPosition(true); +} + +void kinc_mouse_get_position(int windowId, int *x, int *y) { + + NSWindow *window = kinc_get_mac_window_handle(windowId); + NSPoint point = [window mouseLocationOutsideOfEventStream]; + *x = (int)point.x; + *y = (int)point.y; +} + +void kinc_mouse_set_cursor(int cursor_index) {} diff --git a/armorcore/sources/backends/macos/kinc/backend/system.c.h b/armorcore/sources/backends/macos/kinc/backend/system.c.h new file mode 100644 index 000000000..68aea57c7 --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/system.c.h @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include + +static int mouseX, mouseY; +static bool keyboardShown = false; + +void Kinc_Mouse_GetPosition(int window, int *x, int *y) { + *x = mouseX; + *y = mouseY; +} + +void kinc_keyboard_show(void) { + keyboardShown = true; +} + +void kinc_keyboard_hide(void) { + keyboardShown = false; +} + +bool kinc_keyboard_active(void) { + return keyboardShown; +} + +void kinc_vibrate(int ms) {} + +const char *kinc_system_id(void) { + return "macOS"; +} + +static const char *videoFormats[] = {"ogv", NULL}; + +const char **kinc_video_formats(void) { + return videoFormats; +} + +void kinc_set_keep_screen_on(bool on) {} + +#include + +double kinc_frequency(void) { + mach_timebase_info_data_t info; + mach_timebase_info(&info); + return (double)info.denom / (double)info.numer / 1e-9; +} + +kinc_ticks_t kinc_timestamp(void) { + return mach_absolute_time(); +} + +void kinc_login(void) {} + +void kinc_unlock_achievement(int id) {} + +bool kinc_gamepad_connected(int num) { + return true; +} + +void kinc_gamepad_rumble(int gamepad, float left, float right) {} diff --git a/armorcore/sources/backends/macos/kinc/backend/system.m.h b/armorcore/sources/backends/macos/kinc/backend/system.m.h new file mode 100644 index 000000000..443c90a50 --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/system.m.h @@ -0,0 +1,323 @@ +#import "BasicOpenGLView.h" + +#import + +#include +#include +#include +#include +#include +#include + +#include "windowdata.h" + +#include + +bool withAutoreleasepool(bool (*f)(void)) { + @autoreleasepool { + return f(); + } +} + +extern const char *macgetresourcepath(void); + +const char *macgetresourcepath(void) { + return [[[NSBundle mainBundle] resourcePath] cStringUsingEncoding:NSUTF8StringEncoding]; +} + +@interface KincApplication : NSApplication { +} +- (void)terminate:(id)sender; +@end + +@interface KincAppDelegate : NSObject { +} +- (void)windowWillClose:(NSNotification *)notification; +- (void)windowDidResize:(NSNotification *)notification; +- (void)windowWillMiniaturize:(NSNotification *)notification; +- (void)windowDidDeminiaturize:(NSNotification *)notification; +- (void)windowDidResignMain:(NSNotification *)notification; +- (void)windowDidBecomeMain:(NSNotification *)notification; +@end + +static NSApplication *myapp; +static NSWindow *window; +static BasicOpenGLView *view; +static KincAppDelegate *delegate; +static struct HIDManager *hidManager; + +/*struct KoreWindow : public KoreWindowBase { + NSWindow* handle; + BasicOpenGLView* view; + + KoreWindow(NSWindow* handle, BasicOpenGLView* view, int x, int y, int width, int height) + : KoreWindowBase(x, y, width, height), handle(handle), view(view) { + ::view = view; + } +};*/ + +#ifdef KINC_METAL +CAMetalLayer *getMetalLayer(void) { + return [view metalLayer]; +} + +id getMetalDevice(void) { + return [view metalDevice]; +} + +id getMetalLibrary(void) { + return [view metalLibrary]; +} + +id getMetalQueue(void) { + return [view metalQueue]; +} +#endif + +bool kinc_internal_handle_messages(void) { + NSEvent *event = [myapp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; // distantPast: non-blocking + if (event != nil) { + [myapp sendEvent:event]; + [myapp updateWindows]; + } + + // Sleep for a frame to limit the calls when the window is not visible. + if (!window.visible) { + [NSThread sleepForTimeInterval:1.0 / 60]; + } + return true; +} + +void swapBuffersMac(int windowId) { +#ifndef KINC_METAL + [windows[windowId].view switchBuffers]; +#endif +} + +static int createWindow(kinc_window_options_t *options) { + int width = options->width / [[NSScreen mainScreen] backingScaleFactor]; + int height = options->height / [[NSScreen mainScreen] backingScaleFactor]; + int styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable; + if ((options->window_features & KINC_WINDOW_FEATURE_RESIZEABLE) || (options->window_features & KINC_WINDOW_FEATURE_MAXIMIZABLE)) { + styleMask |= NSWindowStyleMaskResizable; + } + if (options->window_features & KINC_WINDOW_FEATURE_MINIMIZABLE) { + styleMask |= NSWindowStyleMaskMiniaturizable; + } + + view = [[BasicOpenGLView alloc] initWithFrame:NSMakeRect(0, 0, width, height)]; + [view registerForDraggedTypes:[NSArray arrayWithObjects:NSURLPboardType, nil]]; + window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, width, height) styleMask:styleMask backing:NSBackingStoreBuffered defer:TRUE]; + delegate = [KincAppDelegate alloc]; + [window setDelegate:delegate]; + [window setTitle:[NSString stringWithCString:options->title encoding:NSUTF8StringEncoding]]; + [window setAcceptsMouseMovedEvents:YES]; + [[window contentView] addSubview:view]; + [window center]; + + windows[windowCounter].handle = window; + windows[windowCounter].view = view; + + [window makeKeyAndOrderFront:nil]; + + if (options->mode == KINC_WINDOW_MODE_FULLSCREEN || options->mode == KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { + [window toggleFullScreen:nil]; + windows[windowCounter].fullscreen = true; + } + + return windowCounter++; +} + +int kinc_count_windows(void) { + return windowCounter; +} + +void kinc_window_change_window_mode(int window_index, kinc_window_mode_t mode) { + switch (mode) { + case KINC_WINDOW_MODE_WINDOW: + if (windows[window_index].fullscreen) { + [window toggleFullScreen:nil]; + windows[window_index].fullscreen = false; + } + break; + case KINC_WINDOW_MODE_FULLSCREEN: + case KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN: + if (!windows[window_index].fullscreen) { + [window toggleFullScreen:nil]; + windows[window_index].fullscreen = true; + } + break; + } +} + +void kinc_window_set_close_callback(int window, bool (*callback)(void *), void *data) { + windows[window].closeCallback = callback; + windows[window].closeCallbackData = data; +} + +static void addMenubar(void) { + NSString *appName = [[NSProcessInfo processInfo] processName]; + + NSMenu *appMenu = [NSMenu new]; + NSString *quitTitle = [@"Quit " stringByAppendingString:appName]; + NSMenuItem *quitMenuItem = [[NSMenuItem alloc] initWithTitle:quitTitle action:@selector(terminate:) keyEquivalent:@"q"]; + [appMenu addItem:quitMenuItem]; + + NSMenuItem *appMenuItem = [NSMenuItem new]; + [appMenuItem setSubmenu:appMenu]; + + NSMenu *menubar = [NSMenu new]; + [menubar addItem:appMenuItem]; + [NSApp setMainMenu:menubar]; +} + +int kinc_init(const char *name, int width, int height, kinc_window_options_t *win, kinc_framebuffer_options_t *frame) { + @autoreleasepool { + myapp = [KincApplication sharedApplication]; + [myapp finishLaunching]; + [[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)]; + NSApp.activationPolicy = NSApplicationActivationPolicyRegular; + + hidManager = (struct HIDManager *)malloc(sizeof(struct HIDManager)); + HIDManager_init(hidManager); + addMenubar(); + } + + // System::_init(name, width, height, &win, &frame); + kinc_window_options_t defaultWindowOptions; + if (win == NULL) { + kinc_window_options_set_defaults(&defaultWindowOptions); + win = &defaultWindowOptions; + } + + kinc_framebuffer_options_t defaultFramebufferOptions; + if (frame == NULL) { + kinc_framebuffer_options_set_defaults(&defaultFramebufferOptions); + frame = &defaultFramebufferOptions; + } + + win->width = width; + win->height = height; + if (win->title == NULL) { + win->title = name; + } + + int windowId = createWindow(win); + kinc_g4_internal_init(); + kinc_g4_internal_init_window(windowId, frame->depth_bits, frame->stencil_bits, true); + + return 0; +} + +int kinc_window_width(int window_index) { + NSWindow *window = windows[window_index].handle; + float scale = [window backingScaleFactor]; + return [[window contentView] frame].size.width * scale; +} + +int kinc_window_height(int window_index) { + NSWindow *window = windows[window_index].handle; + float scale = [window backingScaleFactor]; + return [[window contentView] frame].size.height * scale; +} + +NSWindow *kinc_get_mac_window_handle(int window_index) { + return windows[window_index].handle; +} + +void kinc_load_url(const char *url) { + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithUTF8String:url]]]; +} + +static char language[3]; + +const char *kinc_language(void) { + NSString *nsstr = [[NSLocale preferredLanguages] objectAtIndex:0]; + const char *lang = [nsstr UTF8String]; + language[0] = lang[0]; + language[1] = lang[1]; + language[2] = 0; + return language; +} + +void kinc_internal_shutdown(void) {} + +static const char *getSavePath(void) { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); + NSString *resolvedPath = [paths objectAtIndex:0]; + NSString *appName = [NSString stringWithUTF8String:kinc_application_name()]; + resolvedPath = [resolvedPath stringByAppendingPathComponent:appName]; + + NSFileManager *fileMgr = [[NSFileManager alloc] init]; + + NSError *error; + [fileMgr createDirectoryAtPath:resolvedPath withIntermediateDirectories:YES attributes:nil error:&error]; + + resolvedPath = [resolvedPath stringByAppendingString:@"/"]; + return [resolvedPath cStringUsingEncoding:NSUTF8StringEncoding]; +} + +const char *kinc_internal_save_path(void) { + return getSavePath(); +} + +#ifndef KINC_NO_MAIN +int main(int argc, char **argv) { + return kickstart(argc, argv); +} +#endif + +@implementation KincApplication + +- (void)terminate:(id)sender { + kinc_stop(); +} + +@end + +@implementation KincAppDelegate +- (BOOL)windowShouldClose:(NSWindow *)sender { + if (windows[0].closeCallback != NULL) { + if (windows[0].closeCallback(windows[0].closeCallbackData)) { + return YES; + } + else { + return NO; + } + } + return YES; +} + +- (void)windowWillClose:(NSNotification *)notification { + kinc_stop(); +} + +- (void)windowDidResize:(NSNotification *)notification { + NSWindow *window = [notification object]; + NSSize size = [[window contentView] frame].size; + [view resize:size]; + if (windows[0].resizeCallback != NULL) { + windows[0].resizeCallback(size.width, size.height, windows[0].resizeCallbackData); + } +} + +- (void)windowWillMiniaturize:(NSNotification *)notification { + kinc_internal_background_callback(); +} + +- (void)windowDidDeminiaturize:(NSNotification *)notification { + kinc_internal_foreground_callback(); +} + +- (void)windowDidResignMain:(NSNotification *)notification { + kinc_internal_pause_callback(); +} + +- (void)windowDidBecomeMain:(NSNotification *)notification { + kinc_internal_resume_callback(); +} + +@end diff --git a/armorcore/sources/backends/macos/kinc/backend/window.c.h b/armorcore/sources/backends/macos/kinc/backend/window.c.h new file mode 100644 index 000000000..df14204c9 --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/window.c.h @@ -0,0 +1,58 @@ +#include +#include +#include + +int kinc_window_x(int window) { + return 0; +} + +int kinc_window_y(int window) { + return 0; +} + +void kinc_window_resize(int window, int width, int height) {} + +void kinc_window_move(int window, int x, int y) {} + +void kinc_internal_change_framebuffer(int window, struct kinc_framebuffer_options *frame); + +void kinc_window_change_framebuffer(int window, struct kinc_framebuffer_options *frame) { + kinc_internal_change_framebuffer(0, frame); +} + +#ifdef KINC_METAL +void kinc_internal_change_framebuffer(int window, struct kinc_framebuffer_options *frame) {} +#endif + +void kinc_window_change_features(int window, int features) {} + +void kinc_window_change_mode(int window, kinc_window_mode_t mode) {} + +void kinc_window_destroy(int window) {} + +void kinc_window_show(int window) {} + +void kinc_window_hide(int window) {} + +void kinc_window_set_title(int window, const char *title) {} + +int kinc_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) { + windowCounter += 1; + return 0; +} + +void kinc_window_set_resize_callback(int window, void (*callback)(int x, int y, void *data), void *data) { + assert(window < windowCounter); + windows[window].resizeCallback = callback; + windows[window].resizeCallbackData = data; +} + +void kinc_window_set_ppi_changed_callback(int window, void (*callback)(int ppi, void *data), void *data) {} + +kinc_window_mode_t kinc_window_get_mode(int window) { + return KINC_WINDOW_MODE_WINDOW; +} + +int kinc_window_display(int window) { + return 0; +} diff --git a/armorcore/sources/backends/macos/kinc/backend/windowdata.h b/armorcore/sources/backends/macos/kinc/backend/windowdata.h new file mode 100644 index 000000000..50c888423 --- /dev/null +++ b/armorcore/sources/backends/macos/kinc/backend/windowdata.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +NSWindow *kinc_get_mac_window_handle(int window_index); diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/Metal.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/Metal.h new file mode 100644 index 000000000..1f1ab8ffb --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/Metal.h @@ -0,0 +1,5 @@ +#pragma once + +#include +#include +#include diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/Metal.m.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/Metal.m.h new file mode 100644 index 000000000..3e64e890a --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/Metal.m.h @@ -0,0 +1,253 @@ +#include "Metal.h" + +#include +#include +#include + +#include +#include + +#import +#import + +id getMetalLayer(void); +id getMetalDevice(void); +id getMetalQueue(void); + +int renderTargetWidth; +int renderTargetHeight; +int newRenderTargetWidth; +int newRenderTargetHeight; + +id drawable; +id depthTexture; +int depthBits; +int stencilBits; + +static kinc_g5_render_target_t fallback_render_target; + +id getMetalEncoder(void) { + return render_command_encoder; +} + +void kinc_g5_internal_destroy_window(int window) {} + +void kinc_g5_internal_destroy(void) {} + +extern void kinc_g4_on_g5_internal_resize(int, int, int); + +void kinc_internal_resize(int window, int width, int height) { + kinc_g4_on_g5_internal_resize(window, width, height); +} + +void kinc_g5_internal_init(void) {} + +void kinc_g5_internal_init_window(int window, int depthBufferBits, int stencilBufferBits, bool vsync) { + depthBits = depthBufferBits; + stencilBits = stencilBufferBits; + kinc_g5_render_target_init(&fallback_render_target, 32, 32, KINC_G5_RENDER_TARGET_FORMAT_32BIT, 0, 0); +} + +void kinc_g5_flush(void) {} + +void kinc_g5_draw_indexed_vertices_instanced(int instanceCount) {} + +void kinc_g5_draw_indexed_vertices_instanced_from_to(int instanceCount, int start, int count) {} + +bool kinc_internal_metal_has_depth = false; + +bool kinc_internal_current_render_target_has_depth(void) { + return kinc_internal_metal_has_depth; +} + +static void start_render_pass(void) { + id texture = drawable.texture; + MTLRenderPassDescriptor *renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; + renderPassDescriptor.colorAttachments[0].texture = texture; + renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; + renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; + renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); + renderPassDescriptor.depthAttachment.clearDepth = 1; + renderPassDescriptor.depthAttachment.loadAction = MTLLoadActionClear; + renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionStore; + renderPassDescriptor.depthAttachment.texture = depthTexture; + renderPassDescriptor.stencilAttachment.clearStencil = 0; + renderPassDescriptor.stencilAttachment.loadAction = MTLLoadActionDontCare; + renderPassDescriptor.stencilAttachment.storeAction = MTLStoreActionDontCare; + renderPassDescriptor.stencilAttachment.texture = depthTexture; + + render_command_encoder = [command_buffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; +} + +static void end_render_pass(void) { + [render_command_encoder endEncoding]; + render_command_encoder = nil; +} + +void kinc_g5_begin(kinc_g5_render_target_t *renderTarget, int window) { + CAMetalLayer *metalLayer = getMetalLayer(); + drawable = [metalLayer nextDrawable]; + + if (depthBits > 0 && (depthTexture == nil || depthTexture.width != drawable.texture.width || depthTexture.height != drawable.texture.height)) { + MTLTextureDescriptor *descriptor = [MTLTextureDescriptor new]; + descriptor.textureType = MTLTextureType2D; + descriptor.width = drawable.texture.width; + descriptor.height = drawable.texture.height; + descriptor.depth = 1; + descriptor.pixelFormat = MTLPixelFormatDepth32Float_Stencil8; + descriptor.arrayLength = 1; + descriptor.mipmapLevelCount = 1; + descriptor.resourceOptions = MTLResourceStorageModePrivate; + descriptor.usage = MTLTextureUsageRenderTarget; + id device = getMetalDevice(); + depthTexture = [device newTextureWithDescriptor:descriptor]; + kinc_internal_metal_has_depth = true; + } + else { + kinc_internal_metal_has_depth = false; + } + + id texture = drawable.texture; + MTLRenderPassDescriptor *renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; + renderPassDescriptor.colorAttachments[0].texture = texture; + renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear; + renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; + renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); + renderPassDescriptor.depthAttachment.clearDepth = 1; + renderPassDescriptor.depthAttachment.loadAction = MTLLoadActionClear; + renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionStore; + renderPassDescriptor.depthAttachment.texture = depthTexture; + renderPassDescriptor.stencilAttachment.clearStencil = 0; + renderPassDescriptor.stencilAttachment.loadAction = MTLLoadActionDontCare; + renderPassDescriptor.stencilAttachment.storeAction = MTLStoreActionDontCare; + renderPassDescriptor.stencilAttachment.texture = depthTexture; + + if (command_buffer != nil && render_command_encoder != nil) { + [render_command_encoder endEncoding]; + [command_buffer commit]; + } + + id commandQueue = getMetalQueue(); + command_buffer = [commandQueue commandBuffer]; + render_command_encoder = [command_buffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; +} + +void kinc_g5_end(int window) {} + +bool kinc_g5_swap_buffers(void) { + if (command_buffer != nil && render_command_encoder != nil) { + [render_command_encoder endEncoding]; + [command_buffer presentDrawable:drawable]; + [command_buffer commit]; + } + drawable = nil; + command_buffer = nil; + render_command_encoder = nil; + + return true; +} + +bool kinc_window_vsynced(int window) { + return true; +} + +void kinc_g5_internal_new_render_pass(kinc_g5_render_target_t **renderTargets, int count, bool wait, unsigned clear_flags, unsigned color, float depth, + int stencil) { + if (command_buffer != nil && render_command_encoder != nil) { + [render_command_encoder endEncoding]; + [command_buffer commit]; + if (wait) { + [command_buffer waitUntilCompleted]; + } + } + + MTLRenderPassDescriptor *renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; + for (int i = 0; i < count; ++i) { + if (renderTargets == NULL) { + if (drawable == nil) { + renderPassDescriptor.colorAttachments[i].texture = (__bridge id)fallback_render_target.impl._tex; + renderPassDescriptor.depthAttachment.texture = nil; + renderPassDescriptor.stencilAttachment.texture = nil; + kinc_internal_metal_has_depth = false; + } + else { + renderPassDescriptor.colorAttachments[i].texture = drawable.texture; + renderPassDescriptor.depthAttachment.texture = depthTexture; + renderPassDescriptor.stencilAttachment.texture = depthTexture; + kinc_internal_metal_has_depth = depthTexture != nil; + } + } + else { + renderPassDescriptor.colorAttachments[i].texture = (__bridge id)renderTargets[i]->impl._tex; + renderPassDescriptor.depthAttachment.texture = (__bridge id)renderTargets[0]->impl._depthTex; + renderPassDescriptor.stencilAttachment.texture = (__bridge id)renderTargets[0]->impl._depthTex; + kinc_internal_metal_has_depth = renderTargets[0]->impl._depthTex != nil; + } + if (clear_flags & KINC_G5_CLEAR_COLOR) { + float red, green, blue, alpha; + kinc_color_components(color, &red, &green, &blue, &alpha); + renderPassDescriptor.colorAttachments[i].loadAction = MTLLoadActionClear; + renderPassDescriptor.colorAttachments[i].storeAction = MTLStoreActionStore; + renderPassDescriptor.colorAttachments[i].clearColor = MTLClearColorMake(red, green, blue, alpha); + } + else { + renderPassDescriptor.colorAttachments[i].loadAction = MTLLoadActionLoad; + renderPassDescriptor.colorAttachments[i].storeAction = MTLStoreActionStore; + renderPassDescriptor.colorAttachments[i].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0); + } + } + + if (clear_flags & KINC_G5_CLEAR_DEPTH) { + renderPassDescriptor.depthAttachment.clearDepth = depth; + renderPassDescriptor.depthAttachment.loadAction = MTLLoadActionClear; + renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionStore; + } + else { + renderPassDescriptor.depthAttachment.clearDepth = 1; + renderPassDescriptor.depthAttachment.loadAction = MTLLoadActionLoad; + renderPassDescriptor.depthAttachment.storeAction = MTLStoreActionStore; + } + + if (clear_flags & KINC_G5_CLEAR_STENCIL) { + renderPassDescriptor.stencilAttachment.clearStencil = stencil; + renderPassDescriptor.stencilAttachment.loadAction = MTLLoadActionClear; + renderPassDescriptor.stencilAttachment.storeAction = MTLStoreActionStore; + } + else { + renderPassDescriptor.stencilAttachment.clearStencil = 0; + renderPassDescriptor.stencilAttachment.loadAction = MTLLoadActionDontCare; + renderPassDescriptor.stencilAttachment.storeAction = MTLStoreActionDontCare; + } + + id commandQueue = getMetalQueue(); + command_buffer = [commandQueue commandBuffer]; + render_command_encoder = [command_buffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; +} + +bool kinc_g5_supports_raytracing(void) { + return false; +} + +bool kinc_g5_supports_instanced_rendering(void) { + return true; +} + +bool kinc_g5_supports_compute_shaders(void) { + return true; +} + +bool kinc_g5_supports_blend_constants(void) { + return true; +} + +bool kinc_g5_supports_non_pow2_textures(void) { + return true; +} + +bool kinc_g5_render_targets_inverted_y(void) { + return false; +} + +int kinc_g5_max_bound_textures(void) { + return 16; +} diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/commandlist.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/commandlist.h new file mode 100644 index 000000000..8e89642ca --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/commandlist.h @@ -0,0 +1,7 @@ +#pragma once + +struct kinc_g5_index_buffer; + +typedef struct { + struct kinc_g5_index_buffer *current_index_buffer; +} CommandList5Impl; diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/commandlist.m.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/commandlist.m.h new file mode 100644 index 000000000..750c42f2a --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/commandlist.m.h @@ -0,0 +1,379 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#import +#import + +#include +#include +#include + +id getMetalDevice(void); +id getMetalQueue(void); +id getMetalEncoder(void); + +void kinc_g5_internal_new_render_pass(kinc_g5_render_target_t **renderTargets, int count, bool wait, unsigned clear_flags, unsigned color, float depth, + int stencil); +void kinc_g5_internal_pipeline_set(kinc_g5_pipeline_t *pipeline); + +void kinc_g5_command_list_init(kinc_g5_command_list_t *list) { + list->impl.current_index_buffer = NULL; +} + +void kinc_g5_command_list_destroy(kinc_g5_command_list_t *list) {} + +static kinc_g5_render_target_t *lastRenderTargets[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +static kinc_g5_pipeline_t *lastPipeline = NULL; + +static int formatSize(MTLPixelFormat format) { + switch (format) { + case MTLPixelFormatRGBA32Float: + return 16; + case MTLPixelFormatRGBA16Float: + return 8; + case MTLPixelFormatR16Float: + return 2; + case MTLPixelFormatR8Unorm: + return 1; + default: + return 4; + } +} + +void kinc_g5_command_list_begin(kinc_g5_command_list_t *list) { + list->impl.current_index_buffer = NULL; + lastRenderTargets[0] = NULL; +} + +void kinc_g5_command_list_end(kinc_g5_command_list_t *list) {} + +void kinc_g5_command_list_clear(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget, unsigned flags, unsigned color, float depth, + int stencil) { + if (renderTarget->framebuffer_index >= 0) { + kinc_g5_internal_new_render_pass(NULL, 1, false, flags, color, depth, stencil); + } + else { + kinc_g5_internal_new_render_pass(&renderTarget, 1, false, flags, color, depth, stencil); + } +} + +void kinc_g5_command_list_render_target_to_framebuffer_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) {} + +void kinc_g5_command_list_framebuffer_to_render_target_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) {} + +void kinc_g5_command_list_draw_indexed_vertices(kinc_g5_command_list_t *list) { + kinc_g5_command_list_draw_indexed_vertices_from_to(list, 0, kinc_g5_index_buffer_count(list->impl.current_index_buffer)); +} + +void kinc_g5_command_list_draw_indexed_vertices_from_to(kinc_g5_command_list_t *list, int start, int count) { + id indexBuffer = (__bridge id)list->impl.current_index_buffer->impl.metal_buffer; + id encoder = getMetalEncoder(); + [encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle + indexCount:count + indexType:(list->impl.current_index_buffer->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32) + indexBuffer:indexBuffer + indexBufferOffset:start * 4]; +} + +void kinc_g5_command_list_draw_indexed_vertices_from_to_from(kinc_g5_command_list_t *list, int start, int count, int vertex_offset) { + id indexBuffer = (__bridge id)list->impl.current_index_buffer->impl.metal_buffer; + id encoder = getMetalEncoder(); + [encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle + indexCount:count + indexType:(list->impl.current_index_buffer->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32) + indexBuffer:indexBuffer + indexBufferOffset:start * 4 + instanceCount:1 + baseVertex:vertex_offset + baseInstance:0]; +} + +void kinc_g5_command_list_draw_indexed_vertices_instanced(kinc_g5_command_list_t *list, int instanceCount) { + kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(list, instanceCount, 0, kinc_g5_index_buffer_count(list->impl.current_index_buffer)); +} + +void kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(kinc_g5_command_list_t *list, int instanceCount, int start, int count) { + id indexBuffer = (__bridge id)list->impl.current_index_buffer->impl.metal_buffer; + id encoder = getMetalEncoder(); + [encoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle + indexCount:count + indexType:(list->impl.current_index_buffer->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32) + indexBuffer:indexBuffer + indexBufferOffset:start * 4 + instanceCount:instanceCount + baseVertex:0 + baseInstance:0]; +} +void kinc_g5_command_list_viewport(kinc_g5_command_list_t *list, int x, int y, int width, int height) { + id encoder = getMetalEncoder(); + MTLViewport viewport; + viewport.originX = x; + viewport.originY = y; + viewport.width = width; + viewport.height = height; + [encoder setViewport:viewport]; +} + +void kinc_g5_command_list_scissor(kinc_g5_command_list_t *list, int x, int y, int width, int height) { + id encoder = getMetalEncoder(); + MTLScissorRect scissor; + scissor.x = x; + scissor.y = y; + int target_w = -1; + int target_h = -1; + if (lastRenderTargets[0] != NULL) { + target_w = lastRenderTargets[0]->texWidth; + target_h = lastRenderTargets[0]->texHeight; + } + else { + target_w = kinc_window_width(0); + target_h = kinc_window_height(0); + } + scissor.width = (x + width <= target_w) ? width : target_w - x; + scissor.height = (y + height <= target_h) ? height : target_h - y; + [encoder setScissorRect:scissor]; +} + +void kinc_g5_command_list_disable_scissor(kinc_g5_command_list_t *list) { + id encoder = getMetalEncoder(); + MTLScissorRect scissor; + scissor.x = 0; + scissor.y = 0; + if (lastRenderTargets[0] != NULL) { + scissor.width = lastRenderTargets[0]->texWidth; + scissor.height = lastRenderTargets[0]->texHeight; + } + else { + scissor.width = kinc_window_width(0); + scissor.height = kinc_window_height(0); + } + [encoder setScissorRect:scissor]; +} + +void kinc_g5_command_list_set_pipeline(kinc_g5_command_list_t *list, struct kinc_g5_pipeline *pipeline) { + kinc_g5_internal_pipeline_set(pipeline); + lastPipeline = pipeline; +} + +void kinc_g5_command_list_set_blend_constant(kinc_g5_command_list_t *list, float r, float g, float b, float a) { + id encoder = getMetalEncoder(); + [encoder setBlendColorRed:r green:g blue:b alpha:a]; +} + +void kinc_g5_command_list_set_vertex_buffers(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer **buffers, int *offsets, int count) { + kinc_g5_internal_vertex_buffer_set(buffers[0], offsets[0]); +} + +void kinc_g5_command_list_set_index_buffer(kinc_g5_command_list_t *list, struct kinc_g5_index_buffer *buffer) { + list->impl.current_index_buffer = buffer; +} + +extern bool kinc_internal_metal_has_depth; + +void kinc_g5_command_list_set_render_targets(kinc_g5_command_list_t *list, struct kinc_g5_render_target **targets, int count) { + if (targets[0]->framebuffer_index >= 0) { + for (int i = 0; i < 8; ++i) + lastRenderTargets[i] = NULL; + kinc_g5_internal_new_render_pass(NULL, 1, false, 0, 0, 0.0f, 0); + } + else { + for (int i = 0; i < count; ++i) + lastRenderTargets[i] = targets[i]; + for (int i = count; i < 8; ++i) + lastRenderTargets[i] = NULL; + kinc_g5_internal_new_render_pass(targets, count, false, 0, 0, 0.0f, 0); + } +} + +void kinc_g5_command_list_upload_index_buffer(kinc_g5_command_list_t *list, struct kinc_g5_index_buffer *buffer) {} + +void kinc_g5_command_list_upload_vertex_buffer(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer *buffer) {} + +void kinc_g5_command_list_upload_texture(kinc_g5_command_list_t *list, struct kinc_g5_texture *texture) {} + +void kinc_g5_command_list_get_render_target_pixels(kinc_g5_command_list_t *list, kinc_g5_render_target_t *render_target, uint8_t *data) { + // Create readback buffer + if (render_target->impl._texReadback == NULL) { + id device = getMetalDevice(); + MTLTextureDescriptor *descriptor = [MTLTextureDescriptor new]; + descriptor.textureType = MTLTextureType2D; + descriptor.width = render_target->texWidth; + descriptor.height = render_target->texHeight; + descriptor.depth = 1; + descriptor.pixelFormat = [(__bridge id)render_target->impl._tex pixelFormat]; + descriptor.arrayLength = 1; + descriptor.mipmapLevelCount = 1; + descriptor.usage = MTLTextureUsageUnknown; +#ifdef KINC_APPLE_SOC + descriptor.resourceOptions = MTLResourceStorageModeShared; +#else + descriptor.resourceOptions = MTLResourceStorageModeManaged; +#endif + render_target->impl._texReadback = (__bridge_retained void *)[device newTextureWithDescriptor:descriptor]; + } + + // Copy render target to readback buffer + id commandQueue = getMetalQueue(); + id commandBuffer = [commandQueue commandBuffer]; + id commandEncoder = [commandBuffer blitCommandEncoder]; + [commandEncoder copyFromTexture:(__bridge id)render_target->impl._tex + sourceSlice:0 + sourceLevel:0 + sourceOrigin:MTLOriginMake(0, 0, 0) + sourceSize:MTLSizeMake(render_target->texWidth, render_target->texHeight, 1) + toTexture:(__bridge id)render_target->impl._texReadback + destinationSlice:0 + destinationLevel:0 + destinationOrigin:MTLOriginMake(0, 0, 0)]; +#ifndef KINC_APPLE_SOC + [commandEncoder synchronizeResource:(__bridge id)render_target->impl._texReadback]; +#endif + [commandEncoder endEncoding]; + [commandBuffer commit]; + [commandBuffer waitUntilCompleted]; + + // Read buffer + id tex = (__bridge id)render_target->impl._texReadback; + int formatByteSize = formatSize([(__bridge id)render_target->impl._tex pixelFormat]); + MTLRegion region = MTLRegionMake2D(0, 0, render_target->texWidth, render_target->texHeight); + [tex getBytes:data bytesPerRow:formatByteSize * render_target->texWidth fromRegion:region mipmapLevel:0]; +} + +void kinc_g5_command_list_execute(kinc_g5_command_list_t *list) { + if (lastRenderTargets[0] == NULL) { + kinc_g5_internal_new_render_pass(NULL, 1, false, 0, 0, 0.0f, 0); + } + else { + int count = 1; + while (lastRenderTargets[count] != NULL) + count++; + kinc_g5_internal_new_render_pass(lastRenderTargets, count, false, 0, 0, 0.0f, 0); + } + if (lastPipeline != NULL) + kinc_g5_internal_pipeline_set(lastPipeline); +} + +void kinc_g5_command_list_wait_for_execution_to_finish(kinc_g5_command_list_t *list) { + id commandQueue = getMetalQueue(); + id commandBuffer = [commandQueue commandBuffer]; + [commandBuffer commit]; + [commandBuffer waitUntilCompleted]; +} + +void kinc_g5_command_list_set_vertex_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size) { + id buf = (__bridge id)buffer->impl._buffer; + id encoder = getMetalEncoder(); + [encoder setVertexBuffer:buf offset:offset atIndex:1]; +} + +void kinc_g5_command_list_set_fragment_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size) { + id buf = (__bridge id)buffer->impl._buffer; + id encoder = getMetalEncoder(); + [encoder setFragmentBuffer:buf offset:offset atIndex:0]; +} + +void kinc_g5_command_list_set_compute_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size) { + assert(compute_command_encoder != nil); + id buf = (__bridge id)buffer->impl._buffer; + [compute_command_encoder setBuffer:buf offset:offset atIndex:1]; +} + +void kinc_g5_command_list_render_target_to_texture_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) { +#ifndef KINC_APPLE_SOC + id encoder = getMetalEncoder(); + [encoder textureBarrier]; +#endif +} + +void kinc_g5_command_list_texture_to_render_target_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) {} + +void kinc_g5_command_list_set_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture) { + id tex = (__bridge id)texture->impl._tex; + if (compute_command_encoder != nil) { + [compute_command_encoder setTexture:tex atIndex:unit.stages[KINC_G5_SHADER_TYPE_COMPUTE]]; + } + else { + if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) { + [render_command_encoder setVertexTexture:tex atIndex:unit.stages[KINC_G5_SHADER_TYPE_VERTEX]]; + } + if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) { + [render_command_encoder setFragmentTexture:tex atIndex:unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]]; + } + } +} + +void kinc_g5_command_list_set_image_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture) { + kinc_g5_command_list_set_texture(list, unit, texture); +} + +void kinc_g5_command_list_set_render_target_face(kinc_g5_command_list_t *list, kinc_g5_render_target_t *texture, int face) {} + +void kinc_g5_command_list_set_texture_from_render_target(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_render_target_t *target) { + id encoder = getMetalEncoder(); + id tex = (__bridge id)target->impl._tex; + if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) { + [encoder setVertexTexture:tex atIndex:unit.stages[KINC_G5_SHADER_TYPE_VERTEX]]; + } + if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) { + [encoder setFragmentTexture:tex atIndex:unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]]; + } +} + +void kinc_g5_command_list_set_texture_from_render_target_depth(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_render_target_t *target) { + id encoder = getMetalEncoder(); + id depth_tex = (__bridge id)target->impl._depthTex; + if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) { + [encoder setVertexTexture:depth_tex atIndex:unit.stages[KINC_G5_SHADER_TYPE_VERTEX]]; + } + if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) { + [encoder setFragmentTexture:depth_tex atIndex:unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]]; + } +} + +void kinc_g5_command_list_set_sampler(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_sampler_t *sampler) { + id encoder = getMetalEncoder(); + id mtl_sampler = (__bridge id)sampler->impl.sampler; + + if (unit.stages[KINC_G5_SHADER_TYPE_VERTEX] >= 0) { + [encoder setVertexSamplerState:mtl_sampler atIndex:unit.stages[KINC_G5_SHADER_TYPE_VERTEX]]; + } + if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) { + [encoder setFragmentSamplerState:mtl_sampler atIndex:unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]]; + } +} + +void kinc_g5_command_list_set_compute_shader(kinc_g5_command_list_t *list, kinc_g5_compute_shader *shader) { + if (compute_command_encoder == nil) { + end_render_pass(); + compute_command_encoder = [command_buffer computeCommandEncoder]; + } + + id pipeline = (__bridge id)shader->impl._pipeline; + [compute_command_encoder setComputePipelineState:pipeline]; +} + +void kinc_g5_command_list_compute(kinc_g5_command_list_t *list, int x, int y, int z) { + assert(compute_command_encoder != nil); + + MTLSize perGrid; + perGrid.width = x; + perGrid.height = y; + perGrid.depth = z; + MTLSize perGroup; + perGroup.width = 16; + perGroup.height = 16; + perGroup.depth = 1; + [compute_command_encoder dispatchThreadgroups:perGrid threadsPerThreadgroup:perGroup]; + + [compute_command_encoder endEncoding]; + + compute_command_encoder = nil; + + start_render_pass(); +} diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/compute.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/compute.h new file mode 100644 index 000000000..a2ff02b6a --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/compute.h @@ -0,0 +1,8 @@ +#pragma once + +typedef struct kinc_g5_compute_shader_impl { + char name[1024]; + void *_function; + void *_pipeline; + void *_reflection; +} kinc_g5_compute_shader_impl; diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/compute.m.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/compute.m.h new file mode 100644 index 000000000..ef4c9dfe0 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/compute.m.h @@ -0,0 +1,112 @@ +#include +#include +#include + +#include + +id getMetalDevice(void); +id getMetalLibrary(void); + +void kinc_g5_compute_shader_init(kinc_g5_compute_shader *shader, void *_data, int length) { + shader->impl.name[0] = 0; + + { + uint8_t *data = (uint8_t *)_data; + if (length > 1 && data[0] == '>') { + memcpy(shader->impl.name, data + 1, length - 1); + shader->impl.name[length - 1] = 0; + } + else { + for (int i = 3; i < length; ++i) { + if (data[i] == '\n') { + shader->impl.name[i - 3] = 0; + break; + } + else { + shader->impl.name[i - 3] = data[i]; + } + } + } + } + + char *data = (char *)_data; + id library = nil; + if (length > 1 && data[0] == '>') { + library = getMetalLibrary(); + } + else { + id device = getMetalDevice(); + library = [device newLibraryWithSource:[[NSString alloc] initWithBytes:data length:length encoding:NSUTF8StringEncoding] options:nil error:nil]; + } + id function = [library newFunctionWithName:[NSString stringWithCString:shader->impl.name encoding:NSUTF8StringEncoding]]; + assert(function != nil); + shader->impl._function = (__bridge_retained void *)function; + + id device = getMetalDevice(); + MTLComputePipelineReflection *reflection = nil; + NSError *error = nil; + shader->impl._pipeline = (__bridge_retained void *)[device newComputePipelineStateWithFunction:function + options:MTLPipelineOptionBufferTypeInfo + reflection:&reflection + error:&error]; + if (error != nil) + NSLog(@"%@", [error localizedDescription]); + assert(shader->impl._pipeline != NULL && !error); + shader->impl._reflection = (__bridge_retained void *)reflection; +} + +void kinc_g5_compute_shader_destroy(kinc_g5_compute_shader *shader) { + id function = (__bridge_transfer id)shader->impl._function; + function = nil; + shader->impl._function = NULL; + + id pipeline = (__bridge_transfer id)shader->impl._pipeline; + pipeline = nil; + shader->impl._pipeline = NULL; + + MTLComputePipelineReflection *reflection = (__bridge_transfer MTLComputePipelineReflection *)shader->impl._reflection; + reflection = nil; + shader->impl._reflection = NULL; +} + +kinc_g5_constant_location_t kinc_g5_compute_shader_get_constant_location(kinc_g5_compute_shader *shader, const char *name) { + kinc_g5_constant_location_t location; + location.impl.vertexOffset = -1; + location.impl.fragmentOffset = -1; + location.impl.computeOffset = -1; + + MTLComputePipelineReflection *reflection = (__bridge MTLComputePipelineReflection *)shader->impl._reflection; + + for (MTLArgument *arg in reflection.arguments) { + if (arg.type == MTLArgumentTypeBuffer && [arg.name isEqualToString:@"uniforms"]) { + if ([arg bufferDataType] == MTLDataTypeStruct) { + MTLStructType *structObj = [arg bufferStructType]; + for (MTLStructMember *member in structObj.members) { + if (strcmp([[member name] UTF8String], name) == 0) { + location.impl.computeOffset = (int)[member offset]; + break; + } + } + } + break; + } + } + + return location; +} + +kinc_g5_texture_unit_t kinc_g5_compute_shader_get_texture_unit(kinc_g5_compute_shader *shader, const char *name) { + kinc_g5_texture_unit_t unit; + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + unit.stages[i] = -1; + } + + MTLComputePipelineReflection *reflection = (__bridge MTLComputePipelineReflection *)shader->impl._reflection; + for (MTLArgument *arg in reflection.arguments) { + if ([arg type] == MTLArgumentTypeTexture && strcmp([[arg name] UTF8String], name) == 0) { + unit.stages[KINC_G5_SHADER_TYPE_COMPUTE] = (int)[arg index]; + } + } + + return unit; +} diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/constantbuffer.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/constantbuffer.h new file mode 100644 index 000000000..93c20281b --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/constantbuffer.h @@ -0,0 +1,8 @@ +#pragma once + +typedef struct { + void *_buffer; + int lastStart; + int lastCount; + int mySize; +} ConstantBuffer5Impl; diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/constantbuffer.m.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/constantbuffer.m.h new file mode 100644 index 000000000..c3b6a1e64 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/constantbuffer.m.h @@ -0,0 +1,40 @@ +#include + +#import + +id getMetalDevice(void); + +bool kinc_g5_transposeMat3 = true; +bool kinc_g5_transposeMat4 = true; + +void kinc_g5_constant_buffer_init(kinc_g5_constant_buffer_t *buffer, int size) { + buffer->impl.mySize = size; + buffer->data = NULL; + buffer->impl._buffer = (__bridge_retained void *)[getMetalDevice() newBufferWithLength:size options:MTLResourceOptionCPUCacheModeDefault]; +} + +void kinc_g5_constant_buffer_destroy(kinc_g5_constant_buffer_t *buffer) { + id buf = (__bridge_transfer id)buffer->impl._buffer; + buf = nil; + buffer->impl._buffer = NULL; +} + +void kinc_g5_constant_buffer_lock_all(kinc_g5_constant_buffer_t *buffer) { + kinc_g5_constant_buffer_lock(buffer, 0, kinc_g5_constant_buffer_size(buffer)); +} + +void kinc_g5_constant_buffer_lock(kinc_g5_constant_buffer_t *buffer, int start, int count) { + buffer->impl.lastStart = start; + buffer->impl.lastCount = count; + id buf = (__bridge id)buffer->impl._buffer; + uint8_t *data = (uint8_t *)[buf contents]; + buffer->data = &data[start]; +} + +void kinc_g5_constant_buffer_unlock(kinc_g5_constant_buffer_t *buffer) { + buffer->data = NULL; +} + +int kinc_g5_constant_buffer_size(kinc_g5_constant_buffer_t *buffer) { + return buffer->impl.mySize; +} diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/graphics.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/graphics.h new file mode 100644 index 000000000..056f2a534 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/graphics.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/indexbuffer.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/indexbuffer.h new file mode 100644 index 000000000..cf201904c --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/indexbuffer.h @@ -0,0 +1,10 @@ +#pragma once + +typedef struct { + void *metal_buffer; + int count; + bool gpu_memory; + int format; + int last_start; + int last_count; +} IndexBuffer5Impl; diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/indexbuffer.m.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/indexbuffer.m.h new file mode 100644 index 000000000..d5b19c0fd --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/indexbuffer.m.h @@ -0,0 +1,73 @@ +#include +#include + +#import + +id getMetalDevice(void); + +void kinc_g5_index_buffer_init(kinc_g5_index_buffer_t *buffer, int indexCount, kinc_g5_index_buffer_format_t format, bool gpuMemory) { + buffer->impl.count = indexCount; + buffer->impl.gpu_memory = gpuMemory; + buffer->impl.format = format; + buffer->impl.last_start = 0; + buffer->impl.last_count = indexCount; + + id device = getMetalDevice(); + MTLResourceOptions options = MTLResourceCPUCacheModeWriteCombined; +#ifdef KINC_APPLE_SOC + options |= MTLResourceStorageModeShared; +#else + if (gpuMemory) { + options |= MTLResourceStorageModeManaged; + } + else { + options |= MTLResourceStorageModeShared; + } +#endif + buffer->impl.metal_buffer = (__bridge_retained void *)[device + newBufferWithLength:(format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? sizeof(uint16_t) * indexCount : sizeof(uint32_t) * indexCount) + options:options]; +} + +void kinc_g5_index_buffer_destroy(kinc_g5_index_buffer_t *buffer) { + id buf = (__bridge_transfer id)buffer->impl.metal_buffer; + buf = nil; + buffer->impl.metal_buffer = NULL; +} + +static int kinc_g5_internal_index_buffer_stride(kinc_g5_index_buffer_t *buffer) { + return buffer->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? 2 : 4; +} + +void *kinc_g5_index_buffer_lock_all(kinc_g5_index_buffer_t *buffer) { + return kinc_g5_index_buffer_lock(buffer, 0, kinc_g5_index_buffer_count(buffer)); +} + +void *kinc_g5_index_buffer_lock(kinc_g5_index_buffer_t *buffer, int start, int count) { + buffer->impl.last_start = start; + buffer->impl.last_count = count; + + id metal_buffer = (__bridge id)buffer->impl.metal_buffer; + uint8_t *data = (uint8_t *)[metal_buffer contents]; + return &data[start * kinc_g5_internal_index_buffer_stride(buffer)]; +} + +void kinc_g5_index_buffer_unlock_all(kinc_g5_index_buffer_t *buffer) { + kinc_g5_index_buffer_unlock(buffer, buffer->impl.last_count); +} + +void kinc_g5_index_buffer_unlock(kinc_g5_index_buffer_t *buffer, int count) { +#ifndef KINC_APPLE_SOC + if (buffer->impl.gpu_memory) { + id metal_buffer = (__bridge id)buffer->impl.metal_buffer; + NSRange range; + range.location = buffer->impl.last_start * kinc_g5_internal_index_buffer_stride(buffer); + range.length = count * kinc_g5_internal_index_buffer_stride(buffer); + [metal_buffer didModifyRange:range]; + } +#endif +} + +int kinc_g5_index_buffer_count(kinc_g5_index_buffer_t *buffer) { + return buffer->impl.count; +} diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/metalunit.m b/armorcore/sources/backends/metal/kinc/backend/graphics5/metalunit.m new file mode 100644 index 000000000..b8a70973c --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/metalunit.m @@ -0,0 +1,22 @@ +#import +#import + +static id command_buffer = nil; +static id render_command_encoder = nil; +static id compute_command_encoder = nil; + +static void start_render_pass(void); +static void end_render_pass(void); + +#include "Metal.m.h" +#include "commandlist.m.h" +#include "compute.m.h" +#include "constantbuffer.m.h" +#include "indexbuffer.m.h" +#include "pipeline.m.h" +#include "raytrace.m.h" +#include "rendertarget.m.h" +#include "sampler.m.h" +#include "shader.m.h" +#include "texture.m.h" +#include "vertexbuffer.m.h" diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/pipeline.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/pipeline.h new file mode 100644 index 000000000..d43d21274 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/pipeline.h @@ -0,0 +1,24 @@ +#pragma once + +struct kinc_g5_shader; + +typedef struct { + struct kinc_g5_shader *vertexShader; + struct kinc_g5_shader *fragmentShader; + void *_pipeline; + void *_pipelineDepth; + void *_reflection; + void *_depthStencil; + void *_depthStencilNone; + // void _set(); +} PipelineState5Impl; + +typedef struct { + int a; +} ComputePipelineState5Impl; + +typedef struct { + int vertexOffset; + int fragmentOffset; + int computeOffset; +} ConstantLocation5Impl; diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/pipeline.m.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/pipeline.m.h new file mode 100644 index 000000000..13a932c13 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/pipeline.m.h @@ -0,0 +1,433 @@ +#include +#include +#include + +#import + +#include +#include +#include + +id getMetalDevice(void); +id getMetalEncoder(void); + +static MTLBlendFactor convert_blending_factor(kinc_g5_blending_factor_t factor) { + switch (factor) { + case KINC_G5_BLEND_ONE: + return MTLBlendFactorOne; + case KINC_G5_BLEND_ZERO: + return MTLBlendFactorZero; + case KINC_G5_BLEND_SOURCE_ALPHA: + return MTLBlendFactorSourceAlpha; + case KINC_G5_BLEND_DEST_ALPHA: + return MTLBlendFactorDestinationAlpha; + case KINC_G5_BLEND_INV_SOURCE_ALPHA: + return MTLBlendFactorOneMinusSourceAlpha; + case KINC_G5_BLEND_INV_DEST_ALPHA: + return MTLBlendFactorOneMinusDestinationAlpha; + case KINC_G5_BLEND_SOURCE_COLOR: + return MTLBlendFactorSourceColor; + case KINC_G5_BLEND_DEST_COLOR: + return MTLBlendFactorDestinationColor; + case KINC_G5_BLEND_INV_SOURCE_COLOR: + return MTLBlendFactorOneMinusSourceColor; + case KINC_G5_BLEND_INV_DEST_COLOR: + return MTLBlendFactorOneMinusDestinationColor; + case KINC_G5_BLEND_CONSTANT: + return MTLBlendFactorBlendColor; + case KINC_G5_BLEND_INV_CONSTANT: + return MTLBlendFactorOneMinusBlendColor; + } +} + +static MTLBlendOperation convert_blending_operation(kinc_g5_blending_operation_t op) { + switch (op) { + case KINC_G5_BLENDOP_ADD: + return MTLBlendOperationAdd; + case KINC_G5_BLENDOP_SUBTRACT: + return MTLBlendOperationSubtract; + case KINC_G5_BLENDOP_REVERSE_SUBTRACT: + return MTLBlendOperationReverseSubtract; + case KINC_G5_BLENDOP_MIN: + return MTLBlendOperationMin; + case KINC_G5_BLENDOP_MAX: + return MTLBlendOperationMax; + } +} + +static MTLCompareFunction convert_compare_mode(kinc_g5_compare_mode_t compare) { + switch (compare) { + case KINC_G5_COMPARE_MODE_ALWAYS: + return MTLCompareFunctionAlways; + case KINC_G5_COMPARE_MODE_NEVER: + return MTLCompareFunctionNever; + case KINC_G5_COMPARE_MODE_EQUAL: + return MTLCompareFunctionEqual; + case KINC_G5_COMPARE_MODE_NOT_EQUAL: + return MTLCompareFunctionNotEqual; + case KINC_G5_COMPARE_MODE_LESS: + return MTLCompareFunctionLess; + case KINC_G5_COMPARE_MODE_LESS_EQUAL: + return MTLCompareFunctionLessEqual; + case KINC_G5_COMPARE_MODE_GREATER: + return MTLCompareFunctionGreater; + case KINC_G5_COMPARE_MODE_GREATER_EQUAL: + return MTLCompareFunctionGreaterEqual; + } +} + +static MTLCullMode convert_cull_mode(kinc_g5_cull_mode_t cull) { + switch (cull) { + case KINC_G5_CULL_MODE_CLOCKWISE: + return MTLCullModeFront; + case KINC_G5_CULL_MODE_COUNTERCLOCKWISE: + return MTLCullModeBack; + case KINC_G5_CULL_MODE_NEVER: + return MTLCullModeNone; + } +} + +static MTLPixelFormat convert_render_target_format(kinc_g5_render_target_format_t format) { + switch (format) { + case KINC_G5_RENDER_TARGET_FORMAT_128BIT_FLOAT: + return MTLPixelFormatRGBA32Float; + case KINC_G5_RENDER_TARGET_FORMAT_64BIT_FLOAT: + return MTLPixelFormatRGBA16Float; + case KINC_G5_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT: + return MTLPixelFormatR32Float; + case KINC_G5_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT: + return MTLPixelFormatR16Float; + case KINC_G5_RENDER_TARGET_FORMAT_8BIT_RED: + return MTLPixelFormatR8Unorm; + case KINC_G5_RENDER_TARGET_FORMAT_32BIT: + default: + return MTLPixelFormatBGRA8Unorm; + } +} + +void kinc_g5_pipeline_init(kinc_g5_pipeline_t *pipeline) { + memset(&pipeline->impl, 0, sizeof(pipeline->impl)); +} + +void kinc_g5_pipeline_destroy(kinc_g5_pipeline_t *pipeline) { + pipeline->impl._reflection = NULL; + pipeline->impl._depthStencil = NULL; + + id pipe = (__bridge_transfer id)pipeline->impl._pipeline; + pipe = nil; + pipeline->impl._pipeline = NULL; + + MTLRenderPipelineReflection *reflection = (__bridge_transfer MTLRenderPipelineReflection *)pipeline->impl._reflection; + reflection = nil; + pipeline->impl._reflection = NULL; + + id pipeDepth = (__bridge_transfer id)pipeline->impl._pipelineDepth; + pipeDepth = nil; + pipeline->impl._pipelineDepth = NULL; + + id depthStencil = (__bridge_transfer id)pipeline->impl._depthStencil; + depthStencil = nil; + pipeline->impl._depthStencil = NULL; + + id depthStencilNone = (__bridge_transfer id)pipeline->impl._depthStencilNone; + depthStencilNone = nil; + pipeline->impl._depthStencilNone = NULL; +} + +static int findAttributeIndex(NSArray *attributes, const char *name) { + for (MTLVertexAttribute *attribute in attributes) { + if (strcmp(name, [[attribute name] UTF8String]) == 0) { + return (int)[attribute attributeIndex]; + } + } + return -1; +} + +void kinc_g5_pipeline_compile(kinc_g5_pipeline_t *pipeline) { + MTLRenderPipelineDescriptor *renderPipelineDesc = [[MTLRenderPipelineDescriptor alloc] init]; + renderPipelineDesc.vertexFunction = (__bridge id)pipeline->vertexShader->impl.mtlFunction; + renderPipelineDesc.fragmentFunction = (__bridge id)pipeline->fragmentShader->impl.mtlFunction; + for (int i = 0; i < pipeline->colorAttachmentCount; ++i) { + renderPipelineDesc.colorAttachments[i].pixelFormat = convert_render_target_format(pipeline->colorAttachment[i]); + renderPipelineDesc.colorAttachments[i].blendingEnabled = + pipeline->blend_source != KINC_G5_BLEND_ONE || pipeline->blend_destination != KINC_G5_BLEND_ZERO || + pipeline->alpha_blend_source != KINC_G5_BLEND_ONE || pipeline->alpha_blend_destination != KINC_G5_BLEND_ZERO; + renderPipelineDesc.colorAttachments[i].sourceRGBBlendFactor = convert_blending_factor(pipeline->blend_source); + renderPipelineDesc.colorAttachments[i].destinationRGBBlendFactor = convert_blending_factor(pipeline->blend_destination); + renderPipelineDesc.colorAttachments[i].rgbBlendOperation = convert_blending_operation(pipeline->blend_operation); + renderPipelineDesc.colorAttachments[i].sourceAlphaBlendFactor = convert_blending_factor(pipeline->alpha_blend_source); + renderPipelineDesc.colorAttachments[i].destinationAlphaBlendFactor = convert_blending_factor(pipeline->alpha_blend_destination); + renderPipelineDesc.colorAttachments[i].alphaBlendOperation = convert_blending_operation(pipeline->alpha_blend_operation); + renderPipelineDesc.colorAttachments[i].writeMask = + (pipeline->colorWriteMaskRed[i] ? MTLColorWriteMaskRed : 0) | (pipeline->colorWriteMaskGreen[i] ? MTLColorWriteMaskGreen : 0) | + (pipeline->colorWriteMaskBlue[i] ? MTLColorWriteMaskBlue : 0) | (pipeline->colorWriteMaskAlpha[i] ? MTLColorWriteMaskAlpha : 0); + } + renderPipelineDesc.depthAttachmentPixelFormat = MTLPixelFormatInvalid; + renderPipelineDesc.stencilAttachmentPixelFormat = MTLPixelFormatInvalid; + + float offset = 0; + MTLVertexDescriptor *vertexDescriptor = [[MTLVertexDescriptor alloc] init]; + + for (int i = 0; i < pipeline->inputLayout[0]->size; ++i) { + int index = findAttributeIndex(renderPipelineDesc.vertexFunction.vertexAttributes, pipeline->inputLayout[0]->elements[i].name); + + if (index < 0) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Could not find vertex attribute %s\n", pipeline->inputLayout[0]->elements[i].name); + } + + if (index >= 0) { + vertexDescriptor.attributes[index].bufferIndex = 0; + vertexDescriptor.attributes[index].offset = offset; + } + + offset += kinc_g4_vertex_data_size(pipeline->inputLayout[0]->elements[i].data); + if (index >= 0) { + switch (pipeline->inputLayout[0]->elements[i].data) { + case KINC_G4_VERTEX_DATA_NONE: + assert(false); + break; + case KINC_G4_VERTEX_DATA_F32_1X: + vertexDescriptor.attributes[index].format = MTLVertexFormatFloat; + break; + case KINC_G4_VERTEX_DATA_F32_2X: + vertexDescriptor.attributes[index].format = MTLVertexFormatFloat2; + break; + case KINC_G4_VERTEX_DATA_F32_3X: + vertexDescriptor.attributes[index].format = MTLVertexFormatFloat3; + break; + case KINC_G4_VERTEX_DATA_F32_4X: + vertexDescriptor.attributes[index].format = MTLVertexFormatFloat4; + break; + case KINC_G4_VERTEX_DATA_F32_4X4: + assert(false); + break; + case KINC_G4_VERTEX_DATA_I8_1X: + vertexDescriptor.attributes[index].format = MTLVertexFormatChar; + break; + case KINC_G4_VERTEX_DATA_U8_1X: + vertexDescriptor.attributes[index].format = MTLVertexFormatUChar; + break; + case KINC_G4_VERTEX_DATA_I8_1X_NORMALIZED: + vertexDescriptor.attributes[index].format = MTLVertexFormatCharNormalized; + break; + case KINC_G4_VERTEX_DATA_U8_1X_NORMALIZED: + vertexDescriptor.attributes[index].format = MTLVertexFormatUCharNormalized; + break; + case KINC_G4_VERTEX_DATA_I8_2X: + vertexDescriptor.attributes[index].format = MTLVertexFormatChar2; + break; + case KINC_G4_VERTEX_DATA_U8_2X: + vertexDescriptor.attributes[index].format = MTLVertexFormatUChar2; + break; + case KINC_G4_VERTEX_DATA_I8_2X_NORMALIZED: + vertexDescriptor.attributes[index].format = MTLVertexFormatChar2Normalized; + break; + case KINC_G4_VERTEX_DATA_U8_2X_NORMALIZED: + vertexDescriptor.attributes[index].format = MTLVertexFormatUChar2Normalized; + break; + case KINC_G4_VERTEX_DATA_I8_4X: + vertexDescriptor.attributes[index].format = MTLVertexFormatChar4; + break; + case KINC_G4_VERTEX_DATA_U8_4X: + vertexDescriptor.attributes[index].format = MTLVertexFormatUChar4; + break; + case KINC_G4_VERTEX_DATA_I8_4X_NORMALIZED: + vertexDescriptor.attributes[index].format = MTLVertexFormatChar4Normalized; + break; + case KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED: + vertexDescriptor.attributes[index].format = MTLVertexFormatUChar4Normalized; + break; + case KINC_G4_VERTEX_DATA_I16_1X: + vertexDescriptor.attributes[index].format = MTLVertexFormatShort; + break; + case KINC_G4_VERTEX_DATA_U16_1X: + vertexDescriptor.attributes[index].format = MTLVertexFormatUShort; + break; + case KINC_G4_VERTEX_DATA_I16_1X_NORMALIZED: + vertexDescriptor.attributes[index].format = MTLVertexFormatShortNormalized; + break; + case KINC_G4_VERTEX_DATA_U16_1X_NORMALIZED: + vertexDescriptor.attributes[index].format = MTLVertexFormatUShortNormalized; + break; + case KINC_G4_VERTEX_DATA_I16_2X: + vertexDescriptor.attributes[index].format = MTLVertexFormatShort2; + break; + case KINC_G4_VERTEX_DATA_U16_2X: + vertexDescriptor.attributes[index].format = MTLVertexFormatUShort2; + break; + case KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED: + vertexDescriptor.attributes[index].format = MTLVertexFormatShort2Normalized; + break; + case KINC_G4_VERTEX_DATA_U16_2X_NORMALIZED: + vertexDescriptor.attributes[index].format = MTLVertexFormatUShort2Normalized; + break; + case KINC_G4_VERTEX_DATA_I16_4X: + vertexDescriptor.attributes[index].format = MTLVertexFormatShort4; + break; + case KINC_G4_VERTEX_DATA_U16_4X: + vertexDescriptor.attributes[index].format = MTLVertexFormatUShort4; + break; + case KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED: + vertexDescriptor.attributes[index].format = MTLVertexFormatShort4Normalized; + break; + case KINC_G4_VERTEX_DATA_U16_4X_NORMALIZED: + vertexDescriptor.attributes[index].format = MTLVertexFormatUShort4Normalized; + break; + case KINC_G4_VERTEX_DATA_I32_1X: + vertexDescriptor.attributes[index].format = MTLVertexFormatInt; + break; + case KINC_G4_VERTEX_DATA_U32_1X: + vertexDescriptor.attributes[index].format = MTLVertexFormatUInt; + break; + case KINC_G4_VERTEX_DATA_I32_2X: + vertexDescriptor.attributes[index].format = MTLVertexFormatInt2; + break; + case KINC_G4_VERTEX_DATA_U32_2X: + vertexDescriptor.attributes[index].format = MTLVertexFormatUInt2; + break; + case KINC_G4_VERTEX_DATA_I32_3X: + vertexDescriptor.attributes[index].format = MTLVertexFormatInt3; + break; + case KINC_G4_VERTEX_DATA_U32_3X: + vertexDescriptor.attributes[index].format = MTLVertexFormatUInt3; + break; + case KINC_G4_VERTEX_DATA_I32_4X: + vertexDescriptor.attributes[index].format = MTLVertexFormatInt4; + break; + case KINC_G4_VERTEX_DATA_U32_4X: + vertexDescriptor.attributes[index].format = MTLVertexFormatUInt4; + break; + default: + assert(false); + break; + } + } + } + + vertexDescriptor.layouts[0].stride = offset; + vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex; + + renderPipelineDesc.vertexDescriptor = vertexDescriptor; + + NSError *errors = nil; + MTLRenderPipelineReflection *reflection = nil; + id device = getMetalDevice(); + + pipeline->impl._pipeline = (__bridge_retained void *)[device newRenderPipelineStateWithDescriptor:renderPipelineDesc + options:MTLPipelineOptionBufferTypeInfo + reflection:&reflection + error:&errors]; + if (errors != nil) + NSLog(@"%@", [errors localizedDescription]); + assert(pipeline->impl._pipeline && !errors); + + renderPipelineDesc.depthAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + renderPipelineDesc.stencilAttachmentPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + pipeline->impl._pipelineDepth = (__bridge_retained void *)[device newRenderPipelineStateWithDescriptor:renderPipelineDesc + options:MTLPipelineOptionBufferTypeInfo + reflection:&reflection + error:&errors]; + if (errors != nil) + NSLog(@"%@", [errors localizedDescription]); + assert(pipeline->impl._pipelineDepth && !errors); + + pipeline->impl._reflection = (__bridge_retained void *)reflection; + + MTLDepthStencilDescriptor *depthStencilDescriptor = [MTLDepthStencilDescriptor new]; + depthStencilDescriptor.depthCompareFunction = convert_compare_mode(pipeline->depthMode); + depthStencilDescriptor.depthWriteEnabled = pipeline->depthWrite; + pipeline->impl._depthStencil = (__bridge_retained void *)[device newDepthStencilStateWithDescriptor:depthStencilDescriptor]; + + depthStencilDescriptor.depthCompareFunction = MTLCompareFunctionAlways; + depthStencilDescriptor.depthWriteEnabled = false; + pipeline->impl._depthStencilNone = (__bridge_retained void *)[device newDepthStencilStateWithDescriptor:depthStencilDescriptor]; +} + +bool kinc_internal_current_render_target_has_depth(void); + +void kinc_g5_internal_pipeline_set(kinc_g5_pipeline_t *pipeline) { + id encoder = getMetalEncoder(); + if (kinc_internal_current_render_target_has_depth()) { + id pipe = (__bridge id)pipeline->impl._pipelineDepth; + [encoder setRenderPipelineState:pipe]; + id depthStencil = (__bridge id)pipeline->impl._depthStencil; + [encoder setDepthStencilState:depthStencil]; + } + else { + id pipe = (__bridge id)pipeline->impl._pipeline; + [encoder setRenderPipelineState:pipe]; + id depthStencil = (__bridge id)pipeline->impl._depthStencilNone; + [encoder setDepthStencilState:depthStencil]; + } + [encoder setFrontFacingWinding:MTLWindingClockwise]; + [encoder setCullMode:convert_cull_mode(pipeline->cullMode)]; +} + +kinc_g5_constant_location_t kinc_g5_pipeline_get_constant_location(kinc_g5_pipeline_t *pipeline, const char *name) { + if (strcmp(name, "bias") == 0) { + name = "bias0"; + } + + kinc_g5_constant_location_t location; + location.impl.vertexOffset = -1; + location.impl.fragmentOffset = -1; + location.impl.computeOffset = -1; + + MTLRenderPipelineReflection *reflection = (__bridge MTLRenderPipelineReflection *)pipeline->impl._reflection; + + for (MTLArgument *arg in reflection.vertexArguments) { + if (arg.type == MTLArgumentTypeBuffer && [arg.name isEqualToString:@"uniforms"]) { + if ([arg bufferDataType] == MTLDataTypeStruct) { + MTLStructType *structObj = [arg bufferStructType]; + for (MTLStructMember *member in structObj.members) { + if (strcmp([[member name] UTF8String], name) == 0) { + location.impl.vertexOffset = (int)[member offset]; + break; + } + } + } + break; + } + } + + for (MTLArgument *arg in reflection.fragmentArguments) { + if ([arg type] == MTLArgumentTypeBuffer && [[arg name] isEqualToString:@"uniforms"]) { + if ([arg bufferDataType] == MTLDataTypeStruct) { + MTLStructType *structObj = [arg bufferStructType]; + for (MTLStructMember *member in structObj.members) { + if (strcmp([[member name] UTF8String], name) == 0) { + location.impl.fragmentOffset = (int)[member offset]; + break; + } + } + } + break; + } + } + + return location; +} + +kinc_g5_texture_unit_t kinc_g5_pipeline_get_texture_unit(kinc_g5_pipeline_t *pipeline, const char *name) { + kinc_g5_texture_unit_t unit = {0}; + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + unit.stages[i] = -1; + } + + MTLRenderPipelineReflection *reflection = (__bridge MTLRenderPipelineReflection *)pipeline->impl._reflection; + for (MTLArgument *arg in reflection.fragmentArguments) { + if ([arg type] == MTLArgumentTypeTexture && strcmp([[arg name] UTF8String], name) == 0) { + unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] = (int)[arg index]; + break; + } + } + + for (MTLArgument *arg in reflection.vertexArguments) { + if ([arg type] == MTLArgumentTypeTexture && strcmp([[arg name] UTF8String], name) == 0) { + unit.stages[KINC_G5_SHADER_TYPE_VERTEX] = (int)[arg index]; + break; + } + } + + return unit; +} diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/raytrace.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/raytrace.h new file mode 100644 index 000000000..83cf81fc1 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/raytrace.h @@ -0,0 +1,9 @@ +#pragma once + +typedef struct { + void *_raytracingPipeline; +} kinc_raytrace_pipeline_impl_t; + +typedef struct { + void *_accelerationStructure; +} kinc_raytrace_acceleration_structure_impl_t; diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/raytrace.m.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/raytrace.m.h new file mode 100644 index 000000000..71c771c74 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/raytrace.m.h @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include +#include +#include + +static kinc_raytrace_acceleration_structure_t *accel; +static kinc_raytrace_pipeline_t *pipeline; +static kinc_g5_render_target_t *output = NULL; +static kinc_g5_constant_buffer_t *constant_buf; + +id getMetalDevice(void); +id getMetalQueue(void); + +id _raytracing_pipeline; +NSMutableArray *_primitive_accels; +id _instance_accel; +dispatch_semaphore_t _sem; + +static kinc_g5_render_target_t *_texpaint0; +static kinc_g5_render_target_t *_texpaint1; +static kinc_g5_render_target_t *_texpaint2; +static kinc_g5_texture_t *_texenv; +static kinc_g5_texture_t *_texsobol; +static kinc_g5_texture_t *_texscramble; +static kinc_g5_texture_t *_texrank; +static kinc_g5_vertex_buffer_t *_vb; +static kinc_g5_index_buffer_t *_ib; + +bool kinc_raytrace_supported() { + id device = getMetalDevice(); + return device.supportsRaytracing; +} + +void kinc_raytrace_pipeline_init(kinc_raytrace_pipeline_t *pipeline, kinc_g5_command_list_t *command_list, void *ray_shader, int ray_shader_size, + kinc_g5_constant_buffer_t *constant_buffer) { + id device = getMetalDevice(); + if (!device.supportsRaytracing) return; + constant_buf = constant_buffer; + + NSError *error = nil; + id library = [device newLibraryWithSource:[[NSString alloc] initWithBytes:ray_shader length:ray_shader_size encoding:NSUTF8StringEncoding] + options:nil + error:&error]; + if (library == nil) { + kinc_log(KINC_LOG_LEVEL_ERROR, "%s", error.localizedDescription.UTF8String); + } + + MTLComputePipelineDescriptor *descriptor = [[MTLComputePipelineDescriptor alloc] init]; + descriptor.computeFunction = [library newFunctionWithName:@"raytracingKernel"]; + descriptor.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES; + _raytracing_pipeline = [device newComputePipelineStateWithDescriptor:descriptor options:0 reflection:nil error:&error]; + _sem = dispatch_semaphore_create(2); +} + +void kinc_raytrace_pipeline_destroy(kinc_raytrace_pipeline_t *pipeline) {} + +id create_acceleration_sctructure(MTLAccelerationStructureDescriptor *descriptor) { + id device = getMetalDevice(); + id queue = getMetalQueue(); + + MTLAccelerationStructureSizes accel_sizes = [device accelerationStructureSizesWithDescriptor:descriptor]; + id acceleration_structure = [device newAccelerationStructureWithSize:accel_sizes.accelerationStructureSize]; + + id scratch_buffer = [device newBufferWithLength:accel_sizes.buildScratchBufferSize options:MTLResourceStorageModePrivate]; + id command_buffer = [queue commandBuffer]; + id command_encoder = [command_buffer accelerationStructureCommandEncoder]; + id compacteds_size_buffer = [device newBufferWithLength:sizeof(uint32_t) options:MTLResourceStorageModeShared]; + + [command_encoder buildAccelerationStructure:acceleration_structure descriptor:descriptor scratchBuffer:scratch_buffer scratchBufferOffset:0]; + + [command_encoder writeCompactedAccelerationStructureSize:acceleration_structure toBuffer:compacteds_size_buffer offset:0]; + + [command_encoder endEncoding]; + [command_buffer commit]; + [command_buffer waitUntilCompleted]; + + uint32_t compacted_size = *(uint32_t *)compacteds_size_buffer.contents; + id compacted_acceleration_structure = [device newAccelerationStructureWithSize:compacted_size]; + command_buffer = [queue commandBuffer]; + command_encoder = [command_buffer accelerationStructureCommandEncoder]; + [command_encoder copyAndCompactAccelerationStructure:acceleration_structure toAccelerationStructure:compacted_acceleration_structure]; + [command_encoder endEncoding]; + [command_buffer commit]; + + return compacted_acceleration_structure; +} + +void kinc_raytrace_acceleration_structure_init(kinc_raytrace_acceleration_structure_t *accel, kinc_g5_command_list_t *command_list, kinc_g5_vertex_buffer_t *vb, + kinc_g5_index_buffer_t *ib, float scale) { + id device = getMetalDevice(); + if (!device.supportsRaytracing) return; +#if !TARGET_OS_IPHONE + MTLResourceOptions options = MTLResourceStorageModeManaged; +#else + MTLResourceOptions options = MTLResourceStorageModeShared; +#endif + + _vb = vb; + _ib = ib; + + MTLAccelerationStructureTriangleGeometryDescriptor *descriptor = [MTLAccelerationStructureTriangleGeometryDescriptor descriptor]; + descriptor.indexType = MTLIndexTypeUInt32; + descriptor.indexBuffer = (__bridge id)ib->impl.metal_buffer; + descriptor.vertexBuffer = (__bridge id)vb->impl.mtlBuffer; + descriptor.vertexStride = vb->impl.myStride; + descriptor.triangleCount = ib->impl.count / 3; + descriptor.vertexFormat = MTLAttributeFormatShort4Normalized; + + MTLPrimitiveAccelerationStructureDescriptor *accel_descriptor = [MTLPrimitiveAccelerationStructureDescriptor descriptor]; + accel_descriptor.geometryDescriptors = @[ descriptor ]; + id acceleration_structure = create_acceleration_sctructure(accel_descriptor); + _primitive_accels = [[NSMutableArray alloc] init]; + [_primitive_accels addObject:acceleration_structure]; + + id instance_buffer = [device newBufferWithLength:sizeof(MTLAccelerationStructureInstanceDescriptor) * 1 options:options]; + + MTLAccelerationStructureInstanceDescriptor *instance_descriptors = (MTLAccelerationStructureInstanceDescriptor *)instance_buffer.contents; + instance_descriptors[0].accelerationStructureIndex = 0; + instance_descriptors[0].options = MTLAccelerationStructureInstanceOptionOpaque; + instance_descriptors[0].mask = 1; + instance_descriptors[0].transformationMatrix.columns[0] = MTLPackedFloat3Make(scale, 0, 0); + instance_descriptors[0].transformationMatrix.columns[1] = MTLPackedFloat3Make(0, scale, 0); + instance_descriptors[0].transformationMatrix.columns[2] = MTLPackedFloat3Make(0, 0, scale); + instance_descriptors[0].transformationMatrix.columns[3] = MTLPackedFloat3Make(0, 0, 0); + +#if !TARGET_OS_IPHONE + [instance_buffer didModifyRange:NSMakeRange(0, instance_buffer.length)]; +#endif + + MTLInstanceAccelerationStructureDescriptor *inst_accel_descriptor = [MTLInstanceAccelerationStructureDescriptor descriptor]; + inst_accel_descriptor.instancedAccelerationStructures = _primitive_accels; + inst_accel_descriptor.instanceCount = 1; + inst_accel_descriptor.instanceDescriptorBuffer = instance_buffer; + _instance_accel = create_acceleration_sctructure(inst_accel_descriptor); +} + +void kinc_raytrace_acceleration_structure_destroy(kinc_raytrace_acceleration_structure_t *accel) {} + +void kinc_raytrace_set_textures(kinc_g5_render_target_t *texpaint0, kinc_g5_render_target_t *texpaint1, kinc_g5_render_target_t *texpaint2, kinc_g5_texture_t *texenv, kinc_g5_texture_t *texsobol, kinc_g5_texture_t *texscramble, kinc_g5_texture_t *texrank) { + _texpaint0 = texpaint0; + _texpaint1 = texpaint1; + _texpaint2 = texpaint2; + _texenv = texenv; + _texsobol = texsobol; + _texscramble = texscramble; + _texrank = texrank; +} + +void kinc_raytrace_set_acceleration_structure(kinc_raytrace_acceleration_structure_t *_accel) { + accel = _accel; +} + +void kinc_raytrace_set_pipeline(kinc_raytrace_pipeline_t *_pipeline) { + pipeline = _pipeline; +} + +void kinc_raytrace_set_target(kinc_g5_render_target_t *_output) { + output = _output; +} + +void kinc_raytrace_dispatch_rays(kinc_g5_command_list_t *command_list) { + id device = getMetalDevice(); + if (!device.supportsRaytracing) return; + dispatch_semaphore_wait(_sem, DISPATCH_TIME_FOREVER); + + id queue = getMetalQueue(); + id command_buffer = [queue commandBuffer]; + __block dispatch_semaphore_t sem = _sem; + [command_buffer addCompletedHandler:^(id buffer) { + dispatch_semaphore_signal(sem); + }]; + + NSUInteger width = output->texWidth; + NSUInteger height = output->texHeight; + MTLSize threads_per_threadgroup = MTLSizeMake(8, 8, 1); + MTLSize threadgroups = MTLSizeMake((width + threads_per_threadgroup.width - 1) / threads_per_threadgroup.width, + (height + threads_per_threadgroup.height - 1) / threads_per_threadgroup.height, 1); + + id compute_encoder = [command_buffer computeCommandEncoder]; + [compute_encoder setBuffer:(__bridge id)constant_buf->impl._buffer offset:0 atIndex:0]; + [compute_encoder setAccelerationStructure:_instance_accel atBufferIndex:1]; + [compute_encoder setBuffer: (__bridge id)_ib->impl.metal_buffer offset:0 atIndex:2]; + [compute_encoder setBuffer: (__bridge id)_vb->impl.mtlBuffer offset:0 atIndex:3]; + [compute_encoder setTexture:(__bridge id)output->impl._tex atIndex:0]; + [compute_encoder setTexture:(__bridge id)_texpaint0->impl._tex atIndex:1]; + [compute_encoder setTexture:(__bridge id)_texpaint1->impl._tex atIndex:2]; + [compute_encoder setTexture:(__bridge id)_texpaint2->impl._tex atIndex:3]; + [compute_encoder setTexture:(__bridge id)_texenv->impl._tex atIndex:4]; + [compute_encoder setTexture:(__bridge id)_texsobol->impl._tex atIndex:5]; + [compute_encoder setTexture:(__bridge id)_texscramble->impl._tex atIndex:6]; + [compute_encoder setTexture:(__bridge id)_texrank->impl._tex atIndex:7]; + + for (id primitive_accel in _primitive_accels) + [compute_encoder useResource:primitive_accel usage:MTLResourceUsageRead]; + + [compute_encoder setComputePipelineState:_raytracing_pipeline]; + [compute_encoder dispatchThreadgroups:threadgroups threadsPerThreadgroup:threads_per_threadgroup]; + [compute_encoder endEncoding]; + [command_buffer commit]; +} + +void kinc_raytrace_copy(kinc_g5_command_list_t *command_list, kinc_g5_render_target_t *target, kinc_g5_texture_t *source) { + id queue = getMetalQueue(); + id command_buffer = [queue commandBuffer]; + id command_encoder = [command_buffer blitCommandEncoder]; + [command_encoder copyFromTexture:(__bridge id)source->impl._tex toTexture:(__bridge id)target->impl._tex]; +#ifndef KINC_APPLE_SOC + [command_encoder synchronizeResource:(__bridge id)target->impl._tex]; +#endif + [command_encoder endEncoding]; + [command_buffer commit]; + [command_buffer waitUntilCompleted]; +} diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/rendertarget.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/rendertarget.h new file mode 100644 index 000000000..a10fb89f7 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/rendertarget.h @@ -0,0 +1,7 @@ +#pragma once + +typedef struct { + void *_tex; + void *_texReadback; + void *_depthTex; +} RenderTarget5Impl; diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/rendertarget.m.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/rendertarget.m.h new file mode 100644 index 000000000..0049b8a6c --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/rendertarget.m.h @@ -0,0 +1,164 @@ +#include + +#include +#include + +#import + +id getMetalDevice(void); +id getMetalEncoder(void); + +static MTLPixelFormat convert_format(kinc_g5_render_target_format_t format) { + switch (format) { + case KINC_G5_RENDER_TARGET_FORMAT_128BIT_FLOAT: + return MTLPixelFormatRGBA32Float; + case KINC_G5_RENDER_TARGET_FORMAT_64BIT_FLOAT: + return MTLPixelFormatRGBA16Float; + case KINC_G5_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT: + return MTLPixelFormatR32Float; + case KINC_G5_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT: + return MTLPixelFormatR16Float; + case KINC_G5_RENDER_TARGET_FORMAT_8BIT_RED: + return MTLPixelFormatR8Unorm; + case KINC_G5_RENDER_TARGET_FORMAT_32BIT: + default: + return MTLPixelFormatBGRA8Unorm; + } +} + +static void render_target_init(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, int depthBufferBits, + int stencilBufferBits, int samples_per_pixel, int framebuffer_index) { + memset(target, 0, sizeof(kinc_g5_render_target_t)); + + target->texWidth = width; + target->texHeight = height; + + target->framebuffer_index = framebuffer_index; + + id device = getMetalDevice(); + + MTLTextureDescriptor *descriptor = [MTLTextureDescriptor new]; + descriptor.textureType = MTLTextureType2D; + descriptor.width = width; + descriptor.height = height; + descriptor.depth = 1; + descriptor.pixelFormat = convert_format(format); + descriptor.arrayLength = 1; + descriptor.mipmapLevelCount = 1; + descriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; + descriptor.resourceOptions = MTLResourceStorageModePrivate; + + target->impl._tex = (__bridge_retained void *)[device newTextureWithDescriptor:descriptor]; + + if (depthBufferBits > 0) { + MTLTextureDescriptor *depthDescriptor = [MTLTextureDescriptor new]; + depthDescriptor.textureType = MTLTextureType2D; + depthDescriptor.width = width; + depthDescriptor.height = height; + depthDescriptor.depth = 1; + depthDescriptor.pixelFormat = MTLPixelFormatDepth32Float_Stencil8; + depthDescriptor.arrayLength = 1; + depthDescriptor.mipmapLevelCount = 1; + depthDescriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead; + depthDescriptor.resourceOptions = MTLResourceStorageModePrivate; + + target->impl._depthTex = (__bridge_retained void *)[device newTextureWithDescriptor:depthDescriptor]; + } + + target->impl._texReadback = NULL; +} + +void kinc_g5_render_target_init_with_multisampling(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + render_target_init(target, width, height, format, depthBufferBits, stencilBufferBits, samples_per_pixel, -1); +} + +static int framebuffer_count = 0; + +void kinc_g5_render_target_init_framebuffer_with_multisampling(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + render_target_init(target, width, height, format, depthBufferBits, stencilBufferBits, samples_per_pixel, framebuffer_count); + framebuffer_count += 1; +} + +void kinc_g5_render_target_init_cube_with_multisampling(kinc_g5_render_target_t *target, int cubeMapSize, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + target->impl._tex = NULL; + target->impl._depthTex = NULL; + target->impl._texReadback = NULL; +} + +void kinc_g5_render_target_destroy(kinc_g5_render_target_t *target) { + id tex = (__bridge_transfer id)target->impl._tex; + tex = nil; + target->impl._tex = NULL; + + id depthTex = (__bridge_transfer id)target->impl._depthTex; + depthTex = nil; + target->impl._depthTex = NULL; + + id texReadback = (__bridge_transfer id)target->impl._texReadback; + texReadback = nil; + target->impl._texReadback = NULL; + + if (target->framebuffer_index >= 0) { + framebuffer_count -= 1; + } +} + +#if 0 +void kinc_g5_set_render_target_descriptor(kinc_g5_render_target_t *renderTarget, kinc_g5_texture_descriptor_t descriptor) { + MTLSamplerDescriptor* desc = (MTLSamplerDescriptor*) renderTarget->impl._samplerDesc; + switch(descriptor.filter_minification) { + case KINC_G5_TEXTURE_FILTER_POINT: + desc.minFilter = MTLSamplerMinMagFilterNearest; + break; + default: + desc.minFilter = MTLSamplerMinMagFilterLinear; + } + + switch(descriptor.filter_magnification) { + case KINC_G5_TEXTURE_FILTER_POINT: + desc.magFilter = MTLSamplerMinMagFilterNearest; + break; + default: + desc.minFilter = MTLSamplerMinMagFilterLinear; + } + + switch(descriptor.addressing_u) { + case KINC_G5_TEXTURE_ADDRESSING_REPEAT: + desc.sAddressMode = MTLSamplerAddressModeRepeat; + break; + case KINC_G5_TEXTURE_ADDRESSING_MIRROR: + desc.sAddressMode = MTLSamplerAddressModeMirrorRepeat; + break; + case KINC_G5_TEXTURE_ADDRESSING_CLAMP: + desc.sAddressMode = MTLSamplerAddressModeClampToEdge; + break; + case KINC_G5_TEXTURE_ADDRESSING_BORDER: + desc.sAddressMode = MTLSamplerAddressModeClampToBorderColor; + break; + } + + switch(descriptor.addressing_v) { + case KINC_G5_TEXTURE_ADDRESSING_REPEAT: + desc.tAddressMode = MTLSamplerAddressModeRepeat; + break; + case KINC_G5_TEXTURE_ADDRESSING_MIRROR: + desc.tAddressMode = MTLSamplerAddressModeMirrorRepeat; + break; + case KINC_G5_TEXTURE_ADDRESSING_CLAMP: + desc.tAddressMode = MTLSamplerAddressModeClampToEdge; + break; + case KINC_G5_TEXTURE_ADDRESSING_BORDER: + desc.tAddressMode = MTLSamplerAddressModeClampToBorderColor; + break; + } + id device = getMetalDevice(); + renderTarget->impl._sampler = [device newSamplerStateWithDescriptor:desc]; +} +#endif + +void kinc_g5_render_target_set_depth_stencil_from(kinc_g5_render_target_t *target, kinc_g5_render_target_t *source) { + target->impl._depthTex = source->impl._depthTex; +} diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/sampler.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/sampler.h new file mode 100644 index 000000000..3fce944e2 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/sampler.h @@ -0,0 +1,5 @@ +#pragma once + +typedef struct kinc_g5_sampler_impl { + void *sampler; +} kinc_g5_sampler_impl_t; diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/sampler.m.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/sampler.m.h new file mode 100644 index 000000000..ef5b5fcc5 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/sampler.m.h @@ -0,0 +1,67 @@ +#include + +static MTLSamplerAddressMode convert_addressing(kinc_g5_texture_addressing_t mode) { + switch (mode) { + case KINC_G5_TEXTURE_ADDRESSING_REPEAT: + return MTLSamplerAddressModeRepeat; + case KINC_G5_TEXTURE_ADDRESSING_BORDER: + return MTLSamplerAddressModeClampToBorderColor; + case KINC_G5_TEXTURE_ADDRESSING_CLAMP: + return MTLSamplerAddressModeClampToEdge; + case KINC_G5_TEXTURE_ADDRESSING_MIRROR: + return MTLSamplerAddressModeMirrorRepeat; + default: + assert(false); + return MTLSamplerAddressModeRepeat; + } +} + +static MTLSamplerMipFilter convert_mipmap_mode(kinc_g5_mipmap_filter_t filter) { + switch (filter) { + case KINC_G5_MIPMAP_FILTER_NONE: + return MTLSamplerMipFilterNotMipmapped; + case KINC_G5_MIPMAP_FILTER_POINT: + return MTLSamplerMipFilterNearest; + case KINC_G5_MIPMAP_FILTER_LINEAR: + return MTLSamplerMipFilterLinear; + default: + assert(false); + return MTLSamplerMipFilterNearest; + } +} + +static MTLSamplerMinMagFilter convert_texture_filter(kinc_g5_texture_filter_t filter) { + switch (filter) { + case KINC_G5_TEXTURE_FILTER_POINT: + return MTLSamplerMinMagFilterNearest; + case KINC_G5_TEXTURE_FILTER_LINEAR: + return MTLSamplerMinMagFilterLinear; + case KINC_G5_TEXTURE_FILTER_ANISOTROPIC: + return MTLSamplerMinMagFilterLinear; // ? + default: + assert(false); + return MTLSamplerMinMagFilterNearest; + } +} + +void kinc_g5_sampler_init(kinc_g5_sampler_t *sampler, const kinc_g5_sampler_options_t *options) { + id device = getMetalDevice(); + + MTLSamplerDescriptor *desc = (MTLSamplerDescriptor *)[[MTLSamplerDescriptor alloc] init]; + desc.minFilter = convert_texture_filter(options->minification_filter); + desc.magFilter = convert_texture_filter(options->magnification_filter); + desc.sAddressMode = convert_addressing(options->u_addressing); + desc.tAddressMode = convert_addressing(options->v_addressing); + desc.mipFilter = convert_mipmap_mode(options->mipmap_filter); + desc.maxAnisotropy = options->max_anisotropy; + desc.normalizedCoordinates = YES; + desc.lodMinClamp = options->lod_min_clamp; + desc.lodMaxClamp = options->lod_max_clamp; + + sampler->impl.sampler = (__bridge_retained void *)[device newSamplerStateWithDescriptor:desc]; +} + +void kinc_g5_sampler_destroy(kinc_g5_sampler_t *sampler) { + id mtl_sampler = (__bridge_transfer id)sampler->impl.sampler; + mtl_sampler = nil; +} diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/shader.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/shader.h new file mode 100644 index 000000000..0217ecee1 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/shader.h @@ -0,0 +1,6 @@ +#pragma once + +typedef struct { + char name[1024]; + void *mtlFunction; +} Shader5Impl; diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/shader.m.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/shader.m.h new file mode 100644 index 000000000..8680319f2 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/shader.m.h @@ -0,0 +1,56 @@ +#include +#include +#include +#include + +#include + +id getMetalDevice(void); +id getMetalLibrary(void); + +void kinc_g5_shader_destroy(kinc_g5_shader_t *shader) { + id function = (__bridge_transfer id)shader->impl.mtlFunction; + function = nil; + shader->impl.mtlFunction = NULL; +} + +void kinc_g5_shader_init(kinc_g5_shader_t *shader, const void *source, size_t length, kinc_g5_shader_type_t type) { + shader->impl.name[0] = 0; + + { + uint8_t *data = (uint8_t *)source; + if (length > 1 && data[0] == '>') { + memcpy(shader->impl.name, data + 1, length - 1); + shader->impl.name[length - 1] = 0; + } + else { + for (int i = 3; i < length; ++i) { + if (data[i] == '\n') { + shader->impl.name[i - 3] = 0; + break; + } + else { + shader->impl.name[i - 3] = data[i]; + } + } + } + } + + char *data = (char *)source; + id library = nil; + if (length > 1 && data[0] == '>') { + library = getMetalLibrary(); + } + else { + id device = getMetalDevice(); + NSError *error = nil; + library = [device newLibraryWithSource:[[NSString alloc] initWithBytes:data length:length encoding:NSUTF8StringEncoding] options:nil error:&error]; + if (library == nil) { + kinc_log(KINC_LOG_LEVEL_ERROR, "%s", error.localizedDescription.UTF8String); + } + } + shader->impl.mtlFunction = (__bridge_retained void *)[library newFunctionWithName:[NSString stringWithCString:shader->impl.name + encoding:NSUTF8StringEncoding]]; + + assert(shader->impl.mtlFunction); +} diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/texture.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/texture.h new file mode 100644 index 000000000..b68c4d769 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/texture.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct { + int index; + bool vertex; +} TextureUnit5Impl; + +typedef struct { + void *_tex; + void *data; + bool has_mipmaps; +} Texture5Impl; diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/texture.m.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/texture.m.h new file mode 100644 index 000000000..f9d32c5d6 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/texture.m.h @@ -0,0 +1,291 @@ +#include + +#include +#include +#include +#include + +#import + +id getMetalDevice(void); + +static MTLPixelFormat convert_image_format(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA32: + return MTLPixelFormatRGBA8Unorm; + case KINC_IMAGE_FORMAT_GREY8: + return MTLPixelFormatR8Unorm; + case KINC_IMAGE_FORMAT_RGB24: + return MTLPixelFormatRGBA8Unorm; + case KINC_IMAGE_FORMAT_RGBA128: + return MTLPixelFormatRGBA32Float; + case KINC_IMAGE_FORMAT_RGBA64: + return MTLPixelFormatRGBA16Float; + case KINC_IMAGE_FORMAT_A32: + return MTLPixelFormatR32Float; + case KINC_IMAGE_FORMAT_BGRA32: + return MTLPixelFormatBGRA8Unorm; + case KINC_IMAGE_FORMAT_A16: + return MTLPixelFormatR16Float; + } +} + +static int formatByteSize(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA128: + return 16; + case KINC_IMAGE_FORMAT_RGBA64: + return 8; + case KINC_IMAGE_FORMAT_RGB24: + return 4; + case KINC_IMAGE_FORMAT_A32: + return 4; + case KINC_IMAGE_FORMAT_A16: + return 2; + case KINC_IMAGE_FORMAT_GREY8: + return 1; + case KINC_IMAGE_FORMAT_BGRA32: + case KINC_IMAGE_FORMAT_RGBA32: + return 4; + default: + assert(false); + return 4; + } +} + +static void create(kinc_g5_texture_t *texture, int width, int height, int format, bool writable) { + texture->impl.has_mipmaps = false; + id device = getMetalDevice(); + + MTLTextureDescriptor *descriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:convert_image_format((kinc_image_format_t)format) + width:width + height:height + mipmapped:NO]; + descriptor.textureType = MTLTextureType2D; + descriptor.width = width; + descriptor.height = height; + descriptor.depth = 1; + descriptor.pixelFormat = convert_image_format((kinc_image_format_t)format); + descriptor.arrayLength = 1; + descriptor.mipmapLevelCount = 1; + // TODO: Make less textures writable + if (writable) { + descriptor.usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead; + } + + texture->impl._tex = (__bridge_retained void *)[device newTextureWithDescriptor:descriptor]; +} + +/*void Graphics5::Texture::_init(const char* format, bool readable) { + texWidth = width; + texHeight = height; + + create(width, height, Image::RGBA32, false); + lock(); + unlock(); +}*/ + +void kinc_g5_texture_init(kinc_g5_texture_t *texture, int width, int height, kinc_image_format_t format) { + // Image(width, height, format, readable); + texture->texWidth = width; + texture->texHeight = height; + texture->format = format; + texture->impl.data = malloc(width * height * (format == KINC_IMAGE_FORMAT_GREY8 ? 1 : 4)); + create(texture, width, height, format, true); +} + +void kinc_g5_texture_init3d(kinc_g5_texture_t *texture, int width, int height, int depth, kinc_image_format_t format) {} + +void kinc_g5_texture_init_from_image(kinc_g5_texture_t *texture, struct kinc_image *image) { + texture->texWidth = image->width; + texture->texHeight = image->height; + texture->format = image->format; + texture->impl.data = NULL; + create(texture, image->width, image->height, image->format, true); + id tex = (__bridge id)texture->impl._tex; + [tex replaceRegion:MTLRegionMake2D(0, 0, texture->texWidth, texture->texHeight) + mipmapLevel:0 + slice:0 + withBytes:image->data + bytesPerRow:kinc_g5_texture_stride(texture) + bytesPerImage:kinc_g5_texture_stride(texture) * texture->texHeight]; +} + +void kinc_g5_texture_init_non_sampled_access(kinc_g5_texture_t *texture, int width, int height, kinc_image_format_t format) { + texture->texWidth = width; + texture->texHeight = height; + texture->format = format; + texture->impl.data = malloc(width * height * (format == KINC_IMAGE_FORMAT_GREY8 ? 1 : 4)); + create(texture, width, height, format, true); +} + +void kinc_g5_texture_destroy(kinc_g5_texture_t *texture) { + id tex = (__bridge_transfer id)texture->impl._tex; + tex = nil; + texture->impl._tex = NULL; + + if (texture->impl.data != NULL) { + free(texture->impl.data); + texture->impl.data = NULL; + } +} + +id getMetalDevice(void); +id getMetalEncoder(void); + +#if 0 +void kinc_g5_internal_set_texture_descriptor(kinc_g5_texture_t *texture, kinc_g5_texture_descriptor_t descriptor) { + MTLSamplerDescriptor* desc = (MTLSamplerDescriptor*) texture->impl._samplerDesc; + switch(descriptor.filter_minification) { + case KINC_G5_TEXTURE_FILTER_POINT: + desc.minFilter = MTLSamplerMinMagFilterNearest; + break; + default: + desc.minFilter = MTLSamplerMinMagFilterLinear; + } + + switch(descriptor.filter_magnification) { + case KINC_G5_TEXTURE_FILTER_POINT: + desc.magFilter = MTLSamplerMinMagFilterNearest; + break; + default: + desc.minFilter = MTLSamplerMinMagFilterLinear; + } + + switch(descriptor.addressing_u) { + case KINC_G5_TEXTURE_ADDRESSING_REPEAT: + desc.sAddressMode = MTLSamplerAddressModeRepeat; + break; + case KINC_G5_TEXTURE_ADDRESSING_MIRROR: + desc.sAddressMode = MTLSamplerAddressModeMirrorRepeat; + break; + case KINC_G5_TEXTURE_ADDRESSING_CLAMP: + desc.sAddressMode = MTLSamplerAddressModeClampToEdge; + break; + case KINC_G5_TEXTURE_ADDRESSING_BORDER: + desc.sAddressMode = MTLSamplerAddressModeClampToBorderColor; + break; + } + + switch(descriptor.addressing_v) { + case KINC_G5_TEXTURE_ADDRESSING_REPEAT: + desc.tAddressMode = MTLSamplerAddressModeRepeat; + break; + case KINC_G5_TEXTURE_ADDRESSING_MIRROR: + desc.tAddressMode = MTLSamplerAddressModeMirrorRepeat; + break; + case KINC_G5_TEXTURE_ADDRESSING_CLAMP: + desc.tAddressMode = MTLSamplerAddressModeClampToEdge; + break; + case KINC_G5_TEXTURE_ADDRESSING_BORDER: + desc.tAddressMode = MTLSamplerAddressModeClampToBorderColor; + break; + } + id device = getMetalDevice(); + texture->impl._sampler = [device newSamplerStateWithDescriptor:desc]; +} +#endif + +int kinc_g5_texture_stride(kinc_g5_texture_t *texture) { + switch (texture->format) { + case KINC_IMAGE_FORMAT_GREY8: + return texture->texWidth; + case KINC_IMAGE_FORMAT_RGBA32: + case KINC_IMAGE_FORMAT_BGRA32: + case KINC_IMAGE_FORMAT_RGB24: + return texture->texWidth * 4; + case KINC_IMAGE_FORMAT_RGBA64: + return texture->texWidth * 8; + case KINC_IMAGE_FORMAT_RGBA128: + return texture->texWidth * 16; + case KINC_IMAGE_FORMAT_A16: + return texture->texWidth * 2; + case KINC_IMAGE_FORMAT_A32: + return texture->texWidth * 4; + } +} + +uint8_t *kinc_g5_texture_lock(kinc_g5_texture_t *texture) { + return (uint8_t *)texture->impl.data; +} + +void kinc_g5_texture_unlock(kinc_g5_texture_t *tex) { + id texture = (__bridge id)tex->impl._tex; + [texture replaceRegion:MTLRegionMake2D(0, 0, tex->texWidth, tex->texHeight) + mipmapLevel:0 + slice:0 + withBytes:tex->impl.data + bytesPerRow:kinc_g5_texture_stride(tex) + bytesPerImage:kinc_g5_texture_stride(tex) * tex->texHeight]; +} + +void kinc_g5_texture_clear(kinc_g5_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color) {} + +void kinc_g5_texture_generate_mipmaps(kinc_g5_texture_t *texture, int levels) {} + +void kinc_g5_texture_set_mipmap(kinc_g5_texture_t *texture, kinc_image_t *mipmap, int level) { + if (!texture->impl.has_mipmaps) { + id device = getMetalDevice(); + MTLTextureDescriptor *descriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:convert_image_format((kinc_image_format_t)texture->format) + width:texture->texWidth + height:texture->texHeight + mipmapped:YES]; + descriptor.textureType = MTLTextureType2D; + descriptor.width = texture->texWidth; + descriptor.height = texture->texHeight; + descriptor.depth = 1; + descriptor.pixelFormat = convert_image_format((kinc_image_format_t)texture->format); + descriptor.arrayLength = 1; + bool writable = true; + if (writable) { + descriptor.usage = MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead; + } + void *mipmaptex = (__bridge_retained void *)[device newTextureWithDescriptor:descriptor]; + + id commandQueue = getMetalQueue(); + id commandBuffer = [commandQueue commandBuffer]; + id commandEncoder = [commandBuffer blitCommandEncoder]; + [commandEncoder copyFromTexture:(__bridge id)texture->impl._tex + sourceSlice:0 + sourceLevel:0 + sourceOrigin:MTLOriginMake(0, 0, 0) + sourceSize:MTLSizeMake(texture->texWidth, texture->texHeight, 1) + toTexture:(__bridge id)mipmaptex + destinationSlice:0 + destinationLevel:0 + destinationOrigin:MTLOriginMake(0, 0, 0)]; +#ifndef KINC_APPLE_SOC + [commandEncoder synchronizeResource:(__bridge id)mipmaptex]; +#endif + [commandEncoder endEncoding]; + [commandBuffer commit]; + [commandBuffer waitUntilCompleted]; + + id tex = (__bridge_transfer id)texture->impl._tex; + tex = nil; + texture->impl._tex = mipmaptex; + + texture->impl.has_mipmaps = true; + } + + id tex = (__bridge id)texture->impl._tex; + [tex replaceRegion:MTLRegionMake2D(0, 0, mipmap->width, mipmap->height) + mipmapLevel:level + withBytes:mipmap->data + bytesPerRow:mipmap->width * formatByteSize(mipmap->format)]; +} + +#include + +#if defined(KINC_IOS) || defined(KINC_MACOS) +void kinc_g4_texture_upload(kinc_g4_texture_t *texture_g4, uint8_t *data, int stride) { + kinc_g5_texture_t *tex = &texture_g4->impl._texture; + id texture = (__bridge id)tex->impl._tex; + [texture replaceRegion:MTLRegionMake2D(0, 0, tex->texWidth, tex->texHeight) + mipmapLevel:0 + slice:0 + withBytes:data + bytesPerRow:stride + bytesPerImage:stride * tex->texHeight]; +} +#endif diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/vertexbuffer.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/vertexbuffer.h new file mode 100644 index 000000000..15e2a5d26 --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/vertexbuffer.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct { + // void unset(); + int myCount; + int myStride; + void *mtlBuffer; + bool gpuMemory; + int lastStart; + int lastCount; + // static Graphics5::VertexBuffer* current; +} VertexBuffer5Impl; diff --git a/armorcore/sources/backends/metal/kinc/backend/graphics5/vertexbuffer.m.h b/armorcore/sources/backends/metal/kinc/backend/graphics5/vertexbuffer.m.h new file mode 100644 index 000000000..d72e3933f --- /dev/null +++ b/armorcore/sources/backends/metal/kinc/backend/graphics5/vertexbuffer.m.h @@ -0,0 +1,111 @@ +#include +#include + +#include +#include +#include + +#import + +id getMetalDevice(void); +id getMetalEncoder(void); + +kinc_g5_vertex_buffer_t *currentVertexBuffer = NULL; + +static void vertex_buffer_unset(kinc_g5_vertex_buffer_t *buffer) { + if (currentVertexBuffer == buffer) + currentVertexBuffer = NULL; +} + +void kinc_g5_vertex_buffer_init(kinc_g5_vertex_buffer_t *buffer, int count, kinc_g5_vertex_structure_t *structure, bool gpuMemory, int instanceDataStepRate) { + memset(&buffer->impl, 0, sizeof(buffer->impl)); + buffer->impl.myCount = count; + buffer->impl.gpuMemory = gpuMemory; + for (int i = 0; i < structure->size; ++i) { + kinc_g5_vertex_element_t element = structure->elements[i]; + buffer->impl.myStride += kinc_g4_vertex_data_size(element.data); + } + + id device = getMetalDevice(); + MTLResourceOptions options = MTLResourceCPUCacheModeWriteCombined; +#ifdef KINC_APPLE_SOC + options |= MTLResourceStorageModeShared; +#else + if (gpuMemory) { + options |= MTLResourceStorageModeManaged; + } + else { + options |= MTLResourceStorageModeShared; + } +#endif + id buf = [device newBufferWithLength:count * buffer->impl.myStride options:options]; + buffer->impl.mtlBuffer = (__bridge_retained void *)buf; + + buffer->impl.lastStart = 0; + buffer->impl.lastCount = 0; +} + +void kinc_g5_vertex_buffer_destroy(kinc_g5_vertex_buffer_t *buf) { + id buffer = (__bridge_transfer id)buf->impl.mtlBuffer; + buffer = nil; + buf->impl.mtlBuffer = NULL; + vertex_buffer_unset(buf); +} + +float *kinc_g5_vertex_buffer_lock_all(kinc_g5_vertex_buffer_t *buf) { + buf->impl.lastStart = 0; + buf->impl.lastCount = kinc_g5_vertex_buffer_count(buf); + id buffer = (__bridge id)buf->impl.mtlBuffer; + float *floats = (float *)[buffer contents]; + return floats; +} + +float *kinc_g5_vertex_buffer_lock(kinc_g5_vertex_buffer_t *buf, int start, int count) { + buf->impl.lastStart = start; + buf->impl.lastCount = count; + id buffer = (__bridge id)buf->impl.mtlBuffer; + float *floats = (float *)[buffer contents]; + return &floats[start * buf->impl.myStride / sizeof(float)]; +} + +void kinc_g5_vertex_buffer_unlock_all(kinc_g5_vertex_buffer_t *buf) { +#ifndef KINC_APPLE_SOC + if (buf->impl.gpuMemory) { + id buffer = (__bridge id)buf->impl.mtlBuffer; + NSRange range; + range.location = buf->impl.lastStart * buf->impl.myStride; + range.length = buf->impl.lastCount * buf->impl.myStride; + [buffer didModifyRange:range]; + } +#endif +} + +void kinc_g5_vertex_buffer_unlock(kinc_g5_vertex_buffer_t *buf, int count) { +#ifndef KINC_APPLE_SOC + if (buf->impl.gpuMemory) { + id buffer = (__bridge id)buf->impl.mtlBuffer; + NSRange range; + range.location = buf->impl.lastStart * buf->impl.myStride; + range.length = count * buf->impl.myStride; + [buffer didModifyRange:range]; + } +#endif +} + +int kinc_g5_internal_vertex_buffer_set(kinc_g5_vertex_buffer_t *buf, int offset_) { + currentVertexBuffer = buf; + + id encoder = getMetalEncoder(); + id buffer = (__bridge id)buf->impl.mtlBuffer; + [encoder setVertexBuffer:buffer offset:offset_ * buf->impl.myStride atIndex:0]; + + return offset_; +} + +int kinc_g5_vertex_buffer_count(kinc_g5_vertex_buffer_t *buffer) { + return buffer->impl.myCount; +} + +int kinc_g5_vertex_buffer_stride(kinc_g5_vertex_buffer_t *buffer) { + return buffer->impl.myStride; +} diff --git a/armorcore/sources/backends/microsoft/kinc/backend/MiniWindows.h b/armorcore/sources/backends/microsoft/kinc/backend/MiniWindows.h new file mode 100644 index 000000000..e5c935808 --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/MiniWindows.h @@ -0,0 +1,102 @@ +#pragma once + +#ifdef _WIN64 +typedef __int64 INT_PTR; +typedef unsigned __int64 UINT_PTR; +typedef __int64 LONG_PTR; +typedef unsigned __int64 ULONG_PTR; +#else +typedef _W64 int INT_PTR; +typedef _W64 unsigned int UINT_PTR; +typedef _W64 long LONG_PTR; +typedef _W64 unsigned long ULONG_PTR; +#endif // WIN64 + +typedef unsigned long DWORD; +typedef DWORD *LPDWORD; +#define STD_OUTPUT_HANDLE ((DWORD)-11) +#define STD_ERROR_HANDLE ((DWORD)-12) +#define WINAPI __stdcall +typedef void *HWND; +typedef void *HANDLE; +typedef unsigned int UINT; +#define WINBASEAPI +typedef int BOOL; +#define CONST const +#define VOID void +typedef void *LPVOID; +typedef char CHAR; +typedef const CHAR *LPCSTR; +typedef wchar_t WCHAR; +typedef const WCHAR *LPCWSTR; +typedef CONST CHAR *LPCCH, *PCCH; +#define CP_UTF8 65001 +typedef wchar_t WCHAR; +typedef WCHAR *LPWSTR; +typedef void *PVOID; +typedef long LONG; +typedef LONG *PLONG; +typedef CONST void *LPCVOID; + +#define GENERIC_READ (0x80000000L) +#define GENERIC_WRITE (0x40000000L) + +#define FILE_SHARE_READ 0x00000001 + +#define CREATE_ALWAYS 2 +#define OPEN_EXISTING 3 + +#define FILE_ATTRIBUTE_NORMAL 0x00000080 +#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) +#define FILE_BEGIN 0 +#define FILE_CURRENT 1 +#define MAX_PATH 260 + +typedef struct _SECURITY_ATTRIBUTES { + DWORD nLength; + LPVOID lpSecurityDescriptor; + BOOL bInheritHandle; +} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES; + +typedef struct _OVERLAPPED { + ULONG_PTR Internal; + ULONG_PTR InternalHigh; + union { + struct { + DWORD Offset; + DWORD OffsetHigh; + } DUMMYSTRUCTNAME; + PVOID Pointer; + } DUMMYUNIONNAME; + + HANDLE hEvent; +} OVERLAPPED, *LPOVERLAPPED; + +WINBASEAPI BOOL WINAPI WriteConsoleA(HANDLE hConsoleOutput, CONST VOID *lpBuffer, DWORD nNumberOfCharsToWrite, LPDWORD lpNumberOfCharsWritten, + LPVOID lpReserved); + +WINBASEAPI BOOL WINAPI WriteConsoleW(HANDLE hConsoleOutput, CONST VOID *lpBuffer, DWORD nNumberOfCharsToWrite, LPDWORD lpNumberOfCharsWritten, + LPVOID lpReserved); + +WINBASEAPI VOID WINAPI OutputDebugStringA(LPCSTR lpOutputString); + +WINBASEAPI VOID WINAPI OutputDebugStringW(LPCWSTR lpOutputString); + +WINBASEAPI HANDLE WINAPI GetStdHandle(DWORD nStdHandle); + +int WINAPI MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCCH lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar); + +WINBASEAPI HANDLE WINAPI CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile); + +WINBASEAPI DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh); + +WINBASEAPI BOOL WINAPI ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped); + +WINBASEAPI DWORD WINAPI SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod); + +WINBASEAPI BOOL WINAPI CloseHandle(HANDLE hObject); + +WINBASEAPI BOOL WINAPI WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped); + +int WINAPI MessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType); diff --git a/armorcore/sources/backends/microsoft/kinc/backend/SystemMicrosoft.c.h b/armorcore/sources/backends/microsoft/kinc/backend/SystemMicrosoft.c.h new file mode 100644 index 000000000..f7efb1c24 --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/SystemMicrosoft.c.h @@ -0,0 +1,46 @@ +#include "SystemMicrosoft.h" + +#include +#include + +#define S_OK ((HRESULT)0L) + +static void winerror(HRESULT result) { + LPVOID buffer = NULL; + DWORD dw = GetLastError(); + + __debugbreak(); + +#if defined(KINC_WINDOWS) + if (dw != 0) { + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buffer, 0, NULL); + + kinc_error_message("Error: %s", buffer); + } + else { +#endif + kinc_error_message("Unknown Windows error, return value was 0x%x.", result); +#if defined(KINC_WINDOWS) + } +#endif +} + +void kinc_microsoft_affirm(HRESULT result) { + if (result != S_OK) { + winerror(result); + } +} + +void kinc_microsoft_affirm_message(HRESULT result, const char *format, ...) { + va_list args; + va_start(args, format); + kinc_affirm_args(result == S_OK, format, args); + va_end(args); +} + +void kinc_microsoft_format(const char *format, va_list args, wchar_t *buffer) { + char cbuffer[4096]; + vsprintf(cbuffer, format, args); + MultiByteToWideChar(CP_UTF8, 0, cbuffer, -1, buffer, 4096); +} diff --git a/armorcore/sources/backends/microsoft/kinc/backend/SystemMicrosoft.h b/armorcore/sources/backends/microsoft/kinc/backend/SystemMicrosoft.h new file mode 100644 index 000000000..fa2dfd51e --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/SystemMicrosoft.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef long HRESULT; + +void kinc_microsoft_affirm(HRESULT result); +void kinc_microsoft_affirm_message(HRESULT result, const char *format, ...); +void kinc_microsoft_format(const char *format, va_list args, wchar_t *buffer); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/microsoft/kinc/backend/atomic.h b/armorcore/sources/backends/microsoft/kinc/backend/atomic.h new file mode 100644 index 000000000..7d454e066 --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/atomic.h @@ -0,0 +1,53 @@ +#pragma once + +#ifndef _WIN64 +#include +#endif + +#include + +static inline bool kinc_atomic_compare_exchange(volatile int32_t *pointer, int32_t old_value, int32_t new_value) { + return _InterlockedCompareExchange((volatile long *)pointer, new_value, old_value) == old_value; +} + +#define KINC_ATOMIC_COMPARE_EXCHANGE(pointer, oldValue, newValue) (kinc_atomic_compare_exchange(pointer, oldValue, newValue)) + +static inline bool kinc_atomic_compare_exchange_pointer(void *volatile *pointer, void *old_value, void *new_value) { + return _InterlockedCompareExchangePointer(pointer, new_value, old_value) == old_value; +} + +#define KINC_ATOMIC_COMPARE_EXCHANGE_POINTER(pointer, oldValue, newValue) (kinc_atomic_compare_exchange_pointer(pointer, oldValue, newValue)) + +static inline int32_t kinc_atomic_increment(volatile int32_t *pointer) { + return _InterlockedIncrement((volatile long *)pointer) - 1; +} + +#define KINC_ATOMIC_INCREMENT(pointer) (kinc_atomic_increment(pointer)) + +static inline int32_t kinc_atomic_decrement(volatile int32_t *pointer) { + return _InterlockedDecrement((volatile long *)pointer) + 1; +} + +#define KINC_ATOMIC_DECREMENT(pointer) (kinc_atomic_decrement(pointer)) + +static inline void kinc_atomic_exchange(volatile int32_t *pointer, int32_t value) { + _InterlockedExchange((volatile long *)pointer, value); +} + +#define KINC_ATOMIC_EXCHANGE_32(pointer, value) (kinc_atomic_exchange(pointer, value)) + +static inline void kinc_atomic_exchange_float(volatile float *pointer, float value) { + _InterlockedExchange((volatile long *)pointer, *(long *)&value); +} + +#define KINC_ATOMIC_EXCHANGE_FLOAT(pointer, value) (kinc_atomic_exchange_float(pointer, value)) + +static inline void kinc_atomic_exchange_double(volatile double *pointer, double value) { +#ifdef _WIN64 + _InterlockedExchange64((volatile __int64 *)pointer, *(__int64 *)&value); +#else + kinc_error_message("kinc_atomic_exchange_double is not supported for 32 bit Windows builds"); +#endif +} + +#define KINC_ATOMIC_EXCHANGE_DOUBLE(pointer, value) (kinc_atomic_exchange_double(pointer, value)) diff --git a/armorcore/sources/backends/microsoft/kinc/backend/event.c.h b/armorcore/sources/backends/microsoft/kinc/backend/event.c.h new file mode 100644 index 000000000..932629d57 --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/event.c.h @@ -0,0 +1,25 @@ +#include + +void kinc_event_init(kinc_event_t *event, bool auto_clear) { + event->impl.event = CreateEvent(0, auto_clear ? FALSE : TRUE, 0, 0); +} + +void kinc_event_destroy(kinc_event_t *event) { + CloseHandle(event->impl.event); +} + +void kinc_event_signal(kinc_event_t *event) { + SetEvent(event->impl.event); +} + +void kinc_event_wait(kinc_event_t *event) { + WaitForSingleObject(event->impl.event, INFINITE); +} + +bool kinc_event_try_to_wait(kinc_event_t *event, double seconds) { + return WaitForSingleObject(event->impl.event, (DWORD)(seconds * 1000.0)) != WAIT_TIMEOUT; +} + +void kinc_event_reset(kinc_event_t *event) { + ResetEvent(event->impl.event); +} diff --git a/armorcore/sources/backends/microsoft/kinc/backend/event.h b/armorcore/sources/backends/microsoft/kinc/backend/event.h new file mode 100644 index 000000000..3718cb1e2 --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/event.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void *event; +} kinc_event_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/microsoft/kinc/backend/fiber.c.h b/armorcore/sources/backends/microsoft/kinc/backend/fiber.c.h new file mode 100644 index 000000000..14d88153e --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/fiber.c.h @@ -0,0 +1,25 @@ +#include + +VOID WINAPI fiber_func(LPVOID param) { + kinc_fiber_t *fiber = (kinc_fiber_t *)param; + fiber->impl.func(fiber->impl.param); +} + +void kinc_fiber_init_current_thread(kinc_fiber_t *fiber) { + fiber->impl.fiber = ConvertThreadToFiber(NULL); +} + +void kinc_fiber_init(kinc_fiber_t *fiber, void (*func)(void *param), void *param) { + fiber->impl.func = func; + fiber->impl.param = param; + fiber->impl.fiber = CreateFiber(0, fiber_func, fiber); +} + +void kinc_fiber_destroy(kinc_fiber_t *fiber) { + DeleteFiber(fiber->impl.fiber); + fiber->impl.fiber = NULL; +} + +void kinc_fiber_switch(kinc_fiber_t *fiber) { + SwitchToFiber(fiber->impl.fiber); +} diff --git a/armorcore/sources/backends/microsoft/kinc/backend/fiber.h b/armorcore/sources/backends/microsoft/kinc/backend/fiber.h new file mode 100644 index 000000000..d53c14b4f --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/fiber.h @@ -0,0 +1,15 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void *fiber; + void (*func)(void *param); + void *param; +} kinc_fiber_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/microsoft/kinc/backend/microsoftunit.c b/armorcore/sources/backends/microsoft/kinc/backend/microsoftunit.c new file mode 100644 index 000000000..295574074 --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/microsoftunit.c @@ -0,0 +1,56 @@ +// Windows XP +#define WINVER 0x0501 +#define _WIN32_WINNT 0x0501 + +#define NOATOM +#define NOCLIPBOARD +#define NOCOLOR +#define NOCOMM +#define NOCTLMGR +#define NODEFERWINDOWPOS +#define NODRAWTEXT +#define NOGDI +#define NOGDICAPMASKS +#define NOHELP +#define NOICONS +#define NOKANJI +#define NOKEYSTATES +#define NOMB +#define NOMCX +#define NOMEMMGR +#define NOMENUS +#define NOMETAFILE +#define NOMINMAX +#define NOMSG +//#define NONLS +#define NOOPENFILE +#define NOPROFILER +#define NORASTEROPS +#define NOSCROLL +#define NOSERVICE +#define NOSHOWWINDOW +#define NOSOUND +#define NOSYSCOMMANDS +#define NOSYSMETRICS +#define NOTEXTMETRIC +#define NOUSER +#define NOVIRTUALKEYCODES +#define NOWH +#define NOWINMESSAGES +#define NOWINOFFSETS +#define NOWINSTYLES +#define WIN32_LEAN_AND_MEAN + +#include + +#include +#include +#include + +#include "SystemMicrosoft.c.h" +#include "event.c.h" +#include "fiber.c.h" +#include "mutex.c.h" +#include "semaphore.c.h" +#include "thread.c.h" +#include "threadlocal.c.h" diff --git a/armorcore/sources/backends/microsoft/kinc/backend/mutex.c.h b/armorcore/sources/backends/microsoft/kinc/backend/mutex.c.h new file mode 100644 index 000000000..2913c71b3 --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/mutex.c.h @@ -0,0 +1,60 @@ +#include + +void kinc_mutex_init(kinc_mutex_t *mutex) { + assert(sizeof(RTL_CRITICAL_SECTION) == sizeof(kinc_microsoft_critical_section_t)); + InitializeCriticalSection((LPCRITICAL_SECTION)&mutex->impl.criticalSection); +} + +void kinc_mutex_destroy(kinc_mutex_t *mutex) { + DeleteCriticalSection((LPCRITICAL_SECTION)&mutex->impl.criticalSection); +} + +void kinc_mutex_lock(kinc_mutex_t *mutex) { + EnterCriticalSection((LPCRITICAL_SECTION)&mutex->impl.criticalSection); +} + +bool kinc_mutex_try_to_lock(kinc_mutex_t *mutex) { + return TryEnterCriticalSection((LPCRITICAL_SECTION)&mutex->impl.criticalSection); +} + +void kinc_mutex_unlock(kinc_mutex_t *mutex) { + LeaveCriticalSection((LPCRITICAL_SECTION)&mutex->impl.criticalSection); +} + +bool kinc_uber_mutex_init(kinc_uber_mutex_t *mutex, const char *name) { +#if defined(KINC_WINDOWS) + mutex->impl.id = (void *)CreateMutexA(NULL, FALSE, name); + HRESULT res = GetLastError(); + if (res && res != ERROR_ALREADY_EXISTS) { + mutex->impl.id = NULL; + assert(false); + return false; + } + return true; +#else + return false; +#endif +} + +void kinc_uber_mutex_destroy(kinc_uber_mutex_t *mutex) { +#if defined(KINC_WINDOWS) + if (mutex->impl.id) { + CloseHandle((HANDLE)mutex->impl.id); + mutex->impl.id = NULL; + } +#endif +} + +void kinc_uber_mutex_lock(kinc_uber_mutex_t *mutex) { +#if defined(KINC_WINDOWS) + bool succ = WaitForSingleObject((HANDLE)mutex->impl.id, INFINITE) == WAIT_FAILED ? false : true; + assert(succ); +#endif +} + +void kinc_uber_mutex_unlock(kinc_uber_mutex_t *mutex) { +#if defined(KINC_WINDOWS) + bool succ = ReleaseMutex((HANDLE)mutex->impl.id) == FALSE ? false : true; + assert(succ); +#endif +} diff --git a/armorcore/sources/backends/microsoft/kinc/backend/mutex.h b/armorcore/sources/backends/microsoft/kinc/backend/mutex.h new file mode 100644 index 000000000..4a971b8db --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/mutex.h @@ -0,0 +1,26 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void *DebugInfo; + long LockCount; + long RecursionCount; + void *OwningThread; + void *LockSemaphore; + unsigned long __w64 SpinCount; +} kinc_microsoft_critical_section_t; + +typedef struct { + kinc_microsoft_critical_section_t criticalSection; +} kinc_mutex_impl_t; + +typedef struct { + void *id; +} kinc_uber_mutex_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/microsoft/kinc/backend/semaphore.c.h b/armorcore/sources/backends/microsoft/kinc/backend/semaphore.c.h new file mode 100644 index 000000000..ed18033d0 --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/semaphore.c.h @@ -0,0 +1,22 @@ +#include + +void kinc_semaphore_init(kinc_semaphore_t *semaphore, int current, int max) { + semaphore->impl.handle = CreateSemaphoreA(NULL, current, max, NULL); +} + +void kinc_semaphore_destroy(kinc_semaphore_t *semaphore) { + CloseHandle(semaphore->impl.handle); + semaphore->impl.handle = NULL; +} + +void kinc_semaphore_release(kinc_semaphore_t *semaphore, int count) { + ReleaseSemaphore(semaphore->impl.handle, count, NULL); +} + +void kinc_semaphore_acquire(kinc_semaphore_t *semaphore) { + WaitForSingleObject(semaphore->impl.handle, INFINITE); +} + +bool kinc_semaphore_try_to_acquire(kinc_semaphore_t *semaphore, double seconds) { + return WaitForSingleObject(semaphore->impl.handle, (DWORD)(seconds * 1000)) == WAIT_OBJECT_0; +} diff --git a/armorcore/sources/backends/microsoft/kinc/backend/semaphore.h b/armorcore/sources/backends/microsoft/kinc/backend/semaphore.h new file mode 100644 index 000000000..d4c93ea0a --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/semaphore.h @@ -0,0 +1,5 @@ +#pragma once + +typedef struct { + void *handle; +} kinc_semaphore_impl_t; diff --git a/armorcore/sources/backends/microsoft/kinc/backend/thread.c.h b/armorcore/sources/backends/microsoft/kinc/backend/thread.c.h new file mode 100644 index 000000000..dbc84b6ab --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/thread.c.h @@ -0,0 +1,71 @@ +#include + +void kinc_threads_init() {} + +void kinc_threads_quit() {} + +struct thread_start { + void (*thread)(void *param); + void *param; +}; + +#define THREAD_STARTS 64 +static struct thread_start starts[THREAD_STARTS]; +static int thread_start_index = 0; + +static DWORD WINAPI ThreadProc(LPVOID arg) { + intptr_t start_index = (intptr_t)arg; + starts[start_index].thread(starts[start_index].param); + return 0; +} + +void kinc_thread_init(kinc_thread_t *thread, void (*func)(void *param), void *param) { + thread->impl.func = func; + thread->impl.param = param; + + intptr_t start_index = thread_start_index++; + if (thread_start_index >= THREAD_STARTS) { + thread_start_index = 0; + } + starts[start_index].thread = func; + starts[start_index].param = param; + thread->impl.handle = CreateThread(0, 65536, ThreadProc, (LPVOID)start_index, 0, 0); + assert(thread->impl.handle != NULL); +} + +void kinc_thread_wait_and_destroy(kinc_thread_t *thread) { + WaitForSingleObject(thread->impl.handle, INFINITE); + CloseHandle(thread->impl.handle); +} + +bool kinc_thread_try_to_destroy(kinc_thread_t *thread) { + DWORD code; + GetExitCodeThread(thread->impl.handle, &code); + if (code != STILL_ACTIVE) { + CloseHandle(thread->impl.handle); + return true; + } + return false; +} + +typedef HRESULT(WINAPI *SetThreadDescriptionType)(HANDLE hThread, PCWSTR lpThreadDescription); +static SetThreadDescriptionType MySetThreadDescription = NULL; +static bool set_thread_description_loaded = false; + +void kinc_thread_set_name(const char *name) { + if (!set_thread_description_loaded) { + HMODULE kernel32 = LoadLibraryA("kernel32.dll"); + MySetThreadDescription = (SetThreadDescriptionType)GetProcAddress(kernel32, "SetThreadDescription"); + set_thread_description_loaded = true; + } + + if (MySetThreadDescription != NULL) { + wchar_t wide_name[256]; + MultiByteToWideChar(CP_ACP, 0, name, -1, wide_name, 256); + MySetThreadDescription(GetCurrentThread(), wide_name); + } +} + +void kinc_thread_sleep(int milliseconds) { + Sleep(milliseconds); +} diff --git a/armorcore/sources/backends/microsoft/kinc/backend/thread.h b/armorcore/sources/backends/microsoft/kinc/backend/thread.h new file mode 100644 index 000000000..45833af87 --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/thread.h @@ -0,0 +1,15 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void *handle; + void *param; + void (*func)(void *param); +} kinc_thread_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/microsoft/kinc/backend/threadlocal.c.h b/armorcore/sources/backends/microsoft/kinc/backend/threadlocal.c.h new file mode 100644 index 000000000..9a74de1a6 --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/threadlocal.c.h @@ -0,0 +1,18 @@ +#include + +void kinc_thread_local_init(kinc_thread_local_t *local) { + local->impl.slot = TlsAlloc(); + TlsSetValue(local->impl.slot, 0); +} + +void kinc_thread_local_destroy(kinc_thread_local_t *local) { + TlsFree(local->impl.slot); +} + +void *kinc_thread_local_get(kinc_thread_local_t *local) { + return TlsGetValue(local->impl.slot); +} + +void kinc_thread_local_set(kinc_thread_local_t *local, void *data) { + TlsSetValue(local->impl.slot, data); +} diff --git a/armorcore/sources/backends/microsoft/kinc/backend/threadlocal.h b/armorcore/sources/backends/microsoft/kinc/backend/threadlocal.h new file mode 100644 index 000000000..31e73dd6b --- /dev/null +++ b/armorcore/sources/backends/microsoft/kinc/backend/threadlocal.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int slot; +} kinc_thread_local_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGL.c.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGL.c.h new file mode 100644 index 000000000..1e342fa30 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGL.c.h @@ -0,0 +1,1166 @@ +#include "OpenGL.h" +#include "ogl.h" +#ifdef KINC_EGL +#define EGL_NO_PLATFORM_SPECIFIC_TYPES +#include +#endif + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "OpenGLWindow.h" + +#ifdef KINC_WINDOWS +#include +#endif + +#include +#include +#include + +#ifdef KINC_IOS +#include +#endif + +#ifdef KINC_WINDOWS +#include + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include + +#pragma comment(lib, "opengl32.lib") +#pragma comment(lib, "glu32.lib") +#endif + +#ifndef GL_MAX_COLOR_ATTACHMENTS +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#endif +#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#endif +#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif +#ifndef GL_TEXTURE_COMPARE_MODE +#define GL_TEXTURE_COMPARE_MODE 0x884C +#endif +#ifndef GL_TEXTURE_COMPARE_FUNC +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#endif +#ifndef GL_COMPARE_REF_TO_TEXTURE +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#endif + +#if !defined(KINC_IOS) && !defined(KINC_ANDROID) +bool Kinc_Internal_ProgramUsesTessellation; +#endif +bool Kinc_Internal_SupportsConservativeRaster = false; +bool Kinc_Internal_SupportsDepthTexture = true; + +#if defined(KINC_OPENGL_ES) && defined(KINC_ANDROID) && KINC_ANDROID_API >= 18 +void *glesVertexAttribDivisor; + +GL_APICALL void (*GL_APIENTRY glesGenQueries)(GLsizei n, GLuint *ids); +GL_APICALL void (*GL_APIENTRY glesDeleteQueries)(GLsizei n, const GLuint *ids); +GL_APICALL void (*GL_APIENTRY glesBeginQuery)(GLenum target, GLuint id); +GL_APICALL void (*GL_APIENTRY glesEndQuery)(GLenum target); +GL_APICALL void (*GL_APIENTRY glesGetQueryObjectuiv)(GLuint id, GLenum pname, GLuint *params); +#endif + +#if defined(KINC_WINDOWS) && !defined(NDEBUG) +static void __stdcall debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) { + kinc_log(KINC_LOG_LEVEL_INFO, "OpenGL: %s", message); +} +#endif + +#ifdef KINC_WINDOWS +static HINSTANCE instance = 0; +#endif + +static int currentWindow = 0; + +static kinc_g4_texture_filter_t minFilters[32]; +static kinc_g4_mipmap_filter_t mipFilters[32]; + +static int _renderTargetWidth; +static int _renderTargetHeight; +static bool renderToBackbuffer; + +static int maxColorAttachments; + +static kinc_g4_pipeline_t *lastPipeline = NULL; + +#if defined(KINC_OPENGL_ES) && defined(KINC_ANDROID) && KINC_ANDROID_API >= 18 +static void *glesDrawBuffers; +static void *glesDrawElementsInstanced; +#endif + +static int texModesU[256]; +static int texModesV[256]; + +void kinc_internal_resize(int window, int width, int height) { +#ifdef KINC_WINDOWS + Kinc_Internal_resizeWindowRenderTarget(window, width, height); +#endif + glViewport(0, 0, width, height); +} + +void kinc_internal_change_framebuffer(int window, kinc_framebuffer_options_t *frame) { +#ifdef KINC_WINDOWS + if (window == 0) { + if (wglSwapIntervalEXT != NULL) + wglSwapIntervalEXT(frame->vertical_sync); + } +#endif +} + +#ifdef KINC_EGL +static EGLDisplay egl_display = EGL_NO_DISPLAY; +static EGLContext egl_context = EGL_NO_CONTEXT; +static EGLConfig egl_config = NULL; + +struct { + EGLSurface surface; +} kinc_egl_windows[16] = {0}; + +EGLDisplay kinc_egl_get_display(void); +EGLNativeWindowType kinc_egl_get_native_window(EGLDisplay, EGLConfig, int); +void kinc_egl_init(); +void kinc_egl_init_window(int window); +void kinc_egl_destroy_window(int window); +#endif + +#ifdef KINC_EGL +#define EGL_CHECK_ERROR() \ + { \ + EGLint error = eglGetError(); \ + if (error != EGL_SUCCESS) { \ + kinc_log(KINC_LOG_LEVEL_ERROR, "EGL Error at line %i: %i", __LINE__, error); \ + kinc_debug_break(); \ + exit(1); \ + } \ + } +#endif + +void kinc_g4_internal_destroy() { +#ifdef KINC_EGL + if (egl_display != EGL_NO_DISPLAY) { + eglMakeCurrent(egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + for (int i = 0; i < 16; i++) { + if (kinc_egl_windows[i].surface != EGL_NO_SURFACE) { + eglDestroySurface(egl_display, kinc_egl_windows[i].surface); + kinc_egl_windows[i].surface = EGL_NO_SURFACE; + } + } + if (egl_context != EGL_NO_CONTEXT) { + eglDestroyContext(egl_display, egl_context); + egl_context = EGL_NO_CONTEXT; + } + eglTerminate(egl_display); + } + + egl_display = EGL_NO_DISPLAY; +#endif +} + +void kinc_g4_internal_destroy_window(int window) { +#ifdef KINC_EGL + kinc_egl_destroy_window(window); +#endif +#ifdef KINC_WINDOWS + if (Kinc_Internal_windows[window].glContext) { + assert(wglMakeCurrent(NULL, NULL)); + assert(wglDeleteContext(Kinc_Internal_windows[window].glContext)); + Kinc_Internal_windows[window].glContext = NULL; + } + + HWND windowHandle = kinc_windows_window_handle(window); + + if (Kinc_Internal_windows[window].deviceContext != NULL) { + ReleaseDC(windowHandle, Kinc_Internal_windows[window].deviceContext); + Kinc_Internal_windows[window].deviceContext = NULL; + } +#endif +} + +#ifdef CreateWindow +#undef CreateWindow +#endif + +void kinc_g4_internal_init() { +#ifdef KINC_EGL +#if !defined(KINC_OPENGL_ES) + eglBindAPI(EGL_OPENGL_API); +#else + eglBindAPI(EGL_OPENGL_ES_API); +#endif + kinc_egl_init(); +#endif + + for (int i = 0; i < 32; ++i) { + minFilters[i] = KINC_G4_TEXTURE_FILTER_LINEAR; + mipFilters[i] = KINC_G4_MIPMAP_FILTER_NONE; + } + + for (int i = 0; i < 256; ++i) { + texModesU[i] = GL_CLAMP_TO_EDGE; + texModesV[i] = GL_CLAMP_TO_EDGE; + } + + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &kinc_internal_opengl_max_vertex_attribute_arrays); +} + +#ifdef KINC_EGL +EGLDisplay kinc_egl_get_display(void); +EGLNativeWindowType kinc_egl_get_native_window(EGLDisplay, EGLConfig, int); +#endif + +extern bool kinc_internal_opengl_force_16bit_index_buffer; + +#ifdef KINC_OPENGL_ES +int gles_version = 2; +#endif + +void kinc_g4_internal_init_window(int windowId, int depthBufferBits, int stencilBufferBits, bool vsync) { +#ifdef KINC_WINDOWS + Kinc_Internal_initWindowsGLContext(windowId, depthBufferBits, stencilBufferBits); +#endif +#ifdef KINC_EGL + kinc_egl_init_window(windowId); +#endif + +#ifdef KINC_WINDOWS + if (windowId == 0) { + if (wglSwapIntervalEXT != NULL) + wglSwapIntervalEXT(vsync); + } +#endif + + renderToBackbuffer = true; + +#if defined(KINC_OPENGL_ES) && defined(KINC_ANDROID) && KINC_ANDROID_API >= 18 + glesDrawBuffers = (void *)eglGetProcAddress("glDrawBuffers"); + glesDrawElementsInstanced = (void *)eglGetProcAddress("glDrawElementsInstanced"); + glesVertexAttribDivisor = (void *)eglGetProcAddress("glVertexAttribDivisor"); + + glesGenQueries = (void *)eglGetProcAddress("glGenQueries"); + glesDeleteQueries = (void *)eglGetProcAddress("glDeleteQueries"); + glesBeginQuery = (void *)eglGetProcAddress("glBeginQuery"); + glesEndQuery = (void *)eglGetProcAddress("glEndQuery"); + glesGetQueryObjectuiv = (void *)eglGetProcAddress("glGetQueryObjectuiv"); +#endif + +#if defined(KINC_WINDOWS) && !defined(NDEBUG) + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(debugCallback, NULL); +#endif + +#ifndef KINC_OPENGL_ES + int extensions = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &extensions); + if (glGetError() != GL_NO_ERROR) { + for (int i = 0; i < extensions; ++i) { + const char *extension = (const char *)glGetStringi(GL_EXTENSIONS, i); + if (extension != NULL && strcmp(extension, "GL_NV_conservative_raster") == 0) { + Kinc_Internal_SupportsConservativeRaster = true; + } + } + } + maxColorAttachments = 8; +#endif + +#ifdef KINC_OPENGL_ES + { + int major = -1; + glGetIntegerv(GL_MAJOR_VERSION, &major); + glCheckErrors(); + gles_version = major; + char *exts = (char *)glGetString(GL_EXTENSIONS); + + Kinc_Internal_SupportsDepthTexture = major >= 3 || (exts != NULL && strstr(exts, "GL_OES_depth_texture") != NULL); + maxColorAttachments = 4; + kinc_internal_opengl_force_16bit_index_buffer = major < 3 && strstr(exts, "GL_OES_element_index_uint") == NULL; + } +#else + kinc_internal_opengl_force_16bit_index_buffer = false; +#endif + + glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments); + + lastPipeline = NULL; + +#ifndef KINC_OPENGL_ES + int major = -1, minor = -1; + glGetIntegerv(GL_MAJOR_VERSION, &major); + glGetIntegerv(GL_MINOR_VERSION, &minor); + if (major < 0 || minor < 0) { + const GLubyte *version = glGetString(GL_VERSION); + if (version != NULL) { + major = version[0] - '0'; + } + else { + major = 2; + } + minor = 0; + } + int gl_version = major * 100 + minor * 10; +#endif + +#if defined(KINC_LINUX) || defined(KINC_MACOS) + if (gl_version >= 300) { + unsigned vertexArray; + glGenVertexArrays(1, &vertexArray); + glCheckErrors(); + glBindVertexArray(vertexArray); + glCheckErrors(); + } +#endif +} + +bool kinc_window_vsynced(int window) { +#ifdef KINC_WINDOWS + return wglGetSwapIntervalEXT(); +#else + return true; +#endif +} + +void kinc_g4_set_bool(kinc_g4_constant_location_t location, bool value) { + glUniform1i(location.impl.location, value ? 1 : 0); + glCheckErrors(); +} + +void kinc_g4_set_int(kinc_g4_constant_location_t location, int value) { + glUniform1i(location.impl.location, value); + glCheckErrors(); +} + +void kinc_g4_set_int2(kinc_g4_constant_location_t location, int value1, int value2) { + glUniform2i(location.impl.location, value1, value2); + glCheckErrors(); +} + +void kinc_g4_set_int3(kinc_g4_constant_location_t location, int value1, int value2, int value3) { + glUniform3i(location.impl.location, value1, value2, value3); + glCheckErrors(); +} + +void kinc_g4_set_int4(kinc_g4_constant_location_t location, int value1, int value2, int value3, int value4) { + glUniform4i(location.impl.location, value1, value2, value3, value4); + glCheckErrors(); +} + +void kinc_g4_set_ints(kinc_g4_constant_location_t location, int *values, int count) { + switch (location.impl.type) { + case GL_INT_VEC2: + glUniform2iv(location.impl.location, count / 2, values); + break; + case GL_INT_VEC3: + glUniform3iv(location.impl.location, count / 3, values); + break; + case GL_INT_VEC4: + glUniform4iv(location.impl.location, count / 4, values); + break; + default: + glUniform1iv(location.impl.location, count, values); + break; + } + glCheckErrors(); +} + +void kinc_g4_set_float(kinc_g4_constant_location_t location, float value) { + glUniform1f(location.impl.location, value); + glCheckErrors(); +} + +void kinc_g4_set_float2(kinc_g4_constant_location_t location, float value1, float value2) { + glUniform2f(location.impl.location, value1, value2); + glCheckErrors(); +} + +void kinc_g4_set_float3(kinc_g4_constant_location_t location, float value1, float value2, float value3) { + glUniform3f(location.impl.location, value1, value2, value3); + glCheckErrors(); +} + +void kinc_g4_set_float4(kinc_g4_constant_location_t location, float value1, float value2, float value3, float value4) { + glUniform4f(location.impl.location, value1, value2, value3, value4); + glCheckErrors(); +} + +void kinc_g4_set_floats(kinc_g4_constant_location_t location, float *values, int count) { + switch (location.impl.type) { + case GL_FLOAT_VEC2: + glUniform2fv(location.impl.location, count / 2, values); + break; + case GL_FLOAT_VEC3: + glUniform3fv(location.impl.location, count / 3, values); + break; + case GL_FLOAT_VEC4: + glUniform4fv(location.impl.location, count / 4, values); + break; + case GL_FLOAT_MAT4: + glUniformMatrix4fv(location.impl.location, count / 16, false, values); + break; + default: + glUniform1fv(location.impl.location, count, values); + break; + } + glCheckErrors(); +} + +void kinc_g4_set_matrix4(kinc_g4_constant_location_t location, kinc_matrix4x4_t *value) { + glUniformMatrix4fv(location.impl.location, 1, GL_FALSE, value->m); + glCheckErrors(); +} + +void kinc_g4_set_matrix3(kinc_g4_constant_location_t location, kinc_matrix3x3_t *value) { + glUniformMatrix3fv(location.impl.location, 1, GL_FALSE, value->m); + glCheckErrors(); +} + +kinc_g4_index_buffer_t *Kinc_Internal_CurrentIndexBuffer; + +void kinc_g4_draw_indexed_vertices() { + kinc_g4_draw_indexed_vertices_from_to(0, kinc_g4_index_buffer_count(Kinc_Internal_CurrentIndexBuffer)); +} + +void kinc_g4_draw_indexed_vertices_from_to(int start, int count) { + bool sixteen = Kinc_Internal_CurrentIndexBuffer->impl.format == KINC_G4_INDEX_BUFFER_FORMAT_16BIT || kinc_internal_opengl_force_16bit_index_buffer; + GLenum type = sixteen ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + void *_start = sixteen ? (void *)(start * sizeof(uint16_t)) : (void *)(start * sizeof(uint32_t)); + +#ifndef KINC_OPENGL_ES + if (Kinc_Internal_ProgramUsesTessellation) { + glDrawElements(GL_PATCHES, count, type, _start); + glCheckErrors(); + } + else { +#endif + glDrawElements(GL_TRIANGLES, count, type, _start); + glCheckErrors(); +#ifndef KINC_OPENGL_ES + } +#endif +} + +void kinc_g4_draw_indexed_vertices_from_to_from(int start, int count, int vertex_offset) { + bool sixteen = Kinc_Internal_CurrentIndexBuffer->impl.format == KINC_G4_INDEX_BUFFER_FORMAT_16BIT || kinc_internal_opengl_force_16bit_index_buffer; + GLenum type = sixteen ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + void *_start = sixteen ? (void *)(start * sizeof(uint16_t)) : (void *)(start * sizeof(uint32_t)); +#ifdef KINC_OPENGL_ES + glDrawElements(GL_TRIANGLES, count, type, _start); + glCheckErrors(); +#else + if (Kinc_Internal_ProgramUsesTessellation) { + glDrawElementsBaseVertex(GL_PATCHES, count, type, _start, vertex_offset); + glCheckErrors(); + } + else { + glDrawElementsBaseVertex(GL_TRIANGLES, count, type, _start, vertex_offset); + glCheckErrors(); + } +#endif +} + +void kinc_g4_draw_indexed_vertices_instanced(int instanceCount) { + kinc_g4_draw_indexed_vertices_instanced_from_to(instanceCount, 0, kinc_g4_index_buffer_count(Kinc_Internal_CurrentIndexBuffer)); +} + +void kinc_g4_draw_indexed_vertices_instanced_from_to(int instanceCount, int start, int count) { + bool sixteen = Kinc_Internal_CurrentIndexBuffer->impl.format == KINC_G4_INDEX_BUFFER_FORMAT_16BIT || kinc_internal_opengl_force_16bit_index_buffer; + GLenum type = sixteen ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + void *_start = sixteen ? (void *)(start * sizeof(uint16_t)) : (void *)(start * sizeof(uint32_t)); +#if defined(KINC_OPENGL_ES) && defined(KINC_ANDROID) && KINC_ANDROID_API >= 18 + ((void (*)(GLenum, GLsizei, GLenum, void *, GLsizei))glesDrawElementsInstanced)(GL_TRIANGLES, count, type, _start, instanceCount); +#elif defined(KINC_OPENGL_ES) && defined(KINC_WASM) + glDrawElementsInstanced(GL_TRIANGLES, count, type, _start, instanceCount); + glCheckErrors(); +#elif !defined(KINC_OPENGL_ES) + if (Kinc_Internal_ProgramUsesTessellation) { + glDrawElementsInstanced(GL_PATCHES, count, type, _start, instanceCount); + glCheckErrors(); + } + else { + glDrawElementsInstanced(GL_TRIANGLES, count, type, _start, instanceCount); + glCheckErrors(); + } +#endif +} + +#ifdef KINC_MACOS +void swapBuffersMac(int window); +#endif + +#ifdef KINC_IOS +void swapBuffersiOS(); +#endif + +#ifdef KINC_EGL +static EGLint egl_major = 0; +static EGLint egl_minor = 0; +static int egl_depth_size = 0; + +void kinc_egl_init() { + egl_display = kinc_egl_get_display(); + eglInitialize(egl_display, &egl_major, &egl_minor); + EGL_CHECK_ERROR() + + // clang-format off + const EGLint attribs[] = { + #if !defined(KINC_OPENGL_ES) + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + #else + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + #endif + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_DEPTH_SIZE, 24, + EGL_STENCIL_SIZE, 8, + EGL_NONE, + }; + // clang-format on + egl_depth_size = 24; + + EGLint num_configs = 0; + eglChooseConfig(egl_display, attribs, &egl_config, 1, &num_configs); + EGL_CHECK_ERROR() + + if (!num_configs) { + // clang-format off + const EGLint attribs[] = { + #if !defined(KINC_OPENGL_ES) + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + #else + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + #endif + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 8, + EGL_NONE, + }; + // clang-format on + eglChooseConfig(egl_display, attribs, &egl_config, 1, &num_configs); + EGL_CHECK_ERROR() + egl_depth_size = 16; + } + + if (!num_configs) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Unable to choose EGL config"); + } + +#if !defined(KINC_OPENGL_ES) + EGLint gl_versions[][2] = {{4, 6}, {4, 5}, {4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}, {3, 3}, {3, 2}, {3, 1}, {3, 0}, {2, 1}, {2, 0}}; + bool gl_initialized = false; + for (int i = 0; i < sizeof(gl_versions) / sizeof(EGLint) / 2; ++i) { + { + EGLint contextAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, + gl_versions[i][0], + EGL_CONTEXT_MINOR_VERSION, + gl_versions[i][1], + EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE, + EGL_TRUE, + EGL_NONE}; + egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, contextAttribs); + EGLint error = eglGetError(); + if (error == EGL_SUCCESS) { + gl_initialized = true; + kinc_log(KINC_LOG_LEVEL_INFO, "Using OpenGL version %i.%i (forward-compatible).", gl_versions[i][0], gl_versions[i][1]); + break; + } + } + + { + EGLint contextAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, gl_versions[i][0], EGL_CONTEXT_MINOR_VERSION, gl_versions[i][1], EGL_NONE}; + egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, contextAttribs); + EGLint error = eglGetError(); + if (error == EGL_SUCCESS) { + gl_initialized = true; + kinc_log(KINC_LOG_LEVEL_INFO, "Using OpenGL version %i.%i.", gl_versions[i][0], gl_versions[i][1]); + break; + } + } + } + + if (!gl_initialized) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not create OpenGL-context."); + exit(1); + } +#else + EGLint contextAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, 2, EGL_NONE}; + egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, contextAttribs); + EGL_CHECK_ERROR() +#endif +} + +int kinc_egl_width(int window) { + EGLint w = 0; + eglQuerySurface(egl_display, kinc_egl_windows[window].surface, EGL_WIDTH, &w); + return w; +} + +int kinc_egl_height(int window) { + EGLint h = 0; + eglQuerySurface(egl_display, kinc_egl_windows[window].surface, EGL_HEIGHT, &h); + return h; +} + +void kinc_egl_init_window(int window) { + EGLSurface egl_surface = eglCreateWindowSurface(egl_display, egl_config, kinc_egl_get_native_window(egl_display, egl_config, window), NULL); + EGL_CHECK_ERROR() + eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); + EGL_CHECK_ERROR() + kinc_egl_windows[window].surface = egl_surface; +} + +void kinc_egl_destroy_window(int window) { + eglMakeCurrent(egl_display, kinc_egl_windows[window].surface, kinc_egl_windows[window].surface, egl_context); + EGL_CHECK_ERROR() + eglDestroySurface(egl_display, kinc_egl_windows[window].surface); + EGL_CHECK_ERROR() + kinc_egl_windows[window].surface = NULL; +} +#endif + +bool kinc_g4_swap_buffers() { +#ifdef KINC_WINDOWS + for (int i = 9; i >= 0; --i) { + if (Kinc_Internal_windows[i].deviceContext != NULL) { + wglMakeCurrent(Kinc_Internal_windows[i].deviceContext, Kinc_Internal_windows[i].glContext); + if (i != 0) { + Kinc_Internal_blitWindowContent(i); + } + SwapBuffers(Kinc_Internal_windows[i].deviceContext); + } + } +#elif defined(KINC_EGL) + for (int window = 15; window >= 0; --window) { + if (kinc_egl_windows[window].surface) { + eglMakeCurrent(egl_display, kinc_egl_windows[window].surface, kinc_egl_windows[window].surface, egl_context); + EGL_CHECK_ERROR() + if (!eglSwapBuffers(egl_display, kinc_egl_windows[window].surface)) { + EGLint error = eglGetError(); + if (error == EGL_BAD_SURFACE) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Recreating surface."); + kinc_egl_init_window(window); + } + else if (error == EGL_CONTEXT_LOST || error == EGL_BAD_CONTEXT) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Context lost."); + return false; + } + } + EGL_CHECK_ERROR() + } + } +#elif defined(KINC_MACOS) + swapBuffersMac(0); +#elif defined(KINC_IOS) + swapBuffersiOS(); +#endif + return true; +} + +#ifdef KINC_IOS +int kinc_ios_gl_framebuffer = -1; + +void beginGL(); +#endif + +void kinc_g4_begin(int window) { + currentWindow = window; + +#ifdef KINC_EGL + eglMakeCurrent(egl_display, kinc_egl_windows[window].surface, kinc_egl_windows[window].surface, egl_context); + EGL_CHECK_ERROR() +#endif +#ifdef KINC_IOS + beginGL(); +#endif + kinc_g4_restore_render_target(); + glCheckErrors(); +#ifdef KINC_ANDROID + // if rendered to a texture, strange things happen if the backbuffer is not cleared + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +#endif +} + +void kinc_g4_viewport(int x, int y, int width, int height) { + glViewport(x, _renderTargetHeight - y - height, width, height); +} + +static bool scissor_on = false; + +void kinc_g4_scissor(int x, int y, int width, int height) { + scissor_on = true; + glEnable(GL_SCISSOR_TEST); + if (renderToBackbuffer) { + glScissor(x, _renderTargetHeight - y - height, width, height); + } + else { + glScissor(x, y, width, height); + } +} + +void kinc_g4_disable_scissor() { + scissor_on = false; + glDisable(GL_SCISSOR_TEST); +} + +void kinc_g4_end(int windowId) { + currentWindow = 0; + glCheckErrors(); +} + +void kinc_g4_clear(unsigned flags, unsigned color, float depth, int stencil) { + glColorMask(true, true, true, true); + glCheckErrors(); + glClearColor(((color & 0x00ff0000) >> 16) / 255.0f, ((color & 0x0000ff00) >> 8) / 255.0f, (color & 0x000000ff) / 255.0f, + ((color & 0xff000000) >> 24) / 255.0f); + glCheckErrors(); + if (flags & KINC_G4_CLEAR_DEPTH) { + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glCheckErrors(); + } +#ifdef KINC_OPENGL_ES + glClearDepthf(depth); +#else + glClearDepth(depth); +#endif + glCheckErrors(); + glStencilMask(0xff); + glCheckErrors(); + glClearStencil(stencil); + glCheckErrors(); + GLbitfield oglflags = ((flags & KINC_G4_CLEAR_COLOR) ? GL_COLOR_BUFFER_BIT : 0) | ((flags & KINC_G4_CLEAR_DEPTH) ? GL_DEPTH_BUFFER_BIT : 0) | + ((flags & KINC_G4_CLEAR_STENCIL) ? GL_STENCIL_BUFFER_BIT : 0); + + if (scissor_on) { + glDisable(GL_SCISSOR_TEST); + } + + glClear(oglflags); + glCheckErrors(); + + if (scissor_on) { + glEnable(GL_SCISSOR_TEST); + } + + if (lastPipeline != NULL) { + kinc_g4_set_pipeline(lastPipeline); + } +} + +void kinc_g4_set_vertex_buffers(kinc_g4_vertex_buffer_t **vertexBuffers, int count) { + int offset = 0; + for (int i = 0; i < count; ++i) { + offset += kinc_internal_g4_vertex_buffer_set(vertexBuffers[i], offset); + } +} + +void kinc_g4_set_index_buffer(kinc_g4_index_buffer_t *indexBuffer) { + kinc_internal_g4_index_buffer_set(indexBuffer); +} + +void Kinc_G4_Internal_TextureImageSet(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit); + +void Kinc_G4_Internal_TextureSet(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit); + +void kinc_g4_set_texture(kinc_g4_texture_unit_t unit, kinc_g4_texture_t *texture) { + Kinc_G4_Internal_TextureSet(texture, unit); +} + +void kinc_g4_set_image_texture(kinc_g4_texture_unit_t unit, kinc_g4_texture_t *texture) { + Kinc_G4_Internal_TextureImageSet(texture, unit); +} + +int kinc_g4_max_bound_textures(void) { + int units; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &units); + return units; +} + +static void setTextureAddressingInternal(GLenum target, kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) { + glActiveTexture(GL_TEXTURE0 + unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]); + GLenum texDir; + switch (dir) { + case KINC_G4_TEXTURE_DIRECTION_U: + texDir = GL_TEXTURE_WRAP_S; + break; + case KINC_G4_TEXTURE_DIRECTION_V: + texDir = GL_TEXTURE_WRAP_T; + break; + case KINC_G4_TEXTURE_DIRECTION_W: +#ifndef KINC_OPENGL_ES + texDir = GL_TEXTURE_WRAP_R; +#endif + break; + } + switch (addressing) { + case KINC_G4_TEXTURE_ADDRESSING_CLAMP: + glTexParameteri(target, texDir, GL_CLAMP_TO_EDGE); + if (dir == KINC_G4_TEXTURE_DIRECTION_U) { + texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_CLAMP_TO_EDGE; + } + else { + texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_CLAMP_TO_EDGE; + } + break; + case KINC_G4_TEXTURE_ADDRESSING_REPEAT: + glTexParameteri(target, texDir, GL_REPEAT); + if (dir == KINC_G4_TEXTURE_DIRECTION_U) { + texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_REPEAT; + } + else { + texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_REPEAT; + } + break; + case KINC_G4_TEXTURE_ADDRESSING_BORDER: + // unsupported + glTexParameteri(target, texDir, GL_CLAMP_TO_EDGE); + if (dir == KINC_G4_TEXTURE_DIRECTION_U) { + texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_CLAMP_TO_EDGE; + } + else { + texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_CLAMP_TO_EDGE; + } + break; + case KINC_G4_TEXTURE_ADDRESSING_MIRROR: + // unsupported + glTexParameteri(target, texDir, GL_REPEAT); + if (dir == KINC_G4_TEXTURE_DIRECTION_U) { + texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_REPEAT; + } + else { + texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_REPEAT; + } + break; + } + glCheckErrors(); +} + +int Kinc_G4_Internal_TextureAddressingU(kinc_g4_texture_unit_t unit) { + return texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]; +} + +int Kinc_G4_Internal_TextureAddressingV(kinc_g4_texture_unit_t unit) { + return texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]]; +} + +void kinc_g4_set_texture_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) { + setTextureAddressingInternal(GL_TEXTURE_2D, unit, dir, addressing); +} + +void kinc_g4_set_texture3d_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) { +#ifndef KINC_OPENGL_ES + setTextureAddressingInternal(GL_TEXTURE_3D, unit, dir, addressing); +#endif +} + +static void setTextureMagnificationFilterInternal(GLenum target, kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) { + glActiveTexture(GL_TEXTURE0 + texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]); + glCheckErrors(); + switch (filter) { + case KINC_G4_TEXTURE_FILTER_POINT: + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + break; + case KINC_G4_TEXTURE_FILTER_LINEAR: + case KINC_G4_TEXTURE_FILTER_ANISOTROPIC: + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + break; + } + glCheckErrors(); +} + +void kinc_g4_set_texture_magnification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) { + setTextureMagnificationFilterInternal(GL_TEXTURE_2D, texunit, filter); +} + +void kinc_g4_set_texture3d_magnification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) { +#ifndef KINC_OPENGL_ES + setTextureMagnificationFilterInternal(GL_TEXTURE_3D, texunit, filter); +#endif +} + +static void setMinMipFilters(GLenum target, int unit) { + glActiveTexture(GL_TEXTURE0 + unit); + glCheckErrors(); + switch (minFilters[unit]) { + case KINC_G4_TEXTURE_FILTER_POINT: + switch (mipFilters[unit]) { + case KINC_G4_MIPMAP_FILTER_NONE: + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + break; + case KINC_G4_MIPMAP_FILTER_POINT: + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); + break; + case KINC_G4_MIPMAP_FILTER_LINEAR: + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); + break; + } + break; + case KINC_G4_TEXTURE_FILTER_LINEAR: + case KINC_G4_TEXTURE_FILTER_ANISOTROPIC: + switch (mipFilters[unit]) { + case KINC_G4_MIPMAP_FILTER_NONE: + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + break; + case KINC_G4_MIPMAP_FILTER_POINT: + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + break; + case KINC_G4_MIPMAP_FILTER_LINEAR: + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + break; + } + if (minFilters[unit] == KINC_G4_TEXTURE_FILTER_ANISOTROPIC) { + float maxAniso = 0.0f; + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAniso); + glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAniso); + } + break; + } + glCheckErrors(); +} + +void kinc_g4_set_texture_minification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) { + minFilters[texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = filter; + setMinMipFilters(GL_TEXTURE_2D, texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]); +} + +void kinc_g4_set_texture3d_minification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) { + minFilters[texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = filter; +#ifndef KINC_OPENGL_ES + setMinMipFilters(GL_TEXTURE_3D, texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]); +#endif +} + +void kinc_g4_set_texture_mipmap_filter(kinc_g4_texture_unit_t texunit, kinc_g4_mipmap_filter_t filter) { + mipFilters[texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = filter; + setMinMipFilters(GL_TEXTURE_2D, texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]); +} + +void kinc_g4_set_texture3d_mipmap_filter(kinc_g4_texture_unit_t texunit, kinc_g4_mipmap_filter_t filter) { + mipFilters[texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = filter; +#ifndef KINC_OPENGL_ES + setMinMipFilters(GL_TEXTURE_3D, texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]); +#endif +} + +void kinc_g4_set_texture_compare_mode(kinc_g4_texture_unit_t texunit, bool enabled) { + if (texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) + return; + if (enabled) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + } + else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } +} + +void kinc_g4_set_texture_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) + return; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, Kinc_G4_Internal_StencilFunc(mode)); +} + +void kinc_g4_set_cubemap_compare_mode(kinc_g4_texture_unit_t texunit, bool enabled) { + if (texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) + return; + if (enabled) { + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); + } + else { + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } +} + +void kinc_g4_set_cubemap_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) + return; + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, Kinc_G4_Internal_StencilFunc(mode)); +} + +void kinc_g4_set_texture_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) + return; + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); +} + +void kinc_g4_set_cubemap_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy) { + if (unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] < 0) + return; + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); +} + +void kinc_g4_set_texture_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp) { + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, lod_min_clamp); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, lod_max_clamp); +} + +void kinc_g4_set_cubemap_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp) { + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_LOD, lod_min_clamp); + glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LOD, lod_max_clamp); +} + +void kinc_g4_set_render_targets(kinc_g4_render_target_t **targets, int count) { + glBindFramebuffer(GL_FRAMEBUFFER, targets[0]->impl._framebuffer); + glCheckErrors(); +#ifndef KINC_OPENGL_ES + if (targets[0]->isCubeMap) + glFramebufferTexture(GL_FRAMEBUFFER, targets[0]->isDepthAttachment ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0, targets[0]->impl._texture, + 0); // Layered +#endif + glViewport(0, 0, targets[0]->width, targets[0]->height); + _renderTargetWidth = targets[0]->width; + _renderTargetHeight = targets[0]->height; + renderToBackbuffer = false; + glCheckErrors(); + + if (count > 1) { + for (int i = 0; i < count; ++i) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, targets[i]->impl._texture, 0); + glCheckErrors(); + } + + GLenum buffers[16]; + for (int i = 0; i < count; ++i) + buffers[i] = GL_COLOR_ATTACHMENT0 + i; +#if defined(KINC_OPENGL_ES) && defined(KINC_ANDROID) && KINC_ANDROID_API >= 18 + ((void (*)(GLsizei, GLenum *))glesDrawBuffers)(count, buffers); +#elif !defined(KINC_OPENGL_ES) || defined(KINC_EMSCRIPTEN) || defined(KINC_WASM) + glDrawBuffers(count, buffers); +#endif + glCheckErrors(); + } + + for (int i = count; i < maxColorAttachments; ++i) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, 0, 0); + glCheckErrors(); + } +} + +void kinc_g4_set_render_target_face(kinc_g4_render_target_t *texture, int face) { + glBindFramebuffer(GL_FRAMEBUFFER, texture->impl._framebuffer); + glCheckErrors(); + glFramebufferTexture2D(GL_FRAMEBUFFER, texture->isDepthAttachment ? GL_DEPTH_ATTACHMENT : GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, + texture->impl._texture, 0); + glViewport(0, 0, texture->width, texture->height); + _renderTargetWidth = texture->width; + _renderTargetHeight = texture->height; + renderToBackbuffer = false; + glCheckErrors(); +} + +void kinc_g4_restore_render_target() { +#ifdef KINC_IOS + glBindFramebuffer(GL_FRAMEBUFFER, kinc_ios_gl_framebuffer); +#else + glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE); +#endif + glCheckErrors(); + int w = kinc_window_width(currentWindow); + int h = kinc_window_height(currentWindow); + glViewport(0, 0, w, h); + _renderTargetWidth = w; + _renderTargetHeight = h; + renderToBackbuffer = true; + glCheckErrors(); +#ifdef KINC_WINDOWS + Kinc_Internal_setWindowRenderTarget(currentWindow); +#endif +} + +void kinc_g4_flush() { + glFlush(); + glCheckErrors(); +} + +void kinc_g4_set_pipeline(kinc_g4_pipeline_t *pipeline) { + kinc_g4_internal_set_pipeline(pipeline); + lastPipeline = pipeline; +} + +void kinc_g4_set_blend_constant(float r, float g, float b, float a) { + glBlendColor(r, g, b, a); +} + +void kinc_g4_set_stencil_reference_value(int value) { + glStencilFuncSeparate(GL_FRONT, Kinc_G4_Internal_StencilFunc(lastPipeline->stencil_front_mode), value, lastPipeline->stencil_read_mask); + glStencilFuncSeparate(GL_BACK, Kinc_G4_Internal_StencilFunc(lastPipeline->stencil_back_mode), value, lastPipeline->stencil_read_mask); +} + +int Kinc_G4_Internal_StencilFunc(kinc_g4_compare_mode_t mode) { + switch (mode) { + case KINC_G4_COMPARE_ALWAYS: + return GL_ALWAYS; + case KINC_G4_COMPARE_EQUAL: + return GL_EQUAL; + case KINC_G4_COMPARE_GREATER: + return GL_GREATER; + case KINC_G4_COMPARE_GREATER_EQUAL: + return GL_GEQUAL; + case KINC_G4_COMPARE_LESS: + return GL_LESS; + case KINC_G4_COMPARE_LESS_EQUAL: + return GL_LEQUAL; + case KINC_G4_COMPARE_NEVER: + return GL_NEVER; + case KINC_G4_COMPARE_NOT_EQUAL: + return GL_NOTEQUAL; + } + + return 0; +} + +extern bool kinc_internal_gl_has_compute; + +bool kinc_g4_supports_instanced_rendering() { +#if defined(KINC_OPENGL_ES) && defined(KINC_ANDROID) +#if KINC_ANDROID_API >= 18 + return glesDrawElementsInstanced != NULL; +#else + return false; +#endif +#else + return true; +#endif +} + +bool kinc_g4_supports_compute_shaders() { + return kinc_internal_gl_has_compute; +} + +bool kinc_g4_supports_blend_constants() { + return true; +} + +bool kinc_g4_supports_non_pow2_textures() { + // we use OpenGL 2.0+, which should supports NPOT textures. + // in practice certain very old hardware doesn't, + // but detecting that is not practical + return true; +} + +bool kinc_g4_render_targets_inverted_y(void) { + return true; +} diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGL.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGL.h new file mode 100644 index 000000000..029ccae57 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGL.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int Kinc_G4_Internal_TextureAddressingU(kinc_g4_texture_unit_t unit); +int Kinc_G4_Internal_TextureAddressingV(kinc_g4_texture_unit_t unit); +int Kinc_G4_Internal_StencilFunc(kinc_g4_compare_mode_t mode); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGLWindow.c.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGLWindow.c.h new file mode 100644 index 000000000..33aae942f --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGLWindow.c.h @@ -0,0 +1,212 @@ +#ifdef KINC_WINDOWS +#include "OpenGLWindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ogl.h" + +#include + +Kinc_Internal_OpenGLWindow Kinc_Internal_windows[10] = {0}; + +static kinc_g4_vertex_buffer_t windowVertexBuffer; +static kinc_g4_index_buffer_t windowIndexBuffer; +static kinc_g4_pipeline_t windowPipeline; + +static bool initialized = false; +static kinc_g4_shader_t windowVertexShader; +static kinc_g4_shader_t windowFragmentShader; +static bool glewInitialized = false; + +void Kinc_Internal_initWindowsGLContext(int window, int depthBufferBits, int stencilBufferBits) { + HWND windowHandle = kinc_windows_window_handle(window); + + Kinc_Internal_windows[window].depthBufferBits = depthBufferBits; + + PIXELFORMATDESCRIPTOR pfd = {sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, + 32, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + (BYTE)depthBufferBits, + (BYTE)stencilBufferBits, + 0, + PFD_MAIN_PLANE, + 0, + 0, + 0, + 0}; + + Kinc_Internal_windows[window].deviceContext = GetDC(windowHandle); + GLuint pixelFormat = ChoosePixelFormat(Kinc_Internal_windows[window].deviceContext, &pfd); + SetPixelFormat(Kinc_Internal_windows[window].deviceContext, pixelFormat, &pfd); + HGLRC tempGlContext = wglCreateContext(Kinc_Internal_windows[window].deviceContext); + wglMakeCurrent(Kinc_Internal_windows[window].deviceContext, tempGlContext); + + if (!glewInitialized) { + glewInit(); + glewInitialized = true; + } + + if (wglewIsSupported("WGL_ARB_create_context") == 1) { + int attributes[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, + 4, + WGL_CONTEXT_MINOR_VERSION_ARB, + 2, + WGL_CONTEXT_FLAGS_ARB, + WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + WGL_CONTEXT_PROFILE_MASK_ARB, + WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + 0}; + + Kinc_Internal_windows[window].glContext = + wglCreateContextAttribsARB(Kinc_Internal_windows[window].deviceContext, Kinc_Internal_windows[0].glContext, attributes); + glCheckErrors(); + wglMakeCurrent(NULL, NULL); + wglDeleteContext(tempGlContext); + wglMakeCurrent(Kinc_Internal_windows[window].deviceContext, Kinc_Internal_windows[window].glContext); + glCheckErrors(); + } + else { + Kinc_Internal_windows[window].glContext = tempGlContext; + } + + if (window != 0) { + wglShareLists(Kinc_Internal_windows[0].glContext, Kinc_Internal_windows[window].glContext); + wglMakeCurrent(Kinc_Internal_windows[0].deviceContext, Kinc_Internal_windows[0].glContext); + kinc_g4_render_target_init(&Kinc_Internal_windows[window].renderTarget, kinc_windows_manual_width(window), kinc_windows_manual_height(window), + KINC_G4_RENDER_TARGET_FORMAT_32BIT, depthBufferBits, 0); + if (!initialized) { + wglMakeCurrent(Kinc_Internal_windows[window].deviceContext, Kinc_Internal_windows[window].glContext); + kinc_g4_vertex_structure_t structure; + kinc_g4_vertex_structure_init(&structure); + kinc_g4_vertex_structure_add(&structure, "pos", KINC_G4_VERTEX_DATA_F32_2X); + kinc_g4_vertex_buffer_init(&windowVertexBuffer, 4, &structure, KINC_G4_USAGE_STATIC, 0); + float *vertices = kinc_g4_vertex_buffer_lock_all(&windowVertexBuffer); + vertices[0] = -1.0f; + vertices[1] = -1.0f; + vertices[2] = -1.0f; + vertices[3] = 1.0f; + vertices[4] = 1.0f; + vertices[5] = 1.0f; + vertices[6] = 1.0f; + vertices[7] = -1.0f; + kinc_g4_vertex_buffer_unlock_all(&windowVertexBuffer); + + kinc_g4_index_buffer_init(&windowIndexBuffer, 6, KINC_G4_INDEX_BUFFER_FORMAT_32BIT, KINC_G4_USAGE_STATIC); + int *indices = kinc_g4_index_buffer_lock_all(&windowIndexBuffer); + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + indices[3] = 0; + indices[4] = 2; + indices[5] = 3; + kinc_g4_index_buffer_unlock_all(&windowIndexBuffer); + + char *vertex_shader = "#version 450\n" + "in vec2 pos;\n" + "out vec2 texCoord;\n" + "void main() {\n" + "gl_Position = vec4(pos, 0.5, 1.0);\n" + "texCoord = (pos + 1.0) / 2.0;\n" + "}\n"; + + kinc_g4_shader_init(&windowVertexShader, vertex_shader, strlen(vertex_shader), KINC_G4_SHADER_TYPE_VERTEX); + + char *fragment_shader = "#version 450\n" + "uniform sampler2D tex;\n" + "in vec2 texCoord;\n" + "out vec4 frag;\n" + "void main() {\n" + "frag = texture(tex, texCoord);\n" + "}\n"; + + kinc_g4_shader_init(&windowFragmentShader, fragment_shader, strlen(fragment_shader), KINC_G4_SHADER_TYPE_FRAGMENT); + + kinc_g4_pipeline_init(&windowPipeline); + windowPipeline.input_layout[0] = &structure; + windowPipeline.input_layout[1] = NULL; + windowPipeline.vertex_shader = &windowVertexShader; + windowPipeline.fragment_shader = &windowFragmentShader; + kinc_g4_pipeline_compile(&windowPipeline); + + wglMakeCurrent(Kinc_Internal_windows[0].deviceContext, Kinc_Internal_windows[0].glContext); + + initialized = true; + } + } + wglMakeCurrent(Kinc_Internal_windows[window].deviceContext, Kinc_Internal_windows[window].glContext); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &Kinc_Internal_windows[window].framebuffer); + + glGenVertexArrays(1, &Kinc_Internal_windows[window].vertexArray); + glCheckErrors(); + + wglMakeCurrent(Kinc_Internal_windows[0].deviceContext, Kinc_Internal_windows[0].glContext); + glBindVertexArray(Kinc_Internal_windows[0].vertexArray); + glCheckErrors(); +} + +void Kinc_Internal_blitWindowContent(int window) { + glBindFramebuffer(GL_FRAMEBUFFER, Kinc_Internal_windows[window].framebuffer); + kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0xff00ffff, 0.0f, 0); + kinc_g4_set_pipeline(&windowPipeline); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, Kinc_Internal_windows[window].renderTarget.impl._texture); + kinc_g4_set_index_buffer(&windowIndexBuffer); + + glBindVertexArray(Kinc_Internal_windows[window].vertexArray); + + glCheckErrors(); + kinc_g4_vertex_buffer_t *vertexBuffers[1] = {&windowVertexBuffer}; + kinc_g4_set_vertex_buffers(vertexBuffers, 1); + + glViewport(0, 0, kinc_window_width(window), kinc_window_height(window)); + + kinc_g4_draw_indexed_vertices(); + glCheckErrors(); + + glBindVertexArray(Kinc_Internal_windows[0].vertexArray); + + glCheckErrors(); +} + +void Kinc_Internal_resizeWindowRenderTarget(int window, int width, int height) { + if (window != 0) { + kinc_g4_render_target_destroy(&Kinc_Internal_windows[window].renderTarget); + kinc_g4_render_target_init(&Kinc_Internal_windows[window].renderTarget, width, height, KINC_G4_RENDER_TARGET_FORMAT_32BIT, + Kinc_Internal_windows[window].depthBufferBits, 0); + } +} + +void Kinc_Internal_setWindowRenderTarget(int window) { + if (window == 0) { + glBindFramebuffer(GL_FRAMEBUFFER, Kinc_Internal_windows[window].framebuffer); + } + else { + kinc_g4_render_target_t *renderTargets[1] = {&Kinc_Internal_windows[window].renderTarget}; + kinc_g4_set_render_targets(renderTargets, 1); + } +} +#endif diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGLWindow.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGLWindow.h new file mode 100644 index 000000000..909b2fb45 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/OpenGLWindow.h @@ -0,0 +1,34 @@ +#pragma once + +#ifdef KINC_WINDOWS +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct kinc_g4_render_target; + +typedef struct { + HDC deviceContext; + HGLRC glContext; + + int depthBufferBits; + + int framebuffer; + unsigned vertexArray; + struct kinc_g4_render_target renderTarget; +} Kinc_Internal_OpenGLWindow; + +extern Kinc_Internal_OpenGLWindow Kinc_Internal_windows[10]; + +void Kinc_Internal_initWindowsGLContext(int window, int depthBufferBits, int stencilBufferBits); +void Kinc_Internal_blitWindowContent(int window); +void Kinc_Internal_setWindowRenderTarget(int window); +void Kinc_Internal_resizeWindowRenderTarget(int window, int width, int height); +#ifdef __cplusplus +} +#endif +#endif diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/compute.c.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/compute.c.h new file mode 100644 index 000000000..d7f483189 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/compute.c.h @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#if defined(KINC_WINDOWS) || (defined(KINC_LINUX) && defined(GL_VERSION_4_3)) || (defined(KINC_ANDROID) && defined(GL_ES_VERSION_3_1)) +#define HAS_COMPUTE +bool kinc_internal_gl_has_compute = true; +#else +bool kinc_internal_gl_has_compute = false; +#endif + +#ifdef HAS_COMPUTE +static int convertInternalImageFormat(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA128: + return GL_RGBA32F; + case KINC_IMAGE_FORMAT_RGBA64: + return GL_RGBA16F; + case KINC_IMAGE_FORMAT_RGBA32: + default: + return GL_RGBA8; + case KINC_IMAGE_FORMAT_A32: + return GL_R32F; + case KINC_IMAGE_FORMAT_A16: + return GL_R16F; + case KINC_IMAGE_FORMAT_GREY8: + return GL_R8; + } +} + +static int convertInternalRTFormat(kinc_g4_render_target_format_t format) { + switch (format) { + case KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT: + return GL_RGBA16F; + case KINC_G4_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT: + return GL_R32F; + case KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT: + return GL_RGBA32F; + case KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH: + return GL_DEPTH_COMPONENT16; + case KINC_G4_RENDER_TARGET_FORMAT_8BIT_RED: + return GL_RED; + case KINC_G4_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT: + return GL_R16F; + case KINC_G4_RENDER_TARGET_FORMAT_32BIT: + default: + return GL_RGBA; + } +} +#endif + +void kinc_g4_compute_shader_init(kinc_g4_compute_shader *shader, void *source, int length) { + shader->impl._length = length; + shader->impl.textureCount = 0; + shader->impl._source = (char *)malloc(sizeof(char) * (length + 1)); + for (int i = 0; i < length; ++i) { + shader->impl._source[i] = ((char *)source)[i]; + } + shader->impl._source[length] = 0; + +#ifdef HAS_COMPUTE + shader->impl._id = glCreateShader(GL_COMPUTE_SHADER); + glCheckErrors(); + glShaderSource(shader->impl._id, 1, (const GLchar **)&shader->impl._source, NULL); + glCompileShader(shader->impl._id); + + int result; + glGetShaderiv(shader->impl._id, GL_COMPILE_STATUS, &result); + if (result != GL_TRUE) { + int length; + glGetShaderiv(shader->impl._id, GL_INFO_LOG_LENGTH, &length); + char *errormessage = (char *)malloc(sizeof(char) * length); + glGetShaderInfoLog(shader->impl._id, length, NULL, errormessage); + kinc_log(KINC_LOG_LEVEL_ERROR, "GLSL compiler error: %s\n", errormessage); + free(errormessage); + } + + shader->impl._programid = glCreateProgram(); + glAttachShader(shader->impl._programid, shader->impl._id); + glLinkProgram(shader->impl._programid); + + glGetProgramiv(shader->impl._programid, GL_LINK_STATUS, &result); + if (result != GL_TRUE) { + int length; + glGetProgramiv(shader->impl._programid, GL_INFO_LOG_LENGTH, &length); + char *errormessage = (char *)malloc(sizeof(char) * length); + glGetProgramInfoLog(shader->impl._programid, length, NULL, errormessage); + kinc_log(KINC_LOG_LEVEL_ERROR, "GLSL linker error: %s\n", errormessage); + free(errormessage); + } +#endif + + // TODO: Get rid of allocations + shader->impl.textures = (char **)malloc(sizeof(char *) * 16); + for (int i = 0; i < 16; ++i) { + shader->impl.textures[i] = (char *)malloc(sizeof(char) * 128); + shader->impl.textures[i][0] = 0; + } + shader->impl.textureValues = (int *)malloc(sizeof(int) * 16); +} + +void kinc_g4_compute_shader_destroy(kinc_g4_compute_shader *shader) { + free(shader->impl._source); + shader->impl._source = NULL; +#ifdef HAS_COMPUTE + glDeleteProgram(shader->impl._programid); + glDeleteShader(shader->impl._id); +#endif +} +kinc_g4_constant_location_t kinc_g4_compute_shader_get_constant_location(kinc_g4_compute_shader *shader, const char *name) { + kinc_g4_constant_location_t location; +#ifdef HAS_COMPUTE + location.impl.location = glGetUniformLocation(shader->impl._programid, name); + location.impl.type = GL_FLOAT; + GLint count = 0; + glGetProgramiv(shader->impl._programid, GL_ACTIVE_UNIFORMS, &count); + char arrayName[1024]; + strcpy(arrayName, name); + strcat(arrayName, "[0]"); + for (GLint i = 0; i < count; ++i) { + GLenum type; + char uniformName[1024]; + GLsizei length; + GLint size; + glGetActiveUniform(shader->impl._programid, i, 1024 - 1, &length, &size, &type, uniformName); + if (strcmp(uniformName, name) == 0 || strcmp(uniformName, arrayName) == 0) { + location.impl.type = type; + break; + } + } + glCheckErrors(); + if (location.impl.location < 0) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Uniform %s not found.", name); + } +#endif + return location; +} + +static int compute_findTexture(kinc_g4_compute_shader *shader, const char *name) { + for (int index = 0; index < shader->impl.textureCount; ++index) { + if (strcmp(shader->impl.textures[index], name) == 0) + return index; + } + return -1; +} + +kinc_g4_texture_unit_t kinc_g4_compute_shader_get_texture_unit(kinc_g4_compute_shader *shader, const char *name) { + int index = compute_findTexture(shader, name); + if (index < 0) { + int location = glGetUniformLocation(shader->impl._programid, name); + glCheckErrors(); + index = shader->impl.textureCount; + shader->impl.textureValues[index] = location; + strcpy(shader->impl.textures[index], name); + ++shader->impl.textureCount; + } + kinc_g4_texture_unit_t unit; + for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) { + unit.stages[i] = -1; + } + unit.stages[KINC_G4_SHADER_TYPE_COMPUTE] = index; + return unit; +} + +void kinc_g4_set_compute_shader(kinc_g4_compute_shader *shader) { +#ifdef HAS_COMPUTE + glUseProgram(shader->impl._programid); + glCheckErrors(); + + for (int index = 0; index < shader->impl.textureCount; ++index) { + glUniform1i(shader->impl.textureValues[index], index); + glCheckErrors(); + } +#endif +} + +void kinc_g4_compute(int x, int y, int z) { +#ifdef HAS_COMPUTE + glDispatchCompute(x, y, z); + glCheckErrors(); + glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); + glCheckErrors(); + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); + glCheckErrors(); +#endif +} diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/compute.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/compute.h new file mode 100644 index 000000000..32328f3fa --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/compute.h @@ -0,0 +1,20 @@ +#pragma once + +typedef struct kinc_g4_compute_constant_location_impl { + int location; + unsigned int type; +} kinc_g4_compute_constant_location_impl; + +typedef struct kinc_g4_compute_texture_unit_impl { + int unit; +} kinc_g4_compute_texture_unit_impl; + +typedef struct kinc_g4_compute_shader_impl { + char **textures; + int *textureValues; + int textureCount; + unsigned _id; + unsigned _programid; + char *_source; + int _length; +} kinc_g4_compute_shader_impl; diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/graphics.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/graphics.h new file mode 100644 index 000000000..53f1df391 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/graphics.h @@ -0,0 +1,6 @@ +#pragma once + +#include "IndexBufferImpl.h" +#include "RenderTargetImpl.h" +#include "TextureImpl.h" +#include "VertexBufferImpl.h" diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/indexbuffer.c.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/indexbuffer.c.h new file mode 100644 index 000000000..8b3b96839 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/indexbuffer.c.h @@ -0,0 +1,105 @@ +#include "ogl.h" + +#include + +#include + +kinc_g4_index_buffer_t *Kinc_Internal_CurrentIndexBuffer = NULL; + +void kinc_g4_index_buffer_init(kinc_g4_index_buffer_t *buffer, int count, kinc_g4_index_buffer_format_t format, kinc_g4_usage_t usage) { + buffer->impl.count = count; + buffer->impl.format = format; + + glGenBuffers(1, &buffer->impl.buffer_id); + glCheckErrors(); + if (format == KINC_G4_INDEX_BUFFER_FORMAT_32BIT) { + buffer->impl.data = malloc(count * sizeof(uint32_t)); + } + else { + buffer->impl.data = malloc(count * sizeof(uint16_t)); + } + + if (format == KINC_G4_INDEX_BUFFER_FORMAT_32BIT && kinc_internal_opengl_force_16bit_index_buffer) { + buffer->impl.converted_data = malloc(count * sizeof(uint16_t)); + } + else { + buffer->impl.converted_data = NULL; + } + + switch (usage) { + case KINC_G4_USAGE_STATIC: + buffer->impl.usage = GL_STATIC_DRAW; + break; + case KINC_G4_USAGE_DYNAMIC: + buffer->impl.usage = GL_DYNAMIC_DRAW; + break; + case KINC_G4_USAGE_READABLE: + buffer->impl.usage = GL_DYNAMIC_DRAW; + break; + } +} + +void Kinc_Internal_IndexBufferUnset(kinc_g4_index_buffer_t *buffer) { + if (Kinc_Internal_CurrentIndexBuffer == buffer) { + Kinc_Internal_CurrentIndexBuffer = NULL; + } +} + +void kinc_g4_index_buffer_destroy(kinc_g4_index_buffer_t *buffer) { + Kinc_Internal_IndexBufferUnset(buffer); + glDeleteBuffers(1, &buffer->impl.buffer_id); + free(buffer->impl.data); + buffer->impl.data = NULL; + free(buffer->impl.converted_data); + buffer->impl.converted_data = NULL; +} + +static int kinc_g4_internal_index_buffer_stride(kinc_g4_index_buffer_t *buffer) { + return buffer->impl.format == KINC_G4_INDEX_BUFFER_FORMAT_32BIT ? 4 : 2; +} + +void *kinc_g4_index_buffer_lock_all(kinc_g4_index_buffer_t *buffer) { + return kinc_g4_index_buffer_lock(buffer, 0, kinc_g4_index_buffer_count(buffer)); +} + +void *kinc_g4_index_buffer_lock(kinc_g4_index_buffer_t *buffer, int start, int count) { + uint8_t *data = (uint8_t *)buffer->impl.data; + return &data[start * kinc_g4_internal_index_buffer_stride(buffer)]; +} + +void kinc_g4_index_buffer_unlock_all(kinc_g4_index_buffer_t *buffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->impl.buffer_id); + glCheckErrors(); + + if (buffer->impl.format == KINC_G4_INDEX_BUFFER_FORMAT_32BIT && kinc_internal_opengl_force_16bit_index_buffer) { + uint32_t *data = (uint32_t *)buffer->impl.data; + for (int i = 0; i < buffer->impl.count; ++i) { + buffer->impl.converted_data[i] = (uint16_t)data[i]; + } + glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer->impl.count * sizeof(uint16_t), buffer->impl.converted_data, buffer->impl.usage); + glCheckErrors(); + } + else { + GLsizeiptr size = + buffer->impl.format == KINC_G4_INDEX_BUFFER_FORMAT_16BIT ? buffer->impl.count * sizeof(uint16_t) : buffer->impl.count * sizeof(uint32_t); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, buffer->impl.data, buffer->impl.usage); + glCheckErrors(); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glCheckErrors(); +} + +void kinc_g4_index_buffer_unlock(kinc_g4_index_buffer_t *buffer, int count) { + kinc_g4_index_buffer_unlock_all(buffer); +} + +void kinc_internal_g4_index_buffer_set(kinc_g4_index_buffer_t *buffer) { + Kinc_Internal_CurrentIndexBuffer = buffer; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer->impl.buffer_id); + glCheckErrors(); +} + +int kinc_g4_index_buffer_count(kinc_g4_index_buffer_t *buffer) { + return buffer->impl.count; +} diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/indexbuffer.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/indexbuffer.h new file mode 100644 index 000000000..50f60f0d1 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/indexbuffer.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint16_t *converted_data; + void *data; + int count; + unsigned usage; + int format; + unsigned buffer_id; +} kinc_g4_index_buffer_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/ogl.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/ogl.h new file mode 100644 index 000000000..3c0f6c259 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/ogl.h @@ -0,0 +1,66 @@ +#pragma once + +#ifdef KINC_WINDOWS +#include + +#include +#endif + +#ifdef KINC_MACOS +#include +#include +#endif + +#ifdef KINC_IOS +#import +#import +#import +#endif + +#ifdef KINC_ANDROID +#include +#if KINC_ANDROID_API >= 18 +#include +#endif +#include +#include +#endif + +#ifdef KINC_EMSCRIPTEN +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES +#include +#endif + +#ifdef KINC_LINUX +#define GL_GLEXT_PROTOTYPES +#include +#include +#endif + +#ifdef KINC_RASPBERRY_PI +// #define GL_GLEXT_PROTOTYPES +#include "GLES2/gl2.h" + +#include "EGL/egl.h" +#include "EGL/eglext.h" +#endif + +#ifdef KINC_WASM +#include +#endif + +#include + +#ifdef NDEBUG +#define glCheckErrors() \ + {} +#else +#define glCheckErrors() \ + { \ + GLenum code = glGetError(); \ + if (code != GL_NO_ERROR) { \ + kinc_log(KINC_LOG_LEVEL_ERROR, "GL Error %d %s %d\n", code, __FILE__, __LINE__); \ + } \ + } +#endif diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/openglunit.c b/armorcore/sources/backends/opengl/kinc/backend/graphics4/openglunit.c new file mode 100644 index 000000000..d46fd5f60 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/openglunit.c @@ -0,0 +1,14 @@ +#include + +static bool kinc_internal_opengl_force_16bit_index_buffer = false; +static int kinc_internal_opengl_max_vertex_attribute_arrays = 0; + +#include "OpenGL.c.h" +#include "OpenGLWindow.c.h" +#include "compute.c.h" +#include "indexbuffer.c.h" +#include "pipeline.c.h" +#include "rendertarget.c.h" +#include "shader.c.h" +#include "texture.c.h" +#include "vertexbuffer.c.h" diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/pipeline.c.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/pipeline.c.h new file mode 100644 index 000000000..0ab9f1832 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/pipeline.c.h @@ -0,0 +1,482 @@ +#include + +#include "ogl.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#ifndef KINC_OPENGL_ES +bool Kinc_Internal_ProgramUsesTessellation = false; +#endif +extern bool Kinc_Internal_SupportsConservativeRaster; + +static GLenum convertStencilAction(kinc_g4_stencil_action_t action) { + switch (action) { + default: + case KINC_G4_STENCIL_DECREMENT: + return GL_DECR; + case KINC_G4_STENCIL_DECREMENT_WRAP: + return GL_DECR_WRAP; + case KINC_G4_STENCIL_INCREMENT: + return GL_INCR; + case KINC_G4_STENCIL_INCREMENT_WRAP: + return GL_INCR_WRAP; + case KINC_G4_STENCIL_INVERT: + return GL_INVERT; + case KINC_G4_STENCIL_KEEP: + return GL_KEEP; + case KINC_G4_STENCIL_REPLACE: + return GL_REPLACE; + case KINC_G4_STENCIL_ZERO: + return GL_ZERO; + } +} + +static GLenum convert_blend_factor(kinc_g4_blending_factor_t factor) { + switch (factor) { + case KINC_G4_BLEND_ZERO: + return GL_ZERO; + case KINC_G4_BLEND_ONE: + return GL_ONE; + case KINC_G4_BLEND_SOURCE_ALPHA: + return GL_SRC_ALPHA; + case KINC_G4_BLEND_DEST_ALPHA: + return GL_DST_ALPHA; + case KINC_G4_BLEND_INV_SOURCE_ALPHA: + return GL_ONE_MINUS_SRC_ALPHA; + case KINC_G4_BLEND_INV_DEST_ALPHA: + return GL_ONE_MINUS_DST_ALPHA; + case KINC_G4_BLEND_SOURCE_COLOR: + return GL_SRC_COLOR; + case KINC_G4_BLEND_DEST_COLOR: + return GL_DST_COLOR; + case KINC_G4_BLEND_INV_SOURCE_COLOR: + return GL_ONE_MINUS_SRC_COLOR; + case KINC_G4_BLEND_INV_DEST_COLOR: + return GL_ONE_MINUS_DST_COLOR; + case KINC_G4_BLEND_CONSTANT: + return GL_CONSTANT_COLOR; + case KINC_G4_BLEND_INV_CONSTANT: + return GL_ONE_MINUS_CONSTANT_COLOR; + default: + assert(false); + return GL_ONE; + } +} + +static GLenum convert_blend_operation(kinc_g4_blending_operation_t operation) { + switch (operation) { + case KINC_G4_BLENDOP_ADD: + return GL_FUNC_ADD; + case KINC_G4_BLENDOP_SUBTRACT: + return GL_FUNC_SUBTRACT; + case KINC_G4_BLENDOP_REVERSE_SUBTRACT: + return GL_FUNC_REVERSE_SUBTRACT; + case KINC_G4_BLENDOP_MIN: + return GL_MIN; + case KINC_G4_BLENDOP_MAX: + return GL_MAX; + default: + assert(false); + return GL_FUNC_ADD; + } +} + +void kinc_g4_pipeline_init(kinc_g4_pipeline_t *state) { + memset(state, 0, sizeof(kinc_g4_pipeline_t)); + + kinc_g4_internal_pipeline_set_defaults(state); + + state->impl.textureCount = 0; + // TODO: Get rid of allocations + state->impl.textures = (char **)malloc(sizeof(char *) * 16); + for (int i = 0; i < 16; ++i) { + state->impl.textures[i] = (char *)malloc(sizeof(char) * 128); + state->impl.textures[i][0] = 0; + } + state->impl.textureValues = (int *)malloc(sizeof(int) * 16); + state->impl.programId = glCreateProgram(); + glCheckErrors(); +} + +void kinc_g4_pipeline_destroy(kinc_g4_pipeline_t *state) { + for (int i = 0; i < 16; ++i) { + free(state->impl.textures[i]); + } + free(state->impl.textures); + free(state->impl.textureValues); + glDeleteProgram(state->impl.programId); +} + +static int toGlShader(kinc_g4_shader_type_t type) { + switch (type) { + case KINC_G4_SHADER_TYPE_VERTEX: + default: + return GL_VERTEX_SHADER; + case KINC_G4_SHADER_TYPE_FRAGMENT: + return GL_FRAGMENT_SHADER; +#ifndef KINC_OPENGL_ES + case KINC_G4_SHADER_TYPE_GEOMETRY: + return GL_GEOMETRY_SHADER; + case KINC_G4_SHADER_TYPE_TESSELLATION_CONTROL: + return GL_TESS_CONTROL_SHADER; + case KINC_G4_SHADER_TYPE_TESSELLATION_EVALUATION: + return GL_TESS_EVALUATION_SHADER; +#endif + } +} + +static void compileShader(unsigned *id, const char *source, size_t length, kinc_g4_shader_type_t type) { + *id = glCreateShader(toGlShader(type)); + glCheckErrors(); + glShaderSource(*id, 1, (const GLchar **)&source, 0); + glCompileShader(*id); + + int result; + glGetShaderiv(*id, GL_COMPILE_STATUS, &result); + if (result != GL_TRUE) { + int length = 0; + glGetShaderiv(*id, GL_INFO_LOG_LENGTH, &length); + char *errormessage = (char *)malloc(length); + glGetShaderInfoLog(*id, length, NULL, errormessage); + kinc_log(KINC_LOG_LEVEL_ERROR, "GLSL compiler error: %s", errormessage); + free(errormessage); + } +} + +void kinc_g4_pipeline_compile(kinc_g4_pipeline_t *state) { + compileShader(&state->vertex_shader->impl._glid, state->vertex_shader->impl.source, state->vertex_shader->impl.length, KINC_G4_SHADER_TYPE_VERTEX); + compileShader(&state->fragment_shader->impl._glid, state->fragment_shader->impl.source, state->fragment_shader->impl.length, KINC_G4_SHADER_TYPE_FRAGMENT); +#ifndef KINC_OPENGL_ES + if (state->geometry_shader != NULL) { + compileShader(&state->geometry_shader->impl._glid, state->geometry_shader->impl.source, state->geometry_shader->impl.length, + KINC_G4_SHADER_TYPE_GEOMETRY); + } + if (state->tessellation_control_shader != NULL) { + compileShader(&state->tessellation_control_shader->impl._glid, state->tessellation_control_shader->impl.source, + state->tessellation_control_shader->impl.length, KINC_G4_SHADER_TYPE_TESSELLATION_CONTROL); + } + if (state->tessellation_evaluation_shader != NULL) { + compileShader(&state->tessellation_evaluation_shader->impl._glid, state->tessellation_evaluation_shader->impl.source, + state->tessellation_evaluation_shader->impl.length, KINC_G4_SHADER_TYPE_TESSELLATION_EVALUATION); + } +#endif + glAttachShader(state->impl.programId, state->vertex_shader->impl._glid); + glAttachShader(state->impl.programId, state->fragment_shader->impl._glid); +#ifndef KINC_OPENGL_ES + if (state->geometry_shader != NULL) { + glAttachShader(state->impl.programId, state->geometry_shader->impl._glid); + } + if (state->tessellation_control_shader != NULL) { + glAttachShader(state->impl.programId, state->tessellation_control_shader->impl._glid); + } + if (state->tessellation_evaluation_shader != NULL) { + glAttachShader(state->impl.programId, state->tessellation_evaluation_shader->impl._glid); + } +#endif + glCheckErrors(); + + int index = 0; + for (int i1 = 0; state->input_layout[i1] != NULL; ++i1) { + for (int i2 = 0; i2 < state->input_layout[i1]->size; ++i2) { + kinc_g4_vertex_element_t element = state->input_layout[i1]->elements[i2]; + glBindAttribLocation(state->impl.programId, index, element.name); + glCheckErrors(); + if (element.data == KINC_G4_VERTEX_DATA_F32_4X4) { + index += 4; + } + else { + ++index; + } + } + } + + glLinkProgram(state->impl.programId); + + int result; + glGetProgramiv(state->impl.programId, GL_LINK_STATUS, &result); + if (result != GL_TRUE) { + int length = 0; + glGetProgramiv(state->impl.programId, GL_INFO_LOG_LENGTH, &length); + char *errormessage = (char *)malloc(length); + glGetProgramInfoLog(state->impl.programId, length, NULL, errormessage); + kinc_log(KINC_LOG_LEVEL_ERROR, "GLSL linker error: %s", errormessage); + free(errormessage); + } + +#ifndef KINC_OPENGL_ES +#ifndef KINC_LINUX + if (state->tessellation_control_shader != NULL) { + glPatchParameteri(GL_PATCH_VERTICES, 3); + glCheckErrors(); + } +#endif +#endif +} + +void kinc_g4_internal_set_pipeline(kinc_g4_pipeline_t *pipeline) { +#ifndef KINC_OPENGL_ES + Kinc_Internal_ProgramUsesTessellation = pipeline->tessellation_control_shader != NULL; +#endif + glUseProgram(pipeline->impl.programId); + glCheckErrors(); + for (int index = 0; index < pipeline->impl.textureCount; ++index) { + glUniform1i(pipeline->impl.textureValues[index], index); + glCheckErrors(); + } + + if (pipeline->stencil_front_mode == KINC_G4_COMPARE_ALWAYS && pipeline->stencil_back_mode == KINC_G4_COMPARE_ALWAYS && + pipeline->stencil_front_both_pass == KINC_G4_STENCIL_KEEP && pipeline->stencil_back_both_pass == KINC_G4_STENCIL_KEEP && + pipeline->stencil_front_depth_fail == KINC_G4_STENCIL_KEEP && pipeline->stencil_back_depth_fail == KINC_G4_STENCIL_KEEP && + pipeline->stencil_front_fail == KINC_G4_STENCIL_KEEP && pipeline->stencil_back_fail == KINC_G4_STENCIL_KEEP) { + glDisable(GL_STENCIL_TEST); + } + else { + glEnable(GL_STENCIL_TEST); + + glStencilMaskSeparate(GL_FRONT, pipeline->stencil_write_mask); + glStencilOpSeparate(GL_FRONT, convertStencilAction(pipeline->stencil_front_fail), convertStencilAction(pipeline->stencil_front_depth_fail), + convertStencilAction(pipeline->stencil_front_both_pass)); + glStencilFuncSeparate(GL_FRONT, Kinc_G4_Internal_StencilFunc(pipeline->stencil_front_mode), pipeline->stencil_reference_value, + pipeline->stencil_read_mask); + + glStencilMaskSeparate(GL_BACK, pipeline->stencil_write_mask); + glStencilOpSeparate(GL_BACK, convertStencilAction(pipeline->stencil_back_fail), convertStencilAction(pipeline->stencil_back_depth_fail), + convertStencilAction(pipeline->stencil_back_both_pass)); + glStencilFuncSeparate(GL_BACK, Kinc_G4_Internal_StencilFunc(pipeline->stencil_back_mode), pipeline->stencil_reference_value, + pipeline->stencil_read_mask); + } + +#ifdef KINC_OPENGL_ES + glColorMask(pipeline->color_write_mask_red[0], pipeline->color_write_mask_green[0], pipeline->color_write_mask_blue[0], + pipeline->color_write_mask_alpha[0]); +#else + for (int i = 0; i < 8; ++i) + glColorMaski(i, pipeline->color_write_mask_red[i], pipeline->color_write_mask_green[i], pipeline->color_write_mask_blue[i], + pipeline->color_write_mask_alpha[i]); +#endif + + if (Kinc_Internal_SupportsConservativeRaster) { + if (pipeline->conservative_rasterization) { + glEnable(0x9346); // GL_CONSERVATIVE_RASTERIZATION_NV + } + else { + glDisable(0x9346); + } + } + + glCheckErrors(); + + /*switch (state) { + case Normalize: + device->SetRenderState(D3DRS_NORMALIZENORMALS, on ? TRUE : FALSE); + break; + case BackfaceCulling: + if (on) device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW); + else device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + break; + case FogState: + device->SetRenderState(D3DRS_FOGENABLE, on ? TRUE : FALSE); + break; + case ScissorTestState: + device->SetRenderState(D3DRS_SCISSORTESTENABLE, on ? TRUE : FALSE); + break; + case AlphaTestState: + device->SetRenderState(D3DRS_ALPHATESTENABLE, on ? TRUE : FALSE); + device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); + break; + default: + throw Exception(); + }*/ + + if (pipeline->depth_write) { + glDepthMask(GL_TRUE); + } + else { + glDepthMask(GL_FALSE); + } + + if (pipeline->depth_mode != KINC_G4_COMPARE_ALWAYS) { + glEnable(GL_DEPTH_TEST); + } + else { + glDisable(GL_DEPTH_TEST); + } + + GLenum func = GL_ALWAYS; + switch (pipeline->depth_mode) { + default: + case KINC_G4_COMPARE_ALWAYS: + func = GL_ALWAYS; + break; + case KINC_G4_COMPARE_NEVER: + func = GL_NEVER; + break; + case KINC_G4_COMPARE_EQUAL: + func = GL_EQUAL; + break; + case KINC_G4_COMPARE_NOT_EQUAL: + func = GL_NOTEQUAL; + break; + case KINC_G4_COMPARE_LESS: + func = GL_LESS; + break; + case KINC_G4_COMPARE_LESS_EQUAL: + func = GL_LEQUAL; + break; + case KINC_G4_COMPARE_GREATER: + func = GL_GREATER; + break; + case KINC_G4_COMPARE_GREATER_EQUAL: + func = GL_GEQUAL; + break; + } + glDepthFunc(func); + glCheckErrors(); + + switch (pipeline->cull_mode) { + case KINC_G4_CULL_CLOCKWISE: + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glCheckErrors(); + break; + case KINC_G4_CULL_COUNTER_CLOCKWISE: + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + glCheckErrors(); + break; + case KINC_G4_CULL_NOTHING: + glDisable(GL_CULL_FACE); + glCheckErrors(); + break; + default: + break; + } + + /*switch (state) { + case DepthTestCompare: + switch (v) { + // TODO: Cmp-Konstanten systemabhaengig abgleichen + default: + case ZCmp_Always : v = D3DCMP_ALWAYS; break; + case ZCmp_Never : v = D3DCMP_NEVER; break; + case ZCmp_Equal : v = D3DCMP_EQUAL; break; + case ZCmp_NotEqual : v = D3DCMP_NOTEQUAL; break; + case ZCmp_Less : v = D3DCMP_LESS; break; + case ZCmp_LessEqual : v = D3DCMP_LESSEQUAL; break; + case ZCmp_Greater : v = D3DCMP_GREATER; break; + case ZCmp_GreaterEqual: v = D3DCMP_GREATEREQUAL; break; + } + device->SetRenderState(D3DRS_ZFUNC, v); + break; + case FogTypeState: + switch (v) { + case LinearFog: + device->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_LINEAR); + } + break; + case AlphaReferenceState: + device->SetRenderState(D3DRS_ALPHAREF, (DWORD)v); + break; + default: + throw Exception(); + }*/ + + if (pipeline->blend_source != KINC_G4_BLEND_ONE || pipeline->blend_destination != KINC_G4_BLEND_ZERO || pipeline->alpha_blend_source != KINC_G4_BLEND_ONE || + pipeline->alpha_blend_destination != KINC_G4_BLEND_ZERO) { + glEnable(GL_BLEND); + } + else { + glDisable(GL_BLEND); + } + + // glBlendFunc(convert(pipeline->blendSource), convert(pipeline->blendDestination)); + glBlendFuncSeparate(convert_blend_factor(pipeline->blend_source), convert_blend_factor(pipeline->blend_destination), + convert_blend_factor(pipeline->alpha_blend_source), convert_blend_factor(pipeline->alpha_blend_destination)); + glBlendEquationSeparate(convert_blend_operation(pipeline->blend_operation), convert_blend_operation(pipeline->alpha_blend_operation)); +} + +void kinc_g4_pipeline_get_constant_locations(kinc_g4_pipeline_t *state, kinc_g4_constant_location_t *vertex_locations, + kinc_g4_constant_location_t *fragment_locations, int *vertex_sizes, int *fragment_sizes, int *max_vertex, + int *max_fragment) { + // GLint count = 0; + // glGetProgramiv(state->impl.programId, GL_ACTIVE_UNIFORMS, &count); + // if (locations == NULL || sizes == NULL) { + // *max_count = count; + // } + // else { + // for (GLint i = 0; i < count; ++i) { + // GLenum type; + // char uniformName[1024]; + // GLsizei length; + // GLint size; + // glGetActiveUniform(state->impl.programId, i, 1024 - 1, &length, &size, &type, uniformName); + // locations[i].impl.location = glGetUniformLocation(state->impl.programId, uniformName); + // locations[i].impl.type = type; + // sizes[i] = size; + // } + // } +} + +kinc_g4_constant_location_t kinc_g4_pipeline_get_constant_location(kinc_g4_pipeline_t *state, const char *name) { + kinc_g4_constant_location_t location; + location.impl.location = glGetUniformLocation(state->impl.programId, name); + location.impl.type = GL_FLOAT; + GLint count = 0; + glGetProgramiv(state->impl.programId, GL_ACTIVE_UNIFORMS, &count); + char arrayName[1024]; + strcpy(arrayName, name); + strcat(arrayName, "[0]"); + for (GLint i = 0; i < count; ++i) { + GLenum type; + char uniformName[1024]; + GLsizei length; + GLint size; + glGetActiveUniform(state->impl.programId, i, 1024 - 1, &length, &size, &type, uniformName); + if (strcmp(uniformName, name) == 0 || strcmp(uniformName, arrayName) == 0) { + location.impl.type = type; + break; + } + } + glCheckErrors(); + if (location.impl.location < 0) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Uniform %s not found.", name); + } + return location; +} + +static int findTexture(kinc_g4_pipeline_t *state, const char *name) { + for (int index = 0; index < state->impl.textureCount; ++index) { + if (strcmp(state->impl.textures[index], name) == 0) + return index; + } + return -1; +} + +kinc_g4_texture_unit_t kinc_g4_pipeline_get_texture_unit(kinc_g4_pipeline_t *state, const char *name) { + int index = findTexture(state, name); + if (index < 0) { + int location = glGetUniformLocation(state->impl.programId, name); + glCheckErrors(); + index = state->impl.textureCount; + state->impl.textureValues[index] = location; + strcpy(state->impl.textures[index], name); + ++state->impl.textureCount; + } + kinc_g4_texture_unit_t unit; + for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) { + unit.stages[i] = -1; + } + unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT] = index; + return unit; +} diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/pipeline.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/pipeline.h new file mode 100644 index 000000000..4fcca5b21 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/pipeline.h @@ -0,0 +1,16 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + unsigned programId; + char **textures; + int *textureValues; + int textureCount; +} kinc_g4_pipeline_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/rendertarget.c.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/rendertarget.c.h new file mode 100644 index 000000000..207701e89 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/rendertarget.c.h @@ -0,0 +1,412 @@ +#include + +#include "ogl.h" + +#include + +#include +#include +#include + +#ifndef GL_RGBA16F_EXT +#define GL_RGBA16F_EXT 0x881A +#endif + +#ifndef GL_RGBA32F_EXT +#define GL_RGBA32F_EXT 0x8814 +#endif + +#ifndef GL_R16F_EXT +#define GL_R16F_EXT 0x822D +#endif + +#ifndef GL_R32F_EXT +#define GL_R32F_EXT 0x822E +#endif + +#ifndef GL_HALF_FLOAT +#define GL_HALF_FLOAT 0x140B +#endif + +#ifndef GL_RED +#define GL_RED GL_LUMINANCE +#endif + +#ifndef GL_R8 +#define GL_R8 GL_RED +#endif + +extern bool Kinc_Internal_SupportsDepthTexture; + +static int pow2(int pow) { + int ret = 1; + for (int i = 0; i < pow; ++i) + ret *= 2; + return ret; +} + +static int getPower2(int i) { + for (int power = 0;; ++power) + if (pow2(power) >= i) + return pow2(power); +} + +#ifdef KINC_OPENGL_ES +extern int gles_version; +#endif + +bool kinc_opengl_internal_nonPow2RenderTargetsSupported() { +#ifdef KINC_OPENGL_ES + return gles_version >= 3; +#else + return true; +#endif +} + +static void setupDepthStencil(kinc_g4_render_target_t *renderTarget, GLenum texType, int depthBufferBits, int stencilBufferBits, int width, int height) { + if (depthBufferBits > 0 && stencilBufferBits > 0) { + renderTarget->impl._hasDepth = true; +#if defined(KINC_OPENGL_ES) && !defined(KINC_RASPBERRY_PI) && !defined(KINC_EMSCRIPTEN) + GLenum internalFormat = GL_DEPTH24_STENCIL8_OES; +#elif defined(KINC_OPENGL_ES) + GLenum internalFormat = 0x88F0; // GL_DEPTH24_STENCIL8_OES +#else + GLenum internalFormat; + if (depthBufferBits == 24) + internalFormat = GL_DEPTH24_STENCIL8; + else + internalFormat = GL_DEPTH32F_STENCIL8; +#endif + // Renderbuffer + // glGenRenderbuffers(1, &_depthRenderbuffer); + // glCheckErrors(); + // glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderbuffer); + // glCheckErrors(); + // glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, width, height); + // glCheckErrors(); + // #ifdef KINC_OPENGL_ES + // glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderbuffer); + // glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthRenderbuffer); + // #else + // glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthRenderbuffer); + // #endif + // glCheckErrors(); + // Texture + glGenTextures(1, &renderTarget->impl._depthTexture); + glCheckErrors(); + glBindTexture(texType, renderTarget->impl._depthTexture); + glCheckErrors(); + glTexImage2D(texType, 0, internalFormat, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0); + glCheckErrors(); + glTexParameteri(texType, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texType, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glCheckErrors(); + glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->impl._framebuffer); + glCheckErrors(); +#ifdef KINC_OPENGL_ES + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texType, renderTarget->impl._depthTexture, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, texType, renderTarget->impl._depthTexture, 0); +#else + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, texType, renderTarget->impl._depthTexture, 0); +#endif + glCheckErrors(); + } + else if (depthBufferBits > 0) { + renderTarget->impl._hasDepth = true; + if (!Kinc_Internal_SupportsDepthTexture) { + // Renderbuffer + glGenRenderbuffers(1, &renderTarget->impl._depthTexture); + glCheckErrors(); + glBindRenderbuffer(GL_RENDERBUFFER, renderTarget->impl._depthTexture); + glCheckErrors(); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); + glCheckErrors(); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderTarget->impl._depthTexture); + glCheckErrors(); + } + else { + // Texture + glGenTextures(1, &renderTarget->impl._depthTexture); + glCheckErrors(); + glBindTexture(texType, renderTarget->impl._depthTexture); + glCheckErrors(); +#if defined(KINC_EMSCRIPTEN) || defined(KINC_WASM) + GLint format = GL_DEPTH_COMPONENT16; +#else + GLint format = depthBufferBits == 16 ? GL_DEPTH_COMPONENT16 : GL_DEPTH_COMPONENT; +#endif + glTexImage2D(texType, 0, format, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0); + glCheckErrors(); + glTexParameteri(texType, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texType, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glCheckErrors(); + glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->impl._framebuffer); + glCheckErrors(); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texType, renderTarget->impl._depthTexture, 0); + glCheckErrors(); + } + } +} + +void kinc_g4_render_target_init_with_multisampling(kinc_g4_render_target_t *renderTarget, int width, int height, kinc_g4_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + renderTarget->width = width; + renderTarget->height = height; + renderTarget->isCubeMap = false; + renderTarget->isDepthAttachment = false; + + renderTarget->impl._hasDepth = false; + + if (kinc_opengl_internal_nonPow2RenderTargetsSupported()) { + renderTarget->texWidth = width; + renderTarget->texHeight = height; + } + else { + renderTarget->texWidth = getPower2(width); + renderTarget->texHeight = getPower2(height); + } + + renderTarget->impl.format = (int)format; + + glGenTextures(1, &renderTarget->impl._texture); + glCheckErrors(); + glBindTexture(GL_TEXTURE_2D, renderTarget->impl._texture); + glCheckErrors(); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glCheckErrors(); + + switch (format) { + case KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT: +#ifdef KINC_OPENGL_ES + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_EXT, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_FLOAT, 0); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_FLOAT, 0); +#endif + break; + case KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT: +#ifdef KINC_OPENGL_ES + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_EXT, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_HALF_FLOAT, 0); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_HALF_FLOAT, 0); +#endif + break; + case KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH: +#ifdef KINC_OPENGL_ES + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +#endif + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, renderTarget->texWidth, renderTarget->texHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0); + break; + case KINC_G4_RENDER_TARGET_FORMAT_8BIT_RED: +#ifdef KINC_IOS + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RED, GL_UNSIGNED_BYTE, 0); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RED, GL_UNSIGNED_BYTE, 0); +#endif + break; + case KINC_G4_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT: + glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F_EXT, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RED, GL_HALF_FLOAT, 0); + break; + case KINC_G4_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT: + glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F_EXT, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RED, GL_FLOAT, 0); + break; + case KINC_G4_RENDER_TARGET_FORMAT_32BIT: + default: + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + } + + glCheckErrors(); + glGenFramebuffers(1, &renderTarget->impl._framebuffer); + glCheckErrors(); + glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->impl._framebuffer); + glCheckErrors(); + + setupDepthStencil(renderTarget, GL_TEXTURE_2D, depthBufferBits, stencilBufferBits, renderTarget->texWidth, renderTarget->texHeight); + + if (format == KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH) { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, renderTarget->impl._texture, 0); +#ifndef KINC_OPENGL_ES + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); +#endif + } + else { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTarget->impl._texture, 0); + } + glCheckErrors(); + // GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 }; + // glDrawBuffers(1, drawBuffers); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glCheckErrors(); + glBindTexture(GL_TEXTURE_2D, 0); + glCheckErrors(); +} + +void kinc_g4_render_target_init_cube_with_multisampling(kinc_g4_render_target_t *renderTarget, int cubeMapSize, kinc_g4_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + renderTarget->width = cubeMapSize; + renderTarget->height = cubeMapSize; + renderTarget->isCubeMap = true; + renderTarget->isDepthAttachment = false; + + renderTarget->impl._hasDepth = false; + + if (kinc_opengl_internal_nonPow2RenderTargetsSupported()) { + renderTarget->texWidth = renderTarget->width; + renderTarget->texHeight = renderTarget->height; + } + else { + renderTarget->texWidth = getPower2(renderTarget->width); + renderTarget->texHeight = getPower2(renderTarget->height); + } + + renderTarget->impl.format = (int)format; + + glGenTextures(1, &renderTarget->impl._texture); + glCheckErrors(); + glBindTexture(GL_TEXTURE_CUBE_MAP, renderTarget->impl._texture); + glCheckErrors(); + + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glCheckErrors(); + + switch (format) { + case KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT: +#ifdef KINC_OPENGL_ES + for (int i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA32F_EXT, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_FLOAT, 0); +#else + for (int i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA32F, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_FLOAT, 0); +#endif + break; + case KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT: +#ifdef KINC_OPENGL_ES + for (int i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA16F_EXT, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_HALF_FLOAT, 0); +#else + for (int i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA16F, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_HALF_FLOAT, 0); +#endif + break; + case KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH: +#ifdef KINC_OPENGL_ES + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); +#endif + for (int i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT16, renderTarget->texWidth, renderTarget->texHeight, 0, GL_DEPTH_COMPONENT, + GL_UNSIGNED_INT, 0); + break; + case KINC_G4_RENDER_TARGET_FORMAT_32BIT: + default: + for (int i = 0; i < 6; i++) + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA, renderTarget->texWidth, renderTarget->texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + } + + glCheckErrors(); + glGenFramebuffers(1, &renderTarget->impl._framebuffer); + glCheckErrors(); + glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->impl._framebuffer); + glCheckErrors(); + + setupDepthStencil(renderTarget, GL_TEXTURE_CUBE_MAP, depthBufferBits, stencilBufferBits, renderTarget->texWidth, renderTarget->texHeight); + + if (format == KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH) { + renderTarget->isDepthAttachment = true; +#ifndef KINC_OPENGL_ES + glDrawBuffer(GL_NONE); + glCheckErrors(); + glReadBuffer(GL_NONE); + glCheckErrors(); +#endif + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glCheckErrors(); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + glCheckErrors(); +} + +void kinc_g4_render_target_destroy(kinc_g4_render_target_t *renderTarget) { + { + GLuint textures[] = {renderTarget->impl._texture}; + glDeleteTextures(1, textures); + } + if (renderTarget->impl._hasDepth) { + GLuint textures[] = {renderTarget->impl._depthTexture}; + glDeleteTextures(1, textures); + } + GLuint framebuffers[] = {renderTarget->impl._framebuffer}; + glDeleteFramebuffers(1, framebuffers); +} + +void kinc_g4_render_target_use_color_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit) { + glActiveTexture(GL_TEXTURE0 + unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]); + glCheckErrors(); + glBindTexture(renderTarget->isCubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, renderTarget->impl._texture); + glCheckErrors(); +} + +void kinc_g4_render_target_use_depth_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit) { + glActiveTexture(GL_TEXTURE0 + unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]); + glCheckErrors(); + glBindTexture(renderTarget->isCubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, renderTarget->impl._depthTexture); + glCheckErrors(); +} + +void kinc_g4_render_target_set_depth_stencil_from(kinc_g4_render_target_t *renderTarget, kinc_g4_render_target_t *source) { + renderTarget->impl._depthTexture = source->impl._depthTexture; + glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->impl._framebuffer); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, renderTarget->isCubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, renderTarget->impl._depthTexture, + 0); +} + +void kinc_g4_render_target_get_pixels(kinc_g4_render_target_t *renderTarget, uint8_t *data) { + glBindFramebuffer(GL_FRAMEBUFFER, renderTarget->impl._framebuffer); + switch ((kinc_g4_render_target_format_t)renderTarget->impl.format) { + case KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT: + glReadPixels(0, 0, renderTarget->texWidth, renderTarget->texHeight, GL_RGBA, GL_FLOAT, data); + break; + case KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT: + glReadPixels(0, 0, renderTarget->texWidth, renderTarget->texHeight, GL_RGBA, GL_HALF_FLOAT, data); + break; + case KINC_G4_RENDER_TARGET_FORMAT_8BIT_RED: + glReadPixels(0, 0, renderTarget->texWidth, renderTarget->texHeight, GL_RED, GL_UNSIGNED_BYTE, data); + break; + case KINC_G4_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT: + glReadPixels(0, 0, renderTarget->texWidth, renderTarget->texHeight, GL_RED, GL_HALF_FLOAT, data); + break; + case KINC_G4_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT: + glReadPixels(0, 0, renderTarget->texWidth, renderTarget->texHeight, GL_RED, GL_FLOAT, data); + break; + case KINC_G4_RENDER_TARGET_FORMAT_32BIT: + default: + glReadPixels(0, 0, renderTarget->texWidth, renderTarget->texHeight, GL_RGBA, GL_UNSIGNED_BYTE, data); + } +} + +void kinc_g4_render_target_generate_mipmaps(kinc_g4_render_target_t *renderTarget, int levels) { + glBindTexture(GL_TEXTURE_2D, renderTarget->impl._texture); + glCheckErrors(); + glGenerateMipmap(GL_TEXTURE_2D); + glCheckErrors(); +} diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/rendertarget.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/rendertarget.h new file mode 100644 index 000000000..adbd76c1c --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/rendertarget.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + unsigned _framebuffer; + unsigned _texture; + unsigned _depthTexture; + bool _hasDepth; + // unsigned _depthRenderbuffer; + int format; +} kinc_g4_render_target_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/shader.c.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/shader.c.h new file mode 100644 index 000000000..a689407de --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/shader.c.h @@ -0,0 +1,53 @@ +#include "ogl.h" + +#include + +#include +#include + +void kinc_g4_shader_init(kinc_g4_shader_t *shader, const void *data, size_t length, kinc_g4_shader_type_t type) { + shader->impl.length = length; + shader->impl._glid = 0; + char *source = (char *)malloc(length + 1); + memcpy(source, data, length); + source[length] = 0; + shader->impl.source = source; +} + +#ifdef KRAFIX_LIBRARY +extern int krafix_compile(const char *source, char *output, int *length, const char *targetlang, const char *system, const char *shadertype, int version); +#endif + +int kinc_g4_shader_init_from_source(kinc_g4_shader_t *shader, const char *source, kinc_g4_shader_type_t type) { +#ifdef KRAFIX_LIBRARY + char *output = malloc(1024 * 1024); + int length; +#ifdef KINC_WINDOWS + const char *system = "windows"; +#elif defined(KINC_MACOS) + const char *system = "macos"; +#elif defined(KINC_LINUX) + const char *system = "linux"; +#elif defined(KINC_ANDROID) + const char *system = "android"; +#elif defined(KINC_IOS) + const char *system = "ios"; +#endif + int errors = krafix_compile(source, output, &length, "glsl", system, type == KINC_G4_SHADER_TYPE_FRAGMENT ? "frag" : "vert", -1); + if (errors > 0) { + return errors; + } + kinc_g4_shader_init(shader, output, length, type); + return 0; +#else + return 0; +#endif +} + +void kinc_g4_shader_destroy(kinc_g4_shader_t *shader) { + free((void *)shader->impl.source); + shader->impl.source = NULL; + if (shader->impl._glid != 0) { + glDeleteShader(shader->impl._glid); + } +} diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/shader.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/shader.h new file mode 100644 index 000000000..138725a31 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/shader.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + unsigned _glid; + const char *source; + size_t length; +} kinc_g4_shader_impl_t; + +typedef struct { + int location; + unsigned type; +} kinc_g4_constant_location_impl_t; + +typedef struct { + int unit; +} kinc_g4_texture_unit_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/texture.c.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/texture.c.h new file mode 100644 index 000000000..539e7d3d7 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/texture.c.h @@ -0,0 +1,625 @@ +#include + +#include "ogl.h" + +#include +#include +#include +#include + +#include "OpenGL.h" + +#include + +#ifndef GL_TEXTURE_3D +#define GL_TEXTURE_3D 0x806F +#endif + +#ifndef GL_RGBA16F_EXT +#define GL_RGBA16F_EXT 0x881A +#endif + +#ifndef GL_RGBA32F_EXT +#define GL_RGBA32F_EXT 0x8814 +#endif + +#ifndef GL_R16F_EXT +#define GL_R16F_EXT 0x822D +#endif + +#ifndef GL_R32F_EXT +#define GL_R32F_EXT 0x822E +#endif + +#ifndef GL_HALF_FLOAT +#define GL_HALF_FLOAT 0x140B +#endif + +#ifndef GL_RED +#define GL_RED GL_LUMINANCE +#endif + +#ifndef GL_R8 +#define GL_R8 GL_RED +#endif + +#ifndef GL_RGBA8 +#define GL_RGBA8 GL_RGBA +#endif + +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_KHR_texture_compression_astc_ldr 1 + +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif + +static int convertFormat(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_BGRA32: +#ifdef GL_BGRA + return GL_BGRA; +#else + return GL_RGBA; +#endif + case KINC_IMAGE_FORMAT_RGBA32: + case KINC_IMAGE_FORMAT_RGBA64: + case KINC_IMAGE_FORMAT_RGBA128: + default: + return GL_RGBA; + case KINC_IMAGE_FORMAT_RGB24: + return GL_RGB; + case KINC_IMAGE_FORMAT_A32: + case KINC_IMAGE_FORMAT_A16: + case KINC_IMAGE_FORMAT_GREY8: + return GL_RED; + } +} + +static int convertInternalFormat(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA128: + return GL_RGBA32F_EXT; + case KINC_IMAGE_FORMAT_RGBA64: + return GL_RGBA16F_EXT; + case KINC_IMAGE_FORMAT_RGBA32: + return GL_RGBA8; + default: +#ifdef KINC_IOS + return GL_RGBA; +#else + // #ifdef GL_BGRA + // return GL_BGRA; + // #else + return GL_RGBA; +// #endif +#endif + case KINC_IMAGE_FORMAT_RGB24: + return GL_RGB; + case KINC_IMAGE_FORMAT_A32: + return GL_R32F_EXT; + case KINC_IMAGE_FORMAT_A16: + return GL_R16F_EXT; + case KINC_IMAGE_FORMAT_GREY8: +#ifdef KINC_IOS + return GL_RED; +#else + return GL_R8; +#endif + } +} + +static int convertType(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA128: + case KINC_IMAGE_FORMAT_RGBA64: + case KINC_IMAGE_FORMAT_A32: + case KINC_IMAGE_FORMAT_A16: + return GL_FLOAT; + case KINC_IMAGE_FORMAT_RGBA32: + default: + return GL_UNSIGNED_BYTE; + } +} + +static int astcFormat(uint8_t blockX, uint8_t blockY) { + switch (blockX) { + case 4: + switch (blockY) { + case 4: + return GL_COMPRESSED_RGBA_ASTC_4x4_KHR; + } + case 5: + switch (blockY) { + case 4: + return GL_COMPRESSED_RGBA_ASTC_5x4_KHR; + case 5: + return GL_COMPRESSED_RGBA_ASTC_5x5_KHR; + } + case 6: + switch (blockY) { + case 5: + return GL_COMPRESSED_RGBA_ASTC_6x5_KHR; + case 6: + return GL_COMPRESSED_RGBA_ASTC_6x6_KHR; + } + case 8: + switch (blockY) { + case 5: + return GL_COMPRESSED_RGBA_ASTC_8x5_KHR; + case 6: + return GL_COMPRESSED_RGBA_ASTC_8x6_KHR; + case 8: + return GL_COMPRESSED_RGBA_ASTC_8x8_KHR; + } + case 10: + switch (blockY) { + case 5: + return GL_COMPRESSED_RGBA_ASTC_10x5_KHR; + case 6: + return GL_COMPRESSED_RGBA_ASTC_10x6_KHR; + case 8: + return GL_COMPRESSED_RGBA_ASTC_10x8_KHR; + case 10: + return GL_COMPRESSED_RGBA_ASTC_10x10_KHR; + } + case 12: + switch (blockY) { + case 10: + return GL_COMPRESSED_RGBA_ASTC_12x10_KHR; + case 12: + return GL_COMPRESSED_RGBA_ASTC_12x12_KHR; + } + } + return 0; +} + +/*static int pow2(int pow) { + int ret = 1; + for (int i = 0; i < pow; ++i) ret *= 2; + return ret; +} + +static int getPower2(int i) { + for (int power = 0;; ++power) + if (pow2(power) >= i) return pow2(power); +}*/ + +static void convertImageToPow2(kinc_image_format_t format, uint8_t *from, int fw, int fh, uint8_t *to, int tw, int th) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA32: + for (int y = 0; y < th; ++y) { + for (int x = 0; x < tw; ++x) { + to[tw * 4 * y + x * 4 + 0] = 0; + to[tw * 4 * y + x * 4 + 1] = 0; + to[tw * 4 * y + x * 4 + 2] = 0; + to[tw * 4 * y + x * 4 + 3] = 0; + } + } + for (int y = 0; y < fh; ++y) { + for (int x = 0; x < fw; ++x) { + to[tw * 4 * y + x * 4 + 0] = from[y * fw * 4 + x * 4 + 0]; + to[tw * 4 * y + x * 4 + 1] = from[y * fw * 4 + x * 4 + 1]; + to[tw * 4 * y + x * 4 + 2] = from[y * fw * 4 + x * 4 + 2]; + to[tw * 4 * y + x * 4 + 3] = from[y * fw * 4 + x * 4 + 3]; + } + } + break; + case KINC_IMAGE_FORMAT_GREY8: + for (int y = 0; y < th; ++y) { + for (int x = 0; x < tw; ++x) { + to[tw * y + x] = 0; + } + } + for (int y = 0; y < fh; ++y) { + for (int x = 0; x < fw; ++x) { + to[tw * y + x] = from[y * fw + x]; + } + } + break; + case KINC_IMAGE_FORMAT_RGB24: + case KINC_IMAGE_FORMAT_RGBA128: + case KINC_IMAGE_FORMAT_RGBA64: + case KINC_IMAGE_FORMAT_A32: + case KINC_IMAGE_FORMAT_A16: + case KINC_IMAGE_FORMAT_BGRA32: + break; + } +} + +void kinc_g4_texture_init_from_image(kinc_g4_texture_t *texture, kinc_image_t *image) { + texture->format = image->format; + bool toPow2; +#ifdef KINC_IOS + texture->tex_width = image->width; + texture->tex_height = image->height; + toPow2 = false; +#else + if (kinc_g4_supports_non_pow2_textures()) { + texture->tex_width = image->width; + texture->tex_height = image->height; + toPow2 = false; + } + else { + texture->tex_width = getPower2(image->width); + texture->tex_height = getPower2(image->height); + toPow2 = !(texture->tex_width == image->width && texture->tex_height == image->height); + } +#endif + texture->tex_depth = 1; + + uint8_t *conversionBuffer = NULL; + + switch (image->compression) { + case KINC_IMAGE_COMPRESSION_NONE: + if (toPow2) { + conversionBuffer = (uint8_t *)malloc(texture->tex_width * texture->tex_height * kinc_image_format_sizeof(image->format)); + convertImageToPow2(image->format, (uint8_t *)image->data, image->width, image->height, conversionBuffer, texture->tex_width, texture->tex_height); + } + break; + case KINC_IMAGE_COMPRESSION_PVRTC: + texture->tex_width = kinc_maxi(texture->tex_width, texture->tex_height); + texture->tex_height = kinc_maxi(texture->tex_width, texture->tex_height); + if (texture->tex_width < 8) + texture->tex_width = 8; + if (texture->tex_height < 8) + texture->tex_height = 8; + break; + default: + texture->tex_width = image->width; + texture->tex_height = image->height; + break; + } + +#ifdef KINC_ANDROID + texture->impl.external_oes = false; +#endif + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glCheckErrors(); + glGenTextures(1, &texture->impl.texture); + glCheckErrors(); + glBindTexture(GL_TEXTURE_2D, texture->impl.texture); + glCheckErrors(); + + int convertedType = convertType(image->format); + bool isHdr = convertedType == GL_FLOAT; + + switch (image->compression) { + case KINC_IMAGE_COMPRESSION_PVRTC: +#ifdef KINC_IOS + glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, texture->tex_width, texture->tex_height, 0, + texture->tex_width * texture->tex_height / 2, image->data); +#endif + break; + case KINC_IMAGE_COMPRESSION_ASTC: { + uint8_t blockX = image->internal_format >> 8; + uint8_t blockY = image->internal_format & 0xff; + glCompressedTexImage2D(GL_TEXTURE_2D, 0, astcFormat(blockX, blockY), texture->tex_width, texture->tex_height, 0, image->data_size, image->data); + break; + } + case KINC_IMAGE_COMPRESSION_DXT5: +#ifdef KINC_WINDOWS + glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, texture->tex_width, texture->tex_height, 0, image->data_size, image->data); +#endif + break; + case KINC_IMAGE_COMPRESSION_NONE: { + void *texdata = image->data; + if (!isHdr && toPow2) { + texdata = conversionBuffer; + } + glTexImage2D(GL_TEXTURE_2D, 0, convertInternalFormat(image->format), texture->tex_width, texture->tex_height, 0, convertFormat(image->format), + convertedType, texdata); + glCheckErrors(); + break; + } + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (toPow2) { + free(conversionBuffer); + conversionBuffer = NULL; + } + + /*if (!readable) { + if (isHdr) { + free(texture->image.hdrData); + texture->image.hdrData = NULL; + } + else { + free(texture->image.data); + texture->image.data = NULL; + } + } + + if (readable && texture->image.compression != KINC_IMAGE_COMPRESSION_NONE) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Compressed images can not be readable."); + }*/ +} + +void kinc_g4_texture_init_from_image3d(kinc_g4_texture_t *texture, kinc_image_t *image) { + texture->format = image->format; +#ifndef KINC_OPENGL_ES // Requires GLES 3.0 + texture->tex_width = image->width; + texture->tex_height = image->height; + texture->tex_depth = image->depth; + +#ifdef KINC_ANDROID + external_oes = false; +#endif + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glCheckErrors(); + glGenTextures(1, &texture->impl.texture); + glCheckErrors(); + glBindTexture(GL_TEXTURE_3D, texture->impl.texture); + glCheckErrors(); + + int convertedType = convertType(image->format); + // bool isHdr = convertedType == GL_FLOAT; + + void *texdata = image->data; + glTexImage3D(GL_TEXTURE_3D, 0, convertInternalFormat(image->format), texture->tex_width, texture->tex_height, texture->tex_depth, 0, + convertFormat(image->format), convertedType, texdata); + glCheckErrors(); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glCheckErrors(); + + /*if (!readable) { + if (isHdr) { + free(texture->image.hdrData); + texture->image.hdrData = NULL; + } + else { + free(texture->image.data); + texture->image.data = NULL; + } + } + + if (texture->image.compression != KINC_IMAGE_COMPRESSION_NONE) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Compressed images can not be 3D."); + }*/ +#endif +} + +void kinc_g4_texture_init(kinc_g4_texture_t *texture, int width, int height, kinc_image_format_t format) { +#ifdef KINC_IOS + texture->tex_width = width; + texture->tex_height = height; +#else + if (kinc_g4_supports_non_pow2_textures()) { + texture->tex_width = width; + texture->tex_height = height; + } + else { + texture->tex_width = getPower2(width); + texture->tex_height = getPower2(height); + } +#endif + texture->tex_depth = 1; + texture->format = format; + // conversionBuffer = new u8[texWidth * texHeight * 4]; + +#ifdef KINC_ANDROID + texture->impl.external_oes = false; +#endif + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glCheckErrors(); + glGenTextures(1, &texture->impl.texture); + glCheckErrors(); + glBindTexture(GL_TEXTURE_2D, texture->impl.texture); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glCheckErrors(); + + if (convertType(format) == GL_FLOAT) { + glTexImage2D(GL_TEXTURE_2D, 0, convertInternalFormat(format), texture->tex_width, texture->tex_height, 0, convertFormat(format), GL_FLOAT, NULL); + } + else { + glTexImage2D(GL_TEXTURE_2D, 0, convertInternalFormat(format), texture->tex_width, texture->tex_height, 0, convertFormat(format), GL_UNSIGNED_BYTE, + NULL); + } + glCheckErrors(); +} + +void kinc_g4_texture_init3d(kinc_g4_texture_t *texture, int width, int height, int depth, kinc_image_format_t format) { +#ifndef KINC_OPENGL_ES + texture->tex_width = width; + texture->tex_height = height; + texture->tex_depth = depth; + texture->format = format; + + glGenTextures(1, &texture->impl.texture); + glCheckErrors(); + glBindTexture(GL_TEXTURE_3D, texture->impl.texture); + glCheckErrors(); + + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glCheckErrors(); + + glTexImage3D(GL_TEXTURE_3D, 0, convertInternalFormat(format), width, height, depth, 0, convertFormat(format), GL_UNSIGNED_BYTE, NULL); + glCheckErrors(); +#endif +} + +#ifdef KINC_ANDROID +void kinc_g4_texture_init_from_id(kinc_g4_texture_t *texture, unsigned texid) { + texture->impl.texture = texid; + texture->impl.external_oes = true; + texture->tex_width = 1023; + texture->tex_height = 684; + texture->format = KINC_IMAGE_FORMAT_RGBA32; +} +#endif + +void kinc_g4_texture_destroy(kinc_g4_texture_t *texture) { + glDeleteTextures(1, &texture->impl.texture); + glFlush(); +} + +void Kinc_G4_Internal_TextureSet(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit) { + GLenum target = texture->tex_depth > 1 ? GL_TEXTURE_3D : GL_TEXTURE_2D; + for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) { + if (unit.stages[i] >= 0) { + glActiveTexture(GL_TEXTURE0 + unit.stages[i]); + } + } + glCheckErrors(); +#ifdef KINC_ANDROID + if (texture->impl.external_oes) { + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture->impl.texture); + glCheckErrors(); + } + else { + glBindTexture(target, texture->impl.texture); + glCheckErrors(); + } +#else + glBindTexture(target, texture->impl.texture); + glCheckErrors(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, Kinc_G4_Internal_TextureAddressingU(unit)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, Kinc_G4_Internal_TextureAddressingV(unit)); +#endif +} + +void Kinc_G4_Internal_TextureImageSet(kinc_g4_texture_t *texture, kinc_g4_texture_unit_t unit) { +#if defined(KINC_WINDOWS) || (defined(KINC_LINUX) && defined(GL_VERSION_4_4)) + for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) { + if (unit.stages[i] >= 0) { + glBindImageTexture(unit.stages[i], texture->impl.texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, convertInternalFormat(texture->format)); + } + } + glCheckErrors(); +#endif +} + +int kinc_g4_texture_stride(kinc_g4_texture_t *texture) { + return texture->tex_width * kinc_image_format_sizeof(texture->format); +} + +static uint8_t *lock_cache = NULL; + +unsigned char *kinc_g4_texture_lock(kinc_g4_texture_t *texture) { + if (lock_cache == NULL) { + lock_cache = (uint8_t *)malloc(4096 * 4096 * 4); + } + return lock_cache; //**(texture->image.data ? texture->image.data : (uint8_t *)texture->image.hdrData); +} + +/*void Texture::unlock() { + if (conversionBuffer != nullptr) { + convertImageToPow2(format, (u8*)data, width, height, conversionBuffer, texWidth, texHeight); + glBindTexture(GL_TEXTURE_2D, texture); +#ifndef GL_LUMINANCE +#define GL_LUMINANCE GL_RED +#endif + glTexImage2D(GL_TEXTURE_2D, 0, (format == Image::RGBA32) ? GL_RGBA : GL_LUMINANCE, texWidth, texHeight, 0, (format == Image::RGBA32) ? GL_RGBA : +GL_LUMINANCE, GL_UNSIGNED_BYTE, conversionBuffer); + } +}*/ + +void kinc_g4_texture_unlock(kinc_g4_texture_t *texture) { + GLenum target = texture->tex_depth > 1 ? GL_TEXTURE_3D : GL_TEXTURE_2D; + void *texdata = lock_cache; + glBindTexture(target, texture->impl.texture); + glCheckErrors(); + if (texture->tex_depth > 1) { +#ifndef KINC_OPENGL_ES + glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, texture->tex_width, texture->tex_height, texture->tex_depth, convertFormat(texture->format), + convertType(texture->format), texdata); +#endif + } + else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->tex_width, texture->tex_height, convertFormat(texture->format), convertType(texture->format), texdata); + } + glCheckErrors(); +} + +void kinc_g4_texture_clear(kinc_g4_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color) { +#ifdef GL_VERSION_4_4 + static float clearColor[4]; + clearColor[0] = ((color & 0x00ff0000) >> 16) / 255.0f; + clearColor[1] = ((color & 0x0000ff00) >> 8) / 255.0f; + clearColor[2] = (color & 0x000000ff) / 255.0f; + clearColor[3] = ((color & 0xff000000) >> 24) / 255.0f; + GLenum target = depth > 1 ? GL_TEXTURE_3D : GL_TEXTURE_2D; + glBindTexture(target, texture->impl.texture); + glClearTexSubImage(texture->impl.texture, 0, x, y, z, width, height, depth, convertFormat(texture->format), convertType(texture->format), clearColor); +#endif +} + +#if defined(KINC_IOS) || defined(KINC_MACOS) +void kinc_g4_texture_upload(kinc_g4_texture_t *texture, uint8_t *data, int stride) { + glBindTexture(GL_TEXTURE_2D, texture->impl.texture); + glCheckErrors(); + glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->tex_width, texture->tex_height, convertFormat(texture->format), GL_UNSIGNED_BYTE, data); + glCheckErrors(); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); +} +#endif + +void kinc_g4_texture_generate_mipmaps(kinc_g4_texture_t *texture, int levels) { + GLenum target = texture->tex_depth > 1 ? GL_TEXTURE_3D : GL_TEXTURE_2D; + glBindTexture(target, texture->impl.texture); + glCheckErrors(); + glGenerateMipmap(target); + glCheckErrors(); +} + +void kinc_g4_texture_set_mipmap(kinc_g4_texture_t *texture, kinc_image_t *mipmap, int level) { + int convertedType = convertType(mipmap->format); + // bool isHdr = convertedType == GL_FLOAT; + GLenum target = texture->tex_depth > 1 ? GL_TEXTURE_3D : GL_TEXTURE_2D; + glBindTexture(target, texture->impl.texture); + glCheckErrors(); + glTexImage2D(target, level, convertInternalFormat(mipmap->format), mipmap->width, mipmap->height, 0, convertFormat(mipmap->format), convertedType, + mipmap->data); + glCheckErrors(); +} diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/texture.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/texture.h new file mode 100644 index 000000000..b4cd56cd3 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/texture.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + unsigned int texture; +#ifdef KINC_ANDROID + bool external_oes; +#endif + uint8_t pixfmt; +} kinc_g4_texture_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/vertexbuffer.c.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/vertexbuffer.c.h new file mode 100644 index 000000000..9d294ddde --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/vertexbuffer.c.h @@ -0,0 +1,304 @@ +#include "ogl.h" +#include +#include + +#include +#include + +#include +#include + +extern kinc_g4_index_buffer_t *Kinc_Internal_CurrentIndexBuffer; +static kinc_g4_vertex_buffer_t *currentVertexBuffer = NULL; + +#if defined(KINC_OPENGL_ES) && defined(KINC_ANDROID) && KINC_ANDROID_API >= 18 +void *glesVertexAttribDivisor; +#endif + +void kinc_g4_vertex_buffer_init(kinc_g4_vertex_buffer_t *buffer, int vertexCount, kinc_g4_vertex_structure_t *structure, kinc_g4_usage_t usage, + int instanceDataStepRate) { + buffer->impl.myCount = vertexCount; + buffer->impl.instanceDataStepRate = instanceDataStepRate; +#ifndef NDEBUG + buffer->impl.initialized = false; +#endif + buffer->impl.myStride = 0; + for (int i = 0; i < structure->size; ++i) { + kinc_g4_vertex_element_t element = structure->elements[i]; + buffer->impl.myStride += kinc_g4_vertex_data_size(element.data); + } + buffer->impl.structure = *structure; + + unsigned gl_usage; + switch (usage) { + case KINC_G4_USAGE_STATIC: + default: + gl_usage = GL_STATIC_DRAW; + break; + case KINC_G4_USAGE_DYNAMIC: + gl_usage = GL_DYNAMIC_DRAW; + break; + case KINC_G4_USAGE_READABLE: + gl_usage = GL_DYNAMIC_DRAW; + break; + } + + glGenBuffers(1, &buffer->impl.bufferId); + glCheckErrors(); + glBindBuffer(GL_ARRAY_BUFFER, buffer->impl.bufferId); + glCheckErrors(); + glBufferData(GL_ARRAY_BUFFER, buffer->impl.myStride * buffer->impl.myCount, NULL, gl_usage); + glCheckErrors(); + buffer->impl.data = (float *)malloc(vertexCount * buffer->impl.myStride); +} + +void Kinc_Internal_G4_VertexBuffer_Unset(kinc_g4_vertex_buffer_t *buffer); + +void kinc_g4_vertex_buffer_destroy(kinc_g4_vertex_buffer_t *buffer) { + Kinc_Internal_G4_VertexBuffer_Unset(buffer); + glDeleteBuffers(1, &buffer->impl.bufferId); + free(buffer->impl.data); +} + +float *kinc_g4_vertex_buffer_lock_all(kinc_g4_vertex_buffer_t *buffer) { + buffer->impl.sectionStart = 0; + buffer->impl.sectionSize = buffer->impl.myCount * buffer->impl.myStride; + return buffer->impl.data; +} + +float *kinc_g4_vertex_buffer_lock(kinc_g4_vertex_buffer_t *buffer, int start, int count) { + buffer->impl.sectionStart = start * buffer->impl.myStride; + buffer->impl.sectionSize = count * buffer->impl.myStride; + uint8_t *u8data = (uint8_t *)buffer->impl.data; + return (float *)&u8data[buffer->impl.sectionStart]; +} + +void kinc_g4_vertex_buffer_unlock_all(kinc_g4_vertex_buffer_t *buffer) { + glBindBuffer(GL_ARRAY_BUFFER, buffer->impl.bufferId); + glCheckErrors(); + uint8_t *u8data = (uint8_t *)buffer->impl.data; + glBufferSubData(GL_ARRAY_BUFFER, buffer->impl.sectionStart, buffer->impl.sectionSize, u8data + buffer->impl.sectionStart); + glCheckErrors(); +#ifndef NDEBUG + buffer->impl.initialized = true; +#endif +} + +void kinc_g4_vertex_buffer_unlock(kinc_g4_vertex_buffer_t *buffer, int count) { + glBindBuffer(GL_ARRAY_BUFFER, buffer->impl.bufferId); + glCheckErrors(); + uint8_t *u8data = (uint8_t *)buffer->impl.data; + glBufferSubData(GL_ARRAY_BUFFER, buffer->impl.sectionStart, count * buffer->impl.myStride, u8data + buffer->impl.sectionStart); + glCheckErrors(); +#ifndef NDEBUG + buffer->impl.initialized = true; +#endif +} + +int Kinc_G4_Internal_SetVertexAttributes(kinc_g4_vertex_buffer_t *buffer, int offset); + +int kinc_internal_g4_vertex_buffer_set(kinc_g4_vertex_buffer_t *buffer, int offset) { + // assert(buffer->impl.initialized); // Vertex Buffer is used before lock/unlock was called + int offsetoffset = Kinc_G4_Internal_SetVertexAttributes(buffer, offset); + if (Kinc_Internal_CurrentIndexBuffer != NULL) { + kinc_internal_g4_index_buffer_set(Kinc_Internal_CurrentIndexBuffer); + } + return offsetoffset; +} + +void Kinc_Internal_G4_VertexBuffer_Unset(kinc_g4_vertex_buffer_t *buffer) { + if (currentVertexBuffer == buffer) { + currentVertexBuffer = NULL; + } +} + +int kinc_g4_vertex_buffer_count(kinc_g4_vertex_buffer_t *buffer) { + return buffer->impl.myCount; +} + +int kinc_g4_vertex_buffer_stride(kinc_g4_vertex_buffer_t *buffer) { + return buffer->impl.myStride; +} + +#if !defined(KINC_OPENGL_ES) || (defined(KINC_OPENGL_ES) && defined(KINC_WASM)) || (defined(KINC_OPENGL_ES) && defined(KINC_ANDROID) && KINC_ANDROID_API >= 18) +static bool attribDivisorUsed = false; +#endif + +int Kinc_G4_Internal_SetVertexAttributes(kinc_g4_vertex_buffer_t *buffer, int offset) { + glBindBuffer(GL_ARRAY_BUFFER, buffer->impl.bufferId); + glCheckErrors(); + + int internaloffset = 0; + int actualIndex = 0; + for (int index = 0; index < buffer->impl.structure.size; ++index) { + kinc_g4_vertex_element_t element = buffer->impl.structure.elements[index]; + int size = 0; + GLenum type = GL_FLOAT; + switch (element.data) { + case KINC_G4_VERTEX_DATA_NONE: + break; + case KINC_G4_VERTEX_DATA_F32_1X: + size = 1; + break; + case KINC_G4_VERTEX_DATA_F32_2X: + size = 2; + break; + case KINC_G4_VERTEX_DATA_F32_3X: + size = 3; + break; + case KINC_G4_VERTEX_DATA_F32_4X: + size = 4; + break; + case KINC_G4_VERTEX_DATA_F32_4X4: + size = 16; + break; + case KINC_G4_VERTEX_DATA_I8_1X: + case KINC_G4_VERTEX_DATA_I8_1X_NORMALIZED: + size = 1; + type = GL_BYTE; + break; + case KINC_G4_VERTEX_DATA_U8_1X: + case KINC_G4_VERTEX_DATA_U8_1X_NORMALIZED: + size = 1; + type = GL_UNSIGNED_BYTE; + break; + case KINC_G4_VERTEX_DATA_I8_2X: + case KINC_G4_VERTEX_DATA_I8_2X_NORMALIZED: + size = 2; + type = GL_BYTE; + break; + case KINC_G4_VERTEX_DATA_U8_2X: + case KINC_G4_VERTEX_DATA_U8_2X_NORMALIZED: + size = 2; + type = GL_UNSIGNED_BYTE; + break; + case KINC_G4_VERTEX_DATA_I8_4X: + case KINC_G4_VERTEX_DATA_I8_4X_NORMALIZED: + size = 4; + type = GL_BYTE; + break; + case KINC_G4_VERTEX_DATA_U8_4X: + case KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED: + size = 4; + type = GL_UNSIGNED_BYTE; + break; + case KINC_G4_VERTEX_DATA_I16_1X: + case KINC_G4_VERTEX_DATA_I16_1X_NORMALIZED: + size = 1; + type = GL_SHORT; + break; + case KINC_G4_VERTEX_DATA_U16_1X: + case KINC_G4_VERTEX_DATA_U16_1X_NORMALIZED: + size = 1; + type = GL_UNSIGNED_SHORT; + break; + case KINC_G4_VERTEX_DATA_I16_2X: + case KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED: + size = 2; + type = GL_SHORT; + break; + case KINC_G4_VERTEX_DATA_U16_2X: + case KINC_G4_VERTEX_DATA_U16_2X_NORMALIZED: + size = 2; + type = GL_UNSIGNED_SHORT; + break; + case KINC_G4_VERTEX_DATA_I16_4X: + case KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED: + size = 4; + type = GL_SHORT; + break; + case KINC_G4_VERTEX_DATA_U16_4X: + case KINC_G4_VERTEX_DATA_U16_4X_NORMALIZED: + size = 4; + type = GL_UNSIGNED_SHORT; + break; + case KINC_G4_VERTEX_DATA_I32_1X: + size = 1; + type = GL_INT; + break; + case KINC_G4_VERTEX_DATA_U32_1X: + size = 1; + type = GL_UNSIGNED_INT; + break; + case KINC_G4_VERTEX_DATA_I32_2X: + size = 2; + type = GL_INT; + break; + case KINC_G4_VERTEX_DATA_U32_2X: + size = 2; + type = GL_UNSIGNED_INT; + break; + case KINC_G4_VERTEX_DATA_I32_3X: + size = 3; + type = GL_INT; + break; + case KINC_G4_VERTEX_DATA_U32_3X: + size = 3; + type = GL_UNSIGNED_INT; + break; + case KINC_G4_VERTEX_DATA_I32_4X: + size = 4; + type = GL_INT; + break; + case KINC_G4_VERTEX_DATA_U32_4X: + size = 4; + type = GL_UNSIGNED_INT; + break; + } + if (size > 4) { + int subsize = size; + int addonOffset = 0; + while (subsize > 0) { + glEnableVertexAttribArray(offset + actualIndex); + glCheckErrors(); + glVertexAttribPointer(offset + actualIndex, 4, type, false, buffer->impl.myStride, (void *)(int64_t)(internaloffset + addonOffset)); + glCheckErrors(); +#if !defined(KINC_OPENGL_ES) || (defined(KINC_OPENGL_ES) && defined(KINC_WASM)) + if (attribDivisorUsed || buffer->impl.instanceDataStepRate != 0) { + attribDivisorUsed = true; + glVertexAttribDivisor(offset + actualIndex, buffer->impl.instanceDataStepRate); + glCheckErrors(); + } +#endif +#if (defined(KINC_OPENGL_ES) && defined(KINC_ANDROID) && KINC_ANDROID_API >= 18) + if (attribDivisorUsed || buffer->impl.instanceDataStepRate != 0) { + attribDivisorUsed = true; + ((void (*)(GLuint, GLuint))glesVertexAttribDivisor)(offset + actualIndex, buffer->impl.instanceDataStepRate); + glCheckErrors(); + } +#endif + subsize -= 4; + addonOffset += 4 * 4; + ++actualIndex; + } + } + else { + glEnableVertexAttribArray(offset + actualIndex); + glCheckErrors(); + glVertexAttribPointer(offset + actualIndex, size, type, type == GL_FLOAT ? false : true, buffer->impl.myStride, (void *)(int64_t)internaloffset); + glCheckErrors(); +#if !defined(KINC_OPENGL_ES) || (defined(KINC_OPENGL_ES) && defined(KINC_WASM)) + if (attribDivisorUsed || buffer->impl.instanceDataStepRate != 0) { + attribDivisorUsed = true; + glVertexAttribDivisor(offset + actualIndex, buffer->impl.instanceDataStepRate); + glCheckErrors(); + } +#endif +#if (defined(KINC_OPENGL_ES) && defined(KINC_ANDROID) && KINC_ANDROID_API >= 18) + if (attribDivisorUsed || buffer->impl.instanceDataStepRate != 0) { + attribDivisorUsed = true; + ((void (*)(GLuint, GLuint))glesVertexAttribDivisor)(offset + actualIndex, buffer->impl.instanceDataStepRate); + glCheckErrors(); + } +#endif + ++actualIndex; + } + internaloffset += kinc_g4_vertex_data_size(element.data); + } + int count = kinc_internal_opengl_max_vertex_attribute_arrays - offset; + for (int index = actualIndex; index < count; ++index) { + glDisableVertexAttribArray(offset + index); + glCheckErrors(); + } + return actualIndex; +} diff --git a/armorcore/sources/backends/opengl/kinc/backend/graphics4/vertexbuffer.h b/armorcore/sources/backends/opengl/kinc/backend/graphics4/vertexbuffer.h new file mode 100644 index 000000000..46820a2e0 --- /dev/null +++ b/armorcore/sources/backends/opengl/kinc/backend/graphics4/vertexbuffer.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + float *data; + int myCount; + int myStride; + unsigned bufferId; + int sectionStart; + int sectionSize; + // #if defined KINC_ANDROID || defined KINC_EMSCRIPTEN + kinc_g4_vertex_structure_t structure; + // #endif + int instanceDataStepRate; +#ifndef NDEBUG + bool initialized; +#endif +} kinc_g4_vertex_buffer_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/posix/kinc/backend/atomic.h b/armorcore/sources/backends/posix/kinc/backend/atomic.h new file mode 100644 index 000000000..9013b6494 --- /dev/null +++ b/armorcore/sources/backends/posix/kinc/backend/atomic.h @@ -0,0 +1,103 @@ +#pragma once + +#include + +#if defined(KINC_MACOS) || defined(KINC_IOS) + +#include + +static inline bool kinc_atomic_compare_exchange(volatile int32_t *pointer, int32_t old_value, int32_t new_value) { + return OSAtomicCompareAndSwap32Barrier(old_value, new_value, pointer); +} + +static inline bool kinc_atomic_compare_exchange_pointer(void *volatile *pointer, void *old_value, void *new_value) { + return OSAtomicCompareAndSwapPtrBarrier(old_value, new_value, pointer); +} + +static inline int32_t kinc_atomic_increment(volatile int32_t *pointer) { + return OSAtomicIncrement32Barrier(pointer) - 1; +} + +static inline int32_t kinc_atomic_decrement(volatile int32_t *pointer) { + return OSAtomicDecrement32Barrier(pointer) + 1; +} + +static inline void kinc_atomic_exchange(volatile int32_t *pointer, int32_t value) { + __sync_swap(pointer, value); +} + +static inline void kinc_atomic_exchange_float(volatile float *pointer, float value) { + __sync_swap((volatile int32_t *)pointer, *(int32_t *)&value); +} + +static inline void kinc_atomic_exchange_double(volatile double *pointer, double value) { + __sync_swap((volatile int64_t *)pointer, *(int64_t *)&value); +} + +#else + +// clang/gcc intrinsics + +static inline bool kinc_atomic_compare_exchange(volatile int32_t *pointer, int32_t old_value, int32_t new_value) { + return __sync_val_compare_and_swap(pointer, old_value, new_value) == old_value; +} + +static inline bool kinc_atomic_compare_exchange_pointer(void *volatile *pointer, void *old_value, void *new_value) { + return __sync_val_compare_and_swap(pointer, old_value, new_value) == old_value; +} + +static inline int32_t kinc_atomic_increment(volatile int32_t *pointer) { + return __sync_fetch_and_add(pointer, 1); +} + +static inline int32_t kinc_atomic_decrement(volatile int32_t *pointer) { + return __sync_fetch_and_sub(pointer, 1); +} + +#ifdef __clang__ + +static inline void kinc_atomic_exchange(volatile int32_t *pointer, int32_t value) { + __sync_swap(pointer, value); +} + +static inline void kinc_atomic_exchange_float(volatile float *pointer, float value) { + __sync_swap((volatile int32_t *)pointer, *(int32_t *)&value); +} + +static inline void kinc_atomic_exchange_double(volatile double *pointer, double value) { + __sync_swap((volatile int64_t *)pointer, *(int64_t *)&value); +} + +#else + +// Beware, __sync_lock_test_and_set is not a full barrier and can have platform-specific weirdness + +static inline void kinc_atomic_exchange(volatile int32_t *pointer, int32_t value) { + __sync_lock_test_and_set(pointer, value); +} + +static inline void kinc_atomic_exchange_float(volatile float *pointer, float value) { + __sync_lock_test_and_set((volatile int32_t *)pointer, *(int32_t *)&value); +} + +static inline void kinc_atomic_exchange_double(volatile double *pointer, double value) { + __sync_lock_test_and_set((volatile int64_t *)pointer, *(int64_t *)&value); +} + +#endif + +#endif + +#define KINC_ATOMIC_COMPARE_EXCHANGE(pointer, oldValue, newValue) (kinc_atomic_compare_exchange(pointer, oldValue, newValue)) + +#define KINC_ATOMIC_COMPARE_EXCHANGE_POINTER(pointer, oldValue, newValue) (kinc_atomic_compare_exchange_pointer(pointer, oldValue, newValue)) + +#define KINC_ATOMIC_INCREMENT(pointer) (kinc_atomic_increment(pointer)) + +#define KINC_ATOMIC_DECREMENT(pointer) (kinc_atomic_decrement(pointer)) + +#define KINC_ATOMIC_EXCHANGE_32(pointer, value) (kinc_atomic_exchange(pointer, value)) + +#define KINC_ATOMIC_EXCHANGE_FLOAT(pointer, value) (kinc_atomic_exchange_float(pointer, value)) + +#define KINC_ATOMIC_EXCHANGE_DOUBLE(pointer, value) (kinc_atomic_exchange_double(pointer, value)) diff --git a/armorcore/sources/backends/posix/kinc/backend/event.c.h b/armorcore/sources/backends/posix/kinc/backend/event.c.h new file mode 100644 index 000000000..821a27a8f --- /dev/null +++ b/armorcore/sources/backends/posix/kinc/backend/event.c.h @@ -0,0 +1,78 @@ +#include + +#include +#include +#include + +void kinc_event_init(kinc_event_t *event, bool auto_reset) { + event->impl.auto_reset = auto_reset; + event->impl.set = false; + + pthread_cond_init(&event->impl.event, NULL); + + // pthread_mutexattr_t attr; + // pthread_mutexattr_init(&attr); + // pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&event->impl.mutex, NULL); //&attr); +} + +void kinc_event_destroy(kinc_event_t *event) { + pthread_cond_destroy(&event->impl.event); + pthread_mutex_destroy(&event->impl.mutex); +} + +void kinc_event_signal(kinc_event_t *event) { + pthread_mutex_lock(&event->impl.mutex); + if (!event->impl.set) { + event->impl.set = true; + pthread_cond_signal(&event->impl.event); + } + pthread_mutex_unlock(&event->impl.mutex); +} + +void kinc_event_wait(kinc_event_t *event) { + pthread_mutex_lock(&event->impl.mutex); + while (!event->impl.set) { + pthread_cond_wait(&event->impl.event, &event->impl.mutex); + } + if (event->impl.auto_reset) { + event->impl.set = false; + } + pthread_mutex_unlock(&event->impl.mutex); +} + +bool kinc_event_try_to_wait(kinc_event_t *event, double seconds) { + pthread_mutex_lock(&event->impl.mutex); + + struct timeval tv; + gettimeofday(&tv, 0); + + int isec = (int)seconds; + int usec = (int)((seconds - isec) * 1000000.0); + struct timespec spec; + spec.tv_nsec = (tv.tv_usec + usec) * 1000; + if (spec.tv_nsec > 1000000000) { + spec.tv_nsec -= 1000000000; + isec += 1; + } + spec.tv_sec = tv.tv_sec + isec; + + while (!event->impl.set) { + int result = pthread_cond_timedwait(&event->impl.event, &event->impl.mutex, &spec); + if (result == 0 || result == ETIMEDOUT) { + break; + } + } + bool result = event->impl.set; + if (event->impl.auto_reset) { + event->impl.set = false; + } + pthread_mutex_unlock(&event->impl.mutex); + return result; +} + +void kinc_event_reset(kinc_event_t *event) { + pthread_mutex_lock(&event->impl.mutex); + event->impl.set = false; + pthread_mutex_unlock(&event->impl.mutex); +} diff --git a/armorcore/sources/backends/posix/kinc/backend/event.h b/armorcore/sources/backends/posix/kinc/backend/event.h new file mode 100644 index 000000000..2e965775d --- /dev/null +++ b/armorcore/sources/backends/posix/kinc/backend/event.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + pthread_cond_t event; + pthread_mutex_t mutex; + volatile bool set; + bool auto_reset; +} kinc_event_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/posix/kinc/backend/mutex.c.h b/armorcore/sources/backends/posix/kinc/backend/mutex.c.h new file mode 100644 index 000000000..82cc6b1c9 --- /dev/null +++ b/armorcore/sources/backends/posix/kinc/backend/mutex.c.h @@ -0,0 +1,40 @@ +#include + +#include + +void kinc_mutex_init(kinc_mutex_t *mutex) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mutex->impl.mutex, &attr); +} + +void kinc_mutex_destroy(kinc_mutex_t *mutex) { + pthread_mutex_destroy(&mutex->impl.mutex); +} + +bool kinc_mutex_try_to_lock(kinc_mutex_t *mutex) { + return pthread_mutex_trylock(&mutex->impl.mutex) == 0; +} + +void kinc_mutex_lock(kinc_mutex_t *mutex) { + pthread_mutex_lock(&mutex->impl.mutex); +} + +void kinc_mutex_unlock(kinc_mutex_t *mutex) { + pthread_mutex_unlock(&mutex->impl.mutex); +} + +bool kinc_uber_mutex_init(kinc_uber_mutex_t *mutex, const char *name) { + return false; +} + +void kinc_uber_mutex_destroy(kinc_uber_mutex_t *mutex) {} + +void kinc_uber_mutex_lock(kinc_uber_mutex_t *mutex) { + assert(false); +} + +void kinc_uber_mutex_unlock(kinc_uber_mutex_t *mutex) { + assert(false); +} diff --git a/armorcore/sources/backends/posix/kinc/backend/mutex.h b/armorcore/sources/backends/posix/kinc/backend/mutex.h new file mode 100644 index 000000000..460748500 --- /dev/null +++ b/armorcore/sources/backends/posix/kinc/backend/mutex.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + pthread_mutex_t mutex; +} kinc_mutex_impl_t; + +typedef struct { + int nothing; +} kinc_uber_mutex_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/posix/kinc/backend/posixunit.c b/armorcore/sources/backends/posix/kinc/backend/posixunit.c new file mode 100644 index 000000000..76414a781 --- /dev/null +++ b/armorcore/sources/backends/posix/kinc/backend/posixunit.c @@ -0,0 +1,5 @@ +#include "event.c.h" +#include "mutex.c.h" +#include "semaphore.c.h" +#include "thread.c.h" +#include "threadlocal.c.h" diff --git a/armorcore/sources/backends/posix/kinc/backend/semaphore.c.h b/armorcore/sources/backends/posix/kinc/backend/semaphore.c.h new file mode 100644 index 000000000..3ca5bc8c1 --- /dev/null +++ b/armorcore/sources/backends/posix/kinc/backend/semaphore.c.h @@ -0,0 +1,56 @@ +#include +#include + +#ifdef __APPLE__ + +void kinc_semaphore_init(kinc_semaphore_t *semaphore, int current, int max) { + semaphore->impl.semaphore = dispatch_semaphore_create(current); +} + +void kinc_semaphore_destroy(kinc_semaphore_t *semaphore) {} + +void kinc_semaphore_release(kinc_semaphore_t *semaphore, int count) { + for (int i = 0; i < count; ++i) { + dispatch_semaphore_signal(semaphore->impl.semaphore); + } +} + +void kinc_semaphore_acquire(kinc_semaphore_t *semaphore) { + dispatch_semaphore_wait(semaphore->impl.semaphore, DISPATCH_TIME_FOREVER); +} + +bool kinc_semaphore_try_to_acquire(kinc_semaphore_t *semaphore, double seconds) { + return dispatch_semaphore_wait(semaphore->impl.semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * 1000 * 1000 * 1000))) == 0; +} + +#else + +void kinc_semaphore_init(kinc_semaphore_t *semaphore, int current, int max) { + sem_init(&semaphore->impl.semaphore, 0, current); +} + +void kinc_semaphore_destroy(kinc_semaphore_t *semaphore) { + sem_destroy(&semaphore->impl.semaphore); +} + +void kinc_semaphore_release(kinc_semaphore_t *semaphore, int count) { + for (int i = 0; i < count; ++i) { + sem_post(&semaphore->impl.semaphore); + } +} + +void kinc_semaphore_acquire(kinc_semaphore_t *semaphore) { + sem_wait(&semaphore->impl.semaphore); +} + +bool kinc_semaphore_try_to_acquire(kinc_semaphore_t *semaphore, double seconds) { + double now = kinc_time(); + do { + if (sem_trywait(&semaphore->impl.semaphore) == 0) { + return true; + } + } while (kinc_time() < now + seconds); + return false; +} + +#endif diff --git a/armorcore/sources/backends/posix/kinc/backend/semaphore.h b/armorcore/sources/backends/posix/kinc/backend/semaphore.h new file mode 100644 index 000000000..23c3346a3 --- /dev/null +++ b/armorcore/sources/backends/posix/kinc/backend/semaphore.h @@ -0,0 +1,23 @@ +#pragma once + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { +#ifdef __APPLE__ + dispatch_semaphore_t semaphore; +#else + sem_t semaphore; +#endif +} kinc_semaphore_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/posix/kinc/backend/thread.c.h b/armorcore/sources/backends/posix/kinc/backend/thread.c.h new file mode 100644 index 000000000..ad32f357a --- /dev/null +++ b/armorcore/sources/backends/posix/kinc/backend/thread.c.h @@ -0,0 +1,82 @@ +#include + +#include +#include +#include +#include +#include + +#if !defined(KINC_IOS) && !defined(KINC_MACOS) + +struct thread_start { + void (*thread)(void *param); + void *param; +}; + +#define THREAD_STARTS 64 +static struct thread_start starts[THREAD_STARTS]; +static int thread_start_index = 0; + +static void *ThreadProc(void *arg) { + intptr_t start_index = (intptr_t)arg; + starts[start_index].thread(starts[start_index].param); + pthread_exit(NULL); + return NULL; +} + +void kinc_thread_init(kinc_thread_t *t, void (*thread)(void *param), void *param) { + t->impl.param = param; + t->impl.thread = thread; + pthread_attr_t attr; + pthread_attr_init(&attr); + // pthread_attr_setstacksize(&attr, 1024 * 64); + struct sched_param sp; + memset(&sp, 0, sizeof(sp)); + sp.sched_priority = 0; + pthread_attr_setschedparam(&attr, &sp); + intptr_t start_index = thread_start_index++; + if (thread_start_index >= THREAD_STARTS) { + thread_start_index = 0; + } + starts[start_index].thread = thread; + starts[start_index].param = param; + int ret = pthread_create(&t->impl.pthread, &attr, &ThreadProc, (void *)start_index); + assert(ret == 0); + pthread_attr_destroy(&attr); +} + +void kinc_thread_wait_and_destroy(kinc_thread_t *thread) { + int ret; + do { + ret = pthread_join(thread->impl.pthread, NULL); + } while (ret != 0); +} + +bool kinc_thread_try_to_destroy(kinc_thread_t *thread) { + return pthread_join(thread->impl.pthread, NULL) == 0; +} + +void kinc_threads_init() {} + +void kinc_threads_quit() {} + +#endif + +#if !defined(KINC_IOS) && !defined(KINC_MACOS) +// Alternatively _GNU_SOURCE can be defined to make +// the headers declare it but let's not make it too +// easy to write Linux-specific POSIX-code +int pthread_setname_np(pthread_t thread, const char *name); +#endif + +void kinc_thread_set_name(const char *name) { +#if !defined(KINC_IOS) && !defined(KINC_MACOS) + pthread_setname_np(pthread_self(), name); +#else + pthread_setname_np(name); +#endif +} + +void kinc_thread_sleep(int milliseconds) { + usleep(1000 * (useconds_t)milliseconds); +} diff --git a/armorcore/sources/backends/posix/kinc/backend/thread.h b/armorcore/sources/backends/posix/kinc/backend/thread.h new file mode 100644 index 000000000..90c07024d --- /dev/null +++ b/armorcore/sources/backends/posix/kinc/backend/thread.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void *param; + void (*thread)(void *param); + pthread_t pthread; +} kinc_thread_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/posix/kinc/backend/threadlocal.c.h b/armorcore/sources/backends/posix/kinc/backend/threadlocal.c.h new file mode 100644 index 000000000..da6a58821 --- /dev/null +++ b/armorcore/sources/backends/posix/kinc/backend/threadlocal.c.h @@ -0,0 +1,17 @@ +#include + +void kinc_thread_local_init(kinc_thread_local_t *local) { + pthread_key_create(&local->impl.key, NULL); +} + +void kinc_thread_local_destroy(kinc_thread_local_t *local) { + pthread_key_delete(local->impl.key); +} + +void *kinc_thread_local_get(kinc_thread_local_t *local) { + return pthread_getspecific(local->impl.key); +} + +void kinc_thread_local_set(kinc_thread_local_t *local, void *data) { + pthread_setspecific(local->impl.key, data); +} diff --git a/armorcore/sources/backends/posix/kinc/backend/threadlocal.h b/armorcore/sources/backends/posix/kinc/backend/threadlocal.h new file mode 100644 index 000000000..fb5e65997 --- /dev/null +++ b/armorcore/sources/backends/posix/kinc/backend/threadlocal.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + pthread_key_t key; +} kinc_thread_local_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/MiniVulkan.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/MiniVulkan.h new file mode 100644 index 000000000..449143d72 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/MiniVulkan.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/ShaderHash.c.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/ShaderHash.c.h new file mode 100644 index 000000000..c1114a184 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/ShaderHash.c.h @@ -0,0 +1,11 @@ +#include "ShaderHash.h" + +// djb2 +uint32_t kinc_internal_hash_name(unsigned char *str) { + unsigned long hash = 5381; + int c; + while (c = *str++) { + hash = hash * 33 ^ c; + } + return hash; +} diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/ShaderHash.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/ShaderHash.h new file mode 100644 index 000000000..2963fd345 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/ShaderHash.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint32_t hash; + uint32_t index; +} kinc_internal_hash_index_t; + +uint32_t kinc_internal_hash_name(unsigned char *str); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/Vulkan.c.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/Vulkan.c.h new file mode 100644 index 000000000..1a02c9b1e --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/Vulkan.c.h @@ -0,0 +1,1237 @@ +#include "kinc/graphics4/graphics.h" + +#include "vulkan.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include + +struct vk_funs vk = {0}; +struct vk_context vk_ctx = {0}; + +void kinc_vulkan_get_instance_extensions(const char **extensions, int *index, int max); +VkBool32 kinc_vulkan_get_physical_device_presentation_support(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex); +VkResult kinc_vulkan_create_surface(VkInstance instance, int window_index, VkSurfaceKHR *surface); + +#define GET_INSTANCE_PROC_ADDR(instance, entrypoint) \ + { \ + vk.fp##entrypoint = (PFN_vk##entrypoint)vkGetInstanceProcAddr(instance, "vk" #entrypoint); \ + if (vk.fp##entrypoint == NULL) { \ + kinc_error_message("vkGetInstanceProcAddr failed to find vk" #entrypoint); \ + } \ + } + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +void createDescriptorLayout(void); +static void create_compute_descriptor_layout(void); +void set_image_layout(VkImage image, VkImageAspectFlags aspectMask, VkImageLayout old_image_layout, VkImageLayout new_image_layout); + +// uint32_t current_buffer; + +kinc_g5_texture_t *vulkanTextures[16] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +kinc_g5_render_target_t *vulkanRenderTargets[16] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +VkSampler vulkanSamplers[16] = {VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, + VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE}; + +static bool began = false; + +#ifndef KINC_ANDROID +static VkAllocationCallbacks allocator; +#endif + +static VkPhysicalDeviceProperties gpu_props; +static VkQueueFamilyProperties *queue_props; +static uint32_t graphics_queue_node_index; + +static VkPhysicalDeviceMemoryProperties memory_properties; + +static uint32_t queue_count; + +static VkBool32 vkDebugUtilsMessengerCallbackEXT(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData) { + if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Vulkan ERROR: Code %d : %s", pCallbackData->messageIdNumber, pCallbackData->pMessage); + // kinc_debug_break(); + } + else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Vulkan WARNING: Code %d : %s", pCallbackData->messageIdNumber, pCallbackData->pMessage); + } + return VK_FALSE; +} + +#ifndef KINC_ANDROID +static VKAPI_ATTR void *VKAPI_CALL myrealloc(void *pUserData, void *pOriginal, size_t size, size_t alignment, VkSystemAllocationScope allocationScope) { +#ifdef _MSC_VER + return _aligned_realloc(pOriginal, size, alignment); +#else + return realloc(pOriginal, size); +#endif +} + +static VKAPI_ATTR void *VKAPI_CALL myalloc(void *pUserData, size_t size, size_t alignment, VkSystemAllocationScope allocationScope) { +#ifdef _MSC_VER + return _aligned_malloc(size, alignment); +#else + void *ptr; + + if (alignment % sizeof(void *) != 0) { + alignment *= (sizeof(void *) / alignment); + } + + if (posix_memalign(&ptr, alignment, size) != 0) { + return NULL; + } + return ptr; +#endif +} + +static VKAPI_ATTR void VKAPI_CALL myfree(void *pUserData, void *pMemory) { +#ifdef _MSC_VER + _aligned_free(pMemory); +#else + free(pMemory); +#endif +} +#endif + +bool memory_type_from_properties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex) { + for (uint32_t i = 0; i < 32; i++) { + if ((typeBits & 1) == 1) { + if ((memory_properties.memoryTypes[i].propertyFlags & requirements_mask) == requirements_mask) { + *typeIndex = i; + return true; + } + } + typeBits >>= 1; + } + return false; +} + +extern void kinc_g4_on_g5_internal_resize(int, int, int); + +void kinc_internal_resize(int window_index, int width, int height) { + struct vk_window *window = &vk_ctx.windows[window_index]; + if (window->width != width || window->height != height) { + window->resized = true; + window->width = width; + window->height = height; + } + + kinc_g4_on_g5_internal_resize(window_index, width, height); +} + +void kinc_internal_change_framebuffer(int window, struct kinc_framebuffer_options *frame) {} + +VkSwapchainKHR cleanup_swapchain(int window_index) { + struct vk_window *window = &vk_ctx.windows[window_index]; + + if (window->framebuffer_render_pass != VK_NULL_HANDLE) { + vkDestroyRenderPass(vk_ctx.device, window->framebuffer_render_pass, NULL); + } + + if (window->framebuffers) { + for (uint32_t i = 0; i < window->image_count; i++) { + vkDestroyFramebuffer(vk_ctx.device, window->framebuffers[i], NULL); + } + free(window->framebuffers); + window->framebuffers = NULL; + } + + if (window->depth.image != VK_NULL_HANDLE) { + vkDestroyImageView(vk_ctx.device, window->depth.view, NULL); + vkDestroyImage(vk_ctx.device, window->depth.image, NULL); + vkFreeMemory(vk_ctx.device, window->depth.memory, NULL); + window->depth.image = VK_NULL_HANDLE; + window->depth.memory = VK_NULL_HANDLE; + window->depth.view = VK_NULL_HANDLE; + } + + if (window->images) { + for (uint32_t i = 0; i < window->image_count; i++) { + vkDestroyImageView(vk_ctx.device, window->views[i], NULL); + } + free(window->images); + free(window->views); + window->images = NULL; + window->views = NULL; + } + + VkSwapchainKHR chain = window->swapchain; + window->swapchain = VK_NULL_HANDLE; + return chain; +} + +#define MAX_PRESENT_MODES 256 +static VkPresentModeKHR present_modes[MAX_PRESENT_MODES]; + +void create_swapchain(int window_index) { + struct vk_window *window = &vk_ctx.windows[window_index]; + + VkSwapchainKHR oldSwapchain = cleanup_swapchain(window_index); + if (window->surface_destroyed) { + vk.fpDestroySwapchainKHR(vk_ctx.device, oldSwapchain, NULL); + oldSwapchain = VK_NULL_HANDLE; + vk.fpDestroySurfaceKHR(vk_ctx.instance, window->surface, NULL); + VkResult err = kinc_vulkan_create_surface(vk_ctx.instance, window_index, &window->surface); + assert(!err); + window->width = kinc_window_width(window_index); + window->height = kinc_window_height(window_index); + window->surface_destroyed = false; + } + + // Check the surface capabilities and formats + VkSurfaceCapabilitiesKHR surfCapabilities = {0}; + VkResult err = vk.fpGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_ctx.gpu, window->surface, &surfCapabilities); + assert(!err); + + uint32_t present_mode_count; + err = vk.fpGetPhysicalDeviceSurfacePresentModesKHR(vk_ctx.gpu, window->surface, &present_mode_count, NULL); + present_mode_count = present_mode_count > MAX_PRESENT_MODES ? MAX_PRESENT_MODES : present_mode_count; + assert(!err); + err = vk.fpGetPhysicalDeviceSurfacePresentModesKHR(vk_ctx.gpu, window->surface, &present_mode_count, present_modes); + assert(!err); + + VkExtent2D swapchainExtent; + // width and height are either both -1, or both not -1. + if (surfCapabilities.currentExtent.width == (uint32_t)-1) { + // If the surface size is undefined, the size is set to + // the size of the images requested. + swapchainExtent.width = window->width; + swapchainExtent.height = window->height; + } + else { + // If the surface size is defined, the swap chain size must match + swapchainExtent = surfCapabilities.currentExtent; + window->width = surfCapabilities.currentExtent.width; + window->height = surfCapabilities.currentExtent.height; + } + + VkPresentModeKHR swapchainPresentMode = window->vsynced ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_MAILBOX_KHR; + + // Determine the number of VkImage's to use in the swap chain (we desire to + // own only 1 image at a time, besides the images being displayed and + // queued for display): + uint32_t desiredNumberOfSwapchainImages = surfCapabilities.minImageCount + 1; + if ((surfCapabilities.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCapabilities.maxImageCount)) { + // Application must settle for fewer images than desired: + desiredNumberOfSwapchainImages = surfCapabilities.maxImageCount; + } + + VkSurfaceTransformFlagBitsKHR preTransform = {0}; + if (surfCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { + preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + } + else { + preTransform = surfCapabilities.currentTransform; + } + + VkSwapchainCreateInfoKHR swapchain_info = {0}; + swapchain_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchain_info.pNext = NULL; + swapchain_info.surface = window->surface; + swapchain_info.minImageCount = desiredNumberOfSwapchainImages; + swapchain_info.imageFormat = window->format.format; + swapchain_info.imageColorSpace = window->format.colorSpace; + swapchain_info.imageExtent.width = swapchainExtent.width; + swapchain_info.imageExtent.height = swapchainExtent.height; + swapchain_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapchain_info.preTransform = preTransform; + + if (surfCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) { + swapchain_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + } + else if (surfCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) { + swapchain_info.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; + } + else if (surfCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) { + swapchain_info.compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; + } + else if (surfCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR) { + swapchain_info.compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR; + } + else { + kinc_log(KINC_LOG_LEVEL_ERROR, "No supported composite alpha, this should not happen.\nPlease go complain to the writers of your Vulkan driver."); + exit(1); + } + + swapchain_info.imageArrayLayers = 1; + swapchain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + swapchain_info.queueFamilyIndexCount = 0; + swapchain_info.pQueueFamilyIndices = NULL; + swapchain_info.presentMode = swapchainPresentMode; + swapchain_info.oldSwapchain = oldSwapchain; + swapchain_info.clipped = true; + + err = vk.fpCreateSwapchainKHR(vk_ctx.device, &swapchain_info, NULL, &window->swapchain); + assert(!err); + + // If we just re-created an existing swapchain, we should destroy the old + // swapchain at this point. + // Note: destroying the swapchain also cleans up all its associated + // presentable images once the platform is done with them. + if (oldSwapchain != VK_NULL_HANDLE) { + vk.fpDestroySwapchainKHR(vk_ctx.device, oldSwapchain, NULL); + } + + err = vk.fpGetSwapchainImagesKHR(vk_ctx.device, window->swapchain, &window->image_count, NULL); + assert(!err); + + window->images = (VkImage *)malloc(window->image_count * sizeof(VkImage)); + assert(window->images); + + err = vk.fpGetSwapchainImagesKHR(vk_ctx.device, window->swapchain, &window->image_count, window->images); + assert(!err); + + window->views = (VkImageView *)malloc(window->image_count * sizeof(VkImageView)); + assert(window->views); + + for (uint32_t i = 0; i < window->image_count; i++) { + VkImageViewCreateInfo color_attachment_view = {0}; + color_attachment_view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + color_attachment_view.pNext = NULL; + color_attachment_view.format = window->format.format; + color_attachment_view.components.r = VK_COMPONENT_SWIZZLE_R; + color_attachment_view.components.g = VK_COMPONENT_SWIZZLE_G; + color_attachment_view.components.b = VK_COMPONENT_SWIZZLE_B; + color_attachment_view.components.a = VK_COMPONENT_SWIZZLE_A; + color_attachment_view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + color_attachment_view.subresourceRange.baseMipLevel = 0; + color_attachment_view.subresourceRange.levelCount = 1; + color_attachment_view.subresourceRange.baseArrayLayer = 0; + color_attachment_view.subresourceRange.layerCount = 1; + color_attachment_view.viewType = VK_IMAGE_VIEW_TYPE_2D; + color_attachment_view.flags = 0; + + // Render loop will expect image to have been used before and in + // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + // layout and will change to COLOR_ATTACHMENT_OPTIMAL, so init the image + // to that state + set_image_layout(window->images[i], VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + + color_attachment_view.image = window->images[i]; + + err = vkCreateImageView(vk_ctx.device, &color_attachment_view, NULL, &window->views[i]); + assert(!err); + } + + window->current_image = 0; + + const VkFormat depth_format = VK_FORMAT_D16_UNORM; + + if (window->depth_bits > 0) { + VkImageCreateInfo image = {0}; + image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image.pNext = NULL; + image.imageType = VK_IMAGE_TYPE_2D; + image.format = depth_format; + image.extent.width = window->width; + image.extent.height = window->height; + image.extent.depth = 1; + image.mipLevels = 1; + image.arrayLayers = 1; + image.samples = VK_SAMPLE_COUNT_1_BIT; + image.tiling = VK_IMAGE_TILING_OPTIMAL; + image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + image.flags = 0; + + VkMemoryAllocateInfo mem_alloc = {0}; + mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc.pNext = NULL; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; + + VkImageViewCreateInfo view = {0}; + view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view.pNext = NULL; + view.image = VK_NULL_HANDLE; + view.format = depth_format; + view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + view.subresourceRange.baseMipLevel = 0; + view.subresourceRange.levelCount = 1; + view.subresourceRange.baseArrayLayer = 0; + view.subresourceRange.layerCount = 1; + view.flags = 0; + view.viewType = VK_IMAGE_VIEW_TYPE_2D; + + VkMemoryRequirements mem_reqs = {0}; + bool pass; + + err = vkCreateImage(vk_ctx.device, &image, NULL, &window->depth.image); + assert(!err); + + vkGetImageMemoryRequirements(vk_ctx.device, window->depth.image, &mem_reqs); + + mem_alloc.allocationSize = mem_reqs.size; + pass = memory_type_from_properties(mem_reqs.memoryTypeBits, 0, &mem_alloc.memoryTypeIndex); + assert(pass); + + err = vkAllocateMemory(vk_ctx.device, &mem_alloc, NULL, &window->depth.memory); + assert(!err); + + err = vkBindImageMemory(vk_ctx.device, window->depth.image, window->depth.memory, 0); + assert(!err); + + set_image_layout(window->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + + view.image = window->depth.image; + err = vkCreateImageView(vk_ctx.device, &view, NULL, &window->depth.view); + assert(!err); + } + + VkAttachmentDescription attachments[2]; + attachments[0].format = window->format.format; + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + attachments[0].flags = 0; + + if (window->depth_bits > 0) { + attachments[1].format = depth_format; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].flags = 0; + } + + VkAttachmentReference color_reference = {0}; + color_reference.attachment = 0; + color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkAttachmentReference depth_reference = {0}; + depth_reference.attachment = 1; + depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {0}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = NULL; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_reference; + subpass.pResolveAttachments = NULL; + subpass.pDepthStencilAttachment = window->depth_bits > 0 ? &depth_reference : NULL; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = NULL; + + VkSubpassDependency dependencies[2]; + memset(&dependencies, 0, sizeof(dependencies)); + + // for the frame-buffer + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo rp_info = {0}; + rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rp_info.pNext = NULL; + rp_info.attachmentCount = window->depth_bits > 0 ? 2 : 1; + rp_info.pAttachments = attachments; + rp_info.subpassCount = 1; + rp_info.pSubpasses = &subpass; + rp_info.dependencyCount = 2; + rp_info.pDependencies = dependencies; + + err = vkCreateRenderPass(vk_ctx.device, &rp_info, NULL, &window->framebuffer_render_pass); + assert(!err); + + VkImageView attachmentViews[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE}; + + if (window->depth_bits > 0) { + attachmentViews[1] = window->depth.view; + } + + VkFramebufferCreateInfo fb_info = {0}; + fb_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fb_info.pNext = NULL; + fb_info.renderPass = window->framebuffer_render_pass; + fb_info.attachmentCount = window->depth_bits > 0 ? 2 : 1; + fb_info.pAttachments = attachmentViews; + fb_info.width = window->width; + fb_info.height = window->height; + fb_info.layers = 1; + + window->framebuffers = (VkFramebuffer *)malloc(window->image_count * sizeof(VkFramebuffer)); + assert(window->framebuffers); + + for (uint32_t i = 0; i < window->image_count; i++) { + attachmentViews[0] = window->views[i]; + err = vkCreateFramebuffer(vk_ctx.device, &fb_info, NULL, &window->framebuffers[i]); + assert(!err); + } + + flush_init_cmd(); +} + +void create_render_target_render_pass(struct vk_window *window) { + VkAttachmentDescription attachments[2]; + attachments[0].format = VK_FORMAT_B8G8R8A8_UNORM; // target->impl.format; // TODO + attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + attachments[0].flags = 0; + + VkAttachmentReference color_reference = {0}; + color_reference.attachment = 0; + color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {0}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = NULL; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_reference; + subpass.pResolveAttachments = NULL; + subpass.pDepthStencilAttachment = NULL; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = NULL; + + // for render-targets + VkSubpassDependency dependencies[2]; + memset(&dependencies, 0, sizeof(dependencies)); + + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo rp_info = {0}; + rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rp_info.pNext = NULL; + rp_info.attachmentCount = 1; + rp_info.pAttachments = attachments; + rp_info.subpassCount = 1; + rp_info.pSubpasses = &subpass; + rp_info.dependencyCount = 2; + rp_info.pDependencies = dependencies; + + VkResult err = vkCreateRenderPass(vk_ctx.device, &rp_info, NULL, &window->rendertarget_render_pass); + assert(!err); + + // depthBufferBits > 0 + attachments[1].format = VK_FORMAT_D16_UNORM; + attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[1].flags = 0; + + VkAttachmentReference depth_reference = {0}; + depth_reference.attachment = 1; + depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + subpass.pDepthStencilAttachment = &depth_reference; + + rp_info.attachmentCount = 2; + + err = vkCreateRenderPass(vk_ctx.device, &rp_info, NULL, &window->rendertarget_render_pass_with_depth); + assert(!err); +} + +void destroy_render_target_pass(struct vk_window *window) { + vkDestroyRenderPass(vk_ctx.device, window->rendertarget_render_pass, NULL); +} + +static bool check_extensions(const char **wanted_extensions, int wanted_extension_count, VkExtensionProperties *extensions, int extension_count) { + bool *found_extensions = calloc(wanted_extension_count, 1); + + for (int i = 0; i < extension_count; i++) { + for (int i2 = 0; i2 < wanted_extension_count; i2++) { + if (strcmp(wanted_extensions[i2], extensions[i].extensionName) == 0) { + found_extensions[i2] = true; + } + } + } + + bool missing_extensions = false; + + for (int i = 0; i < wanted_extension_count; i++) { + if (!found_extensions[i]) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to find extension %s", wanted_extensions[i]); + missing_extensions = true; + } + } + + free(found_extensions); + + return missing_extensions; +} + +static bool find_layer(VkLayerProperties *layers, int layer_count, const char *wanted_layer) { + for (int i = 0; i < layer_count; i++) { + if (strcmp(wanted_layer, layers[i].layerName) == 0) { + return true; + } + } + + return false; +} + +void kinc_g5_internal_init() { + VkResult err; + uint32_t instance_layer_count = 0; + + static const char *wanted_instance_layers[64]; + int wanted_instance_layer_count = 0; + + err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL); + assert(!err); + + if (instance_layer_count > 0) { + VkLayerProperties *instance_layers = (VkLayerProperties *)malloc(sizeof(VkLayerProperties) * instance_layer_count); + err = vkEnumerateInstanceLayerProperties(&instance_layer_count, instance_layers); + assert(!err); + +#ifdef VALIDATE + vk_ctx.validation_found = find_layer(instance_layers, instance_layer_count, "VK_LAYER_KHRONOS_validation"); + if (vk_ctx.validation_found) { + kinc_log(KINC_LOG_LEVEL_INFO, "Running with Vulkan validation layers enabled."); + wanted_instance_layers[wanted_instance_layer_count++] = "VK_LAYER_KHRONOS_validation"; + } +#endif + + free(instance_layers); + } + + static const char *wanted_instance_extensions[64]; + int wanted_instance_extension_count = 0; + + uint32_t instance_extension_count = 0; + + wanted_instance_extensions[wanted_instance_extension_count++] = VK_KHR_SURFACE_EXTENSION_NAME; + wanted_instance_extensions[wanted_instance_extension_count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; + kinc_vulkan_get_instance_extensions(wanted_instance_extensions, &wanted_instance_extension_count, ARRAY_SIZE(wanted_instance_extensions)); + + err = vkEnumerateInstanceExtensionProperties(NULL, &instance_extension_count, NULL); + assert(!err); + VkExtensionProperties *instance_extensions = (VkExtensionProperties *)malloc(sizeof(VkExtensionProperties) * instance_extension_count); + err = vkEnumerateInstanceExtensionProperties(NULL, &instance_extension_count, instance_extensions); + assert(!err); + bool missing_instance_extensions = + check_extensions(wanted_instance_extensions, wanted_instance_extension_count, instance_extensions, instance_extension_count); + + if (missing_instance_extensions) { + kinc_error(); + } + +#ifdef VALIDATE + // this extension should be provided by the validation layers + if (vk_ctx.validation_found) { + wanted_instance_extensions[wanted_instance_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + } +#endif + + VkApplicationInfo app = {0}; + app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app.pNext = NULL; + app.pApplicationName = kinc_application_name(); + app.applicationVersion = 0; + app.pEngineName = "Kore"; + app.engineVersion = 0; +#ifdef KINC_VKRT + app.apiVersion = VK_API_VERSION_1_2; +#else + app.apiVersion = VK_API_VERSION_1_0; +#endif + + VkInstanceCreateInfo info = {0}; + info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + info.pNext = NULL; + info.pApplicationInfo = &app; +#ifdef VALIDATE + if (vk_ctx.validation_found) { + info.enabledLayerCount = wanted_instance_layer_count; + info.ppEnabledLayerNames = (const char *const *)wanted_instance_layers; + } + else +#endif + { + info.enabledLayerCount = 0; + info.ppEnabledLayerNames = NULL; + } + info.enabledExtensionCount = wanted_instance_extension_count; + info.ppEnabledExtensionNames = (const char *const *)wanted_instance_extensions; + +#ifndef KINC_ANDROID + allocator.pfnAllocation = myalloc; + allocator.pfnFree = myfree; + allocator.pfnReallocation = myrealloc; + err = vkCreateInstance(&info, &allocator, &vk_ctx.instance); +#else + err = vkCreateInstance(&info, NULL, &vk_ctx.instance); +#endif + if (err == VK_ERROR_INCOMPATIBLE_DRIVER) { + kinc_error_message("Vulkan driver is incompatible"); + } + else if (err == VK_ERROR_EXTENSION_NOT_PRESENT) { + kinc_error_message("Vulkan extension not found"); + } + else if (err) { + kinc_error_message("Can not create Vulkan instance"); + } + + uint32_t gpu_count; + + err = vkEnumeratePhysicalDevices(vk_ctx.instance, &gpu_count, NULL); + assert(!err && gpu_count > 0); + + bool headless = false; + + // TODO: expose gpu selection to user? + if (gpu_count > 0) { + VkPhysicalDevice *physical_devices = (VkPhysicalDevice *)malloc(sizeof(VkPhysicalDevice) * gpu_count); + err = vkEnumeratePhysicalDevices(vk_ctx.instance, &gpu_count, physical_devices); + assert(!err); + // The device with the highest score is chosen. + float best_score = 0.0; + for (uint32_t gpu_idx = 0; gpu_idx < gpu_count; gpu_idx++) { + VkPhysicalDevice gpu = physical_devices[gpu_idx]; + uint32_t queue_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_count, NULL); + VkQueueFamilyProperties *queue_props = (VkQueueFamilyProperties *)malloc(queue_count * sizeof(VkQueueFamilyProperties)); + vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_count, queue_props); + bool can_present = false; + bool can_render = false; + // According to the documentation, a device that supports graphics must also support compute, + // Just to be 100% safe verify that it supports both anyway. + bool can_compute = false; + for (uint32_t i = 0; i < queue_count; i++) { + VkBool32 queue_supports_present = kinc_vulkan_get_physical_device_presentation_support(gpu, i); + if (queue_supports_present) { + can_present = true; + } + VkQueueFamilyProperties queue_properties = queue_props[i]; + uint32_t flags = queue_properties.queueFlags; + if ((flags & VK_QUEUE_GRAPHICS_BIT) != 0) { + can_render = true; + } + if ((flags & VK_QUEUE_COMPUTE_BIT) != 0) { + can_compute = true; + } + } + if (!can_present || !can_render || !can_compute) { + // This device is missing required features so move on + continue; + } + + // Score the device in order to compare it to others. + // Higher score = better. + float score = 0.0; + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(gpu, &properties); + switch (properties.deviceType) { + case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: + score += 10; + break; + case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: + score += 7; + break; + case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: + score += 5; + break; + case VK_PHYSICAL_DEVICE_TYPE_OTHER: + score += 1; + break; + case VK_PHYSICAL_DEVICE_TYPE_CPU: + // CPU gets a score of zero + break; + case VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM: + break; + } + // TODO: look into using more metrics than just the device type for scoring, eg: available memory, max texture sizes, etc. + // If this is the first usable device, skip testing against the previous best. + if (vk_ctx.gpu == VK_NULL_HANDLE || score > best_score) { + vk_ctx.gpu = gpu; + best_score = score; + } + } + if (vk_ctx.gpu == VK_NULL_HANDLE) { + if (headless) { + vk_ctx.gpu = physical_devices[0]; + } + else { + kinc_error_message("No Vulkan device that supports presentation found"); + } + } + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(vk_ctx.gpu, &properties); + kinc_log(KINC_LOG_LEVEL_INFO, "Chosen Vulkan device: %s", properties.deviceName); + free(physical_devices); + } + else { + kinc_error_message("No Vulkan device found"); + } + + static const char *wanted_device_layers[64]; + int wanted_device_layer_count = 0; + + uint32_t device_layer_count = 0; + err = vkEnumerateDeviceLayerProperties(vk_ctx.gpu, &device_layer_count, NULL); + assert(!err); + + if (device_layer_count > 0) { + VkLayerProperties *device_layers = (VkLayerProperties *)malloc(sizeof(VkLayerProperties) * device_layer_count); + err = vkEnumerateDeviceLayerProperties(vk_ctx.gpu, &device_layer_count, device_layers); + assert(!err); + +#ifdef VALIDATE + vk_ctx.validation_found = find_layer(device_layers, device_layer_count, "VK_LAYER_KHRONOS_validation"); + if (vk_ctx.validation_found) { + wanted_device_layers[wanted_device_layer_count++] = "VK_LAYER_KHRONOS_validation"; + } +#endif + + free(device_layers); + } + + const char *wanted_device_extensions[64]; + int wanted_device_extension_count = 0; + + wanted_device_extensions[wanted_device_extension_count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME; + // Allows negative viewport height to flip viewport + wanted_device_extensions[wanted_device_extension_count++] = VK_KHR_MAINTENANCE1_EXTENSION_NAME; + +#ifdef KINC_VKRT + wanted_device_extensions[wanted_device_extension_count++] = VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME; + wanted_device_extensions[wanted_device_extension_count++] = VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME; + wanted_device_extensions[wanted_device_extension_count++] = VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME; + wanted_device_extensions[wanted_device_extension_count++] = VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME; + wanted_device_extensions[wanted_device_extension_count++] = VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME; + wanted_device_extensions[wanted_device_extension_count++] = VK_KHR_SPIRV_1_4_EXTENSION_NAME; + wanted_device_extensions[wanted_device_extension_count++] = VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME; +#endif + +#ifndef VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME // For Dave's Debian +#define VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME "VK_KHR_format_feature_flags2" +#endif + + wanted_device_extensions[wanted_device_extension_count++] = VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME; + + uint32_t device_extension_count = 0; + + err = vkEnumerateDeviceExtensionProperties(vk_ctx.gpu, NULL, &device_extension_count, NULL); + assert(!err); + + VkExtensionProperties *device_extensions = (VkExtensionProperties *)malloc(sizeof(VkExtensionProperties) * device_extension_count); + err = vkEnumerateDeviceExtensionProperties(vk_ctx.gpu, NULL, &device_extension_count, device_extensions); + assert(!err); + + bool missing_device_extensions = check_extensions(wanted_device_extensions, wanted_device_extension_count, device_extensions, device_extension_count); + if (missing_device_extensions) { + wanted_device_extension_count -= 1; // remove VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME + } + missing_device_extensions = check_extensions(wanted_device_extensions, wanted_device_extension_count, device_extensions, device_extension_count); + + free(device_extensions); + + if (missing_device_extensions) { + exit(1); + } + +#ifdef VALIDATE + if (vk_ctx.validation_found) { + GET_INSTANCE_PROC_ADDR(vk_ctx.instance, CreateDebugUtilsMessengerEXT); + + VkDebugUtilsMessengerCreateInfoEXT dbgCreateInfo = {0}; + dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + dbgCreateInfo.flags = 0; + dbgCreateInfo.pfnUserCallback = vkDebugUtilsMessengerCallbackEXT; + dbgCreateInfo.pUserData = NULL; + dbgCreateInfo.pNext = NULL; + dbgCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; + dbgCreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + err = vk.fpCreateDebugUtilsMessengerEXT(vk_ctx.instance, &dbgCreateInfo, NULL, &vk_ctx.debug_messenger); + assert(!err); + } +#endif + + // Having these GIPA queries of vk_ctx.device extension entry points both + // BEFORE and AFTER vkCreateDevice is a good test for the loader + GET_INSTANCE_PROC_ADDR(vk_ctx.instance, GetPhysicalDeviceSurfaceCapabilitiesKHR); + GET_INSTANCE_PROC_ADDR(vk_ctx.instance, GetPhysicalDeviceSurfaceFormatsKHR); + GET_INSTANCE_PROC_ADDR(vk_ctx.instance, GetPhysicalDeviceSurfacePresentModesKHR); + GET_INSTANCE_PROC_ADDR(vk_ctx.instance, GetPhysicalDeviceSurfaceSupportKHR); + GET_INSTANCE_PROC_ADDR(vk_ctx.instance, CreateSwapchainKHR); + GET_INSTANCE_PROC_ADDR(vk_ctx.instance, DestroySwapchainKHR); + GET_INSTANCE_PROC_ADDR(vk_ctx.instance, DestroySurfaceKHR); + GET_INSTANCE_PROC_ADDR(vk_ctx.instance, GetSwapchainImagesKHR); + GET_INSTANCE_PROC_ADDR(vk_ctx.instance, AcquireNextImageKHR); + GET_INSTANCE_PROC_ADDR(vk_ctx.instance, QueuePresentKHR); + + vkGetPhysicalDeviceProperties(vk_ctx.gpu, &gpu_props); + + // Query with NULL data to get count + vkGetPhysicalDeviceQueueFamilyProperties(vk_ctx.gpu, &queue_count, NULL); + + queue_props = (VkQueueFamilyProperties *)malloc(queue_count * sizeof(VkQueueFamilyProperties)); + vkGetPhysicalDeviceQueueFamilyProperties(vk_ctx.gpu, &queue_count, queue_props); + assert(queue_count >= 1); + + if (!headless) { + // Iterate over each queue to learn whether it supports presenting: + VkBool32 *supportsPresent = (VkBool32 *)malloc(queue_count * sizeof(VkBool32)); + for (uint32_t i = 0; i < queue_count; i++) { + supportsPresent[i] = kinc_vulkan_get_physical_device_presentation_support(vk_ctx.gpu, i); + // vk.fpGetPhysicalDeviceSurfaceSupportKHR(vk_ctx.gpu, i, surface, &supportsPresent[i]); + } + + // Search for a graphics and a present queue in the array of queue + // families, try to find one that supports both + uint32_t graphicsQueueNodeIndex = UINT32_MAX; + uint32_t presentQueueNodeIndex = UINT32_MAX; + for (uint32_t i = 0; i < queue_count; i++) { + if ((queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { + if (graphicsQueueNodeIndex == UINT32_MAX) { + graphicsQueueNodeIndex = i; + } + + if (supportsPresent[i] == VK_TRUE) { + graphicsQueueNodeIndex = i; + presentQueueNodeIndex = i; + break; + } + } + } + if (presentQueueNodeIndex == UINT32_MAX) { + // If didn't find a queue that supports both graphics and present, then + // find a separate present queue. + for (uint32_t i = 0; i < queue_count; ++i) { + if (supportsPresent[i] == VK_TRUE) { + presentQueueNodeIndex = i; + break; + } + } + } + free(supportsPresent); + + // Generate error if could not find both a graphics and a present queue + if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX) { + kinc_error_message("Graphics or present queue not found"); + } + + // TODO: Add support for separate queues, including presentation, + // synchronization, and appropriate tracking for QueueSubmit. + // NOTE: While it is possible for an application to use a separate graphics + // and a present queues, this demo program assumes it is only using + // one: + if (graphicsQueueNodeIndex != presentQueueNodeIndex) { + kinc_error_message("Graphics and present queue do not match"); + } + + graphics_queue_node_index = graphicsQueueNodeIndex; + + { + float queue_priorities[1] = {0.0}; + VkDeviceQueueCreateInfo queue = {0}; + queue.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue.pNext = NULL; + queue.queueFamilyIndex = graphics_queue_node_index; + queue.queueCount = 1; + queue.pQueuePriorities = queue_priorities; + + VkDeviceCreateInfo deviceinfo = {0}; + deviceinfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceinfo.pNext = NULL; + deviceinfo.queueCreateInfoCount = 1; + deviceinfo.pQueueCreateInfos = &queue; + + deviceinfo.enabledLayerCount = wanted_device_layer_count; + deviceinfo.ppEnabledLayerNames = (const char *const *)wanted_device_layers; + + deviceinfo.enabledExtensionCount = wanted_device_extension_count; + deviceinfo.ppEnabledExtensionNames = (const char *const *)wanted_device_extensions; + +#ifdef KINC_VKRT + VkPhysicalDeviceRayTracingPipelineFeaturesKHR rayTracingPipelineExt = {0}; + rayTracingPipelineExt.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR; + rayTracingPipelineExt.pNext = NULL; + rayTracingPipelineExt.rayTracingPipeline = VK_TRUE; + + VkPhysicalDeviceAccelerationStructureFeaturesKHR rayTracingAccelerationStructureExt = {0}; + rayTracingAccelerationStructureExt.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR; + rayTracingAccelerationStructureExt.pNext = &rayTracingPipelineExt; + rayTracingAccelerationStructureExt.accelerationStructure = VK_TRUE; + + VkPhysicalDeviceBufferDeviceAddressFeatures bufferDeviceAddressExt = {0}; + bufferDeviceAddressExt.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES; + bufferDeviceAddressExt.pNext = &rayTracingAccelerationStructureExt; + bufferDeviceAddressExt.bufferDeviceAddress = VK_TRUE; + + deviceinfo.pNext = &bufferDeviceAddressExt; +#endif + + err = vkCreateDevice(vk_ctx.gpu, &deviceinfo, NULL, &vk_ctx.device); + assert(!err); + } + + vkGetDeviceQueue(vk_ctx.device, graphics_queue_node_index, 0, &vk_ctx.queue); + + vkGetPhysicalDeviceMemoryProperties(vk_ctx.gpu, &memory_properties); + + VkCommandPoolCreateInfo cmd_pool_info = {0}; + cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + cmd_pool_info.pNext = NULL; + cmd_pool_info.queueFamilyIndex = graphics_queue_node_index; + cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + + err = vkCreateCommandPool(vk_ctx.device, &cmd_pool_info, NULL, &vk_ctx.cmd_pool); + + createDescriptorLayout(); + create_compute_descriptor_layout(); + assert(!err); + } + + VkSemaphoreCreateInfo semInfo = {0}; + semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semInfo.pNext = NULL; + semInfo.flags = 0; + + err = vkCreateSemaphore(vk_ctx.device, &semInfo, NULL, &framebuffer_available); + assert(!err); + + err = vkCreateSemaphore(vk_ctx.device, &semInfo, NULL, &relay_semaphore); + assert(!err); +} + +void kinc_g5_internal_destroy() {} + +// this function is used in the android backend + +void kinc_vulkan_init_window(int window_index) { + assert(window_index < MAXIMUM_WINDOWS); + struct vk_window *window = &vk_ctx.windows[window_index]; + + // delay swapchain/surface recreation + // otherwise trouble ensues due to G4onG5 backend ending the command list in kinc_g4_begin + window->resized = true; + window->surface_destroyed = true; +} + +void kinc_g5_internal_init_window(int window_index, int depthBufferBits, int stencilBufferBits, bool vsync) { + assert(window_index < MAXIMUM_WINDOWS); + struct vk_window *window = &vk_ctx.windows[window_index]; + + window->depth_bits = depthBufferBits; + window->stencil_bits = stencilBufferBits; + window->vsynced = vsync; + + VkResult err = kinc_vulkan_create_surface(vk_ctx.instance, window_index, &window->surface); + assert(!err); + + VkBool32 surface_supported; + err = vkGetPhysicalDeviceSurfaceSupportKHR(vk_ctx.gpu, graphics_queue_node_index, window->surface, &surface_supported); + assert(!err); + assert(surface_supported); + + uint32_t formatCount; + err = vk.fpGetPhysicalDeviceSurfaceFormatsKHR(vk_ctx.gpu, window->surface, &formatCount, NULL); + assert(!err); + VkSurfaceFormatKHR *surfFormats = (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR)); + err = vk.fpGetPhysicalDeviceSurfaceFormatsKHR(vk_ctx.gpu, window->surface, &formatCount, surfFormats); + assert(!err); + // If the format list includes just one entry of VK_FORMAT_UNDEFINED, + // the surface has no preferred format. Otherwise, at least one + // supported format will be returned. + if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) { + window->format = surfFormats[0]; + } + else { + assert(formatCount >= 1); + bool found = false; + for (uint32_t i = 0; i < formatCount; ++i) { + // VK_FORMAT_B8G8R8A8_SRGB causes gamma-correction, making things bright + if (surfFormats[i].format != VK_FORMAT_B8G8R8A8_SRGB) { + window->format = surfFormats[i]; + found = true; + break; + } + } + if (!found) { + window->format = surfFormats[0]; + } + } + free(surfFormats); + window->width = kinc_window_width(window_index); + window->height = kinc_window_height(window_index); + create_swapchain(window_index); + create_render_target_render_pass(window); + + began = false; + kinc_g5_begin(NULL, 0); +} + +void kinc_g5_internal_destroy_window(int window_index) { + struct vk_window *window = &vk_ctx.windows[window_index]; + VkSwapchainKHR swapchain = cleanup_swapchain(window_index); + destroy_render_target_pass(window); + vk.fpDestroySwapchainKHR(vk_ctx.device, swapchain, NULL); + vk.fpDestroySurfaceKHR(vk_ctx.instance, window->surface, NULL); +} + +bool kinc_window_vsynced(int window) { + return true; +} + +bool kinc_g5_swap_buffers() { + return true; +} + +void kinc_g5_begin(kinc_g5_render_target_t *renderTarget, int window_index) { + struct vk_window *window = &vk_ctx.windows[window_index]; + + if (began) + return; + + if (window->resized) { + vkDeviceWaitIdle(vk_ctx.device); + create_swapchain(window_index); + } + + // Get the index of the next available swapchain image: + command_list_should_wait_for_framebuffer(); + VkResult err = -1; + do { + err = vk.fpAcquireNextImageKHR(vk_ctx.device, window->swapchain, UINT64_MAX, framebuffer_available, VK_NULL_HANDLE, &window->current_image); + if (err == VK_ERROR_SURFACE_LOST_KHR || err == VK_ERROR_OUT_OF_DATE_KHR) { + window->surface_destroyed = (err == VK_ERROR_SURFACE_LOST_KHR); + create_swapchain(window_index); + } + else { + assert(err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR); + began = true; + vk_ctx.current_window = window_index; + if (renderTarget != NULL) { + renderTarget->impl.framebuffer = window->framebuffers[window->current_image]; + } + return; + } + } while (err != VK_SUCCESS && err != VK_SUBOPTIMAL_KHR); +} + +void kinc_g5_end(int window) { + VkPresentInfoKHR present = {0}; + present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present.pNext = NULL; + present.swapchainCount = 1; + present.pSwapchains = &vk_ctx.windows[vk_ctx.current_window].swapchain; + present.pImageIndices = &vk_ctx.windows[vk_ctx.current_window].current_image; + present.pWaitSemaphores = &relay_semaphore; + present.waitSemaphoreCount = 1; + wait_for_relay = false; + + VkResult err = vk.fpQueuePresentKHR(vk_ctx.queue, &present); + if (err == VK_ERROR_SURFACE_LOST_KHR) { + vkDeviceWaitIdle(vk_ctx.device); + vk_ctx.windows[window].surface_destroyed = true; + create_swapchain(window); + } + else if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) { + vkDeviceWaitIdle(vk_ctx.device); + create_swapchain(window); + } + else { + assert(err == VK_SUCCESS); + } + + reuse_descriptor_sets(); + reuse_compute_descriptor_sets(); + began = false; +} + +void kinc_g5_flush() { + vkDeviceWaitIdle(vk_ctx.device); +} + +// this is exclusively used by the Android backend at the moment +bool kinc_vulkan_internal_get_size(int *width, int *height) { + if (vk_ctx.windows[0].surface) { + VkSurfaceCapabilitiesKHR capabilities; + VkResult err = vk.fpGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_ctx.gpu, vk_ctx.windows[0].surface, &capabilities); + assert(!err); + *width = capabilities.currentExtent.width; + *height = capabilities.currentExtent.height; + return true; + } + else { + return false; + } +} + +bool kinc_g5_supports_raytracing() { +#ifdef KINC_VKRT + return true; +#else + return false; +#endif +} + +bool kinc_g5_supports_instanced_rendering() { + return true; +} + +bool kinc_g5_supports_compute_shaders() { + return true; +} + +bool kinc_g5_supports_blend_constants() { + return true; +} + +bool kinc_g5_supports_non_pow2_textures() { + return true; +} + +bool kinc_g5_render_targets_inverted_y() { + return false; +} + +int kinc_g5_max_bound_textures(void) { + VkPhysicalDeviceProperties props; + vkGetPhysicalDeviceProperties(vk_ctx.gpu, &props); + return props.limits.maxPerStageDescriptorSamplers; +} diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/commandlist.c.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/commandlist.c.h new file mode 100644 index 000000000..6ab444baf --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/commandlist.c.h @@ -0,0 +1,931 @@ +#include "kinc/graphics5/sampler.h" +#include "vulkan.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +extern kinc_g5_texture_t *vulkanTextures[16]; +extern kinc_g5_render_target_t *vulkanRenderTargets[16]; +VkDescriptorSet getDescriptorSet(void); +static VkDescriptorSet get_compute_descriptor_set(void); +bool memory_type_from_properties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex); +void setImageLayout(VkCommandBuffer _buffer, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout); + +VkRenderPassBeginInfo currentRenderPassBeginInfo; +VkPipeline currentVulkanPipeline; +kinc_g5_render_target_t *currentRenderTargets[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; + +static bool onBackBuffer = false; +static uint32_t lastVertexConstantBufferOffset = 0; +static uint32_t lastFragmentConstantBufferOffset = 0; +static uint32_t lastComputeConstantBufferOffset = 0; +static kinc_g5_pipeline_t *currentPipeline = NULL; +static kinc_g5_compute_shader *current_compute_shader = NULL; +static int mrtIndex = 0; +static VkFramebuffer mrtFramebuffer[16]; +static VkRenderPass mrtRenderPass[16]; + +static bool in_render_pass = false; + +static void endPass(kinc_g5_command_list_t *list) { + if (in_render_pass) { + vkCmdEndRenderPass(list->impl._buffer); + in_render_pass = false; + } + + for (int i = 0; i < 16; ++i) { + vulkanTextures[i] = NULL; + vulkanRenderTargets[i] = NULL; + } +} + +static int formatSize(VkFormat format) { + switch (format) { + case VK_FORMAT_R32G32B32A32_SFLOAT: + return 16; + case VK_FORMAT_R16G16B16A16_SFLOAT: + return 8; + case VK_FORMAT_R16_SFLOAT: + return 2; + case VK_FORMAT_R8_UNORM: + return 1; + default: + return 4; + } +} + +void set_image_layout(VkImage image, VkImageAspectFlags aspectMask, VkImageLayout old_image_layout, VkImageLayout new_image_layout) { + VkResult err; + + if (vk_ctx.setup_cmd == VK_NULL_HANDLE) { + VkCommandBufferAllocateInfo cmd = {0}; + cmd.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmd.pNext = NULL; + cmd.commandPool = vk_ctx.cmd_pool; + cmd.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd.commandBufferCount = 1; + + err = vkAllocateCommandBuffers(vk_ctx.device, &cmd, &vk_ctx.setup_cmd); + assert(!err); + + VkCommandBufferBeginInfo cmd_buf_info = {0}; + cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmd_buf_info.pNext = NULL; + cmd_buf_info.flags = 0; + cmd_buf_info.pInheritanceInfo = NULL; + + err = vkBeginCommandBuffer(vk_ctx.setup_cmd, &cmd_buf_info); + assert(!err); + } + + VkImageMemoryBarrier image_memory_barrier = {0}; + image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + image_memory_barrier.pNext = NULL; + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = 0; + image_memory_barrier.oldLayout = old_image_layout; + image_memory_barrier.newLayout = new_image_layout; + image_memory_barrier.image = image; + image_memory_barrier.subresourceRange.aspectMask = aspectMask; + image_memory_barrier.subresourceRange.baseMipLevel = 0; + image_memory_barrier.subresourceRange.levelCount = 1; + image_memory_barrier.subresourceRange.baseArrayLayer = 0; + image_memory_barrier.subresourceRange.layerCount = 1; + + if (old_image_layout != VK_IMAGE_LAYOUT_UNDEFINED) { + image_memory_barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) { + image_memory_barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + // Make sure anything that was copying from this image has completed + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + image_memory_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + image_memory_barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + // Make sure any Copy or CPU writes to image are flushed + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; + } + + VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + } + if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + } + + vkCmdPipelineBarrier(vk_ctx.setup_cmd, srcStageMask, dstStageMask, 0, 0, NULL, 0, NULL, 1, &image_memory_barrier); +} + +void setup_init_cmd() { + if (vk_ctx.setup_cmd == VK_NULL_HANDLE) { + VkCommandBufferAllocateInfo cmd = {0}; + cmd.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmd.pNext = NULL; + cmd.commandPool = vk_ctx.cmd_pool; + cmd.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd.commandBufferCount = 1; + + VkResult err = vkAllocateCommandBuffers(vk_ctx.device, &cmd, &vk_ctx.setup_cmd); + assert(!err); + + VkCommandBufferBeginInfo cmd_buf_info = {0}; + cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmd_buf_info.pNext = NULL; + cmd_buf_info.flags = 0; + cmd_buf_info.pInheritanceInfo = NULL; + + err = vkBeginCommandBuffer(vk_ctx.setup_cmd, &cmd_buf_info); + assert(!err); + } +} + +void flush_init_cmd() { + VkResult err; + + if (vk_ctx.setup_cmd == VK_NULL_HANDLE) + return; + + err = vkEndCommandBuffer(vk_ctx.setup_cmd); + assert(!err); + + const VkCommandBuffer cmd_bufs[] = {vk_ctx.setup_cmd}; + VkFence nullFence = {VK_NULL_HANDLE}; + VkSubmitInfo submit_info = {0}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = NULL; + submit_info.waitSemaphoreCount = 0; + submit_info.pWaitSemaphores = NULL; + submit_info.pWaitDstStageMask = NULL; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = cmd_bufs; + submit_info.signalSemaphoreCount = 0; + submit_info.pSignalSemaphores = NULL; + + err = vkQueueSubmit(vk_ctx.queue, 1, &submit_info, nullFence); + assert(!err); + + err = vkQueueWaitIdle(vk_ctx.queue); + assert(!err); + + vkFreeCommandBuffers(vk_ctx.device, vk_ctx.cmd_pool, 1, cmd_bufs); + vk_ctx.setup_cmd = VK_NULL_HANDLE; +} + +void set_viewport_and_scissor(kinc_g5_command_list_t *list) { + VkViewport viewport; + memset(&viewport, 0, sizeof(viewport)); + VkRect2D scissor; + memset(&scissor, 0, sizeof(scissor)); + + if (currentRenderTargets[0] == NULL || currentRenderTargets[0]->framebuffer_index >= 0) { + viewport.x = 0; + viewport.y = (float)kinc_window_height(vk_ctx.current_window); + viewport.width = (float)kinc_window_width(vk_ctx.current_window); + viewport.height = -(float)kinc_window_height(vk_ctx.current_window); + viewport.minDepth = (float)0.0f; + viewport.maxDepth = (float)1.0f; + scissor.extent.width = kinc_window_width(vk_ctx.current_window); + scissor.extent.height = kinc_window_height(vk_ctx.current_window); + scissor.offset.x = 0; + scissor.offset.y = 0; + } + else { + viewport.x = 0; + viewport.y = (float)currentRenderTargets[0]->height; + viewport.width = (float)currentRenderTargets[0]->width; + viewport.height = -(float)currentRenderTargets[0]->height; + viewport.minDepth = (float)0.0f; + viewport.maxDepth = (float)1.0f; + scissor.extent.width = currentRenderTargets[0]->width; + scissor.extent.height = currentRenderTargets[0]->height; + scissor.offset.x = 0; + scissor.offset.y = 0; + } + + vkCmdSetViewport(list->impl._buffer, 0, 1, &viewport); + vkCmdSetScissor(list->impl._buffer, 0, 1, &scissor); +} + +void kinc_g5_command_list_init(kinc_g5_command_list_t *list) { + VkCommandBufferAllocateInfo cmd = {0}; + cmd.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmd.pNext = NULL; + cmd.commandPool = vk_ctx.cmd_pool; + cmd.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd.commandBufferCount = 1; + + VkResult err = vkAllocateCommandBuffers(vk_ctx.device, &cmd, &list->impl._buffer); + assert(!err); + + VkFenceCreateInfo fenceInfo = {0}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.pNext = NULL; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + err = vkCreateFence(vk_ctx.device, &fenceInfo, NULL, &list->impl.fence); + assert(!err); + + list->impl._indexCount = 0; +} + +void kinc_g5_command_list_destroy(kinc_g5_command_list_t *list) { + vkFreeCommandBuffers(vk_ctx.device, vk_ctx.cmd_pool, 1, &list->impl._buffer); + vkDestroyFence(vk_ctx.device, list->impl.fence, NULL); +} + +void kinc_g5_command_list_begin(kinc_g5_command_list_t *list) { + VkResult err = vkWaitForFences(vk_ctx.device, 1, &list->impl.fence, VK_TRUE, UINT64_MAX); + assert(!err); + + vkResetCommandBuffer(list->impl._buffer, 0); + VkCommandBufferBeginInfo cmd_buf_info = {0}; + cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + cmd_buf_info.pNext = NULL; + cmd_buf_info.flags = 0; + cmd_buf_info.pInheritanceInfo = NULL; + + VkClearValue clear_values[2]; + memset(clear_values, 0, sizeof(VkClearValue) * 2); + clear_values[0].color.float32[0] = 0.0f; + clear_values[0].color.float32[1] = 0.0f; + clear_values[0].color.float32[2] = 0.0f; + clear_values[0].color.float32[3] = 1.0f; + if (vk_ctx.windows[vk_ctx.current_window].depth_bits > 0) { + clear_values[1].depthStencil.depth = 1.0; + clear_values[1].depthStencil.stencil = 0; + } + + VkRenderPassBeginInfo rp_begin = {0}; + rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + rp_begin.pNext = NULL; + rp_begin.renderPass = vk_ctx.windows[vk_ctx.current_window].framebuffer_render_pass; + rp_begin.framebuffer = vk_ctx.windows[vk_ctx.current_window].framebuffers[vk_ctx.windows[vk_ctx.current_window].current_image]; + rp_begin.renderArea.offset.x = 0; + rp_begin.renderArea.offset.y = 0; + rp_begin.renderArea.extent.width = vk_ctx.windows[vk_ctx.current_window].width; + rp_begin.renderArea.extent.height = vk_ctx.windows[vk_ctx.current_window].height; + rp_begin.clearValueCount = vk_ctx.windows[vk_ctx.current_window].depth_bits > 0 ? 2 : 1; + rp_begin.pClearValues = clear_values; + + err = vkBeginCommandBuffer(list->impl._buffer, &cmd_buf_info); + assert(!err); + + VkImageMemoryBarrier prePresentBarrier = {0}; + prePresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + prePresentBarrier.pNext = NULL; + prePresentBarrier.srcAccessMask = 0; + prePresentBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + prePresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + prePresentBarrier.subresourceRange.baseMipLevel = 0; + prePresentBarrier.subresourceRange.levelCount = 1; + prePresentBarrier.subresourceRange.baseArrayLayer = 0; + prePresentBarrier.subresourceRange.layerCount = 1; + + prePresentBarrier.image = vk_ctx.windows[vk_ctx.current_window].images[vk_ctx.windows[vk_ctx.current_window].current_image]; + VkImageMemoryBarrier *pmemory_barrier = &prePresentBarrier; + vkCmdPipelineBarrier(list->impl._buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1, + pmemory_barrier); + + vkCmdBeginRenderPass(list->impl._buffer, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); + currentRenderPassBeginInfo = rp_begin; + in_render_pass = true; + + set_viewport_and_scissor(list); + + onBackBuffer = true; + + for (int i = 0; i < mrtIndex; ++i) { + vkDestroyFramebuffer(vk_ctx.device, mrtFramebuffer[i], NULL); + vkDestroyRenderPass(vk_ctx.device, mrtRenderPass[i], NULL); + } + mrtIndex = 0; +} + +void kinc_g5_command_list_end(kinc_g5_command_list_t *list) { + vkCmdEndRenderPass(list->impl._buffer); + + VkImageMemoryBarrier prePresentBarrier = {0}; + prePresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + prePresentBarrier.pNext = NULL; + prePresentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + prePresentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + prePresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + prePresentBarrier.subresourceRange.baseMipLevel = 0; + prePresentBarrier.subresourceRange.levelCount = 1; + prePresentBarrier.subresourceRange.baseArrayLayer = 0; + prePresentBarrier.subresourceRange.layerCount = 1; + + prePresentBarrier.image = vk_ctx.windows[vk_ctx.current_window].images[vk_ctx.windows[vk_ctx.current_window].current_image]; + VkImageMemoryBarrier *pmemory_barrier = &prePresentBarrier; + vkCmdPipelineBarrier(list->impl._buffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, pmemory_barrier); + + VkResult err = vkEndCommandBuffer(list->impl._buffer); + assert(!err); +} + +void kinc_g5_command_list_clear(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget, unsigned flags, unsigned color, float depth, + int stencil) { + VkClearRect clearRect = {0}; + clearRect.rect.offset.x = 0; + clearRect.rect.offset.y = 0; + clearRect.rect.extent.width = renderTarget->width; + clearRect.rect.extent.height = renderTarget->height; + clearRect.baseArrayLayer = 0; + clearRect.layerCount = 1; + + int count = 0; + VkClearAttachment attachments[2]; + if (flags & KINC_G5_CLEAR_COLOR) { + VkClearColorValue clearColor = {0}; + clearColor.float32[0] = ((color & 0x00ff0000) >> 16) / 255.0f; + clearColor.float32[1] = ((color & 0x0000ff00) >> 8) / 255.0f; + clearColor.float32[2] = (color & 0x000000ff) / 255.0f; + clearColor.float32[3] = ((color & 0xff000000) >> 24) / 255.0f; + attachments[count].aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + attachments[count].colorAttachment = 0; + attachments[count].clearValue.color = clearColor; + count++; + } + if (((flags & KINC_G5_CLEAR_DEPTH) || (flags & KINC_G5_CLEAR_STENCIL)) && renderTarget->impl.depthBufferBits > 0) { + attachments[count].aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; // | VK_IMAGE_ASPECT_STENCIL_BIT; + attachments[count].clearValue.depthStencil.depth = depth; + attachments[count].clearValue.depthStencil.stencil = stencil; + count++; + } + vkCmdClearAttachments(list->impl._buffer, count, attachments, 1, &clearRect); +} + +void kinc_g5_command_list_render_target_to_framebuffer_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) {} + +void kinc_g5_command_list_framebuffer_to_render_target_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) {} + +void kinc_g5_command_list_draw_indexed_vertices(kinc_g5_command_list_t *list) { + kinc_g5_command_list_draw_indexed_vertices_from_to(list, 0, list->impl._indexCount); +} + +void kinc_g5_command_list_draw_indexed_vertices_from_to(kinc_g5_command_list_t *list, int start, int count) { + vkCmdDrawIndexed(list->impl._buffer, count, 1, start, 0, 0); +} + +void kinc_g5_command_list_draw_indexed_vertices_from_to_from(kinc_g5_command_list_t *list, int start, int count, int vertex_offset) { + vkCmdDrawIndexed(list->impl._buffer, count, 1, start, vertex_offset, 0); +} + +void kinc_g5_command_list_draw_indexed_vertices_instanced(kinc_g5_command_list_t *list, int instanceCount) { + kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(list, instanceCount, 0, list->impl._indexCount); +} + +void kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(kinc_g5_command_list_t *list, int instanceCount, int start, int count) { + vkCmdDrawIndexed(list->impl._buffer, count, instanceCount, start, 0, 0); +} + +void kinc_g5_command_list_viewport(kinc_g5_command_list_t *list, int x, int y, int width, int height) { + VkViewport viewport; + memset(&viewport, 0, sizeof(viewport)); + viewport.x = (float)x; + viewport.y = y + (float)height; + viewport.width = (float)width; + viewport.height = (float)-height; + viewport.minDepth = (float)0.0f; + viewport.maxDepth = (float)1.0f; + vkCmdSetViewport(list->impl._buffer, 0, 1, &viewport); +} + +void kinc_g5_command_list_scissor(kinc_g5_command_list_t *list, int x, int y, int width, int height) { + VkRect2D scissor; + memset(&scissor, 0, sizeof(scissor)); + scissor.extent.width = width; + scissor.extent.height = height; + scissor.offset.x = x; + scissor.offset.y = y; + vkCmdSetScissor(list->impl._buffer, 0, 1, &scissor); +} + +void kinc_g5_command_list_disable_scissor(kinc_g5_command_list_t *list) { + VkRect2D scissor; + memset(&scissor, 0, sizeof(scissor)); + if (currentRenderTargets[0] == NULL || currentRenderTargets[0]->framebuffer_index >= 0) { + scissor.extent.width = kinc_window_width(vk_ctx.current_window); + scissor.extent.height = kinc_window_height(vk_ctx.current_window); + } + else { + scissor.extent.width = currentRenderTargets[0]->width; + scissor.extent.height = currentRenderTargets[0]->height; + } + vkCmdSetScissor(list->impl._buffer, 0, 1, &scissor); +} + +void kinc_g5_command_list_set_pipeline(kinc_g5_command_list_t *list, struct kinc_g5_pipeline *pipeline) { + currentPipeline = pipeline; + lastVertexConstantBufferOffset = 0; + lastFragmentConstantBufferOffset = 0; + + if (onBackBuffer) { + currentVulkanPipeline = currentPipeline->impl.framebuffer_pipeline; + vkCmdBindPipeline(list->impl._buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, currentPipeline->impl.framebuffer_pipeline); + } + else { + currentVulkanPipeline = currentPipeline->impl.rendertarget_pipeline; + vkCmdBindPipeline(list->impl._buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, currentPipeline->impl.rendertarget_pipeline); + } +} + +void kinc_g5_command_list_set_blend_constant(kinc_g5_command_list_t *list, float r, float g, float b, float a) { + const float blendConstants[4] = {r, g, b, a}; + vkCmdSetBlendConstants(list->impl._buffer, blendConstants); +} + +void kinc_g5_command_list_set_vertex_buffers(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer **vertexBuffers, int *offsets_, int count) { +// this seems to be a no-op function? +// kinc_g5_internal_vertex_buffer_set(vertexBuffers[0], 0); +#ifdef KINC_WINDOWS + VkBuffer *buffers = (VkBuffer *)alloca(sizeof(VkBuffer) * count); + VkDeviceSize *offsets = (VkDeviceSize *)alloca(sizeof(VkDeviceSize) * count); +#else + VkBuffer buffers[count]; + VkDeviceSize offsets[count]; +#endif + for (int i = 0; i < count; ++i) { + buffers[i] = vertexBuffers[i]->impl.vertices.buf; + offsets[i] = (VkDeviceSize)(offsets_[i] * kinc_g5_vertex_buffer_stride(vertexBuffers[i])); + } + vkCmdBindVertexBuffers(list->impl._buffer, 0, count, buffers, offsets); +} + +void kinc_g5_command_list_set_index_buffer(kinc_g5_command_list_t *list, struct kinc_g5_index_buffer *indexBuffer) { + list->impl._indexCount = kinc_g5_index_buffer_count(indexBuffer); + vkCmdBindIndexBuffer(list->impl._buffer, indexBuffer->impl.buf, 0, + indexBuffer->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32); +} + +void kinc_internal_restore_render_target(kinc_g5_command_list_t *list, struct kinc_g5_render_target *target) { + VkViewport viewport; + memset(&viewport, 0, sizeof(viewport)); + viewport.x = 0; + viewport.y = (float)kinc_window_height(vk_ctx.current_window); + viewport.width = (float)kinc_window_width(vk_ctx.current_window); + viewport.height = -(float)kinc_window_height(vk_ctx.current_window); + viewport.minDepth = (float)0.0f; + viewport.maxDepth = (float)1.0f; + vkCmdSetViewport(list->impl._buffer, 0, 1, &viewport); + VkRect2D scissor; + memset(&scissor, 0, sizeof(scissor)); + scissor.extent.width = kinc_window_width(vk_ctx.current_window); + scissor.extent.height = kinc_window_height(vk_ctx.current_window); + scissor.offset.x = 0; + scissor.offset.y = 0; + vkCmdSetScissor(list->impl._buffer, 0, 1, &scissor); + + if (onBackBuffer && in_render_pass) { + return; + } + + endPass(list); + + currentRenderTargets[0] = NULL; + onBackBuffer = true; + + VkClearValue clear_values[2]; + memset(clear_values, 0, sizeof(VkClearValue) * 2); + clear_values[0].color.float32[0] = 0.0f; + clear_values[0].color.float32[1] = 0.0f; + clear_values[0].color.float32[2] = 0.0f; + clear_values[0].color.float32[3] = 1.0f; + clear_values[1].depthStencil.depth = 1.0; + clear_values[1].depthStencil.stencil = 0; + VkRenderPassBeginInfo rp_begin = {0}; + rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + rp_begin.pNext = NULL; + rp_begin.renderPass = vk_ctx.windows[vk_ctx.current_window].framebuffer_render_pass; + rp_begin.framebuffer = vk_ctx.windows[vk_ctx.current_window].framebuffers[vk_ctx.windows[vk_ctx.current_window].current_image]; + rp_begin.renderArea.offset.x = 0; + rp_begin.renderArea.offset.y = 0; + rp_begin.renderArea.extent.width = kinc_window_width(vk_ctx.current_window); + rp_begin.renderArea.extent.height = kinc_window_height(vk_ctx.current_window); + rp_begin.clearValueCount = 2; + rp_begin.pClearValues = clear_values; + vkCmdBeginRenderPass(list->impl._buffer, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); + currentRenderPassBeginInfo = rp_begin; + in_render_pass = true; + + if (currentPipeline != NULL) { + currentVulkanPipeline = currentPipeline->impl.framebuffer_pipeline; + vkCmdBindPipeline(list->impl._buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, currentPipeline->impl.framebuffer_pipeline); + } +} + +void kinc_g5_command_list_set_render_targets(kinc_g5_command_list_t *list, struct kinc_g5_render_target **targets, int count) { + for (int i = 0; i < count; ++i) { + currentRenderTargets[i] = targets[i]; + } + for (int i = count; i < 8; ++i) { + currentRenderTargets[i] = NULL; + } + + if (targets[0]->framebuffer_index >= 0) { + kinc_internal_restore_render_target(list, targets[0]); + return; + } + + endPass(list); + + onBackBuffer = false; + + VkClearValue clear_values[9]; + memset(clear_values, 0, sizeof(VkClearValue)); + for (int i = 0; i < count; ++i) { + clear_values[i].color.float32[0] = 0.0f; + clear_values[i].color.float32[1] = 0.0f; + clear_values[i].color.float32[2] = 0.0f; + clear_values[i].color.float32[3] = 1.0f; + } + clear_values[count].depthStencil.depth = 1.0; + clear_values[count].depthStencil.stencil = 0; + + VkRenderPassBeginInfo rp_begin = {0}; + rp_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + rp_begin.pNext = NULL; + rp_begin.renderArea.offset.x = 0; + rp_begin.renderArea.offset.y = 0; + rp_begin.renderArea.extent.width = targets[0]->width; + rp_begin.renderArea.extent.height = targets[0]->height; + rp_begin.clearValueCount = count + 1; + rp_begin.pClearValues = clear_values; + + if (count == 1) { + if (targets[0]->impl.depthBufferBits > 0) { + rp_begin.renderPass = vk_ctx.windows[vk_ctx.current_window].rendertarget_render_pass_with_depth; + } + else { + rp_begin.renderPass = vk_ctx.windows[vk_ctx.current_window].rendertarget_render_pass; + } + rp_begin.framebuffer = targets[0]->impl.framebuffer; + } + else { + VkAttachmentDescription attachments[9]; + for (int i = 0; i < count; ++i) { + attachments[i].format = targets[i]->impl.format; + attachments[i].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[i].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[i].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + attachments[i].flags = 0; + } + + if (targets[0]->impl.depthBufferBits > 0) { + attachments[count].format = VK_FORMAT_D16_UNORM; + attachments[count].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[count].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[count].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[count].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[count].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[count].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[count].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[count].flags = 0; + } + + VkAttachmentReference color_references[8]; + for (int i = 0; i < count; ++i) { + color_references[i].attachment = i; + color_references[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + + VkAttachmentReference depth_reference = {0}; + depth_reference.attachment = count; + depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {0}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = NULL; + subpass.colorAttachmentCount = count; + subpass.pColorAttachments = color_references; + subpass.pResolveAttachments = NULL; + subpass.pDepthStencilAttachment = targets[0]->impl.depthBufferBits > 0 ? &depth_reference : NULL; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = NULL; + + VkSubpassDependency dependencies[2]; + memset(&dependencies, 0, sizeof(dependencies)); + + // TODO: For multi-targets-rendering + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo rp_info = {0}; + rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rp_info.pNext = NULL; + rp_info.attachmentCount = targets[0]->impl.depthBufferBits > 0 ? count + 1 : count; + rp_info.pAttachments = attachments; + rp_info.subpassCount = 1; + rp_info.pSubpasses = &subpass; + rp_info.dependencyCount = 2; + rp_info.pDependencies = dependencies; + + VkResult err = vkCreateRenderPass(vk_ctx.device, &rp_info, NULL, &mrtRenderPass[mrtIndex]); + assert(!err); + + VkImageView attachmentsViews[9]; + for (int i = 0; i < count; ++i) { + attachmentsViews[i] = targets[i]->impl.sourceView; + } + if (targets[0]->impl.depthBufferBits > 0) { + attachmentsViews[count] = targets[0]->impl.depthView; + } + + VkFramebufferCreateInfo fbufCreateInfo = {0}; + fbufCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbufCreateInfo.pNext = NULL; + fbufCreateInfo.renderPass = mrtRenderPass[mrtIndex]; + fbufCreateInfo.attachmentCount = targets[0]->impl.depthBufferBits > 0 ? count + 1 : count; + fbufCreateInfo.pAttachments = attachmentsViews; + fbufCreateInfo.width = targets[0]->width; + fbufCreateInfo.height = targets[0]->height; + fbufCreateInfo.layers = 1; + + err = vkCreateFramebuffer(vk_ctx.device, &fbufCreateInfo, NULL, &mrtFramebuffer[mrtIndex]); + assert(!err); + + rp_begin.renderPass = mrtRenderPass[mrtIndex]; + rp_begin.framebuffer = mrtFramebuffer[mrtIndex]; + mrtIndex++; + } + + vkCmdBeginRenderPass(list->impl._buffer, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); + currentRenderPassBeginInfo = rp_begin; + in_render_pass = true; + + VkViewport viewport; + memset(&viewport, 0, sizeof(viewport)); + viewport.x = 0; + viewport.y = (float)targets[0]->height; + viewport.width = (float)targets[0]->width; + viewport.height = -(float)targets[0]->height; + viewport.minDepth = (float)0.0f; + viewport.maxDepth = (float)1.0f; + vkCmdSetViewport(list->impl._buffer, 0, 1, &viewport); + + VkRect2D scissor; + memset(&scissor, 0, sizeof(scissor)); + scissor.extent.width = targets[0]->width; + scissor.extent.height = targets[0]->height; + scissor.offset.x = 0; + scissor.offset.y = 0; + vkCmdSetScissor(list->impl._buffer, 0, 1, &scissor); + + if (currentPipeline != NULL) { + currentVulkanPipeline = currentPipeline->impl.rendertarget_pipeline; + vkCmdBindPipeline(list->impl._buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, currentPipeline->impl.rendertarget_pipeline); + } +} + +void kinc_g5_command_list_upload_index_buffer(kinc_g5_command_list_t *list, struct kinc_g5_index_buffer *buffer) {} + +void kinc_g5_command_list_upload_vertex_buffer(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer *buffer) {} + +void kinc_g5_command_list_upload_texture(kinc_g5_command_list_t *list, struct kinc_g5_texture *texture) {} + +void kinc_g5_command_list_get_render_target_pixels(kinc_g5_command_list_t *list, kinc_g5_render_target_t *render_target, uint8_t *data) { + VkFormat format = render_target->impl.format; + int formatByteSize = formatSize(format); + + // Create readback buffer + if (!render_target->impl.readbackBufferCreated) { + VkBufferCreateInfo buf_info = {0}; + buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buf_info.pNext = NULL; + buf_info.size = render_target->width * render_target->height * formatByteSize; + buf_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + buf_info.flags = 0; + vkCreateBuffer(vk_ctx.device, &buf_info, NULL, &render_target->impl.readbackBuffer); + + VkMemoryRequirements mem_reqs = {0}; + vkGetBufferMemoryRequirements(vk_ctx.device, render_target->impl.readbackBuffer, &mem_reqs); + + VkMemoryAllocateInfo mem_alloc; + memset(&mem_alloc, 0, sizeof(VkMemoryAllocateInfo)); + mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc.pNext = NULL; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; + mem_alloc.allocationSize = mem_reqs.size; + memory_type_from_properties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &mem_alloc.memoryTypeIndex); + vkAllocateMemory(vk_ctx.device, &mem_alloc, NULL, &render_target->impl.readbackMemory); + vkBindBufferMemory(vk_ctx.device, render_target->impl.readbackBuffer, render_target->impl.readbackMemory, 0); + + render_target->impl.readbackBufferCreated = true; + } + + vkCmdEndRenderPass(list->impl._buffer); + setImageLayout(list->impl._buffer, render_target->impl.sourceImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + VkBufferImageCopy region; + region.bufferOffset = 0; + region.bufferRowLength = render_target->width; + region.bufferImageHeight = render_target->height; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.baseArrayLayer = 0; + region.imageSubresource.layerCount = 1; + region.imageSubresource.mipLevel = 0; + region.imageOffset.x = 0; + region.imageOffset.y = 0; + region.imageOffset.z = 0; + region.imageExtent.width = (uint32_t)render_target->width; + region.imageExtent.height = (uint32_t)render_target->height; + region.imageExtent.depth = 1; + vkCmdCopyImageToBuffer(list->impl._buffer, render_target->impl.sourceImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, render_target->impl.readbackBuffer, 1, + ®ion); + + setImageLayout(list->impl._buffer, render_target->impl.sourceImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + vkCmdBeginRenderPass(list->impl._buffer, ¤tRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + in_render_pass = true; + + kinc_g5_command_list_end(list); + kinc_g5_command_list_execute(list); + kinc_g5_command_list_wait_for_execution_to_finish(list); + kinc_g5_command_list_begin(list); + + // Read buffer + void *p; + vkMapMemory(vk_ctx.device, render_target->impl.readbackMemory, 0, VK_WHOLE_SIZE, 0, (void **)&p); + memcpy(data, p, render_target->texWidth * render_target->texHeight * formatByteSize); + vkUnmapMemory(vk_ctx.device, render_target->impl.readbackMemory); +} + +void kinc_g5_command_list_texture_to_render_target_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) { + // render-passes are used to transition render-targets +} + +void kinc_g5_command_list_render_target_to_texture_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) { + // render-passes are used to transition render-targets +} + +void kinc_g5_command_list_set_vertex_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size) { + lastVertexConstantBufferOffset = offset; +} + +void kinc_g5_command_list_set_fragment_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size) { + lastFragmentConstantBufferOffset = offset; + + VkDescriptorSet descriptor_set = getDescriptorSet(); + uint32_t offsets[2] = {lastVertexConstantBufferOffset, lastFragmentConstantBufferOffset}; + vkCmdBindDescriptorSets(list->impl._buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, currentPipeline->impl.pipeline_layout, 0, 1, &descriptor_set, 2, offsets); +} + +void kinc_g5_command_list_set_compute_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size) { + lastComputeConstantBufferOffset = offset; + + VkDescriptorSet descriptor_set = get_compute_descriptor_set(); + uint32_t offsets[2] = {lastComputeConstantBufferOffset, lastComputeConstantBufferOffset}; + vkCmdBindDescriptorSets(list->impl._buffer, VK_PIPELINE_BIND_POINT_COMPUTE, current_compute_shader->impl.pipeline_layout, 0, 1, &descriptor_set, 2, + offsets); +} + +static bool wait_for_framebuffer = false; + +static void command_list_should_wait_for_framebuffer(void) { + wait_for_framebuffer = true; +} + +void kinc_g5_command_list_execute(kinc_g5_command_list_t *list) { + // Make sure the previous execution is done, so we can reuse the fence + // Not optimal of course + VkResult err = vkWaitForFences(vk_ctx.device, 1, &list->impl.fence, VK_TRUE, UINT64_MAX); + assert(!err); + vkResetFences(vk_ctx.device, 1, &list->impl.fence); + + VkPipelineStageFlags pipe_stage_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + VkSubmitInfo submit_info = {0}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = NULL; + + VkSemaphore semaphores[2] = {framebuffer_available, relay_semaphore}; + VkPipelineStageFlags dst_stage_flags[2] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT}; + if (wait_for_framebuffer) { + submit_info.pWaitSemaphores = semaphores; + submit_info.pWaitDstStageMask = dst_stage_flags; + submit_info.waitSemaphoreCount = wait_for_relay ? 2 : 1; + wait_for_framebuffer = false; + } + else if (wait_for_relay) { + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &semaphores[1]; + submit_info.pWaitDstStageMask = &dst_stage_flags[1]; + } + + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &list->impl._buffer; + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &relay_semaphore; + wait_for_relay = true; + + err = vkQueueSubmit(vk_ctx.queue, 1, &submit_info, list->impl.fence); + assert(!err); +} + +void kinc_g5_command_list_wait_for_execution_to_finish(kinc_g5_command_list_t *list) { + VkResult err = vkWaitForFences(vk_ctx.device, 1, &list->impl.fence, VK_TRUE, UINT64_MAX); + assert(!err); +} + +void kinc_g5_command_list_set_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture) { + vulkanTextures[unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]] = texture; + vulkanRenderTargets[unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]] = NULL; +} + +void kinc_g5_command_list_set_sampler(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_sampler_t *sampler) { + if (unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] >= 0) { + vulkanSamplers[unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]] = sampler->impl.sampler; + } +} + +void kinc_g5_command_list_set_image_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture) { + vulkanTextures[unit.stages[KINC_G5_SHADER_TYPE_COMPUTE]] = texture; + vulkanRenderTargets[unit.stages[KINC_G5_SHADER_TYPE_COMPUTE]] = NULL; +} + +void kinc_g5_command_list_set_render_target_face(kinc_g5_command_list_t *list, kinc_g5_render_target_t *texture, int face) {} + +void kinc_g5_command_list_set_texture_from_render_target(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_render_target_t *target) { + target->impl.stage = unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]; + vulkanRenderTargets[unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]] = target; + vulkanTextures[unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]] = NULL; +} + +void kinc_g5_command_list_set_texture_from_render_target_depth(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_render_target_t *target) { + target->impl.stage_depth = unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]; + vulkanRenderTargets[unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]] = target; + vulkanTextures[unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT]] = NULL; +} + +void kinc_g5_command_list_set_compute_shader(kinc_g5_command_list_t *list, kinc_g5_compute_shader *shader) { + current_compute_shader = shader; + vkCmdBindPipeline(list->impl._buffer, VK_PIPELINE_BIND_POINT_COMPUTE, shader->impl.pipeline); + //**vkCmdBindDescriptorSets(list->impl._buffer, VK_PIPELINE_BIND_POINT_COMPUTE, shader->impl.pipeline_layout, 0, 1, &shader->impl.descriptor_set, 0, 0); +} + +void kinc_g5_command_list_compute(kinc_g5_command_list_t *list, int x, int y, int z) { + if (in_render_pass) { + vkCmdEndRenderPass(list->impl._buffer); + in_render_pass = false; + } + + vkCmdDispatch(list->impl._buffer, x, y, z); + + int render_target_count = 0; + for (int i = 0; i < 8; ++i) { + if (currentRenderTargets[i] == NULL) { + break; + } + ++render_target_count; + } + if (render_target_count > 0) { + kinc_g5_command_list_set_render_targets(list, currentRenderTargets, render_target_count); + } +} diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/commandlist.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/commandlist.h new file mode 100644 index 000000000..91463e980 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/commandlist.h @@ -0,0 +1,9 @@ +#pragma once + +#include "MiniVulkan.h" + +typedef struct { + int _indexCount; + VkCommandBuffer _buffer; + VkFence fence; +} CommandList5Impl; diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/compute.c.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/compute.c.h new file mode 100644 index 000000000..3c26676e0 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/compute.c.h @@ -0,0 +1,237 @@ +#include + +#include +#include +#include + +static void parse_shader(uint32_t *shader_source, int shader_length, kinc_internal_named_number *locations, kinc_internal_named_number *textureBindings, + kinc_internal_named_number *uniformOffsets); + +static VkShaderModule create_shader_module(const void *code, size_t size); + +static VkDescriptorPool compute_descriptor_pool; + +static void create_compute_descriptor_layout(void) { + VkDescriptorSetLayoutBinding layoutBindings[18]; + memset(layoutBindings, 0, sizeof(layoutBindings)); + + layoutBindings[0].binding = 0; + layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + layoutBindings[0].descriptorCount = 1; + layoutBindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + layoutBindings[0].pImmutableSamplers = NULL; + + layoutBindings[1].binding = 1; + layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + layoutBindings[1].descriptorCount = 1; + layoutBindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + layoutBindings[1].pImmutableSamplers = NULL; + + for (int i = 2; i < 18; ++i) { + layoutBindings[i].binding = i; + layoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + layoutBindings[i].descriptorCount = 1; + layoutBindings[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + layoutBindings[i].pImmutableSamplers = NULL; + } + + VkDescriptorSetLayoutCreateInfo descriptor_layout = {0}; + descriptor_layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptor_layout.pNext = NULL; + descriptor_layout.bindingCount = 18; + descriptor_layout.pBindings = layoutBindings; + + VkResult err = vkCreateDescriptorSetLayout(vk_ctx.device, &descriptor_layout, NULL, &compute_descriptor_layout); + assert(!err); + + VkDescriptorPoolSize typeCounts[2]; + memset(typeCounts, 0, sizeof(typeCounts)); + + typeCounts[0].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + typeCounts[0].descriptorCount = 2 * 1024; + + typeCounts[1].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + typeCounts[1].descriptorCount = 16 * 1024; + + VkDescriptorPoolCreateInfo pool_info = {0}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.pNext = NULL; + pool_info.maxSets = 1024; + pool_info.poolSizeCount = 2; + pool_info.pPoolSizes = typeCounts; + + err = vkCreateDescriptorPool(vk_ctx.device, &pool_info, NULL, &compute_descriptor_pool); + assert(!err); +} + +void kinc_g5_compute_shader_init(kinc_g5_compute_shader *shader, void *_data, int length) { + memset(shader->impl.locations, 0, sizeof(kinc_internal_named_number) * KINC_INTERNAL_NAMED_NUMBER_COUNT); + memset(shader->impl.offsets, 0, sizeof(kinc_internal_named_number) * KINC_INTERNAL_NAMED_NUMBER_COUNT); + memset(shader->impl.texture_bindings, 0, sizeof(kinc_internal_named_number) * KINC_INTERNAL_NAMED_NUMBER_COUNT); + parse_shader((uint32_t *)_data, length, shader->impl.locations, shader->impl.texture_bindings, shader->impl.offsets); + + VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {0}; + pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pPipelineLayoutCreateInfo.pNext = NULL; + pPipelineLayoutCreateInfo.setLayoutCount = 1; + pPipelineLayoutCreateInfo.pSetLayouts = &compute_descriptor_layout; + + VkResult err = vkCreatePipelineLayout(vk_ctx.device, &pPipelineLayoutCreateInfo, NULL, &shader->impl.pipeline_layout); + assert(!err); + + VkComputePipelineCreateInfo pipeline_info = {0}; + + memset(&pipeline_info, 0, sizeof(pipeline_info)); + pipeline_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + pipeline_info.layout = shader->impl.pipeline_layout; + + VkPipelineShaderStageCreateInfo shader_stage; + memset(&shader_stage, 0, sizeof(VkPipelineShaderStageCreateInfo)); + + shader_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shader_stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; + shader->impl.shader_module = create_shader_module(_data, (size_t)length); + shader_stage.module = shader->impl.shader_module; + shader_stage.pName = "main"; + + pipeline_info.stage = shader_stage; + + err = vkCreateComputePipelines(vk_ctx.device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &shader->impl.pipeline); + assert(!err); + + vkDestroyShaderModule(vk_ctx.device, shader->impl.shader_module, NULL); + + /*shader->impl.length = (int)(length - index); + shader->impl.data = (uint8_t *)malloc(shader->impl.length); + assert(shader->impl.data != NULL); + memcpy(shader->impl.data, &data[index], shader->impl.length); + + VkShaderModuleCreateInfo module_create_info; + module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + module_create_info.pNext = NULL; + module_create_info.codeSize = shader->impl.length; + module_create_info.pCode = (const uint32_t *)shader->impl.data; + module_create_info.flags = 0; + + VkShaderModule module; + VkResult err = vkCreateShaderModule(vk_ctx.device, &module_create_info, NULL, &module); + assert(!err); + + VkPipelineShaderStageCreateInfo compute_shader_stage_info = {0}; + compute_shader_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + compute_shader_stage_info.stage = VK_SHADER_STAGE_COMPUTE_BIT; + compute_shader_stage_info.module = module; + compute_shader_stage_info.pName = "main"; + + VkDescriptorSetLayoutBinding layout_bindings[1] = {0}; + layout_bindings[0].binding = 0; + layout_bindings[0].descriptorCount = 1; + layout_bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + layout_bindings[0].pImmutableSamplers = NULL; + layout_bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + + /*layout_bindings[1].binding = 1; + layout_bindings[1].descriptorCount = 1; + layout_bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + layout_bindings[1].pImmutableSamplers = NULL; + layout_bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; + + layout_bindings[2].binding = 2; + layout_bindings[2].descriptorCount = 1; + layout_bindings[2].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + layout_bindings[2].pImmutableSamplers = NULL; + layout_bindings[2].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;*/ + + /*VkDescriptorSetLayoutCreateInfo layoutInfo = {0}; + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 1; + layoutInfo.pBindings = layout_bindings; + + VkDescriptorSetLayout descriptor_set_layout; + if (vkCreateDescriptorSetLayout(vk_ctx.device, &layoutInfo, NULL, &descriptor_set_layout) != VK_SUCCESS) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Could not initialize compute shader."); + return; + } + + VkPipelineLayoutCreateInfo pipeline_layout_info = {0}; + pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_info.setLayoutCount = 1; + pipeline_layout_info.pSetLayouts = &descriptor_set_layout; + + if (vkCreatePipelineLayout(vk_ctx.device, &pipeline_layout_info, NULL, &shader->impl.pipeline_layout) != VK_SUCCESS) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Could not initialize compute shader."); + return; + } + + VkComputePipelineCreateInfo pipeline_info = {0}; + memset(&pipeline_info, 0, sizeof(pipeline_info)); + pipeline_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + pipeline_info.layout = shader->impl.pipeline_layout; + pipeline_info.stage = compute_shader_stage_info; + + if (vkCreateComputePipelines(vk_ctx.device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &shader->impl.pipeline) != VK_SUCCESS) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Could not initialize compute shader."); + return; + }*/ +} + +void kinc_g5_compute_shader_destroy(kinc_g5_compute_shader *shader) {} + +kinc_g5_constant_location_t kinc_g5_compute_shader_get_constant_location(kinc_g5_compute_shader *shader, const char *name) { + kinc_g5_constant_location_t location = {0}; + + uint32_t hash = kinc_internal_hash_name((unsigned char *)name); + + /*kinc_compute_internal_shader_constant_t *constant = findComputeConstant(shader->impl.constants, hash); + if (constant == NULL) { + location.impl.computeOffset = 0; + } + else { + location.impl.computeOffset = constant->offset; + } + + if (location.impl.computeOffset == 0) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Uniform %s not found.", name); + }*/ + + return location; +} + +kinc_g5_texture_unit_t kinc_g5_compute_shader_get_texture_unit(kinc_g5_compute_shader *shader, const char *name) { + char unitName[64]; + int unitOffset = 0; + size_t len = strlen(name); + if (len > 63) + len = 63; + strncpy(unitName, name, len + 1); + if (unitName[len - 1] == ']') { // Check for array - mySampler[2] + unitOffset = (int)(unitName[len - 2] - '0'); // Array index is unit offset + unitName[len - 3] = 0; // Strip array from name + } + + uint32_t hash = kinc_internal_hash_name((unsigned char *)unitName); + + kinc_g5_texture_unit_t unit; + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + unit.stages[i] = -1; + } + unit.stages[KINC_G5_SHADER_TYPE_COMPUTE] = 0; + /*kinc_internal_hash_index_t *compute_unit = findComputeTextureUnit(shader->impl.textures, hash); + if (compute_unit == NULL) { +#ifndef NDEBUG + static int notFoundCount = 0; + if (notFoundCount < 10) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Sampler %s not found.", unitName); + ++notFoundCount; + } + else if (notFoundCount == 10) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Giving up on sampler not found messages.", unitName); + ++notFoundCount; + } +#endif + } + else { + unit.stages[KINC_G5_SHADER_TYPE_COMPUTE] = compute_unit->index + unitOffset; + }*/ + return unit; +} diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/compute.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/compute.h new file mode 100644 index 000000000..702516aaa --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/compute.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include "MiniVulkan.h" + +#include "named_number.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_g5_compute_shader_impl { + kinc_internal_named_number locations[KINC_INTERNAL_NAMED_NUMBER_COUNT]; + kinc_internal_named_number texture_bindings[KINC_INTERNAL_NAMED_NUMBER_COUNT]; + kinc_internal_named_number offsets[KINC_INTERNAL_NAMED_NUMBER_COUNT]; + + VkPipelineLayout pipeline_layout; + VkPipeline pipeline; + VkShaderModule shader_module; +} kinc_g5_compute_shader_impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/constantbuffer.c.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/constantbuffer.c.h new file mode 100644 index 000000000..cfa98ab78 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/constantbuffer.c.h @@ -0,0 +1,89 @@ +#include "vulkan.h" + +#include + +bool memory_type_from_properties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex); + +// VkBuffer *vk_ctx.vertex_uniform_buffer = NULL; +// VkBuffer *vk_ctx.fragment_uniform_buffer = NULL; + +bool kinc_g5_transposeMat3 = true; +bool kinc_g5_transposeMat4 = true; + +static void createUniformBuffer(VkBuffer *buf, VkMemoryAllocateInfo *mem_alloc, VkDeviceMemory *mem, VkDescriptorBufferInfo *buffer_info, int size) { + VkBufferCreateInfo buf_info; + memset(&buf_info, 0, sizeof(buf_info)); + buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buf_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + buf_info.size = size; + VkResult err = vkCreateBuffer(vk_ctx.device, &buf_info, NULL, buf); + assert(!err); + + VkMemoryRequirements mem_reqs; + vkGetBufferMemoryRequirements(vk_ctx.device, *buf, &mem_reqs); + + mem_alloc->sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc->pNext = NULL; + mem_alloc->allocationSize = mem_reqs.size; + mem_alloc->memoryTypeIndex = 0; + + bool pass = memory_type_from_properties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &mem_alloc->memoryTypeIndex); + assert(pass); + + err = vkAllocateMemory(vk_ctx.device, mem_alloc, NULL, mem); + assert(!err); + + err = vkBindBufferMemory(vk_ctx.device, *buf, *mem, 0); + assert(!err); + + buffer_info->buffer = *buf; + buffer_info->offset = 0; + buffer_info->range = size; +} + +void kinc_g5_constant_buffer_init(kinc_g5_constant_buffer_t *buffer, int size) { + buffer->impl.mySize = size; + buffer->data = NULL; + + createUniformBuffer(&buffer->impl.buf, &buffer->impl.mem_alloc, &buffer->impl.mem, &buffer->impl.buffer_info, size); + + // buffer hack + if (vk_ctx.vertex_uniform_buffer == NULL) { + vk_ctx.vertex_uniform_buffer = &buffer->impl.buf; + } + else if (vk_ctx.fragment_uniform_buffer == NULL) { + vk_ctx.fragment_uniform_buffer = &buffer->impl.buf; + } + else if (vk_ctx.compute_uniform_buffer == NULL) { + vk_ctx.compute_uniform_buffer = &buffer->impl.buf; + } + + void *p; + VkResult err = vkMapMemory(vk_ctx.device, buffer->impl.mem, 0, buffer->impl.mem_alloc.allocationSize, 0, (void **)&p); + assert(!err); + memset(p, 0, buffer->impl.mem_alloc.allocationSize); + vkUnmapMemory(vk_ctx.device, buffer->impl.mem); +} + +void kinc_g5_constant_buffer_destroy(kinc_g5_constant_buffer_t *buffer) { + vkFreeMemory(vk_ctx.device, buffer->impl.mem, NULL); + vkDestroyBuffer(vk_ctx.device, buffer->impl.buf, NULL); +} + +void kinc_g5_constant_buffer_lock_all(kinc_g5_constant_buffer_t *buffer) { + kinc_g5_constant_buffer_lock(buffer, 0, kinc_g5_constant_buffer_size(buffer)); +} + +void kinc_g5_constant_buffer_lock(kinc_g5_constant_buffer_t *buffer, int start, int count) { + VkResult err = vkMapMemory(vk_ctx.device, buffer->impl.mem, start, count, 0, (void **)&buffer->data); + assert(!err); +} + +void kinc_g5_constant_buffer_unlock(kinc_g5_constant_buffer_t *buffer) { + vkUnmapMemory(vk_ctx.device, buffer->impl.mem); + buffer->data = NULL; +} + +int kinc_g5_constant_buffer_size(kinc_g5_constant_buffer_t *buffer) { + return buffer->impl.mySize; +} diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/constantbuffer.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/constantbuffer.h new file mode 100644 index 000000000..35971d166 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/constantbuffer.h @@ -0,0 +1,13 @@ +#pragma once + +#include "MiniVulkan.h" + +typedef struct { + VkBuffer buf; + VkDescriptorBufferInfo buffer_info; + VkMemoryAllocateInfo mem_alloc; + VkDeviceMemory mem; + int lastStart; + int lastCount; + int mySize; +} ConstantBuffer5Impl; diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/graphics.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/graphics.h new file mode 100644 index 000000000..056f2a534 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/graphics.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/indexbuffer.c.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/indexbuffer.c.h new file mode 100644 index 000000000..8c3ae7187 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/indexbuffer.c.h @@ -0,0 +1,95 @@ +#include "vulkan.h" + +#include + +bool memory_type_from_properties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex); + +kinc_g5_index_buffer_t *currentIndexBuffer = NULL; + +static void unset(kinc_g5_index_buffer_t *buffer) { + if (currentIndexBuffer == buffer) { + currentIndexBuffer = NULL; + } +} + +void kinc_g5_index_buffer_init(kinc_g5_index_buffer_t *buffer, int indexCount, kinc_g5_index_buffer_format_t format, bool gpuMemory) { + buffer->impl.count = indexCount; + buffer->impl.format = format; + + VkBufferCreateInfo buf_info = {0}; + buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buf_info.pNext = NULL; + buf_info.size = format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? indexCount * sizeof(uint16_t) : indexCount * sizeof(uint32_t); + buf_info.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT; +#ifdef KINC_VKRT + buf_info.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + buf_info.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + buf_info.usage |= VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR; +#endif + buf_info.flags = 0; + + memset(&buffer->impl.mem_alloc, 0, sizeof(VkMemoryAllocateInfo)); + buffer->impl.mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + buffer->impl.mem_alloc.pNext = NULL; + buffer->impl.mem_alloc.allocationSize = 0; + buffer->impl.mem_alloc.memoryTypeIndex = 0; + + buffer->impl.buf = NULL; + buffer->impl.mem = NULL; + + VkResult err = vkCreateBuffer(vk_ctx.device, &buf_info, NULL, &buffer->impl.buf); + assert(!err); + + VkMemoryRequirements mem_reqs = {0}; + vkGetBufferMemoryRequirements(vk_ctx.device, buffer->impl.buf, &mem_reqs); + + buffer->impl.mem_alloc.allocationSize = mem_reqs.size; + bool pass = memory_type_from_properties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &buffer->impl.mem_alloc.memoryTypeIndex); + assert(pass); + +#ifdef KINC_VKRT + VkMemoryAllocateFlagsInfo memory_allocate_flags_info = {0}; + memory_allocate_flags_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + memory_allocate_flags_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + buffer->impl.mem_alloc.pNext = &memory_allocate_flags_info; +#endif + + err = vkAllocateMemory(vk_ctx.device, &buffer->impl.mem_alloc, NULL, &buffer->impl.mem); + assert(!err); + + err = vkBindBufferMemory(vk_ctx.device, buffer->impl.buf, buffer->impl.mem, 0); + assert(!err); +} + +void kinc_g5_index_buffer_destroy(kinc_g5_index_buffer_t *buffer) { + unset(buffer); + vkFreeMemory(vk_ctx.device, buffer->impl.mem, NULL); + vkDestroyBuffer(vk_ctx.device, buffer->impl.buf, NULL); +} + +static int kinc_g5_internal_index_buffer_stride(kinc_g5_index_buffer_t *buffer) { + return buffer->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? 2 : 4; +} + +void *kinc_g5_index_buffer_lock_all(kinc_g5_index_buffer_t *buffer) { + return kinc_g5_index_buffer_lock(buffer, 0, kinc_g5_index_buffer_count(buffer)); +} + +void *kinc_g5_index_buffer_lock(kinc_g5_index_buffer_t *buffer, int start, int count) { + uint8_t *data; + VkResult err = vkMapMemory(vk_ctx.device, buffer->impl.mem, 0, buffer->impl.mem_alloc.allocationSize, 0, (void **)&data); + assert(!err); + return &data[start * kinc_g5_internal_index_buffer_stride(buffer)]; +} + +void kinc_g5_index_buffer_unlock_all(kinc_g5_index_buffer_t *buffer) { + vkUnmapMemory(vk_ctx.device, buffer->impl.mem); +} + +void kinc_g5_index_buffer_unlock(kinc_g5_index_buffer_t *buffer, int count) { + kinc_g5_index_buffer_unlock_all(buffer); +} + +int kinc_g5_index_buffer_count(kinc_g5_index_buffer_t *buffer) { + return buffer->impl.count; +} diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/indexbuffer.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/indexbuffer.h new file mode 100644 index 000000000..5ae4a72b3 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/indexbuffer.h @@ -0,0 +1,12 @@ +#pragma once + +#include "MiniVulkan.h" + +typedef struct { + int count; + int format; + + VkBuffer buf; + VkDeviceMemory mem; + VkMemoryAllocateInfo mem_alloc; +} IndexBuffer5Impl; diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/named_number.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/named_number.h new file mode 100644 index 000000000..bc69e1ff8 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/named_number.h @@ -0,0 +1,8 @@ +#pragma once + +#define KINC_INTERNAL_NAMED_NUMBER_COUNT 32 + +typedef struct { + char name[256]; + uint32_t number; +} kinc_internal_named_number; diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/pipeline.c.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/pipeline.c.h new file mode 100644 index 000000000..6e17516eb --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/pipeline.c.h @@ -0,0 +1,1224 @@ +#include "vulkan.h" + +#include +#include +#include + +#include + +VkDescriptorSetLayout desc_layout; +extern kinc_g5_texture_t *vulkanTextures[16]; +extern kinc_g5_render_target_t *vulkanRenderTargets[16]; +extern uint32_t swapchainImageCount; +extern uint32_t current_buffer; +bool memory_type_from_properties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex); + +static VkDescriptorPool descriptor_pool; + +static bool has_number(kinc_internal_named_number *named_numbers, const char *name) { + for (int i = 0; i < KINC_INTERNAL_NAMED_NUMBER_COUNT; ++i) { + if (strcmp(named_numbers[i].name, name) == 0) { + return true; + } + } + return false; +} + +static uint32_t find_number(kinc_internal_named_number *named_numbers, const char *name) { + for (int i = 0; i < KINC_INTERNAL_NAMED_NUMBER_COUNT; ++i) { + if (strcmp(named_numbers[i].name, name) == 0) { + return named_numbers[i].number; + } + } + return -1; +} + +static void set_number(kinc_internal_named_number *named_numbers, const char *name, uint32_t number) { + for (int i = 0; i < KINC_INTERNAL_NAMED_NUMBER_COUNT; ++i) { + if (strcmp(named_numbers[i].name, name) == 0) { + named_numbers[i].number = number; + return; + } + } + + for (int i = 0; i < KINC_INTERNAL_NAMED_NUMBER_COUNT; ++i) { + if (named_numbers[i].name[0] == 0) { + strcpy(named_numbers[i].name, name); + named_numbers[i].number = number; + return; + } + } + + assert(false); +} + +struct indexed_name { + uint32_t id; + char *name; +}; + +struct indexed_index { + uint32_t id; + uint32_t value; +}; + +#define MAX_THINGS 256 +static struct indexed_name names[MAX_THINGS]; +static uint32_t names_size = 0; +static struct indexed_name memberNames[MAX_THINGS]; +static uint32_t memberNames_size = 0; +static struct indexed_index locs[MAX_THINGS]; +static uint32_t locs_size = 0; +static struct indexed_index bindings[MAX_THINGS]; +static uint32_t bindings_size = 0; +static struct indexed_index offsets[MAX_THINGS]; +static uint32_t offsets_size = 0; + +static void add_name(uint32_t id, char *name) { + names[names_size].id = id; + names[names_size].name = name; + ++names_size; +} + +static char *find_name(uint32_t id) { + for (uint32_t i = 0; i < names_size; ++i) { + if (names[i].id == id) { + return names[i].name; + } + } + return NULL; +} + +static void add_member_name(uint32_t id, char *name) { + memberNames[memberNames_size].id = id; + memberNames[memberNames_size].name = name; + ++memberNames_size; +} + +static char *find_member_name(uint32_t id) { + for (uint32_t i = 0; i < memberNames_size; ++i) { + if (memberNames[i].id == id) { + return memberNames[i].name; + } + } + return NULL; +} + +static void add_location(uint32_t id, uint32_t location) { + locs[locs_size].id = id; + locs[locs_size].value = location; + ++locs_size; +} + +static void add_binding(uint32_t id, uint32_t binding) { + bindings[bindings_size].id = id; + bindings[bindings_size].value = binding; + ++bindings_size; +} + +static void add_offset(uint32_t id, uint32_t offset) { + offsets[offsets_size].id = id; + offsets[offsets_size].value = offset; + ++offsets_size; +} + +static void parse_shader(uint32_t *shader_source, int shader_length, kinc_internal_named_number *locations, kinc_internal_named_number *textureBindings, + kinc_internal_named_number *uniformOffsets) { + names_size = 0; + memberNames_size = 0; + locs_size = 0; + bindings_size = 0; + offsets_size = 0; + + uint32_t *spirv = (uint32_t *)shader_source; + int spirvsize = shader_length / 4; + int index = 0; + + uint32_t magicNumber = spirv[index++]; + uint32_t version = spirv[index++]; + uint32_t generator = spirv[index++]; + uint32_t bound = spirv[index++]; + index++; + + while (index < spirvsize) { + int wordCount = spirv[index] >> 16; + uint32_t opcode = spirv[index] & 0xffff; + + uint32_t *operands = wordCount > 1 ? &spirv[index + 1] : NULL; + uint32_t length = wordCount - 1; + + switch (opcode) { + case 5: { // OpName + uint32_t id = operands[0]; + char *string = (char *)&operands[1]; + add_name(id, string); + break; + } + case 6: { // OpMemberName + uint32_t type = operands[0]; + char *name = find_name(type); + if (name != NULL && strcmp(name, "_k_global_uniform_buffer_type") == 0) { + uint32_t member = operands[1]; + char *string = (char *)&operands[2]; + add_member_name(member, string); + } + break; + } + case 71: { // OpDecorate + uint32_t id = operands[0]; + uint32_t decoration = operands[1]; + if (decoration == 30) { // location + uint32_t location = operands[2]; + add_location(id, location); + } + if (decoration == 33) { // binding + uint32_t binding = operands[2]; + add_binding(id, binding); + } + break; + } + case 72: { // OpMemberDecorate + uint32_t type = operands[0]; + char *name = find_name(type); + if (name != NULL && strcmp(name, "_k_global_uniform_buffer_type") == 0) { + uint32_t member = operands[1]; + uint32_t decoration = operands[2]; + if (decoration == 35) { // offset + uint32_t offset = operands[3]; + add_offset(member, offset); + } + } + break; + } + } + + index += wordCount; + } + + for (uint32_t i = 0; i < locs_size; ++i) { + char *name = find_name(locs[i].id); + if (name != NULL) { + set_number(locations, name, locs[i].value); + } + } + + for (uint32_t i = 0; i < bindings_size; ++i) { + char *name = find_name(bindings[i].id); + if (name != NULL) { + set_number(textureBindings, name, bindings[i].value); + } + } + + for (uint32_t i = 0; i < offsets_size; ++i) { + char *name = find_member_name(offsets[i].id); + if (name != NULL) { + set_number(uniformOffsets, name, offsets[i].value); + } + } +} + +static VkShaderModule create_shader_module(const void *code, size_t size) { + VkShaderModuleCreateInfo moduleCreateInfo; + moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + moduleCreateInfo.pNext = NULL; + moduleCreateInfo.codeSize = size; + moduleCreateInfo.pCode = (const uint32_t *)code; + moduleCreateInfo.flags = 0; + + VkShaderModule module; + VkResult err = vkCreateShaderModule(vk_ctx.device, &moduleCreateInfo, NULL, &module); + assert(!err); + + return module; +} + +static VkShaderModule prepare_vs(VkShaderModule *vert_shader_module, kinc_g5_shader_t *vertexShader) { + *vert_shader_module = create_shader_module(vertexShader->impl.source, vertexShader->impl.length); + return *vert_shader_module; +} + +static VkShaderModule prepare_fs(VkShaderModule *frag_shader_module, kinc_g5_shader_t *fragmentShader) { + *frag_shader_module = create_shader_module(fragmentShader->impl.source, fragmentShader->impl.length); + return *frag_shader_module; +} + +static VkFormat convert_format(kinc_g5_render_target_format_t format) { + switch (format) { + case KINC_G5_RENDER_TARGET_FORMAT_128BIT_FLOAT: + return VK_FORMAT_R32G32B32A32_SFLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_64BIT_FLOAT: + return VK_FORMAT_R16G16B16A16_SFLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT: + return VK_FORMAT_R32_SFLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT: + return VK_FORMAT_R16_SFLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_8BIT_RED: + return VK_FORMAT_R8_UNORM; + case KINC_G5_RENDER_TARGET_FORMAT_32BIT: + default: + return VK_FORMAT_B8G8R8A8_UNORM; + } +} + +void kinc_g5_pipeline_init(kinc_g5_pipeline_t *pipeline) { + kinc_g5_internal_pipeline_init(pipeline); +} + +void kinc_g5_pipeline_destroy(kinc_g5_pipeline_t *pipeline) { + vkDestroyPipeline(vk_ctx.device, pipeline->impl.framebuffer_pipeline, NULL); + vkDestroyPipeline(vk_ctx.device, pipeline->impl.rendertarget_pipeline, NULL); + vkDestroyPipelineLayout(vk_ctx.device, pipeline->impl.pipeline_layout, NULL); +} + +kinc_g5_constant_location_t kinc_g5_pipeline_get_constant_location(kinc_g5_pipeline_t *pipeline, const char *name) { + kinc_g5_constant_location_t location; + location.impl.vertexOffset = -1; + location.impl.fragmentOffset = -1; + location.impl.computeOffset = -1; + if (has_number(pipeline->impl.vertexOffsets, name)) { + location.impl.vertexOffset = find_number(pipeline->impl.vertexOffsets, name); + } + if (has_number(pipeline->impl.fragmentOffsets, name)) { + location.impl.fragmentOffset = find_number(pipeline->impl.fragmentOffsets, name); + } + return location; +} + +kinc_g5_texture_unit_t kinc_g5_pipeline_get_texture_unit(kinc_g5_pipeline_t *pipeline, const char *name) { + kinc_g5_texture_unit_t unit; + int number = find_number(pipeline->impl.textureBindings, name); + assert(number == -1 || number >= 2); // something wrong with the SPIR-V when this triggers + + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + unit.stages[i] = -1; + } + + if (number >= 0) { + unit.stages[KINC_G5_SHADER_TYPE_FRAGMENT] = number - 2; + } + + return unit; +} + +static VkCullModeFlagBits convert_cull_mode(kinc_g5_cull_mode_t cullMode) { + switch (cullMode) { + case KINC_G5_CULL_MODE_CLOCKWISE: + return VK_CULL_MODE_BACK_BIT; + case KINC_G5_CULL_MODE_COUNTERCLOCKWISE: + return VK_CULL_MODE_FRONT_BIT; + case KINC_G5_CULL_MODE_NEVER: + default: + return VK_CULL_MODE_NONE; + } +} + +static VkCompareOp convert_compare_mode(kinc_g5_compare_mode_t compare) { + switch (compare) { + default: + case KINC_G5_COMPARE_MODE_ALWAYS: + return VK_COMPARE_OP_ALWAYS; + case KINC_G5_COMPARE_MODE_NEVER: + return VK_COMPARE_OP_NEVER; + case KINC_G5_COMPARE_MODE_EQUAL: + return VK_COMPARE_OP_EQUAL; + case KINC_G5_COMPARE_MODE_NOT_EQUAL: + return VK_COMPARE_OP_NOT_EQUAL; + case KINC_G5_COMPARE_MODE_LESS: + return VK_COMPARE_OP_LESS; + case KINC_G5_COMPARE_MODE_LESS_EQUAL: + return VK_COMPARE_OP_LESS_OR_EQUAL; + case KINC_G5_COMPARE_MODE_GREATER: + return VK_COMPARE_OP_GREATER; + case KINC_G5_COMPARE_MODE_GREATER_EQUAL: + return VK_COMPARE_OP_GREATER_OR_EQUAL; + } +} + +static VkBlendFactor convert_blend_factor(kinc_g5_blending_factor_t factor) { + switch (factor) { + case KINC_G5_BLEND_ONE: + return VK_BLEND_FACTOR_ONE; + case KINC_G5_BLEND_ZERO: + return VK_BLEND_FACTOR_ZERO; + case KINC_G5_BLEND_SOURCE_ALPHA: + return VK_BLEND_FACTOR_SRC_ALPHA; + case KINC_G5_BLEND_DEST_ALPHA: + return VK_BLEND_FACTOR_DST_ALPHA; + case KINC_G5_BLEND_INV_SOURCE_ALPHA: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + case KINC_G5_BLEND_INV_DEST_ALPHA: + return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + case KINC_G5_BLEND_SOURCE_COLOR: + return VK_BLEND_FACTOR_SRC_COLOR; + case KINC_G5_BLEND_DEST_COLOR: + return VK_BLEND_FACTOR_DST_COLOR; + case KINC_G5_BLEND_INV_SOURCE_COLOR: + return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; + case KINC_G5_BLEND_INV_DEST_COLOR: + return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; + case KINC_G5_BLEND_CONSTANT: + return VK_BLEND_FACTOR_CONSTANT_COLOR; + case KINC_G5_BLEND_INV_CONSTANT: + return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; + default: + assert(false); + return VK_BLEND_FACTOR_ONE; + } +} + +static VkBlendOp convert_blend_operation(kinc_g5_blending_operation_t op) { + switch (op) { + case KINC_G5_BLENDOP_ADD: + return VK_BLEND_OP_ADD; + case KINC_G5_BLENDOP_SUBTRACT: + return VK_BLEND_OP_SUBTRACT; + case KINC_G5_BLENDOP_REVERSE_SUBTRACT: + return VK_BLEND_OP_REVERSE_SUBTRACT; + case KINC_G5_BLENDOP_MIN: + return VK_BLEND_OP_MIN; + case KINC_G5_BLENDOP_MAX: + return VK_BLEND_OP_MAX; + default: + assert(false); + return VK_BLEND_OP_ADD; + } +} + +void kinc_g5_pipeline_compile(kinc_g5_pipeline_t *pipeline) { + memset(pipeline->impl.vertexLocations, 0, sizeof(kinc_internal_named_number) * KINC_INTERNAL_NAMED_NUMBER_COUNT); + memset(pipeline->impl.vertexOffsets, 0, sizeof(kinc_internal_named_number) * KINC_INTERNAL_NAMED_NUMBER_COUNT); + memset(pipeline->impl.fragmentLocations, 0, sizeof(kinc_internal_named_number) * KINC_INTERNAL_NAMED_NUMBER_COUNT); + memset(pipeline->impl.fragmentOffsets, 0, sizeof(kinc_internal_named_number) * KINC_INTERNAL_NAMED_NUMBER_COUNT); + memset(pipeline->impl.textureBindings, 0, sizeof(kinc_internal_named_number) * KINC_INTERNAL_NAMED_NUMBER_COUNT); + parse_shader((uint32_t *)pipeline->vertexShader->impl.source, pipeline->vertexShader->impl.length, pipeline->impl.vertexLocations, + pipeline->impl.textureBindings, pipeline->impl.vertexOffsets); + parse_shader((uint32_t *)pipeline->fragmentShader->impl.source, pipeline->fragmentShader->impl.length, pipeline->impl.fragmentLocations, + pipeline->impl.textureBindings, pipeline->impl.fragmentOffsets); + + VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {0}; + pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pPipelineLayoutCreateInfo.pNext = NULL; + pPipelineLayoutCreateInfo.setLayoutCount = 1; + pPipelineLayoutCreateInfo.pSetLayouts = &desc_layout; + + VkResult err = vkCreatePipelineLayout(vk_ctx.device, &pPipelineLayoutCreateInfo, NULL, &pipeline->impl.pipeline_layout); + assert(!err); + + VkGraphicsPipelineCreateInfo pipeline_info = {0}; + + VkPipelineInputAssemblyStateCreateInfo ia = {0}; + VkPipelineRasterizationStateCreateInfo rs = {0}; + VkPipelineColorBlendStateCreateInfo cb = {0}; + VkPipelineDepthStencilStateCreateInfo ds = {0}; + VkPipelineViewportStateCreateInfo vp = {0}; + VkPipelineMultisampleStateCreateInfo ms = {0}; +#define dynamicStatesCount 2 + VkDynamicState dynamicStateEnables[dynamicStatesCount]; + VkPipelineDynamicStateCreateInfo dynamicState = {0}; + + memset(dynamicStateEnables, 0, sizeof(dynamicStateEnables)); + memset(&dynamicState, 0, sizeof(dynamicState)); + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicState.pDynamicStates = dynamicStateEnables; + + memset(&pipeline_info, 0, sizeof(pipeline_info)); + pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_info.layout = pipeline->impl.pipeline_layout; + + int vertexAttributeCount = 0; + int vertexBindingCount = 0; + for (int i = 0; i < 16; ++i) { + if (pipeline->inputLayout[i] == NULL) { + break; + } + vertexAttributeCount += pipeline->inputLayout[i]->size; + vertexBindingCount++; + } + +#ifdef KINC_WINDOWS + VkVertexInputBindingDescription *vi_bindings = (VkVertexInputBindingDescription *)alloca(sizeof(VkVertexInputBindingDescription) * vertexBindingCount); +#else + VkVertexInputBindingDescription vi_bindings[vertexBindingCount]; +#endif +#ifdef KINC_WINDOWS + VkVertexInputAttributeDescription *vi_attrs = (VkVertexInputAttributeDescription *)alloca(sizeof(VkVertexInputAttributeDescription) * vertexAttributeCount); +#else + VkVertexInputAttributeDescription vi_attrs[vertexAttributeCount]; +#endif + VkPipelineVertexInputStateCreateInfo vi = {0}; + memset(&vi, 0, sizeof(vi)); + vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vi.pNext = NULL; + vi.vertexBindingDescriptionCount = vertexBindingCount; + vi.pVertexBindingDescriptions = vi_bindings; + vi.vertexAttributeDescriptionCount = vertexAttributeCount; + vi.pVertexAttributeDescriptions = vi_attrs; + + uint32_t attr = 0; + for (int binding = 0; binding < vertexBindingCount; ++binding) { + uint32_t offset = 0; + uint32_t stride = 0; + for (int i = 0; i < pipeline->inputLayout[binding]->size; ++i) { + kinc_g5_vertex_element_t element = pipeline->inputLayout[binding]->elements[i]; + + vi_attrs[attr].binding = binding; + vi_attrs[attr].location = find_number(pipeline->impl.vertexLocations, element.name); + vi_attrs[attr].offset = offset; + offset += kinc_g4_vertex_data_size(element.data); + stride += kinc_g4_vertex_data_size(element.data); + + switch (element.data) { + case KINC_G4_VERTEX_DATA_F32_1X: + vi_attrs[attr].format = VK_FORMAT_R32_SFLOAT; + break; + case KINC_G4_VERTEX_DATA_F32_2X: + vi_attrs[attr].format = VK_FORMAT_R32G32_SFLOAT; + break; + case KINC_G4_VERTEX_DATA_F32_3X: + vi_attrs[attr].format = VK_FORMAT_R32G32B32_SFLOAT; + break; + case KINC_G4_VERTEX_DATA_F32_4X: + vi_attrs[attr].format = VK_FORMAT_R32G32B32A32_SFLOAT; + break; + case KINC_G4_VERTEX_DATA_I8_1X: + vi_attrs[attr].format = VK_FORMAT_R8_SINT; + break; + case KINC_G4_VERTEX_DATA_U8_1X: + vi_attrs[attr].format = VK_FORMAT_R8_UINT; + break; + case KINC_G4_VERTEX_DATA_I8_1X_NORMALIZED: + vi_attrs[attr].format = VK_FORMAT_R8_SNORM; + break; + case KINC_G4_VERTEX_DATA_U8_1X_NORMALIZED: + vi_attrs[attr].format = VK_FORMAT_R8_UNORM; + break; + case KINC_G4_VERTEX_DATA_I8_2X: + vi_attrs[attr].format = VK_FORMAT_R8G8_SINT; + break; + case KINC_G4_VERTEX_DATA_U8_2X: + vi_attrs[attr].format = VK_FORMAT_R8G8_UINT; + break; + case KINC_G4_VERTEX_DATA_I8_2X_NORMALIZED: + vi_attrs[attr].format = VK_FORMAT_R8G8_SNORM; + break; + case KINC_G4_VERTEX_DATA_U8_2X_NORMALIZED: + vi_attrs[attr].format = VK_FORMAT_R8G8_UNORM; + break; + case KINC_G4_VERTEX_DATA_I8_4X: + vi_attrs[attr].format = VK_FORMAT_R8G8B8A8_SINT; + break; + case KINC_G4_VERTEX_DATA_U8_4X: + vi_attrs[attr].format = VK_FORMAT_R8G8B8A8_UINT; + break; + case KINC_G4_VERTEX_DATA_I8_4X_NORMALIZED: + vi_attrs[attr].format = VK_FORMAT_R8G8B8A8_SNORM; + break; + case KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED: + vi_attrs[attr].format = VK_FORMAT_R8G8B8A8_UNORM; + break; + case KINC_G4_VERTEX_DATA_I16_1X: + vi_attrs[attr].format = VK_FORMAT_R16_SINT; + break; + case KINC_G4_VERTEX_DATA_U16_1X: + vi_attrs[attr].format = VK_FORMAT_R16_UINT; + break; + case KINC_G4_VERTEX_DATA_I16_1X_NORMALIZED: + vi_attrs[attr].format = VK_FORMAT_R16_SNORM; + break; + case KINC_G4_VERTEX_DATA_U16_1X_NORMALIZED: + vi_attrs[attr].format = VK_FORMAT_R16_UNORM; + break; + case KINC_G4_VERTEX_DATA_I16_2X: + vi_attrs[attr].format = VK_FORMAT_R16G16_SINT; + break; + case KINC_G4_VERTEX_DATA_U16_2X: + vi_attrs[attr].format = VK_FORMAT_R16G16_UINT; + break; + case KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED: + vi_attrs[attr].format = VK_FORMAT_R16G16_SNORM; + break; + case KINC_G4_VERTEX_DATA_U16_2X_NORMALIZED: + vi_attrs[attr].format = VK_FORMAT_R16G16_UNORM; + break; + case KINC_G4_VERTEX_DATA_I16_4X: + vi_attrs[attr].format = VK_FORMAT_R16G16B16A16_SINT; + break; + case KINC_G4_VERTEX_DATA_U16_4X: + vi_attrs[attr].format = VK_FORMAT_R16G16B16A16_UINT; + break; + case KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED: + vi_attrs[attr].format = VK_FORMAT_R16G16B16A16_SNORM; + break; + case KINC_G4_VERTEX_DATA_U16_4X_NORMALIZED: + vi_attrs[attr].format = VK_FORMAT_R16G16B16A16_UNORM; + break; + case KINC_G4_VERTEX_DATA_I32_1X: + vi_attrs[attr].format = VK_FORMAT_R32_SINT; + break; + case KINC_G4_VERTEX_DATA_U32_1X: + vi_attrs[attr].format = VK_FORMAT_R32_UINT; + break; + case KINC_G4_VERTEX_DATA_I32_2X: + vi_attrs[attr].format = VK_FORMAT_R32G32_SINT; + break; + case KINC_G4_VERTEX_DATA_U32_2X: + vi_attrs[attr].format = VK_FORMAT_R32G32_UINT; + break; + case KINC_G4_VERTEX_DATA_I32_3X: + vi_attrs[attr].format = VK_FORMAT_R32G32B32_SINT; + break; + case KINC_G4_VERTEX_DATA_U32_3X: + vi_attrs[attr].format = VK_FORMAT_R32G32B32_UINT; + break; + case KINC_G4_VERTEX_DATA_I32_4X: + vi_attrs[attr].format = VK_FORMAT_R32G32B32A32_SINT; + break; + case KINC_G4_VERTEX_DATA_U32_4X: + vi_attrs[attr].format = VK_FORMAT_R32G32B32A32_UINT; + break; + default: + assert(false); + break; + } + attr++; + } + vi_bindings[binding].binding = binding; + vi_bindings[binding].stride = stride; + vi_bindings[binding].inputRate = pipeline->inputLayout[binding]->instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; + } + + memset(&ia, 0, sizeof(ia)); + ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + memset(&rs, 0, sizeof(rs)); + rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rs.polygonMode = VK_POLYGON_MODE_FILL; + rs.cullMode = convert_cull_mode(pipeline->cullMode); + rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rs.depthClampEnable = VK_FALSE; + rs.rasterizerDiscardEnable = VK_FALSE; + rs.depthBiasEnable = VK_FALSE; + rs.lineWidth = 1.0f; + + memset(&cb, 0, sizeof(cb)); + cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + VkPipelineColorBlendAttachmentState att_state[8]; + memset(att_state, 0, sizeof(att_state)); + for (int i = 0; i < pipeline->colorAttachmentCount; ++i) { + att_state[i].colorWriteMask = + (pipeline->colorWriteMaskRed[i] ? VK_COLOR_COMPONENT_R_BIT : 0) | (pipeline->colorWriteMaskGreen[i] ? VK_COLOR_COMPONENT_G_BIT : 0) | + (pipeline->colorWriteMaskBlue[i] ? VK_COLOR_COMPONENT_B_BIT : 0) | (pipeline->colorWriteMaskAlpha[i] ? VK_COLOR_COMPONENT_A_BIT : 0); + att_state[i].blendEnable = pipeline->blend_source != KINC_G5_BLEND_ONE || pipeline->blend_destination != KINC_G5_BLEND_ZERO || + pipeline->alpha_blend_source != KINC_G5_BLEND_ONE || pipeline->alpha_blend_destination != KINC_G5_BLEND_ZERO; + att_state[i].srcColorBlendFactor = convert_blend_factor(pipeline->blend_source); + att_state[i].dstColorBlendFactor = convert_blend_factor(pipeline->blend_destination); + att_state[i].colorBlendOp = convert_blend_operation(pipeline->blend_operation); + att_state[i].srcAlphaBlendFactor = convert_blend_factor(pipeline->alpha_blend_source); + att_state[i].dstAlphaBlendFactor = convert_blend_factor(pipeline->alpha_blend_destination); + att_state[i].alphaBlendOp = convert_blend_operation(pipeline->alpha_blend_operation); + } + cb.attachmentCount = pipeline->colorAttachmentCount; + cb.pAttachments = att_state; + + memset(&vp, 0, sizeof(vp)); + vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + vp.viewportCount = 1; + dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT; + vp.scissorCount = 1; + dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR; + + memset(&ds, 0, sizeof(ds)); + ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + ds.depthTestEnable = pipeline->depthMode != KINC_G5_COMPARE_MODE_ALWAYS; + ds.depthWriteEnable = pipeline->depthWrite; + ds.depthCompareOp = convert_compare_mode(pipeline->depthMode); + ds.depthBoundsTestEnable = VK_FALSE; + ds.back.failOp = VK_STENCIL_OP_KEEP; + ds.back.passOp = VK_STENCIL_OP_KEEP; + ds.back.compareOp = VK_COMPARE_OP_ALWAYS; + ds.stencilTestEnable = VK_FALSE; + ds.front = ds.back; + + memset(&ms, 0, sizeof(ms)); + ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + ms.pSampleMask = NULL; + ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + pipeline_info.stageCount = 2; + VkPipelineShaderStageCreateInfo shaderStages[2]; + memset(&shaderStages, 0, 2 * sizeof(VkPipelineShaderStageCreateInfo)); + + shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + shaderStages[0].module = prepare_vs(&pipeline->impl.vert_shader_module, pipeline->vertexShader); + shaderStages[0].pName = "main"; + + shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + shaderStages[1].module = prepare_fs(&pipeline->impl.frag_shader_module, pipeline->fragmentShader); + shaderStages[1].pName = "main"; + + pipeline_info.pVertexInputState = &vi; + pipeline_info.pInputAssemblyState = &ia; + pipeline_info.pRasterizationState = &rs; + pipeline_info.pColorBlendState = &cb; + pipeline_info.pMultisampleState = &ms; + pipeline_info.pViewportState = &vp; + pipeline_info.pDepthStencilState = &ds; + pipeline_info.pStages = shaderStages; + pipeline_info.renderPass = vk_ctx.windows[vk_ctx.current_window].framebuffer_render_pass; + pipeline_info.pDynamicState = &dynamicState; + + err = vkCreateGraphicsPipelines(vk_ctx.device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &pipeline->impl.framebuffer_pipeline); + assert(!err); + + VkAttachmentDescription attachments[9]; + for (int i = 0; i < pipeline->colorAttachmentCount; ++i) { + attachments[i].format = convert_format(pipeline->colorAttachment[i]); + attachments[i].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[i].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[i].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[i].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + attachments[i].flags = 0; + } + + if (pipeline->depthAttachmentBits > 0) { + attachments[pipeline->colorAttachmentCount].format = VK_FORMAT_D16_UNORM; + attachments[pipeline->colorAttachmentCount].samples = VK_SAMPLE_COUNT_1_BIT; + attachments[pipeline->colorAttachmentCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[pipeline->colorAttachmentCount].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachments[pipeline->colorAttachmentCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachments[pipeline->colorAttachmentCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachments[pipeline->colorAttachmentCount].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachments[pipeline->colorAttachmentCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + attachments[pipeline->colorAttachmentCount].flags = 0; + } + + VkAttachmentReference color_references[8]; + for (int i = 0; i < pipeline->colorAttachmentCount; ++i) { + color_references[i].attachment = i; + color_references[i].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + + VkAttachmentReference depth_reference = {0}; + depth_reference.attachment = pipeline->colorAttachmentCount; + depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass = {0}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.flags = 0; + subpass.inputAttachmentCount = 0; + subpass.pInputAttachments = NULL; + subpass.colorAttachmentCount = pipeline->colorAttachmentCount; + subpass.pColorAttachments = color_references; + subpass.pResolveAttachments = NULL; + subpass.pDepthStencilAttachment = pipeline->depthAttachmentBits > 0 ? &depth_reference : NULL; + subpass.preserveAttachmentCount = 0; + subpass.pPreserveAttachments = NULL; + + VkSubpassDependency dependencies[2]; + memset(&dependencies, 0, sizeof(dependencies)); + + // TODO: For multi-targets-rendering + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + VkRenderPassCreateInfo rp_info = {0}; + rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rp_info.pNext = NULL; + rp_info.attachmentCount = pipeline->depthAttachmentBits > 0 ? pipeline->colorAttachmentCount + 1 : pipeline->colorAttachmentCount; + rp_info.pAttachments = attachments; + rp_info.subpassCount = 1; + rp_info.pSubpasses = &subpass; + rp_info.dependencyCount = 2; + rp_info.pDependencies = dependencies; + + VkRenderPass render_pass; + err = vkCreateRenderPass(vk_ctx.device, &rp_info, NULL, &render_pass); + assert(!err); + + pipeline_info.renderPass = render_pass; + + err = vkCreateGraphicsPipelines(vk_ctx.device, VK_NULL_HANDLE, 1, &pipeline_info, NULL, &pipeline->impl.rendertarget_pipeline); + assert(!err); + + vkDestroyShaderModule(vk_ctx.device, pipeline->impl.frag_shader_module, NULL); + vkDestroyShaderModule(vk_ctx.device, pipeline->impl.vert_shader_module, NULL); +} + +void createDescriptorLayout(void) { + VkDescriptorSetLayoutBinding layoutBindings[18]; + memset(layoutBindings, 0, sizeof(layoutBindings)); + + layoutBindings[0].binding = 0; + layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + layoutBindings[0].descriptorCount = 1; + layoutBindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + layoutBindings[0].pImmutableSamplers = NULL; + + layoutBindings[1].binding = 1; + layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + layoutBindings[1].descriptorCount = 1; + layoutBindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + layoutBindings[1].pImmutableSamplers = NULL; + + for (int i = 2; i < 18; ++i) { + layoutBindings[i].binding = i; + layoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + layoutBindings[i].descriptorCount = 1; + layoutBindings[i].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT; + layoutBindings[i].pImmutableSamplers = NULL; + } + + VkDescriptorSetLayoutCreateInfo descriptor_layout = {0}; + descriptor_layout.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptor_layout.pNext = NULL; + descriptor_layout.bindingCount = 18; + descriptor_layout.pBindings = layoutBindings; + + VkResult err = vkCreateDescriptorSetLayout(vk_ctx.device, &descriptor_layout, NULL, &desc_layout); + assert(!err); + + VkDescriptorPoolSize typeCounts[2]; + memset(typeCounts, 0, sizeof(typeCounts)); + + typeCounts[0].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + typeCounts[0].descriptorCount = 2 * 1024; + + typeCounts[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + typeCounts[1].descriptorCount = 16 * 1024; + + VkDescriptorPoolCreateInfo pool_info = {0}; + pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + pool_info.pNext = NULL; + pool_info.maxSets = 1024; + pool_info.poolSizeCount = 2; + pool_info.pPoolSizes = typeCounts; + + err = vkCreateDescriptorPool(vk_ctx.device, &pool_info, NULL, &descriptor_pool); + assert(!err); +} + +int calc_descriptor_id(void) { + int texture_count = 0; + for (int i = 0; i < 16; ++i) { + if (vulkanTextures[i] != NULL) { + texture_count++; + } + else if (vulkanRenderTargets[i] != NULL) { + texture_count++; + } + } + + bool uniform_buffer = vk_ctx.vertex_uniform_buffer != NULL && vk_ctx.fragment_uniform_buffer != NULL; + + return 1 | (texture_count << 1) | ((uniform_buffer ? 1 : 0) << 8); +} + +int calc_compute_descriptor_id(void) { + int texture_count = 0; + for (int i = 0; i < 16; ++i) { + if (vulkanTextures[i] != NULL) { + texture_count++; + } + else if (vulkanRenderTargets[i] != NULL) { + texture_count++; + } + } + + bool uniform_buffer = vk_ctx.compute_uniform_buffer != NULL; + + return 1 | (texture_count << 1) | ((uniform_buffer ? 1 : 0) << 8); +} + +#define MAX_DESCRIPTOR_SETS 1024 + +struct destriptor_set { + int id; + bool in_use; + VkDescriptorImageInfo tex_desc[16]; + VkDescriptorSet set; +}; + +static struct destriptor_set descriptor_sets[MAX_DESCRIPTOR_SETS] = {0}; +static int descriptor_sets_count = 0; + +static int write_tex_descs(VkDescriptorImageInfo *tex_descs) { + memset(tex_descs, 0, sizeof(VkDescriptorImageInfo) * 16); + + int texture_count = 0; + for (int i = 0; i < 16; ++i) { + if (vulkanTextures[i] != NULL) { + tex_descs[i].sampler = vulkanSamplers[i]; + tex_descs[i].imageView = vulkanTextures[i]->impl.texture.view; + texture_count++; + } + else if (vulkanRenderTargets[i] != NULL) { + tex_descs[i].sampler = vulkanSamplers[i]; + if (vulkanRenderTargets[i]->impl.stage_depth == i) { + tex_descs[i].imageView = vulkanRenderTargets[i]->impl.depthView; + vulkanRenderTargets[i]->impl.stage_depth = -1; + } + else { + tex_descs[i].imageView = vulkanRenderTargets[i]->impl.sourceView; + } + texture_count++; + } + tex_descs[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + } + return texture_count; +} + +static bool textures_changed(struct destriptor_set *set) { + VkDescriptorImageInfo tex_desc[16]; + + write_tex_descs(tex_desc); + + return memcmp(&tex_desc, &set->tex_desc, sizeof(tex_desc)) != 0; +} + +static void update_textures(struct destriptor_set *set) { + memset(&set->tex_desc, 0, sizeof(set->tex_desc)); + + int texture_count = write_tex_descs(set->tex_desc); + + VkWriteDescriptorSet writes[16]; + memset(&writes, 0, sizeof(writes)); + + for (int i = 0; i < 16; ++i) { + writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[i].dstSet = set->set; + writes[i].dstBinding = i + 2; + writes[i].descriptorCount = 1; + writes[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writes[i].pImageInfo = &set->tex_desc[i]; + } + + if (vulkanTextures[0] != NULL || vulkanRenderTargets[0] != NULL) { + vkUpdateDescriptorSets(vk_ctx.device, texture_count, writes, 0, NULL); + } +} + +void reuse_descriptor_sets(void) { + for (int i = 0; i < descriptor_sets_count; ++i) { + descriptor_sets[i].in_use = false; + } +} + +VkDescriptorSet getDescriptorSet() { + int id = calc_descriptor_id(); + for (int i = 0; i < descriptor_sets_count; ++i) { + if (descriptor_sets[i].id == id) { + if (!descriptor_sets[i].in_use) { + descriptor_sets[i].in_use = true; + update_textures(&descriptor_sets[i]); + return descriptor_sets[i].set; + } + else { + if (!textures_changed(&descriptor_sets[i])) { + return descriptor_sets[i].set; + } + } + } + } + + VkDescriptorSetAllocateInfo alloc_info = {0}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.pNext = NULL; + alloc_info.descriptorPool = descriptor_pool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &desc_layout; + VkDescriptorSet descriptor_set; + VkResult err = vkAllocateDescriptorSets(vk_ctx.device, &alloc_info, &descriptor_set); + assert(!err); + + VkDescriptorBufferInfo buffer_descs[2]; + + memset(&buffer_descs, 0, sizeof(buffer_descs)); + + if (vk_ctx.vertex_uniform_buffer != NULL) { + buffer_descs[0].buffer = *vk_ctx.vertex_uniform_buffer; + } + buffer_descs[0].offset = 0; + buffer_descs[0].range = 256 * sizeof(float); + + if (vk_ctx.fragment_uniform_buffer != NULL) { + buffer_descs[1].buffer = *vk_ctx.fragment_uniform_buffer; + } + buffer_descs[1].offset = 0; + buffer_descs[1].range = 256 * sizeof(float); + + VkDescriptorImageInfo tex_desc[16]; + memset(&tex_desc, 0, sizeof(tex_desc)); + + int texture_count = 0; + for (int i = 0; i < 16; ++i) { + if (vulkanTextures[i] != NULL) { + assert(vulkanSamplers[i] != VK_NULL_HANDLE); + tex_desc[i].sampler = vulkanSamplers[i]; + tex_desc[i].imageView = vulkanTextures[i]->impl.texture.view; + texture_count++; + } + else if (vulkanRenderTargets[i] != NULL) { + tex_desc[i].sampler = vulkanSamplers[i]; + if (vulkanRenderTargets[i]->impl.stage_depth == i) { + tex_desc[i].imageView = vulkanRenderTargets[i]->impl.depthView; + vulkanRenderTargets[i]->impl.stage_depth = -1; + } + else { + tex_desc[i].imageView = vulkanRenderTargets[i]->impl.sourceView; + } + texture_count++; + } + tex_desc[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + } + + VkWriteDescriptorSet writes[18]; + memset(&writes, 0, sizeof(writes)); + + writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[0].dstSet = descriptor_set; + writes[0].dstBinding = 0; + writes[0].descriptorCount = 1; + writes[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + writes[0].pBufferInfo = &buffer_descs[0]; + + writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[1].dstSet = descriptor_set; + writes[1].dstBinding = 1; + writes[1].descriptorCount = 1; + writes[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + writes[1].pBufferInfo = &buffer_descs[1]; + + for (int i = 2; i < 18; ++i) { + writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[i].dstSet = descriptor_set; + writes[i].dstBinding = i; + writes[i].descriptorCount = 1; + writes[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + writes[i].pImageInfo = &tex_desc[i - 2]; + } + + if (vulkanTextures[0] != NULL || vulkanRenderTargets[0] != NULL) { + if (vk_ctx.vertex_uniform_buffer != NULL && vk_ctx.fragment_uniform_buffer != NULL) { + vkUpdateDescriptorSets(vk_ctx.device, 2 + texture_count, writes, 0, NULL); + } + else { + vkUpdateDescriptorSets(vk_ctx.device, texture_count, writes + 2, 0, NULL); + } + } + else { + if (vk_ctx.vertex_uniform_buffer != NULL && vk_ctx.fragment_uniform_buffer != NULL) { + vkUpdateDescriptorSets(vk_ctx.device, 2, writes, 0, NULL); + } + } + + assert(descriptor_sets_count + 1 < MAX_DESCRIPTOR_SETS); + descriptor_sets[descriptor_sets_count].id = id; + descriptor_sets[descriptor_sets_count].in_use = true; + descriptor_sets[descriptor_sets_count].set = descriptor_set; + write_tex_descs(descriptor_sets[descriptor_sets_count].tex_desc); + descriptor_sets_count += 1; + + return descriptor_set; +} + +static struct destriptor_set compute_descriptor_sets[MAX_DESCRIPTOR_SETS] = {0}; +static int compute_descriptor_sets_count = 0; + +static int write_compute_tex_descs(VkDescriptorImageInfo *tex_descs) { + memset(tex_descs, 0, sizeof(VkDescriptorImageInfo) * 16); + + int texture_count = 0; + for (int i = 0; i < 16; ++i) { + if (vulkanTextures[i] != NULL) { + tex_descs[i].sampler = vulkanSamplers[i]; + tex_descs[i].imageView = vulkanTextures[i]->impl.texture.view; + texture_count++; + } + else if (vulkanRenderTargets[i] != NULL) { + tex_descs[i].sampler = vulkanSamplers[i]; + if (vulkanRenderTargets[i]->impl.stage_depth == i) { + tex_descs[i].imageView = vulkanRenderTargets[i]->impl.depthView; + vulkanRenderTargets[i]->impl.stage_depth = -1; + } + else { + tex_descs[i].imageView = vulkanRenderTargets[i]->impl.sourceView; + } + texture_count++; + } + tex_descs[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + } + return texture_count; +} + +static bool compute_textures_changed(struct destriptor_set *set) { + VkDescriptorImageInfo tex_desc[16]; + + write_compute_tex_descs(tex_desc); + + return memcmp(&tex_desc, &set->tex_desc, sizeof(tex_desc)) != 0; +} + +static void update_compute_textures(struct destriptor_set *set) { + memset(&set->tex_desc, 0, sizeof(set->tex_desc)); + + int texture_count = write_compute_tex_descs(set->tex_desc); + + VkWriteDescriptorSet writes[16]; + memset(&writes, 0, sizeof(writes)); + + for (int i = 0; i < 16; ++i) { + writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[i].dstSet = set->set; + writes[i].dstBinding = i + 2; + writes[i].descriptorCount = 1; + writes[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + writes[i].pImageInfo = &set->tex_desc[i]; + } + + if (vulkanTextures[0] != NULL || vulkanRenderTargets[0] != NULL) { + vkUpdateDescriptorSets(vk_ctx.device, texture_count, writes, 0, NULL); + } +} + +void reuse_compute_descriptor_sets(void) { + for (int i = 0; i < compute_descriptor_sets_count; ++i) { + compute_descriptor_sets[i].in_use = false; + } +} + +static VkDescriptorSet get_compute_descriptor_set() { + int id = calc_compute_descriptor_id(); + for (int i = 0; i < compute_descriptor_sets_count; ++i) { + if (compute_descriptor_sets[i].id == id) { + if (!compute_descriptor_sets[i].in_use) { + compute_descriptor_sets[i].in_use = true; + update_compute_textures(&compute_descriptor_sets[i]); + return compute_descriptor_sets[i].set; + } + else { + if (!compute_textures_changed(&compute_descriptor_sets[i])) { + return compute_descriptor_sets[i].set; + } + } + } + } + + VkDescriptorSetAllocateInfo alloc_info = {0}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.pNext = NULL; + alloc_info.descriptorPool = descriptor_pool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &compute_descriptor_layout; + VkDescriptorSet descriptor_set; + VkResult err = vkAllocateDescriptorSets(vk_ctx.device, &alloc_info, &descriptor_set); + assert(!err); + + VkDescriptorBufferInfo buffer_descs[2]; + + memset(&buffer_descs, 0, sizeof(buffer_descs)); + + if (vk_ctx.compute_uniform_buffer != NULL) { + buffer_descs[0].buffer = *vk_ctx.compute_uniform_buffer; + } + buffer_descs[0].offset = 0; + buffer_descs[0].range = 256 * sizeof(float); + + if (vk_ctx.compute_uniform_buffer != NULL) { + buffer_descs[1].buffer = *vk_ctx.compute_uniform_buffer; + } + buffer_descs[1].offset = 0; + buffer_descs[1].range = 256 * sizeof(float); + + VkDescriptorImageInfo tex_desc[16]; + memset(&tex_desc, 0, sizeof(tex_desc)); + + int texture_count = 0; + for (int i = 0; i < 16; ++i) { + if (vulkanTextures[i] != NULL) { + // assert(vulkanSamplers[i] != VK_NULL_HANDLE); + tex_desc[i].sampler = VK_NULL_HANDLE; // vulkanSamplers[i]; + tex_desc[i].imageView = vulkanTextures[i]->impl.texture.view; + texture_count++; + } + else if (vulkanRenderTargets[i] != NULL) { + tex_desc[i].sampler = vulkanSamplers[i]; + if (vulkanRenderTargets[i]->impl.stage_depth == i) { + tex_desc[i].imageView = vulkanRenderTargets[i]->impl.depthView; + vulkanRenderTargets[i]->impl.stage_depth = -1; + } + else { + tex_desc[i].imageView = vulkanRenderTargets[i]->impl.sourceView; + } + texture_count++; + } + tex_desc[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + } + + VkWriteDescriptorSet writes[18]; + memset(&writes, 0, sizeof(writes)); + + writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[0].dstSet = descriptor_set; + writes[0].dstBinding = 0; + writes[0].descriptorCount = 1; + writes[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + writes[0].pBufferInfo = &buffer_descs[0]; + + writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[1].dstSet = descriptor_set; + writes[1].dstBinding = 1; + writes[1].descriptorCount = 1; + writes[1].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + writes[1].pBufferInfo = &buffer_descs[1]; + + for (int i = 2; i < 18; ++i) { + writes[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writes[i].dstSet = descriptor_set; + writes[i].dstBinding = i; + writes[i].descriptorCount = 1; + writes[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + writes[i].pImageInfo = &tex_desc[i - 2]; + } + + if (vulkanTextures[0] != NULL || vulkanRenderTargets[0] != NULL) { + if (vk_ctx.compute_uniform_buffer != NULL) { + vkUpdateDescriptorSets(vk_ctx.device, 2 + texture_count, writes, 0, NULL); + } + else { + vkUpdateDescriptorSets(vk_ctx.device, texture_count, writes + 2, 0, NULL); + } + } + else { + if (vk_ctx.compute_uniform_buffer != NULL) { + vkUpdateDescriptorSets(vk_ctx.device, 2, writes, 0, NULL); + } + } + + assert(compute_descriptor_sets_count + 1 < MAX_DESCRIPTOR_SETS); + compute_descriptor_sets[compute_descriptor_sets_count].id = id; + compute_descriptor_sets[compute_descriptor_sets_count].in_use = true; + compute_descriptor_sets[compute_descriptor_sets_count].set = descriptor_set; + write_tex_descs(compute_descriptor_sets[compute_descriptor_sets_count].tex_desc); + compute_descriptor_sets_count += 1; + + return descriptor_set; +} diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/pipeline.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/pipeline.h new file mode 100644 index 000000000..7e521811c --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/pipeline.h @@ -0,0 +1,40 @@ +#pragma once + +#include "MiniVulkan.h" + +#include "named_number.h" + +struct kinc_g5_shader; + +typedef struct PipelineState5Impl_s { + const char **textures; + int *textureValues; + int textureCount; + + VkPipeline framebuffer_pipeline; + VkPipeline rendertarget_pipeline; + VkShaderModule vert_shader_module; + VkShaderModule frag_shader_module; + + kinc_internal_named_number vertexLocations[KINC_INTERNAL_NAMED_NUMBER_COUNT]; + kinc_internal_named_number fragmentLocations[KINC_INTERNAL_NAMED_NUMBER_COUNT]; + kinc_internal_named_number textureBindings[KINC_INTERNAL_NAMED_NUMBER_COUNT]; + kinc_internal_named_number vertexOffsets[KINC_INTERNAL_NAMED_NUMBER_COUNT]; + kinc_internal_named_number fragmentOffsets[KINC_INTERNAL_NAMED_NUMBER_COUNT]; + + VkPipelineLayout pipeline_layout; +} PipelineState5Impl; + +typedef struct ComputePipelineState5Impl_t { + int a; +} ComputePipelineState5Impl; + +typedef struct { + int vertexOffset; + int fragmentOffset; + int computeOffset; +} ConstantLocation5Impl; + +typedef struct { + int nothing; +} AttributeLocation5Impl; diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/raytrace.c.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/raytrace.c.h new file mode 100644 index 000000000..398d008b8 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/raytrace.c.h @@ -0,0 +1,1069 @@ +#include "vulkan.h" + +#include "raytrace.h" + +#ifndef KINC_ANDROID + +#include +#include +#include +#include +#include +#include +#include + +extern VkRenderPassBeginInfo currentRenderPassBeginInfo; +extern VkFramebuffer *framebuffers; +extern uint32_t current_buffer; +bool memory_type_from_properties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex); + +static const int INDEX_RAYGEN = 0; +static const int INDEX_MISS = 1; +static const int INDEX_CLOSEST_HIT = 2; +static const char *raygen_shader_name = "raygeneration"; +static const char *closesthit_shader_name = "closesthit"; +static const char *miss_shader_name = "miss"; + +static VkDescriptorPool raytrace_descriptor_pool; +static kinc_raytrace_acceleration_structure_t *accel; +static kinc_raytrace_pipeline_t *pipeline; +static kinc_g5_render_target_t *output = NULL; +static kinc_g5_render_target_t *texpaint0; +static kinc_g5_render_target_t *texpaint1; +static kinc_g5_render_target_t *texpaint2; +static kinc_g5_texture_t *texenv; +static kinc_g5_texture_t *texsobol; +static kinc_g5_texture_t *texscramble; +static kinc_g5_texture_t *texrank; +static kinc_g5_vertex_buffer_t *vb; +static kinc_g5_index_buffer_t *ib; + +static PFN_vkCreateRayTracingPipelinesKHR _vkCreateRayTracingPipelinesKHR = NULL; +static PFN_vkGetRayTracingShaderGroupHandlesKHR _vkGetRayTracingShaderGroupHandlesKHR = NULL; +static PFN_vkGetBufferDeviceAddressKHR _vkGetBufferDeviceAddressKHR = NULL; +static PFN_vkCreateAccelerationStructureKHR _vkCreateAccelerationStructureKHR = NULL; +static PFN_vkGetAccelerationStructureDeviceAddressKHR _vkGetAccelerationStructureDeviceAddressKHR = NULL; +static PFN_vkGetAccelerationStructureBuildSizesKHR _vkGetAccelerationStructureBuildSizesKHR = NULL; +static PFN_vkCmdBuildAccelerationStructuresKHR _vkCmdBuildAccelerationStructuresKHR = NULL; +static PFN_vkDestroyAccelerationStructureKHR _vkDestroyAccelerationStructureKHR = NULL; +static PFN_vkCmdTraceRaysKHR _vkCmdTraceRaysKHR = NULL; + +void kinc_raytrace_pipeline_init(kinc_raytrace_pipeline_t *pipeline, kinc_g5_command_list_t *command_list, void *ray_shader, int ray_shader_size, + kinc_g5_constant_buffer_t *constant_buffer) { + output = NULL; + pipeline->_constant_buffer = constant_buffer; + + { + VkDescriptorSetLayoutBinding acceleration_structure_layout_binding = {0}; + acceleration_structure_layout_binding.binding = 0; + acceleration_structure_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; + acceleration_structure_layout_binding.descriptorCount = 1; + acceleration_structure_layout_binding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR; + + VkDescriptorSetLayoutBinding result_image_layout_binding = {0}; + result_image_layout_binding.binding = 10; + result_image_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + result_image_layout_binding.descriptorCount = 1; + result_image_layout_binding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR; + + VkDescriptorSetLayoutBinding uniform_buffer_binding = {0}; + uniform_buffer_binding.binding = 11; + uniform_buffer_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uniform_buffer_binding.descriptorCount = 1; + uniform_buffer_binding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR; + + VkDescriptorSetLayoutBinding ib_binding = {0}; + ib_binding.binding = 1; + ib_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + ib_binding.descriptorCount = 1; + ib_binding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR; + + VkDescriptorSetLayoutBinding vb_binding = {0}; + vb_binding.binding = 2; + vb_binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + vb_binding.descriptorCount = 1; + vb_binding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR; + + VkDescriptorSetLayoutBinding tex0_binding = {0}; + tex0_binding.binding = 3; + tex0_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + tex0_binding.descriptorCount = 1; + tex0_binding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR; + + VkDescriptorSetLayoutBinding tex1_binding = {0}; + tex1_binding.binding = 4; + tex1_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + tex1_binding.descriptorCount = 1; + tex1_binding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR; + + VkDescriptorSetLayoutBinding tex2_binding = {0}; + tex2_binding.binding = 5; + tex2_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + tex2_binding.descriptorCount = 1; + tex2_binding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR; + + VkDescriptorSetLayoutBinding texenv_binding = {0}; + texenv_binding.binding = 6; + texenv_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + texenv_binding.descriptorCount = 1; + texenv_binding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR; + + VkDescriptorSetLayoutBinding texsobol_binding = {0}; + texsobol_binding.binding = 7; + texsobol_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + texsobol_binding.descriptorCount = 1; + texsobol_binding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR; + + VkDescriptorSetLayoutBinding texscramble_binding = {0}; + texscramble_binding.binding = 8; + texscramble_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + texscramble_binding.descriptorCount = 1; + texscramble_binding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR; + + VkDescriptorSetLayoutBinding texrank_binding = {0}; + texrank_binding.binding = 9; + texrank_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + texrank_binding.descriptorCount = 1; + texrank_binding.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR | VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR | VK_SHADER_STAGE_MISS_BIT_KHR; + + VkDescriptorSetLayoutBinding bindings[12] = { + acceleration_structure_layout_binding, + result_image_layout_binding, + uniform_buffer_binding, + vb_binding, + ib_binding, + tex0_binding, + tex1_binding, + tex2_binding, + texenv_binding, + texsobol_binding, + texscramble_binding, + texrank_binding + }; + + VkDescriptorSetLayoutCreateInfo layout_info = {0}; + layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layout_info.pNext = NULL; + layout_info.bindingCount = 12; + layout_info.pBindings = &bindings[0]; + vkCreateDescriptorSetLayout(vk_ctx.device, &layout_info, NULL, &pipeline->impl.descriptor_set_layout); + + VkPipelineLayoutCreateInfo pipeline_layout_create_info = {0}; + pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipeline_layout_create_info.pNext = NULL; + pipeline_layout_create_info.setLayoutCount = 1; + pipeline_layout_create_info.pSetLayouts = &pipeline->impl.descriptor_set_layout; + + vkCreatePipelineLayout(vk_ctx.device, &pipeline_layout_create_info, NULL, &pipeline->impl.pipeline_layout); + + VkShaderModuleCreateInfo module_create_info = {0}; + memset(&module_create_info, 0, sizeof(VkShaderModuleCreateInfo)); + module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + module_create_info.codeSize = ray_shader_size; + module_create_info.pCode = (const uint32_t *)ray_shader; + module_create_info.pNext = NULL; + module_create_info.flags = 0; + VkShaderModule shader_module; + vkCreateShaderModule(vk_ctx.device, &module_create_info, NULL, &shader_module); + + VkPipelineShaderStageCreateInfo shader_stages[3]; + shader_stages[INDEX_RAYGEN].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shader_stages[INDEX_RAYGEN].pNext = NULL; + shader_stages[INDEX_RAYGEN].stage = VK_SHADER_STAGE_RAYGEN_BIT_KHR; + shader_stages[INDEX_RAYGEN].module = shader_module; + shader_stages[INDEX_RAYGEN].pName = raygen_shader_name; + shader_stages[INDEX_RAYGEN].flags = 0; + shader_stages[INDEX_RAYGEN].pSpecializationInfo = NULL; + + shader_stages[INDEX_MISS].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shader_stages[INDEX_MISS].pNext = NULL; + shader_stages[INDEX_MISS].stage = VK_SHADER_STAGE_MISS_BIT_KHR; + shader_stages[INDEX_MISS].module = shader_module; + shader_stages[INDEX_MISS].pName = miss_shader_name; + shader_stages[INDEX_MISS].flags = 0; + shader_stages[INDEX_MISS].pSpecializationInfo = NULL; + + shader_stages[INDEX_CLOSEST_HIT].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shader_stages[INDEX_CLOSEST_HIT].pNext = NULL; + shader_stages[INDEX_CLOSEST_HIT].stage = VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR; + shader_stages[INDEX_CLOSEST_HIT].module = shader_module; + shader_stages[INDEX_CLOSEST_HIT].pName = closesthit_shader_name; + shader_stages[INDEX_CLOSEST_HIT].flags = 0; + shader_stages[INDEX_CLOSEST_HIT].pSpecializationInfo = NULL; + + VkRayTracingShaderGroupCreateInfoKHR groups[3]; + groups[INDEX_RAYGEN].sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR; + groups[INDEX_RAYGEN].pNext = NULL; + groups[INDEX_RAYGEN].generalShader = VK_SHADER_UNUSED_KHR; + groups[INDEX_RAYGEN].closestHitShader = VK_SHADER_UNUSED_KHR; + groups[INDEX_RAYGEN].anyHitShader = VK_SHADER_UNUSED_KHR; + groups[INDEX_RAYGEN].intersectionShader = VK_SHADER_UNUSED_KHR; + groups[INDEX_RAYGEN].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR; + groups[INDEX_RAYGEN].generalShader = INDEX_RAYGEN; + + groups[INDEX_MISS].sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR; + groups[INDEX_MISS].pNext = NULL; + groups[INDEX_MISS].generalShader = VK_SHADER_UNUSED_KHR; + groups[INDEX_MISS].closestHitShader = VK_SHADER_UNUSED_KHR; + groups[INDEX_MISS].anyHitShader = VK_SHADER_UNUSED_KHR; + groups[INDEX_MISS].intersectionShader = VK_SHADER_UNUSED_KHR; + groups[INDEX_MISS].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR; + groups[INDEX_MISS].generalShader = INDEX_MISS; + + groups[INDEX_CLOSEST_HIT].sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR; + groups[INDEX_CLOSEST_HIT].pNext = NULL; + groups[INDEX_CLOSEST_HIT].generalShader = VK_SHADER_UNUSED_KHR; + groups[INDEX_CLOSEST_HIT].closestHitShader = VK_SHADER_UNUSED_KHR; + groups[INDEX_CLOSEST_HIT].anyHitShader = VK_SHADER_UNUSED_KHR; + groups[INDEX_CLOSEST_HIT].intersectionShader = VK_SHADER_UNUSED_KHR; + groups[INDEX_CLOSEST_HIT].type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR; + groups[INDEX_CLOSEST_HIT].closestHitShader = INDEX_CLOSEST_HIT; + + VkRayTracingPipelineCreateInfoKHR raytracing_pipeline_create_info = {0}; + raytracing_pipeline_create_info.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR; + raytracing_pipeline_create_info.pNext = NULL; + raytracing_pipeline_create_info.flags = 0; + raytracing_pipeline_create_info.stageCount = 3; + raytracing_pipeline_create_info.pStages = &shader_stages[0]; + raytracing_pipeline_create_info.groupCount = 3; + raytracing_pipeline_create_info.pGroups = &groups[0]; + raytracing_pipeline_create_info.maxPipelineRayRecursionDepth = 1; + raytracing_pipeline_create_info.layout = pipeline->impl.pipeline_layout; + _vkCreateRayTracingPipelinesKHR = (void *)vkGetDeviceProcAddr(vk_ctx.device, "vkCreateRayTracingPipelinesKHR"); + _vkCreateRayTracingPipelinesKHR(vk_ctx.device, VK_NULL_HANDLE, VK_NULL_HANDLE, 1, &raytracing_pipeline_create_info, NULL, &pipeline->impl.pipeline); + } + + { + VkPhysicalDeviceRayTracingPipelinePropertiesKHR ray_tracing_pipeline_properties; + ray_tracing_pipeline_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR; + ray_tracing_pipeline_properties.pNext = NULL; + VkPhysicalDeviceProperties2 device_properties = {0}; + device_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + device_properties.pNext = &ray_tracing_pipeline_properties; + vkGetPhysicalDeviceProperties2(vk_ctx.gpu, &device_properties); + + _vkGetRayTracingShaderGroupHandlesKHR = (void *)vkGetDeviceProcAddr(vk_ctx.device, "vkGetRayTracingShaderGroupHandlesKHR"); + uint32_t handle_size = ray_tracing_pipeline_properties.shaderGroupHandleSize; + uint32_t handle_size_aligned = + (ray_tracing_pipeline_properties.shaderGroupHandleSize + ray_tracing_pipeline_properties.shaderGroupHandleAlignment - 1) & + ~(ray_tracing_pipeline_properties.shaderGroupHandleAlignment - 1); + + VkBufferCreateInfo buf_info = {0}; + buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buf_info.pNext = NULL; + buf_info.size = handle_size; + buf_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + buf_info.flags = 0; + + vkCreateBuffer(vk_ctx.device, &buf_info, NULL, &pipeline->impl.raygen_shader_binding_table); + vkCreateBuffer(vk_ctx.device, &buf_info, NULL, &pipeline->impl.hit_shader_binding_table); + vkCreateBuffer(vk_ctx.device, &buf_info, NULL, &pipeline->impl.miss_shader_binding_table); + + uint8_t shader_handle_storage[1024]; + _vkGetRayTracingShaderGroupHandlesKHR(vk_ctx.device, pipeline->impl.pipeline, 0, 3, handle_size_aligned * 3, shader_handle_storage); + + VkMemoryAllocateFlagsInfo memory_allocate_flags_info = {0}; + memory_allocate_flags_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + memory_allocate_flags_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + + VkMemoryAllocateInfo memory_allocate_info = {0}; + memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memory_allocate_info.pNext = &memory_allocate_flags_info; + + VkMemoryRequirements mem_reqs = {0}; + vkGetBufferMemoryRequirements(vk_ctx.device, pipeline->impl.raygen_shader_binding_table, &mem_reqs); + memory_allocate_info.allocationSize = mem_reqs.size; + memory_type_from_properties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + &memory_allocate_info.memoryTypeIndex); + + VkDeviceMemory mem; + void *data; + vkAllocateMemory(vk_ctx.device, &memory_allocate_info, NULL, &mem); + vkBindBufferMemory(vk_ctx.device, pipeline->impl.raygen_shader_binding_table, mem, 0); + vkMapMemory(vk_ctx.device, mem, 0, handle_size, 0, (void **)&data); + memcpy(data, shader_handle_storage, handle_size); + vkUnmapMemory(vk_ctx.device, mem); + + vkGetBufferMemoryRequirements(vk_ctx.device, pipeline->impl.miss_shader_binding_table, &mem_reqs); + memory_allocate_info.allocationSize = mem_reqs.size; + memory_type_from_properties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memory_allocate_info.memoryTypeIndex); + + vkAllocateMemory(vk_ctx.device, &memory_allocate_info, NULL, &mem); + vkBindBufferMemory(vk_ctx.device, pipeline->impl.miss_shader_binding_table, mem, 0); + vkMapMemory(vk_ctx.device, mem, 0, handle_size, 0, (void **)&data); + memcpy(data, shader_handle_storage + handle_size_aligned, handle_size); + vkUnmapMemory(vk_ctx.device, mem); + + vkGetBufferMemoryRequirements(vk_ctx.device, pipeline->impl.hit_shader_binding_table, &mem_reqs); + memory_allocate_info.allocationSize = mem_reqs.size; + memory_type_from_properties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &memory_allocate_info.memoryTypeIndex); + + vkAllocateMemory(vk_ctx.device, &memory_allocate_info, NULL, &mem); + vkBindBufferMemory(vk_ctx.device, pipeline->impl.hit_shader_binding_table, mem, 0); + vkMapMemory(vk_ctx.device, mem, 0, handle_size, 0, (void **)&data); + memcpy(data, shader_handle_storage + handle_size_aligned * 2, handle_size); + vkUnmapMemory(vk_ctx.device, mem); + } + + { + VkDescriptorPoolSize type_counts[12]; + memset(type_counts, 0, sizeof(type_counts)); + + type_counts[0].type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; + type_counts[0].descriptorCount = 1; + + type_counts[1].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + type_counts[1].descriptorCount = 1; + + type_counts[2].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + type_counts[2].descriptorCount = 1; + + type_counts[3].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + type_counts[3].descriptorCount = 1; + + type_counts[4].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + type_counts[4].descriptorCount = 1; + + type_counts[5].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + type_counts[5].descriptorCount = 1; + + type_counts[6].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + type_counts[6].descriptorCount = 1; + + type_counts[7].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + type_counts[7].descriptorCount = 1; + + type_counts[8].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + type_counts[8].descriptorCount = 1; + + type_counts[9].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + type_counts[9].descriptorCount = 1; + + type_counts[10].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + type_counts[10].descriptorCount = 1; + + type_counts[11].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + type_counts[11].descriptorCount = 1; + + VkDescriptorPoolCreateInfo descriptor_pool_create_info = {0}; + descriptor_pool_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descriptor_pool_create_info.pNext = NULL; + descriptor_pool_create_info.maxSets = 1024; + descriptor_pool_create_info.poolSizeCount = 12; + descriptor_pool_create_info.pPoolSizes = type_counts; + + vkCreateDescriptorPool(vk_ctx.device, &descriptor_pool_create_info, NULL, &raytrace_descriptor_pool); + + VkDescriptorSetAllocateInfo alloc_info = {0}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.pNext = NULL; + alloc_info.descriptorPool = raytrace_descriptor_pool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &pipeline->impl.descriptor_set_layout; + vkAllocateDescriptorSets(vk_ctx.device, &alloc_info, &pipeline->impl.descriptor_set); + } +} + +void kinc_raytrace_pipeline_destroy(kinc_raytrace_pipeline_t *pipeline) { + vkDestroyPipeline(vk_ctx.device, pipeline->impl.pipeline, NULL); + vkDestroyPipelineLayout(vk_ctx.device, pipeline->impl.pipeline_layout, NULL); + vkDestroyDescriptorSetLayout(vk_ctx.device, pipeline->impl.descriptor_set_layout, NULL); +} + +uint64_t get_buffer_device_address(VkBuffer buffer) { + VkBufferDeviceAddressInfoKHR buffer_device_address_info = {0}; + buffer_device_address_info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; + buffer_device_address_info.buffer = buffer; + _vkGetBufferDeviceAddressKHR = (void *)vkGetDeviceProcAddr(vk_ctx.device, "vkGetBufferDeviceAddressKHR"); + return _vkGetBufferDeviceAddressKHR(vk_ctx.device, &buffer_device_address_info); +} + +void kinc_raytrace_acceleration_structure_init(kinc_raytrace_acceleration_structure_t *accel, kinc_g5_command_list_t *command_list, kinc_g5_vertex_buffer_t *_vb, + kinc_g5_index_buffer_t *_ib, float scale) { + _vkGetBufferDeviceAddressKHR = (void *)vkGetDeviceProcAddr(vk_ctx.device, "vkGetBufferDeviceAddressKHR"); + _vkCreateAccelerationStructureKHR = (void *)vkGetDeviceProcAddr(vk_ctx.device, "vkCreateAccelerationStructureKHR"); + _vkGetAccelerationStructureDeviceAddressKHR = (void *)vkGetDeviceProcAddr(vk_ctx.device, "vkGetAccelerationStructureDeviceAddressKHR"); + _vkGetAccelerationStructureBuildSizesKHR = (void *)vkGetDeviceProcAddr(vk_ctx.device, "vkGetAccelerationStructureBuildSizesKHR"); + + vb = _vb; + ib = _ib; + + { + VkDeviceOrHostAddressConstKHR vertex_data_device_address = {0}; + VkDeviceOrHostAddressConstKHR index_data_device_address = {0}; + + vertex_data_device_address.deviceAddress = get_buffer_device_address(vb->impl.vertices.buf); + index_data_device_address.deviceAddress = get_buffer_device_address(ib->impl.buf); + + VkAccelerationStructureGeometryKHR acceleration_geometry = {0}; + acceleration_geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; + acceleration_geometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR; + acceleration_geometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR; + acceleration_geometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR; + acceleration_geometry.geometry.triangles.vertexFormat = VK_FORMAT_R16G16B16A16_SNORM; + acceleration_geometry.geometry.triangles.vertexData.deviceAddress = vertex_data_device_address.deviceAddress; + acceleration_geometry.geometry.triangles.vertexStride = vb->impl.myStride; + acceleration_geometry.geometry.triangles.maxVertex = vb->impl.myCount; + acceleration_geometry.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32; + acceleration_geometry.geometry.triangles.indexData.deviceAddress = index_data_device_address.deviceAddress; + + VkAccelerationStructureBuildGeometryInfoKHR acceleration_structure_build_geometry_info = {0}; + acceleration_structure_build_geometry_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + acceleration_structure_build_geometry_info.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + acceleration_structure_build_geometry_info.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; + acceleration_structure_build_geometry_info.geometryCount = 1; + acceleration_structure_build_geometry_info.pGeometries = &acceleration_geometry; + + VkAccelerationStructureBuildSizesInfoKHR acceleration_build_sizes_info = {0}; + acceleration_build_sizes_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR; + const uint32_t primitive_count = ib->impl.count / 3; + _vkGetAccelerationStructureBuildSizesKHR(vk_ctx.device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &acceleration_structure_build_geometry_info, + &primitive_count, &acceleration_build_sizes_info); + + VkBufferCreateInfo buffer_create_info = {0}; + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.size = acceleration_build_sizes_info.accelerationStructureSize; + buffer_create_info.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR; + buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VkBuffer bottom_level_buffer = VK_NULL_HANDLE; + vkCreateBuffer(vk_ctx.device, &buffer_create_info, NULL, &bottom_level_buffer); + + VkMemoryRequirements memory_requirements2; + vkGetBufferMemoryRequirements(vk_ctx.device, bottom_level_buffer, &memory_requirements2); + + VkMemoryAllocateFlagsInfo memory_allocate_flags_info2 = {0}; + memory_allocate_flags_info2.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + memory_allocate_flags_info2.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + + VkMemoryAllocateInfo memory_allocate_info = {0}; + memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memory_allocate_info.pNext = &memory_allocate_flags_info2; + memory_allocate_info.allocationSize = memory_requirements2.size; + memory_type_from_properties(memory_requirements2.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memory_allocate_info.memoryTypeIndex); + VkDeviceMemory mem; + vkAllocateMemory(vk_ctx.device, &memory_allocate_info, NULL, &mem); + vkBindBufferMemory(vk_ctx.device, bottom_level_buffer, mem, 0); + + VkAccelerationStructureCreateInfoKHR acceleration_create_info = {0}; + acceleration_create_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR; + acceleration_create_info.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + acceleration_create_info.buffer = bottom_level_buffer; + acceleration_create_info.size = acceleration_build_sizes_info.accelerationStructureSize; + _vkCreateAccelerationStructureKHR(vk_ctx.device, &acceleration_create_info, NULL, &accel->impl.bottom_level_acceleration_structure); + + VkBuffer scratch_buffer = VK_NULL_HANDLE; + VkDeviceMemory scratch_memory = VK_NULL_HANDLE; + + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.size = acceleration_build_sizes_info.buildScratchSize; + buffer_create_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + vkCreateBuffer(vk_ctx.device, &buffer_create_info, NULL, &scratch_buffer); + + VkMemoryRequirements memory_requirements; + vkGetBufferMemoryRequirements(vk_ctx.device, scratch_buffer, &memory_requirements); + + VkMemoryAllocateFlagsInfo memory_allocate_flags_info = {0}; + memory_allocate_flags_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + memory_allocate_flags_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + + memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memory_allocate_info.pNext = &memory_allocate_flags_info; + memory_allocate_info.allocationSize = memory_requirements.size; + memory_type_from_properties(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memory_allocate_info.memoryTypeIndex); + vkAllocateMemory(vk_ctx.device, &memory_allocate_info, NULL, &scratch_memory); + vkBindBufferMemory(vk_ctx.device, scratch_buffer, scratch_memory, 0); + + VkBufferDeviceAddressInfoKHR buffer_device_address_info = {0}; + buffer_device_address_info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; + buffer_device_address_info.buffer = scratch_buffer; + uint64_t scratch_buffer_device_address = _vkGetBufferDeviceAddressKHR(vk_ctx.device, &buffer_device_address_info); + + VkAccelerationStructureBuildGeometryInfoKHR acceleration_build_geometry_info = {0}; + acceleration_build_geometry_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + acceleration_build_geometry_info.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR; + acceleration_build_geometry_info.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; + acceleration_build_geometry_info.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; + acceleration_build_geometry_info.dstAccelerationStructure = accel->impl.bottom_level_acceleration_structure; + acceleration_build_geometry_info.geometryCount = 1; + acceleration_build_geometry_info.pGeometries = &acceleration_geometry; + acceleration_build_geometry_info.scratchData.deviceAddress = scratch_buffer_device_address; + + VkAccelerationStructureBuildRangeInfoKHR acceleration_build_range_info = {0}; + acceleration_build_range_info.primitiveCount = ib->impl.count / 3; + acceleration_build_range_info.primitiveOffset = 0x0; + acceleration_build_range_info.firstVertex = 0; + acceleration_build_range_info.transformOffset = 0x0; + + const VkAccelerationStructureBuildRangeInfoKHR *acceleration_build_infos[1] = {&acceleration_build_range_info}; + + { + VkCommandBufferAllocateInfo cmd_buf_allocate_info = {0}; + cmd_buf_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmd_buf_allocate_info.commandPool = vk_ctx.cmd_pool; + cmd_buf_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd_buf_allocate_info.commandBufferCount = 1; + + VkCommandBuffer command_buffer; + vkAllocateCommandBuffers(vk_ctx.device, &cmd_buf_allocate_info, &command_buffer); + + VkCommandBufferBeginInfo command_buffer_info = {0}; + command_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + vkBeginCommandBuffer(command_buffer, &command_buffer_info); + + _vkCmdBuildAccelerationStructuresKHR = (void *)vkGetDeviceProcAddr(vk_ctx.device, "vkCmdBuildAccelerationStructuresKHR"); + _vkCmdBuildAccelerationStructuresKHR(command_buffer, 1, &acceleration_build_geometry_info, &acceleration_build_infos[0]); + + vkEndCommandBuffer(command_buffer); + + VkSubmitInfo submit_info = {0}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + + VkFenceCreateInfo fence_info = {0}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_info.flags = 0; + + VkFence fence; + vkCreateFence(vk_ctx.device, &fence_info, NULL, &fence); + + VkResult result = vkQueueSubmit(vk_ctx.queue, 1, &submit_info, fence); + assert(!result); + vkWaitForFences(vk_ctx.device, 1, &fence, VK_TRUE, 100000000000); + vkDestroyFence(vk_ctx.device, fence, NULL); + vkFreeCommandBuffers(vk_ctx.device, vk_ctx.cmd_pool, 1, &command_buffer); + } + + VkAccelerationStructureDeviceAddressInfoKHR acceleration_device_address_info = {0}; + acceleration_device_address_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR; + acceleration_device_address_info.accelerationStructure = accel->impl.bottom_level_acceleration_structure; + + accel->impl.bottom_level_acceleration_structure_handle = _vkGetAccelerationStructureDeviceAddressKHR(vk_ctx.device, &acceleration_device_address_info); + + vkFreeMemory(vk_ctx.device, scratch_memory, NULL); + vkDestroyBuffer(vk_ctx.device, scratch_buffer, NULL); + } + + { + VkTransformMatrixKHR transform_matrix = {scale, 0.0f, 0.0f, 0.0f, 0.0f, scale, 0.0f, 0.0f, 0.0f, 0.0f, scale, 0.0f}; + + VkAccelerationStructureInstanceKHR instance = {0}; + instance.transform = transform_matrix; + instance.instanceCustomIndex = 0; + instance.mask = 0xFF; + instance.instanceShaderBindingTableRecordOffset = 0; + instance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR; + instance.accelerationStructureReference = accel->impl.bottom_level_acceleration_structure_handle; + + VkBufferCreateInfo buf_info = {0}; + buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buf_info.pNext = NULL; + buf_info.size = sizeof(instance); + buf_info.usage = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR; + buf_info.flags = 0; + + VkMemoryAllocateInfo mem_alloc; + memset(&mem_alloc, 0, sizeof(VkMemoryAllocateInfo)); + mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc.pNext = NULL; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; + + VkBuffer instances_buffer; + vkCreateBuffer(vk_ctx.device, &buf_info, NULL, &instances_buffer); + + VkMemoryRequirements mem_reqs = {0}; + vkGetBufferMemoryRequirements(vk_ctx.device, instances_buffer, &mem_reqs); + + mem_alloc.allocationSize = mem_reqs.size; + memory_type_from_properties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &mem_alloc.memoryTypeIndex); + + VkMemoryAllocateFlagsInfo memory_allocate_flags_info = {0}; + memory_allocate_flags_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + memory_allocate_flags_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + mem_alloc.pNext = &memory_allocate_flags_info; + + VkDeviceMemory mem; + vkAllocateMemory(vk_ctx.device, &mem_alloc, NULL, &mem); + + vkBindBufferMemory(vk_ctx.device, instances_buffer, mem, 0); + void *data; + vkMapMemory(vk_ctx.device, mem, 0, sizeof(VkAccelerationStructureInstanceKHR), 0, (void **)&data); + memcpy(data, &instance, sizeof(VkAccelerationStructureInstanceKHR)); + vkUnmapMemory(vk_ctx.device, mem); + + VkDeviceOrHostAddressConstKHR instance_data_device_address = {0}; + instance_data_device_address.deviceAddress = get_buffer_device_address(instances_buffer); + + VkAccelerationStructureGeometryKHR acceleration_geometry = {0}; + acceleration_geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR; + acceleration_geometry.flags = VK_GEOMETRY_OPAQUE_BIT_KHR; + acceleration_geometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR; + acceleration_geometry.geometry.instances.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR; + acceleration_geometry.geometry.instances.arrayOfPointers = VK_FALSE; + acceleration_geometry.geometry.instances.data.deviceAddress = instance_data_device_address.deviceAddress; + + VkAccelerationStructureBuildGeometryInfoKHR acceleration_structure_build_geometry_info = {0}; + acceleration_structure_build_geometry_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + acceleration_structure_build_geometry_info.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; + acceleration_structure_build_geometry_info.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; + acceleration_structure_build_geometry_info.geometryCount = 1; + acceleration_structure_build_geometry_info.pGeometries = &acceleration_geometry; + + VkAccelerationStructureBuildSizesInfoKHR acceleration_build_sizes_info = {0}; + acceleration_build_sizes_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR; + const uint32_t primitive_count = 1; + _vkGetAccelerationStructureBuildSizesKHR(vk_ctx.device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &acceleration_structure_build_geometry_info, + &primitive_count, &acceleration_build_sizes_info); + + VkBufferCreateInfo buffer_create_info = {0}; + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.size = acceleration_build_sizes_info.accelerationStructureSize; + buffer_create_info.usage = VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR; + buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VkBuffer top_level_buffer = VK_NULL_HANDLE; + vkCreateBuffer(vk_ctx.device, &buffer_create_info, NULL, &top_level_buffer); + + VkMemoryRequirements memory_requirements2; + vkGetBufferMemoryRequirements(vk_ctx.device, top_level_buffer, &memory_requirements2); + + memory_allocate_flags_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + memory_allocate_flags_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + + VkMemoryAllocateInfo memory_allocate_info = {0}; + memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memory_allocate_info.pNext = &memory_allocate_flags_info; + memory_allocate_info.allocationSize = memory_requirements2.size; + memory_type_from_properties(memory_requirements2.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memory_allocate_info.memoryTypeIndex); + vkAllocateMemory(vk_ctx.device, &memory_allocate_info, NULL, &mem); + vkBindBufferMemory(vk_ctx.device, top_level_buffer, mem, 0); + + VkAccelerationStructureCreateInfoKHR acceleration_create_info = {0}; + acceleration_create_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR; + acceleration_create_info.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; + acceleration_create_info.buffer = top_level_buffer; + acceleration_create_info.size = acceleration_build_sizes_info.accelerationStructureSize; + _vkCreateAccelerationStructureKHR(vk_ctx.device, &acceleration_create_info, NULL, &accel->impl.top_level_acceleration_structure); + + VkBuffer scratch_buffer = VK_NULL_HANDLE; + VkDeviceMemory scratch_memory = VK_NULL_HANDLE; + + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.size = acceleration_build_sizes_info.buildScratchSize; + buffer_create_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + vkCreateBuffer(vk_ctx.device, &buffer_create_info, NULL, &scratch_buffer); + + VkMemoryRequirements memory_requirements; + vkGetBufferMemoryRequirements(vk_ctx.device, scratch_buffer, &memory_requirements); + + memory_allocate_flags_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + memory_allocate_flags_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + + memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memory_allocate_info.pNext = &memory_allocate_flags_info; + memory_allocate_info.allocationSize = memory_requirements.size; + memory_type_from_properties(memory_requirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memory_allocate_info.memoryTypeIndex); + vkAllocateMemory(vk_ctx.device, &memory_allocate_info, NULL, &scratch_memory); + vkBindBufferMemory(vk_ctx.device, scratch_buffer, scratch_memory, 0); + + VkBufferDeviceAddressInfoKHR buffer_device_address_info = {0}; + buffer_device_address_info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; + buffer_device_address_info.buffer = scratch_buffer; + uint64_t scratch_buffer_device_address = _vkGetBufferDeviceAddressKHR(vk_ctx.device, &buffer_device_address_info); + + VkAccelerationStructureBuildGeometryInfoKHR acceleration_build_geometry_info = {0}; + acceleration_build_geometry_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR; + acceleration_build_geometry_info.type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR; + acceleration_build_geometry_info.flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR; + acceleration_build_geometry_info.mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR; + acceleration_build_geometry_info.srcAccelerationStructure = VK_NULL_HANDLE; + acceleration_build_geometry_info.dstAccelerationStructure = accel->impl.top_level_acceleration_structure; + acceleration_build_geometry_info.geometryCount = 1; + acceleration_build_geometry_info.pGeometries = &acceleration_geometry; + acceleration_build_geometry_info.scratchData.deviceAddress = scratch_buffer_device_address; + + VkAccelerationStructureBuildRangeInfoKHR acceleration_build_range_info = {0}; + acceleration_build_range_info.primitiveCount = 1; + acceleration_build_range_info.primitiveOffset = 0x0; + acceleration_build_range_info.firstVertex = 0; + acceleration_build_range_info.transformOffset = 0x0; + + const VkAccelerationStructureBuildRangeInfoKHR *acceleration_build_infos[1] = {&acceleration_build_range_info}; + + { + VkCommandBufferAllocateInfo cmd_buf_allocate_info = {0}; + cmd_buf_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + cmd_buf_allocate_info.commandPool = vk_ctx.cmd_pool; + cmd_buf_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmd_buf_allocate_info.commandBufferCount = 1; + + VkCommandBuffer command_buffer; + vkAllocateCommandBuffers(vk_ctx.device, &cmd_buf_allocate_info, &command_buffer); + + VkCommandBufferBeginInfo command_buffer_info = {0}; + command_buffer_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + vkBeginCommandBuffer(command_buffer, &command_buffer_info); + + _vkCmdBuildAccelerationStructuresKHR = (void *)vkGetDeviceProcAddr(vk_ctx.device, "vkCmdBuildAccelerationStructuresKHR"); + _vkCmdBuildAccelerationStructuresKHR(command_buffer, 1, &acceleration_build_geometry_info, &acceleration_build_infos[0]); + + vkEndCommandBuffer(command_buffer); + + VkSubmitInfo submit_info = {0}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + + VkFenceCreateInfo fence_info = {0}; + fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_info.flags = 0; + + VkFence fence; + vkCreateFence(vk_ctx.device, &fence_info, NULL, &fence); + + VkResult result = vkQueueSubmit(vk_ctx.queue, 1, &submit_info, fence); + assert(!result); + vkWaitForFences(vk_ctx.device, 1, &fence, VK_TRUE, 100000000000); + vkDestroyFence(vk_ctx.device, fence, NULL); + + vkFreeCommandBuffers(vk_ctx.device, vk_ctx.cmd_pool, 1, &command_buffer); + } + + VkAccelerationStructureDeviceAddressInfoKHR acceleration_device_address_info = {0}; + acceleration_device_address_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR; + acceleration_device_address_info.accelerationStructure = accel->impl.top_level_acceleration_structure; + + accel->impl.top_level_acceleration_structure_handle = _vkGetAccelerationStructureDeviceAddressKHR(vk_ctx.device, &acceleration_device_address_info); + + vkFreeMemory(vk_ctx.device, scratch_memory, NULL); + vkDestroyBuffer(vk_ctx.device, scratch_buffer, NULL); + } +} + +void kinc_raytrace_acceleration_structure_destroy(kinc_raytrace_acceleration_structure_t *accel) { + _vkDestroyAccelerationStructureKHR = (void *)vkGetDeviceProcAddr(vk_ctx.device, "vkDestroyAccelerationStructureKHR"); + _vkDestroyAccelerationStructureKHR(vk_ctx.device, accel->impl.bottom_level_acceleration_structure, NULL); + _vkDestroyAccelerationStructureKHR(vk_ctx.device, accel->impl.top_level_acceleration_structure, NULL); +} + +void kinc_raytrace_set_textures(kinc_g5_render_target_t *_texpaint0, kinc_g5_render_target_t *_texpaint1, kinc_g5_render_target_t *_texpaint2, kinc_g5_texture_t *_texenv, kinc_g5_texture_t *_texsobol, kinc_g5_texture_t *_texscramble, kinc_g5_texture_t *_texrank) { + texpaint0 = _texpaint0; + texpaint1 = _texpaint1; + texpaint2 = _texpaint2; + texenv = _texenv; + texsobol = _texsobol; + texscramble = _texscramble; + texrank = _texrank; +} + +void kinc_raytrace_set_acceleration_structure(kinc_raytrace_acceleration_structure_t *_accel) { + accel = _accel; +} + +void kinc_raytrace_set_pipeline(kinc_raytrace_pipeline_t *_pipeline) { + pipeline = _pipeline; +} + +void kinc_raytrace_set_target(kinc_g5_render_target_t *_output) { + if (_output != output) { + vkDestroyImage(vk_ctx.device, _output->impl.sourceImage, NULL); + + VkImageCreateInfo image = {0}; + image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image.pNext = NULL; + image.imageType = VK_IMAGE_TYPE_2D; + image.format = _output->impl.format; + image.extent.width = _output->width; + image.extent.height = _output->height; + image.extent.depth = 1; + image.mipLevels = 1; + image.arrayLayers = 1; + image.samples = VK_SAMPLE_COUNT_1_BIT; + image.tiling = VK_IMAGE_TILING_OPTIMAL; + image.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT; + image.flags = 0; + + vkCreateImage(vk_ctx.device, &image, NULL, &_output->impl.sourceImage); + + vkBindImageMemory(vk_ctx.device, _output->impl.sourceImage, _output->impl.sourceMemory, 0); + + VkImageViewCreateInfo colorImageView = {0}; + colorImageView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + colorImageView.pNext = NULL; + colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D; + colorImageView.format = _output->impl.format; + colorImageView.flags = 0; + colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + colorImageView.subresourceRange.baseMipLevel = 0; + colorImageView.subresourceRange.levelCount = 1; + colorImageView.subresourceRange.baseArrayLayer = 0; + colorImageView.subresourceRange.layerCount = 1; + colorImageView.image = _output->impl.sourceImage; + vkCreateImageView(vk_ctx.device, &colorImageView, NULL, &_output->impl.sourceView); + + VkImageView attachments[1]; + attachments[0] = _output->impl.sourceView; + + VkFramebufferCreateInfo fbufCreateInfo = {0}; + fbufCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbufCreateInfo.pNext = NULL; + fbufCreateInfo.renderPass = vk_ctx.windows[vk_ctx.current_window].rendertarget_render_pass; + fbufCreateInfo.attachmentCount = 1; + fbufCreateInfo.pAttachments = attachments; + fbufCreateInfo.width = _output->width; + fbufCreateInfo.height = _output->height; + fbufCreateInfo.layers = 1; + vkCreateFramebuffer(vk_ctx.device, &fbufCreateInfo, NULL, &_output->impl.framebuffer); + } + output = _output; +} + +void kinc_raytrace_dispatch_rays(kinc_g5_command_list_t *command_list) { + VkWriteDescriptorSetAccelerationStructureKHR descriptor_acceleration_structure_info = {0}; + descriptor_acceleration_structure_info.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR; + descriptor_acceleration_structure_info.accelerationStructureCount = 1; + descriptor_acceleration_structure_info.pAccelerationStructures = &accel->impl.top_level_acceleration_structure; + + VkWriteDescriptorSet acceleration_structure_write = {0}; + acceleration_structure_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + acceleration_structure_write.pNext = &descriptor_acceleration_structure_info; + acceleration_structure_write.dstSet = pipeline->impl.descriptor_set; + acceleration_structure_write.dstBinding = 0; + acceleration_structure_write.descriptorCount = 1; + acceleration_structure_write.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; + + VkDescriptorImageInfo image_descriptor = {0}; + image_descriptor.imageView = output->impl.sourceView; + image_descriptor.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + + VkDescriptorBufferInfo buffer_descriptor = {0}; + buffer_descriptor.buffer = pipeline->_constant_buffer->impl.buf; + buffer_descriptor.range = VK_WHOLE_SIZE; + buffer_descriptor.offset = 0; + + VkWriteDescriptorSet result_image_write = {0}; + result_image_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + result_image_write.pNext = NULL; + result_image_write.dstSet = pipeline->impl.descriptor_set; + result_image_write.dstBinding = 10; + result_image_write.descriptorCount = 1; + result_image_write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + result_image_write.pImageInfo = &image_descriptor; + + VkWriteDescriptorSet uniform_buffer_write = {0}; + uniform_buffer_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + uniform_buffer_write.pNext = NULL; + uniform_buffer_write.dstSet = pipeline->impl.descriptor_set; + uniform_buffer_write.dstBinding = 11; + uniform_buffer_write.descriptorCount = 1; + uniform_buffer_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + uniform_buffer_write.pBufferInfo = &buffer_descriptor; + + VkDescriptorBufferInfo ib_descriptor = {0}; + ib_descriptor.buffer = ib->impl.buf; + ib_descriptor.range = VK_WHOLE_SIZE; + ib_descriptor.offset = 0; + + VkWriteDescriptorSet ib_write = {0}; + ib_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + ib_write.pNext = NULL; + ib_write.dstSet = pipeline->impl.descriptor_set; + ib_write.dstBinding = 1; + ib_write.descriptorCount = 1; + ib_write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + ib_write.pBufferInfo = &ib_descriptor; + + VkDescriptorBufferInfo vb_descriptor = {0}; + vb_descriptor.buffer = vb->impl.vertices.buf; + vb_descriptor.range = VK_WHOLE_SIZE; + vb_descriptor.offset = 0; + + VkWriteDescriptorSet vb_write = {0}; + vb_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + vb_write.pNext = NULL; + vb_write.dstSet = pipeline->impl.descriptor_set; + vb_write.dstBinding = 2; + vb_write.descriptorCount = 1; + vb_write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + vb_write.pBufferInfo = &vb_descriptor; + + VkDescriptorImageInfo tex0image_descriptor = {0}; + tex0image_descriptor.imageView = texpaint0->impl.sourceView; + tex0image_descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet tex0_image_write = {0}; + tex0_image_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + tex0_image_write.pNext = NULL; + tex0_image_write.dstSet = pipeline->impl.descriptor_set; + tex0_image_write.dstBinding = 3; + tex0_image_write.descriptorCount = 1; + tex0_image_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + tex0_image_write.pImageInfo = &tex0image_descriptor; + + VkDescriptorImageInfo tex1image_descriptor = {0}; + tex1image_descriptor.imageView = texpaint1->impl.sourceView; + tex1image_descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet tex1_image_write = {0}; + tex1_image_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + tex1_image_write.pNext = NULL; + tex1_image_write.dstSet = pipeline->impl.descriptor_set; + tex1_image_write.dstBinding = 4; + tex1_image_write.descriptorCount = 1; + tex1_image_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + tex1_image_write.pImageInfo = &tex1image_descriptor; + + VkDescriptorImageInfo tex2image_descriptor = {0}; + tex2image_descriptor.imageView = texpaint2->impl.sourceView; + tex2image_descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet tex2_image_write = {0}; + tex2_image_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + tex2_image_write.pNext = NULL; + tex2_image_write.dstSet = pipeline->impl.descriptor_set; + tex2_image_write.dstBinding = 5; + tex2_image_write.descriptorCount = 1; + tex2_image_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + tex2_image_write.pImageInfo = &tex2image_descriptor; + + VkDescriptorImageInfo texenvimage_descriptor = {0}; + texenvimage_descriptor.imageView = texenv->impl.texture.view; + texenvimage_descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet texenv_image_write = {0}; + texenv_image_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + texenv_image_write.pNext = NULL; + texenv_image_write.dstSet = pipeline->impl.descriptor_set; + texenv_image_write.dstBinding = 6; + texenv_image_write.descriptorCount = 1; + texenv_image_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + texenv_image_write.pImageInfo = &texenvimage_descriptor; + + VkDescriptorImageInfo texsobolimage_descriptor = {0}; + texsobolimage_descriptor.imageView = texsobol->impl.texture.view; + texsobolimage_descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet texsobol_image_write = {0}; + texsobol_image_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + texsobol_image_write.pNext = NULL; + texsobol_image_write.dstSet = pipeline->impl.descriptor_set; + texsobol_image_write.dstBinding = 7; + texsobol_image_write.descriptorCount = 1; + texsobol_image_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + texsobol_image_write.pImageInfo = &texsobolimage_descriptor; + + VkDescriptorImageInfo texscrambleimage_descriptor = {0}; + texscrambleimage_descriptor.imageView = texscramble->impl.texture.view; + texscrambleimage_descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet texscramble_image_write = {0}; + texscramble_image_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + texscramble_image_write.pNext = NULL; + texscramble_image_write.dstSet = pipeline->impl.descriptor_set; + texscramble_image_write.dstBinding = 8; + texscramble_image_write.descriptorCount = 1; + texscramble_image_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + texscramble_image_write.pImageInfo = &texscrambleimage_descriptor; + + VkDescriptorImageInfo texrankimage_descriptor = {0}; + texrankimage_descriptor.imageView = texrank->impl.texture.view; + texrankimage_descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + VkWriteDescriptorSet texrank_image_write = {0}; + texrank_image_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + texrank_image_write.pNext = NULL; + texrank_image_write.dstSet = pipeline->impl.descriptor_set; + texrank_image_write.dstBinding = 9; + texrank_image_write.descriptorCount = 1; + texrank_image_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + texrank_image_write.pImageInfo = &texrankimage_descriptor; + + VkWriteDescriptorSet write_descriptor_sets[12] = { + acceleration_structure_write, + result_image_write, + uniform_buffer_write, + vb_write, + ib_write, + tex0_image_write, + tex1_image_write, + tex2_image_write, + texenv_image_write, + texsobol_image_write, + texscramble_image_write, + texrank_image_write + }; + vkUpdateDescriptorSets(vk_ctx.device, 12, write_descriptor_sets, 0, VK_NULL_HANDLE); + + VkPhysicalDeviceRayTracingPipelinePropertiesKHR ray_tracing_pipeline_properties; + ray_tracing_pipeline_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR; + ray_tracing_pipeline_properties.pNext = NULL; + VkPhysicalDeviceProperties2 device_properties = {0}; + device_properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + device_properties.pNext = &ray_tracing_pipeline_properties; + vkGetPhysicalDeviceProperties2(vk_ctx.gpu, &device_properties); + + // Setup the strided buffer regions pointing to the shaders in our shader binding table + const uint32_t handle_size_aligned = + (ray_tracing_pipeline_properties.shaderGroupHandleSize + ray_tracing_pipeline_properties.shaderGroupHandleAlignment - 1) & + ~(ray_tracing_pipeline_properties.shaderGroupHandleAlignment - 1); + + VkStridedDeviceAddressRegionKHR raygen_shader_sbt_entry = {0}; + raygen_shader_sbt_entry.deviceAddress = get_buffer_device_address(pipeline->impl.raygen_shader_binding_table); + raygen_shader_sbt_entry.stride = handle_size_aligned; + raygen_shader_sbt_entry.size = handle_size_aligned; + + VkStridedDeviceAddressRegionKHR miss_shader_sbt_entry = {0}; + miss_shader_sbt_entry.deviceAddress = get_buffer_device_address(pipeline->impl.miss_shader_binding_table); + miss_shader_sbt_entry.stride = handle_size_aligned; + miss_shader_sbt_entry.size = handle_size_aligned; + + VkStridedDeviceAddressRegionKHR hit_shader_sbt_entry = {0}; + hit_shader_sbt_entry.deviceAddress = get_buffer_device_address(pipeline->impl.hit_shader_binding_table); + hit_shader_sbt_entry.stride = handle_size_aligned; + hit_shader_sbt_entry.size = handle_size_aligned; + + VkStridedDeviceAddressRegionKHR callable_shader_sbt_entry = {0}; + + vkCmdEndRenderPass(command_list->impl._buffer); + + // Dispatch the ray tracing commands + vkCmdBindPipeline(command_list->impl._buffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline->impl.pipeline); + vkCmdBindDescriptorSets(command_list->impl._buffer, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline->impl.pipeline_layout, 0, 1, + &pipeline->impl.descriptor_set, 0, 0); + + _vkCmdTraceRaysKHR = (void *)vkGetDeviceProcAddr(vk_ctx.device, "vkCmdTraceRaysKHR"); + _vkCmdTraceRaysKHR(command_list->impl._buffer, &raygen_shader_sbt_entry, &miss_shader_sbt_entry, &hit_shader_sbt_entry, &callable_shader_sbt_entry, + output->texWidth, output->texHeight, 1); + + vkCmdBeginRenderPass(command_list->impl._buffer, ¤tRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); +} + +void kinc_raytrace_copy(kinc_g5_command_list_t *command_list, kinc_g5_render_target_t *target, kinc_g5_texture_t *source) { + + /*vkCmdEndRenderPass(command_list->impl._buffer); + + VkImageCopy copy_region = {0}; + copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_region.srcSubresource.layerCount = 1; + copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_region.dstSubresource.layerCount = 1; + copy_region.extent.width = (uint32_t)output->texWidth; + copy_region.extent.height = (uint32_t)output->texHeight; + copy_region.extent.depth = 1; + + if (target->framebuffer_index >= 0) { + vkCmdCopyImage(command_list->impl._buffer, output->impl.sourceImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + vk_ctx.windows[vk_ctx.current_window].images[vk_ctx.windows[vk_ctx.current_window].current_image], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ©_region); + } + else { + vkCmdCopyImage(command_list->impl._buffer, output->impl.sourceImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, target->impl.sourceImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); + } + + vkCmdBeginRenderPass(command_list->impl._buffer, ¤tRenderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);*/ +} + +#endif diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/raytrace.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/raytrace.h new file mode 100644 index 000000000..31538e74f --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/raytrace.h @@ -0,0 +1,32 @@ +#pragma once + +#ifndef KINC_ANDROID + +#include "MiniVulkan.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + VkPipeline pipeline; + VkPipelineLayout pipeline_layout; + VkDescriptorSet descriptor_set; + VkDescriptorSetLayout descriptor_set_layout; + VkBuffer raygen_shader_binding_table; + VkBuffer miss_shader_binding_table; + VkBuffer hit_shader_binding_table; +} kinc_raytrace_pipeline_impl_t; + +typedef struct { + VkAccelerationStructureKHR top_level_acceleration_structure; + VkAccelerationStructureKHR bottom_level_acceleration_structure; + uint64_t top_level_acceleration_structure_handle; + uint64_t bottom_level_acceleration_structure_handle; +} kinc_raytrace_acceleration_structure_impl_t; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/rendertarget.c.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/rendertarget.c.h new file mode 100644 index 000000000..5bcb34c71 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/rendertarget.c.h @@ -0,0 +1,331 @@ +#include "vulkan.h" + +#include "rendertarget.h" + +#include +#include +#include + +extern uint32_t swapchainImageCount; +extern kinc_g5_texture_t *vulkanTextures[16]; +extern kinc_g5_render_target_t *vulkanRenderTargets[16]; + +bool memory_type_from_properties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex); +void setup_init_cmd(); + +/*static VkFormat convert_format(kinc_g5_render_target_format_t format) { + switch (format) { + case KINC_G5_RENDER_TARGET_FORMAT_128BIT_FLOAT: + return VK_FORMAT_R32G32B32A32_SFLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_64BIT_FLOAT: + return VK_FORMAT_R16G16B16A16_SFLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT: + return VK_FORMAT_R32_SFLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT: + return VK_FORMAT_R16_SFLOAT; + case KINC_G5_RENDER_TARGET_FORMAT_8BIT_RED: + return VK_FORMAT_R8_UNORM; + case KINC_G5_RENDER_TARGET_FORMAT_32BIT: + default: + return VK_FORMAT_B8G8R8A8_UNORM; + } +}*/ + +void setImageLayout(VkCommandBuffer _buffer, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout) { + VkImageMemoryBarrier imageMemoryBarrier = {0}; + imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageMemoryBarrier.pNext = NULL; + imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + imageMemoryBarrier.oldLayout = oldImageLayout; + imageMemoryBarrier.newLayout = newImageLayout; + imageMemoryBarrier.image = image; + imageMemoryBarrier.subresourceRange.aspectMask = aspectMask; + imageMemoryBarrier.subresourceRange.baseMipLevel = 0; + imageMemoryBarrier.subresourceRange.levelCount = 1; + imageMemoryBarrier.subresourceRange.layerCount = 1; + + if (oldImageLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + } + if (oldImageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + imageMemoryBarrier.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + } + if (oldImageLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + } + if (oldImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + } + if (newImageLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + } + if (newImageLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) { + if (oldImageLayout != VK_IMAGE_LAYOUT_UNDEFINED) + imageMemoryBarrier.srcAccessMask = imageMemoryBarrier.srcAccessMask | VK_ACCESS_TRANSFER_READ_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + } + if (newImageLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + if (oldImageLayout != VK_IMAGE_LAYOUT_UNDEFINED) + imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + } + if (newImageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + imageMemoryBarrier.dstAccessMask = imageMemoryBarrier.dstAccessMask | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + } + if (newImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + if (oldImageLayout != VK_IMAGE_LAYOUT_UNDEFINED) + imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; + imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + } + + VkPipelineStageFlags srcStageFlags = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + VkPipelineStageFlags dstStageFlags = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + + vkCmdPipelineBarrier(_buffer, srcStageFlags, dstStageFlags, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier); +} + +static void render_target_init(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, int depthBufferBits, + int stencilBufferBits, int samples_per_pixel, int framebuffer_index) { + target->width = width; + target->height = height; + target->framebuffer_index = framebuffer_index; + target->texWidth = width; + target->texHeight = height; + target->impl.format = convert_format(format); + target->impl.depthBufferBits = depthBufferBits; + target->impl.stage = 0; + target->impl.stage_depth = -1; + target->impl.readbackBufferCreated = false; + + if (framebuffer_index < 0) { + { + VkFormatProperties formatProperties; + VkResult err; + + vkGetPhysicalDeviceFormatProperties(vk_ctx.gpu, target->impl.format, &formatProperties); + assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT); + + VkImageCreateInfo image = {0}; + image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image.pNext = NULL; + image.imageType = VK_IMAGE_TYPE_2D; + image.format = target->impl.format; + image.extent.width = width; + image.extent.height = height; + image.extent.depth = 1; + image.mipLevels = 1; + image.arrayLayers = 1; + image.samples = VK_SAMPLE_COUNT_1_BIT; + image.tiling = VK_IMAGE_TILING_OPTIMAL; + image.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + image.flags = 0; + + VkImageViewCreateInfo colorImageView = {0}; + colorImageView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + colorImageView.pNext = NULL; + colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D; + colorImageView.format = target->impl.format; + colorImageView.flags = 0; + colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + colorImageView.subresourceRange.baseMipLevel = 0; + colorImageView.subresourceRange.levelCount = 1; + colorImageView.subresourceRange.baseArrayLayer = 0; + colorImageView.subresourceRange.layerCount = 1; + + err = vkCreateImage(vk_ctx.device, &image, NULL, &target->impl.sourceImage); + assert(!err); + + VkMemoryRequirements memoryRequirements; + vkGetImageMemoryRequirements(vk_ctx.device, target->impl.sourceImage, &memoryRequirements); + + VkMemoryAllocateInfo allocationInfo = {0}; + allocationInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocationInfo.pNext = NULL; + allocationInfo.memoryTypeIndex = 0; + allocationInfo.allocationSize = memoryRequirements.size; + bool pass = memory_type_from_properties(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &allocationInfo.memoryTypeIndex); + assert(pass); + + err = vkAllocateMemory(vk_ctx.device, &allocationInfo, NULL, &target->impl.sourceMemory); + assert(!err); + + err = vkBindImageMemory(vk_ctx.device, target->impl.sourceImage, target->impl.sourceMemory, 0); + assert(!err); + + setup_init_cmd(); + setImageLayout(vk_ctx.setup_cmd, target->impl.sourceImage, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + flush_init_cmd(); + + colorImageView.image = target->impl.sourceImage; + err = vkCreateImageView(vk_ctx.device, &colorImageView, NULL, &target->impl.sourceView); + assert(!err); + } + + if (depthBufferBits > 0) { + const VkFormat depth_format = VK_FORMAT_D16_UNORM; + VkImageCreateInfo image = {0}; + image.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image.pNext = NULL; + image.imageType = VK_IMAGE_TYPE_2D; + image.format = depth_format; + image.extent.width = width; + image.extent.height = height; + image.extent.depth = 1; + image.mipLevels = 1; + image.arrayLayers = 1; + image.samples = VK_SAMPLE_COUNT_1_BIT; + image.tiling = VK_IMAGE_TILING_OPTIMAL; + image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + image.flags = 0; + + VkResult err = vkCreateImage(vk_ctx.device, &image, NULL, &target->impl.depthImage); + assert(!err); + + VkMemoryAllocateInfo mem_alloc = {0}; + mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc.pNext = NULL; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; + + VkImageViewCreateInfo view = {0}; + view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view.pNext = NULL; + view.image = target->impl.depthImage; + view.format = depth_format; + view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; + view.subresourceRange.baseMipLevel = 0; + view.subresourceRange.levelCount = 1; + view.subresourceRange.baseArrayLayer = 0; + view.subresourceRange.layerCount = 1; + view.flags = 0; + view.viewType = VK_IMAGE_VIEW_TYPE_2D; + + VkMemoryRequirements mem_reqs = {0}; + bool pass; + + /* get memory requirements for this object */ + vkGetImageMemoryRequirements(vk_ctx.device, target->impl.depthImage, &mem_reqs); + + /* select memory size and type */ + mem_alloc.allocationSize = mem_reqs.size; + pass = memory_type_from_properties(mem_reqs.memoryTypeBits, 0, /* No requirements */ &mem_alloc.memoryTypeIndex); + assert(pass); + + /* allocate memory */ + err = vkAllocateMemory(vk_ctx.device, &mem_alloc, NULL, &target->impl.depthMemory); + assert(!err); + + /* bind memory */ + err = vkBindImageMemory(vk_ctx.device, target->impl.depthImage, target->impl.depthMemory, 0); + assert(!err); + + setup_init_cmd(); + setImageLayout(vk_ctx.setup_cmd, target->impl.depthImage, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + flush_init_cmd(); + + /* create image view */ + err = vkCreateImageView(vk_ctx.device, &view, NULL, &target->impl.depthView); + assert(!err); + } + + VkImageView attachments[2]; + attachments[0] = target->impl.sourceView; + + if (depthBufferBits > 0) { + attachments[1] = target->impl.depthView; + } + + VkFramebufferCreateInfo fbufCreateInfo = {0}; + fbufCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbufCreateInfo.pNext = NULL; + if (framebuffer_index >= 0) { + fbufCreateInfo.renderPass = vk_ctx.windows[vk_ctx.current_window].framebuffer_render_pass; + } + else if (depthBufferBits > 0) { + fbufCreateInfo.renderPass = vk_ctx.windows[vk_ctx.current_window].rendertarget_render_pass_with_depth; + } + else { + fbufCreateInfo.renderPass = vk_ctx.windows[vk_ctx.current_window].rendertarget_render_pass; + } + fbufCreateInfo.attachmentCount = depthBufferBits > 0 ? 2 : 1; + fbufCreateInfo.pAttachments = attachments; + fbufCreateInfo.width = width; + fbufCreateInfo.height = height; + fbufCreateInfo.layers = 1; + + VkResult err = vkCreateFramebuffer(vk_ctx.device, &fbufCreateInfo, NULL, &target->impl.framebuffer); + assert(!err); + } +} + +void kinc_g5_render_target_init_with_multisampling(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + render_target_init(target, width, height, format, depthBufferBits, stencilBufferBits, samples_per_pixel, -1); +} + +static int framebuffer_count = 0; + +void kinc_g5_render_target_init_framebuffer_with_multisampling(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + render_target_init(target, width, height, format, depthBufferBits, stencilBufferBits, samples_per_pixel, framebuffer_count); + framebuffer_count += 1; +} + +void kinc_g5_render_target_init_cube_with_multisampling(kinc_g5_render_target_t *target, int cubeMapSize, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) {} + +void kinc_g5_render_target_destroy(kinc_g5_render_target_t *target) { + if (target->framebuffer_index >= 0) { + framebuffer_count -= 1; + } + else { + vkDestroyFramebuffer(vk_ctx.device, target->impl.framebuffer, NULL); + if (target->impl.depthBufferBits > 0) { + vkDestroyImageView(vk_ctx.device, target->impl.depthView, NULL); + vkDestroyImage(vk_ctx.device, target->impl.depthImage, NULL); + vkFreeMemory(vk_ctx.device, target->impl.depthMemory, NULL); + } + vkDestroyImageView(vk_ctx.device, target->impl.sourceView, NULL); + vkDestroyImage(vk_ctx.device, target->impl.sourceImage, NULL); + vkFreeMemory(vk_ctx.device, target->impl.sourceMemory, NULL); + } +} + +void kinc_g5_render_target_set_depth_stencil_from(kinc_g5_render_target_t *target, kinc_g5_render_target_t *source) { + target->impl.depthImage = source->impl.depthImage; + target->impl.depthMemory = source->impl.depthMemory; + target->impl.depthView = source->impl.depthView; + target->impl.depthBufferBits = source->impl.depthBufferBits; + + // vkDestroyFramebuffer(vk_ctx.device, target->impl.framebuffer, nullptr); + + { + VkImageView attachments[2]; + attachments[0] = target->impl.sourceView; + + if (target->impl.depthBufferBits > 0) { + attachments[1] = target->impl.depthView; + } + + VkFramebufferCreateInfo fbufCreateInfo = {0}; + fbufCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbufCreateInfo.pNext = NULL; + if (target->impl.depthBufferBits > 0) { + fbufCreateInfo.renderPass = vk_ctx.windows[vk_ctx.current_window].rendertarget_render_pass_with_depth; + } + else { + fbufCreateInfo.renderPass = vk_ctx.windows[vk_ctx.current_window].rendertarget_render_pass; + } + fbufCreateInfo.attachmentCount = target->impl.depthBufferBits > 0 ? 2 : 1; + fbufCreateInfo.pAttachments = attachments; + fbufCreateInfo.width = target->width; + fbufCreateInfo.height = target->height; + fbufCreateInfo.layers = 1; + + VkResult err = vkCreateFramebuffer(vk_ctx.device, &fbufCreateInfo, NULL, &target->impl.framebuffer); + assert(!err); + } +} diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/rendertarget.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/rendertarget.h new file mode 100644 index 000000000..c55115e70 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/rendertarget.h @@ -0,0 +1,25 @@ +#pragma once + +#include "MiniVulkan.h" + +typedef struct { + VkImage sourceImage; + VkDeviceMemory sourceMemory; + VkImageView sourceView; + + VkImage depthImage; + VkDeviceMemory depthMemory; + VkImageView depthView; + int depthBufferBits; + + VkFramebuffer framebuffer; + + VkFormat format; + + VkBuffer readbackBuffer; + VkDeviceMemory readbackMemory; + bool readbackBufferCreated; + + int stage; + int stage_depth; +} RenderTarget5Impl; diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/sampler.c.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/sampler.c.h new file mode 100644 index 000000000..a09f1f717 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/sampler.c.h @@ -0,0 +1,78 @@ +#include + +static VkCompareOp convert_compare_mode(kinc_g5_compare_mode_t compare); + +static VkSamplerAddressMode convert_addressing(kinc_g5_texture_addressing_t mode) { + switch (mode) { + case KINC_G5_TEXTURE_ADDRESSING_REPEAT: + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + case KINC_G5_TEXTURE_ADDRESSING_BORDER: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + case KINC_G5_TEXTURE_ADDRESSING_CLAMP: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + case KINC_G5_TEXTURE_ADDRESSING_MIRROR: + return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; + default: + assert(false); + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + } +} + +static VkSamplerMipmapMode convert_mipmap_mode(kinc_g5_mipmap_filter_t filter) { + switch (filter) { + case KINC_G5_MIPMAP_FILTER_NONE: + case KINC_G5_MIPMAP_FILTER_POINT: + return VK_SAMPLER_MIPMAP_MODE_NEAREST; + case KINC_G5_MIPMAP_FILTER_LINEAR: + return VK_SAMPLER_MIPMAP_MODE_LINEAR; + default: + assert(false); + return VK_SAMPLER_MIPMAP_MODE_NEAREST; + } +} + +static VkFilter convert_texture_filter(kinc_g5_texture_filter_t filter) { + switch (filter) { + case KINC_G5_TEXTURE_FILTER_POINT: + return VK_FILTER_NEAREST; + case KINC_G5_TEXTURE_FILTER_LINEAR: + return VK_FILTER_LINEAR; + case KINC_G5_TEXTURE_FILTER_ANISOTROPIC: + return VK_FILTER_LINEAR; // ? + default: + assert(false); + return VK_FILTER_NEAREST; + } +} + +void kinc_g5_sampler_init(kinc_g5_sampler_t *sampler, const kinc_g5_sampler_options_t *options) { + VkSamplerCreateInfo info = {0}; + info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + info.pNext = NULL; + info.flags = 0; + + info.addressModeU = convert_addressing(options->u_addressing); + info.addressModeV = convert_addressing(options->v_addressing); + info.addressModeW = convert_addressing(options->w_addressing); + + info.mipmapMode = convert_mipmap_mode(options->mipmap_filter); + + info.magFilter = convert_texture_filter(options->magnification_filter); + info.minFilter = convert_texture_filter(options->minification_filter); + + info.compareEnable = options->is_comparison; + info.compareOp = convert_compare_mode(options->compare_mode); + + info.anisotropyEnable = + (options->magnification_filter == KINC_G5_TEXTURE_FILTER_ANISOTROPIC || options->minification_filter == KINC_G5_TEXTURE_FILTER_ANISOTROPIC); + info.maxAnisotropy = options->max_anisotropy; + + info.maxLod = options->lod_max_clamp; + info.minLod = options->lod_min_clamp; + + vkCreateSampler(vk_ctx.device, &info, NULL, &sampler->impl.sampler); +} + +void kinc_g5_sampler_destroy(kinc_g5_sampler_t *sampler) { + vkDestroySampler(vk_ctx.device, sampler->impl.sampler, NULL); +} diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/sampler.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/sampler.h new file mode 100644 index 000000000..1ffcb2e6b --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/sampler.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +typedef struct kinc_g5_sampler_impl { + VkSampler sampler; +} kinc_g5_sampler_impl_t; diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/shader.c.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/shader.c.h new file mode 100644 index 000000000..fce87a430 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/shader.c.h @@ -0,0 +1,16 @@ +#include + +void kinc_g5_shader_init(kinc_g5_shader_t *shader, const void *source, size_t length, kinc_g5_shader_type_t type) { + shader->impl.length = (int)length; + shader->impl.id = 0; + shader->impl.source = (char *)malloc(length + 1); + for (int i = 0; i < length; ++i) { + shader->impl.source[i] = ((char *)source)[i]; + } + shader->impl.source[length] = 0; +} + +void kinc_g5_shader_destroy(kinc_g5_shader_t *shader) { + free(shader->impl.source); + shader->impl.source = NULL; +} diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/shader.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/shader.h new file mode 100644 index 000000000..0e952dac9 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/shader.h @@ -0,0 +1,7 @@ +#pragma once + +typedef struct { + unsigned id; + char *source; + int length; +} Shader5Impl; diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/texture.c.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/texture.c.h new file mode 100644 index 000000000..6ebd9535e --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/texture.c.h @@ -0,0 +1,420 @@ +#include "vulkan.h" + +#include +#include +#include + +bool use_staging_buffer = false; + +bool memory_type_from_properties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex); +void set_image_layout(VkImage image, VkImageAspectFlags aspectMask, VkImageLayout old_image_layout, VkImageLayout new_image_layout); + +static void prepare_texture_image(uint8_t *tex_colors, uint32_t tex_width, uint32_t tex_height, struct texture_object *tex_obj, VkImageTiling tiling, + VkImageUsageFlags usage, VkFlags required_props, VkDeviceSize *deviceSize, VkFormat tex_format) { + VkResult err; + bool pass; + + tex_obj->tex_width = tex_width; + tex_obj->tex_height = tex_height; + + VkImageCreateInfo image_create_info = {0}; + image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_create_info.pNext = NULL; + image_create_info.imageType = VK_IMAGE_TYPE_2D; + image_create_info.format = tex_format; + image_create_info.extent.width = tex_width; + image_create_info.extent.height = tex_height; + image_create_info.extent.depth = 1; + image_create_info.mipLevels = 1; + image_create_info.arrayLayers = 1; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_create_info.tiling = tiling; + image_create_info.usage = usage; + image_create_info.flags = 0; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + + VkMemoryAllocateInfo mem_alloc = {0}; + mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + mem_alloc.pNext = NULL; + mem_alloc.allocationSize = 0; + mem_alloc.memoryTypeIndex = 0; + + VkMemoryRequirements mem_reqs; + + err = vkCreateImage(vk_ctx.device, &image_create_info, NULL, &tex_obj->image); + assert(!err); + + vkGetImageMemoryRequirements(vk_ctx.device, tex_obj->image, &mem_reqs); + + *deviceSize = mem_alloc.allocationSize = mem_reqs.size; + pass = memory_type_from_properties(mem_reqs.memoryTypeBits, required_props, &mem_alloc.memoryTypeIndex); + assert(pass); + + // allocate memory + err = vkAllocateMemory(vk_ctx.device, &mem_alloc, NULL, &tex_obj->mem); + assert(!err); + + // bind memory + err = vkBindImageMemory(vk_ctx.device, tex_obj->image, tex_obj->mem, 0); + assert(!err); + + if (required_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT && tex_colors != NULL) { + VkImageSubresource subres = {0}; + subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subres.mipLevel = 0; + subres.arrayLayer = 0; + + VkSubresourceLayout layout; + uint8_t *data; + + vkGetImageSubresourceLayout(vk_ctx.device, tex_obj->image, &subres, &layout); + + err = vkMapMemory(vk_ctx.device, tex_obj->mem, 0, mem_alloc.allocationSize, 0, (void **)&data); + assert(!err); + + if (tex_format == VK_FORMAT_R8_UNORM) { + for (uint32_t y = 0; y < tex_height; y++) { + for (uint32_t x = 0; x < tex_width; x++) { + data[y * layout.rowPitch + x] = tex_colors[y * tex_width + x]; + } + } + } + else if (tex_format == VK_FORMAT_R32_SFLOAT) { + uint32_t *data32 = (uint32_t *)data; + uint32_t *tex_colors32 = (uint32_t *)tex_colors; + for (uint32_t y = 0; y < tex_height; y++) { + for (uint32_t x = 0; x < tex_width; x++) { + data32[y * (layout.rowPitch / 4) + x] = tex_colors32[y * tex_width + x]; + } + } + } + else if (tex_format == VK_FORMAT_R16_SFLOAT) { + uint16_t *data16 = (uint16_t *)data; + uint16_t *tex_colors16 = (uint16_t *)tex_colors; + for (uint32_t y = 0; y < tex_height; y++) { + for (uint32_t x = 0; x < tex_width; x++) { + data16[y * (layout.rowPitch / 4) + x] = tex_colors16[y * tex_width + x]; + } + } + } + else if (tex_format == VK_FORMAT_R32G32B32A32_SFLOAT) { + uint32_t *data32 = (uint32_t *)data; + uint32_t *tex_colors32 = (uint32_t *)tex_colors; + for (uint32_t y = 0; y < tex_height; y++) { + for (uint32_t x = 0; x < tex_width; x++) { + data32[y * (layout.rowPitch / 4) + x * 4 + 0] = tex_colors32[y * tex_width * 4 + x * 4 + 0]; + data32[y * (layout.rowPitch / 4) + x * 4 + 1] = tex_colors32[y * tex_width * 4 + x * 4 + 1]; + data32[y * (layout.rowPitch / 4) + x * 4 + 2] = tex_colors32[y * tex_width * 4 + x * 4 + 2]; + data32[y * (layout.rowPitch / 4) + x * 4 + 3] = tex_colors32[y * tex_width * 4 + x * 4 + 3]; + } + } + } + else if (tex_format == VK_FORMAT_R16G16B16A16_SFLOAT) { + uint16_t *data16 = (uint16_t *)data; + uint16_t *tex_colors16 = (uint16_t *)tex_colors; + for (uint32_t y = 0; y < tex_height; y++) { + for (uint32_t x = 0; x < tex_width; x++) { + data16[y * (layout.rowPitch / 4) + x * 4 + 0] = tex_colors16[y * tex_width * 4 + x * 4 + 0]; + data16[y * (layout.rowPitch / 4) + x * 4 + 1] = tex_colors16[y * tex_width * 4 + x * 4 + 1]; + data16[y * (layout.rowPitch / 4) + x * 4 + 2] = tex_colors16[y * tex_width * 4 + x * 4 + 2]; + data16[y * (layout.rowPitch / 4) + x * 4 + 3] = tex_colors16[y * tex_width * 4 + x * 4 + 3]; + } + } + } + else if (tex_format == VK_FORMAT_B8G8R8A8_UNORM) { + for (uint32_t y = 0; y < tex_height; y++) { + // uint32_t *row = (uint32_t *)((char *)data + layout.rowPitch * y); + for (uint32_t x = 0; x < tex_width; x++) { + data[y * layout.rowPitch + x * 4 + 0] = tex_colors[y * tex_width * 4 + x * 4 + 2]; + data[y * layout.rowPitch + x * 4 + 1] = tex_colors[y * tex_width * 4 + x * 4 + 1]; + data[y * layout.rowPitch + x * 4 + 2] = tex_colors[y * tex_width * 4 + x * 4 + 0]; + data[y * layout.rowPitch + x * 4 + 3] = tex_colors[y * tex_width * 4 + x * 4 + 3]; + // row[x] = tex_colors[(x & 1) ^ (y & 1)]; + } + } + } + else { + for (uint32_t y = 0; y < tex_height; y++) { + // uint32_t *row = (uint32_t *)((char *)data + layout.rowPitch * y); + for (uint32_t x = 0; x < tex_width; x++) { + data[y * layout.rowPitch + x * 4 + 0] = tex_colors[y * tex_width * 4 + x * 4 + 0]; + data[y * layout.rowPitch + x * 4 + 1] = tex_colors[y * tex_width * 4 + x * 4 + 1]; + data[y * layout.rowPitch + x * 4 + 2] = tex_colors[y * tex_width * 4 + x * 4 + 2]; + data[y * layout.rowPitch + x * 4 + 3] = tex_colors[y * tex_width * 4 + x * 4 + 3]; + // row[x] = tex_colors[(x & 1) ^ (y & 1)]; + } + } + } + + vkUnmapMemory(vk_ctx.device, tex_obj->mem); + } + + if (usage & VK_IMAGE_USAGE_STORAGE_BIT) { + tex_obj->imageLayout = VK_IMAGE_LAYOUT_GENERAL; + } + else { + tex_obj->imageLayout = VK_IMAGE_LAYOUT_GENERAL; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + set_image_layout(tex_obj->image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, tex_obj->imageLayout); + // setting the image layout does not reference the actual memory so no need to add a mem ref +} + +static void destroy_texture_image(struct texture_object *tex_obj) { + // clean up staging resources + vkDestroyImage(vk_ctx.device, tex_obj->image, NULL); + vkFreeMemory(vk_ctx.device, tex_obj->mem, NULL); +} + +static VkFormat convert_image_format(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA128: + return VK_FORMAT_R32G32B32A32_SFLOAT; + case KINC_IMAGE_FORMAT_RGBA64: + return VK_FORMAT_R16G16B16A16_SFLOAT; + case KINC_IMAGE_FORMAT_RGB24: + return VK_FORMAT_B8G8R8A8_UNORM; + case KINC_IMAGE_FORMAT_A32: + return VK_FORMAT_R32_SFLOAT; + case KINC_IMAGE_FORMAT_A16: + return VK_FORMAT_R16_SFLOAT; + case KINC_IMAGE_FORMAT_GREY8: + return VK_FORMAT_R8_UNORM; + case KINC_IMAGE_FORMAT_BGRA32: + return VK_FORMAT_B8G8R8A8_UNORM; + case KINC_IMAGE_FORMAT_RGBA32: + return VK_FORMAT_R8G8B8A8_UNORM; + default: + return VK_FORMAT_B8G8R8A8_UNORM; + } +} + +static int format_byte_size(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA128: + return 16; + case KINC_IMAGE_FORMAT_RGBA64: + return 8; + case KINC_IMAGE_FORMAT_RGB24: + return 4; + case KINC_IMAGE_FORMAT_A32: + return 4; + case KINC_IMAGE_FORMAT_A16: + return 2; + case KINC_IMAGE_FORMAT_GREY8: + return 1; + case KINC_IMAGE_FORMAT_BGRA32: + case KINC_IMAGE_FORMAT_RGBA32: + return 4; + default: + return 4; + } +} + +static void update_stride(kinc_g5_texture_t *texture) { + VkImageSubresource subres = {0}; + subres.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subres.mipLevel = 0; + subres.arrayLayer = 0; + + VkSubresourceLayout layout; + vkGetImageSubresourceLayout(vk_ctx.device, texture->impl.texture.image, &subres, &layout); + + texture->impl.stride = (int)layout.rowPitch; +} + +void kinc_g5_texture_init_from_image(kinc_g5_texture_t *texture, kinc_image_t *image) { + texture->texWidth = image->width; + texture->texHeight = image->height; + + const VkFormat tex_format = convert_image_format(image->format); + VkFormatProperties props; + VkResult err; + + vkGetPhysicalDeviceFormatProperties(vk_ctx.gpu, tex_format, &props); + + if ((props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && !use_staging_buffer) { + // Device can texture using linear textures + prepare_texture_image((uint8_t *)image->data, (uint32_t)image->width, (uint32_t)image->height, &texture->impl.texture, VK_IMAGE_TILING_LINEAR, + VK_IMAGE_USAGE_SAMPLED_BIT /*| VK_IMAGE_USAGE_STORAGE_BIT*/, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &texture->impl.deviceSize, + tex_format); + + flush_init_cmd(); + } + else if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) { + // Must use staging buffer to copy linear texture to optimized + struct texture_object staging_texture; + + memset(&staging_texture, 0, sizeof(staging_texture)); + prepare_texture_image((uint8_t *)image->data, (uint32_t)image->width, (uint32_t)image->height, &staging_texture, VK_IMAGE_TILING_LINEAR, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &texture->impl.deviceSize, tex_format); + prepare_texture_image((uint8_t *)image->data, (uint32_t)image->width, (uint32_t)image->height, &texture->impl.texture, VK_IMAGE_TILING_OPTIMAL, + (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT /*| VK_IMAGE_USAGE_STORAGE_BIT*/), + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &texture->impl.deviceSize, tex_format); + set_image_layout(staging_texture.image, VK_IMAGE_ASPECT_COLOR_BIT, staging_texture.imageLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + set_image_layout(texture->impl.texture.image, VK_IMAGE_ASPECT_COLOR_BIT, texture->impl.texture.imageLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + VkImageCopy copy_region = {0}; + copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_region.srcSubresource.mipLevel = 0; + copy_region.srcSubresource.baseArrayLayer = 0; + copy_region.srcSubresource.layerCount = 1; + copy_region.srcOffset.x = copy_region.srcOffset.y = copy_region.srcOffset.z = 0; + copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_region.dstSubresource.mipLevel = 0; + copy_region.dstSubresource.baseArrayLayer = 0; + copy_region.dstSubresource.layerCount = 1; + copy_region.dstOffset.x = copy_region.dstOffset.y = copy_region.dstOffset.z = 0; + copy_region.extent.width = (uint32_t)staging_texture.tex_width; + copy_region.extent.height = (uint32_t)staging_texture.tex_height; + copy_region.extent.depth = 1; + + vkCmdCopyImage(vk_ctx.setup_cmd, staging_texture.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture->impl.texture.image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); + + set_image_layout(texture->impl.texture.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, texture->impl.texture.imageLayout); + + flush_init_cmd(); + + destroy_texture_image(&staging_texture); + } + else { + assert(!"No support for B8G8R8A8_UNORM as texture image format"); + } + + update_stride(texture); + + VkImageViewCreateInfo view = {0}; + view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view.pNext = NULL; + view.image = VK_NULL_HANDLE; + view.viewType = VK_IMAGE_VIEW_TYPE_2D; + view.format = tex_format; + view.components.r = VK_COMPONENT_SWIZZLE_R; + view.components.g = VK_COMPONENT_SWIZZLE_G; + view.components.b = VK_COMPONENT_SWIZZLE_B; + view.components.a = VK_COMPONENT_SWIZZLE_A; + view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view.subresourceRange.baseMipLevel = 0; + view.subresourceRange.levelCount = 1; + view.subresourceRange.baseArrayLayer = 0; + view.subresourceRange.layerCount = 1; + view.flags = 0; + + view.image = texture->impl.texture.image; + err = vkCreateImageView(vk_ctx.device, &view, NULL, &texture->impl.texture.view); + assert(!err); +} + +void kinc_g5_texture_init(kinc_g5_texture_t *texture, int width, int height, kinc_image_format_t format) { + texture->texWidth = width; + texture->texHeight = height; + + const VkFormat tex_format = convert_image_format(format); + VkFormatProperties props; + VkResult err; + + vkGetPhysicalDeviceFormatProperties(vk_ctx.gpu, tex_format, &props); + + // Device can texture using linear textures + prepare_texture_image(NULL, (uint32_t)width, (uint32_t)height, &texture->impl.texture, VK_IMAGE_TILING_LINEAR, + VK_IMAGE_USAGE_SAMPLED_BIT /*| VK_IMAGE_USAGE_STORAGE_BIT*/, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &texture->impl.deviceSize, + tex_format); + + flush_init_cmd(); + + update_stride(texture); + + VkImageViewCreateInfo view = {0}; + view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view.pNext = NULL; + view.image = VK_NULL_HANDLE; + view.viewType = VK_IMAGE_VIEW_TYPE_2D; + view.format = tex_format; + view.components.r = VK_COMPONENT_SWIZZLE_R; + view.components.g = VK_COMPONENT_SWIZZLE_G; + view.components.b = VK_COMPONENT_SWIZZLE_B; + view.components.a = VK_COMPONENT_SWIZZLE_A; + view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view.subresourceRange.baseMipLevel = 0; + view.subresourceRange.levelCount = 1; + view.subresourceRange.baseArrayLayer = 0; + view.subresourceRange.layerCount = 1; + view.flags = 0; + + view.image = texture->impl.texture.image; + err = vkCreateImageView(vk_ctx.device, &view, NULL, &texture->impl.texture.view); + assert(!err); +} + +void kinc_g5_texture_init3d(kinc_g5_texture_t *texture, int width, int height, int depth, kinc_image_format_t format) {} + +void kinc_g5_texture_init_non_sampled_access(kinc_g5_texture_t *texture, int width, int height, kinc_image_format_t format) { + texture->texWidth = width; + texture->texHeight = height; + + const VkFormat tex_format = convert_image_format(format); + VkFormatProperties props; + VkResult err; + + vkGetPhysicalDeviceFormatProperties(vk_ctx.gpu, tex_format, &props); + + // Device can texture using linear textures + prepare_texture_image(NULL, (uint32_t)width, (uint32_t)height, &texture->impl.texture, VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &texture->impl.deviceSize, + tex_format); + + flush_init_cmd(); + + update_stride(texture); + + VkImageViewCreateInfo view = {0}; + view.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + view.pNext = NULL; + view.image = VK_NULL_HANDLE; + view.viewType = VK_IMAGE_VIEW_TYPE_2D; + view.format = tex_format; + view.components.r = VK_COMPONENT_SWIZZLE_R; + view.components.g = VK_COMPONENT_SWIZZLE_G; + view.components.b = VK_COMPONENT_SWIZZLE_B; + view.components.a = VK_COMPONENT_SWIZZLE_A; + view.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + view.subresourceRange.baseMipLevel = 0; + view.subresourceRange.levelCount = 1; + view.subresourceRange.baseArrayLayer = 0; + view.subresourceRange.layerCount = 1; + view.flags = 0; + + view.image = texture->impl.texture.image; + err = vkCreateImageView(vk_ctx.device, &view, NULL, &texture->impl.texture.view); + assert(!err); +} + +void kinc_g5_texture_destroy(kinc_g5_texture_t *texture) { + vkDestroyImageView(vk_ctx.device, texture->impl.texture.view, NULL); + destroy_texture_image(&texture->impl.texture); +} + +void kinc_g5_internal_texture_set(kinc_g5_texture_t *texture, int unit) {} + +int kinc_g5_texture_stride(kinc_g5_texture_t *texture) { + return texture->impl.stride; +} + +uint8_t *kinc_g5_texture_lock(kinc_g5_texture_t *texture) { + void *data; + + VkResult err = vkMapMemory(vk_ctx.device, texture->impl.texture.mem, 0, texture->impl.deviceSize, 0, &data); + assert(!err); + + return (uint8_t *)data; +} + +void kinc_g5_texture_unlock(kinc_g5_texture_t *texture) { + vkUnmapMemory(vk_ctx.device, texture->impl.texture.mem); +} + +void kinc_g5_texture_clear(kinc_g5_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color) {} + +void kinc_g5_texture_generate_mipmaps(kinc_g5_texture_t *texture, int levels) {} + +void kinc_g5_texture_set_mipmap(kinc_g5_texture_t *texture, kinc_image_t *mipmap, int level) {} diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/texture.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/texture.h new file mode 100644 index 000000000..bbd6a0560 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/texture.h @@ -0,0 +1,20 @@ +#pragma once + +#include "MiniVulkan.h" + +struct texture_object { + VkImage image; + VkImageLayout imageLayout; + + VkDeviceMemory mem; + VkImageView view; + int32_t tex_width, tex_height; +}; + +typedef struct { + struct texture_object texture; + VkDeviceSize deviceSize; + + uint8_t *conversionBuffer; + int stride; +} Texture5Impl; diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/vertexbuffer.c.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/vertexbuffer.c.h new file mode 100644 index 000000000..cccec9cc9 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/vertexbuffer.c.h @@ -0,0 +1,119 @@ +#include "vulkan.h" + +#include "shader.h" +#include "vertexbuffer.h" + +#include +#include + +bool memory_type_from_properties(uint32_t typeBits, VkFlags requirements_mask, uint32_t *typeIndex); + +kinc_g5_vertex_buffer_t *currentVertexBuffer = NULL; +extern kinc_g5_index_buffer_t *currentIndexBuffer; + +void kinc_g5_vertex_buffer_init(kinc_g5_vertex_buffer_t *buffer, int vertexCount, kinc_g5_vertex_structure_t *structure, bool gpuMemory, + int instanceDataStepRate) { + buffer->impl.myCount = vertexCount; + buffer->impl.instanceDataStepRate = instanceDataStepRate; + buffer->impl.myStride = 0; + for (int i = 0; i < structure->size; ++i) { + kinc_g5_vertex_element_t element = structure->elements[i]; + buffer->impl.myStride += kinc_g4_vertex_data_size(element.data); + } + buffer->impl.structure = *structure; + + VkBufferCreateInfo buf_info = {0}; + buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buf_info.pNext = NULL; + buf_info.size = vertexCount * buffer->impl.myStride; + buf_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; +#ifdef KINC_VKRT + buf_info.usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + buf_info.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + buf_info.usage |= VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR; +#endif + buf_info.flags = 0; + + memset(&buffer->impl.mem_alloc, 0, sizeof(VkMemoryAllocateInfo)); + buffer->impl.mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + buffer->impl.mem_alloc.pNext = NULL; + buffer->impl.mem_alloc.allocationSize = 0; + buffer->impl.mem_alloc.memoryTypeIndex = 0; + + VkMemoryRequirements mem_reqs = {0}; + VkResult err; + bool pass; + + memset(&buffer->impl.vertices, 0, sizeof(buffer->impl.vertices)); + + err = vkCreateBuffer(vk_ctx.device, &buf_info, NULL, &buffer->impl.vertices.buf); + assert(!err); + + vkGetBufferMemoryRequirements(vk_ctx.device, buffer->impl.vertices.buf, &mem_reqs); + assert(!err); + + buffer->impl.mem_alloc.allocationSize = mem_reqs.size; + pass = memory_type_from_properties(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &buffer->impl.mem_alloc.memoryTypeIndex); + assert(pass); + +#ifdef KINC_VKRT + VkMemoryAllocateFlagsInfo memory_allocate_flags_info = {0}; + memory_allocate_flags_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO; + memory_allocate_flags_info.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + buffer->impl.mem_alloc.pNext = &memory_allocate_flags_info; +#endif + + err = vkAllocateMemory(vk_ctx.device, &buffer->impl.mem_alloc, NULL, &buffer->impl.vertices.mem); + assert(!err); + + err = vkBindBufferMemory(vk_ctx.device, buffer->impl.vertices.buf, buffer->impl.vertices.mem, 0); + assert(!err); +} + +static void unset_vertex_buffer(kinc_g5_vertex_buffer_t *buffer) { + if (currentVertexBuffer == buffer) { + currentVertexBuffer = NULL; + } +} + +void kinc_g5_vertex_buffer_destroy(kinc_g5_vertex_buffer_t *buffer) { + unset_vertex_buffer(buffer); + vkFreeMemory(vk_ctx.device, buffer->impl.vertices.mem, NULL); + vkDestroyBuffer(vk_ctx.device, buffer->impl.vertices.buf, NULL); +} + +float *kinc_g5_vertex_buffer_lock_all(kinc_g5_vertex_buffer_t *buffer) { + return kinc_g5_vertex_buffer_lock(buffer, 0, buffer->impl.myCount); +} + +float *kinc_g5_vertex_buffer_lock(kinc_g5_vertex_buffer_t *buffer, int start, int count) { + VkResult err = + vkMapMemory(vk_ctx.device, buffer->impl.vertices.mem, start * buffer->impl.myStride, count * buffer->impl.myStride, 0, (void **)&buffer->impl.data); + assert(!err); + return buffer->impl.data; +} + +void kinc_g5_vertex_buffer_unlock_all(kinc_g5_vertex_buffer_t *buffer) { + vkUnmapMemory(vk_ctx.device, buffer->impl.vertices.mem); +} + +void kinc_g5_vertex_buffer_unlock(kinc_g5_vertex_buffer_t *buffer, int count) { + vkUnmapMemory(vk_ctx.device, buffer->impl.vertices.mem); +} + +static int setVertexAttributes(int offset) { + return 0; +} + +int kinc_g5_internal_vertex_buffer_set(kinc_g5_vertex_buffer_t *buffer, int offset) { + int offsetoffset = setVertexAttributes(offset); + return offsetoffset; +} + +int kinc_g5_vertex_buffer_count(kinc_g5_vertex_buffer_t *buffer) { + return buffer->impl.myCount; +} + +int kinc_g5_vertex_buffer_stride(kinc_g5_vertex_buffer_t *buffer) { + return buffer->impl.myStride; +} diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/vertexbuffer.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/vertexbuffer.h new file mode 100644 index 000000000..db35a0837 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/vertexbuffer.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "MiniVulkan.h" + +struct Vertices { + VkBuffer buf; + VkDeviceMemory mem; +}; + +typedef struct { + float *data; + int myCount; + int myStride; + unsigned bufferId; + kinc_g5_vertex_structure_t structure; + VkMemoryAllocateInfo mem_alloc; + int instanceDataStepRate; + struct Vertices vertices; +} VertexBuffer5Impl; diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/vulkan.h b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/vulkan.h new file mode 100644 index 000000000..0bff54781 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/vulkan.h @@ -0,0 +1,94 @@ +#pragma once + +#include +#include +#include + +struct vk_funs { + PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR; + PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR; + PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR; + PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR; + PFN_vkDestroySurfaceKHR fpDestroySurfaceKHR; + + PFN_vkCreateDebugUtilsMessengerEXT fpCreateDebugUtilsMessengerEXT; + PFN_vkDestroyDebugUtilsMessengerEXT fpDestroyDebugUtilsMessengerEXT; + + PFN_vkQueuePresentKHR fpQueuePresentKHR; + PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR; +}; + +struct vk_depth { + VkImage image; + VkImageView view; + VkDeviceMemory memory; +}; + +struct vk_window { + int width; + int height; + + bool resized; + bool surface_destroyed; + + int depth_bits; + int stencil_bits; + + bool vsynced; + + uint32_t current_image; + + VkSurfaceKHR surface; + VkSurfaceFormatKHR format; + + VkSwapchainKHR swapchain; + uint32_t image_count; + VkImage *images; + VkImageView *views; + VkFramebuffer *framebuffers; + + VkRenderPass framebuffer_render_pass; + VkRenderPass rendertarget_render_pass; + VkRenderPass rendertarget_render_pass_with_depth; + + struct vk_depth depth; +}; + +#define MAXIMUM_WINDOWS 16 + +struct vk_context { + VkInstance instance; + VkPhysicalDevice gpu; + VkDevice device; + VkPhysicalDeviceMemoryProperties memory_properties; + + VkCommandBuffer setup_cmd; + VkCommandPool cmd_pool; + VkQueue queue; + + struct vk_window windows[MAXIMUM_WINDOWS]; + + // buffer hack + VkBuffer *vertex_uniform_buffer; + VkBuffer *fragment_uniform_buffer; + VkBuffer *compute_uniform_buffer; + + int current_window; + +#ifdef VALIDATE + bool validation_found; + VkDebugUtilsMessengerEXT debug_messenger; +#endif +}; + +extern struct vk_funs vk; +extern struct vk_context vk_ctx; + +extern void flush_init_cmd(void); +extern void reuse_descriptor_sets(void); +extern void reuse_compute_descriptor_sets(void); + +#include diff --git a/armorcore/sources/backends/vulkan/kinc/backend/graphics5/vulkanunit.c b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/vulkanunit.c new file mode 100644 index 000000000..762799ad6 --- /dev/null +++ b/armorcore/sources/backends/vulkan/kinc/backend/graphics5/vulkanunit.c @@ -0,0 +1,89 @@ +#ifdef KINC_WINDOWS + +// Windows 7 +#define WINVER 0x0601 +#define _WIN32_WINNT 0x0601 + +#define NOATOM +#define NOCLIPBOARD +#define NOCOLOR +#define NOCOMM +#define NOCTLMGR +#define NODEFERWINDOWPOS +#define NODRAWTEXT +#define NOGDI +#define NOGDICAPMASKS +#define NOHELP +#define NOICONS +#define NOKANJI +#define NOKEYSTATES +// #define NOMB +#define NOMCX +#define NOMEMMGR +#define NOMENUS +#define NOMETAFILE +#define NOMINMAX +#define NOMSG +#define NONLS +#define NOOPENFILE +#define NOPROFILER +#define NORASTEROPS +#define NOSCROLL +#define NOSERVICE +#define NOSHOWWINDOW +#define NOSOUND +#define NOSYSCOMMANDS +#define NOSYSMETRICS +#define NOTEXTMETRIC +// #define NOUSER +#define NOVIRTUALKEYCODES +#define NOWH +#define NOWINMESSAGES +#define NOWINOFFSETS +#define NOWINSTYLES +#define WIN32_LEAN_AND_MEAN + +// avoids a warning in the Windows headers +#define MICROSOFT_WINDOWS_WINBASE_H_DEFINE_INTERLOCKED_CPLUSPLUS_OVERLOADS 0 + +#endif + +#ifndef NDEBUG +#define VALIDATE +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "vulkan.h" + +static VkSemaphore framebuffer_available; +static VkSemaphore relay_semaphore; +static bool wait_for_relay = false; +static void command_list_should_wait_for_framebuffer(void); +static VkDescriptorSetLayout compute_descriptor_layout; + +#include "ShaderHash.c.h" +#include "Vulkan.c.h" +#include "commandlist.c.h" +#include "compute.c.h" +#include "constantbuffer.c.h" +#include "indexbuffer.c.h" +#include "pipeline.c.h" +#include "raytrace.c.h" +#include "rendertarget.c.h" +#include "sampler.c.h" +#include "shader.c.h" +#include "texture.c.h" +#include "vertexbuffer.c.h" \ No newline at end of file diff --git a/armorcore/sources/backends/wasapi/kinc/backend/wasapi.c b/armorcore/sources/backends/wasapi/kinc/backend/wasapi.c new file mode 100644 index 000000000..f730ac361 --- /dev/null +++ b/armorcore/sources/backends/wasapi/kinc/backend/wasapi.c @@ -0,0 +1,308 @@ +#include + +#include + +#include +#include + +// Windows 7 +#define WINVER 0x0601 +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0601 + +#define NOATOM +#define NOCLIPBOARD +#define NOCOLOR +#define NOCOMM +#define NOCTLMGR +#define NODEFERWINDOWPOS +#define NODRAWTEXT +// #define NOGDI +#define NOGDICAPMASKS +#define NOHELP +#define NOICONS +#define NOKANJI +#define NOKEYSTATES +#define NOMB +#define NOMCX +#define NOMEMMGR +#define NOMENUS +#define NOMETAFILE +#define NOMINMAX +// #define NOMSG +#define NONLS +#define NOOPENFILE +#define NOPROFILER +#define NORASTEROPS +#define NOSCROLL +#define NOSERVICE +#define NOSHOWWINDOW +#define NOSOUND +#define NOSYSCOMMANDS +#define NOSYSMETRICS +#define NOTEXTMETRIC +// #define NOUSER +#define NOVIRTUALKEYCODES +#define NOWH +#define NOWINMESSAGES +#define NOWINOFFSETS +#define NOWINSTYLES +#define WIN32_LEAN_AND_MEAN + +#include + +#include +#include + +#ifndef __MINGW32__ +// MIDL_INTERFACE("1CB9AD4C-DBFA-4c32-B178-C2F568A703B2") +DEFINE_GUID(IID_IAudioClient, 0x1CB9AD4C, 0xDBFA, 0x4c32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2); +// MIDL_INTERFACE("F294ACFC-3146-4483-A7BF-ADDCA7C260E2") +DEFINE_GUID(IID_IAudioRenderClient, 0xF294ACFC, 0x3146, 0x4483, 0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2); +// MIDL_INTERFACE("A95664D2-9614-4F35-A746-DE8DB63617E6") +DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6); +// DECLSPEC_UUID("BCDE0395-E52F-467C-8E3D-C4579291692E") +DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E); +#endif + +// based on the implementation in soloud and Microsoft sample code +static kinc_a2_buffer_t a2_buffer; + +static IMMDeviceEnumerator *deviceEnumerator; +static IMMDevice *device; +static IAudioClient *audioClient = NULL; +static IAudioRenderClient *renderClient = NULL; +static HANDLE bufferEndEvent = 0; +static HANDLE audioProcessingDoneEvent; +static UINT32 bufferFrames; +static WAVEFORMATEX requestedFormat; +static WAVEFORMATEX *closestFormat; +static WAVEFORMATEX *format; +static uint32_t samples_per_second = 44100; + +uint32_t kinc_a2_samples_per_second(void) { + return samples_per_second; +} + +static bool initDefaultDevice() { + if (renderClient != NULL) { + renderClient->lpVtbl->Release(renderClient); + renderClient = NULL; + } + + if (audioClient != NULL) { + audioClient->lpVtbl->Release(audioClient); + audioClient = NULL; + } + + if (bufferEndEvent != 0) { + CloseHandle(bufferEndEvent); + bufferEndEvent = 0; + } + + kinc_log(KINC_LOG_LEVEL_INFO, "Initializing a new default audio device."); + + HRESULT hr = deviceEnumerator->lpVtbl->GetDefaultAudioEndpoint(deviceEnumerator, eRender, eConsole, &device); + if (hr == S_OK) { + hr = device->lpVtbl->Activate(device, &IID_IAudioClient, CLSCTX_ALL, 0, (void **)&audioClient); + } + + if (hr == S_OK) { + const int sampleRate = 48000; + + format = &requestedFormat; + memset(&requestedFormat, 0, sizeof(WAVEFORMATEX)); + requestedFormat.nChannels = 2; + requestedFormat.nSamplesPerSec = sampleRate; + requestedFormat.wFormatTag = WAVE_FORMAT_PCM; + requestedFormat.wBitsPerSample = sizeof(short) * 8; + requestedFormat.nBlockAlign = (requestedFormat.nChannels * requestedFormat.wBitsPerSample) / 8; + requestedFormat.nAvgBytesPerSec = requestedFormat.nSamplesPerSec * requestedFormat.nBlockAlign; + requestedFormat.cbSize = 0; + + HRESULT supported = audioClient->lpVtbl->IsFormatSupported(audioClient, AUDCLNT_SHAREMODE_SHARED, format, &closestFormat); + if (supported == S_FALSE) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Falling back to the system's preferred WASAPI mix format.", supported); + if (closestFormat != NULL) { + format = closestFormat; + } + else { + audioClient->lpVtbl->GetMixFormat(audioClient, &format); + } + } + HRESULT result = + audioClient->lpVtbl->Initialize(audioClient, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 40 * 1000 * 10, 0, format, 0); + if (result != S_OK) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Could not initialize WASAPI audio, going silent (error code 0x%x).", result); + return false; + } + + uint32_t old_samples_per_second = samples_per_second; + samples_per_second = format->nSamplesPerSec; + if (samples_per_second != old_samples_per_second) { + kinc_a2_internal_sample_rate_callback(); + } + a2_buffer.channel_count = 2; + + bufferFrames = 0; + kinc_microsoft_affirm(audioClient->lpVtbl->GetBufferSize(audioClient, &bufferFrames)); + kinc_microsoft_affirm(audioClient->lpVtbl->GetService(audioClient, &IID_IAudioRenderClient, (void **)&renderClient)); + + bufferEndEvent = CreateEvent(0, FALSE, FALSE, 0); + kinc_affirm(bufferEndEvent != 0); + + kinc_microsoft_affirm(audioClient->lpVtbl->SetEventHandle(audioClient, bufferEndEvent)); + + return true; + } + else { + kinc_log(KINC_LOG_LEVEL_WARNING, "Could not initialize WASAPI audio."); + return false; + } +} + +static void copyS16Sample(int16_t *left, int16_t *right) { + float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location]; + float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location]; + a2_buffer.read_location += 1; + if (a2_buffer.read_location >= a2_buffer.data_size) { + a2_buffer.read_location = 0; + } + *left = (int16_t)(left_value * 32767); + *right = (int16_t)(right_value * 32767); +} + +static void copyFloatSample(float *left, float *right) { + float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location]; + float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location]; + a2_buffer.read_location += 1; + if (a2_buffer.read_location >= a2_buffer.data_size) { + a2_buffer.read_location = 0; + } + *left = left_value; + *right = right_value; +} + +static void submitEmptyBuffer(unsigned frames) { + BYTE *buffer = NULL; + HRESULT result = renderClient->lpVtbl->GetBuffer(renderClient, frames, &buffer); + if (FAILED(result)) { + return; + } + + memset(buffer, 0, frames * format->nBlockAlign); + + result = renderClient->lpVtbl->ReleaseBuffer(renderClient, frames, 0); +} + +static void submitBuffer(unsigned frames) { + BYTE *buffer = NULL; + HRESULT result = renderClient->lpVtbl->GetBuffer(renderClient, frames, &buffer); + if (FAILED(result)) { + if (result == AUDCLNT_E_DEVICE_INVALIDATED) { + initDefaultDevice(); + submitEmptyBuffer(bufferFrames); + audioClient->lpVtbl->Start(audioClient); + } + return; + } + + if (kinc_a2_internal_callback(&a2_buffer, frames)) { + if (format->wFormatTag == WAVE_FORMAT_PCM) { + for (UINT32 i = 0; i < frames; ++i) { + copyS16Sample((int16_t *)&buffer[i * format->nBlockAlign], (int16_t *)&buffer[i * format->nBlockAlign + 2]); + } + } + else { + for (UINT32 i = 0; i < frames; ++i) { + copyFloatSample((float *)&buffer[i * format->nBlockAlign], (float *)&buffer[i * format->nBlockAlign + 4]); + } + } + } + else { + memset(buffer, 0, frames * format->nBlockAlign); + } + + result = renderClient->lpVtbl->ReleaseBuffer(renderClient, frames, 0); + if (FAILED(result)) { + if (result == AUDCLNT_E_DEVICE_INVALIDATED) { + initDefaultDevice(); + submitEmptyBuffer(bufferFrames); + audioClient->lpVtbl->Start(audioClient); + } + } +} + +static DWORD WINAPI audioThread(LPVOID ignored) { + submitBuffer(bufferFrames); + audioClient->lpVtbl->Start(audioClient); + while (WAIT_OBJECT_0 != WaitForSingleObject(audioProcessingDoneEvent, 0)) { + WaitForSingleObject(bufferEndEvent, INFINITE); + UINT32 padding = 0; + HRESULT result = audioClient->lpVtbl->GetCurrentPadding(audioClient, &padding); + if (FAILED(result)) { + if (result == AUDCLNT_E_DEVICE_INVALIDATED) { + initDefaultDevice(); + submitEmptyBuffer(bufferFrames); + audioClient->lpVtbl->Start(audioClient); + } + continue; + } + UINT32 frames = bufferFrames - padding; + submitBuffer(frames); + } + return 0; +} + +void kinc_windows_co_initialize(void); + +static bool initialized = false; + +void kinc_a2_init() { + if (initialized) { + return; + } + + kinc_a2_internal_init(); + initialized = true; + + a2_buffer.read_location = 0; + a2_buffer.write_location = 0; + a2_buffer.data_size = 128 * 1024; + a2_buffer.channel_count = 2; + a2_buffer.channels[0] = (float *)malloc(a2_buffer.data_size * sizeof(float)); + a2_buffer.channels[1] = (float *)malloc(a2_buffer.data_size * sizeof(float)); + + audioProcessingDoneEvent = CreateEvent(0, FALSE, FALSE, 0); + kinc_affirm(audioProcessingDoneEvent != 0); + + kinc_windows_co_initialize(); + kinc_microsoft_affirm(CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void **)&deviceEnumerator)); + + if (initDefaultDevice()) { + CreateThread(0, 65536, audioThread, NULL, 0, 0); + } +} + +void kinc_a2_update() {} + +#define SAFE_RELEASE(punk) \ + if ((punk) != NULL) { \ + (punk)->Release(); \ + (punk) = NULL; \ + } + +void kinc_a2_shutdown() { + // Wait for last data in buffer to play before stopping. + // Sleep((DWORD)(hnsActualDuration/REFTIMES_PER_MILLISEC/2)); + + // affirm(pAudioClient->Stop()); // Stop playing. + + // CoTaskMemFree(pwfx); + // SAFE_RELEASE(pEnumerator) + // SAFE_RELEASE(pDevice) + // SAFE_RELEASE(pAudioClient) + // SAFE_RELEASE(pRenderClient) +} diff --git a/armorcore/sources/backends/wasm/GL/gl.h b/armorcore/sources/backends/wasm/GL/gl.h new file mode 100644 index 000000000..3536173c2 --- /dev/null +++ b/armorcore/sources/backends/wasm/GL/gl.h @@ -0,0 +1,246 @@ +#pragma once + +#include +#include +#include + +typedef int GLint; +typedef unsigned GLuint; +typedef size_t GLsizei; +typedef float GLfloat; +typedef int GLenum; +typedef bool GLboolean; +typedef uint8_t GLubyte; +typedef float GLclampf; +typedef int GLbitfield; +typedef uint64_t GLsizeiptr; +typedef uint64_t GLintptr; +typedef char GLchar; + +// custom +#define GL_MAJOR_VERSION 2 +#define GL_EXTENSIONS 0 +#define GL_TRUE 1 +#define GL_FALSE 0 + +// not supported +#define GL_INFO_LOG_LENGTH 0xABCD + +// regular WebGL defines +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_FLOAT_MAT4 0x8B5C +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_UNSIGNED_INT 0x1405 +#define GL_TRIANGLES 0x0004 +#define GL_SCISSOR_TEST 15 +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_REPEAT 0x2901 +#define GL_DEPTH_TEST 0x0B71 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_LEQUAL 0x0203 +#define GL_NONE 0 +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_FRAMEBUFFER 0x8D40 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_QUERY_RESULT 0x8866 +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_ALWAYS 0x0207 +#define GL_EQUAL 0x0202 +#define GL_GREATER 0x0204 +#define GL_GEQUAL 0x0206 +#define GL_LESS 0x0201 +#define GL_NEVER 0x0200 +#define GL_NOTEQUAL 0x0205 +#define GL_STATIC_DRAW 0x88E4 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_DECR 0x1E03 +#define GL_DECR_WRAP 0x8508 +#define GL_INCR 0x1E02 +#define GL_INCR_WRAP 0x8507 +#define GL_INVERT 0x150A +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_ALPHA 0x0302 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_SRC_COLOR 0x0300 +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_SUBTRACT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_STENCIL_TEST 0x0B90 +#define GL_CULL_FACE 0x0B44 +#define GL_BLEND 0x0BE2 +#define GL_FLOAT 0x1406 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_DEPTH24_STENCIL8_OES 0x88F0 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RGBA 0x1908 +#define GL_LUMINANCE 0x1909 +#define GL_R8 0x8229 +#define GL_RED 0x1903 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_RGB 0x1907 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_BYTE 0x1400 +#define GL_SHORT 0x1402 +#define GL_INT 0x1404 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_MAX_VERTEX_ATTRIBS 0x8869 + +__attribute__((import_module("imports"), import_name("glUniform1i"))) void glUniform1i(GLint location, GLint v0); +__attribute__((import_module("imports"), import_name("glUniform2i"))) void glUniform2i(GLint location, GLint v0, GLint v1); +__attribute__((import_module("imports"), import_name("glUniform3i"))) void glUniform3i(GLint location, GLint v0, GLint v1, GLint v2); +__attribute__((import_module("imports"), import_name("glUniform4i"))) void glUniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +__attribute__((import_module("imports"), import_name("glUniform1iv"))) void glUniform1iv(GLint location, GLsizei count, const GLint *value); +__attribute__((import_module("imports"), import_name("glUniform2iv"))) void glUniform2iv(GLint location, GLsizei count, const GLint *value); +__attribute__((import_module("imports"), import_name("glUniform3iv"))) void glUniform3iv(GLint location, GLsizei count, const GLint *value); +__attribute__((import_module("imports"), import_name("glUniform4iv"))) void glUniform4iv(GLint location, GLsizei count, const GLint *value); +__attribute__((import_module("imports"), import_name("glUniform1f"))) void glUniform1f(GLint location, GLfloat v0); +__attribute__((import_module("imports"), import_name("glUniform2f"))) void glUniform2f(GLint location, GLfloat v0, GLfloat v1); +__attribute__((import_module("imports"), import_name("glUniform3f"))) void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +__attribute__((import_module("imports"), import_name("glUniform4f"))) void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +__attribute__((import_module("imports"), import_name("glUniform1fv"))) void glUniform1fv(GLint location, GLsizei count, const GLfloat *value); +__attribute__((import_module("imports"), import_name("glUniform2fv"))) void glUniform2fv(GLint location, GLsizei count, const GLfloat *value); +__attribute__((import_module("imports"), import_name("glUniform3fv"))) void glUniform3fv(GLint location, GLsizei count, const GLfloat *value); +__attribute__((import_module("imports"), import_name("glUniform4fv"))) void glUniform4fv(GLint location, GLsizei count, const GLfloat *value); +__attribute__((import_module("imports"), import_name("glUniformMatrix3fv"))) void glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, + const GLfloat *value); +__attribute__((import_module("imports"), import_name("glUniformMatrix4fv"))) void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, + const GLfloat *value); +__attribute__((import_module("imports"), import_name("glViewport"))) void glViewport(GLint x, GLint y, GLsizei width, GLsizei height); +__attribute__((import_module("imports"), import_name("glGetIntegerv"))) void glGetIntegerv(GLenum pname, GLint *data); +__attribute__((import_module("imports"), import_name("glGetString"))) const GLubyte *glGetString(GLenum name); +__attribute__((import_module("imports"), import_name("glDrawElements"))) void glDrawElements(GLenum mode, GLsizei count, GLenum type, const void *indices); +__attribute__((import_module("imports"), import_name("glEnable"))) void glEnable(GLenum cap); +__attribute__((import_module("imports"), import_name("glDisable"))) void glDisable(GLenum cap); +__attribute__((import_module("imports"), import_name("glScissor"))) void glScissor(GLint x, GLint y, GLsizei width, GLsizei height); +__attribute__((import_module("imports"), import_name("glColorMask"))) void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +__attribute__((import_module("imports"), import_name("glClearColor"))) void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +__attribute__((import_module("imports"), import_name("glDepthMask"))) void glDepthMask(GLboolean flag); +__attribute__((import_module("imports"), import_name("glClearDepthf"))) void glClearDepthf(GLclampf depth); +__attribute__((import_module("imports"), import_name("glStencilMask"))) void glStencilMask(GLuint mask); +__attribute__((import_module("imports"), import_name("glClearStencil"))) void glClearStencil(GLint s); +__attribute__((import_module("imports"), import_name("glClear"))) void glClear(GLbitfield mask); +__attribute__((import_module("imports"), import_name("glTexParameteri"))) void glTexParameteri(GLenum target, GLenum pname, GLint param); +__attribute__((import_module("imports"), import_name("glActiveTexture"))) void glActiveTexture(GLenum texture); +__attribute__((import_module("imports"), import_name("glGetFloatv"))) void glGetFloatv(GLenum pname, GLfloat *params); +__attribute__((import_module("imports"), import_name("glTexParameterf"))) void glTexParameterf(GLenum target, GLenum pname, GLfloat param); +__attribute__((import_module("imports"), import_name("glBindFramebuffer"))) void glBindFramebuffer(GLenum target, GLuint framebuffer); +__attribute__((import_module("imports"), import_name("glFramebufferTexture2D"))) void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, + GLuint texture, GLint level); +__attribute__((import_module("imports"), import_name("glGenQueries"))) void glGenQueries(GLsizei n, GLuint *ids); +__attribute__((import_module("imports"), import_name("glDeleteQueries"))) void glDeleteQueries(GLsizei n, const GLuint *ids); +__attribute__((import_module("imports"), import_name("glBeginQuery"))) void glBeginQuery(GLenum target, GLuint id); +__attribute__((import_module("imports"), import_name("glEndQuery"))) void glEndQuery(GLenum target); +__attribute__((import_module("imports"), import_name("glGetQueryObjectuiv"))) void glGetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params); +__attribute__((import_module("imports"), import_name("glDrawArrays"))) void glDrawArrays(GLenum mode, GLint first, GLsizei count); +__attribute__((import_module("imports"), import_name("glFlush"))) void glFlush(void); +__attribute__((import_module("imports"), import_name("glStencilFuncSeparate"))) void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask); +__attribute__((import_module("imports"), import_name("glGenBuffers"))) void glGenBuffers(GLsizei n, GLuint *buffers); +__attribute__((import_module("imports"), import_name("glDeleteBuffers"))) void glDeleteBuffers(GLsizei n, const GLuint *buffers); +__attribute__((import_module("imports"), import_name("glBindBuffer"))) void glBindBuffer(GLenum target, GLuint buffer); +__attribute__((import_module("imports"), import_name("glBufferData"))) void glBufferData(GLenum target, GLsizeiptr size, const void *data, GLenum usage); +__attribute__((import_module("imports"), import_name("glCreateProgram"))) GLuint glCreateProgram(void); +__attribute__((import_module("imports"), import_name("glDeleteProgram"))) void glDeleteProgram(GLuint program); +__attribute__((import_module("imports"), import_name("glCreateShader"))) GLuint glCreateShader(GLenum shaderType); +__attribute__((import_module("imports"), import_name("glShaderSource"))) void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, + const GLint *length); +__attribute__((import_module("imports"), import_name("glCompileShader"))) void glCompileShader(GLuint shader); +__attribute__((import_module("imports"), import_name("glGetShaderiv"))) void glGetShaderiv(GLuint shader, GLenum pname, GLint *params); +__attribute__((import_module("imports"), import_name("glGetShaderInfoLog"))) void glGetShaderInfoLog(GLuint shader, GLsizei maxLength, GLsizei *length, + GLchar *infoLog); +__attribute__((import_module("imports"), import_name("glAttachShader"))) void glAttachShader(GLuint program, GLuint shader); +__attribute__((import_module("imports"), import_name("glBindAttribLocation"))) void glBindAttribLocation(GLuint program, GLuint index, const GLchar *name); +__attribute__((import_module("imports"), import_name("glLinkProgram"))) void glLinkProgram(GLuint program); +__attribute__((import_module("imports"), import_name("glGetProgramiv"))) void glGetProgramiv(GLuint program, GLenum pname, GLint *params); +__attribute__((import_module("imports"), import_name("glGetProgramInfoLog"))) void glGetProgramInfoLog(GLuint program, GLsizei maxLength, GLsizei *length, + GLchar *infoLog); +__attribute__((import_module("imports"), import_name("glUseProgram"))) void glUseProgram(GLuint program); +__attribute__((import_module("imports"), import_name("glStencilMaskSeparate"))) void glStencilMaskSeparate(GLenum face, GLuint mask); +__attribute__((import_module("imports"), import_name("glStencilOpSeparate"))) void glStencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +__attribute__((import_module("imports"), import_name("glDepthFunc"))) void glDepthFunc(GLenum func); +__attribute__((import_module("imports"), import_name("glCullFace"))) void glCullFace(GLenum mode); +__attribute__((import_module("imports"), import_name("glBlendFuncSeparate"))) void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, + GLenum dstAlpha); +__attribute__((import_module("imports"), import_name("glBlendEquationSeparate"))) void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha); +__attribute__((import_module("imports"), import_name("glGetUniformLocation"))) GLint glGetUniformLocation(GLuint program, const GLchar *name); +__attribute__((import_module("imports"), import_name("glGetActiveUniform"))) void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, + GLsizei *length, GLint *size, GLenum *type, GLchar *name); +__attribute__((import_module("imports"), import_name("glGenTextures"))) void glGenTextures(GLsizei n, GLuint *textures); +__attribute__((import_module("imports"), import_name("glDeleteTextures"))) void glDeleteTextures(GLsizei n, const GLuint *textures); +__attribute__((import_module("imports"), import_name("glBindTexture"))) void glBindTexture(GLenum target, GLuint texture); +__attribute__((import_module("imports"), import_name("glTexImage2D"))) void +glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *data); +__attribute__((import_module("imports"), import_name("glGenRenderbuffers"))) void glGenRenderbuffers(GLsizei n, GLuint *renderbuffers); +__attribute__((import_module("imports"), import_name("glBindRenderbuffer"))) void glBindRenderbuffer(GLenum target, GLuint renderbuffer); +__attribute__((import_module("imports"), import_name("glRenderbufferStorage"))) void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, + GLsizei height); +__attribute__((import_module("imports"), import_name("glGenFramebuffers"))) void glGenFramebuffers(GLsizei n, GLuint *ids); +__attribute__((import_module("imports"), import_name("glDeleteFramebuffers"))) void glDeleteFramebuffers(GLsizei n, const GLuint *framebuffers); +__attribute__((import_module("imports"), import_name("glReadPixels"))) void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, + GLenum type, void *data); +__attribute__((import_module("imports"), import_name("glFramebufferRenderbuffer"))) void +glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +__attribute__((import_module("imports"), import_name("glGenerateMipmap"))) void glGenerateMipmap(GLenum target); +__attribute__((import_module("imports"), import_name("glDeleteShader"))) void glDeleteShader(GLuint shader); +__attribute__((import_module("imports"), import_name("glPixelStorei"))) void glPixelStorei(GLenum pname, GLint param); +__attribute__((import_module("imports"), import_name("glCompressedTexImage2D"))) void +glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +__attribute__((import_module("imports"), import_name("glTexSubImage2D"))) void +glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +__attribute__((import_module("imports"), import_name("glBufferSubData"))) void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, + const void *data); +__attribute__((import_module("imports"), import_name("glEnableVertexAttribArray"))) void glEnableVertexAttribArray(GLuint index); +__attribute__((import_module("imports"), import_name("glDisableVertexAttribArray"))) void glDisableVertexAttribArray(GLuint index); +__attribute__((import_module("imports"), import_name("glVertexAttribPointer"))) void +glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +__attribute__((import_module("imports"), import_name("glBlendColor"))) void glBlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +__attribute__((import_module("imports"), import_name("glDrawBuffers"))) void glDrawBuffers(GLsizei n, const GLenum *bufs); +__attribute__((import_module("imports"), import_name("glDrawElementsInstanced"))) void glDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, + const void *indices, GLsizei instancecount); +__attribute__((import_module("imports"), import_name("glVertexAttribDivisor"))) void glVertexAttribDivisor(GLuint index, GLuint divisor); diff --git a/armorcore/sources/backends/wasm/kinc/backend/atomic.h b/armorcore/sources/backends/wasm/kinc/backend/atomic.h new file mode 100644 index 000000000..fe56e9689 --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/atomic.h @@ -0,0 +1,13 @@ +#pragma once + +#define KINC_ATOMIC_COMPARE_EXCHANGE(pointer, oldValue, newValue) + +#define KINC_ATOMIC_COMPARE_EXCHANGE_POINTER(pointer, oldValue, newValue) + +#define KINC_ATOMIC_INCREMENT(pointer) + +#define KINC_ATOMIC_DECREMENT(pointer) + +#define KINC_ATOMIC_EXCHANGE_32(pointer, value) + +#define KINC_ATOMIC_EXCHANGE_FLOAT(pointer, value) diff --git a/armorcore/sources/backends/wasm/kinc/backend/audio.c.h b/armorcore/sources/backends/wasm/kinc/backend/audio.c.h new file mode 100644 index 000000000..3be8307a5 --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/audio.c.h @@ -0,0 +1,18 @@ +#include +#include + +static kinc_a2_buffer_t a2_buffer; + +void kinc_a2_init() { + kinc_a2_internal_init(); +} + +void kinc_a2_update() {} + +void kinc_a2_shutdown() {} + +static uint32_t samples_per_second = 44100; + +uint32_t kinc_a2_samples_per_second(void) { + return samples_per_second; +} diff --git a/armorcore/sources/backends/wasm/kinc/backend/display.c.h b/armorcore/sources/backends/wasm/kinc/backend/display.c.h new file mode 100644 index 000000000..f563001ca --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/display.c.h @@ -0,0 +1,47 @@ +#include + +void kinc_display_init(void) {} + +int kinc_primary_display(void) { + return 0; +} + +int kinc_count_displays(void) { + return 1; +} + +bool kinc_display_available(int display_index) { + return false; +} + +const char *kinc_display_name(int display_index) { + return "Browser"; +} + +kinc_display_mode_t kinc_display_current_mode(int display_index) { + kinc_display_mode_t mode; + mode.x = 0; + mode.y = 0; + mode.width = 800; + mode.height = 600; + mode.pixels_per_inch = 96; + mode.frequency = 60; + mode.bits_per_pixel = 32; + return mode; +} + +int kinc_display_count_available_modes(int display_index) { + return 1; +} + +kinc_display_mode_t kinc_display_available_mode(int display_index, int mode_index) { + kinc_display_mode_t mode; + mode.x = 0; + mode.y = 0; + mode.width = 800; + mode.height = 600; + mode.pixels_per_inch = 96; + mode.frequency = 60; + mode.bits_per_pixel = 32; + return mode; +} diff --git a/armorcore/sources/backends/wasm/kinc/backend/event.c.h b/armorcore/sources/backends/wasm/kinc/backend/event.c.h new file mode 100644 index 000000000..c00ba1d44 --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/event.c.h @@ -0,0 +1,15 @@ +#include + +void kinc_event_init(kinc_event_t *event, bool auto_reset) {} + +void kinc_event_destroy(kinc_event_t *event) {} + +void kinc_event_signal(kinc_event_t *event) {} + +void kinc_event_wait(kinc_event_t *event) {} + +bool kinc_event_try_to_wait(kinc_event_t *event, double seconds) { + return false; +} + +void kinc_event_reset(kinc_event_t *event) {} diff --git a/armorcore/sources/backends/wasm/kinc/backend/event.h b/armorcore/sources/backends/wasm/kinc/backend/event.h new file mode 100644 index 000000000..1767329c2 --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/event.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int nothing; +} kinc_event_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/wasm/kinc/backend/html5unit.c b/armorcore/sources/backends/wasm/kinc/backend/html5unit.c new file mode 100644 index 000000000..48072f369 --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/html5unit.c @@ -0,0 +1,11 @@ +#include "audio.c.h" +#include "display.c.h" +#include "event.c.h" +#include "mouse.c.h" +#include "mutex.c.h" +#include "semaphore.c.h" +#include "system.c.h" +#include "thread.c.h" +#include "threadlocal.c.h" +#include "video.c.h" +#include "window.c.h" diff --git a/armorcore/sources/backends/wasm/kinc/backend/mouse.c.h b/armorcore/sources/backends/wasm/kinc/backend/mouse.c.h new file mode 100644 index 000000000..db49ee8bf --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/mouse.c.h @@ -0,0 +1,17 @@ +#include + +void kinc_internal_mouse_lock(int window) {} + +void kinc_internal_mouse_unlock(void) {} + +bool kinc_mouse_can_lock(void) { + return false; +} + +void kinc_mouse_show() {} + +void kinc_mouse_hide() {} + +void kinc_mouse_set_position(int window, int x, int y) {} + +void kinc_mouse_get_position(int window, int *x, int *y) {} diff --git a/armorcore/sources/backends/wasm/kinc/backend/mutex.c.h b/armorcore/sources/backends/wasm/kinc/backend/mutex.c.h new file mode 100644 index 000000000..671b7182a --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/mutex.c.h @@ -0,0 +1,23 @@ +#include + +void kinc_mutex_init(kinc_mutex_t *mutex) {} + +void kinc_mutex_destroy(kinc_mutex_t *mutex) {} + +bool kinc_mutex_try_to_lock(kinc_mutex_t *mutex) { + return false; +} + +void kinc_mutex_lock(kinc_mutex_t *mutex) {} + +void kinc_mutex_unlock(kinc_mutex_t *mutex) {} + +bool kinc_uber_mutex_init(kinc_uber_mutex_t *mutex, const char *name) { + return false; +} + +void kinc_uber_mutex_destroy(kinc_uber_mutex_t *mutex) {} + +void kinc_uber_mutex_lock(kinc_uber_mutex_t *mutex) {} + +void kinc_uber_mutex_unlock(kinc_uber_mutex_t *mutex) {} diff --git a/armorcore/sources/backends/wasm/kinc/backend/mutex.h b/armorcore/sources/backends/wasm/kinc/backend/mutex.h new file mode 100644 index 000000000..4f733e058 --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/mutex.h @@ -0,0 +1,17 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int nothing; +} kinc_mutex_impl_t; + +typedef struct { + int nothing; +} kinc_uber_mutex_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/wasm/kinc/backend/semaphore.c.h b/armorcore/sources/backends/wasm/kinc/backend/semaphore.c.h new file mode 100644 index 000000000..c2940ab55 --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/semaphore.c.h @@ -0,0 +1,13 @@ +#include + +void kinc_semaphore_init(kinc_semaphore_t *semaphore, int current, int max) {} + +void kinc_semaphore_destroy(kinc_semaphore_t *semaphore) {} + +void kinc_semaphore_release(kinc_semaphore_t *semaphore, int count) {} + +void kinc_semaphore_acquire(kinc_semaphore_t *semaphore) {} + +bool kinc_semaphore_try_to_acquire(kinc_semaphore_t *semaphore, double seconds) { + return false; +} diff --git a/armorcore/sources/backends/wasm/kinc/backend/semaphore.h b/armorcore/sources/backends/wasm/kinc/backend/semaphore.h new file mode 100644 index 000000000..77adf8ff9 --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/semaphore.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int nothing; +} kinc_semaphore_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/wasm/kinc/backend/system.c.h b/armorcore/sources/backends/wasm/kinc/backend/system.c.h new file mode 100644 index 000000000..615d8b49f --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/system.c.h @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +__attribute__((import_module("imports"), import_name("js_time"))) int js_time(); + +extern int kinc_internal_window_width; +extern int kinc_internal_window_height; + +int kinc_init(const char *name, int width, int height, kinc_window_options_t *win, kinc_framebuffer_options_t *frame) { + kinc_window_options_t defaultWin; + if (win == NULL) { + kinc_window_options_set_defaults(&defaultWin); + win = &defaultWin; + } + kinc_framebuffer_options_t defaultFrame; + if (frame == NULL) { + kinc_framebuffer_options_set_defaults(&defaultFrame); + frame = &defaultFrame; + } + win->width = width; + win->height = height; + + kinc_internal_window_width = width; + kinc_internal_window_height = height; + + kinc_g4_internal_init(); + kinc_g4_internal_init_window(0, frame->depth_bits, frame->stencil_bits, true); + + return 0; +} + +bool kinc_internal_handle_messages() { + return true; +} + +void kinc_set_keep_screen_on(bool on) {} + +double kinc_frequency(void) { + return 1000.0; +} + +kinc_ticks_t kinc_timestamp(void) { + return (kinc_ticks_t)(js_time()); +} + +double kinc_time(void) { + return js_time() / 1000.0; +} + +int kinc_cpu_cores(void) { + return 4; +} + +int kinc_hardware_threads(void) { + return 4; +} + +void kinc_internal_shutdown(void) {} + +extern int kickstart(int argc, char **argv); + +__attribute__((export_name("_start"))) void _start(void) { + kickstart(0, NULL); +} + +__attribute__((export_name("_update"))) void _update(void) { + kinc_internal_update_callback(); + kinc_a2_update(); +} + +__attribute__((export_name("_mousedown"))) void _mousedown(int button, int x, int y) { + kinc_internal_mouse_trigger_press(0, button, x, y); +} + +__attribute__((export_name("_mouseup"))) void _mouseup(int button, int x, int y) { + kinc_internal_mouse_trigger_release(0, button, x, y); +} + +__attribute__((export_name("_mousemove"))) void _mousemove(int x, int y) { + kinc_internal_mouse_trigger_move(0, x, y); +} + +__attribute__((export_name("_wheel"))) void _wheel(int delta) { + kinc_internal_mouse_trigger_scroll(0, delta); +} + +__attribute__((export_name("_keydown"))) void _keydown(int key) { + kinc_internal_keyboard_trigger_key_down(key); +} + +__attribute__((export_name("_keyup"))) void _keyup(int key) { + kinc_internal_keyboard_trigger_key_up(key); +} diff --git a/armorcore/sources/backends/wasm/kinc/backend/thread.c.h b/armorcore/sources/backends/wasm/kinc/backend/thread.c.h new file mode 100644 index 000000000..bc38a181e --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/thread.c.h @@ -0,0 +1,15 @@ +#include + +void kinc_thread_init(kinc_thread_t *t, void (*thread)(void *param), void *param) {} + +void kinc_thread_wait_and_destroy(kinc_thread_t *thread) {} + +bool kinc_thread_try_to_destroy(kinc_thread_t *thread) { + return false; +} + +void kinc_threads_init() {} + +void kinc_threads_quit() {} + +void kinc_thread_sleep(int milliseconds) {} diff --git a/armorcore/sources/backends/wasm/kinc/backend/thread.h b/armorcore/sources/backends/wasm/kinc/backend/thread.h new file mode 100644 index 000000000..bb0ac770b --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/thread.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int nothing; +} kinc_thread_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/wasm/kinc/backend/threadlocal.c.h b/armorcore/sources/backends/wasm/kinc/backend/threadlocal.c.h new file mode 100644 index 000000000..114701832 --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/threadlocal.c.h @@ -0,0 +1,13 @@ +#include + +#include + +void kinc_thread_local_init(kinc_thread_local_t *local) {} + +void kinc_thread_local_destroy(kinc_thread_local_t *local) {} + +void *kinc_thread_local_get(kinc_thread_local_t *local) { + return NULL; +} + +void kinc_thread_local_set(kinc_thread_local_t *local, void *data) {} diff --git a/armorcore/sources/backends/wasm/kinc/backend/threadlocal.h b/armorcore/sources/backends/wasm/kinc/backend/threadlocal.h new file mode 100644 index 000000000..f1f0e9063 --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/threadlocal.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int nothing; +} kinc_thread_local_impl_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/wasm/kinc/backend/video.c.h b/armorcore/sources/backends/wasm/kinc/backend/video.c.h new file mode 100644 index 000000000..bedd1549d --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/video.c.h @@ -0,0 +1,57 @@ +#include + +void kinc_video_init(kinc_video_t *video, const char *filename) {} + +void kinc_video_destroy(kinc_video_t *video) {} + +void kinc_video_play(kinc_video_t *video, bool loop) {} + +void kinc_video_pause(kinc_video_t *video) {} + +void kinc_video_stop(kinc_video_t *video) {} + +int kinc_video_width(kinc_video_t *video) { + return 256; +} + +int kinc_video_height(kinc_video_t *video) { + return 256; +} + +kinc_g4_texture_t *kinc_video_current_image(kinc_video_t *video) { + return NULL; +} + +double kinc_video_duration(kinc_video_t *video) { + return 0.0; +} + +double kinc_video_position(kinc_video_t *video) { + return 0.0; +} + +bool kinc_video_finished(kinc_video_t *video) { + return false; +} + +bool kinc_video_paused(kinc_video_t *video) { + return false; +} + +void kinc_video_update(kinc_video_t *video, double time) {} + +void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency) {} + +void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream) {} + +void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count) {} + +static float samples[2] = {0}; + +float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream) { + return samples; +} + +bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream) { + return true; +} diff --git a/armorcore/sources/backends/wasm/kinc/backend/video.h b/armorcore/sources/backends/wasm/kinc/backend/video.h new file mode 100644 index 000000000..146a7d4c1 --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/video.h @@ -0,0 +1,27 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int nothing; +} kinc_video_impl_t; + +typedef struct kinc_internal_video_sound_stream { + int nothing; +} kinc_internal_video_sound_stream_t; + +void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency); + +void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream); + +void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count); + +float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream); + +bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/wasm/kinc/backend/window.c.h b/armorcore/sources/backends/wasm/kinc/backend/window.c.h new file mode 100644 index 000000000..3f1220c33 --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/window.c.h @@ -0,0 +1,81 @@ +#include +#include +#include + +#include + +int kinc_internal_window_width = 0; +int kinc_internal_window_height = 0; +kinc_window_mode_t kinc_internal_window_mode = KINC_WINDOW_MODE_WINDOW; + +int kinc_count_windows(void) { + return 1; +} + +int kinc_window_x(int window_index) { + return 0; +} + +int kinc_window_y(int window_index) { + return 0; +} + +int kinc_window_width(int window_index) { + return kinc_internal_window_width; +} + +int kinc_window_height(int window_index) { + return kinc_internal_window_height; +} + +void kinc_window_resize(int window_index, int width, int height) {} + +void kinc_window_move(int window_index, int x, int y) {} + +void kinc_window_change_framebuffer(int window_index, kinc_framebuffer_options_t *frame) { + //**kinc_g4_changeFramebuffer(0, frame); +} + +void kinc_window_change_features(int window_index, int features) {} + +// In HTML5 fullscreen is activable only from user input. +void kinc_window_change_mode(int window_index, kinc_window_mode_t mode) { + if (mode == KINC_WINDOW_MODE_FULLSCREEN || mode == KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { + if (kinc_internal_window_mode == KINC_WINDOW_MODE_FULLSCREEN || kinc_internal_window_mode == KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { + kinc_internal_window_mode = mode; + return; + } + // TODO: call js Fullscreen API + kinc_internal_window_mode = mode; + } + else { + if (mode == kinc_internal_window_mode) { + return; + } + // TODO: call js Fullscreen API + kinc_internal_window_mode = mode; + } +} + +void kinc_window_destroy(int window_index) {} + +void kinc_window_show(int window_index) {} + +void kinc_window_hide(int window_index) {} + +// TODO: change browser title. +void kinc_window_set_title(int window_index, const char *title) {} + +int kinc_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) { + return 0; +} + +void kinc_window_set_resize_callback(int window_index, void (*callback)(int x, int y, void *data), void *data) {} + +void kinc_window_set_ppi_changed_callback(int window_index, void (*callback)(int ppi, void *data), void *data) {} + +void kinc_window_set_close_callback(int window, bool (*callback)(void *), void *data) {} + +kinc_window_mode_t kinc_window_get_mode(int window_index) { + return kinc_internal_window_mode; +} diff --git a/armorcore/sources/backends/wasm/kinc/backend/windowdata.h b/armorcore/sources/backends/wasm/kinc/backend/windowdata.h new file mode 100644 index 000000000..564060737 --- /dev/null +++ b/armorcore/sources/backends/wasm/kinc/backend/windowdata.h @@ -0,0 +1,8 @@ +#pragma once + +namespace Kore { + struct WindowData { + int width, height, mode; + WindowData(); + }; +} diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/WebGPU.c b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/WebGPU.c new file mode 100644 index 000000000..b448d1b6d --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/WebGPU.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int renderTargetWidth; +int renderTargetHeight; +int newRenderTargetWidth; +int newRenderTargetHeight; + +WGPUDevice device; +WGPUQueue queue; +WGPUSwapChain swapChain; + +void kinc_g5_internal_destroy_window(int windowId) {} + +void kinc_g5_internal_destroy() {} + +void kinc_g5_internal_init() {} + +void kinc_g5_internal_init_window(int window, int depthBufferBits, int stencilBufferBits, bool vsync) { + newRenderTargetWidth = renderTargetWidth = kinc_width(); + newRenderTargetHeight = renderTargetHeight = kinc_height(); + + device = emscripten_webgpu_get_device(); + queue = wgpuDeviceGetQueue(device); + + WGPUSurfaceDescriptorFromCanvasHTMLSelector canvasDesc; + memset(&canvasDesc, 0, sizeof(canvasDesc)); + canvasDesc.selector = "canvas"; + + WGPUSurfaceDescriptor surfDesc; + memset(&surfDesc, 0, sizeof(surfDesc)); + surfDesc.nextInChain = &canvasDesc; + WGPUInstance instance = 0; + WGPUSurface surface = wgpuInstanceCreateSurface(instance, &surfDesc); + + WGPUSwapChainDescriptor scDesc; + memset(&scDesc, 0, sizeof(scDesc)); + scDesc.usage = WGPUTextureUsage_RenderAttachment; + scDesc.format = WGPUTextureFormat_BGRA8Unorm; + scDesc.width = kinc_width(); + scDesc.height = kinc_height(); + scDesc.presentMode = WGPUPresentMode_Fifo; + swapChain = wgpuDeviceCreateSwapChain(device, surface, &scDesc); +} + +void kinc_g5_draw_indexed_vertices_instanced(int instanceCount) {} + +void kinc_g5_draw_indexed_vertices_instanced_from_to(int instanceCount, int start, int count) {} + +void kinc_g5_draw_indexed_vertices_instanced_from_to_from(int instanceCount, int start, int count, int vertex_offset) {} + +void kinc_g5_begin(kinc_g5_render_target_t *renderTarget, int window) {} + +void kinc_g5_end(int window) {} + +bool kinc_g5_swap_buffers() { + return true; +} + +void kinc_g5_flush() {} + +bool kinc_g5_supports_raytracing() { + return false; +} + +bool kinc_g5_supports_instanced_rendering() { + return true; +} + +bool kinc_g5_supports_compute_shaders() { + return true; +} + +bool kinc_g5_supports_blend_constants() { + return true; +} + +bool kinc_g5_supports_non_pow2_textures() { + return true; +} + +bool kinc_g5_render_targets_inverted_y() { + return false; +} \ No newline at end of file diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/WebGPU.h b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/WebGPU.h new file mode 100644 index 000000000..654181c14 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/WebGPU.h @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/commandlist.c b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/commandlist.c new file mode 100644 index 000000000..124ca4927 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/commandlist.c @@ -0,0 +1,148 @@ +#include +#include +#include +#include +#include +#include +#include + +extern WGPUDevice device; +extern WGPUQueue queue; +extern WGPUSwapChain swapChain; + +void kinc_g5_command_list_init(kinc_g5_command_list_t *list) {} + +void kinc_g5_command_list_destroy(kinc_g5_command_list_t *list) {} + +void kinc_g5_command_list_begin(kinc_g5_command_list_t *list) { + WGPUCommandEncoderDescriptor ceDesc; + memset(&ceDesc, 0, sizeof(ceDesc)); + list->impl.encoder = wgpuDeviceCreateCommandEncoder(device, &ceDesc); + + WGPURenderPassColorAttachment attachment; + memset(&attachment, 0, sizeof(attachment)); + attachment.view = wgpuSwapChainGetCurrentTextureView(swapChain);; + attachment.loadOp = WGPULoadOp_Clear; + attachment.storeOp = WGPUStoreOp_Store; + WGPUColor color = {0, 0, 0, 1}; + attachment.clearValue = color; + + WGPURenderPassDescriptor passDesc; + memset(&passDesc, 0, sizeof(passDesc)); + passDesc.colorAttachmentCount = 1; + passDesc.colorAttachments = &attachment; + + list->impl.pass = wgpuCommandEncoderBeginRenderPass(list->impl.encoder, &passDesc); +} + +void kinc_g5_command_list_end(kinc_g5_command_list_t *list) { + wgpuRenderPassEncoderEnd(list->impl.pass); + + WGPUCommandBufferDescriptor cbDesc; + memset(&cbDesc, 0, sizeof(cbDesc)); + WGPUCommandBuffer commands = wgpuCommandEncoderFinish(list->impl.encoder, &cbDesc); + wgpuQueueSubmit(queue, 1, &commands); +} + +void kinc_g5_command_list_clear(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget, unsigned flags, unsigned color, float depth, + int stencil) { + +} + +void kinc_g5_command_list_render_target_to_framebuffer_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) {} +void kinc_g5_command_list_framebuffer_to_render_target_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) {} +void kinc_g5_command_list_texture_to_render_target_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) {} +void kinc_g5_command_list_render_target_to_texture_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget) {} + +void kinc_g5_command_list_draw_indexed_vertices(kinc_g5_command_list_t *list) { + wgpuRenderPassEncoderDrawIndexed(list->impl.pass, list->impl.indexCount, 1, 0, 0, 0); +} + +void kinc_g5_command_list_draw_indexed_vertices_from_to(kinc_g5_command_list_t *list, int start, int count) { + +} + +void kinc_g5_command_list_draw_indexed_vertices_instanced(kinc_g5_command_list_t *list, int instanceCount) { + +} +void kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(kinc_g5_command_list_t *list, int instanceCount, int start, int count) { + +} + +void kinc_g5_command_list_viewport(kinc_g5_command_list_t *list, int x, int y, int width, int height) { + +} + +void kinc_g5_command_list_scissor(kinc_g5_command_list_t *list, int x, int y, int width, int height) { + +} + +void kinc_g5_command_list_disable_scissor(kinc_g5_command_list_t *list) {} + +void kinc_g5_command_list_set_pipeline(kinc_g5_command_list_t *list, struct kinc_g5_pipeline *pipeline) { + wgpuRenderPassEncoderSetPipeline(list->impl.pass, pipeline->impl.pipeline); +} + +void kinc_g5_command_list_set_blend_constant(kinc_g5_command_list_t *list, float r, float g, float b, float a) { + // TODO +} + +void kinc_g5_command_list_set_pipeline_layout(kinc_g5_command_list_t *list) {} + +void kinc_g5_command_list_set_vertex_buffers(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer **buffers, int *offsets, int count) { + uint64_t size = (kinc_g5_vertex_buffer_count(buffers[0]) - offsets[0]) * kinc_g5_vertex_buffer_stride(buffers[0]); + wgpuRenderPassEncoderSetVertexBuffer(list->impl.pass, 0, buffers[0]->impl.buffer, 0, size); +} + +void kinc_g5_command_list_set_index_buffer(kinc_g5_command_list_t *list, struct kinc_g5_index_buffer *buffer) { + list->impl.indexCount = kinc_g5_index_buffer_count(buffer); + uint64_t size = kinc_g5_index_buffer_count(buffer) * sizeof(int); + wgpuRenderPassEncoderSetIndexBuffer(list->impl.pass, buffer->impl.buffer, buffer->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32, 0, size); +} + +void kinc_g5_command_list_set_render_targets(kinc_g5_command_list_t *list, struct kinc_g5_render_target **targets, int count) { + +} + +void kinc_g5_command_list_upload_index_buffer(kinc_g5_command_list_t *list, struct kinc_g5_index_buffer *buffer) {} +void kinc_g5_command_list_upload_vertex_buffer(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer *buffer) {} +void kinc_g5_command_list_upload_texture(kinc_g5_command_list_t *list, struct kinc_g5_texture *texture) {} +void kinc_g5_command_list_get_render_target_pixels(kinc_g5_command_list_t *list, kinc_g5_render_target_t *render_target, uint8_t *data) {} + +void kinc_g5_command_list_execute(kinc_g5_command_list_t *list) { + +} + +void kinc_g5_command_list_wait_for_execution_to_finish(kinc_g5_command_list_t *list) { + +} + +void kinc_g5_command_list_set_vertex_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size) { + +} + +void kinc_g5_command_list_set_fragment_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size) { + +} + +void kinc_g5_command_list_set_compute_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size) { + +} + +void kinc_g5_command_list_set_render_target_face(kinc_g5_command_list_t *list, kinc_g5_render_target_t *texture, int face) {} + +void kinc_g5_command_list_set_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture) { + +} + +void kinc_g5_command_list_set_sampler(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_sampler_t *sampler) {} + +void kinc_g5_command_list_set_image_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture) {} + +void kinc_g5_command_list_set_texture_from_render_target(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_render_target_t *renderTarget) {} + +void kinc_g5_command_list_set_texture_from_render_target_depth(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_render_target_t *renderTarget) {} + +void kinc_g5_command_list_set_compute_shader(kinc_g5_command_list_t *list, kinc_g5_compute_shader *shader) {} + +void kinc_g5_command_list_compute(kinc_g5_command_list_t *list, int x, int y, int z) {} diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/commandlist.h b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/commandlist.h new file mode 100644 index 000000000..d568278b1 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/commandlist.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + WGPUCommandEncoder encoder; + WGPURenderPassEncoder pass; + int indexCount; +} CommandList5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/compute.c b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/compute.c new file mode 100644 index 000000000..6869e08c8 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/compute.c @@ -0,0 +1,16 @@ +#include +#include + +void kinc_g5_compute_shader_init(kinc_g5_compute_shader *shader, void *source, int length) {} + +void kinc_g5_compute_shader_destroy(kinc_g5_compute_shader *shader) {} + +kinc_g5_constant_location_t kinc_g5_compute_shader_get_constant_location(kinc_g5_compute_shader *shader, const char *name) { + kinc_g5_constant_location_t location = {0}; + return location; +} + +kinc_g5_texture_unit_t kinc_g5_compute_shader_get_texture_unit(kinc_g5_compute_shader *shader, const char *name) { + kinc_g5_texture_unit_t unit = {0}; + return unit; +} diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/compute.h b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/compute.h new file mode 100644 index 000000000..cbea4ecd2 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/compute.h @@ -0,0 +1,5 @@ +#pragma once + +typedef struct kinc_g5_compute_shader_impl { + int nothing; +} kinc_g5_compute_shader_impl; diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/constantbuffer.c b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/constantbuffer.c new file mode 100644 index 000000000..2edbd5483 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/constantbuffer.c @@ -0,0 +1,34 @@ +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool kinc_g5_transposeMat3 = false; +bool kinc_g5_transposeMat4 = false; + +void kinc_g5_constant_buffer_init(kinc_g5_constant_buffer_t *buffer, int size) { + +} + +void kinc_g5_constant_buffer_destroy(kinc_g5_constant_buffer_t *buffer) {} + +void kinc_g5_constant_buffer_lock_all(kinc_g5_constant_buffer_t *buffer) { + kinc_g5_constant_buffer_lock(buffer, 0, kinc_g5_constant_buffer_size(buffer)); +} + +void kinc_g5_constant_buffer_lock(kinc_g5_constant_buffer_t *buffer, int start, int count) {} + +void kinc_g5_constant_buffer_unlock(kinc_g5_constant_buffer_t *buffer) { + +} + +int kinc_g5_constant_buffer_size(kinc_g5_constant_buffer_t *buffer) { + return 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/constantbuffer.h b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/constantbuffer.h new file mode 100644 index 000000000..dc70ff48c --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/constantbuffer.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + WGPUBuffer buffer; +} ConstantBuffer5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/graphics.h b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/graphics.h new file mode 100644 index 000000000..056f2a534 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/graphics.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/indexbuffer.c b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/indexbuffer.c new file mode 100644 index 000000000..ff73833a5 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/indexbuffer.c @@ -0,0 +1,52 @@ +#include + +#include +#include +#include + +extern WGPUDevice device; + +kinc_g5_index_buffer_t *kinc_g5_internal_current_index_buffer = NULL; + +void kinc_g5_index_buffer_init(kinc_g5_index_buffer_t *buffer, int count, kinc_g5_index_buffer_format_t format, bool gpuMemory) { + buffer->impl.count = count; + buffer->impl.format = format; +} + +void kinc_g5_index_buffer_destroy(kinc_g5_index_buffer_t *buffer) { + +} + +static int kinc_g5_internal_index_buffer_stride(kinc_g5_index_buffer_t *buffer) { + return buffer->impl.format == KINC_G5_INDEX_BUFFER_FORMAT_16BIT ? 2 : 4; +} + +void *kinc_g5_index_buffer_lock_all(kinc_g5_index_buffer_t *buffer) { + kinc_g5_index_buffer_lock(buffer, 0, kinc_g5_index_buffer_count(buffer)); +} + +void *kinc_g5_index_buffer_lock(kinc_g5_index_buffer_t *buffer, int start, int count) { + WGPUBufferDescriptor bDesc; + memset(&bDesc, 0, sizeof(bDesc)); + bDesc.size = count * kinc_g5_internal_index_buffer_stride(buffer); + bDesc.usage = WGPUBufferUsage_Index | WGPUBufferUsage_CopyDst; + bDesc.mappedAtCreation = true; + buffer->impl.buffer = wgpuDeviceCreateBuffer(device, &bDesc); + return wgpuBufferGetMappedRange(buffer->impl.buffer, start * kinc_g5_internal_index_buffer_stride(buffer), bDesc.size); +} + +void kinc_g5_index_buffer_unlock_all(kinc_g5_index_buffer_t *buffer) { + wgpuBufferUnmap(buffer->impl.buffer); +} + +void kinc_g5_index_buffer_unlock(kinc_g5_index_buffer_t *buffer, int count) { + kinc_g5_index_buffer_unlock_all(buffer); +} + +void kinc_g5_internal_index_buffer_set(kinc_g5_index_buffer_t *buffer) { + +} + +int kinc_g5_index_buffer_count(kinc_g5_index_buffer_t *buffer) { + return buffer->impl.count; +} diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/indexbuffer.h b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/indexbuffer.h new file mode 100644 index 000000000..6ae064b53 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/indexbuffer.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + WGPUBuffer buffer; + int count; + int format; +} IndexBuffer5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/pipeline.c b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/pipeline.c new file mode 100644 index 000000000..675ebd7ac --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/pipeline.c @@ -0,0 +1,223 @@ +#include +#include + +#include +#include + +extern WGPUDevice device; + +void kinc_g5_pipeline_init(kinc_g5_pipeline_t *pipe) { + kinc_g5_internal_pipeline_init(pipe); +} + +kinc_g5_constant_location_t kinc_g5_pipeline_get_constant_location(kinc_g5_pipeline_t *pipe, const char* name) { + kinc_g5_constant_location_t location; + return location; +} + +kinc_g5_texture_unit_t kinc_g5_pipeline_get_texture_unit(kinc_g5_pipeline_t *pipe, const char *name) { + kinc_g5_texture_unit_t unit; + return unit; +} + +void kinc_g5_pipeline_compile(kinc_g5_pipeline_t *pipe) { + WGPUColorTargetState csDesc; + memset(&csDesc, 0, sizeof(csDesc)); + csDesc.format = WGPUTextureFormat_BGRA8Unorm; + csDesc.writeMask = WGPUColorWriteMask_All; + WGPUBlendState blend; + memset(&blend, 0, sizeof(blend)); + blend.color.operation = WGPUBlendOperation_Add; + blend.color.srcFactor = WGPUBlendFactor_One; + blend.color.dstFactor = WGPUBlendFactor_Zero; + blend.alpha.operation = WGPUBlendOperation_Add; + blend.alpha.srcFactor = WGPUBlendFactor_One; + blend.alpha.dstFactor = WGPUBlendFactor_Zero; + csDesc.blend = &blend; + + WGPUPipelineLayoutDescriptor plDesc; + memset(&plDesc, 0, sizeof(plDesc)); + plDesc.bindGroupLayoutCount = 0; + plDesc.bindGroupLayouts = NULL; + + WGPUVertexAttribute vaDesc[8]; + memset(&vaDesc[0], 0, sizeof(vaDesc[0]) * 8); + uint64_t offset = 0; + for (int i = 0; i < pipe->inputLayout[0]->size; ++i) { + vaDesc[i].shaderLocation = i; + vaDesc[i].offset = offset; + offset += kinc_g4_vertex_data_size(pipe->inputLayout[0]->elements[i].data); + switch (pipe->inputLayout[0]->elements[i].data) { + case KINC_G4_VERTEX_DATA_NONE: + vaDesc[i].format = WGPUVertexFormat_Undefined; + assert(false); + break; + case KINC_G4_VERTEX_DATA_F32_1X: + vaDesc[i].format = WGPUVertexFormat_Float32; + break; + case KINC_G4_VERTEX_DATA_F32_2X: + vaDesc[i].format = WGPUVertexFormat_Float32x2; + break; + case KINC_G4_VERTEX_DATA_F32_3X: + vaDesc[i].format = WGPUVertexFormat_Float32x3; + break; + case KINC_G4_VERTEX_DATA_F32_4X: + vaDesc[i].format = WGPUVertexFormat_Float32x4; + break; + case KINC_G4_VERTEX_DATA_F32_4X4: + vaDesc[i].format = WGPUVertexFormat_Undefined; + assert(false); + break; + case KINC_G4_VERTEX_DATA_I8_1X: + vaDesc[i].format = WGPUVertexFormat_Undefined; + assert(false); + break; + case KINC_G4_VERTEX_DATA_U8_1X: + vaDesc[i].format = WGPUVertexFormat_Undefined; + assert(false); + break; + case KINC_G4_VERTEX_DATA_I8_1X_NORMALIZED: + vaDesc[i].format = WGPUVertexFormat_Undefined; + assert(false); + break; + case KINC_G4_VERTEX_DATA_U8_1X_NORMALIZED: + vaDesc[i].format = WGPUVertexFormat_Undefined; + assert(false); + break; + case KINC_G4_VERTEX_DATA_I8_2X: + vaDesc[i].format = WGPUVertexFormat_Sint8x2; + break; + case KINC_G4_VERTEX_DATA_U8_2X: + vaDesc[i].format = WGPUVertexFormat_Uint8x2 ; + break; + case KINC_G4_VERTEX_DATA_I8_2X_NORMALIZED: + vaDesc[i].format = WGPUVertexFormat_Snorm8x2; + break; + case KINC_G4_VERTEX_DATA_U8_2X_NORMALIZED: + vaDesc[i].format = WGPUVertexFormat_Unorm8x2; + break; + case KINC_G4_VERTEX_DATA_I8_4X: + vaDesc[i].format = WGPUVertexFormat_Sint8x4; + break; + case KINC_G4_VERTEX_DATA_U8_4X: + vaDesc[i].format = WGPUVertexFormat_Uint8x4; + break; + case KINC_G4_VERTEX_DATA_I8_4X_NORMALIZED: + vaDesc[i].format = WGPUVertexFormat_Snorm8x4; + break; + case KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED: + vaDesc[i].format = WGPUVertexFormat_Unorm8x4; + break; + case KINC_G4_VERTEX_DATA_I16_1X: + vaDesc[i].format = WGPUVertexFormat_Undefined; + assert(false); + break; + case KINC_G4_VERTEX_DATA_U16_1X: + vaDesc[i].format = WGPUVertexFormat_Undefined; + assert(false); + break; + case KINC_G4_VERTEX_DATA_I16_1X_NORMALIZED: + vaDesc[i].format = WGPUVertexFormat_Undefined; + assert(false); + break; + case KINC_G4_VERTEX_DATA_U16_1X_NORMALIZED: + vaDesc[i].format = WGPUVertexFormat_Undefined; + assert(false); + break; + case KINC_G4_VERTEX_DATA_I16_2X: + vaDesc[i].format = WGPUVertexFormat_Sint16x2; + break; + case KINC_G4_VERTEX_DATA_U16_2X: + vaDesc[i].format = WGPUVertexFormat_Uint16x2; + break; + case KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED: + vaDesc[i].format = WGPUVertexFormat_Snorm16x2; + break; + case KINC_G4_VERTEX_DATA_U16_2X_NORMALIZED: + vaDesc[i].format = WGPUVertexFormat_Unorm16x2; + break; + case KINC_G4_VERTEX_DATA_I16_4X: + vaDesc[i].format = WGPUVertexFormat_Sint16x4; + break; + case KINC_G4_VERTEX_DATA_U16_4X: + vaDesc[i].format = WGPUVertexFormat_Uint16x4; + break; + case KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED: + vaDesc[i].format = WGPUVertexFormat_Snorm16x4; + break; + case KINC_G4_VERTEX_DATA_U16_4X_NORMALIZED: + vaDesc[i].format = WGPUVertexFormat_Unorm16x4; + break; + case KINC_G4_VERTEX_DATA_I32_1X: + vaDesc[i].format = WGPUVertexFormat_Sint32; + break; + case KINC_G4_VERTEX_DATA_U32_1X: + vaDesc[i].format = WGPUVertexFormat_Uint32; + break; + case KINC_G4_VERTEX_DATA_I32_2X: + vaDesc[i].format = WGPUVertexFormat_Sint32x2; + break; + case KINC_G4_VERTEX_DATA_U32_2X: + vaDesc[i].format = WGPUVertexFormat_Uint32x2; + break; + case KINC_G4_VERTEX_DATA_I32_3X: + vaDesc[i].format = WGPUVertexFormat_Sint32x3; + break; + case KINC_G4_VERTEX_DATA_U32_3X: + vaDesc[i].format = WGPUVertexFormat_Uint32x3; + break; + case KINC_G4_VERTEX_DATA_I32_4X: + vaDesc[i].format = WGPUVertexFormat_Sint32x4; + break; + case KINC_G4_VERTEX_DATA_U32_4X: + vaDesc[i].format = WGPUVertexFormat_Uint32x4; + break; + } + } + + WGPUVertexBufferLayout vbDesc; + memset(&vbDesc, 0, sizeof(vbDesc)); + vbDesc.arrayStride = offset; + vbDesc.attributeCount = pipe->inputLayout[0]->size; + vbDesc.attributes = &vaDesc[0]; + + WGPUVertexState vsDest; + memset(&vsDest, 0, sizeof(vsDest)); + + vsDest.module = pipe->vertexShader->impl.module; + vsDest.entryPoint = "main"; + + vsDest.bufferCount = 1; + vsDest.buffers = &vbDesc; + + WGPUFragmentState fragmentDest; + memset(&fragmentDest, 0, sizeof(fragmentDest)); + + fragmentDest.module = pipe->fragmentShader->impl.module; + fragmentDest.entryPoint = "main"; + + fragmentDest.targetCount = 1; + fragmentDest.targets = &csDesc; + + WGPUPrimitiveState rsDesc; + memset(&rsDesc, 0, sizeof(rsDesc)); + rsDesc.topology = WGPUPrimitiveTopology_TriangleList; + rsDesc.stripIndexFormat = WGPUIndexFormat_Uint32; + rsDesc.frontFace = WGPUFrontFace_CW; + rsDesc.cullMode = WGPUCullMode_None; + + WGPUMultisampleState multisample; + memset(&multisample, 0, sizeof(multisample)); + multisample.count = 1; + multisample.mask = 0xffffffff; + multisample.alphaToCoverageEnabled = false; + + WGPURenderPipelineDescriptor rpDesc; + memset(&rpDesc, 0, sizeof(rpDesc)); + rpDesc.layout = wgpuDeviceCreatePipelineLayout(device, &plDesc); + rpDesc.fragment = &fragmentDest; + rpDesc.vertex = vsDest; + rpDesc.multisample = multisample; + rpDesc.primitive = rsDesc; + pipe->impl.pipeline = wgpuDeviceCreateRenderPipeline(device, &rpDesc); +} diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/pipeline.h b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/pipeline.h new file mode 100644 index 000000000..fb006092d --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/pipeline.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + WGPURenderPipeline pipeline; +} PipelineState5Impl; + +typedef struct { + WGPUComputePipeline pipeline; +} ComputePipelineState5Impl; + +typedef struct { + int vertexOffset; + int fragmentOffset; + int computeOffset; +} ConstantLocation5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/rendertarget.c b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/rendertarget.c new file mode 100644 index 000000000..582e7028e --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/rendertarget.c @@ -0,0 +1,21 @@ +#include +#include + +void kinc_g5_render_target_init_with_multisampling(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) { + +} + +void kinc_g5_render_target_init_framebuffer_with_multisampling(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) {} + +void kinc_g5_render_target_init_cube_with_multisampling(kinc_g5_render_target_t *render_target, int cubeMapSize, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel) {} + +void kinc_g5_render_target_destroy(kinc_g5_render_target_t *renderTarget) {} + +void kinc_g5_render_target_set_depth_stencil_from(kinc_g5_render_target_t *renderTarget, kinc_g5_render_target_t *source) {} + +void kinc_g5_render_target_get_pixels(kinc_g5_render_target_t *renderTarget, uint8_t *data) {} + +void kinc_g5_render_target_generate_mipmaps(kinc_g5_render_target_t *renderTarget, int levels) {} diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/rendertarget.h b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/rendertarget.h new file mode 100644 index 000000000..26ed2667b --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/rendertarget.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + WGPUTexture texture; +} RenderTarget5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/sampler.c b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/sampler.c new file mode 100644 index 000000000..c3d6c4006 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/sampler.c @@ -0,0 +1,5 @@ +#include + +void kinc_g5_sampler_init(kinc_g5_sampler_t *sampler, const kinc_g5_sampler_options_t *options) {} + +void kinc_g5_sampler_destroy(kinc_g5_sampler_t *sampler) {} diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/sampler.h b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/sampler.h new file mode 100644 index 000000000..01d504802 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/sampler.h @@ -0,0 +1,5 @@ +#pragma once + +typedef struct kinc_g5_sampler_impl { + int a; +} kinc_g5_sampler_impl_t; diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/shader.c b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/shader.c new file mode 100644 index 000000000..4b769e518 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/shader.c @@ -0,0 +1,19 @@ +#include + +#include + +extern WGPUDevice device; + +void kinc_g5_shader_init(kinc_g5_shader_t *shader, const void *source, size_t length, kinc_g5_shader_type_t type) { + WGPUShaderModuleSPIRVDescriptor smSpirvDesc; + memset(&smSpirvDesc, 0, sizeof(smSpirvDesc)); + smSpirvDesc.chain.sType = WGPUSType_ShaderModuleSPIRVDescriptor; + smSpirvDesc.codeSize = length / 4; + smSpirvDesc.code = source; + WGPUShaderModuleDescriptor smDesc; + memset(&smDesc, 0, sizeof(smDesc)); + smDesc.nextInChain = &smSpirvDesc; + shader->impl.module = wgpuDeviceCreateShaderModule(device, &smDesc); +} + +void kinc_g5_shader_destroy(kinc_g5_shader_t *shader) {} diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/shader.h b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/shader.h new file mode 100644 index 000000000..8a7036e59 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/shader.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct WGPUShaderModuleImpl; + +typedef struct { + WGPUShaderModule module; +} Shader5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/texture.c b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/texture.c new file mode 100644 index 000000000..dc5b58327 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/texture.c @@ -0,0 +1,55 @@ +#include + +void kinc_g5_texture_init(kinc_g5_texture_t *texture, int width, int height, kinc_image_format_t format) { + + // WGPUExtent3D size = {}; + // size.width = kinc_width(); + // size.height = kinc_height(); + // size.depth = 1; + + // WGPUTextureDescriptor tDesc = {}; + // tDesc.sampleCount = 1; + // tDesc.format = WGPUTextureFormat_BGRA8Unorm; + // tDesc.usage = WGPUTextureUsage_OutputAttachment; + // tDesc.size = size; + // tDesc.dimension = WGPUTextureDimension_2D; + // tDesc.mipLevelCount = 1; + // tDesc.arrayLayerCount = 1; + // WGPUTexture texture = wgpuDeviceCreateTexture(device, &tDesc); + + // WGPUTextureViewDescriptor tvDesc = {}; + // tvDesc.format = WGPUTextureFormat_BGRA8Unorm; + // tvDesc.dimension = WGPUTextureViewDimension_2D; + // WGPUTextureView textureView = wgpuTextureCreateView(texture, &tvDesc); + +} +void kinc_g5_texture_init3d(kinc_g5_texture_t *texture, int width, int height, int depth, kinc_image_format_t format) {} +void kinc_g5_texture_init_from_image(kinc_g5_texture_t *texture, kinc_image_t *image) {} +void kinc_g5_texture_init_from_encoded_data(kinc_g5_texture_t *texture, void *data, int size, const char *format, bool readable) {} +void kinc_g5_texture_init_from_data(kinc_g5_texture_t *texture, void *data, int width, int height, int format, bool readable) {} +void kinc_g5_texture_init_non_sampled_access(kinc_g5_texture_t *texture, int width, int height, kinc_image_format_t format) {} +void kinc_g5_texture_destroy(kinc_g5_texture_t *texture) {} + +// void Texture5Impl::unmipmap() { +// mipmap = false; +//} + +// void Graphics5::Texture::_set(TextureUnit unit) {} + +// void Texture5Impl::unset() {} + +uint8_t *kinc_g5_texture_lock(kinc_g5_texture_t *texture) { + return NULL; +} + +void kinc_g5_texture_unlock(kinc_g5_texture_t *texture) {} + +void kinc_g5_texture_clear(kinc_g5_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color) {} + +int kinc_g5_texture_stride(kinc_g5_texture_t *texture) { + return 32; +} + +void kinc_g5_texture_generate_mipmaps(kinc_g5_texture_t *texture, int levels) {} + +void kinc_g5_texture_set_mipmap(kinc_g5_texture_t *texture, kinc_image_t *mipmap, int level) {} diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/texture.h b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/texture.h new file mode 100644 index 000000000..d835069cd --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/texture.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + WGPUTexture texture; +} Texture5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/vertexbuffer.c b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/vertexbuffer.c new file mode 100644 index 000000000..ad7e81b0b --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/vertexbuffer.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +extern WGPUDevice device; + +kinc_g5_vertex_buffer_t *kinc_g5_internal_current_vertex_buffer = NULL; + +void kinc_g5_vertex_buffer_init(kinc_g5_vertex_buffer_t *buffer, int count, kinc_g5_vertex_structure_t *structure, bool gpuMemory, int instanceDataStepRate) { + buffer->impl.count = count; + buffer->impl.stride = 0; + for (int i = 0; i < structure->size; ++i) { + buffer->impl.stride += kinc_g4_vertex_data_size(structure->elements[i].data); + } +} + +void kinc_g5_vertex_buffer_destroy(kinc_g5_vertex_buffer_t *buffer) { + +} + +float *kinc_g5_vertex_buffer_lock_all(kinc_g5_vertex_buffer_t *buffer) { + WGPUBufferDescriptor bDesc; + memset(&bDesc, 0, sizeof(bDesc)); + bDesc.size = buffer->impl.count * buffer->impl.stride * sizeof(float); + bDesc.usage = WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst; + bDesc.mappedAtCreation = true; + buffer->impl.buffer = wgpuDeviceCreateBuffer(device, &bDesc); + return wgpuBufferGetMappedRange(buffer->impl.buffer, 0, bDesc.size); +} + +float *kinc_g5_vertex_buffer_lock(kinc_g5_vertex_buffer_t *buffer, int start, int count) { + return NULL; +} + +void kinc_g5_vertex_buffer_unlock_all(kinc_g5_vertex_buffer_t *buffer) { + wgpuBufferUnmap(buffer->impl.buffer); +} + +void kinc_g5_vertex_buffer_unlock(kinc_g5_vertex_buffer_t* buffer, int count) { + +} + +int kinc_g5_vertex_buffer_count(kinc_g5_vertex_buffer_t *buffer) { + return buffer->impl.count; +} + +int kinc_g5_vertex_buffer_stride(kinc_g5_vertex_buffer_t *buffer) { + return buffer->impl.stride; +} diff --git a/armorcore/sources/backends/webgpu/kinc/backend/graphics5/vertexbuffer.h b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/vertexbuffer.h new file mode 100644 index 000000000..44fec6792 --- /dev/null +++ b/armorcore/sources/backends/webgpu/kinc/backend/graphics5/vertexbuffer.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + WGPUBuffer buffer; + int count; + int stride; +} VertexBuffer5Impl; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/windows/kinc/backend/Windows.c.h b/armorcore/sources/backends/windows/kinc/backend/Windows.c.h new file mode 100644 index 000000000..7bd72ae10 --- /dev/null +++ b/armorcore/sources/backends/windows/kinc/backend/Windows.c.h @@ -0,0 +1 @@ +#include "Windows.h" diff --git a/armorcore/sources/backends/windows/kinc/backend/Windows.h b/armorcore/sources/backends/windows/kinc/backend/Windows.h new file mode 100644 index 000000000..dc72dda23 --- /dev/null +++ b/armorcore/sources/backends/windows/kinc/backend/Windows.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct HMONITOR__; +struct HWND__; + +int kinc_windows_get_display_for_monitor(struct HMONITOR__ *monitor); +bool kinc_windows_set_display_mode(int display_index, int width, int height, int bpp, int frequency); +void kinc_windows_restore_display(int display_index); +void kinc_windows_restore_displays(); +void kinc_windows_hide_windows(); +void kinc_windows_destroy_windows(); +struct HWND__ *kinc_windows_window_handle(int window_index); +int kinc_windows_window_index_from_hwnd(struct HWND__ *handle); +int kinc_windows_manual_width(int window); +int kinc_windows_manual_height(int window); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/windows/kinc/backend/base.c.h b/armorcore/sources/backends/windows/kinc/backend/base.c.h new file mode 100644 index 000000000..dde710bde --- /dev/null +++ b/armorcore/sources/backends/windows/kinc/backend/base.c.h @@ -0,0 +1,31 @@ +#ifdef KINC_NO_CLIB + +#ifndef NDEBUG +void _wassert(wchar_t const *message, wchar_t const *filename, unsigned line) { + __debugbreak(); +} + +void _RTC_CheckStackVars(void) {} + +void _RTC_InitBase(void) {} + +void _RTC_Shutdown(void) {} + +void _RTC_AllocaHelper(void) {} + +void _RTC_CheckStackVars2(void) {} + +void __GSHandlerCheck(void) {} + +void __fastcall __security_check_cookie(_In_ uintptr_t _StackCookie) {} + +uintptr_t __security_cookie; + +int _fltused = 1; + +void __report_rangecheckfailure(void) {} + +void __chkstk(void) {} +#endif + +#endif diff --git a/armorcore/sources/backends/windows/kinc/backend/display.c.h b/armorcore/sources/backends/windows/kinc/backend/display.c.h new file mode 100644 index 000000000..b7226b457 --- /dev/null +++ b/armorcore/sources/backends/windows/kinc/backend/display.c.h @@ -0,0 +1,201 @@ +#include + +#include +#include +#include + +#undef RegisterClass + +#define MAXIMUM_DISPLAYS 16 + +typedef struct { + struct HMONITOR__ *monitor; + char name[32]; + bool primary, available, mode_changed; + int index, x, y, width, height, ppi, frequency, bpp; +} DisplayData; + +static DisplayData displays[MAXIMUM_DISPLAYS]; +static DEVMODEA original_modes[MAXIMUM_DISPLAYS]; +static int screen_counter = 0; +static bool display_initialized = false; + +typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE; +typedef HRESULT(WINAPI *GetDpiForMonitorType)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY); +static GetDpiForMonitorType MyGetDpiForMonitor = NULL; + +static BOOL CALLBACK EnumerationCallback(HMONITOR monitor, HDC hdc_unused, LPRECT rect_unused, LPARAM lparam) { + MONITORINFOEXA info; + memset(&info, 0, sizeof(MONITORINFOEXA)); + info.cbSize = sizeof(MONITORINFOEXA); + + if (GetMonitorInfoA(monitor, (MONITORINFO *)&info) == FALSE) { + return FALSE; + } + + int free_slot = 0; + for (; free_slot < MAXIMUM_DISPLAYS; ++free_slot) { + if (displays[free_slot].monitor == monitor) { + return FALSE; + } + + if (displays[free_slot].monitor == NULL) { + break; + } + } + + DisplayData *display = &displays[free_slot]; + strncpy(display->name, info.szDevice, 31); + display->name[31] = 0; + display->index = free_slot; + display->monitor = monitor; + display->primary = (info.dwFlags & MONITORINFOF_PRIMARY) != 0; + display->available = true; + display->x = info.rcMonitor.left; + display->y = info.rcMonitor.top; + display->width = info.rcMonitor.right - info.rcMonitor.left; + display->height = info.rcMonitor.bottom - info.rcMonitor.top; + + HDC hdc = CreateDCA(NULL, display->name, NULL, NULL); + display->ppi = GetDeviceCaps(hdc, LOGPIXELSX); + int scale = GetDeviceCaps(hdc, SCALINGFACTORX); + DeleteDC(hdc); + + if (MyGetDpiForMonitor != NULL) { + unsigned dpiX, dpiY; + MyGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + display->ppi = (int)dpiX; + } + + memset(&original_modes[free_slot], 0, sizeof(DEVMODEA)); + original_modes[free_slot].dmSize = sizeof(DEVMODEA); + EnumDisplaySettingsA(display->name, ENUM_CURRENT_SETTINGS, &original_modes[free_slot]); + display->frequency = original_modes[free_slot].dmDisplayFrequency; + display->bpp = original_modes[free_slot].dmBitsPerPel; + + ++screen_counter; + return TRUE; +} + +void kinc_display_init() { + if (display_initialized) { + return; + } + HMODULE shcore = LoadLibraryA("Shcore.dll"); + if (shcore != NULL) { + MyGetDpiForMonitor = (GetDpiForMonitorType)GetProcAddress(shcore, "GetDpiForMonitor"); + } + memset(displays, 0, sizeof(DisplayData) * MAXIMUM_DISPLAYS); + EnumDisplayMonitors(NULL, NULL, EnumerationCallback, 0); + display_initialized = true; +} + +int kinc_windows_get_display_for_monitor(struct HMONITOR__ *monitor) { + for (int i = 0; i < MAXIMUM_DISPLAYS; ++i) { + if (displays[i].monitor == monitor) { + return i; + } + } + + kinc_error_message("Display for monitor not found"); + return -1; +} + +int kinc_count_displays() { + return screen_counter; +} + +int kinc_primary_display() { + for (int i = 0; i < MAXIMUM_DISPLAYS; ++i) { + DisplayData *display = &displays[i]; + + if (display->available && display->primary) { + return i; + } + } + + kinc_error_message("No primary display defined"); + return -1; +} + +kinc_display_mode_t kinc_display_available_mode(int display_index, int mode_index) { + DEVMODEA dev_mode = {0}; + dev_mode.dmSize = sizeof(DEVMODEA); + EnumDisplaySettingsA(displays[display_index].name, mode_index, &dev_mode); + kinc_display_mode_t mode; + mode.x = displays[display_index].x; + mode.y = displays[display_index].y; + mode.width = dev_mode.dmPelsWidth; + mode.height = dev_mode.dmPelsHeight; + mode.frequency = dev_mode.dmDisplayFrequency; + mode.bits_per_pixel = dev_mode.dmBitsPerPel; + mode.pixels_per_inch = displays[display_index].ppi * mode.width / original_modes[display_index].dmPelsWidth; + return mode; +} + +int kinc_display_count_available_modes(int display_index) { + DEVMODEA dev_mode = {0}; + dev_mode.dmSize = sizeof(DEVMODEA); + int i = 0; + for (; EnumDisplaySettingsA(displays[display_index].name, i, &dev_mode) != FALSE; ++i) + ; + return i; +} + +bool kinc_windows_set_display_mode(int display_index, int width, int height, int bpp, int frequency) { + DisplayData *display = &displays[display_index]; + display->mode_changed = true; + DEVMODEA mode = {0}; + mode.dmSize = sizeof(mode); + strcpy((char *)mode.dmDeviceName, display->name); + mode.dmPelsWidth = width; + mode.dmPelsHeight = height; + mode.dmBitsPerPel = bpp; + mode.dmDisplayFrequency = frequency; + mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; + + bool success = ChangeDisplaySettingsA(&mode, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL; + + if (!success) { + mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + success = ChangeDisplaySettingsA(&mode, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL; + } + + return success; +} + +void kinc_windows_restore_display(int display) { + if (displays[display].mode_changed) { + ChangeDisplaySettingsA(&original_modes[display], 0); + } +} + +void kinc_windows_restore_displays() { + for (int i = 0; i < MAXIMUM_DISPLAYS; ++i) { + kinc_windows_restore_display(i); + } +} + +bool kinc_display_available(int display_index) { + if (display_index < 0 || display_index >= MAXIMUM_DISPLAYS) { + return false; + } + return displays[display_index].available; +} + +const char *kinc_display_name(int display_index) { + return displays[display_index].name; +} + +kinc_display_mode_t kinc_display_current_mode(int display_index) { + DisplayData *display = &displays[display_index]; + kinc_display_mode_t mode; + mode.x = display->x; + mode.y = display->y; + mode.width = display->width; + mode.height = display->height; + mode.pixels_per_inch = display->ppi; + mode.frequency = display->frequency; + mode.bits_per_pixel = display->bpp; + return mode; +} diff --git a/armorcore/sources/backends/windows/kinc/backend/http.c.h b/armorcore/sources/backends/windows/kinc/backend/http.c.h new file mode 100644 index 000000000..a0d93a11f --- /dev/null +++ b/armorcore/sources/backends/windows/kinc/backend/http.c.h @@ -0,0 +1,111 @@ +#include +#include + +#include +#include + +#include + +static const wchar_t *convert(int method) { + switch (method) { + case KINC_HTTP_GET: + default: + return L"GET"; + case KINC_HTTP_POST: + return L"POST"; + case KINC_HTTP_PUT: + return L"PUT"; + case KINC_HTTP_DELETE: + return L"DELETE"; + } +} + +static char *returnData = NULL; +static int returnDataSize = 0; + +void kinc_http_request(const char *url, const char *path, const char *data, int port, bool secure, int method, const char *header, + kinc_http_callback_t callback, void *callbackdata) { + // based on https://docs.microsoft.com/en-us/windows/desktop/winhttp/winhttp-sessions-overview + + HINTERNET hSession = WinHttpOpen(L"WinHTTP via Kore/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); + + HINTERNET hConnect = NULL; + if (hSession) { + wchar_t wurl[4096]; + MultiByteToWideChar(CP_UTF8, 0, url, -1, wurl, 4096); + hConnect = WinHttpConnect(hSession, wurl, port, 0); + } + + HINTERNET hRequest = NULL; + if (hConnect) { + wchar_t wpath[4096]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, 4096); + hRequest = + WinHttpOpenRequest(hConnect, convert(method), wpath, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, secure ? WINHTTP_FLAG_SECURE : 0); + } + + BOOL bResults = FALSE; + + if (hRequest) { + wchar_t wheader[4096]; + if (header) { + MultiByteToWideChar(CP_UTF8, 0, header, -1, wheader, 4096); + } + DWORD optionalLength = (data != 0 && strlen(data) > 0) ? (DWORD)strlen(data) : 0; + bResults = WinHttpSendRequest(hRequest, header == 0 ? WINHTTP_NO_ADDITIONAL_HEADERS : wheader, header == 0 ? 0 : -1L, + data == 0 ? WINHTTP_NO_REQUEST_DATA : (LPVOID)data, optionalLength, optionalLength, 0); + } + + if (bResults) + bResults = WinHttpReceiveResponse(hRequest, NULL); + + int returnDataIndex = 0; + if (bResults) { + DWORD dwSize; + do { + dwSize = 0; + if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Error %d in WinHttpQueryDataAvailable.\n", GetLastError()); + } + + if ((int)dwSize + 1 > returnDataSize - returnDataIndex) { + int newReturnDataSize = (returnDataIndex + dwSize + 1) * 2; + char *newReturnData = (char *)malloc(newReturnDataSize); + if (newReturnData == 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Out of memory\n"); + } + memcpy(newReturnData, returnData, returnDataSize); + returnDataSize = newReturnDataSize; + returnData = newReturnData; + } + + DWORD dwDownloaded = 0; + if (!WinHttpReadData(hRequest, (LPVOID)(&returnData[returnDataIndex]), dwSize, &dwDownloaded)) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Error %d in WinHttpReadData.\n", GetLastError()); + } + returnDataIndex += dwSize; + } while (dwSize > 0); + } + else { + callback(1, 404, NULL, callbackdata); + return; + } + + returnData[returnDataIndex] = 0; + + if (!bResults) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Error %d has occurred.\n", GetLastError()); + } + + if (hRequest) { + WinHttpCloseHandle(hRequest); + } + if (hConnect) { + WinHttpCloseHandle(hConnect); + } + if (hSession) { + WinHttpCloseHandle(hSession); + } + + callback(0, 200, returnData, callbackdata); +} diff --git a/armorcore/sources/backends/windows/kinc/backend/mouse.c.h b/armorcore/sources/backends/windows/kinc/backend/mouse.c.h new file mode 100644 index 000000000..364ac3449 --- /dev/null +++ b/armorcore/sources/backends/windows/kinc/backend/mouse.c.h @@ -0,0 +1,52 @@ +#include +#include +#include + +#include + +void kinc_internal_mouse_lock(int window) { + kinc_mouse_hide(); + HWND handle = kinc_windows_window_handle(window); + SetCapture(handle); + RECT rect; + GetWindowRect(handle, &rect); + ClipCursor(&rect); +} + +void kinc_internal_mouse_unlock(void) { + kinc_mouse_show(); + ReleaseCapture(); + ClipCursor(NULL); +} + +bool kinc_mouse_can_lock(void) { + return true; +} + +void kinc_mouse_show() { + // Work around the internal counter of ShowCursor + while (ShowCursor(true) < 0) { + } +} + +void kinc_mouse_hide() { + // Work around the internal counter of ShowCursor + while (ShowCursor(false) >= 0) { + } +} + +void kinc_mouse_set_position(int window, int x, int y) { + POINT point; + point.x = x; + point.y = y; + ClientToScreen(kinc_windows_window_handle(window), &point); + SetCursorPos(point.x, point.y); +} + +void kinc_mouse_get_position(int window, int *x, int *y) { + POINT point; + GetCursorPos(&point); + ScreenToClient(kinc_windows_window_handle(window), &point); + *x = point.x; + *y = point.y; +} diff --git a/armorcore/sources/backends/windows/kinc/backend/system.c.h b/armorcore/sources/backends/windows/kinc/backend/system.c.h new file mode 100644 index 000000000..373dba4b6 --- /dev/null +++ b/armorcore/sources/backends/windows/kinc/backend/system.c.h @@ -0,0 +1,1463 @@ +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DIRECTINPUT_VERSION 0x0800 +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef KINC_G4ONG5 +#define Graphics Graphics5 +#elif KINC_G4 +#define Graphics Graphics4 +#else +#define Graphics Graphics3 +#endif + +__declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; +__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; +void kinc_internal_resize(int window, int width, int height); + +typedef BOOL(WINAPI *GetPointerInfoType)(UINT32 pointerId, POINTER_INFO *pointerInfo); +static GetPointerInfoType MyGetPointerInfo = NULL; +typedef BOOL(WINAPI *GetPointerPenInfoType)(UINT32 pointerId, POINTER_PEN_INFO *penInfo); +static GetPointerPenInfoType MyGetPointerPenInfo = NULL; +typedef BOOL(WINAPI *EnableNonClientDpiScalingType)(HWND hwnd); +static EnableNonClientDpiScalingType MyEnableNonClientDpiScaling = NULL; + +#define MAX_TOUCH_POINTS 10 + +#define KINC_DINPUT_MAX_COUNT 8 + +struct touchpoint { + int sysID; + int x; + int y; +}; + +static struct touchpoint touchPoints[MAX_TOUCH_POINTS]; +static int mouseX, mouseY; +static bool keyPressed[256]; +static int keyTranslated[256]; // http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx + +static int GetTouchIndex(int dwID) { + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + if (touchPoints[i].sysID == dwID) { + return i; + } + } + return -1; +} + +static int GetAddTouchIndex(int dwID) { + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + if (touchPoints[i].sysID == dwID) { + return i; + } + } + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + if (touchPoints[i].sysID == -1) { + touchPoints[i].sysID = dwID; + return i; + } + } + return -1; +} + +static void ReleaseTouchIndex(int dwID) { + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + if (touchPoints[i].sysID == dwID) { + touchPoints[i].sysID = -1; + touchPoints[i].x = -1; + touchPoints[i].y = -1; + } + } +} + +static void initKeyTranslation() { + for (int i = 0; i < 256; ++i) + keyTranslated[i] = KINC_KEY_UNKNOWN; + + keyTranslated[VK_BACK] = KINC_KEY_BACKSPACE; + keyTranslated[VK_TAB] = KINC_KEY_TAB; + keyTranslated[VK_CLEAR] = KINC_KEY_CLEAR; + keyTranslated[VK_RETURN] = KINC_KEY_RETURN; + keyTranslated[VK_SHIFT] = KINC_KEY_SHIFT; + keyTranslated[VK_CONTROL] = KINC_KEY_CONTROL; + keyTranslated[VK_MENU] = KINC_KEY_ALT; + keyTranslated[VK_PAUSE] = KINC_KEY_PAUSE; + keyTranslated[VK_CAPITAL] = KINC_KEY_CAPS_LOCK; + keyTranslated[VK_KANA] = KINC_KEY_KANA; + // keyTranslated[VK_HANGUEL] + keyTranslated[VK_HANGUL] = KINC_KEY_HANGUL; + keyTranslated[VK_JUNJA] = KINC_KEY_JUNJA; + keyTranslated[VK_FINAL] = KINC_KEY_FINAL; + keyTranslated[VK_HANJA] = KINC_KEY_HANJA; + keyTranslated[VK_KANJI] = KINC_KEY_KANJI; + keyTranslated[VK_ESCAPE] = KINC_KEY_ESCAPE; + // keyTranslated[VK_CONVERT] + // keyTranslated[VK_NONCONVERT + // keyTranslated[VK_ACCEPT + // keyTranslated[VK_MODECHANGE + keyTranslated[VK_SPACE] = KINC_KEY_SPACE; + keyTranslated[VK_PRIOR] = KINC_KEY_PAGE_UP; + keyTranslated[VK_NEXT] = KINC_KEY_PAGE_DOWN; + keyTranslated[VK_END] = KINC_KEY_END; + keyTranslated[VK_HOME] = KINC_KEY_HOME; + keyTranslated[VK_LEFT] = KINC_KEY_LEFT; + keyTranslated[VK_UP] = KINC_KEY_UP; + keyTranslated[VK_RIGHT] = KINC_KEY_RIGHT; + keyTranslated[VK_DOWN] = KINC_KEY_DOWN; + // keyTranslated[VK_SELECT + keyTranslated[VK_PRINT] = KINC_KEY_PRINT; + // keyTranslated[VK_EXECUTE + // keyTranslated[VK_SNAPSHOT + keyTranslated[VK_INSERT] = KINC_KEY_INSERT; + keyTranslated[VK_DELETE] = KINC_KEY_DELETE; + keyTranslated[VK_HELP] = KINC_KEY_HELP; + keyTranslated[0x30] = KINC_KEY_0; + keyTranslated[0x31] = KINC_KEY_1; + keyTranslated[0x32] = KINC_KEY_2; + keyTranslated[0x33] = KINC_KEY_3; + keyTranslated[0x34] = KINC_KEY_4; + keyTranslated[0x35] = KINC_KEY_5; + keyTranslated[0x36] = KINC_KEY_6; + keyTranslated[0x37] = KINC_KEY_7; + keyTranslated[0x38] = KINC_KEY_8; + keyTranslated[0x39] = KINC_KEY_9; + keyTranslated[0x41] = KINC_KEY_A; + keyTranslated[0x42] = KINC_KEY_B; + keyTranslated[0x43] = KINC_KEY_C; + keyTranslated[0x44] = KINC_KEY_D; + keyTranslated[0x45] = KINC_KEY_E; + keyTranslated[0x46] = KINC_KEY_F; + keyTranslated[0x47] = KINC_KEY_G; + keyTranslated[0x48] = KINC_KEY_H; + keyTranslated[0x49] = KINC_KEY_I; + keyTranslated[0x4A] = KINC_KEY_J; + keyTranslated[0x4B] = KINC_KEY_K; + keyTranslated[0x4C] = KINC_KEY_L; + keyTranslated[0x4D] = KINC_KEY_M; + keyTranslated[0x4E] = KINC_KEY_N; + keyTranslated[0x4F] = KINC_KEY_O; + keyTranslated[0x50] = KINC_KEY_P; + keyTranslated[0x51] = KINC_KEY_Q; + keyTranslated[0x52] = KINC_KEY_R; + keyTranslated[0x53] = KINC_KEY_S; + keyTranslated[0x54] = KINC_KEY_T; + keyTranslated[0x55] = KINC_KEY_U; + keyTranslated[0x56] = KINC_KEY_V; + keyTranslated[0x57] = KINC_KEY_W; + keyTranslated[0x58] = KINC_KEY_X; + keyTranslated[0x59] = KINC_KEY_Y; + keyTranslated[0x5A] = KINC_KEY_Z; + keyTranslated[VK_LWIN] = KINC_KEY_WIN; + keyTranslated[VK_RWIN] = KINC_KEY_WIN; + keyTranslated[VK_APPS] = KINC_KEY_CONTEXT_MENU; + // keyTranslated[VK_SLEEP + keyTranslated[VK_NUMPAD0] = KINC_KEY_NUMPAD_0; + keyTranslated[VK_NUMPAD1] = KINC_KEY_NUMPAD_1; + keyTranslated[VK_NUMPAD2] = KINC_KEY_NUMPAD_2; + keyTranslated[VK_NUMPAD3] = KINC_KEY_NUMPAD_3; + keyTranslated[VK_NUMPAD4] = KINC_KEY_NUMPAD_4; + keyTranslated[VK_NUMPAD5] = KINC_KEY_NUMPAD_5; + keyTranslated[VK_NUMPAD6] = KINC_KEY_NUMPAD_6; + keyTranslated[VK_NUMPAD7] = KINC_KEY_NUMPAD_7; + keyTranslated[VK_NUMPAD8] = KINC_KEY_NUMPAD_8; + keyTranslated[VK_NUMPAD9] = KINC_KEY_NUMPAD_9; + keyTranslated[VK_MULTIPLY] = KINC_KEY_MULTIPLY; + keyTranslated[VK_ADD] = KINC_KEY_ADD; + // keyTranslated[VK_SEPARATOR + keyTranslated[VK_SUBTRACT] = KINC_KEY_SUBTRACT; + keyTranslated[VK_DECIMAL] = KINC_KEY_DECIMAL; + keyTranslated[VK_DIVIDE] = KINC_KEY_DIVIDE; + keyTranslated[VK_F1] = KINC_KEY_F1; + keyTranslated[VK_F2] = KINC_KEY_F2; + keyTranslated[VK_F3] = KINC_KEY_F3; + keyTranslated[VK_F4] = KINC_KEY_F4; + keyTranslated[VK_F5] = KINC_KEY_F5; + keyTranslated[VK_F6] = KINC_KEY_F6; + keyTranslated[VK_F7] = KINC_KEY_F7; + keyTranslated[VK_F8] = KINC_KEY_F8; + keyTranslated[VK_F9] = KINC_KEY_F9; + keyTranslated[VK_F10] = KINC_KEY_F10; + keyTranslated[VK_F11] = KINC_KEY_F11; + keyTranslated[VK_F12] = KINC_KEY_F12; + keyTranslated[VK_F13] = KINC_KEY_F13; + keyTranslated[VK_F14] = KINC_KEY_F14; + keyTranslated[VK_F15] = KINC_KEY_F15; + keyTranslated[VK_F16] = KINC_KEY_F16; + keyTranslated[VK_F17] = KINC_KEY_F17; + keyTranslated[VK_F18] = KINC_KEY_F18; + keyTranslated[VK_F19] = KINC_KEY_F19; + keyTranslated[VK_F20] = KINC_KEY_F20; + keyTranslated[VK_F21] = KINC_KEY_F21; + keyTranslated[VK_F22] = KINC_KEY_F22; + keyTranslated[VK_F23] = KINC_KEY_F23; + keyTranslated[VK_F24] = KINC_KEY_F24; + keyTranslated[VK_NUMLOCK] = KINC_KEY_NUM_LOCK; + keyTranslated[VK_SCROLL] = KINC_KEY_SCROLL_LOCK; + // 0x92-96 //OEM specific + keyTranslated[VK_LSHIFT] = KINC_KEY_SHIFT; + keyTranslated[VK_RSHIFT] = KINC_KEY_SHIFT; + keyTranslated[VK_LCONTROL] = KINC_KEY_CONTROL; + keyTranslated[VK_RCONTROL] = KINC_KEY_CONTROL; + // keyTranslated[VK_LMENU + // keyTranslated[VK_RMENU + // keyTranslated[VK_BROWSER_BACK + // keyTranslated[VK_BROWSER_FORWARD + // keyTranslated[VK_BROWSER_REFRESH + // keyTranslated[VK_BROWSER_STOP + // keyTranslated[VK_BROWSER_SEARCH + // keyTranslated[VK_BROWSER_FAVORITES + // keyTranslated[VK_BROWSER_HOME + // keyTranslated[VK_VOLUME_MUTE + // keyTranslated[VK_VOLUME_DOWN + // keyTranslated[VK_VOLUME_UP + // keyTranslated[VK_MEDIA_NEXT_TRACK + // keyTranslated[VK_MEDIA_PREV_TRACK + // keyTranslated[VK_MEDIA_STOP + // keyTranslated[VK_MEDIA_PLAY_PAUSE + // keyTranslated[VK_LAUNCH_MAIL + // keyTranslated[VK_LAUNCH_MEDIA_SELECT + // keyTranslated[VK_LAUNCH_APP1 + // keyTranslated[VK_LAUNCH_APP2 + keyTranslated[VK_OEM_1] = KINC_KEY_SEMICOLON; // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key + keyTranslated[VK_OEM_PLUS] = KINC_KEY_PLUS; + keyTranslated[VK_OEM_COMMA] = KINC_KEY_COMMA; + keyTranslated[VK_OEM_MINUS] = KINC_KEY_HYPHEN_MINUS; + keyTranslated[VK_OEM_PERIOD] = KINC_KEY_PERIOD; + keyTranslated[VK_OEM_2] = KINC_KEY_SLASH; // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key + keyTranslated[VK_OEM_3] = KINC_KEY_BACK_QUOTE; // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key + keyTranslated[VK_OEM_4] = KINC_KEY_OPEN_BRACKET; // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key + keyTranslated[VK_OEM_5] = KINC_KEY_BACK_SLASH; // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\|' key + keyTranslated[VK_OEM_6] = KINC_KEY_CLOSE_BRACKET; // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key + keyTranslated[VK_OEM_7] = KINC_KEY_QUOTE; // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the + // 'single-quote/double-quote' key keyTranslated[VK_OEM_8 //Used for miscellaneous characters; it can vary by + // keyboard. keyTranslated[0xE1 //OEM specific keyTranslated[VK_OEM_102 //Either the angle bracket key or the + // backslash key on the RT 102-key keyboard 0xE3-E4 //OEM specific keyTranslated[VK_PROCESSKEY 0xE6 //OEM specific + // keyTranslated[VK_PACKET //Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value + // used for non-keyboard input methods. + // 0xE9-F5 //OEM specific + // keyTranslated[VK_ATTN + // keyTranslated[VK_CRSEL + // keyTranslated[VK_EXSEL + // keyTranslated[VK_EREOF + // keyTranslated[VK_PLAY + // keyTranslated[VK_ZOOM + // keyTranslated[VK_NONAME + // keyTranslated[VK_PA1 + // keyTranslated[PA1 key + // keyTranslated[VK_OEM_CLEAR +} + +static bool detectGamepad = true; +static bool gamepadFound = false; +static unsigned r = 0; + +static wchar_t toUnicode(WPARAM wParam, LPARAM lParam) { + wchar_t buffer[11]; + BYTE state[256]; + GetKeyboardState(state); + ToUnicode((UINT)wParam, (lParam >> 8) & 0xFFFFFF00, state, buffer, 10, 0); + return buffer[0]; +} + +#if !defined(KINC_DIRECT3D9) && !defined(KINC_DIRECT3D11) && !defined(KINC_DIRECT3D12) +#define HANDLE_ALT_ENTER +#endif + +static bool cursors_initialized = false; +static int cursor = 0; +#define NUM_CURSORS 14 +static HCURSOR cursors[NUM_CURSORS]; + +void kinc_mouse_set_cursor(int set_cursor) { + cursor = set_cursor >= NUM_CURSORS ? 0 : set_cursor; + if (cursors_initialized) { + SetCursor(cursors[cursor]); + } +} + +LRESULT WINAPI KoreWindowsMessageProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + int windowId; + DWORD pointerId; + POINTER_INFO pointerInfo = {0}; + POINTER_PEN_INFO penInfo = {0}; + static bool controlDown = false; +#ifdef HANDLE_ALT_ENTER + static bool altDown = false; +#endif + static int last_window_width = -1; + static int last_window_height = -1; + static int last_window_x = INT_MIN; + static int last_window_y = INT_MIN; + + switch (msg) { + case WM_NCCREATE: + if (MyEnableNonClientDpiScaling != NULL) { + MyEnableNonClientDpiScaling(hWnd); + } + break; + case WM_DPICHANGED: { + int window = kinc_windows_window_index_from_hwnd(hWnd); + if (window >= 0) { + kinc_internal_call_ppi_changed_callback(window, LOWORD(wParam)); + } + break; + } + case WM_MOVE: + case WM_MOVING: + case WM_SIZING: + // Scheduler::breakTime(); + break; + case WM_SIZE: { + int window = kinc_windows_window_index_from_hwnd(hWnd); + if (window >= 0) { + int width = LOWORD(lParam); + int height = HIWORD(lParam); + kinc_internal_resize(window, width, height); + kinc_internal_call_resize_callback(window, width, height); + } + break; + } + case WM_CLOSE: { + int window_index = kinc_windows_window_index_from_hwnd(hWnd); + if (kinc_internal_call_close_callback(window_index)) { + kinc_window_destroy(window_index); + if (kinc_count_windows() <= 0) { + kinc_stop(); + return 0; + } + } + return 0; + } + case WM_ERASEBKGND: + return 1; + case WM_ACTIVATE: + if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) { + kinc_internal_mouse_window_activated(kinc_windows_window_index_from_hwnd(hWnd)); + kinc_internal_foreground_callback(); + } + else { + kinc_internal_mouse_window_deactivated(kinc_windows_window_index_from_hwnd(hWnd)); + kinc_internal_background_callback(); +#ifdef HANDLE_ALT_ENTER + altDown = false; +#endif + } + RegisterTouchWindow(hWnd, 0); + break; + case WM_MOUSELEAVE: + windowId = kinc_windows_window_index_from_hwnd(hWnd); + //**windows[windowId]->isMouseInside = false; + kinc_internal_mouse_trigger_leave_window(windowId); + break; + case WM_MOUSEMOVE: + windowId = kinc_windows_window_index_from_hwnd(hWnd); + /*if (!windows[windowId]->isMouseInside) { + windows[windowId]->isMouseInside = true; + TRACKMOUSEEVENT tme; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hWnd; + TrackMouseEvent(&tme); + }*/ + mouseX = GET_X_LPARAM(lParam); + mouseY = GET_Y_LPARAM(lParam); + kinc_internal_mouse_trigger_move(windowId, mouseX, mouseY); + break; + case WM_CREATE: + cursors[0] = LoadCursor(0, IDC_ARROW); + cursors[1] = LoadCursor(0, IDC_HAND); + cursors[2] = LoadCursor(0, IDC_IBEAM); + cursors[3] = LoadCursor(0, IDC_SIZEWE); + cursors[4] = LoadCursor(0, IDC_SIZENS); + cursors[5] = LoadCursor(0, IDC_SIZENESW); + cursors[6] = LoadCursor(0, IDC_SIZENWSE); + cursors[7] = LoadCursor(0, IDC_SIZENWSE); + cursors[8] = LoadCursor(0, IDC_SIZENESW); + cursors[9] = LoadCursor(0, IDC_SIZEALL); + cursors[10] = LoadCursor(0, IDC_SIZEALL); + cursors[11] = LoadCursor(0, IDC_NO); + cursors[12] = LoadCursor(0, IDC_WAIT); + cursors[13] = LoadCursor(0, IDC_CROSS); + cursors_initialized = true; + if (cursor != 0) { + SetCursor(cursors[cursor]); + } + return TRUE; + case WM_SETCURSOR: + if (LOWORD(lParam) == HTCLIENT) { + SetCursor(cursors[cursor]); + return TRUE; + } + break; + case WM_LBUTTONDOWN: + if (!kinc_mouse_is_locked()) + SetCapture(hWnd); + mouseX = GET_X_LPARAM(lParam); + mouseY = GET_Y_LPARAM(lParam); + kinc_internal_mouse_trigger_press(kinc_windows_window_index_from_hwnd(hWnd), 0, mouseX, mouseY); + break; + case WM_LBUTTONUP: + if (!kinc_mouse_is_locked()) + ReleaseCapture(); + mouseX = GET_X_LPARAM(lParam); + mouseY = GET_Y_LPARAM(lParam); + kinc_internal_mouse_trigger_release(kinc_windows_window_index_from_hwnd(hWnd), 0, mouseX, mouseY); + break; + case WM_RBUTTONDOWN: + mouseX = GET_X_LPARAM(lParam); + mouseY = GET_Y_LPARAM(lParam); + kinc_internal_mouse_trigger_press(kinc_windows_window_index_from_hwnd(hWnd), 1, mouseX, mouseY); + break; + case WM_RBUTTONUP: + mouseX = GET_X_LPARAM(lParam); + mouseY = GET_Y_LPARAM(lParam); + kinc_internal_mouse_trigger_release(kinc_windows_window_index_from_hwnd(hWnd), 1, mouseX, mouseY); + break; + case WM_MBUTTONDOWN: + mouseX = GET_X_LPARAM(lParam); + mouseY = GET_Y_LPARAM(lParam); + kinc_internal_mouse_trigger_press(kinc_windows_window_index_from_hwnd(hWnd), 2, mouseX, mouseY); + break; + case WM_MBUTTONUP: + mouseX = GET_X_LPARAM(lParam); + mouseY = GET_Y_LPARAM(lParam); + kinc_internal_mouse_trigger_release(kinc_windows_window_index_from_hwnd(hWnd), 2, mouseX, mouseY); + break; + case WM_XBUTTONDOWN: + mouseX = GET_X_LPARAM(lParam); + mouseY = GET_Y_LPARAM(lParam); + kinc_internal_mouse_trigger_press(kinc_windows_window_index_from_hwnd(hWnd), HIWORD(wParam) + 2, mouseX, mouseY); + break; + case WM_XBUTTONUP: + mouseX = GET_X_LPARAM(lParam); + mouseY = GET_Y_LPARAM(lParam); + kinc_internal_mouse_trigger_release(kinc_windows_window_index_from_hwnd(hWnd), HIWORD(wParam) + 2, mouseX, mouseY); + break; + case WM_MOUSEWHEEL: + kinc_internal_mouse_trigger_scroll(kinc_windows_window_index_from_hwnd(hWnd), GET_WHEEL_DELTA_WPARAM(wParam) / -120); + break; + case WM_POINTERDOWN: + pointerId = GET_POINTERID_WPARAM(wParam); + MyGetPointerInfo(pointerId, &pointerInfo); + if (pointerInfo.pointerType == PT_PEN) { + MyGetPointerPenInfo(pointerId, &penInfo); + ScreenToClient(hWnd, &pointerInfo.ptPixelLocation); + kinc_internal_pen_trigger_press(kinc_windows_window_index_from_hwnd(hWnd), pointerInfo.ptPixelLocation.x, pointerInfo.ptPixelLocation.y, + penInfo.pressure / 1024.0f); + } + break; + case WM_POINTERUP: + pointerId = GET_POINTERID_WPARAM(wParam); + MyGetPointerInfo(pointerId, &pointerInfo); + if (pointerInfo.pointerType == PT_PEN) { + MyGetPointerPenInfo(pointerId, &penInfo); + ScreenToClient(hWnd, &pointerInfo.ptPixelLocation); + kinc_internal_pen_trigger_release(kinc_windows_window_index_from_hwnd(hWnd), pointerInfo.ptPixelLocation.x, pointerInfo.ptPixelLocation.y, + penInfo.pressure / 1024.0f); + } + break; + case WM_POINTERUPDATE: + pointerId = GET_POINTERID_WPARAM(wParam); + MyGetPointerInfo(pointerId, &pointerInfo); + if (pointerInfo.pointerType == PT_PEN) { + MyGetPointerPenInfo(pointerId, &penInfo); + ScreenToClient(hWnd, &pointerInfo.ptPixelLocation); + kinc_internal_pen_trigger_move(kinc_windows_window_index_from_hwnd(hWnd), pointerInfo.ptPixelLocation.x, pointerInfo.ptPixelLocation.y, + penInfo.pressure / 1024.0f); + } + break; + case WM_TOUCH: { + BOOL bHandled = FALSE; + UINT cInputs = LOWORD(wParam); + PTOUCHINPUT pInputs = _alloca(cInputs * sizeof(TOUCHINPUT)); + POINT ptInput; + int tindex; + if (pInputs) { + if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) { + for (int i = 0; i < (int)cInputs; i++) { + TOUCHINPUT ti = pInputs[i]; + if (ti.dwID != 0) { + ptInput.x = TOUCH_COORD_TO_PIXEL(ti.x); + ptInput.y = TOUCH_COORD_TO_PIXEL(ti.y); + ScreenToClient(hWnd, &ptInput); + + if (ti.dwFlags & TOUCHEVENTF_UP) { + tindex = GetTouchIndex(ti.dwID); + ReleaseTouchIndex(ti.dwID); + kinc_internal_surface_trigger_touch_end(tindex, ptInput.x, ptInput.y); + } + else { + bool touchExisits = GetTouchIndex(ti.dwID) != -1; + tindex = GetAddTouchIndex(ti.dwID); + if (tindex >= 0) { + if (touchExisits) { + if (touchPoints[tindex].x != ptInput.x || touchPoints[tindex].y != ptInput.y) { + touchPoints[tindex].x = ptInput.x; + touchPoints[tindex].y = ptInput.y; + kinc_internal_surface_trigger_move(tindex, ptInput.x, ptInput.y); + } + } + else { + touchPoints[tindex].x = ptInput.x; + touchPoints[tindex].y = ptInput.y; + kinc_internal_surface_trigger_touch_start(tindex, ptInput.x, ptInput.y); + } + } + } + } + bHandled = TRUE; + + if (!CloseTouchInputHandle((HTOUCHINPUT)lParam)) { + } + } + } + } + if (bHandled) + CloseTouchInputHandle((HTOUCHINPUT)lParam); + else + DefWindowProcW(hWnd, WM_TOUCH, wParam, lParam); + + InvalidateRect(hWnd, NULL, FALSE); + } break; + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (!keyPressed[wParam]) { + keyPressed[wParam] = true; + + if (keyTranslated[wParam] == KINC_KEY_CONTROL) { + controlDown = true; + } +#ifdef HANDLE_ALT_ENTER + else if (keyTranslated[wParam] == KINC_KEY_ALT) { + altDown = true; + } +#endif + else { + if (controlDown && keyTranslated[wParam] == KINC_KEY_X) { + char *text = kinc_internal_cut_callback(); + if (text != NULL) { + wchar_t wtext[4096]; + MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, 4096); + OpenClipboard(hWnd); + EmptyClipboard(); + size_t size = (wcslen(wtext) + 1) * sizeof(wchar_t); + HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, size); + void *data = GlobalLock(handle); + memcpy(data, wtext, size); + GlobalUnlock(handle); + SetClipboardData(CF_UNICODETEXT, handle); + CloseClipboard(); + } + } + + if (controlDown && keyTranslated[wParam] == KINC_KEY_C) { + char *text = kinc_internal_copy_callback(); + if (text != NULL) { + wchar_t wtext[4096]; + MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, 4096); + OpenClipboard(hWnd); + EmptyClipboard(); + size_t size = (wcslen(wtext) + 1) * sizeof(wchar_t); + HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, size); + void *data = GlobalLock(handle); + memcpy(data, wtext, size); + GlobalUnlock(handle); + SetClipboardData(CF_UNICODETEXT, handle); + CloseClipboard(); + } + } + + if (controlDown && keyTranslated[wParam] == KINC_KEY_V) { + if (IsClipboardFormatAvailable(CF_UNICODETEXT)) { + OpenClipboard(hWnd); + HANDLE handle = GetClipboardData(CF_UNICODETEXT); + if (handle != NULL) { + wchar_t *wtext = (wchar_t *)GlobalLock(handle); + if (wtext != NULL) { + char text[4096]; + WideCharToMultiByte(CP_UTF8, 0, wtext, -1, text, 4096, NULL, NULL); + kinc_internal_paste_callback(text); + GlobalUnlock(handle); + } + } + CloseClipboard(); + } + } + +#ifdef HANDLE_ALT_ENTER + if (altDown && keyTranslated[wParam] == KINC_KEY_RETURN) { + if (kinc_window_get_mode(0) == KINC_WINDOW_MODE_WINDOW) { + last_window_width = kinc_window_width(0); + last_window_height = kinc_window_height(0); + last_window_x = kinc_window_x(0); + last_window_y = kinc_window_y(0); + kinc_window_change_mode(0, KINC_WINDOW_MODE_FULLSCREEN); + } + else { + kinc_window_change_mode(0, KINC_WINDOW_MODE_WINDOW); + if (last_window_width > 0 && last_window_height > 0) { + kinc_window_resize(0, last_window_width, last_window_height); + } + if (last_window_x > INT_MIN && last_window_y > INT_MIN) { + kinc_window_move(0, last_window_x, last_window_y); + } + } + } +#endif + } + // No auto-repeat + kinc_internal_keyboard_trigger_key_down(keyTranslated[wParam]); + } + // Auto-repeat + // kinc_internal_keyboard_trigger_key_down(keyTranslated[wParam]); + break; + case WM_KEYUP: + case WM_SYSKEYUP: + keyPressed[wParam] = false; + + if (keyTranslated[wParam] == KINC_KEY_CONTROL) { + controlDown = false; + } +#ifdef HANDLE_ALT_ENTER + if (keyTranslated[wParam] == KINC_KEY_ALT) { + altDown = false; + } +#endif + + kinc_internal_keyboard_trigger_key_up(keyTranslated[wParam]); + break; + case WM_CHAR: + switch (wParam) { + case 0x1B: // escape + break; + default: + kinc_internal_keyboard_trigger_key_press((unsigned)wParam); + break; + } + break; + case WM_SYSCOMMAND: + switch (wParam) { + case SC_KEYMENU: + return 0; // Prevent from happening + case SC_SCREENSAVE: + case SC_MONITORPOWER: + return 0; // Prevent from happening + + // Pause game when window is minimized, continue when it's restored or maximized. + // + // Unfortunately, if the game would continue to run when minimized, the graphics in + // the Windows Vista/7 taskbar would not be updated, even when Direct3DDevice::Present() + // is called without error. I do not know why. + case SC_MINIMIZE: + // Scheduler::haltTime(); // haltTime()/unhaltTime() is incremental, meaning that this doesn't interfere with when the game itself calls these + // functions + break; + case SC_RESTORE: + case SC_MAXIMIZE: + // Scheduler::unhaltTime(); + break; + } + break; + case WM_DEVICECHANGE: + detectGamepad = true; + break; + case WM_DROPFILES: { + HDROP hDrop = (HDROP)wParam; + unsigned count = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0); + for (unsigned i = 0; i < count; ++i) { + wchar_t filePath[260]; + if (DragQueryFileW(hDrop, i, filePath, 260)) { + kinc_internal_drop_files_callback(filePath); + } + } + DragFinish(hDrop); + break; + } + } + return DefWindowProcW(hWnd, msg, wParam, lParam); +} + +static float axes[12 * 6]; +static float buttons[12 * 16]; + +typedef DWORD(WINAPI *XInputGetStateType)(DWORD dwUserIndex, XINPUT_STATE *pState); +typedef DWORD(WINAPI *XInputSetStateType)(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration); +static XInputGetStateType InputGetState = NULL; +static XInputSetStateType InputSetState = NULL; + +void loadXInput() { + HMODULE lib = LoadLibraryA("xinput1_4.dll"); + if (lib == NULL) { + lib = LoadLibraryA("xinput1_3.dll"); + } + if (lib == NULL) { + lib = LoadLibraryA("xinput9_1_0.dll"); + } + + if (lib != NULL) { + InputGetState = (XInputGetStateType)GetProcAddress(lib, "XInputGetState"); + InputSetState = (XInputSetStateType)GetProcAddress(lib, "XInputSetState"); + } +} + +static IDirectInput8W *di_instance = NULL; +static IDirectInputDevice8W *di_pads[KINC_DINPUT_MAX_COUNT]; +static DIJOYSTATE2 di_padState[KINC_DINPUT_MAX_COUNT]; +static DIJOYSTATE2 di_lastPadState[KINC_DINPUT_MAX_COUNT]; +static DIDEVCAPS di_deviceCaps[KINC_DINPUT_MAX_COUNT]; +static int padCount = 0; + +static void cleanupPad(int padIndex) { + if (di_pads[padIndex] != NULL) { + di_pads[padIndex]->lpVtbl->Unacquire(di_pads[padIndex]); + di_pads[padIndex]->lpVtbl->Release(di_pads[padIndex]); + di_pads[padIndex] = 0; + } +} + +#ifndef SAFE_RELEASE +#define SAFE_RELEASE(x) \ + if (x != NULL) { \ + x->lpVtbl->Release(x); \ + x = NULL; \ + } +#endif + +// From +//----------------------------------------------------------------------------- +// Enum each PNP device using WMI and check each device ID to see if it contains +// "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it's an XInput device +// Unfortunately this information can not be found by just using DirectInput +//----------------------------------------------------------------------------- +static BOOL IsXInputDevice(const GUID *pGuidProductFromDirectInput) { + IWbemLocator *pIWbemLocator = NULL; + IEnumWbemClassObject *pEnumDevices = NULL; + IWbemClassObject *pDevices[20] = {0}; + IWbemServices *pIWbemServices = NULL; + BSTR bstrNamespace = NULL; + BSTR bstrDeviceID = NULL; + BSTR bstrClassName = NULL; + DWORD uReturned = 0; + bool bIsXinputDevice = false; + UINT iDevice = 0; + VARIANT var; + HRESULT hr; + + // CoInit if needed + hr = CoInitialize(NULL); + bool bCleanupCOM = SUCCEEDED(hr); + + // Create WMI + hr = CoCreateInstance(&CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, &IID_IWbemLocator, (LPVOID *)&pIWbemLocator); + if (FAILED(hr) || pIWbemLocator == NULL) + goto LCleanup; + + bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); + if (bstrNamespace == NULL) + goto LCleanup; + bstrClassName = SysAllocString(L"Win32_PNPEntity"); + if (bstrClassName == NULL) + goto LCleanup; + bstrDeviceID = SysAllocString(L"DeviceID"); + if (bstrDeviceID == NULL) + goto LCleanup; + + // Connect to WMI + hr = pIWbemLocator->lpVtbl->ConnectServer(pIWbemLocator, bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices); + if (FAILED(hr) || pIWbemServices == NULL) + goto LCleanup; + + // Switch security level to IMPERSONATE. + CoSetProxyBlanket((IUnknown *)pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, + EOAC_NONE); + + hr = pIWbemServices->lpVtbl->CreateInstanceEnum(pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices); + if (FAILED(hr) || pEnumDevices == NULL) + goto LCleanup; + + // Loop over all devices + for (;;) { + // Get 20 at a time + hr = pEnumDevices->lpVtbl->Next(pEnumDevices, 10000, 20, pDevices, &uReturned); + if (FAILED(hr)) + goto LCleanup; + if (uReturned == 0) + break; + + for (iDevice = 0; iDevice < uReturned; iDevice++) { + // For each device, get its device ID + hr = pDevices[iDevice]->lpVtbl->Get(pDevices[iDevice], bstrDeviceID, 0L, &var, NULL, NULL); + if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) { + // Check if the device ID contains "IG_". If it does, then it's an XInput device + // This information can not be found from DirectInput + // TODO: Doesn't work with an Xbox Series X|S controller + if (wcsstr(var.bstrVal, L"IG_")) { + // If it does, then get the VID/PID from var.bstrVal + DWORD dwPid = 0, dwVid = 0; + WCHAR *strVid = wcsstr(var.bstrVal, L"VID_"); +#ifndef KINC_NO_CLIB + if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1) { + dwVid = 0; + } + WCHAR *strPid = wcsstr(var.bstrVal, L"PID_"); + if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1) { + dwPid = 0; + } +#endif + + // Compare the VID/PID to the DInput device + DWORD dwVidPid = MAKELONG(dwVid, dwPid); + if (dwVidPid == pGuidProductFromDirectInput->Data1) { + bIsXinputDevice = true; + goto LCleanup; + } + } + } + SAFE_RELEASE(pDevices[iDevice]); + } + } + +LCleanup: + if (bstrNamespace) + SysFreeString(bstrNamespace); + if (bstrDeviceID) + SysFreeString(bstrDeviceID); + if (bstrClassName) + SysFreeString(bstrClassName); + for (iDevice = 0; iDevice < 20; iDevice++) + SAFE_RELEASE(pDevices[iDevice]); + SAFE_RELEASE(pEnumDevices); + SAFE_RELEASE(pIWbemLocator); + SAFE_RELEASE(pIWbemServices); + + if (bCleanupCOM) + CoUninitialize(); + + return bIsXinputDevice; +} + +// TODO (DK) this should probably be called from somewhere? +static void cleanupDirectInput() { + for (int padIndex = 0; padIndex < KINC_DINPUT_MAX_COUNT; ++padIndex) { + cleanupPad(padIndex); + } + + if (di_instance != NULL) { + di_instance->lpVtbl->Release(di_instance); + di_instance = NULL; + } +} + +static BOOL CALLBACK enumerateJoystickAxesCallback(LPCDIDEVICEOBJECTINSTANCEW ddoi, LPVOID context) { + HWND hwnd = (HWND)context; + + DIPROPRANGE propertyRange; + propertyRange.diph.dwSize = sizeof(DIPROPRANGE); + propertyRange.diph.dwHeaderSize = sizeof(DIPROPHEADER); + propertyRange.diph.dwHow = DIPH_BYID; + propertyRange.diph.dwObj = ddoi->dwType; + propertyRange.lMin = -32768; + propertyRange.lMax = 32768; + + HRESULT hr = di_pads[padCount]->lpVtbl->SetProperty(di_pads[padCount], DIPROP_RANGE, &propertyRange.diph); + + if (FAILED(hr)) { + kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8 / Pad%i / SetProperty() failed (HRESULT=0x%x)", padCount, hr); + + // TODO (DK) cleanup? + // cleanupPad(padCount); + + return DIENUM_STOP; + } + + return DIENUM_CONTINUE; +} + +static BOOL CALLBACK enumerateJoysticksCallback(LPCDIDEVICEINSTANCEW ddi, LPVOID context) { + if (padCount < XUSER_MAX_COUNT && IsXInputDevice(&ddi->guidProduct)) { + ++padCount; + return DIENUM_CONTINUE; + } + + HRESULT hr = di_instance->lpVtbl->CreateDevice(di_instance, &ddi->guidInstance, &di_pads[padCount], NULL); + + if (SUCCEEDED(hr)) { + hr = di_pads[padCount]->lpVtbl->SetDataFormat(di_pads[padCount], &c_dfDIJoystick2); + + // TODO (DK) required? + // hr = di_pads[padCount]->SetCooperativeLevel(NULL, DISCL_EXCLUSIVE | DISCL_FOREGROUND); + + if (SUCCEEDED(hr)) { + di_deviceCaps[padCount].dwSize = sizeof(DIDEVCAPS); + hr = di_pads[padCount]->lpVtbl->GetCapabilities(di_pads[padCount], &di_deviceCaps[padCount]); + + if (SUCCEEDED(hr)) { + hr = di_pads[padCount]->lpVtbl->EnumObjects(di_pads[padCount], enumerateJoystickAxesCallback, NULL, DIDFT_AXIS); + + if (SUCCEEDED(hr)) { + hr = di_pads[padCount]->lpVtbl->Acquire(di_pads[padCount]); + + if (SUCCEEDED(hr)) { + memset(&di_padState[padCount], 0, sizeof(DIJOYSTATE2)); + hr = di_pads[padCount]->lpVtbl->GetDeviceState(di_pads[padCount], sizeof(DIJOYSTATE2), &di_padState[padCount]); + + if (SUCCEEDED(hr)) { + kinc_log(KINC_LOG_LEVEL_INFO, "DirectInput8 / Pad%i / initialized", padCount); + } + else { + kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8 / Pad%i / GetDeviceState() failed (HRESULT=0x%x)", padCount, hr); + // cleanupPad(padCount); // (DK) don't kill it, we try again in handleDirectInputPad() + } + } + else { + kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8 / Pad%i / Acquire() failed (HRESULT=0x%x)", padCount, hr); + cleanupPad(padCount); + } + } + else { + kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8 / Pad%i / EnumObjects(DIDFT_AXIS) failed (HRESULT=0x%x)", padCount, hr); + cleanupPad(padCount); + } + } + else { + kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8 / Pad%i / GetCapabilities() failed (HRESULT=0x%x)", padCount, hr); + cleanupPad(padCount); + } + } + else { + kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8 / Pad%i / SetDataFormat() failed (HRESULT=0x%x)", padCount, hr); + cleanupPad(padCount); + } + + ++padCount; + + if (padCount >= KINC_DINPUT_MAX_COUNT) { + return DIENUM_STOP; + } + } + + return DIENUM_CONTINUE; +} + +static void initializeDirectInput() { + HINSTANCE hinstance = GetModuleHandleW(NULL); + + memset(&di_pads, 0, sizeof(IDirectInputDevice8) * KINC_DINPUT_MAX_COUNT); + memset(&di_padState, 0, sizeof(DIJOYSTATE2) * KINC_DINPUT_MAX_COUNT); + memset(&di_lastPadState, 0, sizeof(DIJOYSTATE2) * KINC_DINPUT_MAX_COUNT); + memset(&di_deviceCaps, 0, sizeof(DIDEVCAPS) * KINC_DINPUT_MAX_COUNT); + + HRESULT hr = DirectInput8Create(hinstance, DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void **)&di_instance, NULL); + + if (SUCCEEDED(hr)) { + hr = di_instance->lpVtbl->EnumDevices(di_instance, DI8DEVCLASS_GAMECTRL, enumerateJoysticksCallback, NULL, DIEDFL_ATTACHEDONLY); + + if (SUCCEEDED(hr)) { + } + else { + cleanupDirectInput(); + } + } + else { + kinc_log(KINC_LOG_LEVEL_WARNING, "DirectInput8Create failed (HRESULT=0x%x)", hr); + } +} + +bool handleDirectInputPad(int padIndex) { + if (di_pads[padIndex] == NULL) { + return false; + } + + HRESULT hr = di_pads[padIndex]->lpVtbl->GetDeviceState(di_pads[padIndex], sizeof(DIJOYSTATE2), &di_padState[padIndex]); + + switch (hr) { + case S_OK: { + // TODO (DK) there is a lot more to handle + for (int axisIndex = 0; axisIndex < 2; ++axisIndex) { + LONG *now = NULL; + LONG *last = NULL; + + switch (axisIndex) { + case 0: { + now = &di_padState[padIndex].lX; + last = &di_lastPadState[padIndex].lX; + } break; + case 1: { + now = &di_padState[padIndex].lY; + last = &di_lastPadState[padIndex].lY; + } break; + case 2: { + now = &di_padState[padIndex].lZ; + last = &di_lastPadState[padIndex].lZ; + } break; + } + + if (*now != *last) { + kinc_internal_gamepad_trigger_axis(padIndex, axisIndex, *now / 32768.0f); + } + } + + for (int buttonIndex = 0; buttonIndex < 128; ++buttonIndex) { + BYTE *now = &di_padState[padIndex].rgbButtons[buttonIndex]; + BYTE *last = &di_lastPadState[padIndex].rgbButtons[buttonIndex]; + + if (*now != *last) { + kinc_internal_gamepad_trigger_button(padIndex, buttonIndex, *now / 255.0f); + } + } + + for (int povIndex = 0; povIndex < 4; ++povIndex) { + DWORD *now = &di_padState[padIndex].rgdwPOV[povIndex]; + DWORD *last = &di_lastPadState[padIndex].rgdwPOV[povIndex]; + + bool up = (*now == 0 || *now == 31500 || *now == 4500); + bool down = (*now == 18000 || *now == 13500 || *now == 22500); + bool left = (*now == 27000 || *now == 22500 || *now == 31500); + bool right = (*now == 9000 || *now == 4500 || *now == 13500); + + bool lastUp = (*last == 0 || *last == 31500 || *last == 4500); + bool lastDown = (*last == 18000 || *last == 13500 || *last == 22500); + bool lastLeft = (*last == 27000 || *last == 22500 || *last == 31500); + bool lastRight = (*last == 9000 || *last == 4500 || *last == 13500); + + if (up != lastUp) { + kinc_internal_gamepad_trigger_button(padIndex, 12, up ? 1.0f : 0.0f); + } + if (down != lastDown) { + kinc_internal_gamepad_trigger_button(padIndex, 13, down ? 1.0f : 0.0f); + } + if (left != lastLeft) { + kinc_internal_gamepad_trigger_button(padIndex, 14, left ? 1.0f : 0.0f); + } + if (right != lastRight) { + kinc_internal_gamepad_trigger_button(padIndex, 15, right ? 1.0f : 0.0f); + } + } + + memcpy(&di_lastPadState[padIndex], &di_padState[padIndex], sizeof(DIJOYSTATE2)); + break; + } + case DIERR_INPUTLOST: // fall through + case DIERR_NOTACQUIRED: { + hr = di_pads[padIndex]->lpVtbl->Acquire(di_pads[padIndex]); + break; + } + } + + return hr == S_OK; +} + +static bool isXInputGamepad(int gamepad) { + //if gamepad is greater than XInput max, treat it as DINPUT. + if (gamepad >= XUSER_MAX_COUNT) + { + return false; + } + XINPUT_STATE state; + memset(&state, 0, sizeof(XINPUT_STATE)); + DWORD dwResult = InputGetState(gamepad, &state); + return dwResult == ERROR_SUCCESS; +} + +static bool isDirectInputGamepad(int gamepad) { + if (di_pads[gamepad] == NULL) { + return false; + } + HRESULT hr = di_pads[gamepad]->lpVtbl->GetDeviceState(di_pads[gamepad], sizeof(DIJOYSTATE2), &di_padState[gamepad]); + return hr == S_OK; +} + +const char *kinc_gamepad_vendor(int gamepad) { + if (isXInputGamepad(gamepad)) { + return "Microsoft"; + } + else { + return "DirectInput8"; + } +} + +const char *kinc_gamepad_product_name(int gamepad) { + if (isXInputGamepad(gamepad)) { + return "Xbox 360 Controller"; + } + else { + return "Generic Gamepad"; + } +} + +bool kinc_internal_handle_messages() { + MSG message; + + while (PeekMessageW(&message, 0, 0, 0, PM_REMOVE)) { + TranslateMessage(&message); + DispatchMessageW(&message); + } + + if (InputGetState != NULL && (detectGamepad || gamepadFound)) { + detectGamepad = false; + for (DWORD i = 0; i < KINC_DINPUT_MAX_COUNT; ++i) { + XINPUT_STATE state; + memset(&state, 0, sizeof(XINPUT_STATE)); + DWORD dwResult = InputGetState(i, &state); + + if (dwResult == ERROR_SUCCESS) { + gamepadFound = true; + + float newaxes[6]; + newaxes[0] = state.Gamepad.sThumbLX / 32768.0f; + newaxes[1] = state.Gamepad.sThumbLY / 32768.0f; + newaxes[2] = state.Gamepad.sThumbRX / 32768.0f; + newaxes[3] = state.Gamepad.sThumbRY / 32768.0f; + newaxes[4] = state.Gamepad.bLeftTrigger / 255.0f; + newaxes[5] = state.Gamepad.bRightTrigger / 255.0f; + for (int i2 = 0; i2 < 6; ++i2) { + if (axes[i * 6 + i2] != newaxes[i2]) { + kinc_internal_gamepad_trigger_axis(i, i2, newaxes[i2]); + axes[i * 6 + i2] = newaxes[i2]; + } + } + float newbuttons[16]; + newbuttons[0] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_A) ? 1.0f : 0.0f; + newbuttons[1] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_B) ? 1.0f : 0.0f; + newbuttons[2] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_X) ? 1.0f : 0.0f; + newbuttons[3] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_Y) ? 1.0f : 0.0f; + newbuttons[4] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? 1.0f : 0.0f; + newbuttons[5] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? 1.0f : 0.0f; + newbuttons[6] = state.Gamepad.bLeftTrigger / 255.0f; + newbuttons[7] = state.Gamepad.bRightTrigger / 255.0f; + newbuttons[8] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) ? 1.0f : 0.0f; + newbuttons[9] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_START) ? 1.0f : 0.0f; + newbuttons[10] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) ? 1.0f : 0.0f; + newbuttons[11] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) ? 1.0f : 0.0f; + newbuttons[12] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) ? 1.0f : 0.0f; + newbuttons[13] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) ? 1.0f : 0.0f; + newbuttons[14] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) ? 1.0f : 0.0f; + newbuttons[15] = (state.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) ? 1.0f : 0.0f; + for (int i2 = 0; i2 < 16; ++i2) { + if (buttons[i * 16 + i2] != newbuttons[i2]) { + kinc_internal_gamepad_trigger_button(i, i2, newbuttons[i2]); + buttons[i * 16 + i2] = newbuttons[i2]; + } + } + } + else { + if (handleDirectInputPad(i)) { + gamepadFound = true; + } + } + } + } + + return true; +} + +//**vec2i Kore::System::mousePos() { +//** return vec2i(mouseX, mouseY); +//**} + +static bool keyboardshown = false; +static char language[3] = {0}; + +void kinc_keyboard_show() { + keyboardshown = true; +} + +void kinc_keyboard_hide() { + keyboardshown = false; +} + +bool kinc_keyboard_active() { + return keyboardshown; +} + +void kinc_load_url(const char *url) { +#define WURL_SIZE 1024 +#define HTTP "http://" +#define HTTPS "https://" + if (strncmp(url, HTTP, sizeof(HTTP) - 1) == 0 || strncmp(url, HTTPS, sizeof(HTTPS) - 1) == 0) { + wchar_t wurl[WURL_SIZE]; + MultiByteToWideChar(CP_UTF8, 0, url, -1, wurl, WURL_SIZE); + INT_PTR ret = (INT_PTR)ShellExecuteW(NULL, L"open", wurl, NULL, NULL, SW_SHOWNORMAL); + // According to ShellExecuteW's documentation return values > 32 indicate a success. + if (ret <= 32) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Error opening url %s", url); + } + } +#undef HTTPS +#undef HTTP +#undef WURL_SIZE +} + +void kinc_set_keep_screen_on(bool on) {} +void kinc_vibrate(int ms) {} + +const char *kinc_language() { + wchar_t wlanguage[3] = {0}; + + if (GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SISO639LANGNAME, wlanguage, 3)) { + WideCharToMultiByte(CP_UTF8, 0, wlanguage, -1, language, 3, NULL, NULL); + return language; + } + return "en"; +} + +const char *kinc_system_id() { + return "Windows"; +} + +static bool co_initialized = false; + +void kinc_windows_co_initialize(void) { + if (!co_initialized) { + kinc_microsoft_affirm(CoInitializeEx(0, COINIT_MULTITHREADED)); + co_initialized = true; + } +} + +static wchar_t savePathw[2048] = {0}; +static char savePath[2048] = {0}; + +static void findSavePath() { + kinc_windows_co_initialize(); + IKnownFolderManager *folders = NULL; + CoCreateInstance(&CLSID_KnownFolderManager, NULL, CLSCTX_INPROC_SERVER, &IID_IKnownFolderManager, (LPVOID *)&folders); + IKnownFolder *folder = NULL; + folders->lpVtbl->GetFolder(folders, &FOLDERID_SavedGames, &folder); + + LPWSTR path; + folder->lpVtbl->GetPath(folder, 0, &path); + + wcscpy(savePathw, path); + wcscat(savePathw, L"\\"); + wchar_t name[1024]; + MultiByteToWideChar(CP_UTF8, 0, kinc_application_name(), -1, name, 1024); + wcscat(savePathw, name); + wcscat(savePathw, L"\\"); + + SHCreateDirectoryExW(NULL, savePathw, NULL); + WideCharToMultiByte(CP_UTF8, 0, savePathw, -1, savePath, 1024, NULL, NULL); + + CoTaskMemFree(path); + folder->lpVtbl->Release(folder); + folders->lpVtbl->Release(folders); + // CoUninitialize(); +} + +const char *kinc_internal_save_path() { + if (savePath[0] == 0) + findSavePath(); + return savePath; +} + +static const char *videoFormats[] = {"ogv", NULL}; +static LARGE_INTEGER frequency; +static LARGE_INTEGER startCount; + +const char **kinc_video_formats() { + return videoFormats; +} + +void kinc_login(void) {} + +void kinc_unlock_achievement(int id) {} + +bool kinc_gamepad_connected(int num) { + return isXInputGamepad(num) || isDirectInputGamepad(num); +} + +void kinc_gamepad_rumble(int gamepad, float left, float right) { + if (isXInputGamepad(gamepad)) { + XINPUT_VIBRATION vibration; + memset(&vibration, 0, sizeof(XINPUT_VIBRATION)); + vibration.wLeftMotorSpeed = (WORD)(65535.f * left); + vibration.wRightMotorSpeed = (WORD)(65535.f * right); + InputSetState(gamepad, &vibration); + } +} + +double kinc_frequency() { + return (double)frequency.QuadPart; +} + +kinc_ticks_t kinc_timestamp(void) { + LARGE_INTEGER stamp; + QueryPerformanceCounter(&stamp); + return stamp.QuadPart - startCount.QuadPart; +} + +double kinc_time(void) { + LARGE_INTEGER stamp; + QueryPerformanceCounter(&stamp); + return (double)(stamp.QuadPart - startCount.QuadPart) / (double)frequency.QuadPart; +} + +#if !defined(KINC_NO_MAIN) && !defined(KINC_NO_CLIB) +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + int ret = kickstart(__argc, __argv); + if (ret != 0) { +#ifdef NDEBUG + MessageBox(0, L"Unknown Error", L"Error", MB_OK); +#else + __debugbreak(); +#endif + } + return ret; +} +#endif + +typedef BOOL(__stdcall *MiniDumpWriteDumpType)(IN HANDLE hProcess, IN DWORD ProcessId, IN HANDLE hFile, IN MINIDUMP_TYPE DumpType, + IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + OPTIONAL IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + OPTIONAL IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL); + +static MiniDumpWriteDumpType MyMiniDumpWriteDump; + +static LONG __stdcall MyCrashHandlerExceptionFilter(EXCEPTION_POINTERS *pEx) { + HANDLE file = CreateFileA("kinc.dmp", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file != INVALID_HANDLE_VALUE) { + MINIDUMP_EXCEPTION_INFORMATION stMDEI; + stMDEI.ThreadId = GetCurrentThreadId(); + stMDEI.ExceptionPointers = pEx; + stMDEI.ClientPointers = TRUE; + MyMiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), file, MiniDumpNormal, &stMDEI, NULL, NULL); + CloseHandle(file); + } + return EXCEPTION_CONTINUE_SEARCH; +} + +static void init_crash_handler() { + HMODULE dbghelp = LoadLibraryA("dbghelp.dll"); + if (dbghelp != NULL) { + MyMiniDumpWriteDump = (MiniDumpWriteDumpType)GetProcAddress(dbghelp, "MiniDumpWriteDump"); + SetUnhandledExceptionFilter(MyCrashHandlerExceptionFilter); + } +} + +int kinc_init(const char *name, int width, int height, kinc_window_options_t *win, kinc_framebuffer_options_t *frame) { + init_crash_handler(); + + // Pen functions are only in Windows 8 and later, so load them dynamically + HMODULE user32 = LoadLibraryA("user32.dll"); + MyGetPointerInfo = (GetPointerInfoType)GetProcAddress(user32, "GetPointerInfo"); + MyGetPointerPenInfo = (GetPointerPenInfoType)GetProcAddress(user32, "GetPointerPenInfo"); + MyEnableNonClientDpiScaling = (EnableNonClientDpiScalingType)GetProcAddress(user32, "EnableNonClientDpiScaling"); + initKeyTranslation(); + for (int i = 0; i < 256; ++i) { + keyPressed[i] = false; + } + + for (int i = 0; i < MAX_TOUCH_POINTS; i++) { + touchPoints[i].sysID = -1; + touchPoints[i].x = -1; + touchPoints[i].y = -1; + } + + kinc_display_init(); + + QueryPerformanceCounter(&startCount); + QueryPerformanceFrequency(&frequency); + + for (int i = 0; i < 256; ++i) { + keyPressed[i] = false; + } + + // Kore::System::_init(name, width, height, &win, &frame); + kinc_set_application_name(name); + kinc_window_options_t defaultWin; + if (win == NULL) { + kinc_window_options_set_defaults(&defaultWin); + win = &defaultWin; + } + win->width = width; + win->height = height; + if (win->title == NULL) { + win->title = name; + } + + kinc_g4_internal_init(); + + int window = kinc_window_create(win, frame); + loadXInput(); + initializeDirectInput(); + + return window; +} + +void kinc_internal_shutdown() { + kinc_windows_hide_windows(); + kinc_internal_shutdown_callback(); + kinc_windows_destroy_windows(); + kinc_g4_internal_destroy(); + kinc_windows_restore_displays(); +} + +void kinc_copy_to_clipboard(const char *text) { + wchar_t wtext[4096]; + MultiByteToWideChar(CP_UTF8, 0, text, -1, wtext, 4096); + OpenClipboard(kinc_windows_window_handle(0)); + EmptyClipboard(); + size_t size = (wcslen(wtext) + 1) * sizeof(wchar_t); + HANDLE handle = GlobalAlloc(GMEM_MOVEABLE, size); + void *data = GlobalLock(handle); + memcpy(data, wtext, size); + GlobalUnlock(handle); + SetClipboardData(CF_UNICODETEXT, handle); + CloseClipboard(); +} + +int kinc_cpu_cores(void) { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION info[1024]; + DWORD returnLength = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) * 1024; + BOOL success = GetLogicalProcessorInformation(&info[0], &returnLength); + + int proper_cpu_count = 0; + + if (success) { + DWORD byteOffset = 0; + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = &info[0]; + while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) { + if (ptr->Relationship == RelationProcessorCore) { + ++proper_cpu_count; + } + + byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); + ++ptr; + } + } + else { + proper_cpu_count = 1; + } + + return proper_cpu_count; +} + +int kinc_hardware_threads(void) { + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; +} diff --git a/armorcore/sources/backends/windows/kinc/backend/video.c.h b/armorcore/sources/backends/windows/kinc/backend/video.c.h new file mode 100644 index 000000000..c2880aabe --- /dev/null +++ b/armorcore/sources/backends/windows/kinc/backend/video.c.h @@ -0,0 +1,59 @@ +#include + +void kinc_video_init(kinc_video_t *video, const char *filename) {} + +void kinc_video_destroy(kinc_video_t *video) {} + +kinc_g4_texture_t *kinc_video_current_image(kinc_video_t *video) { + return NULL; +} + +int kinc_video_width(kinc_video_t *video) { + return 64; +} + +int kinc_video_height(kinc_video_t *video) { + return 64; +} + +void kinc_video_play(kinc_video_t *video, bool loop) {} + +void kinc_video_pause(kinc_video_t *video) {} + +void kinc_video_stop(kinc_video_t *video) {} + +void kinc_video_update(kinc_video_t *video, double time) {} + +double kinc_video_duration(kinc_video_t *video) { + return 0.0; +} + +double kinc_video_position(kinc_video_t *video) { + return 0.0; +} + +bool kinc_video_finished(kinc_video_t *video) { + return true; +} + +bool kinc_video_paused(kinc_video_t *video) { + return true; +} + +void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency) {} + +void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream) {} + +void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count) {} + +static float samples[2]; + +float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream) { + samples[0] = 0.0f; + samples[1] = 0.0f; + return samples; +} + +bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream) { + return true; +} diff --git a/armorcore/sources/backends/windows/kinc/backend/video.h b/armorcore/sources/backends/windows/kinc/backend/video.h new file mode 100644 index 000000000..de21038cd --- /dev/null +++ b/armorcore/sources/backends/windows/kinc/backend/video.h @@ -0,0 +1,31 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + void *renderer; + double duration; + double position; + bool finished; + bool paused; +} kinc_video_impl_t; + +typedef struct kinc_internal_video_sound_stream { + int nothing; +} kinc_internal_video_sound_stream_t; + +void kinc_internal_video_sound_stream_init(kinc_internal_video_sound_stream_t *stream, int channel_count, int frequency); + +void kinc_internal_video_sound_stream_destroy(kinc_internal_video_sound_stream_t *stream); + +void kinc_internal_video_sound_stream_insert_data(kinc_internal_video_sound_stream_t *stream, float *data, int sample_count); + +float *kinc_internal_video_sound_stream_next_frame(kinc_internal_video_sound_stream_t *stream); + +bool kinc_internal_video_sound_stream_ended(kinc_internal_video_sound_stream_t *stream); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/backends/windows/kinc/backend/window.c.h b/armorcore/sources/backends/windows/kinc/backend/window.c.h new file mode 100644 index 000000000..eb8ee1914 --- /dev/null +++ b/armorcore/sources/backends/windows/kinc/backend/window.c.h @@ -0,0 +1,471 @@ +#include +#include +#include + +#include + +#undef CreateWindow + +struct HWND__; +typedef unsigned long DWORD; + +typedef struct { + struct HWND__ *handle; + int display_index; + bool mouseInside; + int index; + int x, y, mode, bpp, frequency, features; + int manualWidth, manualHeight; + void (*resizeCallback)(int x, int y, void *data); + void *resizeCallbackData; + void (*ppiCallback)(int ppi, void *data); + void *ppiCallbackData; + bool (*closeCallback)(void *data); + void *closeCallbackData; +} WindowData; + +LRESULT WINAPI KoreWindowsMessageProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +#define MAXIMUM_WINDOWS 16 +static WindowData windows[MAXIMUM_WINDOWS] = {0}; +static int window_counter = 0; + +#ifdef KINC_OCULUS +const wchar_t *windowClassName = L"ORT"; +#else +const wchar_t *windowClassName = L"KoreWindow"; +#endif + +#ifdef KINC_VULKAN +#include +#include + +VkResult kinc_vulkan_create_surface(VkInstance instance, int window_index, VkSurfaceKHR *surface) { + VkWin32SurfaceCreateInfoKHR createInfo = {0}; + createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + createInfo.pNext = NULL; + createInfo.flags = 0; + createInfo.hinstance = GetModuleHandle(NULL); + createInfo.hwnd = windows[window_index].handle; + return vkCreateWin32SurfaceKHR(instance, &createInfo, NULL, surface); +} + +#include + +void kinc_vulkan_get_instance_extensions(const char **names, int *index, int max) { + assert(*index + 1 < max); + names[(*index)++] = VK_KHR_WIN32_SURFACE_EXTENSION_NAME; +} + +VkBool32 kinc_vulkan_get_physical_device_presentation_support(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex) { + return vkGetPhysicalDeviceWin32PresentationSupportKHR(physicalDevice, queueFamilyIndex); +} +#endif + +static void RegisterWindowClass(HINSTANCE hInstance, const wchar_t *className) { + WNDCLASSEXW wc = {sizeof(WNDCLASSEXA), + CS_OWNDC /*CS_CLASSDC*/, + KoreWindowsMessageProcedure, + 0L, + 0L, + hInstance, + LoadIconW(hInstance, MAKEINTRESOURCEW(107)), + LoadCursor(NULL, IDC_ARROW), + 0, + 0, + className, + 0}; + RegisterClassExW(&wc); +} + +static DWORD getStyle(int features) { + DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP; + + if ((features & KINC_WINDOW_FEATURE_RESIZEABLE) && ((features & KINC_WINDOW_FEATURE_BORDERLESS) == 0)) { + style |= WS_SIZEBOX; + } + + if (features & KINC_WINDOW_FEATURE_MAXIMIZABLE) { + style |= WS_MAXIMIZEBOX; + } + + if (features & KINC_WINDOW_FEATURE_MINIMIZABLE) { + style |= WS_MINIMIZEBOX; + } + + if ((features & KINC_WINDOW_FEATURE_BORDERLESS) == 0) { + style |= WS_CAPTION | WS_SYSMENU; + } + + return style; +} + +static DWORD getExStyle(int features) { + DWORD exStyle = WS_EX_APPWINDOW; + + if ((features & KINC_WINDOW_FEATURE_BORDERLESS) == 0) { + exStyle |= WS_EX_WINDOWEDGE; + } + + if (features & KINC_WINDOW_FEATURE_ON_TOP) { + exStyle |= WS_EX_TOPMOST; + } + + return exStyle; +} + +int kinc_windows_window_index_from_hwnd(struct HWND__ *handle) { + for (int i = 0; i < MAXIMUM_WINDOWS; ++i) { + if (windows[i].handle == handle) { + return i; + } + } + return -1; +} + +int kinc_count_windows() { + return window_counter; +} + +int kinc_window_x(int window_index) { + RECT rect; + GetWindowRect(windows[window_index].handle, &rect); + windows[window_index].x = rect.left; + return windows[window_index].x; +} + +int kinc_window_y(int window_index) { + RECT rect; + GetWindowRect(windows[window_index].handle, &rect); + windows[window_index].y = rect.top; + return windows[window_index].y; +} + +int kinc_window_width(int window_index) { + RECT rect; + GetClientRect(windows[window_index].handle, &rect); + return rect.right; +} + +int kinc_window_height(int window_index) { + RECT rect; + GetClientRect(windows[window_index].handle, &rect); + return rect.bottom; +} + +static DWORD getDwStyle(kinc_window_mode_t mode, int features) { + switch (mode) { + case KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN: { + return WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP; + } + case KINC_WINDOW_MODE_FULLSCREEN: + return WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP; + case KINC_WINDOW_MODE_WINDOW: + default: + return getStyle(features); + } +} + +static DWORD getDwExStyle(kinc_window_mode_t mode, int features) { + switch (mode) { + case KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN: { + return WS_EX_APPWINDOW; + } + case KINC_WINDOW_MODE_FULLSCREEN: + return WS_EX_APPWINDOW; + case KINC_WINDOW_MODE_WINDOW: + default: + return getExStyle(features); + } +} + +static int createWindow(const wchar_t *title, int x, int y, int width, int height, int bpp, int frequency, int features, kinc_window_mode_t windowMode, + int target_display_index) { + HINSTANCE inst = GetModuleHandleW(NULL); + + if (window_counter == 0) { + RegisterWindowClass(inst, windowClassName); + } + + int display_index = target_display_index == -1 ? kinc_primary_display() : target_display_index; + + RECT WindowRect; + WindowRect.left = 0; + WindowRect.right = width; + WindowRect.top = 0; + WindowRect.bottom = height; + + if (windowMode == KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN) { + kinc_windows_set_display_mode(display_index, width, height, bpp, frequency); + } + + AdjustWindowRectEx(&WindowRect, getDwStyle(windowMode, features), FALSE, getDwExStyle(windowMode, features)); + + kinc_display_mode_t display_mode = kinc_display_current_mode(display_index); + + int dstx = display_mode.x; + int dsty = display_mode.y; + int dstw = width; + int dsth = height; + + switch (windowMode) { + case KINC_WINDOW_MODE_WINDOW: + dstx += x < 0 ? (display_mode.width - width) / 2 : x; + dsty += y < 0 ? (display_mode.height - height) / 2 : y; + dstw = WindowRect.right - WindowRect.left; + dsth = WindowRect.bottom - WindowRect.top; + break; + case KINC_WINDOW_MODE_FULLSCREEN: + dstw = display_mode.width; + dsth = display_mode.height; + break; + case KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN: + break; + } + + HWND hwnd = CreateWindowExW(getDwExStyle(windowMode, features), windowClassName, title, getDwStyle(windowMode, features), dstx, dsty, dstw, dsth, NULL, NULL, + inst, NULL); + + SetCursor(LoadCursor(NULL, IDC_ARROW)); + DragAcceptFiles(hwnd, true); + + windows[window_counter].handle = hwnd; + windows[window_counter].x = dstx; + windows[window_counter].y = dsty; + windows[window_counter].mode = windowMode; + windows[window_counter].display_index = display_index; + windows[window_counter].bpp = bpp; + windows[window_counter].frequency = frequency; + windows[window_counter].features = features; + windows[window_counter].manualWidth = width; + windows[window_counter].manualHeight = height; + windows[window_counter].index = window_counter; + + return window_counter++; +} + +void kinc_window_resize(int window_index, int width, int height) { + WindowData *win = &windows[window_index]; + win->manualWidth = width; + win->manualHeight = height; + switch (win->mode) { + case KINC_WINDOW_MODE_WINDOW: { + RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = width; + rect.bottom = height; + AdjustWindowRectEx(&rect, getDwStyle((kinc_window_mode_t)win->mode, win->features), FALSE, getDwExStyle((kinc_window_mode_t)win->mode, win->features)); + SetWindowPos(win->handle, NULL, kinc_window_x(window_index), kinc_window_y(window_index), rect.right - rect.left, rect.bottom - rect.top, 0); + break; + } + case KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN: { + int display_index = kinc_window_display(window_index); + kinc_display_mode_t display_mode = kinc_display_current_mode(display_index); + kinc_windows_set_display_mode(display_index, width, height, win->bpp, win->frequency); + SetWindowPos(win->handle, NULL, display_mode.x, display_mode.y, display_mode.width, display_mode.height, 0); + break; + } + } +} + +void kinc_window_move(int window_index, int x, int y) { + WindowData *win = &windows[window_index]; + + if (win->mode != 0) { + return; + } + + win->x = x; + win->y = y; + + RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = kinc_window_width(window_index); + rect.bottom = kinc_window_height(window_index); + AdjustWindowRectEx(&rect, getDwStyle((kinc_window_mode_t)win->mode, win->features), FALSE, getDwExStyle((kinc_window_mode_t)win->mode, win->features)); + + SetWindowPos(win->handle, NULL, x, y, rect.right - rect.left, rect.bottom - rect.top, 0); +} + +void kinc_internal_change_framebuffer(int window, struct kinc_framebuffer_options *frame); + +void kinc_window_change_framebuffer(int window, kinc_framebuffer_options_t *frame) { + kinc_internal_change_framebuffer(window, frame); +} + +void kinc_window_change_features(int window_index, int features) { + WindowData *win = &windows[window_index]; + win->features = features; + SetWindowLongW(win->handle, GWL_STYLE, getStyle(features)); + SetWindowLongW(win->handle, GWL_EXSTYLE, getExStyle(features)); + + HWND on_top = (features & KINC_WINDOW_FEATURE_ON_TOP) ? HWND_TOPMOST : HWND_NOTOPMOST; + SetWindowPos(win->handle, on_top, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); + + kinc_window_show(window_index); +} + +void kinc_window_change_mode(int window_index, kinc_window_mode_t mode) { + WindowData *win = &windows[window_index]; + int display_index = kinc_window_display(window_index); + kinc_display_mode_t display_mode = kinc_display_current_mode(display_index); + switch (mode) { + case KINC_WINDOW_MODE_WINDOW: + kinc_windows_restore_display(display_index); + kinc_window_change_features(window_index, win->features); + kinc_window_show(window_index); + break; + case KINC_WINDOW_MODE_FULLSCREEN: { + kinc_windows_restore_display(display_index); + SetWindowLongW(win->handle, GWL_STYLE, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP); + SetWindowLongW(win->handle, GWL_EXSTYLE, WS_EX_APPWINDOW); + SetWindowPos(win->handle, NULL, display_mode.x, display_mode.y, display_mode.width, display_mode.height, 0); + kinc_window_show(window_index); + break; + } + case KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN: + kinc_windows_set_display_mode(display_index, win->manualWidth, win->manualHeight, win->bpp, win->frequency); + SetWindowLongW(win->handle, GWL_STYLE, WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP); + SetWindowLongW(win->handle, GWL_EXSTYLE, WS_EX_APPWINDOW); + SetWindowPos(win->handle, NULL, display_mode.x, display_mode.y, display_mode.width, display_mode.height, 0); + kinc_window_show(window_index); + break; + } + win->mode = mode; +} + +kinc_window_mode_t kinc_window_get_mode(int window_index) { + return (kinc_window_mode_t)windows[window_index].mode; +} + +void kinc_window_destroy(int window_index) { + WindowData *win = &windows[window_index]; + if (win->handle != NULL) { + kinc_g4_internal_destroy_window(window_index); + DestroyWindow(win->handle); + win->handle = NULL; + --window_counter; + } +} + +void kinc_windows_hide_windows(void) { + for (int i = 0; i < MAXIMUM_WINDOWS; ++i) { + if (windows[i].handle != NULL) { + ShowWindow(windows[i].handle, SW_HIDE); + UpdateWindow(windows[i].handle); + } + } +} + +void kinc_windows_destroy_windows(void) { + for (int i = 0; i < MAXIMUM_WINDOWS; ++i) { + kinc_window_destroy(i); + } + UnregisterClassW(windowClassName, GetModuleHandleW(NULL)); +} + +void kinc_window_show(int window_index) { + ShowWindow(windows[window_index].handle, SW_SHOWDEFAULT); + UpdateWindow(windows[window_index].handle); +} + +void kinc_window_hide(int window_index) { + ShowWindow(windows[window_index].handle, SW_HIDE); + UpdateWindow(windows[window_index].handle); +} + +void kinc_window_set_title(int window_index, const char *title) { + wchar_t buffer[1024]; + MultiByteToWideChar(CP_UTF8, 0, title, -1, buffer, 1024); + SetWindowTextW(windows[window_index].handle, buffer); +} + +int kinc_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame) { + kinc_window_options_t defaultWin; + kinc_framebuffer_options_t defaultFrame; + + if (win == NULL) { + kinc_window_options_set_defaults(&defaultWin); + win = &defaultWin; + } + + if (frame == NULL) { + kinc_framebuffer_options_set_defaults(&defaultFrame); + frame = &defaultFrame; + } + + if (win->title == NULL) { + win->title = ""; + } + + wchar_t wbuffer[1024]; + MultiByteToWideChar(CP_UTF8, 0, win->title, -1, wbuffer, 1024); + + int windowId = createWindow(wbuffer, win->x, win->y, win->width, win->height, frame->color_bits, frame->frequency, win->window_features, win->mode, + win->display_index); + + kinc_g4_set_antialiasing_samples(frame->samples_per_pixel); + bool vsync = frame->vertical_sync; +#ifdef KINC_OCULUS + vsync = false; +#endif + kinc_g4_internal_init_window(windowId, frame->depth_bits, frame->stencil_bits, vsync); + + if (win->visible) { + kinc_window_show(windowId); + } + + return windowId; +} + +void kinc_window_set_resize_callback(int window_index, void (*callback)(int x, int y, void *data), void *data) { + windows[window_index].resizeCallback = callback; + windows[window_index].resizeCallbackData = data; +} + +void kinc_window_set_ppi_changed_callback(int window_index, void (*callback)(int ppi, void *data), void *data) { + windows[window_index].ppiCallback = callback; + windows[window_index].ppiCallbackData = data; +} + +void kinc_window_set_close_callback(int window_index, bool (*callback)(void *data), void *data) { + windows[window_index].closeCallback = callback; + windows[window_index].closeCallbackData = data; +} + +int kinc_window_display(int window_index) { + return kinc_windows_get_display_for_monitor(MonitorFromWindow(windows[window_index].handle, MONITOR_DEFAULTTOPRIMARY)); +} + +struct HWND__ *kinc_windows_window_handle(int window_index) { + return windows[window_index].handle; +} + +int kinc_windows_manual_width(int window) { + return windows[window].manualWidth; +} + +int kinc_windows_manual_height(int window) { + return windows[window].manualHeight; +} + +void kinc_internal_call_resize_callback(int window_index, int width, int height) { + if (windows[window_index].resizeCallback != NULL) { + windows[window_index].resizeCallback(width, height, windows[window_index].resizeCallbackData); + } +} + +void kinc_internal_call_ppi_changed_callback(int window_index, int ppi) { + if (windows[window_index].ppiCallback != NULL) { + windows[window_index].ppiCallback(ppi, windows[window_index].ppiCallbackData); + } +} + +bool kinc_internal_call_close_callback(int window_index) { + if (windows[window_index].closeCallback != NULL) { + return windows[window_index].closeCallback(windows[window_index].closeCallbackData); + } + return true; +} diff --git a/armorcore/sources/backends/windows/kinc/backend/windowsunit.c b/armorcore/sources/backends/windows/kinc/backend/windowsunit.c new file mode 100644 index 000000000..09e3e8075 --- /dev/null +++ b/armorcore/sources/backends/windows/kinc/backend/windowsunit.c @@ -0,0 +1,127 @@ +// Windows 7 +#define WINVER 0x0601 +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0601 + +#define NOATOM +//#define NOCLIPBOARD +#define NOCOLOR +#define NOCOMM +//#define NOCTLMGR +#define NODEFERWINDOWPOS +#define NODRAWTEXT +//#define NOGDI +#define NOGDICAPMASKS +#define NOHELP +#define NOICONS +#define NOKANJI +#define NOKEYSTATES +//#define NOMB +#define NOMCX +#define NOMEMMGR +#define NOMENUS +#define NOMETAFILE +#define NOMINMAX +//#define NOMSG +//#define NONLS +#define NOOPENFILE +#define NOPROFILER +#define NORASTEROPS +#define NOSCROLL +#define NOSERVICE +//#define NOSHOWWINDOW +#define NOSOUND +//#define NOSYSCOMMANDS +#define NOSYSMETRICS +#define NOTEXTMETRIC +//#define NOUSER +//#define NOVIRTUALKEYCODES +#define NOWH +//#define NOWINMESSAGES +//#define NOWINOFFSETS +//#define NOWINSTYLES +#define WIN32_LEAN_AND_MEAN + +#include +#include + +// Some types for features exclusive to later versions of Windows are copied in here. +// Use with care, make sure not to break backwards-compatibility when using them. + +typedef DWORD POINTER_INPUT_TYPE; + +typedef UINT32 POINTER_FLAGS; + +typedef enum tagPOINTER_BUTTON_CHANGE_TYPE { + POINTER_CHANGE_NONE, + POINTER_CHANGE_FIRSTBUTTON_DOWN, + POINTER_CHANGE_FIRSTBUTTON_UP, + POINTER_CHANGE_SECONDBUTTON_DOWN, + POINTER_CHANGE_SECONDBUTTON_UP, + POINTER_CHANGE_THIRDBUTTON_DOWN, + POINTER_CHANGE_THIRDBUTTON_UP, + POINTER_CHANGE_FOURTHBUTTON_DOWN, + POINTER_CHANGE_FOURTHBUTTON_UP, + POINTER_CHANGE_FIFTHBUTTON_DOWN, + POINTER_CHANGE_FIFTHBUTTON_UP, +} POINTER_BUTTON_CHANGE_TYPE; + +typedef struct tagPOINTER_INFO { + POINTER_INPUT_TYPE pointerType; + UINT32 pointerId; + UINT32 frameId; + POINTER_FLAGS pointerFlags; + HANDLE sourceDevice; + HWND hwndTarget; + POINT ptPixelLocation; + POINT ptHimetricLocation; + POINT ptPixelLocationRaw; + POINT ptHimetricLocationRaw; + DWORD dwTime; + UINT32 historyCount; + INT32 InputData; + DWORD dwKeyStates; + UINT64 PerformanceCount; + POINTER_BUTTON_CHANGE_TYPE ButtonChangeType; +} POINTER_INFO; + +typedef UINT32 PEN_FLAGS; + +typedef UINT32 PEN_MASK; + +typedef struct tagPOINTER_PEN_INFO { + POINTER_INFO pointerInfo; + PEN_FLAGS penFlags; + PEN_MASK penMask; + UINT32 pressure; + UINT32 rotation; + INT32 tiltX; + INT32 tiltY; +} POINTER_PEN_INFO; + +#define WM_POINTERUPDATE 0x0245 +#define WM_POINTERDOWN 0x0246 +#define WM_POINTERUP 0x0247 + +#define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam)) + +enum tagPOINTER_INPUT_TYPE { + PT_POINTER = 1, // Generic pointer + PT_TOUCH = 2, // Touch + PT_PEN = 3, // Pen + PT_MOUSE = 4, // Mouse + PT_TOUCHPAD = 5, // Touchpad +}; + +#include + +#include "Windows.c.h" +#include "base.c.h" +#include "display.c.h" +#include "http.c.h" +#include "mouse.c.h" +#include "system.c.h" +#include "window.c.h" +#include "video.c.h" diff --git a/armorcore/sources/const_data.c b/armorcore/sources/const_data.c new file mode 100644 index 000000000..df4403500 --- /dev/null +++ b/armorcore/sources/const_data.c @@ -0,0 +1,12 @@ + +// Generated in Blender with armory_tools/export_skydome_geometry.py, using +// a UV sphere with 24 segments and 12 rings. + +const int _const_data_skydome_indices[] = {261,8,7,258,5,4,265,12,11,257,2,156,262,9,8,259,6,5,179,12,265,0,3,2,263,10,9,260,7,6,1,4,3,264,11,10,6,17,16,179,23,12,3,14,13,10,21,20,7,18,17,4,15,14,11,22,21,8,19,18,5,16,15,12,23,22,2,13,156,9,20,19,16,27,26,23,34,33,13,24,156,20,31,30,17,28,27,179,34,23,14,25,24,21,32,31,18,29,28,15,26,25,22,33,32,19,30,29,29,40,39,26,37,36,33,44,43,30,41,40,27,38,37,34,45,44,24,35,156,31,42,41,28,39,38,179,45,34,25,36,35,32,43,42,39,50,49,179,56,45,36,47,46,43,54,53,40,51,50,37,48,47,44,55,54,41,52,51,38,49,48,45,56,55,35,46,156,42,53,52,52,63,62,49,60,59,56,67,66,46,57,156,53,64,63,50,61,60,179,67,56,47,58,57,54,65,64,51,62,61,48,59,58,55,66,65,62,73,72,59,70,69,66,77,76,63,74,73,60,71,70,67,78,77,57,68,156,64,75,74,61,72,71,179,78,67,58,69,68,65,76,75,75,86,85,72,83,82,179,89,78,69,80,79,76,87,86,73,84,83,70,81,80,77,88,87,74,85,84,71,82,81,78,89,88,68,79,156,85,96,95,82,93,92,89,100,99,79,90,156,86,97,96,83,94,93,179,100,89,80,91,90,87,98,97,84,95,94,81,92,91,88,99,98,98,109,108,95,106,105,92,103,102,99,110,109,96,107,106,93,104,103,100,111,110,90,101,156,97,108,107,94,105,104,179,111,100,91,102,101,101,112,156,108,119,118,105,116,115,179,122,111,102,113,112,109,120,119,106,117,116,103,114,113,110,121,120,107,118,117,104,115,114,111,122,121,121,132,131,118,129,128,115,126,125,122,133,132,112,123,156,119,130,129,116,127,126,179,133,122,113,124,123,120,131,130,117,128,127,114,125,124,124,135,134,131,142,141,128,139,138,125,136,135,132,143,142,129,140,139,126,137,136,133,144,143,123,134,156,130,141,140,127,138,137,179,144,133,144,155,154,134,145,156,141,152,151,138,149,148,179,155,144,135,146,145,142,153,152,139,150,149,136,147,146,143,154,153,140,151,150,137,148,147,147,159,158,154,166,165,151,163,162,148,160,159,155,167,166,145,157,156,152,164,163,149,161,160,179,167,155,146,158,157,153,165,164,150,162,161,179,178,167,158,169,168,165,176,175,162,173,172,159,170,169,166,177,176,163,174,173,160,171,170,167,178,177,157,168,156,164,175,174,161,172,171,171,183,182,178,190,189,168,180,156,175,187,186,172,184,183,179,190,178,169,181,180,176,188,187,173,185,184,170,182,181,177,189,188,174,186,185,182,193,192,189,200,199,186,197,196,183,194,193,190,201,200,180,191,156,187,198,197,184,195,194,179,201,190,181,192,191,188,199,198,185,196,195,195,206,205,179,212,201,192,203,202,199,210,209,196,207,206,193,204,203,200,211,210,197,208,207,194,205,204,201,212,211,191,202,156,198,209,208,205,216,215,212,223,222,202,213,156,209,220,219,206,217,216,179,223,212,203,214,213,210,221,220,207,218,217,204,215,214,211,222,221,208,219,218,218,229,228,215,226,225,222,233,232,219,230,229,216,227,226,223,234,233,213,224,156,220,231,230,217,228,227,179,234,223,214,225,224,221,232,231,228,239,238,179,245,234,225,236,235,232,243,242,229,240,239,226,237,236,233,244,243,230,241,240,227,238,237,234,245,244,224,235,156,231,242,241,241,252,251,238,249,248,245,256,255,235,246,156,242,253,252,239,250,249,179,256,245,236,247,246,243,254,253,240,251,250,237,248,247,244,255,254,251,260,259,248,1,0,255,264,263,252,261,260,249,258,1,256,265,264,246,257,156,253,262,261,250,259,258,179,265,256,247,0,257,254,263,262,261,7,260,258,4,1,265,11,264,262,8,261,259,5,258,0,2,257,263,9,262,260,6,259,1,3,0,264,10,263,6,16,5,3,13,2,10,20,9,7,17,6,4,14,3,11,21,10,8,18,7,5,15,4,12,22,11,9,19,8,16,26,15,23,33,22,20,30,19,17,27,16,14,24,13,21,31,20,18,28,17,15,25,14,22,32,21,19,29,18,29,39,28,26,36,25,33,43,32,30,40,29,27,37,26,34,44,33,31,41,30,28,38,27,25,35,24,32,42,31,39,49,38,36,46,35,43,53,42,40,50,39,37,47,36,44,54,43,41,51,40,38,48,37,45,55,44,42,52,41,52,62,51,49,59,48,56,66,55,53,63,52,50,60,49,47,57,46,54,64,53,51,61,50,48,58,47,55,65,54,62,72,61,59,69,58,66,76,65,63,73,62,60,70,59,67,77,66,64,74,63,61,71,60,58,68,57,65,75,64,75,85,74,72,82,71,69,79,68,76,86,75,73,83,72,70,80,69,77,87,76,74,84,73,71,81,70,78,88,77,85,95,84,82,92,81,89,99,88,86,96,85,83,93,82,80,90,79,87,97,86,84,94,83,81,91,80,88,98,87,98,108,97,95,105,94,92,102,91,99,109,98,96,106,95,93,103,92,100,110,99,97,107,96,94,104,93,91,101,90,108,118,107,105,115,104,102,112,101,109,119,108,106,116,105,103,113,102,110,120,109,107,117,106,104,114,103,111,121,110,121,131,120,118,128,117,115,125,114,122,132,121,119,129,118,116,126,115,113,123,112,120,130,119,117,127,116,114,124,113,124,134,123,131,141,130,128,138,127,125,135,124,132,142,131,129,139,128,126,136,125,133,143,132,130,140,129,127,137,126,144,154,143,141,151,140,138,148,137,135,145,134,142,152,141,139,149,138,136,146,135,143,153,142,140,150,139,137,147,136,147,158,146,154,165,153,151,162,150,148,159,147,155,166,154,152,163,151,149,160,148,146,157,145,153,164,152,150,161,149,158,168,157,165,175,164,162,172,161,159,169,158,166,176,165,163,173,162,160,170,159,167,177,166,164,174,163,161,171,160,171,182,170,178,189,177,175,186,174,172,183,171,169,180,168,176,187,175,173,184,172,170,181,169,177,188,176,174,185,173,182,192,181,189,199,188,186,196,185,183,193,182,190,200,189,187,197,186,184,194,183,181,191,180,188,198,187,185,195,184,195,205,194,192,202,191,199,209,198,196,206,195,193,203,192,200,210,199,197,207,196,194,204,193,201,211,200,198,208,197,205,215,204,212,222,211,209,219,208,206,216,205,203,213,202,210,220,209,207,217,206,204,214,203,211,221,210,208,218,207,218,228,217,215,225,214,222,232,221,219,229,218,216,226,215,223,233,222,220,230,219,217,227,216,214,224,213,221,231,220,228,238,227,225,235,224,232,242,231,229,239,228,226,236,225,233,243,232,230,240,229,227,237,226,234,244,233,231,241,230,241,251,240,238,248,237,245,255,244,242,252,241,239,249,238,236,246,235,243,253,242,240,250,239,237,247,236,244,254,243,251,259,250,248,0,247,255,263,254,252,260,251,249,1,248,256,264,255,253,261,252,250,258,249,247,257,246,254,262,253}; +const int _const_data_skydome_indices_count = sizeof(_const_data_skydome_indices) / sizeof(_const_data_skydome_indices[0]); + +const float _const_data_skydome_pos[] = {0.0,0.5,0.86603,0.0,0.70711,0.70711,0.06699,0.25,0.96593,0.12941,0.48296,0.86603,0.18301,0.68301,0.70711,0.22414,0.83652,0.5,0.25,0.93301,0.25882,0.25882,0.96593,-0.0,0.25,0.93301,-0.25882,0.22414,0.83652,-0.5,0.18301,0.68301,-0.70711,0.12941,0.48296,-0.86603,0.06699,0.25,-0.96593,0.12941,0.22414,0.96593,0.25,0.43301,0.86603,0.35355,0.61237,0.70711,0.43301,0.75,0.5,0.48296,0.83652,0.25882,0.5,0.86603,-0.0,0.48296,0.83652,-0.25882,0.43301,0.75,-0.5,0.35355,0.61237,-0.70711,0.25,0.43301,-0.86603,0.12941,0.22414,-0.96593,0.18301,0.18301,0.96593,0.35355,0.35355,0.86603,0.5,0.5,0.70711,0.61237,0.61237,0.5,0.68301,0.68301,0.25882,0.70711,0.70711,-0.0,0.68301,0.68301,-0.25882,0.61237,0.61237,-0.5,0.5,0.5,-0.70711,0.35355,0.35355,-0.86603,0.18301,0.18301,-0.96593,0.22414,0.12941,0.96593,0.43301,0.25,0.86603,0.61237,0.35355,0.70711,0.75,0.43301,0.5,0.83652,0.48296,0.25882,0.86603,0.5,-0.0,0.83652,0.48296,-0.25882,0.75,0.43301,-0.5,0.61237,0.35355,-0.70711,0.43301,0.25,-0.86603,0.22414,0.12941,-0.96593,0.25,0.06699,0.96593,0.48296,0.12941,0.86603,0.68301,0.18301,0.70711,0.83652,0.22414,0.5,0.93301,0.25,0.25882,0.96593,0.25882,-0.0,0.93301,0.25,-0.25882,0.83652,0.22414,-0.5,0.68301,0.18301,-0.70711,0.48296,0.12941,-0.86603,0.25,0.06699,-0.96593,0.25882,-0.0,0.96593,0.5,-0.0,0.86603,0.70711,-0.0,0.70711,0.86603,0.0,0.5,0.96593,-0.0,0.25882,1.0,-0.0,-0.0,0.96593,-0.0,-0.25882,0.86603,-0.0,-0.5,0.70711,-0.0,-0.70711,0.5,-0.0,-0.86603,0.25882,-0.0,-0.96593,0.25,-0.06699,0.96593,0.48296,-0.12941,0.86603,0.68301,-0.18301,0.70711,0.83652,-0.22414,0.5,0.93301,-0.25,0.25882,0.96593,-0.25882,-0.0,0.93301,-0.25,-0.25882,0.83652,-0.22414,-0.5,0.68301,-0.18301,-0.70711,0.48296,-0.12941,-0.86603,0.25,-0.06699,-0.96593,0.22414,-0.12941,0.96593,0.43301,-0.25,0.86603,0.61237,-0.35355,0.70711,0.75,-0.43301,0.5,0.83652,-0.48296,0.25882,0.86603,-0.5,-0.0,0.83652,-0.48296,-0.25882,0.75,-0.43301,-0.5,0.61237,-0.35355,-0.70711,0.43301,-0.25,-0.86603,0.22414,-0.12941,-0.96593,0.18301,-0.18301,0.96593,0.35355,-0.35355,0.86603,0.5,-0.5,0.70711,0.61237,-0.61237,0.5,0.68301,-0.68301,0.25882,0.70711,-0.70711,-0.0,0.68301,-0.68301,-0.25882,0.61237,-0.61237,-0.5,0.5,-0.5,-0.70711,0.35355,-0.35355,-0.86603,0.18301,-0.18301,-0.96593,0.12941,-0.22414,0.96593,0.25,-0.43301,0.86603,0.35355,-0.61237,0.70711,0.43301,-0.75,0.5,0.48296,-0.83652,0.25882,0.5,-0.86603,-0.0,0.48296,-0.83652,-0.25882,0.43301,-0.75,-0.5,0.35355,-0.61237,-0.70711,0.25,-0.43301,-0.86603,0.12941,-0.22414,-0.96593,0.06699,-0.25,0.96593,0.12941,-0.48296,0.86603,0.18301,-0.68301,0.70711,0.22414,-0.83652,0.5,0.25,-0.93301,0.25882,0.25882,-0.96593,-0.0,0.25,-0.93301,-0.25882,0.22414,-0.83652,-0.5,0.18301,-0.68301,-0.70711,0.12941,-0.48296,-0.86603,0.06699,-0.25,-0.96593,0.0,-0.25882,0.96593,-0.0,-0.5,0.86603,0.0,-0.70711,0.70711,0.0,-0.86603,0.5,0.0,-0.96593,0.25882,-0.0,-1.0,-0.0,0.0,-0.96593,-0.25882,0.0,-0.86603,-0.5,0.0,-0.70711,-0.70711,0.0,-0.5,-0.86603,0.0,-0.25882,-0.96593,-0.06699,-0.25,0.96593,-0.12941,-0.48296,0.86603,-0.18301,-0.68301,0.70711,-0.22414,-0.83652,0.5,-0.25,-0.93301,0.25882,-0.25882,-0.96593,-0.0,-0.25,-0.93301,-0.25882,-0.22414,-0.83652,-0.5,-0.18301,-0.68301,-0.70711,-0.12941,-0.48296,-0.86603,-0.06699,-0.25,-0.96593,-0.12941,-0.22414,0.96593,-0.25,-0.43301,0.86603,-0.35355,-0.61237,0.70711,-0.43301,-0.75,0.5,-0.48296,-0.83652,0.25882,-0.5,-0.86603,-0.0,-0.48296,-0.83652,-0.25882,-0.43301,-0.75,-0.5,-0.35355,-0.61237,-0.70711,-0.25,-0.43301,-0.86603,-0.12941,-0.22414,-0.96593,-0.0,-0.0,1.0,-0.18301,-0.18301,0.96593,-0.35355,-0.35355,0.86603,-0.5,-0.5,0.70711,-0.61237,-0.61237,0.5,-0.68301,-0.68301,0.25882,-0.70711,-0.70711,-0.0,-0.68301,-0.68301,-0.25882,-0.61237,-0.61237,-0.5,-0.5,-0.5,-0.70711,-0.35355,-0.35355,-0.86603,-0.18301,-0.18301,-0.96593,-0.22414,-0.12941,0.96593,-0.43301,-0.25,0.86603,-0.61237,-0.35355,0.70711,-0.75,-0.43301,0.5,-0.83652,-0.48296,0.25882,-0.86602,-0.5,-0.0,-0.83652,-0.48296,-0.25882,-0.75,-0.43301,-0.5,-0.61237,-0.35355,-0.70711,-0.43301,-0.25,-0.86603,-0.22414,-0.12941,-0.96593,0.0,-0.0,-1.0,-0.25,-0.06699,0.96593,-0.48296,-0.12941,0.86603,-0.68301,-0.18301,0.70711,-0.83652,-0.22414,0.5,-0.93301,-0.25,0.25882,-0.96593,-0.25882,-0.0,-0.93301,-0.25,-0.25882,-0.83652,-0.22414,-0.5,-0.68301,-0.18301,-0.70711,-0.48296,-0.12941,-0.86603,-0.25,-0.06699,-0.96593,-0.25882,-0.0,0.96593,-0.5,-0.0,0.86603,-0.70711,-0.0,0.70711,-0.86603,-0.0,0.5,-0.96593,-0.0,0.25882,-1.0,-0.0,-0.0,-0.96593,-0.0,-0.25882,-0.86603,-0.0,-0.5,-0.70711,-0.0,-0.70711,-0.5,-0.0,-0.86603,-0.25882,-0.0,-0.96593,-0.25,0.06699,0.96593,-0.48296,0.12941,0.86603,-0.68301,0.18301,0.70711,-0.83652,0.22414,0.5,-0.93301,0.25,0.25882,-0.96593,0.25882,-0.0,-0.93301,0.25,-0.25882,-0.83652,0.22414,-0.5,-0.68301,0.18301,-0.70711,-0.48296,0.12941,-0.86603,-0.25,0.06699,-0.96593,-0.22414,0.12941,0.96593,-0.43301,0.25,0.86603,-0.61237,0.35355,0.70711,-0.75,0.43301,0.5,-0.83652,0.48296,0.25882,-0.86602,0.5,-0.0,-0.83652,0.48296,-0.25882,-0.75,0.43301,-0.5,-0.61237,0.35355,-0.70711,-0.43301,0.25,-0.86603,-0.22414,0.12941,-0.96593,-0.18301,0.18301,0.96593,-0.35355,0.35355,0.86603,-0.5,0.5,0.70711,-0.61237,0.61237,0.5,-0.68301,0.68301,0.25882,-0.70711,0.70711,-0.0,-0.68301,0.68301,-0.25882,-0.61237,0.61237,-0.5,-0.5,0.5,-0.70711,-0.35355,0.35355,-0.86603,-0.18301,0.18301,-0.96593,-0.12941,0.22414,0.96593,-0.25,0.43301,0.86603,-0.35355,0.61237,0.70711,-0.43301,0.75,0.5,-0.48296,0.83652,0.25882,-0.5,0.86602,-0.0,-0.48296,0.83652,-0.25882,-0.43301,0.75,-0.5,-0.35355,0.61237,-0.70711,-0.25,0.43301,-0.86603,-0.12941,0.22414,-0.96593,-0.06699,0.25,0.96593,-0.12941,0.48296,0.86603,-0.18301,0.68301,0.70711,-0.22414,0.83652,0.5,-0.25,0.93301,0.25882,-0.25882,0.96593,-0.0,-0.25,0.93301,-0.25882,-0.22414,0.83652,-0.5,-0.18301,0.68301,-0.70711,-0.12941,0.48296,-0.86603,-0.06699,0.25,-0.96593,-0.0,0.25882,0.96593,-0.0,0.86603,0.5,-0.0,0.96593,0.25882,0.0,1.0,-0.0,-0.0,0.96593,-0.25882,-0.0,0.86603,-0.5,0.0,0.70711,-0.70711,0.0,0.5,-0.86603,-0.0,0.25882,-0.96593}; +const int _const_data_skydome_pos_count = sizeof(_const_data_skydome_pos) / sizeof(_const_data_skydome_pos[0]); + +const float _const_data_skydome_nor[] = {0.0,-0.50807,-0.86132,0.0,-0.71246,-0.70172,-0.0696,-0.25975,-0.96317,-0.1315,-0.49075,-0.86132,-0.1844,-0.68818,-0.70172,-0.22483,-0.83909,-0.49536,-0.25018,-0.9337,-0.25615,-0.25882,-0.96593,-0.0,-0.25018,-0.9337,0.25615,-0.22483,-0.83909,0.49536,-0.1844,-0.68818,0.70172,-0.1315,-0.49075,0.86132,-0.0696,-0.25975,0.96317,-0.13445,-0.23288,-0.96317,-0.25403,-0.44,-0.86132,-0.35623,-0.61701,-0.70172,-0.43434,-0.75231,-0.49536,-0.48332,-0.83713,-0.25615,-0.5,-0.86603,0.0,-0.48332,-0.83713,0.25615,-0.43434,-0.75231,0.49536,-0.35623,-0.61701,0.70172,-0.25403,-0.44,0.86132,-0.13445,-0.23288,0.96317,-0.19015,-0.19015,-0.96317,-0.35926,-0.35926,-0.86132,-0.50378,-0.50378,-0.70172,-0.61426,-0.61426,-0.49536,-0.68352,-0.68352,-0.25615,-0.70711,-0.70711,0.0,-0.68352,-0.68352,0.25615,-0.61426,-0.61426,0.49536,-0.50378,-0.50378,0.70172,-0.35926,-0.35926,0.86132,-0.19015,-0.19015,0.96317,-0.23288,-0.13445,-0.96317,-0.44,-0.25403,-0.86132,-0.61701,-0.35623,-0.70172,-0.75231,-0.43434,-0.49536,-0.83713,-0.48332,-0.25615,-0.86603,-0.5,0.0,-0.83713,-0.48332,0.25615,-0.75231,-0.43434,0.49536,-0.61701,-0.35623,0.70172,-0.44,-0.25403,0.86132,-0.23288,-0.13445,0.96317,-0.25975,-0.0696,-0.96317,-0.49075,-0.1315,-0.86132,-0.68818,-0.1844,-0.70172,-0.83909,-0.22483,-0.49536,-0.9337,-0.25018,-0.25615,-0.96593,-0.25882,0.0,-0.9337,-0.25018,0.25615,-0.83909,-0.22483,0.49536,-0.68818,-0.1844,0.70172,-0.49075,-0.1315,0.86132,-0.25975,-0.0696,0.96317,-0.26891,-0.0,-0.96317,-0.50807,0.0,-0.86132,-0.71246,0.0,-0.70172,-0.86869,-0.0,-0.49536,-0.96664,0.0,-0.25615,-1.0,0.0,0.0,-0.96664,0.0,0.25615,-0.86869,0.0,0.49536,-0.71246,0.0,0.70172,-0.50807,0.0,0.86132,-0.26891,-0.0,0.96317,-0.25975,0.0696,-0.96317,-0.49075,0.1315,-0.86132,-0.68818,0.1844,-0.70172,-0.83909,0.22483,-0.49536,-0.9337,0.25018,-0.25615,-0.96593,0.25882,0.0,-0.9337,0.25018,0.25615,-0.83909,0.22483,0.49536,-0.68818,0.1844,0.70172,-0.49075,0.1315,0.86132,-0.25975,0.0696,0.96317,-0.23288,0.13445,-0.96317,-0.44,0.25403,-0.86132,-0.61701,0.35623,-0.70172,-0.75231,0.43434,-0.49536,-0.83713,0.48332,-0.25615,-0.86603,0.5,0.0,-0.83713,0.48332,0.25615,-0.75231,0.43434,0.49536,-0.61701,0.35623,0.70172,-0.44,0.25403,0.86132,-0.23288,0.13445,0.96317,-0.19015,0.19015,-0.96317,-0.35926,0.35926,-0.86132,-0.50378,0.50378,-0.70172,-0.61426,0.61426,-0.49536,-0.68352,0.68352,-0.25615,-0.70711,0.70711,0.0,-0.68352,0.68352,0.25615,-0.61426,0.61426,0.49536,-0.50378,0.50378,0.70172,-0.35926,0.35926,0.86132,-0.19015,0.19015,0.96317,-0.13445,0.23288,-0.96317,-0.25403,0.44,-0.86132,-0.35623,0.61701,-0.70172,-0.43434,0.75231,-0.49536,-0.48332,0.83713,-0.25615,-0.5,0.86603,0.0,-0.48332,0.83713,0.25615,-0.43434,0.75231,0.49536,-0.35623,0.61701,0.70172,-0.25403,0.44,0.86132,-0.13445,0.23288,0.96317,-0.0696,0.25975,-0.96317,-0.1315,0.49075,-0.86132,-0.1844,0.68818,-0.70172,-0.22483,0.83909,-0.49536,-0.25018,0.9337,-0.25615,-0.25882,0.96593,0.0,-0.25018,0.9337,0.25615,-0.22483,0.83909,0.49536,-0.1844,0.68818,0.70172,-0.1315,0.49075,0.86132,-0.0696,0.25975,0.96317,-0.0,0.26891,-0.96317,0.0,0.50807,-0.86132,0.0,0.71246,-0.70172,-0.0,0.86869,-0.49536,0.0,0.96664,-0.25615,0.0,1.0,0.0,-0.0,0.96664,0.25615,0.0,0.86869,0.49536,0.0,0.71246,0.70172,0.0,0.50807,0.86132,0.0,0.26891,0.96317,0.0696,0.25975,-0.96317,0.1315,0.49075,-0.86132,0.1844,0.68818,-0.70172,0.22483,0.83909,-0.49536,0.25018,0.9337,-0.25615,0.25882,0.96593,0.0,0.25018,0.9337,0.25615,0.22483,0.83909,0.49536,0.1844,0.68818,0.70172,0.1315,0.49075,0.86132,0.0696,0.25975,0.96317,0.13445,0.23288,-0.96317,0.25403,0.44,-0.86132,0.35623,0.61701,-0.70172,0.43434,0.75231,-0.49536,0.48332,0.83713,-0.25615,0.5,0.86603,0.0,0.48332,0.83713,0.25615,0.43434,0.75231,0.49536,0.35623,0.61701,0.70172,0.25403,0.44,0.86132,0.13445,0.23288,0.96317,0.0,0.0,-1.0,0.19015,0.19015,-0.96317,0.35926,0.35926,-0.86132,0.50378,0.50378,-0.70172,0.61426,0.61426,-0.49536,0.68352,0.68352,-0.25615,0.70711,0.70711,0.0,0.68352,0.68352,0.25615,0.61426,0.61426,0.49536,0.50378,0.50378,0.70172,0.35926,0.35926,0.86132,0.19015,0.19015,0.96317,0.23288,0.13445,-0.96317,0.44,0.25403,-0.86132,0.61701,0.35623,-0.70172,0.75231,0.43434,-0.49536,0.83713,0.48332,-0.25615,0.86603,0.5,0.0,0.83713,0.48332,0.25615,0.75231,0.43434,0.49536,0.61701,0.35623,0.70172,0.44,0.25403,0.86132,0.23288,0.13445,0.96317,0.0,-0.0,1.0,0.25975,0.0696,-0.96317,0.49075,0.1315,-0.86132,0.68818,0.1844,-0.70172,0.83909,0.22483,-0.49536,0.9337,0.25018,-0.25615,0.96593,0.25882,0.0,0.9337,0.25018,0.25615,0.83909,0.22483,0.49536,0.68818,0.1844,0.70172,0.49075,0.1315,0.86132,0.25975,0.0696,0.96317,0.26891,-0.0,-0.96317,0.50807,-0.0,-0.86132,0.71246,0.0,-0.70172,0.86869,0.0,-0.49536,0.96664,-0.0,-0.25615,1.0,-0.0,0.0,0.96664,0.0,0.25615,0.86869,0.0,0.49536,0.71246,-0.0,0.70172,0.50807,0.0,0.86132,0.26891,-0.0,0.96317,0.25975,-0.0696,-0.96317,0.49075,-0.1315,-0.86132,0.68818,-0.1844,-0.70172,0.83909,-0.22483,-0.49536,0.9337,-0.25018,-0.25615,0.96593,-0.25882,0.0,0.9337,-0.25018,0.25615,0.83909,-0.22483,0.49536,0.68818,-0.1844,0.70172,0.49075,-0.1315,0.86132,0.25975,-0.0696,0.96317,0.23288,-0.13445,-0.96317,0.44,-0.25403,-0.86132,0.61701,-0.35623,-0.70172,0.75231,-0.43434,-0.49536,0.83713,-0.48332,-0.25615,0.86603,-0.5,0.0,0.83713,-0.48332,0.25615,0.75231,-0.43434,0.49536,0.617,-0.35623,0.70172,0.44,-0.25403,0.86132,0.23288,-0.13445,0.96317,0.19015,-0.19015,-0.96317,0.35926,-0.35926,-0.86132,0.50378,-0.50378,-0.70172,0.61426,-0.61426,-0.49536,0.68352,-0.68352,-0.25615,0.70711,-0.70711,-0.0,0.68352,-0.68352,0.25615,0.61426,-0.61426,0.49536,0.50378,-0.50378,0.70172,0.35926,-0.35926,0.86132,0.19015,-0.19015,0.96317,0.13445,-0.23288,-0.96317,0.25403,-0.44,-0.86132,0.35623,-0.61701,-0.70172,0.43434,-0.75231,-0.49536,0.48332,-0.83713,-0.25615,0.5,-0.86603,0.0,0.48332,-0.83713,0.25615,0.43434,-0.75231,0.49536,0.35623,-0.617,0.70172,0.25403,-0.44,0.86132,0.13445,-0.23288,0.96317,0.0696,-0.25975,-0.96317,0.1315,-0.49075,-0.86132,0.1844,-0.68818,-0.70172,0.22483,-0.83909,-0.49536,0.25018,-0.9337,-0.25615,0.25882,-0.96593,0.0,0.25018,-0.9337,0.25615,0.22483,-0.83909,0.49536,0.1844,-0.68818,0.70172,0.1315,-0.49075,0.86132,0.0696,-0.25975,0.96317,0.0,-0.26891,-0.96317,0.0,-0.86869,-0.49536,0.0,-0.96664,-0.25615,0.0,-1.0,-0.0,0.0,-0.96664,0.25615,0.0,-0.86869,0.49536,0.0,-0.71246,0.70172,0.0,-0.50807,0.86132,0.0,-0.26891,0.96317}; +const int _const_data_skydome_nor_count = sizeof(_const_data_skydome_nor) / sizeof(_const_data_skydome_nor[0]); diff --git a/armorcore/sources/const_data.h b/armorcore/sources/const_data.h new file mode 100644 index 000000000..57acb31af --- /dev/null +++ b/armorcore/sources/const_data.h @@ -0,0 +1,9 @@ + +#pragma once + +extern const int _const_data_skydome_indices[]; +extern const int _const_data_skydome_indices_count; +extern const float _const_data_skydome_pos[]; +extern const int _const_data_skydome_pos_count; +extern const float _const_data_skydome_nor[]; +extern const int _const_data_skydome_nor_count; diff --git a/armorcore/sources/iron.c b/armorcore/sources/iron.c new file mode 100644 index 000000000..ff042b413 --- /dev/null +++ b/armorcore/sources/iron.c @@ -0,0 +1 @@ +#include IRON_C_PATH diff --git a/armorcore/sources/iron.h b/armorcore/sources/iron.h new file mode 100644 index 000000000..0a4ba658c --- /dev/null +++ b/armorcore/sources/iron.h @@ -0,0 +1,2995 @@ +#pragma once + +#pragma clang diagnostic ignored "-Wincompatible-pointer-types" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "iron_string.h" +#include "iron_array.h" +#include "iron_map.h" +#include "iron_armpack.h" +#include "iron_json.h" +#include "iron_gc.h" +#include "iron_obj.h" +#include "iron_vec2.h" +#include "iron_vec3.h" +#include "iron_vec4.h" +#include "iron_quat.h" +#include "iron_mat3.h" +#include "iron_mat4.h" +#include "iron_ui.h" +#include "iron_ui_ext.h" +#include "iron_ui_nodes.h" +#ifdef KINC_WINDOWS +#include +#endif +#if defined(IDLE_SLEEP) && !defined(KINC_WINDOWS) +#include +#endif +#ifdef WITH_AUDIO +#include +#endif +#include +#include +#ifdef WITH_EMBED +#include EMBED_H_PATH +#endif +#ifdef WITH_ONNX +#include +#ifdef KINC_WINDOWS +#include +#elif defined(KINC_MACOS) +#include +#endif +const OrtApi *ort = NULL; +OrtEnv *ort_env; +OrtSessionOptions *ort_session_options; +OrtSession *session = NULL; +#endif + +int _argc; +char **_argv; + +#ifdef WITH_EVAL +#include "quickjs/quickjs.h" +#include "quickjs/quickjs-libc.h" +JSRuntime *js_runtime = NULL; +JSContext *js_ctx; +#ifdef WITH_PLUGINS +void plugin_api_init(); +#endif + +void js_init() { + js_runtime = JS_NewRuntime(); + js_ctx = JS_NewContext(js_runtime); + js_std_add_helpers(js_ctx, _argc, _argv); + js_init_module_std(js_ctx, "std"); + js_init_module_os(js_ctx, "os"); + #ifdef WITH_PLUGINS + plugin_api_init(); + #endif +} + +float js_eval(const char *js) { + if (js_runtime == NULL) { + js_init(); + } + JSValue ret = JS_Eval(js_ctx, js, strlen(js), "armorcore", JS_EVAL_TYPE_GLOBAL); + if (JS_IsException(ret)) { + js_std_dump_error(js_ctx); + JS_ResetUncatchableError(js_ctx); + } + double d; + JS_ToFloat64(js_ctx, &d, ret); + JS_RunGC(js_runtime); + if (d != d) { // nan + d = 0.0; + } + return d; +} + +JSValue js_call_result; + +JSValue *js_call_arg(void *p, int argc, JSValue *argv) { + if (js_runtime == NULL) { + js_init(); + } + JSValue fn = *(JSValue *)p; + JSValue global_obj = JS_GetGlobalObject(js_ctx); + js_call_result = JS_Call(js_ctx, fn, global_obj, argc, argv); + if (JS_IsException(js_call_result)) { + js_std_dump_error(js_ctx); + JS_ResetUncatchableError(js_ctx); + } + JS_FreeValue(js_ctx, global_obj); + return &js_call_result; +} + +char *js_call_ptr(void *p, void *arg) { + JSValue argv[] = { JS_NewInt64(js_ctx, (int64_t)arg) }; + return (char *)JS_ToCString(js_ctx, *js_call_arg(p, 1, argv)); +} + +char *js_call_ptr_str(void *p, void *arg0, char *arg1) { + JSValue argv[] = { JS_NewInt64(js_ctx, (int64_t)arg0), JS_NewString(js_ctx, arg1) }; + return (char *)JS_ToCString(js_ctx, *js_call_arg(p, 2, argv)); +} + +void *js_pcall_str(void *p, char *arg0) { + JSValue argv[] = { JS_NewString(js_ctx, arg0) }; + int64_t result; + JS_ToInt64(js_ctx, &result, *js_call_arg(p, 1, argv)); + return (void *)result; +} + +char *js_call(void *p) { + return (char *)JS_ToCString(js_ctx, *js_call_arg(p, 0, NULL)); +} +#endif + +#ifdef WITH_EMBED +buffer_t *embed_get(char *key) { + #ifdef KINC_WINDOWS + key = string_replace_all(key, "\\", "/"); + #endif + for (int i = 0; i < embed_count; ++i) { + if (strcmp(embed_keys[i], key) == 0) { + buffer_t *buffer = buffer_create(0); + buffer->buffer = embed_values[i]; + buffer->length = embed_sizes[i]; + return buffer; + } + } + return NULL; +} +#endif + +#define f64 double +#define i64 int64_t +#define u64 uint64_t +#define f32 float +#define i32 int32_t +#define u32 uint32_t +#define i16 int16_t +#define u16 uint16_t +#define i8 int8_t +#define u8 uint8_t +#define string_t char +#define any void * +#define any_ptr void ** +#define f64_ptr f64 * +#define i64_ptr i64 * +#define u64_ptr u64 * +#define f32_ptr f32 * +#define i32_ptr i32 * +#define u32_ptr u32 * +#define i16_ptr i16 * +#define u16_ptr u16 * +#define i8_ptr i8 * +#define u8_ptr u8 * +#define null NULL +#define DEREFERENCE * +#define ADDRESS & +#define ARRAY_ACCESS(a, i) a[i] + +void _kickstart(); + +bool enable_window = true; +bool in_background = false; +int paused_frames = 0; +bool save_and_quit_callback_set = false; +#ifdef IDLE_SLEEP +bool input_down = false; +int last_window_width = 0; +int last_window_height = 0; +#endif + +char temp_string[1024 * 32]; +char temp_string_vs[1024 * 128]; +char temp_string_fs[1024 * 128]; +char temp_string_vstruct[4][32][32]; +#ifdef KINC_WINDOWS +wchar_t temp_wstring[1024 * 32]; +bool show_window = false; +#endif + +void (*iron_update)(void); +void (*iron_drop_files)(char *); +char *(*iron_cut)(void *); +char *(*iron_copy)(void *); +void (*iron_paste)(char *, void *); +void (*iron_foreground)(void); +void (*iron_resume)(void); +void (*iron_pause)(void); +void (*iron_background)(void); +void (*iron_shutdown)(void); +void (*iron_pause)(void); +void (*iron_key_down)(int); +void (*iron_key_up)(int); +void (*iron_key_press)(int); +void (*iron_mouse_down)(int, int, int); +void (*iron_mouse_up)(int, int, int); +void (*iron_mouse_move)(int, int, int, int); +void (*iron_mouse_wheel)(int); +void (*iron_touch_down)(int, int, int); +void (*iron_touch_up)(int, int, int); +void (*iron_touch_move)(int, int, int); +void (*iron_pen_down)(int, int, float); +void (*iron_pen_up)(int, int, float); +void (*iron_pen_move)(int, int, float); +void (*iron_gamepad_axis)(int, int, float); +void (*iron_gamepad_button)(int, int, float); +void (*iron_save_and_quit)(bool); + +char *_substring(char *s, int32_t start, int32_t end) { + char *buffer = calloc(1, end - start + 1); + for (int i = 0; i < end - start; ++i) { + buffer[i] = s[start + i]; + } + return buffer; +} + +int kickstart(int argc, char **argv) { + _argc = argc; + _argv = argv; +#ifdef KINC_ANDROID + char *bindir = "/"; +#elif defined(KINC_IOS) + char *bindir = ""; +#else + char *bindir = argv[0]; +#endif + +#ifdef KINC_WINDOWS // Handle non-ascii path + HMODULE hmodule = GetModuleHandleW(NULL); + GetModuleFileNameW(hmodule, temp_wstring, 1024); + WideCharToMultiByte(CP_UTF8, 0, temp_wstring, -1, temp_string, 4096, NULL, NULL); + bindir = temp_string; +#endif + +#ifdef KINC_WINDOWS + bindir = _substring(bindir, 0, string_last_index_of(bindir, "\\")); +#else + bindir = _substring(bindir, 0, string_last_index_of(bindir, "/")); +#endif + char *assetsdir = argc > 1 ? argv[1] : bindir; + + // Opening a file + int l = strlen(assetsdir); + if ((l > 6 && assetsdir[l - 6] == '.') || + (l > 5 && assetsdir[l - 5] == '.') || + (l > 4 && assetsdir[l - 4] == '.')) { + assetsdir = bindir; + } + + for (int i = 2; i < argc; ++i) { + if (strcmp(argv[i], "--nowindow") == 0) { + enable_window = false; + } + } + +#if !defined(KINC_MACOS) && !defined(KINC_IOS) + kinc_internal_set_files_location(assetsdir); +#endif + +#ifdef KINC_MACOS + // Handle loading assets located outside of '.app/Contents/Resources/Deployment' folder + // when assets and shaders dir is passed as an argument + if (argc > 2) { + kinc_internal_set_files_location(&assetsdir[0u]); + } +#endif + + kinc_threads_init(); + kinc_display_init(); + + gc_start(&argc); + _kickstart(); + + #ifdef WITH_AUDIO + kinc_a2_shutdown(); + #endif + #ifdef WITH_ONNX + if (ort != NULL) { + ort->ReleaseEnv(ort_env); + ort->ReleaseSessionOptions(ort_session_options); + } + #endif + gc_stop(); + return 0; +} + +i32 iron_get_arg_count() { + return _argc; +} + +string_t *iron_get_arg(i32 index) { + return _argv[index]; +} + +// ██╗██████╗ ██████╗ ███╗ ██╗ █████╗ ██████╗ ██╗ +// ██║██╔══██╗██╔═══██╗████╗ ██║ ██╔══██╗██╔══██╗██║ +// ██║██████╔╝██║ ██║██╔██╗ ██║ ███████║██████╔╝██║ +// ██║██╔══██╗██║ ██║██║╚██╗██║ ██╔══██║██╔═══╝ ██║ +// ██║██║ ██║╚██████╔╝██║ ╚████║ ██║ ██║██║ ██║ +// ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝ + +#ifndef NO_IRON_API + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef WITH_AUDIO +#include +#include +#endif +int LZ4_decompress_safe(const char *source, char *dest, int compressed_size, int maxOutputSize); +#define STB_IMAGE_IMPLEMENTATION +#include +#ifdef KINC_DIRECT3D11 +#include +#endif +#ifdef KINC_DIRECT3D12 +#include +extern bool waitAfterNextDraw; +#endif +#if defined(KINC_DIRECT3D12) || defined(KINC_VULKAN) || defined(KINC_METAL) +#include +#include +#include +#endif + +#ifdef KINC_WINDOWS +#include +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif +struct HWND__ *kinc_windows_window_handle(int window_index); +// Enable visual styles for ui controls +#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#endif +#ifdef WITH_D3DCOMPILER +#include +#include +#endif +#ifdef WITH_NFD +#include +#elif defined(KINC_ANDROID) +#include "kinc/backend/android_file_dialog.h" +#include "kinc/backend/android_http_request.h" +#elif defined(KINC_IOS) +#include +#include +#endif +#include "dir.h" +#ifdef WITH_IMAGE_WRITE +#ifdef WITH_COMPRESS +unsigned char *iron_deflate_raw(unsigned char *data, int data_len, int *out_len, int quality); +#define STBIW_ZLIB_COMPRESS iron_deflate_raw +#endif +#define STBI_WINDOWS_UTF8 +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include +#endif +#ifdef WITH_MPEG_WRITE +#include +#endif +#ifdef WITH_COMPRESS +#define SDEFL_IMPLEMENTATION +#include "sdefl.h" +#define SINFL_IMPLEMENTATION +#include "sinfl.h" +#endif +#if defined(IDLE_SLEEP) && !defined(KINC_WINDOWS) +#include +#endif + +#ifdef KINC_MACOS +const char *macgetresourcepath(); +#endif +#ifdef KINC_IOS +const char *iphonegetresourcepath(); +#endif + +#if defined(KINC_IOS) || defined(KINC_ANDROID) +char mobile_title[1024]; +#endif + +#if defined(KINC_VULKAN) && defined(KRAFIX_LIBRARY) +int krafix_compile(const char *source, char *output, int *length, const char *targetlang, const char *system, const char *shadertype, int version); +#endif + +#if defined(KINC_DIRECT3D12) || defined(KINC_VULKAN) || defined(KINC_METAL) +extern kinc_g5_command_list_t commandList; +static kinc_g5_constant_buffer_t constant_buffer; +static kinc_g4_render_target_t *render_target; +static kinc_raytrace_pipeline_t pipeline; +static kinc_raytrace_acceleration_structure_t accel; +static bool accel_created = false; +const int constant_buffer_size = 24; +#endif + +void _update(void *data) { + #ifdef KINC_WINDOWS + if (show_window && enable_window) { + show_window = false; + kinc_window_show(0); + } + + if (in_background && ++paused_frames > 3) { + Sleep(1); + return; + } + #endif + + #ifdef IDLE_SLEEP + if (last_window_width != kinc_window_width(0) || last_window_height != kinc_window_height(0)) { + last_window_width = kinc_window_width(0); + last_window_height = kinc_window_height(0); + paused_frames = 0; + } + #if defined(KINC_IOS) || defined(KINC_ANDROID) + const int start_sleep = 1200; + #else + const int start_sleep = 120; + #endif + if (++paused_frames > start_sleep && !input_down) { + #ifdef KINC_WINDOWS + Sleep(1); + #else + usleep(1000); + #endif + return; + } + if (paused_frames == 30) { + gc_run(); + } + #endif + + #ifdef WITH_AUDIO + kinc_a2_update(); + #endif + + kinc_g4_begin(0); + iron_update(); + kinc_g4_end(0); + kinc_g4_swap_buffers(); +} + +char *_copy(void *data) { + // strcpy(temp_string, iron_copy()); + strcpy(temp_string, ui_copy()); + return temp_string; +} + +char *_cut(void *data) { + // strcpy(temp_string, iron_cut()); + strcpy(temp_string, ui_cut()); + return temp_string; +} + +void _paste(char *text, void *data) { + // iron_paste(text); + ui_paste(text); +} + +void _foreground(void *data) { + iron_foreground(); + in_background = false; +} + +void _resume(void *data) { + iron_resume(); +} + +void _pause(void *data) { + iron_pause(); +} + +void _background(void *data) { + iron_background(); + in_background = true; + paused_frames = 0; +} + +void _shutdown(void *data) { + iron_shutdown(); +} + +void _key_down(int code, void *data) { + iron_key_down(code); + + for (int i = 0; i < ui_instances_count; ++i) { + ui_key_down(ui_instances[i], code); + } + + #ifdef IDLE_SLEEP + input_down = true; + paused_frames = 0; + #endif +} + +void _key_up(int code, void *data) { + iron_key_up(code); + + for (int i = 0; i < ui_instances_count; ++i) { + ui_key_up(ui_instances[i], code); + } + + #ifdef IDLE_SLEEP + input_down = false; + paused_frames = 0; + #endif +} + +void _key_press(unsigned int character, void *data) { + iron_key_press(character); + + for (int i = 0; i < ui_instances_count; ++i) { + ui_key_press(ui_instances[i], character); + } + + #ifdef IDLE_SLEEP + paused_frames = 0; + #endif +} + +void _mouse_down(int window, int button, int x, int y, void *data) { + iron_mouse_down(button, x, y); + + for (int i = 0; i < ui_instances_count; ++i) { + ui_mouse_down(ui_instances[i], button, x, y); + } + + #ifdef IDLE_SLEEP + input_down = true; + paused_frames = 0; + #endif +} + +void _mouse_up(int window, int button, int x, int y, void *data) { + iron_mouse_up(button, x, y); + + for (int i = 0; i < ui_instances_count; ++i) { + ui_mouse_up(ui_instances[i], button, x, y); + } + + #ifdef IDLE_SLEEP + input_down = false; + paused_frames = 0; + #endif +} + +void _mouse_move(int window, int x, int y, int mx, int my, void *data) { + iron_mouse_move(x, y, mx, my); + + for (int i = 0; i < ui_instances_count; ++i) { + ui_mouse_move(ui_instances[i], x, y, mx, my); + } + + #ifdef IDLE_SLEEP + paused_frames = 0; + #endif +} + +void _mouse_wheel(int window, int delta, void *data) { + iron_mouse_wheel(delta); + + for (int i = 0; i < ui_instances_count; ++i) { + ui_mouse_wheel(ui_instances[i], delta); + } + + #ifdef IDLE_SLEEP + paused_frames = 0; + #endif +} + +void _touch_move(int index, int x, int y) { + iron_touch_move(index, x, y); + + #if defined(KINC_ANDROID) || defined(KINC_IOS) + for (int i = 0; i < ui_instances_count; ++i) { + ui_touch_move(ui_instances[i], index, x, y); + } + #endif + + #ifdef IDLE_SLEEP + paused_frames = 0; + #endif +} + +void _touch_down(int index, int x, int y) { + iron_touch_down(index, x, y); + + #if defined(KINC_ANDROID) || defined(KINC_IOS) + for (int i = 0; i < ui_instances_count; ++i) { + ui_touch_down(ui_instances[i], index, x, y); + } + #endif + + #ifdef IDLE_SLEEP + input_down = true; + paused_frames = 0; + #endif +} + +void _touch_up(int index, int x, int y) { + iron_touch_up(index, x, y); + + #if defined(KINC_ANDROID) || defined(KINC_IOS) + for (int i = 0; i < ui_instances_count; ++i) { + ui_touch_up(ui_instances[i], index, x, y); + } + #endif + + #ifdef IDLE_SLEEP + input_down = false; + paused_frames = 0; + #endif +} + +void _pen_down(int window, int x, int y, float pressure) { + iron_pen_down(x, y, pressure); + + for (int i = 0; i < ui_instances_count; ++i) { + ui_pen_down(ui_instances[i], x, y, pressure); + } + + #ifdef IDLE_SLEEP + input_down = true; + paused_frames = 0; + #endif +} + +void _pen_up(int window, int x, int y, float pressure) { + iron_pen_up(x, y, pressure); + + for (int i = 0; i < ui_instances_count; ++i) { + ui_pen_up(ui_instances[i], x, y, pressure); + } + + #ifdef IDLE_SLEEP + input_down = false; + paused_frames = 0; + #endif +} + +void _pen_move(int window, int x, int y, float pressure) { + iron_pen_move(x, y, pressure); + + for (int i = 0; i < ui_instances_count; ++i) { + ui_pen_move(ui_instances[i], x, y, pressure); + } + + #ifdef IDLE_SLEEP + paused_frames = 0; + #endif +} + +void _gamepad_axis(int gamepad, int axis, float value, void *data) { + iron_gamepad_axis(gamepad, axis, value); + + #ifdef IDLE_SLEEP + paused_frames = 0; + #endif +} + +void _gamepad_button(int gamepad, int button, float value, void *data) { + iron_gamepad_button(gamepad, button, value); + + #ifdef IDLE_SLEEP + paused_frames = 0; + #endif +} + +void _drop_files(wchar_t *file_path, void *data) { +// Update mouse position +#ifdef KINC_WINDOWS + POINT p; + GetCursorPos(&p); + ScreenToClient(kinc_windows_window_handle(0), &p); + _mouse_move(0, p.x, p.y, 0, 0, NULL); +#endif + + // if (sizeof(wchar_t) == 2) { + // (const uint16_t *)file_path + // } + // else { + // size_t len = wcslen(file_path); + // uint16_t *str = new uint16_t[len + 1]; + // for (int i = 0; i < len; i++) str[i] = file_path[i]; + // str[len] = 0; + // } + + char buffer[512]; + wcstombs(buffer, file_path, sizeof(buffer)); + iron_drop_files(buffer); + in_background = false; + +#ifdef IDLE_SLEEP + paused_frames = 0; +#endif +} + +char *uri_decode(const char *src) { + char *res = gc_alloc(1024); + char *dst = res; + char a, b; + while (*src) { + if ((*src == '%') && ((a = src[1]) && (b = src[2])) && (isxdigit(a) && isxdigit(b))) { + if (a >= 'a') { + a -= 'a' - 'A'; + } + if (a >= 'A') { + a -= ('A' - 10); + } + else { + a -= '0'; + } + if (b >= 'a') { + b -= 'a' - 'A'; + } + if (b >= 'A') { + b -= ('A' - 10); + } + else { + b -= '0'; + } + *dst++ = 16 * a + b; + src += 3; + } + else if (*src == '+') { + *dst++ = ' '; + src++; + } + else { + *dst++ = *src++; + } + } + *dst++ = '\0'; + return res; +} + +f32 math_floor(f32 x) { return floorf(x); } +f32 math_cos(f32 x) { return cosf(x); } +f32 math_sin(f32 x) { return sinf(x); } +f32 math_tan(f32 x) { return tanf(x); } +f32 math_sqrt(f32 x) { return sqrtf(x); } +f32 math_abs(f32 x) { return fabsf(x); } +f32 math_random() { return rand() / (float)RAND_MAX; } +f32 math_atan2(f32 y, f32 x) { return atan2f(y, x); } +f32 math_asin(f32 x) { return asinf(x); } +f32 math_pi() { return 3.14159265358979323846; } +f32 math_pow(f32 x, f32 y) { return powf(x, y); } +f32 math_round(f32 x) { return roundf(x); } +f32 math_ceil(f32 x) { return ceilf(x); } +f32 math_min(f32 x, f32 y) { return x < y ? x : y; } +f32 math_max(f32 x, f32 y) { return x > y ? x : y; } +f32 math_log(f32 x) { return logf(x); } +f32 math_log2(f32 x) { return log2f(x); } +f32 math_atan(f32 x) { return atanf(x); } +f32 math_acos(f32 x) { return acosf(x); } +f32 math_exp(f32 x) { return expf(x); } +f32 math_fmod(f32 x, f32 y) { return fmod(x, y); } + +#ifdef _WIN32 +i32 parse_int(const char *s) { return _strtoi64(s, NULL, 10); } +i32 parse_int_hex(const char *s) { return _strtoi64(s, NULL, 16); } +#else +i32 parse_int(const char *s) { return strtol(s, NULL, 10); } +i32 parse_int_hex(const char *s) { return strtol(s, NULL, 16); } +#endif +f32 parse_float(const char *s) { return strtof(s, NULL); } + +i32 color_from_floats(f32 r, f32 g, f32 b, f32 a) { + return ((int)(a * 255) << 24) | ((int)(r * 255) << 16) | ((int)(g * 255) << 8) | (int)(b * 255); +} + +u8 color_get_rb(i32 c) { + return (c & 0x00ff0000) >> 16; +} + +u8 color_get_gb(i32 c) { + return (c & 0x0000ff00) >> 8; +} + +u8 color_get_bb(i32 c) { + return c & 0x000000ff; +} + +u8 color_get_ab(i32 c) { + return c & 0x000000ff; +} + +i32 color_set_rb(i32 c, u8 i) { + return (color_get_ab(c) << 24) | (i << 16) | (color_get_gb(c) << 8) | color_get_bb(c); +} + +i32 color_set_gb(i32 c, u8 i) { + return (color_get_ab(c) << 24) | (color_get_rb(c) << 16) | (i << 8) | color_get_bb(c); +} + +i32 color_set_bb(i32 c, u8 i) { + return (color_get_ab(c) << 24) | (color_get_rb(c) << 16) | (color_get_gb(c) << 8) | i; +} + +i32 color_set_ab(i32 c, u8 i) { + return (i << 24) | (color_get_rb(c) << 16) | (color_get_gb(c) << 8) | color_get_bb(c); +} + +// ██╗ ██╗ ██████╗ ██████╗ ███╗ ███╗ +// ██║ ██╔╝ ██╔══██╗ ██╔═══██╗ ████╗ ████║ +// █████╔╝ ██████╔╝ ██║ ██║ ██╔████╔██║ +// ██╔═██╗ ██╔══██╗ ██║ ██║ ██║╚██╔╝██║ +// ██║ ██╗ ██║ ██║ ╚██████╔╝ ██║ ╚═╝ ██║ +// ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ + +void iron_init(string_t *title, i32 width, i32 height, bool vsync, i32 window_mode, i32 window_features, i32 x, i32 y, i32 frequency) { + kinc_window_options_t win; + kinc_framebuffer_options_t frame; + win.title = title; + win.width = width; + win.height = height; + frame.vertical_sync = vsync; + win.mode = (kinc_window_mode_t)window_mode; + win.window_features = window_features; + win.x = x; + win.y = y; + frame.frequency = frequency; + + win.display_index = -1; + #ifdef KINC_WINDOWS + win.visible = false; // Prevent white flicker when opening the window + #else + win.visible = enable_window; + #endif + frame.color_bits = 32; + frame.depth_bits = 0; + frame.stencil_bits = 0; + frame.samples_per_pixel = 1; + kinc_init(title, win.width, win.height, &win, &frame); + kinc_random_init((int)(kinc_time() * 1000)); + + #ifdef KINC_WINDOWS + // Maximized window has x < -1, prevent window centering done by kinc + if (win.x < -1 && win.y < -1) { + kinc_window_move(0, win.x, win.y); + } + + char vdata[4]; + DWORD cbdata = 4 * sizeof(char); + RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", L"AppsUseLightTheme", RRF_RT_REG_DWORD, NULL, vdata, &cbdata); + BOOL use_dark_mode = (int)(vdata[3] << 24 | vdata[2] << 16 | vdata[1] << 8 | vdata[0]) != 1; + DwmSetWindowAttribute(kinc_windows_window_handle(0), DWMWA_USE_IMMERSIVE_DARK_MODE, &use_dark_mode, sizeof(use_dark_mode)); + + show_window = true; + #endif + + #ifdef WITH_AUDIO + kinc_a1_init(); + kinc_a2_init(); + #endif + + #ifdef KINC_ANDROID + android_check_permissions(); + #endif +} + +void iron_set_app_name(string_t *name) { + kinc_set_application_name(name); +} + +void iron_log(string_t *value) { + if (value != NULL) { + kinc_log(KINC_LOG_LEVEL_INFO, value); + } + else { + kinc_log(KINC_LOG_LEVEL_INFO, "null"); + } +} + +void iron_g4_clear(i32 flags, i32 color, f32 depth) { + kinc_g4_clear(flags, color, depth, 0); +} + +void iron_set_update_callback(void (*callback)(void)) { + iron_update = callback; + kinc_set_update_callback(_update, NULL); +} + +void iron_set_drop_files_callback(void (*callback)(char *)) { + iron_drop_files = callback; + kinc_set_drop_files_callback(_drop_files, NULL); +} + +void iron_set_cut_copy_paste_callback(char *(*on_cut)(void *), char *(*on_copy)(void *), void (*on_paste)(char *, void *)) { + kinc_set_cut_callback(_cut, NULL); + kinc_set_copy_callback(_copy, NULL); + kinc_set_paste_callback(_paste, NULL); + iron_cut = on_cut; + iron_copy = on_copy; + iron_paste = on_paste; +} + +void iron_set_application_state_callback(void (*on_foreground)(void), void (*on_resume)(void), void (*on_pause)(void), void (*on_background)(void), void (*on_shutdown)(void)) { + kinc_set_foreground_callback(on_foreground != NULL ? _foreground : NULL, NULL); + kinc_set_resume_callback(on_resume != NULL ? _resume : NULL, NULL); + kinc_set_pause_callback(on_pause != NULL ? _pause : NULL, NULL); + kinc_set_background_callback(on_background != NULL ? _background : NULL, NULL); + kinc_set_shutdown_callback(on_shutdown != NULL ? _shutdown : NULL, NULL); + iron_foreground = on_foreground; + iron_resume = on_resume; + iron_pause = on_pause; + iron_background = on_background; + iron_shutdown = on_shutdown; +} + +void iron_set_keyboard_down_callback(void (*callback)(int)) { + iron_key_down = callback; + kinc_keyboard_set_key_down_callback(_key_down, NULL); +} + +void iron_set_keyboard_up_callback(void (*callback)(int)) { + iron_key_up = callback; + kinc_keyboard_set_key_up_callback(_key_up, NULL); +} + +void iron_set_keyboard_press_callback(void (*callback)(int)) { + iron_key_press = callback; + kinc_keyboard_set_key_press_callback(_key_press, NULL); +} + +void iron_set_mouse_down_callback(void (*callback)(int, int, int)) { + iron_mouse_down = callback; + kinc_mouse_set_press_callback(_mouse_down, NULL); +} + +void iron_set_mouse_up_callback(void (*callback)(int, int, int)) { + iron_mouse_up = callback; + kinc_mouse_set_release_callback(_mouse_up, NULL); +} + +void iron_set_mouse_move_callback(void (*callback)(int, int, int, int)) { + iron_mouse_move = callback; + kinc_mouse_set_move_callback(_mouse_move, NULL); +} + +void iron_set_mouse_wheel_callback(void (*callback)(int)) { + iron_mouse_wheel = callback; + kinc_mouse_set_scroll_callback(_mouse_wheel, NULL); +} + +void iron_set_touch_down_callback(void (*callback)(int, int, int)) { + iron_touch_down = callback; + kinc_surface_set_touch_start_callback(_touch_down); +} + +void iron_set_touch_up_callback(void (*callback)(int, int, int)) { + iron_touch_up = callback; + kinc_surface_set_touch_end_callback(_touch_up); +} + +void iron_set_touch_move_callback(void (*callback)(int, int, int)) { + iron_touch_move = callback; + kinc_surface_set_move_callback(_touch_move); +} + +void iron_set_pen_down_callback(void (*callback)(int, int, float)) { + iron_pen_down = callback; + kinc_pen_set_press_callback(_pen_down); +} + +void iron_set_pen_up_callback(void (*callback)(int, int, float)) { + iron_pen_up = callback; + kinc_pen_set_release_callback(_pen_up); +} + +void iron_set_pen_move_callback(void (*callback)(int, int, float)) { + iron_pen_move = callback; + kinc_pen_set_move_callback(_pen_move); +} + +void iron_set_gamepad_axis_callback(void (*callback)(int, int, float)) { + iron_gamepad_axis = callback; + kinc_gamepad_set_axis_callback(_gamepad_axis, NULL); +} + +void iron_set_gamepad_button_callback(void (*callback)(int, int, float)) { + iron_gamepad_button = callback; + kinc_gamepad_set_button_callback(_gamepad_button, NULL); +} + +void iron_lock_mouse() { + kinc_mouse_lock(0); +} + +void iron_unlock_mouse() { + kinc_mouse_unlock(); +} + +bool iron_can_lock_mouse() { + return kinc_mouse_can_lock(); +} + +bool iron_is_mouse_locked() { + return kinc_mouse_is_locked(); +} + +void iron_set_mouse_position(i32 x, i32 y) { + kinc_mouse_set_position(0, x, y); +} + +void iron_show_mouse(bool show) { + show ? kinc_mouse_show() : kinc_mouse_hide(); +} + +void iron_show_keyboard(bool show) { + show ? kinc_keyboard_show() : kinc_keyboard_hide(); +} + + +any iron_g4_create_index_buffer(i32 count) { + kinc_g4_index_buffer_t *buffer = (kinc_g4_index_buffer_t *)malloc(sizeof(kinc_g4_index_buffer_t)); + kinc_g4_index_buffer_init(buffer, count, KINC_G4_INDEX_BUFFER_FORMAT_32BIT, KINC_G4_USAGE_STATIC); + return buffer; +} + +void iron_g4_delete_index_buffer(kinc_g4_index_buffer_t *buffer) { + kinc_g4_index_buffer_destroy(buffer); + free(buffer); +} + +u32_array_t *iron_g4_lock_index_buffer(kinc_g4_index_buffer_t *buffer) { + void *vertices = kinc_g4_index_buffer_lock_all(buffer); + u32_array_t *ar = (u32_array_t *)malloc(sizeof(u32_array_t)); + ar->buffer = vertices; + ar->length = kinc_g4_index_buffer_count(buffer); + return ar; +} + +void iron_g4_unlock_index_buffer(kinc_g4_index_buffer_t *buffer) { + kinc_g4_index_buffer_unlock_all(buffer); +} + +void iron_g4_set_index_buffer(kinc_g4_index_buffer_t *buffer) { + kinc_g4_set_index_buffer(buffer); +} + +typedef struct kinc_vertex_elem { + char *name; + int data; // vertex_data_t +} kinc_vertex_elem_t; + +any iron_g4_create_vertex_buffer(i32 count, any_array_t *elements, i32 usage, i32 inst_data_step_rate) { + kinc_g4_vertex_structure_t structure; + kinc_g4_vertex_structure_init(&structure); + for (int32_t i = 0; i < elements->length; ++i) { + kinc_vertex_elem_t *element = elements->buffer[i]; + char *str = element->name; + int32_t data = element->data; + strcpy(temp_string_vstruct[0][i], str); + kinc_g4_vertex_structure_add(&structure, temp_string_vstruct[0][i], (kinc_g4_vertex_data_t)data); + } + kinc_g4_vertex_buffer_t *buffer = (kinc_g4_vertex_buffer_t *)malloc(sizeof(kinc_g4_vertex_buffer_t)); + kinc_g4_vertex_buffer_init(buffer, count, &structure, (kinc_g4_usage_t)usage, inst_data_step_rate); + return buffer; +} + +void iron_g4_delete_vertex_buffer(kinc_g4_vertex_buffer_t *buffer) { + kinc_g4_vertex_buffer_destroy(buffer); + free(buffer); +} + +buffer_t *iron_g4_lock_vertex_buffer(kinc_g4_vertex_buffer_t *buffer) { + float *vertices = kinc_g4_vertex_buffer_lock_all(buffer); + buffer_t *b = (buffer_t *)malloc(sizeof(buffer_t)); + b->buffer = vertices; + b->length = kinc_g4_vertex_buffer_count(buffer) * kinc_g4_vertex_buffer_stride(buffer); + return b; +} + +void iron_g4_unlock_vertex_buffer(kinc_g4_vertex_buffer_t *buffer) { + kinc_g4_vertex_buffer_unlock_all(buffer); +} + +void iron_g4_set_vertex_buffer(kinc_g4_vertex_buffer_t *buffer) { + kinc_g4_set_vertex_buffer(buffer); +} + +void iron_g4_set_vertex_buffers(any_array_t *vertex_buffers) { + kinc_g4_set_vertex_buffers(vertex_buffers->buffer, vertex_buffers->length); +} + +void iron_g4_draw_indexed_vertices(i32 start, i32 count) { + #ifdef KINC_DIRECT3D12 + // TODO: Prevent heapIndex overflow in texture.c.h/kinc_g5_internal_set_textures + waitAfterNextDraw = true; + #endif + if (count < 0) { + kinc_g4_draw_indexed_vertices(); + } + else { + kinc_g4_draw_indexed_vertices_from_to(start, count); + } +} + +void iron_g4_draw_indexed_vertices_instanced(i32 instance_count, i32 start, i32 count) { + if (count < 0) { + kinc_g4_draw_indexed_vertices_instanced(instance_count); + } + else { + kinc_g4_draw_indexed_vertices_instanced_from_to(instance_count, start, count); + } +} + +kinc_g4_shader_t *iron_g4_create_shader(buffer_t *data, i32 shader_type) { + kinc_g4_shader_t *shader = (kinc_g4_shader_t *)malloc(sizeof(kinc_g4_shader_t)); + kinc_g4_shader_init(shader, data->buffer, data->length, (kinc_g4_shader_type_t)shader_type); + return shader; +} + +kinc_g4_shader_t *iron_g4_create_vertex_shader_from_source(string_t *source) { + +#ifdef WITH_D3DCOMPILER + + strcpy(temp_string_vs, source); + + ID3DBlob *error_message; + ID3DBlob *shader_buffer; + UINT flags = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_SKIP_VALIDATION;// D3DCOMPILE_OPTIMIZATION_LEVEL0 + HRESULT hr = D3DCompile(temp_string_vs, strlen(source) + 1, NULL, NULL, NULL, "main", "vs_5_0", flags, 0, &shader_buffer, &error_message); + if (hr != S_OK) { + kinc_log(KINC_LOG_LEVEL_INFO, "%s", (char *)error_message->lpVtbl->GetBufferPointer(error_message)); + return NULL; + } + + ID3D11ShaderReflection *reflector = NULL; + D3DReflect(shader_buffer->lpVtbl->GetBufferPointer(shader_buffer), shader_buffer->lpVtbl->GetBufferSize(shader_buffer), &IID_ID3D11ShaderReflection, (void **)&reflector); + + int size = shader_buffer->lpVtbl->GetBufferSize(shader_buffer); + char *file = malloc(size * 2); + int output_len = 0; + + bool has_bone = strstr(temp_string_vs, " bone :") != NULL; + bool has_col = strstr(temp_string_vs, " col :") != NULL; + bool has_nor = strstr(temp_string_vs, " nor :") != NULL; + bool has_pos = strstr(temp_string_vs, " pos :") != NULL; + bool has_tex = strstr(temp_string_vs, " tex :") != NULL; + + i32_map_t *attributes = i32_map_create(); + int index = 0; + if (has_bone) i32_map_set(attributes, "bone", index++); + if (has_col) i32_map_set(attributes, "col", index++); + if (has_nor) i32_map_set(attributes, "nor", index++); + if (has_pos) i32_map_set(attributes, "pos", index++); + if (has_tex) i32_map_set(attributes, "tex", index++); + if (has_bone) i32_map_set(attributes, "weight", index++); + + file[output_len] = (char)index; + output_len += 1; + + any_array_t *keys = map_keys(attributes); + for (int i = 0; i < keys->length; ++i) { + strcpy(file + output_len, keys->buffer[i]); + output_len += strlen(keys->buffer[i]); + file[output_len] = 0; + output_len += 1; + file[output_len] = i32_map_get(attributes, keys->buffer[i]); + output_len += 1; + } + + D3D11_SHADER_DESC desc; + reflector->lpVtbl->GetDesc(reflector, &desc); + + file[output_len] = desc.BoundResources; + output_len += 1; + for (int i = 0; i < desc.BoundResources; ++i) { + D3D11_SHADER_INPUT_BIND_DESC bindDesc; + reflector->lpVtbl->GetResourceBindingDesc(reflector, i, &bindDesc); + strcpy(file + output_len, bindDesc.Name); + output_len += strlen(bindDesc.Name); + file[output_len] = 0; + output_len += 1; + file[output_len] = bindDesc.BindPoint; + output_len += 1; + } + + ID3D11ShaderReflectionConstantBuffer *constants = reflector->lpVtbl->GetConstantBufferByName(reflector, "$Globals"); + D3D11_SHADER_BUFFER_DESC buffer_desc; + hr = constants->lpVtbl->GetDesc(constants, &buffer_desc); + if (hr == S_OK) { + file[output_len] = buffer_desc.Variables; + output_len += 1; + for (int i = 0; i < buffer_desc.Variables; ++i) { + ID3D11ShaderReflectionVariable *variable = constants->lpVtbl->GetVariableByIndex(constants, i); + D3D11_SHADER_VARIABLE_DESC variable_desc; + hr = variable->lpVtbl->GetDesc(variable, &variable_desc); + if (hr == S_OK) { + strcpy(file + output_len, variable_desc.Name); + output_len += strlen(variable_desc.Name); + file[output_len] = 0; + output_len += 1; + + *(uint32_t *)(file + output_len) = variable_desc.StartOffset; + output_len += 4; + + *(uint32_t *)(file + output_len) = variable_desc.Size; + output_len += 4; + + D3D11_SHADER_TYPE_DESC type_desc; + ID3D11ShaderReflectionType *type = variable->lpVtbl->GetType(variable); + hr = type->lpVtbl->GetDesc(type, &type_desc); + if (hr == S_OK) { + file[output_len] = type_desc.Columns; + output_len += 1; + file[output_len] = type_desc.Rows; + output_len += 1; + } + else { + file[output_len] = 0; + output_len += 1; + file[output_len] = 0; + output_len += 1; + } + } + } + } + else { + file[output_len] = 0; + output_len += 1; + } + + memcpy(file + output_len, (char *)shader_buffer->lpVtbl->GetBufferPointer(shader_buffer), shader_buffer->lpVtbl->GetBufferSize(shader_buffer)); + output_len += shader_buffer->lpVtbl->GetBufferSize(shader_buffer); + + shader_buffer->lpVtbl->Release(shader_buffer); + reflector->lpVtbl->Release(reflector); + + kinc_g4_shader_t *shader = (kinc_g4_shader_t *)malloc(sizeof(kinc_g4_shader_t)); + kinc_g4_shader_init(shader, file, (int)output_len, KINC_G4_SHADER_TYPE_VERTEX); + free(file); + + #elif defined(KINC_METAL) + + strcpy(temp_string_vs, "// my_main\n"); + strcat(temp_string_vs, source); + kinc_g4_shader_t *shader = (kinc_g4_shader_t *)malloc(sizeof(kinc_g4_shader_t)); + kinc_g4_shader_init(shader, temp_string_vs, strlen(temp_string_vs), KINC_G4_SHADER_TYPE_VERTEX); + + #elif defined(KINC_VULKAN) && defined(KRAFIX_LIBRARY) + + char *output = malloc(1024 * 1024); + int length; + krafix_compile(source, output, &length, "spirv", "windows", "vert", -1); + kinc_g4_shader_t *shader = (kinc_g4_shader_t *)malloc(sizeof(kinc_g4_shader_t)); + kinc_g4_shader_init(shader, output, length, KINC_G4_SHADER_TYPE_VERTEX); + + #else + + char *source_ = malloc(strlen(source) + 1); + strcpy(source_, source); + kinc_g4_shader_t *shader = (kinc_g4_shader_t *)malloc(sizeof(kinc_g4_shader_t)); + kinc_g4_shader_init(shader, source_, strlen(source_), KINC_G4_SHADER_TYPE_VERTEX); + + #endif + + return shader; +} + +kinc_g4_shader_t *iron_g4_create_fragment_shader_from_source(string_t *source) { + + #ifdef WITH_D3DCOMPILER + + strcpy(temp_string_fs, source); + + ID3DBlob *error_message; + ID3DBlob *shader_buffer; + UINT flags = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_SKIP_VALIDATION;// D3DCOMPILE_OPTIMIZATION_LEVEL0 + HRESULT hr = D3DCompile(temp_string_fs, strlen(source) + 1, NULL, NULL, NULL, "main", "ps_5_0", flags, 0, &shader_buffer, &error_message); + if (hr != S_OK) { + kinc_log(KINC_LOG_LEVEL_INFO, "%s", (char *)error_message->lpVtbl->GetBufferPointer(error_message)); + return NULL; + } + + ID3D11ShaderReflection *reflector = NULL; + D3DReflect(shader_buffer->lpVtbl->GetBufferPointer(shader_buffer), shader_buffer->lpVtbl->GetBufferSize(shader_buffer), &IID_ID3D11ShaderReflection, (void **)&reflector); + + int size = shader_buffer->lpVtbl->GetBufferSize(shader_buffer); + char *file = malloc(size * 2); + int output_len = 0; + file[output_len] = 0; + output_len += 1; + + D3D11_SHADER_DESC desc; + reflector->lpVtbl->GetDesc(reflector, &desc); + + file[output_len] = desc.BoundResources; + output_len += 1; + for (int i = 0; i < desc.BoundResources; ++i) { + D3D11_SHADER_INPUT_BIND_DESC bindDesc; + reflector->lpVtbl->GetResourceBindingDesc(reflector, i, &bindDesc); + strcpy(file + output_len, bindDesc.Name); + output_len += strlen(bindDesc.Name); + file[output_len] = 0; + output_len += 1; + file[output_len] = bindDesc.BindPoint; + output_len += 1; + } + + ID3D11ShaderReflectionConstantBuffer *constants = reflector->lpVtbl->GetConstantBufferByName(reflector, "$Globals"); + D3D11_SHADER_BUFFER_DESC buffer_desc; + hr = constants->lpVtbl->GetDesc(constants, &buffer_desc); + if (hr == S_OK) { + file[output_len] = buffer_desc.Variables; + output_len += 1; + for (int i = 0; i < buffer_desc.Variables; ++i) { + ID3D11ShaderReflectionVariable *variable = constants->lpVtbl->GetVariableByIndex(constants, i); + D3D11_SHADER_VARIABLE_DESC variable_desc; + hr = variable->lpVtbl->GetDesc(variable, &variable_desc); + if (hr == S_OK) { + strcpy(file + output_len, variable_desc.Name); + output_len += strlen(variable_desc.Name); + file[output_len] = 0; + output_len += 1; + + *(uint32_t *)(file + output_len) = variable_desc.StartOffset; + output_len += 4; + + *(uint32_t *)(file + output_len) = variable_desc.Size; + output_len += 4; + + D3D11_SHADER_TYPE_DESC type_desc; + ID3D11ShaderReflectionType *type = variable->lpVtbl->GetType(variable); + hr = type->lpVtbl->GetDesc(type, &type_desc); + if (hr == S_OK) { + file[output_len] = type_desc.Columns; + output_len += 1; + file[output_len] = type_desc.Rows; + output_len += 1; + } + else { + file[output_len] = 0; + output_len += 1; + file[output_len] = 0; + output_len += 1; + } + } + } + } + else { + file[output_len] = 0; + output_len += 1; + } + + memcpy(file + output_len, (char *)shader_buffer->lpVtbl->GetBufferPointer(shader_buffer), shader_buffer->lpVtbl->GetBufferSize(shader_buffer)); + output_len += shader_buffer->lpVtbl->GetBufferSize(shader_buffer); + + shader_buffer->lpVtbl->Release(shader_buffer); + reflector->lpVtbl->Release(reflector); + + kinc_g4_shader_t *shader = (kinc_g4_shader_t *)malloc(sizeof(kinc_g4_shader_t)); + kinc_g4_shader_init(shader, file, (int)output_len, KINC_G4_SHADER_TYPE_FRAGMENT); + free(file); + + #elif defined(KINC_METAL) + + strcpy(temp_string_fs, "// my_main\n"); + strcat(temp_string_fs, source); + kinc_g4_shader_t *shader = (kinc_g4_shader_t *)malloc(sizeof(kinc_g4_shader_t)); + kinc_g4_shader_init(shader, temp_string_fs, strlen(temp_string_fs), KINC_G4_SHADER_TYPE_FRAGMENT); + + #elif defined(KINC_VULKAN) && defined(KRAFIX_LIBRARY) + + char *output = malloc(1024 * 1024); + int length; + krafix_compile(source, output, &length, "spirv", "windows", "frag", -1); + kinc_g4_shader_t *shader = (kinc_g4_shader_t *)malloc(sizeof(kinc_g4_shader_t)); + kinc_g4_shader_init(shader, output, length, KINC_G4_SHADER_TYPE_FRAGMENT); + + #else + + char *source_ = malloc(strlen(source) + 1); + strcpy(source_, source); + kinc_g4_shader_t *shader = (kinc_g4_shader_t *)malloc(sizeof(kinc_g4_shader_t)); + kinc_g4_shader_init(shader, source_, strlen(source_), KINC_G4_SHADER_TYPE_FRAGMENT); + + #endif + + return shader; +} + +void iron_g4_delete_shader(kinc_g4_shader_t *shader) { + kinc_g4_shader_destroy(shader); +} + +kinc_g4_pipeline_t *iron_g4_create_pipeline() { + kinc_g4_pipeline_t *pipeline = (kinc_g4_pipeline_t *)malloc(sizeof(kinc_g4_pipeline_t)); + kinc_g4_pipeline_init(pipeline); + return pipeline; +} + +void iron_g4_delete_pipeline(kinc_g4_pipeline_t *pipeline) { + kinc_g4_pipeline_destroy(pipeline); + free(pipeline); +} + +typedef struct vertex_struct { + any_array_t *elements; // kinc_vertex_elem_t + bool instanced; +} vertex_struct_t; + +typedef struct iron_pipeline_state { + int cull_mode; // cull_mode_t; + bool depth_write; + int depth_mode; // compare_mode_t; + int blend_source; // blend_factor_t; + int blend_dest; // blend_factor_t; + int alpha_blend_source; // blend_factor_t; + int alpha_blend_dest; // blend_factor_t; + u8_array_t *color_write_masks_red; + u8_array_t *color_write_masks_green; + u8_array_t *color_write_masks_blue; + u8_array_t *color_write_masks_alpha; + int color_attachment_count; + i32_array_t *color_attachments; // tex_format_t + int depth_attachment_bits; +} iron_pipeline_state_t; + +void iron_g4_compile_pipeline(kinc_g4_pipeline_t *pipeline, vertex_struct_t *structure0, vertex_struct_t *structure1, vertex_struct_t *structure2, vertex_struct_t *structure3, i32 length, kinc_g4_shader_t *vertex_shader, kinc_g4_shader_t *fragment_shader, kinc_g4_shader_t *geometry_shader, iron_pipeline_state_t *state) { + kinc_g4_vertex_structure_t s0, s1, s2, s3; + kinc_g4_vertex_structure_init(&s0); + kinc_g4_vertex_structure_init(&s1); + kinc_g4_vertex_structure_init(&s2); + kinc_g4_vertex_structure_init(&s3); + kinc_g4_vertex_structure_t *structures[4] = { &s0, &s1, &s2, &s3 }; + + for (int32_t i1 = 0; i1 < length; ++i1) { + vertex_struct_t *structure = i1 == 0 ? structure0 : i1 == 1 ? structure1 : i1 == 2 ? structure2 : structure3; + structures[i1]->instanced = structure->instanced; + any_array_t *elements = structure->elements; + for (int32_t i2 = 0; i2 < elements->length; ++i2) { + kinc_vertex_elem_t *element = elements->buffer[i2]; + char *str = element->name; + int32_t data = element->data; + strcpy(temp_string_vstruct[i1][i2], str); + kinc_g4_vertex_structure_add(structures[i1], temp_string_vstruct[i1][i2], (kinc_g4_vertex_data_t)data); + } + } + + pipeline->vertex_shader = vertex_shader; + pipeline->fragment_shader = fragment_shader; + if (geometry_shader != null) { + pipeline->geometry_shader = geometry_shader; + } + for (int i = 0; i < length; ++i) { + pipeline->input_layout[i] = structures[i]; + } + pipeline->input_layout[length] = NULL; + + pipeline->cull_mode = (kinc_g4_cull_mode_t)state->cull_mode; + pipeline->depth_write = state->depth_write; + pipeline->depth_mode = (kinc_g4_compare_mode_t)state->depth_mode; + pipeline->blend_source = (kinc_g4_blending_factor_t)state->blend_source; + pipeline->blend_destination = (kinc_g4_blending_factor_t)state->blend_dest; + pipeline->alpha_blend_source = (kinc_g4_blending_factor_t)state->alpha_blend_source; + pipeline->alpha_blend_destination = (kinc_g4_blending_factor_t)state->alpha_blend_dest; + + u8_array_t *mask_red_array = state->color_write_masks_red; + u8_array_t *mask_green_array = state->color_write_masks_green; + u8_array_t *mask_blue_array = state->color_write_masks_blue; + u8_array_t *mask_alpha_array = state->color_write_masks_alpha; + + for (int i = 0; i < 8; ++i) { + pipeline->color_write_mask_red[i] = mask_red_array->buffer[i]; + pipeline->color_write_mask_green[i] = mask_green_array->buffer[i]; + pipeline->color_write_mask_blue[i] = mask_blue_array->buffer[i]; + pipeline->color_write_mask_alpha[i] = mask_alpha_array->buffer[i]; + } + + pipeline->color_attachment_count = state->color_attachment_count; + i32_array_t *color_attachment_array = state->color_attachments; + for (int i = 0; i < 8; ++i) { + pipeline->color_attachment[i] = (kinc_g4_render_target_format_t)color_attachment_array->buffer[i]; + } + pipeline->depth_attachment_bits = state->depth_attachment_bits; + pipeline->stencil_attachment_bits = 0; + + kinc_g4_pipeline_compile(pipeline); +} + +void iron_g4_set_pipeline(kinc_g4_pipeline_t *pipeline) { + kinc_g4_set_pipeline(pipeline); +} + +bool _load_image(kinc_file_reader_t *reader, const char *filename, unsigned char **output, int *width, int *height, kinc_image_format_t *format) { + *format = KINC_IMAGE_FORMAT_RGBA32; + int size = (int)kinc_file_reader_size(reader); + bool success = true; + unsigned char *data = (unsigned char *)malloc(size); + kinc_file_reader_read(reader, data, size); + kinc_file_reader_close(reader); + + if (ends_with(filename, "k")) { + *width = kinc_read_s32le(data); + *height = kinc_read_s32le(data + 4); + char fourcc[5]; + fourcc[0] = data[8]; + fourcc[1] = data[9]; + fourcc[2] = data[10]; + fourcc[3] = data[11]; + fourcc[4] = 0; + int compressed_size = size - 12; + if (strcmp(fourcc, "LZ4 ") == 0) { + int output_size = *width * *height * 4; + *output = (unsigned char *)malloc(output_size); + LZ4_decompress_safe((char *)(data + 12), (char *)*output, compressed_size, output_size); + } + else if (strcmp(fourcc, "LZ4F") == 0) { + int output_size = *width * *height * 16; + *output = (unsigned char *)malloc(output_size); + LZ4_decompress_safe((char *)(data + 12), (char *)*output, compressed_size, output_size); + *format = KINC_IMAGE_FORMAT_RGBA128; + + #ifdef KINC_IOS // No RGBA128 filtering, convert to RGBA64 + uint32_t *_output32 = (uint32_t *)*output; + unsigned char *_output = (unsigned char *)malloc(output_size / 2); + uint16_t *_output16 = (uint16_t *)_output; + for (int i = 0; i < output_size / 4; ++i) { + uint32_t x = *((uint32_t *)&_output32[i]); + _output16[i] = ((x >> 16) & 0x8000) | ((((x & 0x7f800000) - 0x38000000) >> 13) & 0x7c00) | ((x >> 13) & 0x03ff); + } + *format = KINC_IMAGE_FORMAT_RGBA64; + free(*output); + *output = _output; + #endif + } + else { + success = false; + } + } + else if (ends_with(filename, "hdr")) { + int comp; + *output = (unsigned char *)stbi_loadf_from_memory(data, size, width, height, &comp, 4); + if (*output == NULL) { + kinc_log(KINC_LOG_LEVEL_ERROR, stbi_failure_reason()); + success = false; + } + *format = KINC_IMAGE_FORMAT_RGBA128; + } + else { // jpg, png, .. + int comp; + *output = stbi_load_from_memory(data, size, width, height, &comp, 4); + if (*output == NULL) { + kinc_log(KINC_LOG_LEVEL_ERROR, stbi_failure_reason()); + success = false; + } + } + free(data); + return success; +} + +kinc_g4_texture_t *iron_g4_create_texture_from_encoded_bytes(buffer_t *data, string_t *format, bool readable); + +kinc_g4_texture_t *iron_load_image(string_t *file, bool readable) { + #ifdef WITH_EMBED + buffer_t *b = embed_get(file); + if (b != NULL) { + kinc_g4_texture_t *texture = iron_g4_create_texture_from_encoded_bytes(b, ".k", readable); + return texture; + } + #endif + + kinc_image_t *image = (kinc_image_t *)malloc(sizeof(kinc_image_t)); + kinc_file_reader_t reader; + if (kinc_file_reader_open(&reader, file, KINC_FILE_TYPE_ASSET)) { + unsigned char *image_data; + int image_width; + int image_height; + kinc_image_format_t image_format; + if (!_load_image(&reader, file, &image_data, &image_width, &image_height, &image_format)) { + free(image); + return NULL; + } + kinc_image_init(image, image_data, image_width, image_height, image_format); + } + else { + free(image); + return NULL; + } + + kinc_g4_texture_t *texture = (kinc_g4_texture_t *)malloc(sizeof(kinc_g4_texture_t)); + kinc_g4_texture_init_from_image(texture, image); + if (!readable) { + free(image->data); + kinc_image_destroy(image); + free(image); + } + else { + texture->image = image; + } + + return texture; +} + +typedef struct image { + any texture_; + any render_target_; + int format; // tex_format_t; + bool readable; + buffer_t *pixels; + int width; + int height; + int depth; +} image_t; + +void iron_unload_image(image_t *image) { + if (image == NULL) { + return; + } + if (image->texture_ != NULL) { + kinc_g4_texture_destroy(image->texture_); + free(image->texture_); + } + else if (image->render_target_ != NULL) { + kinc_g4_render_target_destroy(image->render_target_); + free(image->render_target_); + } +} + +#ifdef WITH_AUDIO + +typedef kinc_a1_channel_t audio_channel_t; + +any iron_load_sound(string_t *file) { + kinc_a1_sound_t *sound = kinc_a1_sound_create(file); + return sound; +} + +void iron_unload_sound(kinc_a1_sound_t *sound) { + kinc_a1_sound_destroy(sound); +} + +audio_channel_t *iron_play_sound(kinc_a1_sound_t *sound, bool loop, float pitch, bool unique) { + return kinc_a1_play_sound(sound, loop, pitch, unique); +} + +void iron_stop_sound(kinc_a1_sound_t *sound) { + kinc_a1_stop_sound(sound); +} + +void iron_sound_set_pitch(audio_channel_t *channel, float pitch) { + kinc_a1_channel_set_pitch(channel, pitch); +} + +#endif + +buffer_t *iron_load_blob(string_t *file) { + #ifdef WITH_EMBED + buffer_t *b = embed_get(file); + if (b != NULL) { + return b; + } + #endif + + kinc_file_reader_t reader; + if (!kinc_file_reader_open(&reader, file, KINC_FILE_TYPE_ASSET)) { + return NULL; + } + uint32_t reader_size = (uint32_t)kinc_file_reader_size(&reader); + buffer_t *buffer = buffer_create(reader_size); + kinc_file_reader_read(&reader, buffer->buffer, reader_size); + kinc_file_reader_close(&reader); + return buffer; +} + +void iron_load_url(string_t *url) { + kinc_load_url(url); +} + +void iron_copy_to_clipboard(string_t *text) { + kinc_copy_to_clipboard(text); +} + + +kinc_g4_constant_location_t *iron_g4_get_constant_location(kinc_g4_pipeline_t *pipeline, string_t *name) { + kinc_g4_constant_location_t location = kinc_g4_pipeline_get_constant_location(pipeline, name); + kinc_g4_constant_location_t *location_copy = (kinc_g4_constant_location_t *)malloc(sizeof(kinc_g4_constant_location_t)); + memcpy(location_copy, &location, sizeof(kinc_g4_constant_location_t)); // TODO + return location_copy; +} + +kinc_g4_texture_unit_t *iron_g4_get_texture_unit(kinc_g4_pipeline_t *pipeline, string_t *name) { + kinc_g4_texture_unit_t unit = kinc_g4_pipeline_get_texture_unit(pipeline, name); + kinc_g4_texture_unit_t *unit_copy = (kinc_g4_texture_unit_t *)malloc(sizeof(kinc_g4_texture_unit_t)); + memcpy(unit_copy, &unit, sizeof(kinc_g4_texture_unit_t)); // TODO + return unit_copy; +} + +void iron_g4_set_texture(kinc_g4_texture_unit_t *unit, kinc_g4_texture_t *texture) { + kinc_g4_set_texture(*unit, texture); +} + +void iron_g4_set_render_target(kinc_g4_texture_unit_t *unit, kinc_g4_render_target_t *render_target) { + kinc_g4_render_target_use_color_as_texture(render_target, *unit); +} + +void iron_g4_set_texture_depth(kinc_g4_texture_unit_t *unit, kinc_g4_render_target_t *render_target) { + kinc_g4_render_target_use_depth_as_texture(render_target, *unit); +} + +void iron_g4_set_image_texture(kinc_g4_texture_unit_t *unit, kinc_g4_texture_t *texture) { + kinc_g4_set_image_texture(*unit, texture); +} + +void iron_g4_set_texture_parameters(kinc_g4_texture_unit_t *unit, i32 u_addr, i32 v_addr, i32 min_filter, i32 mag_filter, i32 mip_filter) { + kinc_g4_set_texture_addressing(*unit, KINC_G4_TEXTURE_DIRECTION_U, (kinc_g4_texture_addressing_t)u_addr); + kinc_g4_set_texture_addressing(*unit, KINC_G4_TEXTURE_DIRECTION_V, (kinc_g4_texture_addressing_t)v_addr); + kinc_g4_set_texture_minification_filter(*unit, (kinc_g4_texture_filter_t)min_filter); + kinc_g4_set_texture_magnification_filter(*unit, (kinc_g4_texture_filter_t)mag_filter); + kinc_g4_set_texture_mipmap_filter(*unit, (kinc_g4_mipmap_filter_t)mip_filter); +} + +void iron_g4_set_texture3d_parameters(kinc_g4_texture_unit_t *unit, i32 u_addr, i32 v_addr, i32 w_addr, i32 min_filter, i32 mag_filter, i32 mip_filter) { + kinc_g4_set_texture3d_addressing(*unit, KINC_G4_TEXTURE_DIRECTION_U, (kinc_g4_texture_addressing_t)u_addr); + kinc_g4_set_texture3d_addressing(*unit, KINC_G4_TEXTURE_DIRECTION_V, (kinc_g4_texture_addressing_t)v_addr); + kinc_g4_set_texture3d_addressing(*unit, KINC_G4_TEXTURE_DIRECTION_W, (kinc_g4_texture_addressing_t)w_addr); + kinc_g4_set_texture3d_minification_filter(*unit, (kinc_g4_texture_filter_t)min_filter); + kinc_g4_set_texture3d_magnification_filter(*unit, (kinc_g4_texture_filter_t)mag_filter); + kinc_g4_set_texture3d_mipmap_filter(*unit, (kinc_g4_mipmap_filter_t)mip_filter); +} + +void iron_g4_set_bool(kinc_g4_constant_location_t *location, bool value) { + kinc_g4_set_bool(*location, value != 0); +} + +void iron_g4_set_int(kinc_g4_constant_location_t *location, i32 value) { + kinc_g4_set_int(*location, value); +} + +void iron_g4_set_float(kinc_g4_constant_location_t *location, f32 value) { + kinc_g4_set_float(*location, value); +} + +void iron_g4_set_float2(kinc_g4_constant_location_t *location, f32 value1, f32 value2) { + kinc_g4_set_float2(*location, value1, value2); +} + +void iron_g4_set_float3(kinc_g4_constant_location_t *location, f32 value1, f32 value2, f32 value3) { + kinc_g4_set_float3(*location, value1, value2, value3); +} + +void iron_g4_set_float4(kinc_g4_constant_location_t *location, f32 value1, f32 value2, f32 value3, f32 value4) { + kinc_g4_set_float4(*location, value1, value2, value3, value4); +} + +void iron_g4_set_floats(kinc_g4_constant_location_t *location, buffer_t *values) { + kinc_g4_set_floats(*location, (float *)values->buffer, (int)(values->length / 4)); +} + +void iron_g4_set_matrix4(kinc_g4_constant_location_t *location, mat4_t m) { + kinc_g4_set_matrix4(*location, &m); +} + +void iron_g4_set_matrix3(kinc_g4_constant_location_t *location, mat3_t m) { + kinc_g4_set_matrix3(*location, &m); +} + + +f32 iron_get_time() { + return kinc_time(); +} + +i32 iron_window_width() { + return kinc_window_width(0); +} + +i32 iron_window_height() { + return kinc_window_height(0); +} + +void iron_set_window_title(string_t *title) { + kinc_window_set_title(0, title); + #if defined(KINC_IOS) || defined(KINC_ANDROID) + strcpy(mobile_title, title); + #endif +} + +i32 iron_get_window_mode() { + return kinc_window_get_mode(0); +} + +void iron_set_window_mode(i32 mode) { + kinc_window_change_mode(0, (kinc_window_mode_t)mode); +} + +void iron_resize_window(i32 width, i32 height) { + kinc_window_resize(0, width, height); +} + +void iron_move_window(i32 x, i32 y) { + kinc_window_move(0, x, y); +} + +i32 iron_screen_dpi() { + return kinc_display_current_mode(kinc_primary_display()).pixels_per_inch; +} + +string_t *iron_system_id() { + return kinc_system_id(); +} + +void iron_request_shutdown() { + kinc_stop(); + + #ifdef KINC_LINUX + exit(1); + #endif +} + +i32 iron_display_count() { + return kinc_count_displays(); +} + +i32 iron_display_width(i32 index) { + return kinc_display_current_mode(index).width; +} + +i32 iron_display_height(i32 index) { + return kinc_display_current_mode(index).height; +} + +i32 iron_display_x(i32 index) { + return kinc_display_current_mode(index).x; +} + +i32 iron_display_y(i32 index) { + return kinc_display_current_mode(index).y; +} + +i32 iron_display_frequency(i32 index) { + return kinc_display_current_mode(index).frequency; +} + +bool iron_display_is_primary(i32 index) { + #ifdef KINC_LINUX // TODO: Primary display detection broken in Kinc + return true; + #else + return index == kinc_primary_display(); + #endif +} + +void iron_write_storage(string_t *name, buffer_t *data) { + kinc_file_writer_t writer; + kinc_file_writer_open(&writer, name); + kinc_file_writer_write(&writer, data->buffer, data->length); + kinc_file_writer_close(&writer); +} + +buffer_t *iron_read_storage(string_t *name) { + kinc_file_reader_t reader; + if (!kinc_file_reader_open(&reader, name, KINC_FILE_TYPE_SAVE)) { + return NULL; + } + int reader_size = (int)kinc_file_reader_size(&reader); + buffer_t *buffer = buffer_create(reader_size); + kinc_file_reader_read(&reader, buffer->buffer, reader_size); + kinc_file_reader_close(&reader); + return buffer; +} + + +kinc_g4_render_target_t *iron_g4_create_render_target(i32 width, i32 height, i32 format, i32 depth_buffer_bits, i32 stencil_buffer_bits) { + kinc_g4_render_target_t *render_target = (kinc_g4_render_target_t *)malloc(sizeof(kinc_g4_render_target_t)); + kinc_g4_render_target_init(render_target, width, height, (kinc_g4_render_target_format_t)format, depth_buffer_bits, stencil_buffer_bits); + return render_target; +} + +kinc_g4_texture_t *iron_g4_create_texture(i32 width, i32 height, i32 format) { + kinc_g4_texture_t *texture = (kinc_g4_texture_t *)malloc(sizeof(kinc_g4_texture_t)); + kinc_g4_texture_init(texture, width, height, (kinc_image_format_t)format); + return texture; +} + +kinc_g4_texture_t *iron_g4_create_texture3d(i32 width, i32 height, i32 depth, i32 format) { + kinc_g4_texture_t *texture = (kinc_g4_texture_t *)malloc(sizeof(kinc_g4_texture_t)); + kinc_g4_texture_init3d(texture, width, height, depth, (kinc_image_format_t)format); + return texture; +} + +kinc_g4_texture_t *iron_g4_create_texture_from_bytes(buffer_t *data, i32 width, i32 height, i32 format, bool readable) { + kinc_g4_texture_t *texture = (kinc_g4_texture_t *)malloc(sizeof(kinc_g4_texture_t)); + kinc_image_t *image = (kinc_image_t *)malloc(sizeof(kinc_image_t)); + void *image_data; + if (readable) { + image_data = malloc(data->length); + memcpy(image_data, data->buffer, data->length); + } + else { + image_data = data->buffer; + } + + kinc_image_init(image, image_data, width, height, (kinc_image_format_t)format); + kinc_g4_texture_init_from_image(texture, image); + if (!readable) { + kinc_image_destroy(image); + free(image); + } + else { + texture->image = image; + } + return texture; +} + +kinc_g4_texture_t *iron_g4_create_texture_from_bytes3d(buffer_t *data, i32 width, i32 height, i32 depth, i32 format, bool readable) { + kinc_g4_texture_t *texture = (kinc_g4_texture_t *)malloc(sizeof(kinc_g4_texture_t)); + kinc_image_t *image = (kinc_image_t *)malloc(sizeof(kinc_image_t)); + void *image_data; + if (readable) { + image_data = malloc(data->length); + memcpy(image_data, data->buffer, data->length); + } + else { + image_data = data->buffer; + } + + kinc_image_init3d(image, image_data, width, height, depth, (kinc_image_format_t)format); + kinc_g4_texture_init_from_image3d(texture, image); + + if (!readable) { + kinc_image_destroy(image); + free(image); + } + else { + texture->image = image; + } + return texture; +} + +kinc_g4_texture_t *iron_g4_create_texture_from_encoded_bytes(buffer_t *data, string_t *format, bool readable) { + kinc_g4_texture_t *texture = (kinc_g4_texture_t *)malloc(sizeof(kinc_g4_texture_t)); + kinc_image_t *image = (kinc_image_t *)malloc(sizeof(kinc_image_t)); + + unsigned char *content_data = (unsigned char *)data->buffer; + int content_length = (int)data->length; + unsigned char *image_data; + kinc_image_format_t image_format; + int image_width; + int image_height; + + if (ends_with(format, "k")) { + image_width = kinc_read_s32le(content_data); + image_height = kinc_read_s32le(content_data + 4); + char fourcc[5]; + fourcc[0] = content_data[8]; + fourcc[1] = content_data[9]; + fourcc[2] = content_data[10]; + fourcc[3] = content_data[11]; + fourcc[4] = 0; + int compressed_size = content_length - 12; + if (strcmp(fourcc, "LZ4 ") == 0) { + int output_size = image_width * image_height * 4; + image_data = (unsigned char *)malloc(output_size); + LZ4_decompress_safe((char *)content_data + 12, (char *)image_data, compressed_size, output_size); + image_format = KINC_IMAGE_FORMAT_RGBA32; + } + else if (strcmp(fourcc, "LZ4F") == 0) { + int output_size = image_width * image_height * 16; + image_data = (unsigned char *)malloc(output_size); + LZ4_decompress_safe((char *)content_data + 12, (char *)image_data, compressed_size, output_size); + image_format = KINC_IMAGE_FORMAT_RGBA128; + } + } + else if (ends_with(format, "hdr")) { + int comp; + image_data = (unsigned char *)stbi_loadf_from_memory(content_data, content_length, &image_width, &image_height, &comp, 4); + image_format = KINC_IMAGE_FORMAT_RGBA128; + } + else { // jpg, png, .. + int comp; + image_data = stbi_load_from_memory(content_data, content_length, &image_width, &image_height, &comp, 4); + image_format = KINC_IMAGE_FORMAT_RGBA32; + } + + kinc_image_init(image, image_data, image_width, image_height, image_format); + kinc_g4_texture_init_from_image(texture, image); + if (!readable) { + free(image->data); + kinc_image_destroy(image); + free(image); + } + else { + texture->image = image; + } + + return texture; +} + +int _format_byte_size(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA128: + return 16; + case KINC_IMAGE_FORMAT_RGBA64: + return 8; + case KINC_IMAGE_FORMAT_RGB24: + return 4; + case KINC_IMAGE_FORMAT_A32: + return 4; + case KINC_IMAGE_FORMAT_A16: + return 2; + case KINC_IMAGE_FORMAT_GREY8: + return 1; + case KINC_IMAGE_FORMAT_BGRA32: + case KINC_IMAGE_FORMAT_RGBA32: + default: + return 4; + } +} + +buffer_t *iron_g4_get_texture_pixels(kinc_image_t *image) { + uint8_t *data = kinc_image_get_pixels(image); + int byte_length = _format_byte_size(image->format) * image->width * image->height * image->depth; + buffer_t *buffer = malloc(sizeof(buffer_t)); + buffer->buffer = data; + buffer->length = byte_length; + return buffer; +} + +void iron_g4_get_render_target_pixels(kinc_g4_render_target_t *rt, buffer_t *data) { + uint8_t *b = (uint8_t *)data->buffer; + kinc_g4_render_target_get_pixels(rt, b); + + // Release staging texture immediately to save memory + #ifdef KINC_DIRECT3D11 + rt->impl.textureStaging->lpVtbl->Release(rt->impl.textureStaging); + rt->impl.textureStaging = NULL; + #elif defined(KINC_DIRECT3D12) + rt->impl._renderTarget.impl.renderTargetReadback->lpVtbl->Release(rt->impl._renderTarget.impl.renderTargetReadback); + rt->impl._renderTarget.impl.renderTargetReadback = NULL; + #elif defined(KINC_METAL) + // id texReadback = (__bridge_transfer id)rt->impl._renderTarget.impl._texReadback; + // texReadback = nil; + // rt->impl._renderTarget.impl._texReadback = NULL; + #endif +} + +buffer_t *iron_g4_lock_texture(kinc_g4_texture_t *texture, i32 level) { + uint8_t *tex = kinc_g4_texture_lock(texture); + int stride = kinc_g4_texture_stride(texture); + int byte_length = stride * texture->tex_height * texture->tex_depth; + buffer_t *buffer = malloc(sizeof(buffer_t)); + buffer->buffer = tex; + buffer->length = byte_length; + return buffer; +} + +void iron_g4_unlock_texture(kinc_g4_texture_t *texture) { + kinc_g4_texture_unlock(texture); +} + +void iron_g4_clear_texture(kinc_g4_texture_t *texture, i32 x, i32 y, i32 z, i32 width, i32 height, i32 depth, i32 color) { + kinc_g4_texture_clear(texture, x, y, z, width, height, depth, color); +} + +void iron_g4_generate_texture_mipmaps(kinc_g4_texture_t *texture, i32 levels) { + kinc_g4_texture_generate_mipmaps(texture, levels); +} + +void iron_g4_generate_render_target_mipmaps(kinc_g4_render_target_t *render_target, i32 levels) { + kinc_g4_render_target_generate_mipmaps(render_target, levels); +} + +void iron_g4_set_mipmaps(kinc_g4_texture_t *texture, any_array_t *mipmaps) { + for (int32_t i = 0; i < mipmaps->length; ++i) { + image_t *img = mipmaps->buffer[i]; + kinc_g4_texture_t *img_tex = img->texture_; + kinc_g4_texture_set_mipmap(texture, img_tex->image, i + 1); + } +} + +void iron_g4_set_depth_from(kinc_g4_render_target_t *target, kinc_g4_render_target_t *source) { + kinc_g4_render_target_set_depth_stencil_from(target, source); +} + +void iron_g4_viewport(i32 x, i32 y, i32 width, i32 height) { + kinc_g4_viewport(x, y, width, height); +} + +void iron_g4_scissor(i32 x, i32 y, i32 width, i32 height) { + kinc_g4_scissor(x, y, width, height); +} + +void iron_g4_disable_scissor() { + kinc_g4_disable_scissor(); +} + +bool iron_g4_render_targets_inverted_y() { + return kinc_g4_render_targets_inverted_y(); +} + +void iron_g4_begin(image_t *render_target, any_array_t *additional) { + if (render_target == NULL) { + kinc_g4_restore_render_target(); + } + else { + kinc_g4_render_target_t *rt = (kinc_g4_render_target_t *)render_target->render_target_; + int32_t length = 1; + kinc_g4_render_target_t *render_targets[8] = { rt, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + if (additional != NULL) { + length = additional->length + 1; + if (length > 8) { + length = 8; + } + for (int32_t i = 1; i < length; ++i) { + image_t *img = additional->buffer[i - 1]; + kinc_g4_render_target_t *art = (kinc_g4_render_target_t *)img->render_target_; + render_targets[i] = art; + } + } + kinc_g4_set_render_targets(render_targets, length); + } +} + +void iron_g4_end() { + +} + +void iron_g4_swap_buffers() { + kinc_g4_end(0); + kinc_g4_swap_buffers(); + kinc_g4_begin(0); +} + +void iron_file_save_bytes(string_t *path, buffer_t *bytes, i32 length) { + int byte_length = length > 0 ? length : (int)bytes->length; + if (byte_length > (int)bytes->length) { + byte_length = (int)bytes->length; + } + + #ifdef KINC_WINDOWS + MultiByteToWideChar(CP_UTF8, 0, path, -1, temp_wstring, 1024); + FILE *file = _wfopen(temp_wstring, L"wb"); + #else + FILE *file = fopen(path, "wb"); + #endif + if (file == NULL) { + return; + } + fwrite(bytes->buffer, 1, byte_length, file); + fclose(file); +} + +i32 iron_sys_command(string_t *cmd) { + #ifdef KINC_WINDOWS + int wlen = MultiByteToWideChar(CP_UTF8, 0, cmd, -1, NULL, 0); + wchar_t *wstr = malloc(sizeof(wchar_t) * wlen); + MultiByteToWideChar(CP_UTF8, 0, cmd, -1, wstr, wlen); + int result = _wsystem(wstr); + free(wstr); + #elif defined(KINC_IOS) + int result = 0; + #else + int result = system(cmd); + #endif + return result; +} + +string_t *iron_save_path() { + return kinc_internal_save_path(); +} + +string_t *iron_get_files_location() { + #ifdef KINC_MACOS + char path[1024]; + strcpy(path, macgetresourcepath()); + strcat(path, "/"); + strcat(path, KINC_DEBUGDIR); + strcat(path, "/"); + return path; + #elif defined(KINC_IOS) + char path[1024]; + strcpy(path, iphonegetresourcepath()); + strcat(path, "/"); + strcat(path, KINC_DEBUGDIR); + strcat(path, "/"); + return path; + #else + return kinc_internal_get_files_location(); + #endif +} + +typedef struct _callback_data { + int32_t size; + char url[512]; + void (*func)(char *, buffer_t *); +} _callback_data_t; + +void _http_callback(int error, int response, const char *body, void *callback_data) { + _callback_data_t *cbd = (_callback_data_t *)callback_data; + buffer_t *buffer = NULL; + if (body != NULL) { + buffer = malloc(sizeof(buffer_t)); + buffer->length = cbd->size > 0 ? cbd->size : strlen(body); + buffer->buffer = body; + } + cbd->func(cbd->url, buffer); + free(cbd); +} + +void iron_http_request(string_t *url, i32 size, void (*callback)(char *, buffer_t *)) { + _callback_data_t *cbd = malloc(sizeof(_callback_data_t)); + cbd->size = size; + strcpy(cbd->url, url); + cbd->func = callback; + + char url_base[512]; + char url_path[512]; + const char *curl = url; + int i = 0; + for (; i < strlen(curl) - 8; ++i) { + if (curl[i + 8] == '/') { + break; + } + url_base[i] = curl[i + 8]; // Strip https:// + } + url_base[i] = 0; + int j = 0; + if (strlen(url_base) < strlen(curl) - 8) { + ++i; // Skip / + } + for (; j < strlen(curl) - 8 - i; ++j) { + if (curl[i + 8 + j] == 0) { + break; + } + url_path[j] = curl[i + 8 + j]; + } + url_path[j] = 0; + #ifdef KINC_ANDROID // TODO: move to Kinc + android_http_request(curl, url_path, NULL, 443, true, 0, NULL, &_http_callback, cbd); + #else + kinc_http_request(url_base, url_path, NULL, 443, true, 0, NULL, &_http_callback, cbd); + #endif +} + +void iron_g2_init(buffer_t *image_vert, buffer_t *image_frag, buffer_t *colored_vert, buffer_t *colored_frag, buffer_t *text_vert, buffer_t *text_frag) { + arm_g2_init(image_vert->buffer, image_vert->length, image_frag->buffer, image_frag->length, colored_vert->buffer, colored_vert->length, colored_frag->buffer, colored_frag->length, text_vert->buffer, text_vert->length, text_frag->buffer, text_frag->length); +} + +void iron_g2_begin() { + arm_g2_begin(); +} + +void iron_g2_end() { + arm_g2_end(); +} + +void iron_g2_draw_scaled_sub_image(image_t *image, f32 sx, f32 sy, f32 sw, f32 sh, f32 dx, f32 dy, f32 dw, f32 dh) { + #ifdef KINC_DIRECT3D12 + waitAfterNextDraw = true; + #endif + if (image->texture_ != NULL) { + kinc_g4_texture_t *texture = (kinc_g4_texture_t *)image->texture_; + arm_g2_draw_scaled_sub_image(texture, sx, sy, sw, sh, dx, dy, dw, dh); + } + else { + kinc_g4_render_target_t *render_target = (kinc_g4_render_target_t *)image->render_target_; + arm_g2_draw_scaled_sub_render_target(render_target, sx, sy, sw, sh, dx, dy, dw, dh); + } +} + +void iron_g2_fill_triangle(f32 x0, f32 y0, f32 x1, f32 y1, f32 x2, f32 y2) { + arm_g2_fill_triangle(x0, y0, x1, y1, x2, y2); +} + +void iron_g2_fill_rect(f32 x, f32 y, f32 width, f32 height) { + arm_g2_fill_rect(x, y, width, height); +} + +void iron_g2_draw_rect(f32 x, f32 y, f32 width, f32 height, f32 strength) { + arm_g2_draw_rect(x, y, width, height, strength); +} + +void iron_g2_draw_line(f32 x0, f32 y0, f32 x1, f32 y1, f32 strength) { + arm_g2_draw_line(x0, y0, x1, y1, strength); +} + +void iron_g2_draw_string(string_t *text, f32 x, f32 y) { + arm_g2_draw_string(text, x, y); +} + +void iron_g2_set_font(arm_g2_font_t *font, i32 size) { + arm_g2_set_font(font, size); +} + +arm_g2_font_t *iron_g2_font_init(buffer_t *blob, i32 font_index) { + arm_g2_font_t *font = (arm_g2_font_t *)malloc(sizeof(arm_g2_font_t)); + arm_g2_font_init(font, blob->buffer, font_index); + return font; +} + +arm_g2_font_t *iron_g2_font_13(buffer_t *blob) { + arm_g2_font_t *font = (arm_g2_font_t *)malloc(sizeof(arm_g2_font_t)); + arm_g2_font_13(font, blob->buffer); + return font; +} + +void iron_g2_font_set_glyphs(i32_array_t *glyphs) { + arm_g2_font_set_glyphs(glyphs->buffer, glyphs->length); +} + +i32 iron_g2_font_count(arm_g2_font_t *font) { + return arm_g2_font_count(font); +} + +i32 iron_g2_font_height(arm_g2_font_t *font, i32 size) { + return (int)arm_g2_font_height(font, size); +} + +i32 iron_g2_string_width(arm_g2_font_t *font, i32 size, string_t *text) { + return (int)arm_g2_string_width(font, size, text); +} + +void iron_g2_set_bilinear_filter(bool bilinear) { + arm_g2_set_bilinear_filter(bilinear); +} + +void iron_g2_restore_render_target() { + arm_g2_restore_render_target(); +} + +void iron_g2_set_render_target(kinc_g4_render_target_t *render_target) { + arm_g2_set_render_target(render_target); +} + +void iron_g2_set_color(i32 color) { + arm_g2_set_color(color); +} + +void iron_g2_set_pipeline(kinc_g4_pipeline_t *pipeline) { + arm_g2_set_pipeline(pipeline); +} + +void iron_g2_set_transform(buffer_t *matrix) { + arm_g2_set_transform(matrix != NULL ? (kinc_matrix3x3_t *)matrix->buffer : NULL); +} + +void iron_g2_fill_circle(f32 cx, f32 cy, f32 radius, i32 segments) { + arm_g2_fill_circle(cx, cy, radius, segments); +} + +void iron_g2_draw_circle(f32 cx, f32 cy, f32 radius, i32 segments, f32 strength) { + arm_g2_draw_circle(cx, cy, radius, segments, strength); +} + +void iron_g2_draw_cubic_bezier(f32_array_t *x, f32_array_t *y, i32 segments, f32 strength) { + arm_g2_draw_cubic_bezier(x->buffer, y->buffer, segments, strength); +} + +bool _window_close_callback(void *data) { + #ifdef KINC_WINDOWS + bool save = false; + wchar_t title[1024]; + GetWindowTextW(kinc_windows_window_handle(0), title, sizeof(title)); + bool dirty = wcsstr(title, L"* - ArmorPaint") != NULL; + if (dirty) { + int res = MessageBox(kinc_windows_window_handle(0), L"Project has been modified, save changes?", L"Save Changes?", MB_YESNOCANCEL | MB_ICONEXCLAMATION); + if (res == IDYES) { + save = true; + } + else if (res == IDNO) { + save = false; + } + else { // Cancel + return false; + } + } + //if (save_and_quit_func_set) { + // iron_save_and_quit(save); + // return false; + //} + #endif + return true; +} + +void iron_set_save_and_quit_callback(void (*callback)(bool)) { + iron_save_and_quit = callback; + save_and_quit_callback_set = true; + kinc_window_set_close_callback(0, _window_close_callback, NULL); +} + +void iron_set_mouse_cursor(i32 id) { + kinc_mouse_set_cursor(id); + #ifdef KINC_WINDOWS + // Set hand icon for drag even when mouse button is pressed + if (id == 1) { + SetCursor(LoadCursor(NULL, IDC_HAND)); + } + #endif +} + +void iron_delay_idle_sleep() { + paused_frames = 0; +} + +#ifdef WITH_NFD +char_ptr_array_t *iron_open_dialog(char *filter_list, char *default_path, bool open_multiple) { + nfdpathset_t out_paths; + nfdchar_t* out_path; + nfdresult_t result = open_multiple ? NFD_OpenDialogMultiple(filter_list, default_path, &out_paths) : NFD_OpenDialog(filter_list, default_path, &out_path); + + if (result == NFD_OKAY) { + int path_count = open_multiple ? (int)NFD_PathSet_GetCount(&out_paths) : 1; + char_ptr_array_t *result = any_array_create(path_count); + + if (open_multiple) { + for (int i = 0; i < path_count; ++i) { + nfdchar_t* out_path = NFD_PathSet_GetPath(&out_paths, i); + result->buffer[i] = out_path; + } + // NFD_PathSet_Free(&out_paths); + } + else { + result->buffer[0] = out_path; + // free(out_path); + } + return result; + } + return NULL; +} + +static char iron_save_dialog_path[512]; + +char *iron_save_dialog(char *filter_list, char *default_path) { + nfdchar_t *out_path = NULL; + nfdresult_t result = NFD_SaveDialog(filter_list, default_path, &out_path); + if (result == NFD_OKAY) { + strcpy(iron_save_dialog_path, out_path); + free(out_path); + return iron_save_dialog_path; + } + return NULL; +} + +#elif defined(KINC_ANDROID) + +char_ptr_array_t *iron_open_dialog(char *filter_list, char *default_path, bool open_multiple) { + AndroidFileDialogOpen(); + return NULL; +} + +char *iron_save_dialog(char *filter_list, char *default_path) { + wchar_t *out_path = AndroidFileDialogSave(); + size_t len = wcslen(out_path); + uint16_t *str = malloc(sizeof(uint16_t) * (len + 1)); + for (int i = 0; i < len; i++) { + str[i] = out_path[i]; + } + str[len] = 0; + // free(str); + return str; +} + +#elif defined(KINC_IOS) + +char_ptr_array_t *iron_open_dialog(char *filter_list, char *default_path, bool open_multiple) { + // Once finished drop_files callback is called + IOSFileDialogOpen(); + return NULL; +} + +char *iron_save_dialog(char *filter_list, char *default_path) { + // Path to app document directory + wchar_t *out_path = IOSFileDialogSave(); + size_t len = wcslen(out_path); + uint16_t *str = malloc(sizeof(uint16_t) * (len + 1)); + for (int i = 0; i < len; i++) { + str[i] = out_path[i]; + } + str[len] = 0; + // free(str); + return str;; +} +#endif + +char *iron_read_directory(char *path) { + char *files = temp_string; + files[0] = 0; + + directory dir = open_dir(path); + if (dir.handle == NULL) { + return files; + } + + while (true) { + file f = read_next_file(&dir); + if (!f.valid) { + break; + } + + #ifdef KINC_WINDOWS + char file_path[512]; + strcpy(file_path, path); + strcat(file_path, "\\"); + strcat(file_path, f.name); + if (FILE_ATTRIBUTE_HIDDEN & GetFileAttributesA(file_path)) { + continue; // Skip hidden files + } + #endif + + if (files[0] != '\0') { + strcat(files, "\n"); + } + strcat(files, f.name); + } + close_dir(&dir); + return files; +} + +bool iron_file_exists(char *path) { + kinc_file_reader_t reader; + if (kinc_file_reader_open(&reader, path, KINC_FILE_TYPE_ASSET)) { + kinc_file_reader_close(&reader); + return true; + } + return false; +} + +void iron_delete_file(char *path) { + #ifdef KINC_IOS + IOSDeleteFile(path); + #elif defined(KINC_WINDOWS) + char cmd[1024]; + strcpy(cmd, "del /f \""); + strcat(cmd, path); + strcat(cmd, "\""); + iron_sys_command(cmd); + #else + char cmd[1024]; + strcpy(cmd, "rm \""); + strcat(cmd, path); + strcat(cmd, "\""); + iron_sys_command(cmd); + #endif +} + +#ifdef WITH_COMPRESS +buffer_t *iron_inflate(buffer_t *bytes, bool raw) { + unsigned char *inflated; + int inflated_len = bytes->length * 2; + int out_len = -1; + while (out_len == -1) { + inflated_len *= 2; + inflated = (unsigned char *)realloc(inflated, inflated_len); + out_len = sinflate(inflated, inflated_len, bytes->buffer, bytes->length); + } + buffer_t *output = buffer_create(0); + output->buffer = inflated; + output->length = out_len; + return output; +} + +buffer_t *iron_deflate(buffer_t *bytes, bool raw) { + struct sdefl sdefl; + memset(&sdefl, 0, sizeof(sdefl)); + void *deflated = malloc(sdefl_bound(bytes->length)); + // raw == sdeflate + int out_len = zsdeflate(&sdefl, deflated, bytes->buffer, bytes->length, SDEFL_LVL_MIN); + buffer_t *output = buffer_create(0); + output->buffer = deflated; + output->length = out_len; + return output; +} + +unsigned char *iron_deflate_raw(unsigned char *data, int data_len, int *out_len, int quality) { + struct sdefl sdefl; + memset(&sdefl, 0, sizeof(sdefl)); + void *deflated = malloc(sdefl_bound(data_len)); + *out_len = zsdeflate(&sdefl, deflated, data, data_len, SDEFL_LVL_MIN); + return (unsigned char *)deflated; +} +#endif + +#ifdef WITH_IMAGE_WRITE +void _write_image(char *path, buffer_t *bytes, i32 w, i32 h, i32 format, int image_format, int quality) { + int comp = 0; + unsigned char *pixels = NULL; + unsigned char *rgba = (unsigned char *)bytes->buffer; + if (format == 0) { // RGBA + comp = 4; + pixels = rgba; + } + else if (format == 1) { // R + comp = 1; + pixels = rgba; + } + else if (format == 2) { // RGB1 + comp = 3; + pixels = (unsigned char *)malloc(w * h * comp); + for (int i = 0; i < w * h; ++i) { + #if defined(KINC_METAL) || defined(KINC_VULKAN) + pixels[i * 3 ] = rgba[i * 4 + 2]; + pixels[i * 3 + 1] = rgba[i * 4 + 1]; + pixels[i * 3 + 2] = rgba[i * 4 ]; + #else + pixels[i * 3 ] = rgba[i * 4 ]; + pixels[i * 3 + 1] = rgba[i * 4 + 1]; + pixels[i * 3 + 2] = rgba[i * 4 + 2]; + #endif + } + } + else if (format > 2) { // RRR1, GGG1, BBB1, AAA1 + comp = 1; + pixels = (unsigned char *)malloc(w * h * comp); + int off = format - 3; + #if defined(KINC_METAL) || defined(KINC_VULKAN) + off = 2 - off; + #endif + for (int i = 0; i < w * h; ++i) { + pixels[i] = rgba[i * 4 + off]; + } + } + + image_format == 0 ? + stbi_write_jpg(path, w, h, comp, pixels, quality) : + stbi_write_png(path, w, h, comp, pixels, w * comp); + + if (pixels != rgba) { + free(pixels); + } +} + +void iron_write_jpg(char *path, buffer_t *bytes, i32 w, i32 h, i32 format, i32 quality) { + // RGBA, R, RGB1, RRR1, GGG1, BBB1, AAA1 + _write_image(path, bytes, w, h, format, 0, quality); +} + +void iron_write_png(char *path, buffer_t *bytes, i32 w, i32 h, i32 format) { + _write_image(path, bytes, w, h, format, 1, 100); +} + +unsigned char *_encode_data; +int _encode_size; +void _encode_image_func(void *context, void *data, int size) { + memcpy(_encode_data + _encode_size, data, size); + _encode_size += size; +} + +buffer_t *_encode_image(buffer_t *bytes, i32 w, i32 h, i32 format, i32 quality) { + _encode_data = (unsigned char *)malloc(w * h * 4); + _encode_size = 0; + format == 0 ? + stbi_write_jpg_to_func(&_encode_image_func, NULL, w, h, 4, bytes->buffer, quality) : + stbi_write_png_to_func(&_encode_image_func, NULL, w, h, 4, bytes->buffer, w * 4); + buffer_t *buffer = malloc(sizeof(buffer_t)); + buffer->buffer = _encode_data; + buffer->length = _encode_size; + return buffer; +} + +buffer_t *iron_encode_jpg(buffer_t *bytes, i32 w, i32 h, i32 format, i32 quality) { + return _encode_image(bytes, w, h, 0, quality); +} + +buffer_t *iron_encode_png(buffer_t *bytes, i32 w, i32 h, i32 format) { + return _encode_image(bytes, w, h, 1, 100); +} +#endif + +#ifdef WITH_MPEG_WRITE +buffer_t *iron_write_mpeg() { + return NULL; +} +#endif + +#ifdef WITH_ONNX +buffer_t *iron_ml_inference(buffer_t *model, any_array_t *tensors, any_array_t *input_shape, i32_array_t *output_shape, bool use_gpu) { + OrtStatus *onnx_status = NULL; + static bool use_gpu_last = false; + if (ort == NULL || use_gpu_last != use_gpu) { + use_gpu_last = use_gpu; + ort = OrtGetApiBase()->GetApi(ORT_API_VERSION); + ort->CreateEnv(ORT_LOGGING_LEVEL_WARNING, "armorcore", &ort_env); + + ort->CreateSessionOptions(&ort_session_options); + ort->SetIntraOpNumThreads(ort_session_options, 8); + ort->SetInterOpNumThreads(ort_session_options, 8); + + if (use_gpu) { + #ifdef KINC_WINDOWS + ort->SetSessionExecutionMode(ort_session_options, ORT_SEQUENTIAL); + ort->DisableMemPattern(ort_session_options); + onnx_status = OrtSessionOptionsAppendExecutionProvider_DML(ort_session_options, 0); + #elif defined(KINC_LINUX) + // onnx_status = OrtSessionOptionsAppendExecutionProvider_CUDA(ort_session_options, 0); + #elif defined(KINC_MACOS) + onnx_status = OrtSessionOptionsAppendExecutionProvider_CoreML(ort_session_options, 0); + #endif + if (onnx_status != NULL) { + const char *msg = ort->GetErrorMessage(onnx_status); + kinc_log(KINC_LOG_LEVEL_ERROR, "%s", msg); + ort->ReleaseStatus(onnx_status); + } + } + } + + static void *content_last = 0; + if (content_last != model->buffer || session == NULL) { + if (session != NULL) { + ort->ReleaseSession(session); + session = NULL; + } + onnx_status = ort->CreateSessionFromArray(ort_env, model->buffer, (int)model->length, ort_session_options, &session); + if (onnx_status != NULL) { + const char* msg = ort->GetErrorMessage(onnx_status); + kinc_log(KINC_LOG_LEVEL_ERROR, "%s", msg); + ort->ReleaseStatus(onnx_status); + } + } + content_last = model->buffer; + + OrtAllocator *allocator; + ort->GetAllocatorWithDefaultOptions(&allocator); + OrtMemoryInfo *memory_info; + ort->CreateCpuMemoryInfo(OrtArenaAllocator, OrtMemTypeDefault, &memory_info); + + int32_t length = tensors->length; + if (length > 4) { + length = 4; + } + char *input_node_names[4]; + OrtValue *input_tensors[4]; + for (int32_t i = 0; i < length; ++i) { + ort->SessionGetInputName(session, i, allocator, &input_node_names[i]); + + OrtTypeInfo *input_type_info; + ort->SessionGetInputTypeInfo(session, i, &input_type_info); + const OrtTensorTypeAndShapeInfo *input_tensor_info; + ort->CastTypeInfoToTensorInfo(input_type_info, &input_tensor_info); + size_t num_input_dims; + ort->GetDimensionsCount(input_tensor_info, &num_input_dims); + int64_t input_node_dims[32]; + + if (input_shape != NULL) { + for (int32_t j = 0; j < num_input_dims; ++j) { + i32_array_t *a = input_shape->buffer[i]; + input_node_dims[j] = a->buffer[j]; + } + } + else { + ort->GetDimensions(input_tensor_info, (int64_t *)input_node_dims, num_input_dims); + } + ONNXTensorElementDataType tensor_element_type; + ort->GetTensorElementType(input_tensor_info, &tensor_element_type); + + buffer_t *b = tensors->buffer[i]; + ort->CreateTensorWithDataAsOrtValue(memory_info, b->buffer, (int)b->length, input_node_dims, num_input_dims, tensor_element_type, &input_tensors[i]); + ort->ReleaseTypeInfo(input_type_info); + } + + char *output_node_name; + ort->SessionGetOutputName(session, 0, allocator, &output_node_name); + OrtValue *output_tensor = NULL; + onnx_status = ort->Run(session, NULL, input_node_names, input_tensors, length, &output_node_name, 1, &output_tensor); + if (onnx_status != NULL) { + const char* msg = ort->GetErrorMessage(onnx_status); + kinc_log(KINC_LOG_LEVEL_ERROR, "%s", msg); + ort->ReleaseStatus(onnx_status); + } + float *float_array; + ort->GetTensorMutableData(output_tensor, (void **)&float_array); + + size_t output_byte_length = 4; + if (output_shape != NULL) { + int32_t length = output_shape->length; + for (int i = 0; i < length; ++i) { + output_byte_length *= output_shape->buffer[i]; + } + } + else { + OrtTypeInfo *output_type_info; + ort->SessionGetOutputTypeInfo(session, 0, &output_type_info); + const OrtTensorTypeAndShapeInfo *output_tensor_info; + ort->CastTypeInfoToTensorInfo(output_type_info, &output_tensor_info); + size_t num_output_dims; + ort->GetDimensionsCount(output_tensor_info, &num_output_dims); + int64_t output_node_dims[32]; + ort->GetDimensions(output_tensor_info, (int64_t *)output_node_dims, num_output_dims); + ort->ReleaseTypeInfo(output_type_info); + for (int i = 0; i < num_output_dims; ++i) { + if (output_node_dims[i] > 1) { + output_byte_length *= output_node_dims[i]; + } + } + } + + buffer_t *output = buffer_create(output_byte_length); + memcpy(output->buffer, float_array, output_byte_length); + + ort->ReleaseMemoryInfo(memory_info); + ort->ReleaseValue(output_tensor); + for (int i = 0; i < length; ++i) { + ort->ReleaseValue(input_tensors[i]); + } + return output; +} + +void iron_ml_unload() { + if (session != NULL) { + ort->ReleaseSession(session); + session = NULL; + } +} +#endif + +#if defined(KINC_DIRECT3D12) || defined(KINC_VULKAN) || defined(KINC_METAL) +bool iron_raytrace_supported() { + #ifdef KINC_METAL + return kinc_raytrace_supported(); + #else + return true; + #endif +} + +void iron_raytrace_init(buffer_t *shader, kinc_g4_vertex_buffer_t *vb, kinc_g4_index_buffer_t *ib, f32 scale) { + if (accel_created) { + kinc_g5_constant_buffer_destroy(&constant_buffer); + kinc_raytrace_acceleration_structure_destroy(&accel); + kinc_raytrace_pipeline_destroy(&pipeline); + } + + kinc_g5_vertex_buffer_t *vertex_buffer = &vb->impl._buffer; + kinc_g5_index_buffer_t *index_buffer = &ib->impl._buffer; + + kinc_g5_constant_buffer_init(&constant_buffer, constant_buffer_size * 4); + kinc_raytrace_pipeline_init(&pipeline, &commandList, shader->buffer, (int)shader->length, &constant_buffer); + kinc_raytrace_acceleration_structure_init(&accel, &commandList, vertex_buffer, index_buffer, scale); + accel_created = true; +} + +void iron_raytrace_set_textures(image_t *tex0, image_t *tex1, image_t *tex2, kinc_g4_texture_t *texenv, kinc_g4_texture_t *texsobol, kinc_g4_texture_t *texscramble, kinc_g4_texture_t *texrank) { + kinc_g4_render_target_t *texpaint0; + kinc_g4_render_target_t *texpaint1; + kinc_g4_render_target_t *texpaint2; + + image_t *texpaint0_image = tex0; + kinc_g4_texture_t *texpaint0_tex = texpaint0_image->texture_; + kinc_g4_render_target_t *texpaint0_rt = texpaint0_image->render_target_; + + if (texpaint0_tex != NULL) { + #ifdef KINC_DIRECT3D12 + kinc_g4_texture_t *texture = texpaint0_tex; + if (!texture->impl._uploaded) { + kinc_g5_command_list_upload_texture(&commandList, &texture->impl._texture); + texture->impl._uploaded = true; + } + texpaint0 = (kinc_g4_render_target_t *)malloc(sizeof(kinc_g4_render_target_t)); + texpaint0->impl._renderTarget.impl.srvDescriptorHeap = texture->impl._texture.impl.srvDescriptorHeap; + #endif + } + else { + texpaint0 = texpaint0_rt; + } + + image_t *texpaint1_image = tex1; + kinc_g4_texture_t *texpaint1_tex = texpaint1_image->texture_; + kinc_g4_render_target_t *texpaint1_rt = texpaint1_image->render_target_; + + if (texpaint1_tex != NULL) { + #ifdef KINC_DIRECT3D12 + kinc_g4_texture_t *texture = texpaint1_tex; + if (!texture->impl._uploaded) { + kinc_g5_command_list_upload_texture(&commandList, &texture->impl._texture); + texture->impl._uploaded = true; + } + texpaint1 = (kinc_g4_render_target_t *)malloc(sizeof(kinc_g4_render_target_t)); + texpaint1->impl._renderTarget.impl.srvDescriptorHeap = texture->impl._texture.impl.srvDescriptorHeap; + #endif + } + else { + texpaint1 = texpaint1_rt; + } + + image_t *texpaint2_image = tex2; + kinc_g4_texture_t *texpaint2_tex = texpaint2_image->texture_; + kinc_g4_render_target_t *texpaint2_rt = texpaint2_image->render_target_; + + if (texpaint2_tex != NULL) { + #ifdef KINC_DIRECT3D12 + kinc_g4_texture_t *texture = (kinc_g4_texture_t *)texpaint2_tex; + if (!texture->impl._uploaded) { + kinc_g5_command_list_upload_texture(&commandList, &texture->impl._texture); + texture->impl._uploaded = true; + } + texpaint2 = (kinc_g4_render_target_t *)malloc(sizeof(kinc_g4_render_target_t)); + texpaint2->impl._renderTarget.impl.srvDescriptorHeap = texture->impl._texture.impl.srvDescriptorHeap; + #endif + } + else { + texpaint2 = texpaint2_rt; + } + + if (!texenv->impl._uploaded) { + kinc_g5_command_list_upload_texture(&commandList, &texenv->impl._texture); + texenv->impl._uploaded = true; + } + if (!texsobol->impl._uploaded) { + kinc_g5_command_list_upload_texture(&commandList, &texsobol->impl._texture); + texsobol->impl._uploaded = true; + } + if (!texscramble->impl._uploaded) { + kinc_g5_command_list_upload_texture(&commandList, &texscramble->impl._texture); + texscramble->impl._uploaded = true; + } + if (!texrank->impl._uploaded) { + kinc_g5_command_list_upload_texture(&commandList, &texrank->impl._texture); + texrank->impl._uploaded = true; + } + + kinc_raytrace_set_textures(&texpaint0->impl._renderTarget, &texpaint1->impl._renderTarget, &texpaint2->impl._renderTarget, &texenv->impl._texture, &texsobol->impl._texture, &texscramble->impl._texture, &texrank->impl._texture); + + if (texpaint0_tex != NULL) { + free(texpaint0); + } + if (texpaint1_tex != NULL) { + free(texpaint1); + } + if (texpaint2_tex != NULL) { + free(texpaint2); + } +} + +void iron_raytrace_dispatch_rays(kinc_g4_render_target_t *render_target, buffer_t *buffer) { + float *cb = (float *)buffer->buffer; + kinc_g5_constant_buffer_lock_all(&constant_buffer); + for (int i = 0; i < constant_buffer_size; ++i) { + kinc_g5_constant_buffer_set_float(&constant_buffer, i * 4, cb[i]); + } + kinc_g5_constant_buffer_unlock(&constant_buffer); + + kinc_raytrace_set_acceleration_structure(&accel); + kinc_raytrace_set_pipeline(&pipeline); + kinc_raytrace_set_target(&render_target->impl._renderTarget); + kinc_raytrace_dispatch_rays(&commandList); +} +#endif + +i32 iron_window_x() { + return kinc_window_x(0); +} + +i32 iron_window_y() { + return kinc_window_y(0); +} + +char *iron_language() { + return kinc_language(); +} + +raw_mesh_t *iron_obj_parse(buffer_t *file_bytes, i32 split_code, u64 start_pos, bool udim) { + raw_mesh_t *part = obj_parse(file_bytes, split_code, start_pos, udim); + return part; +} + +#endif diff --git a/armorcore/sources/iron_armpack.c b/armorcore/sources/iron_armpack.c new file mode 100644 index 000000000..1c6140011 --- /dev/null +++ b/armorcore/sources/iron_armpack.c @@ -0,0 +1,749 @@ + +#include "iron_armpack.h" + +#include +#include +#include +#include "iron_gc.h" + +static const int PTR_SIZE = 8; +static uint32_t di; // Decoded index +static uint32_t ei; // Encoded index +static uint32_t bottom; // Decoded bottom +static uint8_t *decoded; +static uint8_t *encoded; +static uint32_t capacity; +static uint32_t array_count; +static uint32_t string_length; +static void read_store(); + +static inline uint64_t pad(int di, int n) { + return (n - (di % n)) % n; +} + +static void store_u8(uint8_t u8) { + *(uint8_t *)(decoded + di) = u8; + di += 1; +} + +static void store_i16(int16_t i16) { + di += pad(di, 2); + *(int16_t *)(decoded + di) = i16; + di += 2; +} + +static void store_i32(int32_t i32) { + di += pad(di, 4); + *(int32_t *)(decoded + di) = i32; + di += 4; +} + +static void store_f32(float f32) { + di += pad(di, 4); + *(float *)(decoded + di) = f32; + di += 4; +} + +static void store_ptr(uint32_t ptr) { + di += pad(di, PTR_SIZE); + *(uint64_t *)(decoded + di) = (uint64_t)decoded + (uint64_t)ptr; + di += PTR_SIZE; +} + +static void store_ptr_abs(void *ptr) { + di += pad(di, PTR_SIZE); + *(uint64_t *)(decoded + di) = (uint64_t)ptr; + di += PTR_SIZE; +} + +static void store_string_bytes(char *str) { + for (int i = 0; i < string_length; ++i) { + store_u8(str[i]); + } + store_u8('\0'); +} + +static void store_string(char *str) { + // Put string at the bottom and store a pointer to it + store_ptr(bottom); + uint32_t _di = di; + di = bottom; + store_string_bytes(str); + bottom = pad(di, PTR_SIZE) + di; + di = _di; +} + +static uint8_t read_u8() { + uint8_t u8 = *(uint8_t *)(encoded + ei); + ei += 1; + return u8; +} + +static int16_t read_i16() { + int16_t i16 = *(int16_t *)(encoded + ei); + ei += 2; + return i16; +} + +static int32_t read_i32() { + int32_t i32 = *(int32_t *)(encoded + ei); + ei += 4; + return i32; +} + +static uint32_t read_u32() { + uint32_t u32 = *(uint32_t *)(encoded + ei); + ei += 4; + return u32; +} + +static float read_f32() { + float f32 = *(float *)(encoded + ei); + ei += 4; + return f32; +} + +static char *read_string() { + string_length = read_u32(); + char *str = (char *)(encoded + ei); + ei += string_length; + return str; +} + +static uint32_t traverse(int di, bool count_arrays) { + uint8_t flag = read_u8(); + switch (flag) { + case 0xc0: // NULL + return pad(di, PTR_SIZE) + PTR_SIZE; + case 0xc2: // false + return 1; + case 0xc3: // true + return 1; + case 0xca: // f32 + ei += 4; + return pad(di, 4) + 4; + case 0xd2: // i32 + ei += 4; + return pad(di, 4) + 4; + case 0xdf: { // map + uint32_t len = 0; + int count = read_i32(); + for (int i = 0; i < count; ++i) { + read_u8(); // 0xdb string + read_string(); // key + len += traverse(di + len, count_arrays); // value + } + len += pad(di + len, PTR_SIZE) + PTR_SIZE; // void *_ + return len; + } + case 0xdd: { // array + uint32_t len = 0; + int count = read_i32(); + uint8_t flag2 = read_u8(); + switch (flag2) { + case 0xca: // Typed f32 + ei += 4 * count; + len += 4 * count; + break; + case 0xd2: // Typed i32 + ei += 4 * count; + len += 4 * count; + break; + case 0xd1: // Typed i16 + ei += 2 * count; + len += 2 * count; + break; + case 0xc4: // Typed u8 + ei += count; + len += count; + break; + case 0xc2: // Bool array + ei -= 1; + ei += count; + len += count; + break; + case 0xc3: // Bool array + ei -= 1; + ei += count; + len += count; + break; + default: // Dynamic (type - value) + ei -= 1; // Undo flag2 read + for (int j = 0; j < count; ++j) { + len += traverse(0, count_arrays); + } + } + len += 32; // buffer ptr, length, capacity + align? + if (!count_arrays) { + len = 0; + } + // ptr (to array_t) + len += pad(di, PTR_SIZE) + PTR_SIZE; + return len; + } + case 0xdb: { // string + uint32_t len = read_u32(); // string_length + ei += len; + len += 1; // '\0' + if (!count_arrays) { + len = 0; + } + len += pad(di, PTR_SIZE) + PTR_SIZE; + return len; + } + default: + return 0; + } +} + +static uint32_t get_struct_length() { + uint32_t _ei = ei; + uint32_t len = traverse(0, false); + ei = _ei; + return len; +} + +static void read_store_map(int count) { + + ei -= 5; // u8 map, i32 count + uint32_t size = get_struct_length(); + size += pad(size, PTR_SIZE); + bottom += size * array_count; + array_count = 0; + ei += 5; + + for (int i = 0; i < count; ++i) { + read_u8(); // 0xdb string + read_string(); // key + read_store(); // value + } + di += pad(di, PTR_SIZE); + di += PTR_SIZE; // void *_ for runtime storage +} + +static bool is_typed_array(uint8_t flag) { + return flag == 0xca || flag == 0xd2 || flag == 0xd1 || flag == 0xc4 || flag == 0xc2 || flag == 0xc3; +} + +static uint8_t flag_to_byte_size(uint8_t flag) { + if (flag == 0xca) return 4; // f32 + if (flag == 0xd2) return 4; // i32 + if (flag == 0xd1) return 2; // i16 + if (flag == 0xc4) return 1; // u8 + if (flag == 0xc2) return 1; // u8 (true) + if (flag == 0xc3) return 1; // u8 (false) + return 0; +} + +static void store_typed_array(uint8_t flag, uint32_t count) { + uint32_t size = flag_to_byte_size(flag) * count; + memcpy(decoded + di, encoded + ei, size); + if (size > 4096) { + gc_cut(decoded, di, size); + } + ei += size; + di += size; +} + +static void read_store_array(int count) { // Store in any/i32/../_array_t format + // Store pointer to array_t struct + // Put array contents at the bottom + // - pointer to array_t buffer + // - element count + // - capacity (same as count) + // - buffer + store_ptr(bottom); + + uint32_t _di = di; + di = bottom; + + if (count > 0) { + store_ptr(di + PTR_SIZE + 4 + 4); // Pointer to buffer contents + } + else { + store_ptr_abs(NULL); + } + store_i32(count); // Element count + store_i32(0); // Capacity = 0 -> do not free on first realloc + bottom = di; + + if (count == 0) { + di = _di; + return; + } + + uint8_t flag = read_u8(); + if (is_typed_array(flag)) { + if (flag == 0xc2 || flag == 0xc3) { + ei--; // Bool array + } + store_typed_array(flag, count); + bottom = pad(di, PTR_SIZE) + di; + } + // Dynamic (type - value) + else { + ei -= 1; // Undo flag read + + // Strings + if (flag == 0xdb) { + // String pointers + uint32_t _ei = ei; + uint32_t strings_length = 0; + for (int i = 0; i < count; ++i) { + store_ptr(bottom + count * PTR_SIZE + strings_length); + if (i < count - 1) { + ei += 1; // String flag + uint32_t length = read_u32(); // String length + ei += length; + strings_length += length; + strings_length += 1; // '\0' + } + } + ei = _ei; + + // String bytes + for (int i = 0; i < count; ++i) { + ei += 1; // String flag + store_string_bytes(read_string()); + } + bottom = pad(di, PTR_SIZE) + di; + } + // Arrays + else if (flag == 0xdd) { + // Array pointers + uint32_t _ei = ei; + uint32_t arrays_length = 0; + for (int i = 0; i < count; ++i) { + store_ptr(bottom + count * PTR_SIZE + arrays_length); + if (i < count - 1) { + ei += 1; // Array flag + uint32_t length = read_u32(); // Array length + ei += length; + length += pad(length, PTR_SIZE); + arrays_length += length; + arrays_length += 8 + 8 + 4 + 4; + } + } + ei = _ei; + + // Array contents + bottom = pad(di, PTR_SIZE) + di; + + array_count = count; + + for (int i = 0; i < count; ++i) { + uint8_t flag = read_u8(); + read_store_array(read_i32()); + } + } + // Structs + else { + uint32_t size = get_struct_length(); + size += pad(size, PTR_SIZE); + + // Struct pointers + for (int i = 0; i < count; ++i) { + store_ptr(bottom + count * PTR_SIZE + i * size); + } + + // Struct contents + bottom = pad(di, PTR_SIZE) + di; + + array_count = count; + + for (int i = 0; i < count; ++i) { + di = pad(di, PTR_SIZE) + di; + uint8_t flag = read_u8(); + read_store_map(read_i32()); + } + } + } + + di = _di; +} + +static void read_store() { + uint8_t flag = read_u8(); + switch (flag) { + case 0xc0: + store_ptr_abs(NULL); + break; + case 0xc2: + store_u8(false); + break; + case 0xc3: + store_u8(true); + break; + case 0xca: + store_f32(read_f32()); + break; + case 0xd2: + store_i32(read_i32()); + break; + case 0xdf: { + array_count = 1; + read_store_map(read_i32()); + break; + } + case 0xdd: + read_store_array(read_i32()); + break; + case 0xdb: + store_string(read_string()); + break; + } +} + +static void reset() { + di = 0; + ei = 0; + bottom = 0; +} + +void *armpack_decode(buffer_t *b) { + reset(); + encoded = b->buffer; + capacity = traverse(0, true); + reset(); + decoded = gc_alloc(capacity); + read_store(); + return decoded; +} + +void armpack_encode_start(void *_encoded) { + encoded = _encoded; + ei = 0; +} + +int armpack_encode_end() { + return ei; +} + +static void armpack_write_u8(uint8_t i) { + *(uint8_t *)(encoded + ei) = i; + ei += 1; +} + +static void armpack_write_i16(int16_t i) { + *(int16_t *)(encoded + ei) = i; + ei += 2; +} + +static void armpack_write_i32(int32_t i) { + *(int32_t *)(encoded + ei) = i; + ei += 4; +} + +static void armpack_write_f32(float i) { + *(float *)(encoded + ei) = i; + ei += 4; +} + +void armpack_encode_map(uint32_t count) { + armpack_write_u8(0xdf); + armpack_write_i32(count); +} + +void armpack_encode_array(uint32_t count) { // any + armpack_write_u8(0xdd); + armpack_write_i32(count); +} + +void armpack_encode_array_f32(f32_array_t *f32a) { + if (f32a == NULL) { + armpack_write_u8(0xc0); // NULL + return; + } + armpack_write_u8(0xdd); + armpack_write_i32(f32a->length); + armpack_write_u8(0xca); + for (int i = 0; i < f32a->length; ++i) { + armpack_write_f32(f32a->buffer[i]); + } +} + +void armpack_encode_array_i32(i32_array_t *i32a) { + if (i32a == NULL) { + armpack_write_u8(0xc0); // NULL + return; + } + armpack_write_u8(0xdd); + armpack_write_i32(i32a->length); + armpack_write_u8(0xd2); + for (int i = 0; i < i32a->length; ++i) { + armpack_write_i32(i32a->buffer[i]); + } +} + +void armpack_encode_array_i16(i16_array_t *i16a) { + if (i16a == NULL) { + armpack_write_u8(0xc0); // NULL + return; + } + armpack_write_u8(0xdd); + armpack_write_i32(i16a->length); + armpack_write_u8(0xd1); + for (int i = 0; i < i16a->length; ++i) { + armpack_write_i16(i16a->buffer[i]); + } +} + +void armpack_encode_array_u8(u8_array_t *u8a) { + if (u8a == NULL) { + armpack_write_u8(0xc0); // NULL + return; + } + armpack_write_u8(0xdd); + armpack_write_i32(u8a->length); + armpack_write_u8(0xc4); + for (int i = 0; i < u8a->length; ++i) { + armpack_write_u8(u8a->buffer[i]); + } +} + +void armpack_encode_array_string(char_ptr_array_t *strings) { + if (strings == NULL) { + armpack_write_u8(0xc0); // NULL + return; + } + armpack_write_u8(0xdd); + armpack_write_i32(strings->length); + for (int i = 0; i < strings->length; ++i) { + armpack_encode_string(strings->buffer[i]); + } +} + +void armpack_encode_string(char *str) { + if (str == NULL) { + armpack_write_u8(0xc0); // NULL + return; + } + armpack_write_u8(0xdb); + size_t len = strlen(str); + armpack_write_i32(len); + for (int i = 0; i < len; ++i) { + armpack_write_u8(str[i]); + } +} + +void armpack_encode_i32(int32_t i) { + armpack_write_u8(0xd2); + armpack_write_i32(i); +} + +void armpack_encode_f32(float f) { + armpack_write_u8(0xca); + armpack_write_f32(f); +} + +void armpack_encode_bool(bool b) { + armpack_write_u8(b ? 0xc3 : 0xc2); +} + +void armpack_encode_null() { + armpack_write_u8(0xc0); +} + +int armpack_size_map() { + return 1 + 4; // u8 tag + i32 count +} + +int armpack_size_array() { + return 1 + 4; // u8 tag + i32 count +} + +int armpack_size_array_f32(f32_array_t *f32a) { + return 1 + 4 + 1 + f32a->length * 4; // u8 tag + i32 count + u8 flag + f32* contents +} + +int armpack_size_array_u8(u8_array_t *u8a) { + return 1 + 4 + 1 + u8a->length; // u8 tag + i32 count + u8 flag + u8* contents +} + +int armpack_size_string(char *str) { + return 1 + 4 + strlen(str); // u8 tag + i32 length + contents +} + +int armpack_size_i32() { + return 1 + 4; // u8 tag + i32 +} + +int armpack_size_f32() { + return 1 + 4; // u8 tag + f32 +} + +int armpack_size_bool() { + return 1; // u8 tag +} + +static char *read_string_alloc() { + char *s = read_string(); + char *allocated = gc_alloc(string_length + 1); + memcpy(allocated, s, string_length); + allocated[string_length] = '\0'; + return allocated; +} + +typedef union ptr_storage { + void *p; + struct { + float f; + int type; // 0 - float, 1 - int + }; + int i; +} ptr_storage_t; + +any_map_t *_armpack_decode_to_map() { + any_map_t *result = any_map_create(); + int32_t count = read_i32(); + + for (int i = 0; i < count; i++) { + read_u8(); // 0xdb string + char *key = read_string_alloc(); + + uint8_t flag = read_u8(); + switch (flag) { + case 0xc0: { // NULL + any_map_set(result, key, NULL); + break; + } + case 0xc2: { // false + ptr_storage_t s; + s.i = 0; + s.type = 1; + any_map_set(result, key, s.p); + break; + } + case 0xc3: { // true + ptr_storage_t s; + s.i = 1; + s.type = 1; + any_map_set(result, key, s.p); + break; + } + case 0xca: { // f32 + ptr_storage_t s; + s.f = read_f32(); + s.type = 0; + any_map_set(result, key, s.p); + break; + } + case 0xd2: { // i32 + ptr_storage_t s; + s.i = read_i32(); + s.type = 1; + any_map_set(result, key, s.p); + break; + } + case 0xdb: { // string + any_map_set(result, key, read_string_alloc()); + break; + } + case 0xdf: { // map + any_map_t *nested_map = _armpack_decode_to_map(); + any_map_set(result, key, nested_map); + break; + } + case 0xdd: { // array + int32_t array_count = read_i32(); + uint8_t element_flag = read_u8(); + if (element_flag == 0xca) { // f32 + f32_array_t *array = f32_array_create(array_count); + for (int j = 0; j < array_count; j++) { + array->buffer[j] = read_f32(); + } + any_map_set(result, key, array); + } + else if (element_flag == 0xd2) { // i32 + i32_array_t *array = i32_array_create(array_count); + for (int j = 0; j < array_count; j++) { + array->buffer[j] = read_i32(); + } + any_map_set(result, key, array); + } + else if (element_flag == 0xd1) { // i16 + i16_array_t *array = i16_array_create(array_count); + for (int j = 0; j < array_count; j++) { + array->buffer[j] = read_i16(); + } + any_map_set(result, key, array); + } + else if (element_flag == 0xc4) { // u8 + u8_array_t *array = u8_array_create(array_count); + for (int j = 0; j < array_count; j++) { + array->buffer[j] = read_u8(); + } + any_map_set(result, key, array); + } + else if (element_flag == 0xdb) { // string + ei--; + char_ptr_array_t *array = char_ptr_array_create(array_count); + for (int j = 0; j < array_count; j++) { + read_u8(); // flag + array->buffer[j] = read_string_alloc(); + } + any_map_set(result, key, array); + } + else if (element_flag == 0xdf) { // map + ei--; + any_array_t *array = any_array_create(array_count); + for (int j = 0; j < array_count; j++) { + read_u8(); // flag + array->buffer[j] = _armpack_decode_to_map(); + } + any_map_set(result, key, array); + } + else if (element_flag == 0xc6) { // buffer_t (deprecated) + ei--; + any_array_t *array = any_array_create(array_count); + for (int j = 0; j < array_count; j++) { + read_u8(); // flag + int32_t buffer_size = read_i32(); + buffer_t *buffer = buffer_create(buffer_size); + for (int k = 0; k < buffer_size; k++) { + buffer->buffer[k] = read_u8(); + } + array->buffer[j] = buffer; + } + any_map_set(result, key, array); + } + break; + } + case 0xc6: { // buffer_t (deprecated) + int32_t buffer_size = read_i32(); + buffer_t *buffer = buffer_create(buffer_size); + for (int j = 0; j < buffer_size; j++) { + buffer->buffer[j] = read_u8(); + } + any_map_set(result, key, buffer); + } + } + } + + return result; +} + +any_map_t *armpack_decode_to_map(buffer_t *b) { + encoded = b->buffer; + ei = 0; + read_u8(); // Must be 0xdf for a map + return _armpack_decode_to_map(); +} + +float armpack_map_get_f32(any_map_t *map, char *key) { + ptr_storage_t ps; + ps.p = any_map_get(map, key); + if (ps.p == NULL) { + return 0.0; + } + return ps.type == 0 ? ps.f : (float)ps.i; +} + +int armpack_map_get_i32(any_map_t *map, char *key) { + ptr_storage_t ps; + ps.p = any_map_get(map, key); + if (ps.p == NULL) { + return 0; + } + return ps.type == 1 ? ps.i : (int)ps.f; +} diff --git a/armorcore/sources/iron_armpack.h b/armorcore/sources/iron_armpack.h new file mode 100644 index 000000000..839cbd087 --- /dev/null +++ b/armorcore/sources/iron_armpack.h @@ -0,0 +1,99 @@ + +// .arm file format parser +// msgpack with typed arrays + +#pragma once + +#include +#include +#include "iron_array.h" +#include "iron_map.h" + +void *armpack_decode(buffer_t *b); + +void armpack_encode_start(void *encoded); +int armpack_encode_end(); +void armpack_encode_map(uint32_t count); +void armpack_encode_array(uint32_t count); +void armpack_encode_array_f32(f32_array_t *f32a); +void armpack_encode_array_i32(i32_array_t *i32a); +void armpack_encode_array_i16(i16_array_t *i16a); +void armpack_encode_array_u8(u8_array_t *u8a); +void armpack_encode_array_string(char_ptr_array_t *strings); +void armpack_encode_string(char *str); +void armpack_encode_i32(int32_t i); +void armpack_encode_f32(float f); +void armpack_encode_bool(bool b); +void armpack_encode_null(); + +int armpack_size_map(); +int armpack_size_array(); +int armpack_size_array_f32(f32_array_t *f32a); +int armpack_size_array_u8(u8_array_t *u8a); +int armpack_size_string(char *str); +int armpack_size_i32(); +int armpack_size_f32(); +int armpack_size_bool(); + +any_map_t *armpack_decode_to_map(buffer_t *b); +float armpack_map_get_f32(any_map_t *map, char *key); +int armpack_map_get_i32(any_map_t *map, char *key); + +/* JS object: + + let test = { + name: "test", + point: { x: 2, y: 4 }, + array: i32_array_create([1, 2, 3]) + }; +*/ + +/* C struct: + + typedef struct point { + int x; + int y; + } point_t; + + typedef struct test { + char *name; + point_t point; + int32_array_t *array; + // Optional pointer for storing runtime data + void *_; + } test_t; +*/ + +/* + void encode_decode_test() { + point_t a; + a.x = 3; + a.y = 9; + + uint32_t size = 0; + size += armpack_size_map(); + size += armpack_size_string("x"); + size += armpack_size_i32(); + size += armpack_size_string("y"); + size += armpack_size_i32(); + + void *encoded = malloc(size); + armpack_encode_start(encoded); + armpack_encode_map(2); + armpack_encode_string("x"); + armpack_encode_i32(a.x); + armpack_encode_string("y"); + armpack_encode_i32(a.y); + + buffer_t b = { .buffer = encoded, .length = size }; + point_t *decoded = armpack_decode(b); + } +*/ + +// #ifdef __GNUC__ +// #define PACK(__Declaration__) __Declaration__ __attribute__((__packed__)) +// #endif + +// #ifdef _MSC_VER +// #define PACK(__Declaration__) __pragma(pack(push, 1)) __Declaration__ __pragma(pack(pop)) +// #endif diff --git a/armorcore/sources/iron_array.c b/armorcore/sources/iron_array.c new file mode 100644 index 000000000..450248e26 --- /dev/null +++ b/armorcore/sources/iron_array.c @@ -0,0 +1,615 @@ +#include "iron_array.h" + +#include +#include +#include "iron_string.h" +#include "iron_gc.h" + +void array_free(void *a) { + u8_array_t *tmp = (u8_array_t *)a; + gc_free(tmp->buffer); + tmp->buffer = NULL; + tmp->length = tmp->capacity = 0; +} + +static void *gc_realloc_no_free(void *ptr, size_t old_size, size_t new_size) { + void *buffer = gc_alloc(new_size); + memcpy(buffer, ptr, old_size); + return buffer; +} + +static void array_alloc(void *a, uint8_t element_size) { + u8_array_t *tmp = (u8_array_t *)a; + if (tmp->length >= tmp->capacity) { + if (tmp->capacity == 0) { + // If the array was created in armpack, length can already be > 0 + tmp->capacity = tmp->length + 1; + size_t old_size = tmp->length * element_size; + size_t new_size = tmp->capacity * element_size; + tmp->buffer = gc_realloc_no_free(tmp->buffer, old_size, new_size); + } + else { + tmp->capacity *= 2; + tmp->buffer = gc_realloc(tmp->buffer, tmp->capacity * element_size); + } + if (element_size < 8) { + gc_leaf(tmp->buffer); + } + else { + gc_array(tmp->buffer, &tmp->length); + } + } +} + +void i8_array_push(i8_array_t *a, int8_t e) { + array_alloc(a, sizeof(int8_t)); + a->buffer[a->length++] = e; +} + +void u8_array_push(u8_array_t *a, uint8_t e) { + array_alloc(a, sizeof(uint8_t)); + a->buffer[a->length++] = e; +} + +void i16_array_push(i16_array_t *a, int16_t e) { + array_alloc(a, sizeof(int16_t)); + a->buffer[a->length++] = e; +} + +void u16_array_push(u16_array_t *a, uint16_t e) { + array_alloc(a, sizeof(uint16_t)); + a->buffer[a->length++] = e; +} + +void i32_array_push(i32_array_t *a, int32_t e) { + array_alloc(a, sizeof(int32_t)); + a->buffer[a->length++] = e; +} + +void u32_array_push(u32_array_t *a, uint32_t e) { + array_alloc(a, sizeof(uint32_t)); + a->buffer[a->length++] = e; +} + +void f32_array_push(f32_array_t *a, float e) { + array_alloc(a, sizeof(float)); + a->buffer[a->length++] = e; +} + +void any_array_push(any_array_t *a, void *e) { + array_alloc(a, sizeof(uintptr_t)); + a->buffer[a->length++] = e; +} + +void char_ptr_array_push(char_ptr_array_t *a, void *e) { + array_alloc(a, sizeof(uintptr_t)); + a->buffer[a->length++] = e; +} + +void i8_array_resize(i8_array_t *a, int32_t size) { + a->capacity = size; + a->buffer = gc_realloc(a->buffer, a->capacity * sizeof(int8_t)); + gc_leaf(a->buffer); +} + +void u8_array_resize(u8_array_t *a, int32_t size) { + a->capacity = size; + a->buffer = gc_realloc(a->buffer, a->capacity * sizeof(uint8_t)); + gc_leaf(a->buffer); +} + +void i16_array_resize(i16_array_t *a, int32_t size) { + a->capacity = size; + a->buffer = gc_realloc(a->buffer, a->capacity * sizeof(int16_t)); + gc_leaf(a->buffer); +} + +void u16_array_resize(u16_array_t *a, int32_t size) { + a->capacity = size; + a->buffer = gc_realloc(a->buffer, a->capacity * sizeof(uint16_t)); + gc_leaf(a->buffer); +} + +void i32_array_resize(i32_array_t *a, int32_t size) { + a->capacity = size; + a->buffer = gc_realloc(a->buffer, a->capacity * sizeof(int32_t)); + gc_leaf(a->buffer); +} + +void u32_array_resize(u32_array_t *a, int32_t size) { + a->capacity = size; + a->buffer = gc_realloc(a->buffer, a->capacity * sizeof(uint32_t)); + gc_leaf(a->buffer); +} + +void f32_array_resize(f32_array_t *a, int32_t size) { + a->capacity = size; + a->buffer = gc_realloc(a->buffer, a->capacity * sizeof(float)); + gc_leaf(a->buffer); +} + +void any_array_resize(any_array_t *a, int32_t size) { + a->capacity = size; + a->buffer = gc_realloc(a->buffer, a->capacity * sizeof(void *)); + gc_array(a->buffer, &a->length); +} + +void char_ptr_array_resize(char_ptr_array_t *a, int32_t size) { + a->capacity = size; + a->buffer = gc_realloc(a->buffer, a->capacity * sizeof(void *)); + gc_array(a->buffer, &a->length); +} + +void buffer_resize(buffer_t *b, int32_t size) { + b->length = size; + b->buffer = gc_realloc(b->buffer, b->length * sizeof(uint8_t)); + gc_leaf(b->buffer); +} + +void array_sort(any_array_t *ar, int (*compare)(const void *, const void *)) { + qsort(ar->buffer, ar->length, sizeof(ar->buffer[0]), compare); +} + +void i32_array_sort(i32_array_t *ar, int (*compare)(const void *, const void *)) { + qsort(ar->buffer, ar->length, sizeof(ar->buffer[0]), compare); +} + +void *array_pop(any_array_t *ar) { + ar->length--; + return ar->buffer[ar->length]; +} + +void *array_shift(any_array_t *ar) { + void *first = ar->buffer[0]; + for (int i = 0; i < ar->length - 1; ++i) { + ar->buffer[i] = ar->buffer[i + 1]; + } + ar->length--; + return first; +} + +void array_splice(any_array_t *ar, int32_t start, int32_t delete_count) { + for (int i = start; i < ar->length; ++i) { + if (i + delete_count >= ar->length) { + ar->buffer[i] = NULL; + } + else { + ar->buffer[i] = ar->buffer[i + delete_count]; + } + } + ar->length -= delete_count; +} + +void i32_array_splice(i32_array_t *ar, int32_t start, int32_t delete_count) { + for (int i = start; i < ar->length; ++i) { + if (i + delete_count >= ar->length) { + ar->buffer[i] = 0; + } + else { + ar->buffer[i] = ar->buffer[i + delete_count]; + } + } + ar->length -= delete_count; +} + +any_array_t *array_concat(any_array_t *a, any_array_t *b) { + any_array_t *ar = gc_alloc(sizeof(any_array_t)); + ar->length = a->length + b->length; + any_array_resize(ar, ar->length); + for (int i = 0; i < a->length; ++i) { + ar->buffer[i] = a->buffer[i]; + } + for (int i = 0; i < b->length; ++i) { + ar->buffer[a->length + i] = b->buffer[i]; + } + return ar; +} + +any_array_t *array_slice(any_array_t *a, int32_t begin, int32_t end) { + any_array_t *ar = gc_alloc(sizeof(any_array_t)); + ar->length = end - begin; + any_array_resize(ar, ar->length); + for (int i = 0; i < ar->length; ++i) { + ar->buffer[i] = a->buffer[begin + i]; + } + return ar; +} + +void array_insert(any_array_t *a, int at, void *e) { + array_alloc(a, sizeof(uintptr_t)); + for (int i = a->length; i > at; --i) { + a->buffer[i] = a->buffer[i - 1]; + } + a->length++; + a->buffer[at] = e; +} + +void array_remove(any_array_t *ar, void *e) { + int i = array_index_of(ar, e); + if (i > -1) { + array_splice(ar, i, 1); + } +} + +void char_ptr_array_remove(char_ptr_array_t *ar, char *e) { + int i = char_ptr_array_index_of(ar, e); + if (i > -1) { + array_splice(ar, i, 1); + } +} + +void i32_array_remove(i32_array_t *ar, int e) { + int i = i32_array_index_of(ar, e); + if (i > -1) { + i32_array_splice(ar, i, 1); + } +} + +int array_index_of(any_array_t *ar, void *e) { + for (int i = 0; i < ar->length; ++i) { + if (ar->buffer[i] == e) { + return i; + } + } + return -1; +} + +int char_ptr_array_index_of(char_ptr_array_t *ar, char *e) { + for (int i = 0; i < ar->length; ++i) { + if (string_equals(ar->buffer[i], e)) { + return i; + } + } + return -1; +} + +int i32_array_index_of(i32_array_t *ar, int e) { + for (int i = 0; i < ar->length; ++i) { + if (ar->buffer[i] == e) { + return i; + } + } + return -1; +} + +void array_reverse(any_array_t *ar) { + for (int i = 0; i < ar->length / 2; ++i) { + void *tmp = ar->buffer[i]; + ar->buffer[i] = ar->buffer[ar->length - 1 - i]; + ar->buffer[ar->length - 1 - i] = tmp; + } +} + +buffer_t *buffer_slice(buffer_t *a, int32_t begin, int32_t end) { + buffer_t *b = gc_alloc(sizeof(buffer_t)); + buffer_resize(b, end - begin); + for (int i = 0; i < b->length; ++i) { + b->buffer[i] = a->buffer[begin + i]; + } + return b; +} + +uint8_t buffer_get_u8(buffer_t *b, int32_t p) { + return *(uint8_t *)(b->buffer + p); +} + +int8_t buffer_get_i8(buffer_t *b, int32_t p) { + return *(int8_t *)(b->buffer + p); +} + +uint16_t buffer_get_u16(buffer_t *b, int32_t p) { + return *(uint16_t *)(b->buffer + p); +} + +int16_t buffer_get_i16(buffer_t *b, int32_t p) { + return *(int16_t *)(b->buffer + p); +} + +uint32_t buffer_get_u32(buffer_t *b, int32_t p) { + return *(uint32_t *)(b->buffer + p); +} + +int32_t buffer_get_i32(buffer_t *b, int32_t p) { + return *(int32_t *)(b->buffer + p); +} + +float buffer_get_f32(buffer_t *b, int32_t p) { + return *(float *)(b->buffer + p); +} + +double buffer_get_f64(buffer_t *b, int32_t p) { + return *(double *)(b->buffer + p); +} + +int64_t buffer_get_i64(buffer_t *b, int32_t p) { + return *(int64_t *)(b->buffer + p); +} + +void buffer_set_u8(buffer_t *b, int32_t p, uint8_t n) { + *(uint8_t *)(b->buffer + p) = n; +} + +void buffer_set_i8(buffer_t *b, int32_t p, int8_t n) { + *(int8_t *)(b->buffer + p) = n; +} + +void buffer_set_u16(buffer_t *b, int32_t p, uint16_t n) { + *(uint16_t *)(b->buffer + p) = n; +} + +void buffer_set_i16(buffer_t *b, int32_t p, uint16_t n) { + *(int16_t *)(b->buffer + p) = n; +} + +void buffer_set_u32(buffer_t *b, int32_t p, uint32_t n) { + *(uint32_t *)(b->buffer + p) = n; +} + +void buffer_set_i32(buffer_t *b, int32_t p, int32_t n) { + *(int32_t *)(b->buffer + p) = n; +} + +void buffer_set_f32(buffer_t *b, int32_t p, float n) { + *(float *)(b->buffer + p) = n; +} + +buffer_t *buffer_create(int32_t length) { + buffer_t * b = gc_alloc(sizeof(buffer_t)); + buffer_resize(b, length); + return b; +} + +buffer_t *buffer_create_from_raw(char *raw, int length) { + buffer_t * b = gc_alloc(sizeof(buffer_t)); + b->buffer = raw; + b->length = length; + b->capacity = length; + return b; +} + +f32_array_t *f32_array_create(int32_t length) { + f32_array_t *a = gc_alloc(sizeof(f32_array_t)); + if (length > 0) { + f32_array_resize(a, length); + a->length = length; + } + return a; +} + +f32_array_t *f32_array_create_from_buffer(buffer_t *b) { + f32_array_t *a = gc_alloc(sizeof(f32_array_t)); + a->buffer = b->buffer; + a->length = b->length / 4; + a->capacity = b->length / 4; + return a; +} + +f32_array_t *f32_array_create_from_array(f32_array_t *from) { + f32_array_t *a = f32_array_create(from->length); + for (int i = 0; i < from->length; ++i) { + a->buffer[i] = from->buffer[i]; + } + return a; +} + +f32_array_t *f32_array_create_from_raw(float *raw, int length) { + f32_array_t *a = f32_array_create(length); + for (int i = 0; i < length; ++i) { + a->buffer[i] = raw[i]; + } + return a; +} + +f32_array_t *f32_array_create_x(float x) { + f32_array_t *a = f32_array_create(1); + a->buffer[0] = x; + return a; +} + +f32_array_t *f32_array_create_xy(float x, float y) { + f32_array_t *a = f32_array_create(2); + a->buffer[0] = x; + a->buffer[1] = y; + return a; +} + +f32_array_t *f32_array_create_xyz(float x, float y, float z) { + f32_array_t *a = f32_array_create(3); + a->buffer[0] = x; + a->buffer[1] = y; + a->buffer[2] = z; + return a; +} + +f32_array_t *f32_array_create_xyzw(float x, float y, float z, float w) { + f32_array_t *a = f32_array_create(4); + a->buffer[0] = x; + a->buffer[1] = y; + a->buffer[2] = z; + a->buffer[3] = w; + return a; +} + +f32_array_t *f32_array_create_xyzwv(float x, float y, float z, float w, float v) { + f32_array_t *a = f32_array_create(5); + a->buffer[0] = x; + a->buffer[1] = y; + a->buffer[2] = z; + a->buffer[3] = w; + a->buffer[4] = v; + return a; +} + +u32_array_t *u32_array_create(int32_t length) { + u32_array_t *a = gc_alloc(sizeof(u32_array_t)); + if (length > 0) { + u32_array_resize(a, length); + a->length = length; + } + return a; +} + +u32_array_t *u32_array_create_from_array(u32_array_t *from) { + u32_array_t *a = u32_array_create(from->length); + for (int i = 0; i < from->length; ++i) { + a->buffer[i] = from->buffer[i]; + } + return a; +} + +u32_array_t *u32_array_create_from_raw(uint32_t *raw, int length) { + u32_array_t *a = u32_array_create(length); + for (int i = 0; i < length; ++i) { + a->buffer[i] = raw[i]; + } + return a; +} + +i32_array_t *i32_array_create(int32_t length) { + i32_array_t *a = gc_alloc(sizeof(i32_array_t)); + if (length > 0) { + i32_array_resize(a, length); + a->length = length; + } + return a; +} + +i32_array_t *i32_array_create_from_array(i32_array_t *from) { + i32_array_t *a = i32_array_create(from->length); + for (int i = 0; i < from->length; ++i) { + a->buffer[i] = from->buffer[i]; + } + return a; +} + +i32_array_t *i32_array_create_from_raw(int32_t *raw, int length) { + i32_array_t *a = i32_array_create(length); + for (int i = 0; i < length; ++i) { + a->buffer[i] = raw[i]; + } + return a; +} + +u16_array_t *u16_array_create(int32_t length) { + u16_array_t *a = gc_alloc(sizeof(u16_array_t)); + if (length > 0) { + u16_array_resize(a, length); + a->length = length; + } + return a; +} + +u16_array_t *u16_array_create_from_raw(uint16_t *raw, int length) { + u16_array_t *a = u16_array_create(length); + for (int i = 0; i < length; ++i) { + a->buffer[i] = raw[i]; + } + return a; +} + +i16_array_t *i16_array_create(int32_t length) { + i16_array_t *a = gc_alloc(sizeof(i16_array_t)); + if (length > 0) { + i16_array_resize(a, length); + a->length = length; + } + return a; +} + +i16_array_t *i16_array_create_from_array(i16_array_t *from) { + i16_array_t *a = i16_array_create(from->length); + for (int i = 0; i < from->length; ++i) { + a->buffer[i] = from->buffer[i]; + } + return a; +} + +i16_array_t *i16_array_create_from_raw(int16_t *raw, int length) { + i16_array_t *a = i16_array_create(length); + for (int i = 0; i < length; ++i) { + a->buffer[i] = raw[i]; + } + return a; +} + +u8_array_t *u8_array_create(int32_t length) { + u8_array_t *a = gc_alloc(sizeof(u8_array_t)); + if (length > 0) { + u8_array_resize(a, length); + a->length = length; + } + return a; +} + +u8_array_t *u8_array_create_from_array(u8_array_t *from) { + u8_array_t *a = u8_array_create(from->length); + for (int i = 0; i < from->length; ++i) { + a->buffer[i] = from->buffer[i]; + } + return a; +} + +u8_array_t *u8_array_create_from_raw(uint8_t *raw, int length) { + u8_array_t *a = u8_array_create(length); + for (int i = 0; i < length; ++i) { + a->buffer[i] = raw[i]; + } + return a; +} + +u8_array_t *u8_array_create_from_string(char *s) { + u8_array_t *a = u8_array_create(strlen(s) + 1); + for (int i = 0; i < strlen(s); ++i) { + a->buffer[i] = s[i]; + } + return a; +} + +char *u8_array_to_string(u8_array_t *a) { + char *r = gc_alloc(a->length + 1); + memcpy(r, a->buffer, a->length); + return r; +} + +i8_array_t *i8_array_create(int32_t length) { + i8_array_t *a = gc_alloc(sizeof(i8_array_t)); + if (length > 0) { + i8_array_resize(a, length); + a->length = length; + } + return a; +} + +i8_array_t *i8_array_create_from_raw(int8_t *raw, int length) { + i8_array_t *a = i8_array_create(length); + for (int i = 0; i < length; ++i) { + a->buffer[i] = raw[i]; + } + return a; +} + +any_array_t *any_array_create(int32_t length) { + any_array_t *a = gc_alloc(sizeof(any_array_t)); + if (length > 0) { + any_array_resize(a, length); + a->length = length; + } + return a; +} + +any_array_t *any_array_create_from_raw(void **raw, int length) { + any_array_t *a = any_array_create(length); + for (int i = 0; i < length; ++i) { + a->buffer[i] = raw[i]; + } + return a; +} + +char_ptr_array_t *char_ptr_array_create(int32_t length) { + char_ptr_array_t *a = gc_alloc(sizeof(char_ptr_array_t)); + if (length > 0) { + char_ptr_array_resize(a, length); + a->length = length; + } + return a; +} diff --git a/armorcore/sources/iron_array.h b/armorcore/sources/iron_array.h new file mode 100644 index 000000000..84d688a7d --- /dev/null +++ b/armorcore/sources/iron_array.h @@ -0,0 +1,149 @@ +#pragma once + +#include + +typedef struct i8_array { + int8_t *buffer; + int length; + int capacity; +} i8_array_t; + +typedef struct u8_array { + uint8_t *buffer; + int length; + int capacity; +} u8_array_t; + +typedef struct i16_array { + int16_t *buffer; + int length; + int capacity; +} i16_array_t; + +typedef struct u16_array { + uint16_t *buffer; + int length; + int capacity; +} u16_array_t; + +typedef struct i32_array { + int32_t *buffer; + int length; + int capacity; +} i32_array_t; + +typedef struct u32_array { + uint32_t *buffer; + int length; + int capacity; +} u32_array_t; + +typedef struct f32_array { + float *buffer; + int length; + int capacity; +} f32_array_t; + +typedef struct any_array { + void **buffer; + int length; + int capacity; +} any_array_t; + +typedef struct char_ptr_array { + char **buffer; + int length; + int capacity; +} char_ptr_array_t; + +typedef u8_array_t buffer_t; + +void array_free(void *a); +void i8_array_push(i8_array_t *a, int8_t e); +void u8_array_push(u8_array_t *a, uint8_t e); +void i16_array_push(i16_array_t *a, int16_t e); +void u16_array_push(u16_array_t *a, uint16_t e); +void i32_array_push(i32_array_t *a, int32_t e); +void u32_array_push(u32_array_t *a, uint32_t e); +void f32_array_push(f32_array_t *a, float e); +void any_array_push(any_array_t *a, void *e); +void char_ptr_array_push(char_ptr_array_t *a, void *e); + +void i8_array_resize(i8_array_t *a, int32_t size); +void u8_array_resize(u8_array_t *a, int32_t size); +void i16_array_resize(i16_array_t *a, int32_t size); +void u16_array_resize(u16_array_t *a, int32_t size); +void i32_array_resize(i32_array_t *a, int32_t size); +void u32_array_resize(u32_array_t *a, int32_t size); +void f32_array_resize(f32_array_t *a, int32_t size); +void any_array_resize(any_array_t *a, int32_t size); +void char_ptr_array_resize(char_ptr_array_t *a, int32_t size); +void buffer_resize(buffer_t *b, int32_t size); + +void array_sort(any_array_t *ar, int (*compare)(const void *, const void *)); +void i32_array_sort(i32_array_t *ar, int (*compare)(const void *, const void *)); +void *array_pop(any_array_t *ar); +void *array_shift(any_array_t *ar); +void array_splice(any_array_t *ar, int32_t start, int32_t delete_count); +void i32_array_splice(i32_array_t *ar, int32_t start, int32_t delete_count); +any_array_t *array_concat(any_array_t *a, any_array_t *b); +any_array_t *array_slice(any_array_t *a, int32_t begin, int32_t end); +void array_insert(any_array_t *a, int at, void *e); +void array_remove(any_array_t *ar, void *e); +void char_ptr_array_remove(char_ptr_array_t *ar, char *e); +void i32_array_remove(i32_array_t *ar, int e); +int array_index_of(any_array_t *ar, void *e); +int char_ptr_array_index_of(char_ptr_array_t *ar, char *e); +int i32_array_index_of(i32_array_t *ar, int e); +void array_reverse(any_array_t *ar); + +buffer_t *buffer_slice(buffer_t *a, int32_t begin, int32_t end); +uint8_t buffer_get_u8(buffer_t *b, int32_t p); +int8_t buffer_get_i8(buffer_t *b, int32_t p); +uint16_t buffer_get_u16(buffer_t *b, int32_t p); +int16_t buffer_get_i16(buffer_t *b, int32_t p); +uint32_t buffer_get_u32(buffer_t *b, int32_t p); +int32_t buffer_get_i32(buffer_t *b, int32_t p); +float buffer_get_f32(buffer_t *b, int32_t p); +double buffer_get_f64(buffer_t *b, int32_t p); +int64_t buffer_get_i64(buffer_t *b, int32_t p); +void buffer_set_u8(buffer_t *b, int32_t p, uint8_t n); +void buffer_set_i8(buffer_t *b, int32_t p, int8_t n); +void buffer_set_u16(buffer_t *b, int32_t p, uint16_t n); +void buffer_set_i16(buffer_t *b, int32_t p, uint16_t n); +void buffer_set_u32(buffer_t *b, int32_t p, uint32_t n); +void buffer_set_i32(buffer_t *b, int32_t p, int32_t n); +void buffer_set_f32(buffer_t *b, int32_t p, float n); + +buffer_t *buffer_create(int32_t length); +buffer_t *buffer_create_from_raw(char *raw, int length); +f32_array_t *f32_array_create(int32_t length); +f32_array_t *f32_array_create_from_buffer(buffer_t *b); +f32_array_t *f32_array_create_from_array(f32_array_t *from); +f32_array_t *f32_array_create_from_raw(float *raw, int length); +f32_array_t *f32_array_create_x(float x); +f32_array_t *f32_array_create_xy(float x, float y); +f32_array_t *f32_array_create_xyz(float x, float y, float z); +f32_array_t *f32_array_create_xyzw(float x, float y, float z, float w); +f32_array_t *f32_array_create_xyzwv(float x, float y, float z, float w, float v); +u32_array_t *u32_array_create(int32_t length); +u32_array_t *u32_array_create_from_array(u32_array_t *from); +u32_array_t *u32_array_create_from_raw(uint32_t *raw, int length); +i32_array_t *i32_array_create(int32_t length); +i32_array_t *i32_array_create_from_array(i32_array_t *from); +i32_array_t *i32_array_create_from_raw(int32_t *raw, int length); +u16_array_t *u16_array_create(int32_t length); +u16_array_t *u16_array_create_from_raw(uint16_t *raw, int length); +i16_array_t *i16_array_create(int32_t length); +i16_array_t *i16_array_create_from_array(i16_array_t *from); +i16_array_t *i16_array_create_from_raw(int16_t *raw, int length); +u8_array_t *u8_array_create(int32_t length); +u8_array_t *u8_array_create_from_array(u8_array_t *from); +u8_array_t *u8_array_create_from_raw(uint8_t *raw, int length); +u8_array_t *u8_array_create_from_string(char *s); +char *u8_array_to_string(u8_array_t *a); +i8_array_t *i8_array_create(int32_t length); +i8_array_t *i8_array_create_from_raw(int8_t *raw, int length); +any_array_t *any_array_create(int32_t length); +any_array_t *any_array_create_from_raw(void **raw, int length); +char_ptr_array_t *char_ptr_array_create(int32_t length); diff --git a/armorcore/sources/iron_gc.c b/armorcore/sources/iron_gc.c new file mode 100644 index 000000000..f036c3a3f --- /dev/null +++ b/armorcore/sources/iron_gc.c @@ -0,0 +1,131 @@ + +#include "iron_gc.h" + +#ifdef NO_GC + +#include +#include + +#define HEAP_SIZE 512 * 1024 * 1024 +static uint8_t *heap = NULL; +static size_t heap_top = 0; + +void *gc_alloc(size_t size) { + #ifdef HEAP_SIZE + size_t old_top = heap_top; + heap_top += size; + return &heap[old_top]; + #else + return calloc(size, 1); + #endif +} + +void gc_array(void *ptr, int *length) { +} + +void gc_leaf(void *ptr) { +} + +void gc_root(void *ptr) { +} + +void gc_unroot(void *ptr) { +} + +void *gc_cut(void *ptr, size_t pos, size_t size) { + return NULL; +} + +void *gc_realloc(void *ptr, size_t size) { + #ifdef HEAP_SIZE + // NOTE: gc_realloc is not implemented when HEAP_SIZE is defined + return gc_alloc(size); + #else + return realloc(ptr, size); + #endif +} + +void gc_free(void *ptr) { + #ifdef HEAP_SIZE + #else + free(ptr); + #endif +} + +void gc_pause() { +} + +void gc_resume() { +} + +void gc_run() { +} + +void gc_start(void *bos) { + #ifdef HEAP_SIZE + heap = (uint8_t *)calloc(HEAP_SIZE, 1); + #endif +} + +void gc_stop() { +} + +#else + +#include + +void *gc_alloc(size_t size) { + return _gc_calloc(size, sizeof(uint8_t)); +} + +void gc_array(void *ptr, int *length) { + _gc_array(ptr, length); +} + +void gc_leaf(void *ptr) { + _gc_leaf(ptr); +} + +void gc_root(void *ptr) { + _gc_root(ptr); +} + +void gc_unroot(void *ptr) { + _gc_unroot(ptr); +} + +void *gc_cut(void *ptr, size_t pos, size_t size) { + return _gc_cut(ptr, pos, size); +} + +void *gc_realloc(void *ptr, size_t size) { + return ptr == NULL ? gc_alloc(size) : _gc_realloc(ptr, size); +} + +void gc_free(void *ptr) { + if (ptr != NULL) { + _gc_free(ptr); + } +} + +void gc_pause() { + _gc_pause(); +} + +void gc_resume() { + _gc_resume(); +} + +void gc_run() { + _gc_run(); +} + +void gc_start(void *bos) { + _gc_start(bos); +} + +void gc_stop() { + _gc_stop(); +} + +#endif diff --git a/armorcore/sources/iron_gc.h b/armorcore/sources/iron_gc.h new file mode 100644 index 000000000..d9962576d --- /dev/null +++ b/armorcore/sources/iron_gc.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +// point_t *p = GC_ALLOC_INIT(point_t, {x: 1.5, y: 3.5}); +#define GC_ALLOC_INIT(type, ...) (type *)memcpy(gc_alloc(sizeof(type)), (type[]){ __VA_ARGS__ }, sizeof(type)) + +void *gc_alloc(size_t size); +void gc_array(void *ptr, int *length); +void gc_leaf(void *ptr); +void gc_root(void *ptr); +void gc_unroot(void *ptr); +void *gc_cut(void *ptr, size_t pos, size_t size); +void *gc_realloc(void *ptr, size_t size); +void gc_free(void *ptr); +void gc_pause(); +void gc_resume(); +void gc_run(); +void gc_start(void *bos); +void gc_stop(); diff --git a/armorcore/sources/iron_json.c b/armorcore/sources/iron_json.c new file mode 100644 index 000000000..5433c28ec --- /dev/null +++ b/armorcore/sources/iron_json.c @@ -0,0 +1,416 @@ + +#include "iron_json.h" +#include "iron_string.h" +#include +#include +#include +#include + +void *gc_alloc(size_t size); + +static const int PTR_SIZE = 8; +static char *source; +static jsmntok_t *tokens; +static int num_tokens; +static uint32_t ti; // token index +static uint8_t *decoded; +static uint32_t wi; // write index +static uint32_t bottom; +static int array_count; + +static inline uint64_t pad(int di, int n) { + return (n - (di % n)) % n; +} + +static void store_u8(uint8_t u8) { + *(uint8_t *)(decoded + wi) = u8; + wi += 1; +} + +static void store_i32(int32_t i32) { + // TODO: signed overflow is UB + // if (i32 > INT32_MAX) + // i32 = (int32_t)(i32 - INT32_MAX - 1) - INT32_MAX - 1; + wi += pad(wi, 4); + *(int32_t *)(decoded + wi) = i32; + wi += 4; +} + +static void store_f32(float f32) { + wi += pad(wi, 4); + *(float *)(decoded + wi) = f32; + wi += 4; +} + +static void store_ptr(uint32_t ptr) { + wi += pad(wi, PTR_SIZE); + *(uint64_t *)(decoded + wi) = (uint64_t)decoded + (uint64_t)ptr; + wi += PTR_SIZE; +} + +static void store_ptr_abs(void *ptr) { + wi += pad(wi, PTR_SIZE); + *(uint64_t *)(decoded + wi) = (uint64_t)ptr; + wi += PTR_SIZE; +} + +static void store_string_bytes(char *str, int len) { + for (int i = 0; i < len; ++i) { + store_u8(str[i]); + } + store_u8('\0'); +} + +static bool is_key(char *s, jsmntok_t *t) { + return t->type == JSMN_STRING && s[t->end + 1] == ':'; +} + +static jsmntok_t get_token() { + jsmntok_t t = tokens[ti]; + while (is_key(source, &t)) { + ti++; + t = tokens[ti]; + } + return t; +} + +static int traverse(int wi) { + jsmntok_t t = get_token(); + if (t.type == JSMN_OBJECT) { + ti++; + int size = 0; + for (int i = 0; i < t.size; ++i) { + size += traverse(wi + size); + } + return pad(wi, PTR_SIZE) + size; + } + else if (t.type == JSMN_PRIMITIVE) { + ti++; + if (source[t.start] == 't' || source[t.start] == 'f') { // bool + return 1; + } + else if (source[t.start] == 'n') { // null + return pad(wi, PTR_SIZE) + PTR_SIZE; + } + else { // number + return pad(wi, 4) + 4; + } + } + else if (t.type == JSMN_ARRAY) { + ti++; + for (int i = 0; i < t.size; ++i) { + traverse(0); + } + return pad(wi, PTR_SIZE) + PTR_SIZE; + } + else if (t.type == JSMN_STRING) { + ti++; + return pad(wi, PTR_SIZE) + PTR_SIZE; + } + + return 0; +} + +static int token_size() { + uint32_t _ti = ti; + uint32_t len = traverse(0); + ti = _ti; + return len; +} + +static bool has_dot(char *str, int len) { + for (int i = 0; i < len; ++i) { + if (str[i] == '.') { + return true; + } + } + return false; +} + +static void token_write() { + jsmntok_t t = get_token(); + + if (t.type == JSMN_OBJECT) { + // TODO: Object containing another object + // Write object contents + int size = token_size(); + size += pad(size, PTR_SIZE); + bottom += size * array_count; + ti++; + for (int i = 0; i < t.size; ++i) { + token_write(); + } + } + else if (t.type == JSMN_PRIMITIVE) { + ti++; + if (source[t.start] == 't' || source[t.start] == 'f') { // bool + store_u8(source[t.start] == 't' ? 1 : 0); + } + else if (source[t.start] == 'n') { // null + store_ptr_abs(NULL); + } + else { + has_dot(source + t.start, t.end - t.start) ? + store_f32(strtof(source + t.start, NULL)) : + #ifdef _WIN32 + store_i32(_strtoi64(source + t.start, NULL, 10)); + #else + store_i32(strtol(source + t.start, NULL, 10)); + #endif + } + } + else if (t.type == JSMN_ARRAY) { + ti++; + store_ptr(bottom); + + uint32_t _wi = wi; + wi = bottom; + store_ptr(bottom + PTR_SIZE + 4 + 4); // Pointer to buffer contents + store_i32(t.size); // Element count + store_i32(0); // Capacity = 0 -> do not free on first realloc + bottom = wi; + + if (t.size == 0) { + wi = _wi; + return; + } + + int count = t.size; + array_count = count; + t = get_token(); + + if (t.type == JSMN_OBJECT) { + // Struct pointers + uint32_t size = token_size(); + size += pad(size, PTR_SIZE); + + for (int i = 0; i < count; ++i) { + store_ptr(bottom + count * PTR_SIZE + i * size); + } + + // Struct contents + bottom = pad(wi, PTR_SIZE) + wi; + for (int i = 0; i < count; ++i) { + wi = pad(wi, PTR_SIZE) + wi; + token_write(); + } + } + else if (t.type == JSMN_STRING) { + // String pointers + uint32_t _ti = ti; + uint32_t strings_length = 0; + for (int i = 0; i < count; ++i) { + store_ptr(bottom + count * PTR_SIZE + strings_length); + uint32_t length = t.end - t.start; // String length + strings_length += length; + strings_length += 1; // '\0' + ti++; + t = get_token(); + } + ti = _ti; + t = get_token(); + + // String bytes + for (int i = 0; i < count; ++i) { + store_string_bytes(source + t.start, t.end - t.start); + ti++; + t = get_token(); + } + bottom = pad(wi, PTR_SIZE) + wi; + } + else { + // Array contents + for (int i = 0; i < count; ++i) { + token_write(); + } + bottom = pad(wi, PTR_SIZE) + wi; + } + + wi = _wi; + array_count = 1; + } + else if (t.type == JSMN_STRING) { + ti++; + store_ptr(bottom); + + uint32_t _wi = wi; + wi = bottom; + store_string_bytes(source + t.start, t.end - t.start); + bottom = pad(wi, PTR_SIZE) + wi; + wi = _wi; + } +} + +static void load_tokens(char *s) { + jsmn_parser parser; + jsmn_init(&parser); + num_tokens = jsmn_parse(&parser, s, strlen(s), NULL, 0); + + tokens = malloc(sizeof(jsmntok_t) * num_tokens); + jsmn_init(&parser); + jsmn_parse(&parser, s, strlen(s), tokens, num_tokens); + + source = s; + ti = 0; +} + +void *json_parse(char *s) { + load_tokens(s); + + int out_size = strlen(s) * 2; + decoded = gc_alloc(out_size); + wi = 0; + bottom = 0; + array_count = 1; + token_write(); + + free(tokens); + return decoded; +} + +static void token_write_to_map(any_map_t *m) { + jsmntok_t t = get_token(); + + if (t.type == JSMN_OBJECT) { + // TODO: Object containing another object + ti++; + for (int i = 0; i < t.size; ++i) { + token_write_to_map(m); + } + } + else if (t.type == JSMN_PRIMITIVE) { + jsmntok_t tkey = tokens[ti - 1]; + ti++; + any_map_set(m, substring(source, tkey.start, tkey.end), substring(source, t.start, t.end)); + } + else if (t.type == JSMN_ARRAY) { + ti++; + } + else if (t.type == JSMN_STRING) { + jsmntok_t tkey = tokens[ti - 1]; + ti++; + any_map_set(m, substring(source, tkey.start, tkey.end), substring(source, t.start, t.end)); + } +} + +any_map_t *json_parse_to_map(char *s) { + load_tokens(s); + + any_map_t *m = any_map_create(); + token_write_to_map(m); + + free(tokens); + return m; +} + +static char *encoded; +static int keys; + +void json_encode_begin() { + encoded = "{"; + keys = 0; +} + +char *json_encode_end() { + encoded = string_join(encoded, "}"); + return encoded; +} + +void json_encode_key(char *k) { + if (keys > 0) { + encoded = string_join(encoded, ","); + } + encoded = string_join(encoded, "\""); + encoded = string_join(encoded, k); + encoded = string_join(encoded, "\":"); + keys++; +} + +void json_encode_string_value(char *v) { + encoded = string_join(encoded, "\""); + encoded = string_join(encoded, v); + encoded = string_join(encoded, "\""); +} + +void json_encode_string(char *k, char *v) { + json_encode_key(k); + json_encode_string_value(v); +} + +void json_encode_string_array(char *k, char_ptr_array_t *a) { + json_encode_begin_array(k); + for (int i = 0; i < a->length; ++i) { + if (i > 0) { + encoded = string_join(encoded, ","); + } + json_encode_string_value(a->buffer[i]); + } + json_encode_end_array(); +} + +void json_encode_f32(char *k, float f) { + json_encode_key(k); + encoded = string_join(encoded, f32_to_string_with_zeros(f)); +} + +void json_encode_i32(char *k, int i) { + json_encode_key(k); + encoded = string_join(encoded, i32_to_string(i)); +} + +void json_encode_null(char *k) { + json_encode_key(k); + encoded = string_join(encoded, "null"); +} + +void json_encode_f32_array(char *k, f32_array_t *a) { + json_encode_begin_array(k); + for (int i = 0; i < a->length; ++i) { + if (i > 0) { + encoded = string_join(encoded, ","); + } + encoded = string_join(encoded, f32_to_string(a->buffer[i])); + } + json_encode_end_array(); +} + +void json_encode_i32_array(char *k, i32_array_t *a) { + json_encode_begin_array(k); + for (int i = 0; i < a->length; ++i) { + if (i > 0) { + encoded = string_join(encoded, ","); + } + encoded = string_join(encoded, i32_to_string(a->buffer[i])); + } + json_encode_end_array(); +} + +void json_encode_bool(char *k, bool b) { + json_encode_key(k); + encoded = string_join(encoded, b ? "true" : "false"); +} + +void json_encode_begin_array(char *k) { + json_encode_key(k); + encoded = string_join(encoded, "["); +} + +void json_encode_end_array() { + encoded = string_join(encoded, "]"); +} + +void json_encode_begin_object() { + keys = 0; + encoded = string_join(encoded, "{"); +} + +void json_encode_end_object() { + encoded = string_join(encoded, "}"); +} + +void json_encode_map(any_map_t *m) { + any_array_t *keys = map_keys(m); + for (int i = 0; i < keys->length; i++) { + json_encode_string(keys->buffer[i], any_map_get(m, keys->buffer[i])); + } +} diff --git a/armorcore/sources/iron_json.h b/armorcore/sources/iron_json.h new file mode 100644 index 000000000..95da3427a --- /dev/null +++ b/armorcore/sources/iron_json.h @@ -0,0 +1,24 @@ +#pragma once + +#include "iron_array.h" +#include "iron_map.h" +#include + +void *json_parse(char *s); +any_map_t *json_parse_to_map(char *s); + +void json_encode_begin(); +char *json_encode_end(); +void json_encode_string(char *k, char *v); +void json_encode_string_array(char *k, char_ptr_array_t *a); +void json_encode_f32(char *k, float f); +void json_encode_i32(char *k, int i); +void json_encode_null(char *k); +void json_encode_f32_array(char *k, f32_array_t *a); +void json_encode_i32_array(char *k, i32_array_t *a); +void json_encode_bool(char *k, bool b); +void json_encode_begin_array(char *k); +void json_encode_end_array(); +void json_encode_begin_object(); +void json_encode_end_object(); +void json_encode_map(any_map_t *m); diff --git a/armorcore/sources/iron_map.c b/armorcore/sources/iron_map.c new file mode 100644 index 000000000..22bf86943 --- /dev/null +++ b/armorcore/sources/iron_map.c @@ -0,0 +1,220 @@ + +#include "iron_map.h" +#include "iron_gc.h" +#include "iron_string.h" + +static size_t hash(const char *k) { + // fnv1a + size_t hash = 0x811c9dc5; + while (*k) { + hash ^= (unsigned char) *k++; + hash *= 0x01000193; + } + return hash; +} + +static size_t index_set(any_map_t *m, char *k) { + size_t i = hash(k) & (m->keys->capacity - 1); // % m->keys->capacity + while (true) { + if (m->keys->buffer[i] == NULL) { + m->keys->length++; + break; + } + if (string_equals(k, m->keys->buffer[i])) { + break; + } + ++i; + if (i > m->keys->capacity - 1) { + i = 0; + } + } + return i; +} + +static size_t index_get(char_ptr_array_t *keys, char *k) { + if (k == NULL || keys->capacity == 0) { + return -1; + } + size_t i = hash(k) & (keys->capacity - 1); + while (!string_equals(k, keys->buffer[i])) { + if (keys->buffer[i] == NULL) { + return -1; + } + ++i; + if (i > keys->capacity - 1) { + i = 0; + } + } + return i; +} + +static void resize(any_map_t *m, int elem_size) { + if (m->keys->length < m->keys->capacity * 0.5) { + return; + } + + int cap = m->keys->capacity == 0 ? 16 : m->keys->capacity * 2; + any_map_t *tmp = any_map_create(); + tmp->keys->capacity = cap; + tmp->keys->buffer = gc_realloc(tmp->keys->buffer, cap * sizeof(void *)); + tmp->values->capacity = cap; + tmp->values->buffer = gc_realloc(tmp->values->buffer, cap * elem_size); + if (elem_size < 8) { + gc_leaf(tmp->values->buffer); + } + + any_array_t *old_keys = map_keys(m); + for (int i = 0; i < old_keys->length; ++i) { + char *k = old_keys->buffer[i]; + size_t j = index_set(tmp, k); + tmp->keys->buffer[j] = k; + if (elem_size == 8) { + void **buf = tmp->values->buffer; + buf[j] = any_map_get(m, k); + } + else { + int32_t *buf = tmp->values->buffer; + buf[j] = i32_map_get(m, k); + } + } + + m->keys = tmp->keys; + m->values = tmp->values; +} + +void i32_map_set(i32_map_t *m, char *k, int v) { + resize(m, 4); + size_t i = index_set(m, k); + m->keys->buffer[i] = k; + m->values->buffer[i] = v; +} + +void f32_map_set(f32_map_t *m, char *k, float v) { + resize(m, 4); + size_t i = index_set(m, k); + m->keys->buffer[i] = k; + m->values->buffer[i] = v; +} + +void any_map_set(any_map_t *m, char *k, void *v) { + resize(m, 8); + size_t i = index_set(m, k); + m->keys->buffer[i] = k; + m->values->buffer[i] = v; +} + +int32_t i32_map_get(i32_map_t *m, char *k) { + size_t i = index_get(m->keys, k); + return i == -1 ? -1 : m->values->buffer[i]; +} + +float f32_map_get(f32_map_t *m, char *k) { + size_t i = index_get(m->keys, k); + return i == -1 ? -1.0 : m->values->buffer[i]; +} + +void *any_map_get(any_map_t *m, char *k) { + size_t i = index_get(m->keys, k); + return i == -1 ? NULL : m->values->buffer[i]; +} + +void map_delete(any_map_t *m, char *k) { + size_t i = index_get(m->keys, k); + if (i != -1) { + m->keys->buffer[i] = NULL; + m->keys->length--; + } +} + +any_array_t *map_keys(any_map_t *m) { + char_ptr_array_t *keys = gc_alloc(sizeof(char_ptr_array_t)); + char_ptr_array_resize(keys, m->keys->length); + for (int i = 0; i < m->keys->capacity; ++i) { + if (m->keys->buffer[i] != NULL) { + char_ptr_array_push(keys, m->keys->buffer[i]); + } + } + return keys; +} + +i32_map_t *i32_map_create() { + i32_map_t *r = gc_alloc(sizeof(i32_map_t)); + r->keys = gc_alloc(sizeof(char_ptr_array_t)); + r->values = gc_alloc(sizeof(i32_array_t)); + return r; +} + +any_map_t *any_map_create() { + any_map_t *r = gc_alloc(sizeof(any_map_t)); + r->keys = gc_alloc(sizeof(char_ptr_array_t)); + r->values = gc_alloc(sizeof(any_array_t)); + return r; +} + +// imap + +void i32_imap_set(i32_imap_t *m, int k, int v) { + int i = i32_array_index_of(m->keys, k); + if (i == -1) { + i32_array_push(m->keys, k); + i32_array_push(m->values, v); + } + else { + m->keys->buffer[i] = k; + m->values->buffer[i] = v; + } +} + +void any_imap_set(any_imap_t *m, int k, void *v) { + int i = i32_array_index_of(m->keys, k); + if (i == -1) { + i32_array_push(m->keys, k); + any_array_push(m->values, v); + } + else { + m->keys->buffer[i] = k; + m->values->buffer[i] = v; + } +} + +int32_t i32_imap_get(i32_imap_t *m, int k) { + int i = i32_array_index_of(m->keys, k); + if (i > -1) { + return m->values->buffer[i]; + } + return -1; +} + +void *any_imap_get(any_imap_t *m, int k) { + int i = i32_array_index_of(m->keys, k); + if (i > -1) { + return m->values->buffer[i]; + } + return NULL; +} + +void imap_delete(any_imap_t *m, int k) { + int i = i32_array_index_of(m->keys, k); + if (i > -1) { + array_splice(m->keys, i, 1); + array_splice(m->values, i, 1); + } +} + +i32_array_t *imap_keys(any_imap_t *m) { + return m->keys; +} + +i32_imap_t *i32_imap_create() { + i32_imap_t *r = gc_alloc(sizeof(i32_imap_t)); + r->keys = gc_alloc(sizeof(i32_array_t)); + r->values = gc_alloc(sizeof(i32_array_t)); + return r; +} + +any_imap_t *any_imap_create() { + any_imap_t *r = gc_alloc(sizeof(any_imap_t)); + r->keys = gc_alloc(sizeof(i32_array_t)); + r->values = gc_alloc(sizeof(any_array_t)); + return r; +} diff --git a/armorcore/sources/iron_map.h b/armorcore/sources/iron_map.h new file mode 100644 index 000000000..0773a8653 --- /dev/null +++ b/armorcore/sources/iron_map.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include "iron_array.h" + +typedef struct i32_map { + char_ptr_array_t *keys; + i32_array_t *values; +} i32_map_t; + +typedef struct f32_map { + char_ptr_array_t *keys; + f32_array_t *values; +} f32_map_t; + +typedef struct any_map { + char_ptr_array_t *keys; + any_array_t *values; +} any_map_t; + +void i32_map_set(i32_map_t *m, char *k, int v); +void f32_map_set(f32_map_t *m, char *k, float v); +void any_map_set(any_map_t *m, char *k, void *v); + +int32_t i32_map_get(i32_map_t *m, char *k); +float f32_map_get(f32_map_t *m, char *k); +void *any_map_get(any_map_t *m, char *k); + +void map_delete(any_map_t *m, char *k); +any_array_t *map_keys(any_map_t *m); + +i32_map_t *i32_map_create(); +any_map_t *any_map_create(); + +// imap + +typedef struct i32_imap { + i32_array_t *keys; + i32_array_t *values; +} i32_imap_t; + +typedef struct any_imap { + i32_array_t *keys; + any_array_t *values; +} any_imap_t; + +void i32_imap_set(i32_imap_t *m, int k, int v); +void any_imap_set(any_imap_t *m, int k, void *v); +int i32_imap_get(i32_imap_t *m, int k); +void *any_imap_get(any_imap_t *m, int k); +void imap_delete(any_imap_t *m, int k); +i32_array_t *imap_keys(any_imap_t *m); +any_imap_t *any_imap_create(); diff --git a/armorcore/sources/iron_mat3.c b/armorcore/sources/iron_mat3.c new file mode 100644 index 000000000..0b6be1978 --- /dev/null +++ b/armorcore/sources/iron_mat3.c @@ -0,0 +1,82 @@ +#include "iron_mat3.h" + +#include +#include + +mat3_t mat3_create(float _00, float _10, float _20, + float _01, float _11, float _21, + float _02, float _12, float _22) { + mat3_t m; + m.m[0] = _00; + m.m[1] = _01; + m.m[2] = _02; + m.m[3] = _10; + m.m[4] = _11; + m.m[5] = _12; + m.m[6] = _20; + m.m[7] = _21; + m.m[8] = _22; + return m; +} + +mat3_t mat3_identity() { + return mat3_create( + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 + ); +} + +mat3_t mat3_translation(float x, float y) { + return mat3_create( + 1, 0, x, + 0, 1, y, + 0, 0, 1 + ); +} + +mat3_t mat3_rotation(float alpha) { + return mat3_create( + cosf(alpha), -sinf(alpha), 0, + sinf(alpha), cosf(alpha), 0, + 0, 0, 1 + ); +} + +mat3_t mat3_set_from4(mat4_t m4) { + mat3_t m; + m.m[0] = m4.m[0]; + m.m[1] = m4.m[1]; + m.m[2] = m4.m[2]; + m.m[3] = m4.m[4]; + m.m[4] = m4.m[5]; + m.m[5] = m4.m[6]; + m.m[6] = m4.m[8]; + m.m[7] = m4.m[9]; + m.m[8] = m4.m[10]; + return m; +} + +mat3_t mat3_multmat(mat3_t a, mat3_t b) { + return mat3_create( + a.m[0] * b.m[0] + a.m[3] * b.m[1] + a.m[6] * b.m[2], + a.m[0] * b.m[3] + a.m[3] * b.m[4] + a.m[6] * b.m[5], + a.m[0] * b.m[6] + a.m[3] * b.m[7] + a.m[6] * b.m[8], + a.m[1] * b.m[0] + a.m[4] * b.m[1] + a.m[7] * b.m[2], + a.m[1] * b.m[3] + a.m[4] * b.m[4] + a.m[7] * b.m[5], + a.m[1] * b.m[6] + a.m[4] * b.m[7] + a.m[7] * b.m[8], + a.m[2] * b.m[0] + a.m[5] * b.m[1] + a.m[8] * b.m[2], + a.m[2] * b.m[3] + a.m[5] * b.m[4] + a.m[8] * b.m[5], + a.m[2] * b.m[6] + a.m[5] * b.m[7] + a.m[8] * b.m[8] + ); +} + +mat3_t mat3_nan() { + mat3_t m; + m.m[0] = NAN; + return m; +} + +bool mat3_isnan(mat3_t m) { + return isnan(m.m[0]); +} diff --git a/armorcore/sources/iron_mat3.h b/armorcore/sources/iron_mat3.h new file mode 100644 index 000000000..05763087a --- /dev/null +++ b/armorcore/sources/iron_mat3.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include "iron_mat4.h" + +mat3_t mat3_create(float _00, float _10, float _20, + float _01, float _11, float _21, + float _02, float _12, float _22); +mat3_t mat3_identity(); +mat3_t mat3_translation(float x, float y); +mat3_t mat3_rotation(float alpha); +mat3_t mat3_set_from4(mat4_t m4); +mat3_t mat3_multmat(mat3_t a, mat3_t b); +mat3_t mat3_nan(); +bool mat3_isnan(mat3_t m); diff --git a/armorcore/sources/iron_mat4.c b/armorcore/sources/iron_mat4.c new file mode 100644 index 000000000..4045bcbde --- /dev/null +++ b/armorcore/sources/iron_mat4.c @@ -0,0 +1,556 @@ +#include "iron_mat4.h" + +#include +#include "iron_gc.h" + +mat4_t mat4_create(float _00, float _10, float _20, float _30, + float _01, float _11, float _21, float _31, + float _02, float _12, float _22, float _32, + float _03, float _13, float _23, float _33) { + mat4_t m; + m.m[0] = _00; + m.m[1] = _01; + m.m[2] = _02; + m.m[3] = _03; + m.m[4] = _10; + m.m[5] = _11; + m.m[6] = _12; + m.m[7] = _13; + m.m[8] = _20; + m.m[9] = _21; + m.m[10] = _22; + m.m[11] = _23; + m.m[12] = _30; + m.m[13] = _31; + m.m[14] = _32; + m.m[15] = _33; + return m; +} + +mat4_t mat4_identity() { + return mat4_create( + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); +} + +mat4_t mat4_from_f32_array(f32_array_t *a, int offset) { + return mat4_create( + a->buffer[0 + offset], a->buffer[1 + offset], a->buffer[2 + offset], a->buffer[3 + offset], + a->buffer[4 + offset], a->buffer[5 + offset], a->buffer[6 + offset], a->buffer[7 + offset], + a->buffer[8 + offset], a->buffer[9 + offset], a->buffer[10 + offset], a->buffer[11 + offset], + a->buffer[12 + offset], a->buffer[13 + offset], a->buffer[14 + offset], a->buffer[15 + offset] + ); +} + +mat4_t mat4_persp(float fov_y, float aspect, float zn, float zf) { + float uh = 1.0 / tanf(fov_y / 2); + float uw = uh / aspect; + return mat4_create( + uw, 0, 0, 0, + 0, uh, 0, 0, + 0, 0, (zf + zn) / (zn - zf), 2 * zf * zn / (zn - zf), + 0, 0, -1, 0 + ); +} + +mat4_t mat4_ortho(float left, float right, float bottom, float top, float znear, float zfar) { + float rl = right - left; + float tb = top - bottom; + float fn = zfar - znear; + float tx = -(right + left) / (rl); + float ty = -(top + bottom) / (tb); + float tz = -(zfar + znear) / (fn); + return mat4_create( + 2 / rl, 0, 0, tx, + 0, 2 / tb, 0, ty, + 0, 0, -2 / fn, tz, + 0, 0, 0, 1 + ); +} + +mat4_t mat4_rot_z(float alpha) { + float ca = cosf(alpha); + float sa = sinf(alpha); + return mat4_create( + ca, -sa, 0, 0, + sa, ca, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ); +} + +mat4_t mat4_compose(vec4_t loc, quat_t rot, vec4_t scl) { + mat4_t m = mat4_from_quat(rot); + m = mat4_scale(m, scl); + m = mat4_set_loc(m, loc); + return m; +} + +mat4_decomposed_t *mat4_decompose(mat4_t m) { + vec4_t loc; + quat_t rot; + vec4_t scl; + loc.x = m.m[12]; + loc.y = m.m[13]; + loc.z = m.m[14]; + scl.x = vec4_len(vec4_create(m.m[0], m.m[1], m.m[2], 1.0)); + scl.y = vec4_len(vec4_create(m.m[4], m.m[5], m.m[6], 1.0)); + scl.z = vec4_len(vec4_create(m.m[8], m.m[9], m.m[10], 1.0)); + if (mat4_determinant(m) < 0.0) { + scl.x = -scl.x; + } + float invs = 1.0 / scl.x; // Scale the rotation part + m.m[0] *= invs; + m.m[1] *= invs; + m.m[2] *= invs; + invs = 1.0 / scl.y; + m.m[4] *= invs; + m.m[5] *= invs; + m.m[6] *= invs; + invs = 1.0 / scl.z; + m.m[8] *= invs; + m.m[9] *= invs; + m.m[10] *= invs; + rot = quat_from_rot_mat(m); + + mat4_decomposed_t *dec = gc_alloc(sizeof(mat4_decomposed_t)); + dec->loc = loc; + dec->rot = rot; + dec->scl = scl; + return dec; +} + +mat4_t mat4_set_loc(mat4_t m, vec4_t v) { + m.m[12] = v.x; + m.m[13] = v.y; + m.m[14] = v.z; + return m; +} + +mat4_t mat4_from_quat(quat_t q) { + float x2 = q.x + q.x; + float y2 = q.y + q.y; + float z2 = q.z + q.z; + float xx = q.x * x2; + float xy = q.x * y2; + float xz = q.x * z2; + float yy = q.y * y2; + float yz = q.y * z2; + float zz = q.z * z2; + float wx = q.w * x2; + float wy = q.w * y2; + float wz = q.w * z2; + + mat4_t m; + m.m[0] = 1.0 - (yy + zz); + m.m[4] = xy - wz; + m.m[8] = xz + wy; + + m.m[1] = xy + wz; + m.m[5] = 1.0 - (xx + zz); + m.m[9] = yz - wx; + + m.m[2] = xz - wy; + m.m[6] = yz + wx; + m.m[10] = 1.0 - (xx + yy); + + m.m[3] = 0.0; + m.m[7] = 0.0; + m.m[11] = 0.0; + m.m[12] = 0.0; + m.m[13] = 0.0; + m.m[14] = 0.0; + m.m[15] = 1.0; + + return m; +} + +mat4_t mat4_init_translate(float x, float y, float z) { + mat4_t m; + m.m[0] = 1.0; + m.m[1] = 0.0; + m.m[2] = 0.0; + m.m[3] = 0.0; + m.m[4] = 0.0; + m.m[5] = 1.0; + m.m[6] = 0.0; + m.m[7] = 0.0; + m.m[8] = 0.0; + m.m[9] = 0.0; + m.m[10] = 1.0; + m.m[11] = 0.0; + m.m[12] = x; + m.m[13] = y; + m.m[14] = z; + m.m[15] = 1.0; + return m; +} + +mat4_t mat4_translate(mat4_t m, float x, float y, float z) { + m.m[0] += x * m.m[3]; + m.m[1] += y * m.m[3]; + m.m[2] += z * m.m[3]; + m.m[4] += x * m.m[7]; + m.m[5] += y * m.m[7]; + m.m[6] += z * m.m[7]; + m.m[8] += x * m.m[11]; + m.m[9] += y * m.m[11]; + m.m[10] += z * m.m[11]; + m.m[12] += x * m.m[15]; + m.m[13] += y * m.m[15]; + m.m[14] += z * m.m[15]; + return m; +} + +mat4_t mat4_scale(mat4_t m, vec4_t v) { + m.m[0] *= v.x; + m.m[1] *= v.x; + m.m[2] *= v.x; + m.m[3] *= v.x; + m.m[4] *= v.y; + m.m[5] *= v.y; + m.m[6] *= v.y; + m.m[7] *= v.y; + m.m[8] *= v.z; + m.m[9] *= v.z; + m.m[10] *= v.z; + m.m[11] *= v.z; + return m; +} + +mat4_t mat4_mult_mat3x4(mat4_t a, mat4_t b) { + float a00 = a.m[0]; + float a01 = a.m[1]; + float a02 = a.m[2]; + float a03 = a.m[3]; + float a10 = a.m[4]; + float a11 = a.m[5]; + float a12 = a.m[6]; + float a13 = a.m[7]; + float a20 = a.m[8]; + float a21 = a.m[9]; + float a22 = a.m[10]; + float a23 = a.m[11]; + float a30 = a.m[12]; + float a31 = a.m[13]; + float a32 = a.m[14]; + float a33 = a.m[15]; + + float b0 = b.m[0]; + float b1 = b.m[4]; + float b2 = b.m[8]; + float b3 = b.m[12]; + + mat4_t m; + m.m[0] = a00 * b0 + a01 * b1 + a02 * b2 + a03 * b3; + m.m[4] = a10 * b0 + a11 * b1 + a12 * b2 + a13 * b3; + m.m[8] = a20 * b0 + a21 * b1 + a22 * b2 + a23 * b3; + m.m[12] = a30 * b0 + a31 * b1 + a32 * b2 + a33 * b3; + + b0 = b.m[1]; + b1 = b.m[5]; + b2 = b.m[9]; + b3 = b.m[13]; + m.m[1] = a00 * b0 + a01 * b1 + a02 * b2 + a03 * b3; + m.m[5] = a10 * b0 + a11 * b1 + a12 * b2 + a13 * b3; + m.m[9] = a20 * b0 + a21 * b1 + a22 * b2 + a23 * b3; + m.m[13] = a30 * b0 + a31 * b1 + a32 * b2 + a33 * b3; + + b0 = b.m[2]; + b1 = b.m[6]; + b2 = b.m[10]; + b3 = b.m[14]; + m.m[2] = a00 * b0 + a01 * b1 + a02 * b2 + a03 * b3; + m.m[6] = a10 * b0 + a11 * b1 + a12 * b2 + a13 * b3; + m.m[10] = a20 * b0 + a21 * b1 + a22 * b2 + a23 * b3; + m.m[14] = a30 * b0 + a31 * b1 + a32 * b2 + a33 * b3; + + m.m[3] = 0; + m.m[7] = 0; + m.m[11] = 0; + m.m[15] = 1; + return m; +} + +mat4_t mat4_mult_mat(mat4_t a, mat4_t b) { + float a00 = a.m[0]; + float a01 = a.m[1]; + float a02 = a.m[2]; + float a03 = a.m[3]; + float a10 = a.m[4]; + float a11 = a.m[5]; + float a12 = a.m[6]; + float a13 = a.m[7]; + float a20 = a.m[8]; + float a21 = a.m[9]; + float a22 = a.m[10]; + float a23 = a.m[11]; + float a30 = a.m[12]; + float a31 = a.m[13]; + float a32 = a.m[14]; + float a33 = a.m[15]; + + float b0 = b.m[0]; + float b1 = b.m[4]; + float b2 = b.m[8]; + float b3 = b.m[12]; + a.m[0] = a00 * b0 + a01 * b1 + a02 * b2 + a03 * b3; + a.m[4] = a10 * b0 + a11 * b1 + a12 * b2 + a13 * b3; + a.m[8] = a20 * b0 + a21 * b1 + a22 * b2 + a23 * b3; + a.m[12] = a30 * b0 + a31 * b1 + a32 * b2 + a33 * b3; + + b0 = b.m[1]; + b1 = b.m[5]; + b2 = b.m[9]; + b3 = b.m[13]; + a.m[1] = a00 * b0 + a01 * b1 + a02 * b2 + a03 * b3; + a.m[5] = a10 * b0 + a11 * b1 + a12 * b2 + a13 * b3; + a.m[9] = a20 * b0 + a21 * b1 + a22 * b2 + a23 * b3; + a.m[13] = a30 * b0 + a31 * b1 + a32 * b2 + a33 * b3; + + b0 = b.m[2]; + b1 = b.m[6]; + b2 = b.m[10]; + b3 = b.m[14]; + a.m[2] = a00 * b0 + a01 * b1 + a02 * b2 + a03 * b3; + a.m[6] = a10 * b0 + a11 * b1 + a12 * b2 + a13 * b3; + a.m[10] = a20 * b0 + a21 * b1 + a22 * b2 + a23 * b3; + a.m[14] = a30 * b0 + a31 * b1 + a32 * b2 + a33 * b3; + + b0 = b.m[3]; + b1 = b.m[7]; + b2 = b.m[11]; + b3 = b.m[15]; + a.m[3] = a00 * b0 + a01 * b1 + a02 * b2 + a03 * b3; + a.m[7] = a10 * b0 + a11 * b1 + a12 * b2 + a13 * b3; + a.m[11] = a20 * b0 + a21 * b1 + a22 * b2 + a23 * b3; + a.m[15] = a30 * b0 + a31 * b1 + a32 * b2 + a33 * b3; + + return a; +} + +mat4_t mat4_inv(mat4_t a) { + float a00 = a.m[0]; + float a01 = a.m[1]; + float a02 = a.m[2]; + float a03 = a.m[3]; + float a10 = a.m[4]; + float a11 = a.m[5]; + float a12 = a.m[6]; + float a13 = a.m[7]; + float a20 = a.m[8]; + float a21 = a.m[9]; + float a22 = a.m[10]; + float a23 = a.m[11]; + float a30 = a.m[12]; + float a31 = a.m[13]; + float a32 = a.m[14]; + float a33 = a.m[15]; + float b00 = a00 * a11 - a01 * a10; + float b01 = a00 * a12 - a02 * a10; + float b02 = a00 * a13 - a03 * a10; + float b03 = a01 * a12 - a02 * a11; + float b04 = a01 * a13 - a03 * a11; + float b05 = a02 * a13 - a03 * a12; + float b06 = a20 * a31 - a21 * a30; + float b07 = a20 * a32 - a22 * a30; + float b08 = a20 * a33 - a23 * a30; + float b09 = a21 * a32 - a22 * a31; + float b10 = a21 * a33 - a23 * a31; + float b11 = a22 * a33 - a23 * a32; + + float det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + if (det == 0.0) { + return mat4_identity(); + } + det = 1.0 / det; + + mat4_t m; + m.m[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; + m.m[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det; + m.m[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det; + m.m[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det; + m.m[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det; + m.m[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det; + m.m[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det; + m.m[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det; + m.m[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det; + m.m[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det; + m.m[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det; + m.m[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det; + m.m[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det; + m.m[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det; + m.m[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det; + m.m[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det; + + return m; +} + +mat4_t mat4_transpose(mat4_t m) { + float f = m.m[1]; + m.m[1] = m.m[4]; + m.m[4] = f; + + f = m.m[2]; + m.m[2] = m.m[8]; + m.m[8] = f; + + f = m.m[3]; + m.m[3] = m.m[12]; + m.m[12] = f; + + f = m.m[6]; + m.m[6] = m.m[9]; + m.m[9] = f; + + f = m.m[7]; + m.m[7] = m.m[13]; + m.m[13] = f; + + f = m.m[11]; + m.m[11] = m.m[14]; + m.m[14] = f; + + return m; +} + +mat4_t mat4_transpose3x3(mat4_t m) { + float f = m.m[1]; + m.m[1] = m.m[4]; + m.m[4] = f; + + f = m.m[2]; + m.m[2] = m.m[8]; + m.m[8] = f; + + f = m.m[6]; + m.m[6] = m.m[9]; + m.m[9] = f; + + return m; +} + +mat4_t mat4_clone(mat4_t m) { + return mat4_create( + m.m[0], m.m[4], m.m[8], m.m[12], + m.m[1], m.m[5], m.m[9], m.m[13], + m.m[2], m.m[6], m.m[10], m.m[14], + m.m[3], m.m[7], m.m[11], m.m[15] + ); +} + +vec4_t mat4_get_loc(mat4_t m) { + return vec4_create(m.m[12], m.m[13], m.m[14], m.m[15]); +} + +vec4_t mat4_get_scale(mat4_t m) { + return vec4_create( + sqrtf(m.m[0] * m.m[0] + m.m[4] * m.m[4] + m.m[8] * m.m[8]), + sqrtf(m.m[1] * m.m[1] + m.m[5] * m.m[5] + m.m[9] * m.m[9]), + sqrtf(m.m[2] * m.m[2] + m.m[6] * m.m[6] + m.m[10] * m.m[10]), + 1.0 + ); +} + +mat4_t mat4_mult(mat4_t m, float s) { + m.m[0] *= s; + m.m[4] *= s; + m.m[8] *= s; + m.m[12] *= s; + m.m[1] *= s; + m.m[5] *= s; + m.m[9] *= s; + m.m[13] *= s; + m.m[2] *= s; + m.m[6] *= s; + m.m[10] *= s; + m.m[14] *= s; + m.m[3] *= s; + m.m[7] *= s; + m.m[11] *= s; + m.m[15] *= s; + return m; +} + +mat4_t mat4_to_rot(mat4_t m) { + float scale = 1.0 / vec4_len(vec4_create(m.m[0], m.m[1], m.m[2], 1.0)); + m.m[0] = m.m[0] * scale; + m.m[1] = m.m[1] * scale; + m.m[2] = m.m[2] * scale; + scale = 1.0 / vec4_len(vec4_create(m.m[4], m.m[5], m.m[6], 1.0)); + m.m[4] = m.m[4] * scale; + m.m[5] = m.m[5] * scale; + m.m[6] = m.m[6] * scale; + scale = 1.0 / vec4_len(vec4_create(m.m[8], m.m[9], m.m[10], 1.0)); + m.m[8] = m.m[8] * scale; + m.m[9] = m.m[9] * scale; + m.m[10] = m.m[10] * scale; + m.m[3] = 0.0; + m.m[7] = 0.0; + m.m[11] = 0.0; + m.m[12] = 0.0; + m.m[13] = 0.0; + m.m[14] = 0.0; + m.m[15] = 1.0; + return m; +} + +vec4_t mat4_right(mat4_t m) { + return vec4_norm(vec4_create(m.m[0], m.m[1], m.m[2], 1.0)); +} + +vec4_t mat4_look(mat4_t m) { + return vec4_norm(vec4_create(m.m[4], m.m[5], m.m[6], 1.0)); +} + +vec4_t mat4_up(mat4_t m) { + return vec4_norm(vec4_create(m.m[8], m.m[9], m.m[10], 1.0)); +} + +f32_array_t *mat4_to_f32_array(mat4_t m) { + f32_array_t *a = f32_array_create(16); + a->buffer[0] = m.m[0]; + a->buffer[1] = m.m[4]; + a->buffer[2] = m.m[8]; + a->buffer[3] = m.m[12]; + a->buffer[4] = m.m[1]; + a->buffer[5] = m.m[5]; + a->buffer[6] = m.m[9]; + a->buffer[7] = m.m[13]; + a->buffer[8] = m.m[2]; + a->buffer[9] = m.m[6]; + a->buffer[10] = m.m[10]; + a->buffer[11] = m.m[14]; + a->buffer[12] = m.m[3]; + a->buffer[13] = m.m[7]; + a->buffer[14] = m.m[11]; + a->buffer[15] = m.m[15]; + return a; +} + +float mat4_cofactor(float m0, float m1, float m2, + float m3, float m4, float m5, + float m6, float m7, float m8) { + return m0 * (m4 * m8 - m5 * m7) - m1 * (m3 * m8 - m5 * m6) + m2 * (m3 * m7 - m4 * m6); +} + +float mat4_determinant(mat4_t m) { + float c00 = mat4_cofactor(m.m[5], m.m[9], m.m[13], m.m[6], m.m[10], m.m[14], m.m[7], m.m[11], m.m[15]); + float c01 = mat4_cofactor(m.m[4], m.m[8], m.m[12], m.m[6], m.m[10], m.m[14], m.m[7], m.m[11], m.m[15]); + float c02 = mat4_cofactor(m.m[4], m.m[8], m.m[12], m.m[5], m.m[9], m.m[13], m.m[7], m.m[11], m.m[15]); + float c03 = mat4_cofactor(m.m[4], m.m[8], m.m[12], m.m[5], m.m[9], m.m[13], m.m[6], m.m[10], m.m[14]); + return m.m[0] * c00 - m.m[1] * c01 + m.m[2] * c02 - m.m[3] * c03; +} + +mat4_t mat4_nan() { + mat4_t m; + m.m[0] = NAN; + return m; +} + +bool mat4_isnan(mat4_t m) { + return isnan(m.m[0]); +} diff --git a/armorcore/sources/iron_mat4.h b/armorcore/sources/iron_mat4.h new file mode 100644 index 000000000..1499b7c0b --- /dev/null +++ b/armorcore/sources/iron_mat4.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include "iron_vec4.h" +#include "iron_quat.h" +#include "iron_array.h" + +typedef struct mat4_decomposed { + vec4_t loc; + quat_t rot; + vec4_t scl; +} mat4_decomposed_t; + +mat4_t mat4_create(float _00, float _10, float _20, float _30, + float _01, float _11, float _21, float _31, + float _02, float _12, float _22, float _32, + float _03, float _13, float _23, float _33); +mat4_t mat4_identity(); +mat4_t mat4_from_f32_array(f32_array_t *a, int offset); +mat4_t mat4_persp(float fov_y, float aspect, float zn, float zf); +mat4_t mat4_ortho(float left, float right, float bottom, float top, float znear, float zfar); +mat4_t mat4_rot_z(float alpha); +mat4_t mat4_compose(vec4_t loc, quat_t rot, vec4_t scl); +mat4_decomposed_t *mat4_decompose(mat4_t m); +mat4_t mat4_set_loc(mat4_t m, vec4_t v); +mat4_t mat4_from_quat(quat_t q); +mat4_t mat4_init_translate(float x, float y, float z); +mat4_t mat4_translate(mat4_t m, float x, float y, float z); +mat4_t mat4_scale(mat4_t m, vec4_t v); +mat4_t mat4_mult_mat3x4(mat4_t a, mat4_t b); +mat4_t mat4_mult_mat(mat4_t a, mat4_t b); +mat4_t mat4_inv(mat4_t a); +mat4_t mat4_transpose(mat4_t m); +mat4_t mat4_transpose3x3(mat4_t m); +mat4_t mat4_clone(mat4_t m); +vec4_t mat4_get_loc(mat4_t m); +vec4_t mat4_get_scale(mat4_t m); +mat4_t mat4_mult(mat4_t m, float s); +mat4_t mat4_to_rot(mat4_t m); +vec4_t mat4_right(mat4_t m); +vec4_t mat4_look(mat4_t m); +vec4_t mat4_up(mat4_t m); +f32_array_t *mat4_to_f32_array(mat4_t m); +float mat4_cofactor(float m0, float m1, float m2, float m3, float m4, float m5, float m6, float m7, float m8); +float mat4_determinant(mat4_t m); +mat4_t mat4_nan(); +bool mat4_isnan(mat4_t m); + +#define mat4nan (mat4_nan()) diff --git a/armorcore/sources/iron_obj.c b/armorcore/sources/iron_obj.c new file mode 100644 index 000000000..152e9f918 --- /dev/null +++ b/armorcore/sources/iron_obj.c @@ -0,0 +1,642 @@ +#include "iron_obj.h" + +#include +#include +#include +#include "iron_array.h" +#include "iron_vec4.h" +#include "iron_string.h" +#include "iron_gc.h" + +static raw_mesh_t *part = NULL; +static f32_array_t pos_temp; +static f32_array_t uv_temp; +static f32_array_t nor_temp; +static uint32_t va[512]; +static uint32_t ua[512]; +static uint32_t na[512]; +static int vi = 0; +static int ui = 0; +static int ni = 0; +static uint8_t buf[128]; +static char str[256]; + +static int vind_off = 0; +static int tind_off = 0; +static int nind_off = 0; +static uint8_t *bytes = NULL; +static size_t bytes_length = 0; +static f32_array_t *pos_first; +static f32_array_t *uv_first; +static f32_array_t *nor_first; + +static int read_int() { + int bi = 0; + while (true) { // Read into buffer + char c = bytes[part->pos]; + if (c == '/' || c == '\n' || c == '\r' || c == ' ') { + break; + } + part->pos++; + buf[bi++] = c; + } + int res = 0; // Parse buffer into int + int dec = 1; + int off = buf[0] == '-' ? 1 : 0; + int len = bi - 1; + for (int i = 0; i < bi - off; ++i) { + res += (buf[len - i] - 48) * dec; + dec *= 10; + } + if (off > 0) { + res *= -1; + } + return res; +} + +static void read_face_fast() { + while (true) { + va[vi++] = read_int() - 1; + part->pos++; // '/' + ua[ui++] = read_int() - 1; + part->pos++; // '/' + na[ni++] = read_int() - 1; + if (bytes[part->pos] == '\n' || bytes[part->pos] == '\r') { + break; + } + part->pos++; // ' ' + // Some exporters put space at the end of "f" line + if (vi >= 3 && (bytes[part->pos] == '\n' || bytes[part->pos] == '\r')) { + break; + } + } +} + +static void read_face() { + while (true) { + va[vi++] = read_int() - 1; + if (uv_temp.length > 0 || nor_temp.length > 0) { + part->pos++; // "/" + + if (uv_temp.length > 0) { + ua[ui++] = read_int() - 1; + } + if (nor_temp.length > 0) { + part->pos++; // "/" + na[ni++] = read_int() - 1; + } + } + // Some exporters put "//" even when normal and uv data are not present (f 1//) + else if (uv_temp.length == 0 && nor_temp.length == 0 && bytes[part->pos] == '/') { + part->pos += 2; + } + + if (bytes[part->pos] == '\n' || bytes[part->pos] == '\r') { + break; + } + part->pos++; // " " + // Some exporters put space at the end of "f" line + if (vi >= 3 && (bytes[part->pos] == '\n' || bytes[part->pos] == '\r')) { + break; + } + } +} + +static float read_float() { + int bi = 0; + while (true) { // Read into buffer + char c = bytes[part->pos]; + if (c == ' ' || c == '\n' || c == '\r') { + break; + } + if (c == 'E' || c == 'e') { + part->pos++; + int first = buf[0] == '-' ? -(buf[1] - 48) : buf[0] - 48; + int exp = read_int(); + int dec = 1; + int loop = exp > 0 ? exp : -exp; + for (int i = 0; i < loop; ++i) { + dec *= 10; + } + return exp > 0 ? (float)first * dec : (float)first / dec; + } + part->pos++; + buf[bi++] = c; + } + float res = 0.0; // Parse buffer into float + int64_t dot = 1; + int64_t dec = 1; + int off = buf[0] == '-' ? 1 : 0; + int len = bi - 1; + for (int i = 0; i < bi - off; ++i) { + char c = buf[len - i]; + if (c == '.') { + dot = dec; + continue; + } + res += (c - 48) * dec; + dec *= 10; + } + if (off > 0) { + res /= -dot; + } + else { + res /= dot; + } + return res; +} + +static char *read_string() { + size_t begin = part->pos; + while (true) { + char c = bytes[part->pos]; + if (c == '\n' || c == '\r' || c == ' ') { + break; + } + part->pos++; + } + for (int i = 0; i < part->pos - begin; ++i) { + str[i] = bytes[begin + i]; + } + str[part->pos - begin] = '\0'; + return str; +} + +static void next_line() { + while (true) { + char c = bytes[part->pos++]; + if (c == '\n' || part->pos >= bytes_length) { + break; // \n, \r\n + } + } +} + +static int get_tile(int i1, int i2, int i3, i32_array_t *uv_indices, int tiles_u) { + float u1 = uv_temp.buffer[uv_indices->buffer[i1] * 2 ]; + float v1 = uv_temp.buffer[uv_indices->buffer[i1] * 2 + 1]; + float u2 = uv_temp.buffer[uv_indices->buffer[i2] * 2 ]; + float v2 = uv_temp.buffer[uv_indices->buffer[i2] * 2 + 1]; + float u3 = uv_temp.buffer[uv_indices->buffer[i3] * 2 ]; + float v3 = uv_temp.buffer[uv_indices->buffer[i3] * 2 + 1]; + int tile_u = (int)((u1 + u2 + u3) / 3); + int tile_v = (int)((v1 + v2 + v3) / 3); + return tile_u + tile_v * tiles_u; +} + +static bool pnpoly(float v0x, float v0y, float v1x, float v1y, float v2x, float v2y, float px, float py) { + // https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html + bool c = false; + if (((v0y > py) != (v2y > py)) && (px < (v2x - v0x) * (py - v0y) / (v2y - v0y) + v0x)) { + c = !c; + } + if (((v1y > py) != (v0y > py)) && (px < (v0x - v1x) * (py - v1y) / (v0y - v1y) + v1x)) { + c = !c; + } + if (((v2y > py) != (v1y > py)) && (px < (v1x - v2x) * (py - v2y) / (v1y - v2y) + v2x)) { + c = !c; + } + return c; +} + +kinc_vector4_t calc_normal(kinc_vector4_t a, kinc_vector4_t b, kinc_vector4_t c) { + kinc_vector4_t cb = vec4_sub(c, b); + kinc_vector4_t ab = vec4_sub(a, b); + cb = vec4_cross(cb, ab); + cb = vec4_norm(cb); + return cb; +} + +// 'o' for object split, 'g' for groups, 'u'semtl for materials +raw_mesh_t *obj_parse(buffer_t *file_bytes, char split_code, uint64_t start_pos, bool udim) { + bytes = file_bytes->buffer; + bytes_length = file_bytes->length; + + part = gc_alloc(sizeof(raw_mesh_t)); + part->scale_pos = 1.0; + part->scale_tex = 1.0; + part->pos = start_pos; + part->udims_u = 1; + part->udims_v = 1; + part->name = string_copy(str); + + i32_array_t pos_indices = {0}; + i32_array_t uv_indices = {0}; + i32_array_t nor_indices = {0}; + + bool reading_faces = false; + bool reading_object = false; + bool full_attrib = false; + + if (start_pos == 0) { + vind_off = tind_off = nind_off = 0; + } + + if (split_code == 'u' && start_pos > 0) { + pos_temp = *pos_first; + nor_temp = *nor_first; + uv_temp = *uv_first; + } + else { + memset(&pos_temp, 0, sizeof(pos_temp)); + memset(&uv_temp, 0, sizeof(uv_temp)); + memset(&nor_temp, 0, sizeof(nor_temp)); + } + + while (true) { + if (part->pos >= bytes_length) { + break; + } + + char c0 = bytes[part->pos++]; + if (reading_object && reading_faces && (c0 == 'v' || c0 == split_code)) { + part->pos--; + part->has_next = true; + break; + } + + if (c0 == 'v') { + char c1 = bytes[part->pos++]; + if (c1 == ' ') { + if (bytes[part->pos] == ' ') part->pos++; // Some exporters put additional space directly after "v" + f32_array_push(&pos_temp, read_float()); + part->pos++; // Space + f32_array_push(&pos_temp, read_float()); + part->pos++; // Space + f32_array_push(&pos_temp, read_float()); + } + else if (c1 == 't') { + part->pos++; // Space + f32_array_push(&uv_temp, read_float()); + part->pos++; // Space + f32_array_push(&uv_temp, read_float()); + if (nor_temp.length > 0) { + full_attrib = true; + } + } + else if (c1 == 'n') { + part->pos++; // Space + f32_array_push(&nor_temp, read_float()); + part->pos++; // Space + f32_array_push(&nor_temp, read_float()); + part->pos++; // Space + f32_array_push(&nor_temp, read_float()); + if (uv_temp.length > 0) { + full_attrib = true; + } + } + } + else if (c0 == 'f') { + part->pos++; // Space + if (bytes[part->pos] == ' ') { + part->pos++; // Some exporters put additional space directly after "f" + } + reading_faces = true; + vi = ui = ni = 0; + full_attrib ? read_face_fast() : read_face(); + + if (vi <= 4) { // Convex, fan triangulation + i32_array_push(&pos_indices, va[0]); + i32_array_push(&pos_indices, va[1]); + i32_array_push(&pos_indices, va[2]); + for (int i = 3; i < vi; ++i) { + i32_array_push(&pos_indices, va[0]); + i32_array_push(&pos_indices, va[i - 1]); + i32_array_push(&pos_indices, va[i]); + } + if (uv_temp.length > 0) { + i32_array_push(&uv_indices, ua[0]); + i32_array_push(&uv_indices, ua[1]); + i32_array_push(&uv_indices, ua[2]); + for (int i = 3; i < ui; ++i) { + i32_array_push(&uv_indices, ua[0]); + i32_array_push(&uv_indices, ua[i - 1]); + i32_array_push(&uv_indices, ua[i]); + } + } + if (nor_temp.length > 0) { + i32_array_push(&nor_indices, na[0]); + i32_array_push(&nor_indices, na[1]); + i32_array_push(&nor_indices, na[2]); + for (int i = 3; i < ni; ++i) { + i32_array_push(&nor_indices, na[0]); + i32_array_push(&nor_indices, na[i - 1]); + i32_array_push(&nor_indices, na[i]); + } + } + } + else { // Convex or concave, ear clipping + int _vind_off = split_code == 'u' ? 0 : vind_off; + int _nind_off = split_code == 'u' ? 0 : nind_off; + float nx = 0.0; + float ny = 0.0; + float nz = 0.0; + if (nor_temp.length > 0) { + nx = nor_temp.buffer[(na[0] - _nind_off) * 3 ]; + ny = nor_temp.buffer[(na[0] - _nind_off) * 3 + 1]; + nz = nor_temp.buffer[(na[0] - _nind_off) * 3 + 2]; + } + else { + kinc_vector4_t n = calc_normal( + vec4_create(pos_temp.buffer[(va[0] - _vind_off) * 3], pos_temp.buffer[(va[0] - _vind_off) * 3 + 1], pos_temp.buffer[(va[0] - _vind_off) * 3 + 2], 1.0f), + vec4_create(pos_temp.buffer[(va[1] - _vind_off) * 3], pos_temp.buffer[(va[1] - _vind_off) * 3 + 1], pos_temp.buffer[(va[1] - _vind_off) * 3 + 2], 1.0f), + vec4_create(pos_temp.buffer[(va[2] - _vind_off) * 3], pos_temp.buffer[(va[2] - _vind_off) * 3 + 1], pos_temp.buffer[(va[2] - _vind_off) * 3 + 2], 1.0f) + ); + nx = n.x; + ny = n.y; + nz = n.z; + } + float nxabs = (float)fabs(nx); + float nyabs = (float)fabs(ny); + float nzabs = (float)fabs(nz); + bool flip = nx + ny + nz > 0; + int axis = nxabs > nyabs && nxabs > nzabs ? 0 : nyabs > nxabs && nyabs > nzabs ? 1 : 2; + int axis0 = axis == 0 ? (flip ? 2 : 1) : axis == 1 ? (flip ? 0 : 2) : (flip ? 1 : 0); + int axis1 = axis == 0 ? (flip ? 1 : 2) : axis == 1 ? (flip ? 2 : 0) : (flip ? 0 : 1); + + int loops = 0; + int i = -1; + while (vi > 3 && loops++ < vi) { + i = (i + 1) % vi; + int i1 = (i + 1) % vi; + int i2 = (i + 2) % vi; + int vi0 = (va[i ] - _vind_off) * 3; + int vi1 = (va[i1] - _vind_off) * 3; + int vi2 = (va[i2] - _vind_off) * 3; + float v0x = pos_temp.buffer[vi0 + axis0]; + float v0y = pos_temp.buffer[vi0 + axis1]; + float v1x = pos_temp.buffer[vi1 + axis0]; + float v1y = pos_temp.buffer[vi1 + axis1]; + float v2x = pos_temp.buffer[vi2 + axis0]; + float v2y = pos_temp.buffer[vi2 + axis1]; + + float e0x = v0x - v1x; // Not an interior vertex + float e0y = v0y - v1y; + float e1x = v2x - v1x; + float e1y = v2y - v1y; + float cross = e0x * e1y - e0y * e1x; + if (cross <= 0) { + continue; + } + + bool overlap = false; // Other vertex found inside this triangle + for (int j = 0; j < vi - 3; ++j) { + int j0 = (va[(i + 3 + j) % vi] - _vind_off) * 3; + float px = pos_temp.buffer[j0 + axis0]; + float py = pos_temp.buffer[j0 + axis1]; + if (pnpoly(v0x, v0y, v1x, v1y, v2x, v2y, px, py)) { + overlap = true; + break; + } + } + if (overlap) { + continue; + } + + i32_array_push(&pos_indices, va[i ]); // Found ear + i32_array_push(&pos_indices, va[i1]); + i32_array_push(&pos_indices, va[i2]); + if (uv_temp.length > 0) { + i32_array_push(&uv_indices, ua[i ]); + i32_array_push(&uv_indices, ua[i1]); + i32_array_push(&uv_indices, ua[i2]); + } + if (nor_temp.length > 0) { + i32_array_push(&nor_indices, na[i ]); + i32_array_push(&nor_indices, na[i1]); + i32_array_push(&nor_indices, na[i2]); + } + + for (int j = ((i + 1) % vi); j < vi - 1; ++j) { // Consume vertex + va[j] = va[j + 1]; + ua[j] = ua[j + 1]; + na[j] = na[j + 1]; + } + vi--; + i--; + loops = 0; + } + i32_array_push(&pos_indices, va[0]); // Last one + i32_array_push(&pos_indices, va[1]); + i32_array_push(&pos_indices, va[2]); + if (uv_temp.length > 0) { + i32_array_push(&uv_indices, ua[0]); + i32_array_push(&uv_indices, ua[1]); + i32_array_push(&uv_indices, ua[2]); + } + if (nor_temp.length > 0) { + i32_array_push(&nor_indices, na[0]); + i32_array_push(&nor_indices, na[1]); + i32_array_push(&nor_indices, na[2]); + } + } + } + else if (c0 == split_code) { + if (split_code == 'u') { + part->pos += 5; // "u"semtl + } + part->pos++; // Space + if (!udim) { + reading_object = true; + } + part->name = string_copy(read_string()); + } + next_line(); + } + + if (start_pos > 0) { + if (split_code != 'u') { + for (int i = 0; i < pos_indices.length; ++i) { + pos_indices.buffer[i] -= vind_off; + } + for (int i = 0; i < uv_indices.length; ++i) { + uv_indices.buffer[i] -= tind_off; + } + for (int i = 0; i < nor_indices.length; ++i) { + nor_indices.buffer[i] -= nind_off; + } + } + } + else { + if (split_code == 'u') { + pos_first = &pos_temp; + nor_first = &nor_temp; + uv_first = &uv_temp; + } + } + vind_off += (int)(pos_temp.length / 3); // Assumes separate vertex data per object + tind_off += (int)(uv_temp.length / 2); + nind_off += (int)(nor_temp.length / 3); + + // Pack positions to (-1, 1) range + part->scale_pos = 0.0; + for (int i = 0; i < pos_temp.length; ++i) { + float f = (float)fabs(pos_temp.buffer[i]); + if (part->scale_pos < f) { + part->scale_pos = f; + } + } + float inv = 32767 * (1 / part->scale_pos); + + part->posa = calloc(sizeof(i16_array_t), 1); + part->posa->length = part->posa->capacity = pos_indices.length * 4; + part->posa->buffer = malloc(part->posa->capacity * sizeof(int16_t)); + + part->inda = calloc(sizeof(u32_array_t), 1); + part->inda->length = part->inda->capacity = pos_indices.length; + part->inda->buffer = malloc(part->inda->capacity * sizeof(uint32_t)); + + part->vertex_count = pos_indices.length; + part->index_count = pos_indices.length; + int inda_length = pos_indices.length; + for (int i = 0; i < pos_indices.length; ++i) { + part->posa->buffer[i * 4 ] = (int)( pos_temp.buffer[pos_indices.buffer[i] * 3 ] * inv); + part->posa->buffer[i * 4 + 1] = (int)(-pos_temp.buffer[pos_indices.buffer[i] * 3 + 2] * inv); + part->posa->buffer[i * 4 + 2] = (int)( pos_temp.buffer[pos_indices.buffer[i] * 3 + 1] * inv); + part->inda->buffer[i] = i; + } + + if (nor_indices.length > 0) { + part->nora = calloc(sizeof(i16_array_t), 1); + part->nora->length = part->nora->capacity = nor_indices.length * 2; + part->nora->buffer = malloc(part->nora->capacity * sizeof(int16_t)); + + for (int i = 0; i < pos_indices.length; ++i) { + part->nora->buffer[i * 2 ] = (int)( nor_temp.buffer[nor_indices.buffer[i] * 3 ] * 32767); + part->nora->buffer[i * 2 + 1] = (int)(-nor_temp.buffer[nor_indices.buffer[i] * 3 + 2] * 32767); + part->posa->buffer[i * 4 + 3] = (int)( nor_temp.buffer[nor_indices.buffer[i] * 3 + 1] * 32767); + } + } + else { + // Calc normals + part->nora = calloc(sizeof(i16_array_t), 1); + part->nora->length = part->nora->capacity = inda_length * 2; + part->nora->buffer = malloc(part->nora->capacity * sizeof(int16_t)); + + for (int i = 0; i < (int)(inda_length / 3); ++i) { + int i1 = part->inda->buffer[i * 3 ]; + int i2 = part->inda->buffer[i * 3 + 1]; + int i3 = part->inda->buffer[i * 3 + 2]; + kinc_vector4_t n = calc_normal( + vec4_create(part->posa->buffer[i1 * 4], part->posa->buffer[i1 * 4 + 1], part->posa->buffer[i1 * 4 + 2], 1.0), + vec4_create(part->posa->buffer[i2 * 4], part->posa->buffer[i2 * 4 + 1], part->posa->buffer[i2 * 4 + 2], 1.0), + vec4_create(part->posa->buffer[i3 * 4], part->posa->buffer[i3 * 4 + 1], part->posa->buffer[i3 * 4 + 2], 1.0) + ); + part->nora->buffer[i1 * 2 ] = (int)(n.x * 32767); + part->nora->buffer[i1 * 2 + 1] = (int)(n.y * 32767); + part->posa->buffer[i1 * 4 + 3] = (int)(n.z * 32767); + part->nora->buffer[i2 * 2 ] = (int)(n.x * 32767); + part->nora->buffer[i2 * 2 + 1] = (int)(n.y * 32767); + part->posa->buffer[i2 * 4 + 3] = (int)(n.z * 32767); + part->nora->buffer[i3 * 2 ] = (int)(n.x * 32767); + part->nora->buffer[i3 * 2 + 1] = (int)(n.y * 32767); + part->posa->buffer[i3 * 4 + 3] = (int)(n.z * 32767); + } + } + + if (uv_indices.length > 0) { + if (udim) { + // Find number of tiles + int tiles_u = 1; + int tiles_v = 1; + for (int i = 0; i < (int)(uv_temp.length / 2); ++i) { + while (uv_temp.buffer[i * 2 ] > tiles_u) tiles_u++; + while (uv_temp.buffer[i * 2 + 1] > tiles_v) tiles_v++; + } + + // Amount of indices pre tile + uint32_t *num = (uint32_t *)malloc(tiles_u * tiles_v * sizeof(uint32_t)); + memset(num, 0, tiles_u * tiles_v * sizeof(uint32_t)); + for (int i = 0; i < (int)(inda_length / 3); ++i) { + int tile = get_tile(part->inda->buffer[i * 3], part->inda->buffer[i * 3 + 1], part->inda->buffer[i * 3 + 2], &uv_indices, tiles_u); + num[tile] += 3; + } + + // Split indices per tile + part->udims = any_array_create(tiles_u * tiles_v); + part->udims_u = tiles_u; + part->udims_v = tiles_v; + for (int i = 0; i < tiles_u * tiles_v; ++i) { + part->udims->buffer[i] = u32_array_create(num[i]); + num[i] = 0; + } + + for (int i = 0; i < (int)(inda_length / 3); ++i) { + int i1 = part->inda->buffer[i * 3 ]; + int i2 = part->inda->buffer[i * 3 + 1]; + int i3 = part->inda->buffer[i * 3 + 2]; + int tile = get_tile(i1, i2, i3, &uv_indices, tiles_u); + u32_array_t *a = part->udims->buffer[tile]; + a->buffer[num[tile]++] = i1; + a->buffer[num[tile]++] = i2; + a->buffer[num[tile]++] = i3; + } + + // Normalize uvs to 0-1 range + int16_t *uvtiles = (int16_t *)malloc(uv_temp.length * sizeof(int16_t)); + for (int i = 0; i < (int)(inda_length / 3); ++i) { // TODO: merge loops + int i1 = part->inda->buffer[i * 3 ]; + int i2 = part->inda->buffer[i * 3 + 1]; + int i3 = part->inda->buffer[i * 3 + 2]; + int tile = get_tile(i1, i2, i3, &uv_indices, tiles_u); + int tile_u = tile % tiles_u; + int tile_v = (int)(tile / tiles_u); + uvtiles[uv_indices.buffer[i1] * 2 ] = tile_u; + uvtiles[uv_indices.buffer[i1] * 2 + 1] = tile_v; + uvtiles[uv_indices.buffer[i2] * 2 ] = tile_u; + uvtiles[uv_indices.buffer[i2] * 2 + 1] = tile_v; + uvtiles[uv_indices.buffer[i3] * 2 ] = tile_u; + uvtiles[uv_indices.buffer[i3] * 2 + 1] = tile_v; + } + for (int i = 0; i < uv_temp.length; ++i) { + uv_temp.buffer[i] -= uvtiles[i]; + } + free(uvtiles); + free(num); + } + + part->texa = calloc(sizeof(i16_array_t), 1); + part->texa->length = part->texa->capacity = uv_indices.length * 2; + part->texa->buffer = malloc(part->texa->capacity * sizeof(int16_t)); + + for (int i = 0; i < uv_indices.length; ++i) { + float uvx = uv_temp.buffer[uv_indices.buffer[i] * 2]; + if (uvx > 1.0) { + uvx = uvx - (int)(uvx); + } + float uvy = uv_temp.buffer[uv_indices.buffer[i] * 2 + 1]; + if (uvy > 1.0) { + uvy = uvy - (int)(uvy); + } + part->texa->buffer[i * 2 ] = (int)( uvx * 32767); + part->texa->buffer[i * 2 + 1] = (int)((1.0 - uvy) * 32767); + } + } + bytes = NULL; + if (!part->has_next) { + pos_first = nor_first = uv_first = NULL; + array_free(&pos_temp); + array_free(&uv_temp); + array_free(&nor_temp); + } + + array_free(&pos_indices); + array_free(&uv_indices); + array_free(&nor_indices); + + return part; +} + +void obj_destroy(raw_mesh_t *part) { + // if (part->udims != NULL) { + // for (int i = 0; i < part->udims_u * part->udims_v; ++i) { + // free(part->udims[i]); + // } + // free(part->udims); + // } + + free(part->posa); + free(part->nora); + free(part->texa); + free(part->inda); + gc_free(part); +} diff --git a/armorcore/sources/iron_obj.h b/armorcore/sources/iron_obj.h new file mode 100644 index 000000000..3b70a21e1 --- /dev/null +++ b/armorcore/sources/iron_obj.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include "iron_armpack.h" + +typedef struct raw_mesh { + struct i16_array *posa; + struct i16_array *nora; + struct i16_array *texa; + struct i16_array *cola; + struct u32_array *inda; + int vertex_count; + int index_count; + float scale_pos; + float scale_tex; + char *name; + bool has_next; // File contains multiple objects + uint64_t pos; + struct any_array *udims; // u32_array_t[] - Indices split per udim tile + int udims_u; // Number of horizontal udim tiles + int udims_v; + void *vertex_arrays; // vertex_array_t[] + void *index_arrays; // index_array_t[] +} raw_mesh_t; + +raw_mesh_t *obj_parse(buffer_t *file_bytes, char split_code, uint64_t start_pos, bool udim); +void obj_destroy(raw_mesh_t *part); diff --git a/armorcore/sources/iron_quat.c b/armorcore/sources/iron_quat.c new file mode 100644 index 000000000..4f870cb50 --- /dev/null +++ b/armorcore/sources/iron_quat.c @@ -0,0 +1,175 @@ + +#include "iron_quat.h" +#include + +#define MATH_PI 3.14159265358979323846 + +quat_t quat_create(float x, float y, float z, float w) { + quat_t q; + q.x = x; + q.y = y; + q.z = z; + q.w = w; + return q; +} + +quat_t quat_from_axis_angle(vec4_t axis, float angle) { + float s = sinf(angle * 0.5); + quat_t q; + q.x = axis.x * s; + q.y = axis.y * s; + q.z = axis.z * s; + q.w = cosf(angle * 0.5); + return quat_norm(q); +} + +quat_t quat_from_mat(mat4_t m) { + m = mat4_to_rot(m); + return quat_from_rot_mat(m); +} + +quat_t quat_from_rot_mat(mat4_t m) { + // Assumes the upper 3x3 is a pure rotation matrix + float m11 = m.m[0]; + float m12 = m.m[4]; + float m13 = m.m[8]; + float m21 = m.m[1]; + float m22 = m.m[5]; + float m23 = m.m[9]; + float m31 = m.m[2]; + float m32 = m.m[6]; + float m33 = m.m[10]; + float tr = m11 + m22 + m33; + float s = 0.0; + quat_t q; + + if (tr > 0) { + s = 0.5 / sqrtf(tr + 1.0); + q.w = 0.25 / s; + q.x = (m32 - m23) * s; + q.y = (m13 - m31) * s; + q.z = (m21 - m12) * s; + } + else if (m11 > m22 && m11 > m33) { + s = 2.0 * sqrtf(1.0 + m11 - m22 - m33); + q.w = (m32 - m23) / s; + q.x = 0.25 * s; + q.y = (m12 + m21) / s; + q.z = (m13 + m31) / s; + } + else if (m22 > m33) { + s = 2.0 * sqrtf(1.0 + m22 - m11 - m33); + q.w = (m13 - m31) / s; + q.x = (m12 + m21) / s; + q.y = 0.25 * s; + q.z = (m23 + m32) / s; + } + else { + s = 2.0 * sqrtf(1.0 + m33 - m11 - m22); + q.w = (m21 - m12) / s; + q.x = (m13 + m31) / s; + q.y = (m23 + m32) / s; + q.z = 0.25 * s; + } + return q; +} + +quat_t quat_mult(quat_t a, quat_t b) { + quat_t q; + q.x = a.x * b.w + a.w * b.x + a.y * b.z - a.z * b.y; + q.y = a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x; + q.z = a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w; + q.w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z; + return q; +} + +quat_t quat_norm(quat_t q) { + float l = sqrtf(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w); + if (l == 0.0) { + q.x = 0; + q.y = 0; + q.z = 0; + q.w = 0; + } + else { + l = 1.0 / l; + q.x *= l; + q.y *= l; + q.z *= l; + q.w *= l; + } + return q; +} + +quat_t quat_clone(quat_t q) { + return q; +} + +vec4_t quat_get_euler(quat_t q) { + float a = -2 * (q.x * q.z - q.w * q.y); + float b = q.w * q.w + q.x * q.x - q.y * q.y - q.z * q.z; + float c = 2 * (q.x * q.y + q.w * q.z); + float d = -2 * (q.y * q.z - q.w * q.x); + float e = q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z; + return vec4_create(atan2f(d, e), atan2f(a, b), asinf(c), 1.0); +} + +quat_t quat_from_euler(float x, float y, float z) { + float f = x / 2; + float c1 = cosf(f); + float s1 = sinf(f); + f = y / 2; + float c2 = cosf(f); + float s2 = sinf(f); + f = z / 2; + float c3 = cosf(f); + float s3 = sinf(f); + // YZX + quat_t q; + q.x = s1 * c2 * c3 + c1 * s2 * s3; + q.y = c1 * s2 * c3 + s1 * c2 * s3; + q.z = c1 * c2 * s3 - s1 * s2 * c3; + q.w = c1 * c2 * c3 - s1 * s2 * s3; + return q; +} + +quat_t quat_lerp(quat_t from, quat_t to, float s) { + float dot = quat_dot(from, to); + if (dot < 0.0) { + from.x = -from.x; + from.y = -from.y; + from.z = -from.z; + from.w = -from.w; + } + from.x += (to.x - from.x) * s; + from.y += (to.y - from.y) * s; + from.z += (to.z - from.z) * s; + from.w += (to.w - from.w) * s; + return quat_norm(from); +} + +float quat_dot(quat_t a, quat_t b) { + return (a.x * b.x) + (a.y * b.y) + (a.z * b.z) + (a.w * b.w); +} + +quat_t quat_from_to(vec4_t v0, vec4_t v1) { + // Rotation formed by direction vectors + // v0 and v1 should be normalized first + float dot = vec4_dot(v0, v1); + if (dot < -0.999999) { + vec4_t a = vec4_cross(vec4_x_axis(), v0); + if (vec4_len(a) < 0.000001) { + a = vec4_cross(vec4_y_axis(), v0); + } + a = vec4_norm(a); + return quat_from_axis_angle(a, MATH_PI); + } + else if (dot > 0.999999) { + return quat_create(0, 0, 0, 1); + } + else { + vec4_t a = vec4_cross(v0, v1); + quat_t q = quat_create(a.x, a.y, a.z, 1.0 + dot); + return quat_norm(q); + } +} diff --git a/armorcore/sources/iron_quat.h b/armorcore/sources/iron_quat.h new file mode 100644 index 000000000..efa603027 --- /dev/null +++ b/armorcore/sources/iron_quat.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "iron_vec4.h" +#include "iron_mat4.h" + +quat_t quat_create(float x, float y, float z, float w); +quat_t quat_from_axis_angle(vec4_t axis, float angle); +quat_t quat_from_mat(mat4_t m); +quat_t quat_from_rot_mat(mat4_t m); +quat_t quat_mult(quat_t a, quat_t b); +quat_t quat_norm(quat_t q); +quat_t quat_clone(quat_t q); +vec4_t quat_get_euler(quat_t q); +quat_t quat_from_euler(float x, float y, float z); +quat_t quat_lerp(quat_t from, quat_t to, float s); +float quat_dot(quat_t a, quat_t b); +quat_t quat_from_to(vec4_t v0, vec4_t v1); diff --git a/armorcore/sources/iron_string.c b/armorcore/sources/iron_string.c new file mode 100644 index 000000000..71deb0fef --- /dev/null +++ b/armorcore/sources/iron_string.c @@ -0,0 +1,273 @@ +#include "iron_string.h" + +#include +#include +#include +#include + +void *gc_alloc(size_t size); +void *gc_leaf(void *ptr); + +char *string_alloc(int size) { + char *r = gc_alloc(size); + gc_leaf(r); + return r; +} + +char *string_join(char *a, char *b) { + char *r = string_alloc(strlen(a) + strlen(b) + 1); + strcpy(r, a); + strcat(r, b); + return r; +} + +char *string_copy(char *a) { + if (a == NULL) { + return NULL; + } + char *r = string_alloc(strlen(a) + 1); + strcpy(r, a); + return r; +} + +int string_length(char *str) { + return strlen(str); +} + +bool string_equals(char *a, char *b) { + if (a == NULL || b == NULL) { + return false; + } + return strcmp(a, b) == 0; +} + +char *i32_to_string(int32_t i) { + int l = snprintf(NULL, 0, "%d", i); + char *r = string_alloc(l + 1); + sprintf(r, "%d", i); + return r; +} + +char *i32_to_string_hex(int32_t i) { + int l = snprintf(NULL, 0, "%X", i); + char *r = string_alloc(l + 1); + sprintf(r, "%X", i); + return r; +} + +char *i64_to_string(int64_t i) { + int l = snprintf(NULL, 0, "%ld", i); + char *r = string_alloc(l + 1); + sprintf(r, "%ld", i); + return r; +} + +char *u64_to_string(uint64_t i) { + int l = snprintf(NULL, 0, "%lu", i); + char *r = string_alloc(l + 1); + sprintf(r, "%lu", i); + return r; +} + +char *f32_to_string_with_zeros(float f) { + int l = snprintf(NULL, 0, "%f", f); + char *r = string_alloc(l + 1); + sprintf(r, "%f", f); + return r; +} + +char *f32_to_string(float f) { + char *r = f32_to_string_with_zeros(f); + string_strip_trailing_zeros(r); + return r; +} + +void string_strip_trailing_zeros(char *str) { + int len = strlen(str); + while (str[--len] == '0') { + str[len] = '\0'; + } + if (str[len] == '.') { + str[len] = '\0'; + } +} + +int32_t string_index_of_pos(char *s, char *search, int pos) { + char *found = strstr(s + pos, search); + if (found != NULL) { + return found - s; + } + return -1; +} + +int32_t string_index_of(char *s, char *search) { + return string_index_of_pos(s, search, 0); +} + +int32_t string_last_index_of(char *str, char *search) { + char *s = str; + char *found = NULL; + while (1) { + char *p = strstr(s, search); + if (p == NULL) { + break; + } + found = p; + s = p + 1; + } + if (found != NULL) { + return found - str; + } + return -1; +} + +any_array_t *string_split(char *s, char *sep) { + char *r = string_alloc(strlen(s) + 1); // Modified by strtok + any_array_t *a = gc_alloc(sizeof(any_array_t)); + + strcpy(r, s); + int count = 0; + char *token = strtok(r, sep); + while (token != NULL) { + count++; + token = strtok(NULL, sep); + } + any_array_resize(a, count); + + strcpy(r, s); + token = strtok(r, sep); + while (token != NULL) { + any_array_push(a, string_copy(token)); // Make a copy to keep gc ref + // TODO: this works only for single char sep + token = strtok(NULL, sep); + } + + if (a->length == 0) { // Separator not found + any_array_push(a, r); + } + + return a; +} + +char *string_array_join(any_array_t *a, char *separator) { + int len = 0; + int len_sep = strlen(separator); + for (int i = 0; i < a->length; ++i) { + len += strlen(a->buffer[i]); + if (i < a->length - 1) { + len += len_sep; + } + } + + char *r = string_alloc(len + 1); + for (int i = 0; i < a->length; ++i) { + strcat(r, a->buffer[i]); + if (i < a->length - 1) { + strcat(r, separator); + } + } + return r; +} + +char *string_replace_all(char *s, char *search, char *replace) { + char *buffer = string_alloc(1024); + char *buffer_pos = buffer; + size_t search_len = strlen(search); + size_t replace_len = strlen(replace); + while (1) { + char *p = strstr(s, search); + if (p == NULL) { + strcpy(buffer_pos, s); + break; + } + memcpy(buffer_pos, s, p - s); + buffer_pos += p - s; + memcpy(buffer_pos, replace, replace_len); + buffer_pos += replace_len; + s = p + search_len; + } + return buffer; +} + +char *substring(char *s, int32_t start, int32_t end) { + char *buffer = string_alloc(end - start + 1); + for (int i = 0; i < end - start; ++i) { + buffer[i] = s[start + i]; + } + return buffer; +} + +char *string_from_char_code(int32_t c) { + char *r = string_alloc(2); + r[0] = c; + r[1] = '\0'; + return r; +} + +int32_t char_code_at(char *s, int32_t i) { + return s[i]; +} + +char *char_at(char *s, int32_t i) { + char *r = string_alloc(2); + r[0] = s[i]; + r[1] = '\0'; + return r; +} + +bool starts_with(char *s, char *start) { + return strncmp(start, s, strlen(start)) == 0; +} + +bool ends_with(char *s, char *end) { + size_t len_s = strlen(s); + size_t len_end = strlen(end); + return strncmp(s + len_s - len_end, end, len_end) == 0; +} + +char *to_lower_case(char *s) { + char *r = string_alloc(strlen(s) + 1); + strcpy(r, s); + int len = string_length(r); + for (int i = 0; i < len; ++i) { + r[i] = tolower(r[i]); + } + return r; +} + +char *to_upper_case(char *s) { + char *r = string_alloc(strlen(s) + 1); + strcpy(r, s); + int len = string_length(r); + for (int i = 0; i < len; ++i) { + r[i] = toupper(r[i]); + } + return r; +} + +char *trim_end(char *str) { + int pos = string_length(str) - 1; + while (pos >= 0 && (str[pos] == ' ' || str[pos] == '\n' || str[pos] == '\r')) { + pos--; + } + return substring(str, 0, pos + 1); +} + +// Per Lowgren, CC BY-SA 3.0 +// https://stackoverflow.com/a/35332046 +#define is_unicode(c) (((c) & 0xc0) == 0xc0) +int string_utf8_decode(const char *str, int *i) { + const unsigned char *s = (const unsigned char *)str; + int u = *s, l = 1; + if (is_unicode(u)) { + int a = (u & 0x20) ? ((u & 0x10) ? ((u & 0x08) ? ((u & 0x04) ? 6 : 5) : 4) : 3) : 2; + if (a < 6 || !(u & 0x02)) { + int b, p = 0; + u = ((u << (a + 1)) & 0xff) >> (a + 1); + for (b = 1; b < a; ++b) + u = (u << 6) | (s[l++] & 0x3f); + } + } + if (i) *i += l; + return u; +} diff --git a/armorcore/sources/iron_string.h b/armorcore/sources/iron_string.h new file mode 100644 index 000000000..c2070030d --- /dev/null +++ b/armorcore/sources/iron_string.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include "iron_array.h" + +char *string_join(char *a, char *b); +char *string_copy(char *a); +int string_length(char *str); +bool string_equals(char *a, char *b); +char *i32_to_string(int32_t i); +char *i32_to_string_hex(int32_t i); +char *i64_to_string(int64_t i); +char *u64_to_string(uint64_t i); +char *f32_to_string(float f); +char *f32_to_string_with_zeros(float f); + +void string_strip_trailing_zeros(char *str); +int32_t string_index_of(char *s, char *search); +int32_t string_index_of_pos(char *s, char *search, int pos); +int32_t string_last_index_of(char *s, char *search); +any_array_t *string_split(char *s, char *sep); +char *string_array_join(any_array_t *a, char *separator); +char *string_replace_all(char *s, char *search, char *replace); +char *substring(char *s, int32_t start, int32_t end); +char *string_from_char_code(int32_t c); +int32_t char_code_at(char *s, int32_t i); +char *char_at(char *s, int32_t i); +bool starts_with(char *s, char *start); +bool ends_with(char *s, char *end); +char *to_lower_case(char *s); +char *to_upper_case(char *s); +char *trim_end(char *str); +int string_utf8_decode(const char *str, int *i); diff --git a/armorcore/sources/iron_ui.c b/armorcore/sources/iron_ui.c new file mode 100644 index 000000000..c570bb1c8 --- /dev/null +++ b/armorcore/sources/iron_ui.c @@ -0,0 +1,2579 @@ +#include "iron_ui.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "iron_string.h" +#include "iron_gc.h" + +static ui_t *current = NULL; +static ui_theme_t *theme; +static bool ui_key_repeat = true; // Emulate key repeat for non-character keys +static bool ui_dynamic_glyph_load = true; // Allow text input fields to push new glyphs into the font atlas +static float ui_key_repeat_time = 0.0; +static char ui_text_to_paste[1024]; +static char ui_text_to_copy[1024]; +static ui_t *ui_copy_receiver = NULL; +static int ui_copy_frame = 0; +static bool ui_combo_first = true; +static ui_handle_t *ui_combo_search_handle = NULL; +ui_t *ui_instances[UI_MAX_INSTANCES]; +int ui_instances_count; +bool ui_always_redraw_window = true; // Redraw cached window texture each frame or on changes only +bool ui_touch_scroll = false; // Pan with finger to scroll +bool ui_touch_hold = false; // Touch and hold finger for right click +bool ui_touch_tooltip = false; // Show extra tooltips above finger / on-screen keyboard +bool ui_is_cut = false; +bool ui_is_copy = false; +bool ui_is_paste = false; +void (*ui_on_border_hover)(ui_handle_t *, int) = NULL; // Mouse over window border, use for resizing +void (*ui_on_text_hover)(void) = NULL; // Mouse over text input, use to set I-cursor +void (*ui_on_deselect_text)(void) = NULL; // Text editing finished +void (*ui_on_tab_drop)(ui_handle_t *, int, ui_handle_t *, int) = NULL; // Tab reorder via drag and drop +#ifdef WITH_EVAL +float js_eval(char *str); +#endif + +float UI_SCALE() { + return current->ops->scale_factor; +} + +float UI_ELEMENT_W() { + return theme->ELEMENT_W * UI_SCALE(); +} + +float UI_ELEMENT_H() { + return theme->ELEMENT_H * UI_SCALE(); +} + +float UI_ELEMENT_OFFSET() { + return theme->ELEMENT_OFFSET * UI_SCALE(); +} + +float UI_ARROW_SIZE() { + return theme->ARROW_SIZE * UI_SCALE(); +} + +float UI_BUTTON_H() { + return theme->BUTTON_H * UI_SCALE(); +} + +float UI_CHECK_SIZE() { + return theme->CHECK_SIZE * UI_SCALE(); +} + +float UI_CHECK_SELECT_SIZE() { + return theme->CHECK_SELECT_SIZE * UI_SCALE(); +} + +float UI_FONT_SIZE() { + return theme->FONT_SIZE * UI_SCALE(); +} + +float UI_SCROLL_W() { + return theme->SCROLL_W * UI_SCALE(); +} + +float UI_SCROLL_MINI_W() { + return theme->SCROLL_MINI_W * UI_SCALE(); +} + +float UI_TEXT_OFFSET() { + return theme->TEXT_OFFSET * UI_SCALE(); +} + +float UI_TAB_W() { + return theme->TAB_W * UI_SCALE(); +} + +float UI_HEADER_DRAG_H() { + return 15.0 * UI_SCALE(); +} + +float UI_TOOLTIP_DELAY() { + return 1.0; +} + +ui_t *ui_get_current() { + return current; +} + +void ui_set_current(ui_t *_current) { + current = _current; + theme = current->ops->theme; +} + +ui_handle_t *ui_handle_create() { + ui_handle_t *h = (ui_handle_t *)gc_alloc(sizeof(ui_handle_t)); + memset(h, 0, sizeof(ui_handle_t)); + h->redraws = 2; + h->color = 0xffffffff; + h->text = ""; + h->init = true; + return h; +} + +ui_handle_t *ui_nest(ui_handle_t *handle, int pos) { + if (handle->children == NULL) { + handle->children = any_array_create(0); + } + while(pos >= handle->children->length) { + ui_handle_t *h = ui_handle_create(); + any_array_push(handle->children, h); + if (pos == handle->children->length - 1) { + // Return now so init stays true + return h; + } + } + // This handle already exists, set init to false + handle->children->buffer[pos]->init = false; + return handle->children->buffer[pos]; +} + +void ui_fade_color(float alpha) { + uint32_t color = arm_g2_get_color(); + uint8_t r = (color & 0x00ff0000) >> 16; + uint8_t g = (color & 0x0000ff00) >> 8; + uint8_t b = (color & 0x000000ff); + uint8_t a = (uint8_t)(255.0 * alpha); + arm_g2_set_color((a << 24) | (r << 16) | (g << 8) | b); +} + +void ui_fill(float x, float y, float w, float h, uint32_t color) { + arm_g2_set_color(color); + if (!current->enabled) { + ui_fade_color(0.25); + } + arm_g2_fill_rect(current->_x + x * UI_SCALE(), current->_y + y * UI_SCALE() - 1, w * UI_SCALE(), h * UI_SCALE()); + arm_g2_set_color(0xffffffff); +} + +void ui_rect(float x, float y, float w, float h, uint32_t color, float strength) { + arm_g2_set_color(color); + if (!current->enabled) { + ui_fade_color(0.25); + } + arm_g2_draw_rect(current->_x + x * UI_SCALE(), current->_y + y * UI_SCALE(), w * UI_SCALE(), h * UI_SCALE(), strength); + arm_g2_set_color(0xffffffff); +} + +void ui_draw_shadow(float x, float y, float w, float h) { + if (theme->SHADOWS) { + // for (int i = 0; i < 8; i++) { + // float offset = i * UI_SCALE(); + // float a = (8 - i + 1) * 0.01; + // arm_g2_set_color(((uint8_t)(a * 255) << 24) | (0 << 16) | (0 << 8) | 0); + // ui_draw_rect(true, x - offset, y - offset, w + offset * 2, h + offset * 2); + // } + float max_offset = 4.0 * UI_SCALE(); + for (int i = 0; i < 4; i++) { + float offset = (max_offset / 4) * (i + 1); + float a = 0.1 - (0.1 / 4) * i; + arm_g2_set_color(((uint8_t)(a * 255) << 24) | (0 << 16) | (0 << 8) | 0); + ui_draw_rect(true, x + offset, y + offset, w + (max_offset - offset) * 2, h + (max_offset - offset) * 2); + } + } +} + +void ui_draw_rect(bool fill, float x, float y, float w, float h) { + float strength = 1.0; + if (!current->enabled) { + ui_fade_color(0.25); + } + x = (int)x; + y = (int)y; + w = (int)w; + h = (int)h; + + if (fill) { + int r = current->filled_round_corner_image.width; + if (theme->ROUND_CORNERS && current->enabled && r > 0 && w >= r * 2.0) { + y -= 1; // Make it pixel perfect with non-round draw + h += 1; + arm_g2_draw_scaled_render_target(¤t->filled_round_corner_image, x, y, r, r); + arm_g2_draw_scaled_render_target(¤t->filled_round_corner_image, x, y + h, r, -r); + arm_g2_draw_scaled_render_target(¤t->filled_round_corner_image, x + w, y, -r, r); + arm_g2_draw_scaled_render_target(¤t->filled_round_corner_image, x + w, y + h, -r, -r); + arm_g2_fill_rect(x + r, y, w - r * 2.0, h); + arm_g2_fill_rect(x, y + r, w, h - r * 2.0); + } + else { + arm_g2_fill_rect(x, y - 1, w, h + 1); + } + } + else { + int r = current->round_corner_image.width; + if (theme->ROUND_CORNERS && current->enabled && r > 0) { + x -= 1; + w += 1; + y -= 1; + h += 1; + arm_g2_draw_scaled_render_target(¤t->round_corner_image, x, y, r, r); + arm_g2_draw_scaled_render_target(¤t->round_corner_image, x, y + h, r, -r); + arm_g2_draw_scaled_render_target(¤t->round_corner_image, x + w, y, -r, r); + arm_g2_draw_scaled_render_target(¤t->round_corner_image, x + w, y + h, -r, -r); + arm_g2_fill_rect(x + r, y, w - r * 2.0, strength); + arm_g2_fill_rect(x + r, y + h - 1, w - r * 2.0, strength); + arm_g2_fill_rect(x, y + r, strength, h - r * 2.0); + arm_g2_fill_rect(x + w - 1, y + r, strength, h - r * 2.0); + } + else { + arm_g2_draw_rect(x, y, w, h, strength); + } + } +} + +void ui_draw_round_bottom(float x, float y, float w) { + if (theme->ROUND_CORNERS) { + int r = current->filled_round_corner_image.width; + int h = 4; + y -= 1; // Make it pixel perfect with non-round draw + h += 1; + arm_g2_set_color(theme->SEPARATOR_COL); + arm_g2_draw_scaled_render_target(¤t->filled_round_corner_image, x, y + h, r, -r); + arm_g2_draw_scaled_render_target(¤t->filled_round_corner_image, x + w, y + h, -r, -r); + arm_g2_fill_rect(x + r, y, w - r * 2.0, h); + } +} + +bool ui_is_char(int code) { + return (code >= 65 && code <= 90) || (code >= 97 && code <= 122); +} + +int ui_check_start(int i, char *text, char **start, int start_count) { + for (int x = 0; x < start_count; ++x) { + if (strncmp(text + i, start[x], strlen(start[x])) == 0) { + return strlen(start[x]); + } + } + return 0; +} + +ui_text_extract_t ui_extract_coloring(char *text, ui_coloring_t *col) { + ui_text_extract_t res; + res.colored[0] = '\0'; + res.uncolored[0] = '\0'; + bool coloring = false; + int start_from = 0; + int start_length = 0; + for (int i = 0; i < strlen(text); ++i) { + bool skip_first = false; + // Check if upcoming text should be colored + int length = ui_check_start(i, text, col->start->buffer, col->start->length); + // Not touching another character + bool separated_left = i == 0 || !ui_is_char(text[i - 1]); + bool separated_right = i + length >= strlen(text) || !ui_is_char(text[i + length]); + bool is_separated = separated_left && separated_right; + // Start coloring + if (length > 0 && (!coloring || col->end[0] == '\0') && (!col->separated || is_separated)) { + coloring = true; + start_from = i; + start_length = length; + if (col->end[0] != '\0' && col->end[0] != '\n') skip_first = true; + } + // End coloring + else if (col->end[0] == '\0') { + if (i == start_from + start_length) coloring = false; + } + else if (strncmp(text + i, col->end, strlen(col->end)) == 0) { + coloring = false; + } + // If true, add current character to colored string + int len_c = strlen(res.colored); + int len_uc = strlen(res.uncolored); + if (coloring && !skip_first) { + res.colored[len_c] = text[i]; + res.colored[len_c + 1] = '\0'; + res.uncolored[len_uc] = ' '; + res.uncolored[len_uc + 1] = '\0'; + } + else { + res.colored[len_c] = ' '; + res.colored[len_c + 1] = '\0'; + res.uncolored[len_uc] = text[i]; + res.uncolored[len_uc + 1] = '\0'; + } + } + return res; +} + +void ui_draw_string(char *text, float x_offset, float y_offset, int align, bool truncation) { + static char temp[1024]; + if (text == NULL) { + return; + } + if (truncation) { + assert(strlen(text) < 1024 - 2); + char *full_text = text; + strcpy(temp, text); + text = &temp[0]; + while (strlen(text) > 0 && arm_g2_string_width(current->ops->font->font_, current->font_size, text) > current->_w - 6.0 * UI_SCALE()) { + text[strlen(text) - 1] = 0; + } + if (strlen(text) < strlen(full_text)) { + strcat(text, ".."); + // Strip more to fit ".." + while (strlen(text) > 2 && arm_g2_string_width(current->ops->font->font_, current->font_size, text) > current->_w - 10.0 * UI_SCALE()) { + text[strlen(text) - 3] = 0; + strcat(text, ".."); + } + if (current->is_hovered) { + ui_tooltip(full_text); + } + } + } + + if (ui_dynamic_glyph_load) { + int len = strlen(text); + for (int i = 0; i < len; ++i) { + if (text[i] > 126 && !arm_g2_font_has_glyph((int)text[i])) { + int glyph = text[i]; + arm_g2_font_add_glyph(glyph); + } + } + } + + if (x_offset < 0) { + x_offset = theme->TEXT_OFFSET; + } + x_offset *= UI_SCALE(); + arm_g2_set_font(current->ops->font->font_, current->font_size); + if (align == UI_ALIGN_CENTER) { + x_offset = current->_w / 2.0 - arm_g2_string_width(current->ops->font->font_, current->font_size, text) / 2.0; + } + else if (align == UI_ALIGN_RIGHT) { + x_offset = current->_w - arm_g2_string_width(current->ops->font->font_, current->font_size, text) - UI_TEXT_OFFSET(); + } + + if (!current->enabled) { + ui_fade_color(0.25); + } + + if (current->text_coloring == NULL) { + arm_g2_draw_string(text, current->_x + x_offset, current->_y + current->font_offset_y + y_offset); + } + else { + // Monospace fonts only for now + strcpy(temp, text); + for (int i = 0; i < current->text_coloring->colorings->length; ++i) { + ui_coloring_t *coloring = current->text_coloring->colorings->buffer[i]; + ui_text_extract_t result = ui_extract_coloring(temp, coloring); + if (result.colored[0] != '\0') { + arm_g2_set_color(coloring->color); + arm_g2_draw_string(result.colored, current->_x + x_offset, current->_y + current->font_offset_y + y_offset); + } + strcpy(temp, result.uncolored); + } + arm_g2_set_color(current->text_coloring->default_color); + arm_g2_draw_string(temp, current->_x + x_offset, current->_y + current->font_offset_y + y_offset); + } +} + +bool ui_get_initial_hover(float elem_h) { + if (current->scissor && current->input_y < current->_window_y + current->window_header_h) { + return false; + } + return current->enabled && current->input_enabled && + current->input_started_x >= current->_window_x + current->_x && current->input_started_x < (current->_window_x + current->_x + current->_w) && + current->input_started_y >= current->_window_y + current->_y && current->input_started_y < (current->_window_y + current->_y + elem_h); +} + +bool ui_get_hover(float elem_h) { + if (current->scissor && current->input_y < current->_window_y + current->window_header_h) { + return false; + } + current->is_hovered = current->enabled && current->input_enabled && + current->input_x >= current->_window_x + current->_x && current->input_x < (current->_window_x + current->_x + current->_w) && + current->input_y >= current->_window_y + current->_y && current->input_y < (current->_window_y + current->_y + elem_h); + return current->is_hovered; +} + +bool ui_get_released(float elem_h) { // Input selection + current->is_released = current->enabled && current->input_enabled && current->input_released && ui_get_hover(elem_h) && ui_get_initial_hover(elem_h); + return current->is_released; +} + +bool ui_get_pushed(float elem_h) { + current->is_pushed = current->enabled && current->input_enabled && current->input_down && ui_get_hover(elem_h) && ui_get_initial_hover(elem_h); + return current->is_pushed; +} + +bool ui_get_started(float elem_h) { + current->is_started = current->enabled && current->input_enabled && current->input_started && ui_get_hover(elem_h); + return current->is_started; +} + +bool ui_is_visible(float elem_h) { + if (current->current_window == NULL) return true; + return (current->_y + elem_h > current->window_header_h && current->_y < current->current_window->texture.height); +} + +float ui_get_ratio(float ratio, float dyn) { + return ratio < 0 ? -ratio : ratio * dyn; +} + +// Draw the upcoming elements in the same row +// Negative values will be treated as absolute, positive values as ratio to `window width` +void ui_row(f32_array_t *ratios) { + if (ratios->length == 0) { + current->ratios = NULL; + return; + } + current->ratios = ratios; + current->current_ratio = 0; + current->x_before_split = current->_x; + current->w_before_split = current->_w; + current->_w = ui_get_ratio(ratios->buffer[current->current_ratio], current->_w); +} + +void ui_indent() { + current->_x += UI_TAB_W(); + current->_w -= UI_TAB_W(); +} + +void ui_unindent() { + current->_x -= UI_TAB_W(); + current->_w += UI_TAB_W(); +} + +void ui_end_element_of_size(float element_size) { + if (current->current_window == NULL || current->current_window->layout == UI_LAYOUT_VERTICAL) { + if (current->current_ratio == -1 || (current->ratios != NULL && current->current_ratio == current->ratios->length - 1)) { // New line + current->_y += element_size; + + if ((current->ratios != NULL && current->current_ratio == current->ratios->length - 1)) { // Last row element + current->current_ratio = -1; + current->ratios = NULL; + current->_x = current->x_before_split; + current->_w = current->w_before_split; + } + } + else { // Row + current->current_ratio++; + current->_x += current->_w; // More row elements to place + current->_w = ui_get_ratio(current->ratios->buffer[current->current_ratio], current->w_before_split); + } + } + else { // Horizontal + current->_x += current->_w + UI_ELEMENT_OFFSET(); + } +} + +void ui_end_element() { + ui_end_element_of_size(UI_ELEMENT_H() + UI_ELEMENT_OFFSET()); +} + +void ui_resize(ui_handle_t *handle, int w, int h) { + handle->redraws = 2; + if (handle->texture.width != 0) { + kinc_g4_render_target_destroy(&handle->texture); + } + if (w < 1) { + w = 1; + } + if (h < 1) { + h = 1; + } + kinc_g4_render_target_init(&handle->texture, w, h, KINC_G4_RENDER_TARGET_FORMAT_32BIT, 0, 0); +} + +bool ui_input_in_rect(float x, float y, float w, float h) { + return current->enabled && current->input_enabled && + current->input_x >= x && current->input_x < (x + w) && + current->input_y >= y && current->input_y < (y + h); +} + +bool ui_input_changed() { + return current->input_dx != 0 || current->input_dy != 0 || current->input_wheel_delta != 0 || current->input_started || current->input_started_r || current->input_released || current->input_released_r || current->input_down || current->input_down_r || current->is_key_pressed; +} + +void ui_end_input() { + if (ui_on_tab_drop != NULL && current->drag_tab_handle != NULL) { + if (current->input_dx != 0 || current->input_dy != 0) { + kinc_mouse_set_cursor(1); // Hand + } + if (current->input_released) { + kinc_mouse_set_cursor(0); // Default + current->drag_tab_handle = NULL; + } + } + + current->is_key_pressed = false; + current->input_started = false; + current->input_started_r = false; + current->input_released = false; + current->input_released_r = false; + current->input_dx = 0; + current->input_dy = 0; + current->input_wheel_delta = 0; + current->pen_in_use = false; + if (ui_key_repeat && current->is_key_down && kinc_time() - ui_key_repeat_time > 0.05) { + if (current->key_code == KINC_KEY_BACKSPACE || current->key_code == KINC_KEY_DELETE || current->key_code == KINC_KEY_LEFT || current->key_code == KINC_KEY_RIGHT || current->key_code == KINC_KEY_UP || current->key_code == KINC_KEY_DOWN) { + ui_key_repeat_time = kinc_time(); + current->is_key_pressed = true; + } + } + if (ui_touch_hold && current->input_down && current->input_x == current->input_started_x && current->input_y == current->input_started_y && current->input_started_time > 0 && kinc_time() - current->input_started_time > 0.7) { + current->touch_hold_activated = true; + current->input_released_r = true; + current->input_started_time = 0; + } +} + +void ui_scroll(float delta) { + current->current_window->scroll_offset -= delta; +} + +int ui_line_count(char *str) { + if (str == NULL) return 0; + int i = 0; + int count = 1; + while (str[i] != '\0') { + if (str[i] == '\n') count++; + i++; + } + return count; +} + +char *ui_extract_line(char *str, int line) { + static char temp[1024]; + int pos = 0; + int len = strlen(str); + int line_i = 0; + for (int i = 0; i < len; ++i) { + if (str[i] == '\n') { + line_i++; continue; + } + if (line_i < line) { + continue; + } + if (line_i > line) { + break; + } + temp[pos++] = str[i]; + } + temp[pos] = 0; + return temp; +} + +char *ui_lower_case(char *dest, char *src) { + int len = strlen(src); + assert(len < 1024); + for (int i = 0; i < len; ++i) { + dest[i] = tolower(src[i]); + } + return dest; +} + +void ui_draw_tooltip_text(bool bind_global_g) { + arm_g2_set_color(theme->TEXT_COL); + int line_count = ui_line_count(current->tooltip_text); + float tooltip_w = 0.0; + for (int i = 0; i < line_count; ++i) { + float line_tooltip_w = arm_g2_string_width(current->ops->font->font_, current->font_size, ui_extract_line(current->tooltip_text, i)); + if (line_tooltip_w > tooltip_w) { + tooltip_w = line_tooltip_w; + } + } + current->tooltip_x = fmin(current->tooltip_x, kinc_window_width(0) - tooltip_w - 20); + if (bind_global_g) arm_g2_restore_render_target(); + float font_height = arm_g2_font_height(current->ops->font->font_, current->font_size); + float off = 0; + if (current->tooltip_img != NULL) { + float w = current->tooltip_img->tex_width; + if (current->tooltip_img_max_width != 0 && w > current->tooltip_img_max_width) { + w = current->tooltip_img_max_width; + } + off = current->tooltip_img->tex_height * (w / current->tooltip_img->tex_width); + } + else if (current->tooltip_rt != NULL) { + float w = current->tooltip_rt->width; + if (current->tooltip_img_max_width != 0 && w > current->tooltip_img_max_width) { + w = current->tooltip_img_max_width; + } + off = current->tooltip_rt->height * (w / current->tooltip_rt->width); + } + arm_g2_fill_rect(current->tooltip_x, current->tooltip_y + off, tooltip_w + 20, font_height * line_count); + arm_g2_set_font(current->ops->font->font_, current->font_size); + arm_g2_set_color(theme->BUTTON_COL); + for (int i = 0; i < line_count; ++i) { + arm_g2_draw_string(ui_extract_line(current->tooltip_text, i), current->tooltip_x + 5, current->tooltip_y + off + i * current->font_size); + } +} + +void ui_draw_tooltip_image(bool bind_global_g) { + float w = current->tooltip_img->tex_width; + if (current->tooltip_img_max_width != 0 && w > current->tooltip_img_max_width) { + w = current->tooltip_img_max_width; + } + float h = current->tooltip_img->tex_height * (w / current->tooltip_img->tex_width); + current->tooltip_x = fmin(current->tooltip_x, kinc_window_width(0) - w - 20); + current->tooltip_y = fmin(current->tooltip_y, kinc_window_height(0) - h - 20); + if (bind_global_g) { + arm_g2_restore_render_target(); + } + arm_g2_set_color(0xff000000); + arm_g2_fill_rect(current->tooltip_x, current->tooltip_y, w, h); + arm_g2_set_color(0xffffffff); + current->tooltip_invert_y ? + arm_g2_draw_scaled_image(current->tooltip_img, current->tooltip_x, current->tooltip_y + h, w, -h) : + arm_g2_draw_scaled_image(current->tooltip_img, current->tooltip_x, current->tooltip_y, w, h); +} + +void ui_draw_tooltip_rt(bool bind_global_g) { + float w = current->tooltip_rt->width; + if (current->tooltip_img_max_width != 0 && w > current->tooltip_img_max_width) { + w = current->tooltip_img_max_width; + } + float h = current->tooltip_rt->height * (w / current->tooltip_rt->width); + current->tooltip_x = fmin(current->tooltip_x, kinc_window_width(0) - w - 20); + current->tooltip_y = fmin(current->tooltip_y, kinc_window_height(0) - h - 20); + if (bind_global_g) { + arm_g2_restore_render_target(); + } + arm_g2_set_color(0xff000000); + arm_g2_fill_rect(current->tooltip_x, current->tooltip_y, w, h); + arm_g2_set_color(0xffffffff); + current->tooltip_invert_y ? + arm_g2_draw_scaled_render_target(current->tooltip_rt, current->tooltip_x, current->tooltip_y + h, w, -h) : + arm_g2_draw_scaled_render_target(current->tooltip_rt, current->tooltip_x, current->tooltip_y, w, h); +} + +void ui_draw_tooltip(bool bind_global_g) { + static char temp[1024]; + if (current->slider_tooltip) { + if (bind_global_g) { + arm_g2_restore_render_target(); + } + arm_g2_set_font(current->ops->font->font_, current->font_size * 2); + sprintf(temp, "%f", round(current->scroll_handle->value * 100.0) / 100.0); + string_strip_trailing_zeros(temp); + char *text = temp; + float x_off = arm_g2_string_width(current->ops->font->font_, current->font_size * 2.0, text) / 2.0; + float y_off = arm_g2_font_height(current->ops->font->font_, current->font_size * 2.0); + float x = fmin(fmax(current->slider_tooltip_x, current->input_x), current->slider_tooltip_x + current->slider_tooltip_w); + arm_g2_set_color(theme->BUTTON_COL); + arm_g2_fill_rect(x - x_off, current->slider_tooltip_y - y_off, x_off * 2.0, y_off); + arm_g2_set_color(theme->TEXT_COL); + arm_g2_draw_string(text, x - x_off, current->slider_tooltip_y - y_off); + } + if (ui_touch_tooltip && current->text_selected_handle != NULL) { + if (bind_global_g) { + arm_g2_restore_render_target(); + } + arm_g2_set_font(current->ops->font->font_, current->font_size * 2.0); + float x_off = arm_g2_string_width(current->ops->font->font_, current->font_size * 2.0, current->text_selected) / 2.0; + float y_off = arm_g2_font_height(current->ops->font->font_, current->font_size * 2.0) / 2.0; + float x = kinc_window_width(0) / 2.0; + float y = kinc_window_height(0) / 3.0; + arm_g2_set_color(theme->BUTTON_COL); + arm_g2_fill_rect(x - x_off, y - y_off, x_off * 2.0, y_off * 2.0); + arm_g2_set_color(theme->TEXT_COL); + arm_g2_draw_string(current->text_selected, x - x_off, y - y_off); + } + + if (current->tooltip_text[0] != '\0' || current->tooltip_img != NULL || current->tooltip_rt != NULL) { + if (ui_input_changed()) { + current->tooltip_shown = false; + current->tooltip_wait = current->input_dx == 0 && current->input_dy == 0; // Wait for movement before showing up again + } + if (!current->tooltip_shown) { + current->tooltip_shown = true; + current->tooltip_x = current->input_x; + current->tooltip_time = kinc_time(); + } + if (!current->tooltip_wait && kinc_time() - current->tooltip_time > UI_TOOLTIP_DELAY()) { + if (current->tooltip_img != NULL) { + ui_draw_tooltip_image(bind_global_g); + } + else if (current->tooltip_rt != NULL) { + ui_draw_tooltip_rt(bind_global_g); + } + if (current->tooltip_text[0] != '\0') { + ui_draw_tooltip_text(bind_global_g); + } + } + } + else current->tooltip_shown = false; +} + +void ui_draw_combo(bool begin /*= true*/) { + if (current->combo_selected_handle == NULL) { + return; + } + arm_g2_set_color(theme->SEPARATOR_COL); + if (begin) { + arm_g2_restore_render_target(); + } + + float combo_h = (current->combo_selected_texts->length + (current->combo_selected_label != NULL ? 1 : 0) + (current->combo_search_bar ? 1 : 0)) * UI_ELEMENT_H(); + float dist_top = current->combo_selected_y - combo_h - UI_ELEMENT_H() - current->window_border_top; + float dist_bottom = kinc_window_height(0) - current->window_border_bottom - (current->combo_selected_y + combo_h ); + bool unroll_up = dist_bottom < 0 && dist_bottom < dist_top; + + ui_begin_region(current, current->combo_selected_x, current->combo_selected_y, current->combo_selected_w); + + if (unroll_up) { + float off = current->combo_selected_label != NULL ? UI_ELEMENT_H() / UI_SCALE() : 0.0; + ui_draw_shadow(current->_x, current->_y - combo_h - off, current->_w, combo_h); + } + else { + ui_draw_shadow(current->_x, current->_y, current->_w, combo_h); + } + + if (current->is_key_pressed || current->input_wheel_delta != 0) { + int arrow_up = current->is_key_pressed && current->key_code == (unroll_up ? KINC_KEY_DOWN : KINC_KEY_UP); + int arrow_down = current->is_key_pressed && current->key_code == (unroll_up ? KINC_KEY_UP : KINC_KEY_DOWN); + int wheel_up = (unroll_up && current->input_wheel_delta > 0) || (!unroll_up && current->input_wheel_delta < 0); + int wheel_down = (unroll_up && current->input_wheel_delta < 0) || (!unroll_up && current->input_wheel_delta > 0); + if ((arrow_up || wheel_up) && current->combo_to_submit > 0) { + int step = 1; + if (current->combo_search_bar && strlen(current->text_selected) > 0) { + char search[512]; + char str[512]; + ui_lower_case(search, current->text_selected); + while (true) { + ui_lower_case(str, current->combo_selected_texts->buffer[current->combo_to_submit - step]); + if (strstr(str, search) == NULL && current->combo_to_submit - step > 0) { + ++step; + } + else { + break; + } + } + + // Corner case: current position is the top one according to the search pattern + ui_lower_case(str, current->combo_selected_texts->buffer[current->combo_to_submit - step]); + if (strstr(str, search) == NULL) { + step = 0; + } + } + current->combo_to_submit -= step; + current->submit_combo_handle = current->combo_selected_handle; + } + else if ((arrow_down || wheel_down) && current->combo_to_submit < current->combo_selected_texts->length - 1) { + int step = 1; + if (current->combo_search_bar && strlen(current->text_selected) > 0) { + char search[512]; + char str[512]; + ui_lower_case(search, current->text_selected); + while (true) { + ui_lower_case(str, current->combo_selected_texts->buffer[current->combo_to_submit + step]); + if (strstr(str, search) == NULL && current->combo_to_submit + step > 0) { + ++step; + } + else { + break; + } + } + + // Corner case: current position is the top one according to the search pattern + ui_lower_case(str, current->combo_selected_texts->buffer[current->combo_to_submit + step]); + if (strstr(str, search) == NULL) { + step = 0; + } + } + + current->combo_to_submit += step; + current->submit_combo_handle = current->combo_selected_handle; + } + if (current->combo_selected_window != NULL) { + current->combo_selected_window->redraws = 2; + } + } + + current->input_enabled = true; + int _BUTTON_COL = theme->BUTTON_COL; + int _ELEMENT_OFFSET = theme->ELEMENT_OFFSET; + theme->ELEMENT_OFFSET = 0; + float unroll_right = current->_x + current->combo_selected_w * 2.0 < kinc_window_width(0) - current->window_border_right ? 1 : -1; + bool reset_position = false; + char search[512]; + search[0] = '\0'; + if (current->combo_search_bar) { + if (unroll_up) { + current->_y -= UI_ELEMENT_H() * 2.0; + } + if (ui_combo_first) { + ui_combo_search_handle->text = ""; + } + ui_fill(0, 0, current->_w / UI_SCALE(), UI_ELEMENT_H() / UI_SCALE(), theme->SEPARATOR_COL); + strcpy(search, ui_text_input(ui_combo_search_handle, "", UI_ALIGN_LEFT, true, true)); + ui_lower_case(search, search); + if (current->is_released) { + ui_combo_first = true; // Keep combo open + } + if (ui_combo_first) { + #if !defined(KINC_ANDROID) && !defined(KINC_IOS) + ui_start_text_edit(ui_combo_search_handle, UI_ALIGN_LEFT); // Focus search bar + #endif + } + reset_position = ui_combo_search_handle->changed; + } + + for (int i = 0; i < current->combo_selected_texts->length; ++i) { + char str[512]; + ui_lower_case(str, current->combo_selected_texts->buffer[i]); + if (strlen(search) > 0 && strstr(str, search) == NULL) { + continue; // Don't show items that don't fit the current search pattern + } + + if (reset_position) { // The search has changed, select first entry that matches + current->combo_to_submit = current->combo_selected_handle->position = i; + current->submit_combo_handle = current->combo_selected_handle; + reset_position = false; + } + if (unroll_up) { + current->_y -= UI_ELEMENT_H() * 2.0; + } + theme->BUTTON_COL = i == current->combo_selected_handle->position ? + theme->HIGHLIGHT_COL : + theme->SEPARATOR_COL; + ui_fill(0, 0, current->_w / UI_SCALE(), UI_ELEMENT_H() / UI_SCALE(), theme->SEPARATOR_COL); + if (ui_button(current->combo_selected_texts->buffer[i], current->combo_selected_align, "")) { + current->combo_to_submit = i; + current->submit_combo_handle = current->combo_selected_handle; + if (current->combo_selected_window != NULL) { + current->combo_selected_window->redraws = 2; + } + break; + } + if (current->_y + UI_ELEMENT_H() > kinc_window_height(0) - current->window_border_bottom || current->_y - UI_ELEMENT_H() * 2 < current->window_border_top) { + current->_x += current->combo_selected_w * unroll_right; // Next column + current->_y = current->combo_selected_y; + } + } + theme->BUTTON_COL = _BUTTON_COL; + theme->ELEMENT_OFFSET = _ELEMENT_OFFSET; + + if (current->combo_selected_label != NULL) { // Unroll down + if (unroll_up) { + current->_y -= UI_ELEMENT_H() * 2.0; + ui_fill(0, 0, current->_w / UI_SCALE(), UI_ELEMENT_H() / UI_SCALE(), theme->SEPARATOR_COL); + arm_g2_set_color(theme->LABEL_COL); + ui_draw_string(current->combo_selected_label, theme->TEXT_OFFSET, 0, UI_ALIGN_RIGHT, true); + current->_y += UI_ELEMENT_H(); + ui_fill(0, 0, current->_w / UI_SCALE(), 1.0 * UI_SCALE(), theme->ACCENT_COL); // Separator + } + else { + ui_fill(0, 0, current->_w / UI_SCALE(), UI_ELEMENT_H() / UI_SCALE(), theme->SEPARATOR_COL); + ui_fill(0, 0, current->_w / UI_SCALE(), 1.0 * UI_SCALE(), theme->ACCENT_COL); // Separator + arm_g2_set_color(theme->LABEL_COL); + ui_draw_string(current->combo_selected_label, theme->TEXT_OFFSET, 0, UI_ALIGN_RIGHT, true); + current->_y += UI_ELEMENT_H(); + ui_draw_round_bottom(current->_x, current->_y - 1, current->_w); + } + } + + if ((current->input_released || current->input_released_r || current->is_escape_down || current->is_return_down) && !ui_combo_first) { + current->combo_selected_handle = NULL; + ui_combo_first = true; + } + else { + ui_combo_first = false; + } + current->input_enabled = current->combo_selected_handle == NULL; + ui_end_region(false); +} + +void ui_bake_elements() { + if (current->check_select_image.width != 0) { + kinc_g4_render_target_destroy(¤t->check_select_image); + } + float r = UI_CHECK_SELECT_SIZE(); + kinc_g4_render_target_init(¤t->check_select_image, r, r, KINC_G4_RENDER_TARGET_FORMAT_32BIT, 0, 0); + arm_g2_set_render_target(¤t->check_select_image); + kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0x00000000, 0, 0); + arm_g2_set_color(0xffffffff); + arm_g2_draw_line(0, r / 2.0, r / 2.0 - 2.0 * UI_SCALE(), r - 2.0 * UI_SCALE(), 2.0 * UI_SCALE()); + arm_g2_draw_line(r / 2.0 - 3.0 * UI_SCALE(), r - 3.0 * UI_SCALE(), r / 2.0 + 5.0 * UI_SCALE(), r - 11.0 * UI_SCALE(), 2.0 * UI_SCALE()); + arm_g2_end(); + + if (current->radio_image.width != 0) { + kinc_g4_render_target_destroy(¤t->radio_image); + } + r = UI_CHECK_SIZE(); + kinc_g4_render_target_init(¤t->radio_image, r, r, KINC_G4_RENDER_TARGET_FORMAT_32BIT, 0, 0); + arm_g2_set_render_target(¤t->radio_image); + kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0x00000000, 0, 0); + arm_g2_set_color(0xffaaaaaa); + arm_g2_fill_circle(r / 2.0, r / 2.0, r / 2.0, 0); + arm_g2_set_color(0xffffffff); + arm_g2_draw_circle(r / 2.0, r / 2.0, r / 2.0, 0, 1.0 * UI_SCALE()); + arm_g2_end(); + + if (current->radio_select_image.width != 0) { + kinc_g4_render_target_destroy(¤t->radio_select_image); + } + r = UI_CHECK_SELECT_SIZE(); + kinc_g4_render_target_init(¤t->radio_select_image, r, r, KINC_G4_RENDER_TARGET_FORMAT_32BIT, 0, 0); + arm_g2_set_render_target(¤t->radio_select_image); + kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0x00000000, 0, 0); + arm_g2_set_color(0xffaaaaaa); + arm_g2_fill_circle(r / 2.0, r / 2.0, 4.5 * UI_SCALE(), 0); + arm_g2_set_color(0xffffffff); + arm_g2_fill_circle(r / 2.0, r / 2.0, 4.0 * UI_SCALE(), 0); + arm_g2_end(); + + if (theme->ROUND_CORNERS) { + if (current->filled_round_corner_image.width != 0) { + kinc_g4_render_target_destroy(¤t->filled_round_corner_image); + } + r = 4.0 * UI_SCALE(); + kinc_g4_render_target_init(¤t->filled_round_corner_image, r, r, KINC_G4_RENDER_TARGET_FORMAT_32BIT, 0, 0); + arm_g2_set_render_target(¤t->filled_round_corner_image); + kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0x00000000, 0, 0); + arm_g2_set_color(0xffffffff); + arm_g2_fill_circle(r, r, r, 0); + arm_g2_end(); + + if (current->round_corner_image.width != 0) { + kinc_g4_render_target_destroy(¤t->round_corner_image); + } + kinc_g4_render_target_init(¤t->round_corner_image, r, r, KINC_G4_RENDER_TARGET_FORMAT_32BIT, 0, 0); + arm_g2_set_render_target(¤t->round_corner_image); + kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0x00000000, 0, 0); + arm_g2_set_color(0xffffffff); + arm_g2_draw_circle(r, r, r, 0, 1); + arm_g2_end(); + } + + arm_g2_restore_render_target(); + current->elements_baked = true; +} + +void ui_begin_region(ui_t *ui, int x, int y, int w) { + ui_set_current(ui); + if (!current->elements_baked) { + ui_bake_elements(); + } + current->changed = false; + current->current_window = NULL; + current->tooltip_text[0] = '\0'; + current->tooltip_img = NULL; + current->tooltip_rt = NULL; + current->_window_x = 0; + current->_window_y = 0; + current->_window_w = w; + current->_x = x; + current->_y = y; + current->_w = w; + current->_h = 0; +} + +void ui_end_region(bool last) { + ui_draw_tooltip(false); + current->tab_pressed_handle = NULL; + if (last) { + ui_draw_combo(false); // Handle active combo + ui_end_input(); + } +} + +void ui_set_cursor_to_input(int align) { + float off = align == UI_ALIGN_LEFT ? UI_TEXT_OFFSET() : current->_w - arm_g2_string_width(current->ops->font->font_, current->font_size, current->text_selected); + float x = current->input_x - (current->_window_x + current->_x + off); + current->cursor_x = 0; + while (current->cursor_x < strlen(current->text_selected) && arm_g2_sub_string_width(current->ops->font->font_, current->font_size, current->text_selected, 0, current->cursor_x) < x) { + current->cursor_x++; + } + current->highlight_anchor = current->cursor_x; +} + +void ui_start_text_edit(ui_handle_t *handle, int align) { + current->is_typing = true; + current->submit_text_handle = current->text_selected_handle; + strcpy(current->text_to_submit, current->text_selected); + current->text_selected_handle = handle; + strcpy(current->text_selected, handle->text); + current->cursor_x = strlen(handle->text); + if (current->tab_pressed) { + current->tab_pressed = false; + current->is_key_pressed = false; // Prevent text deselect after tab press + } + else if (!current->highlight_on_select) { // Set cursor to click location + ui_set_cursor_to_input(align); + } + current->tab_pressed_handle = handle; + current->highlight_anchor = current->highlight_on_select ? 0 : current->cursor_x; + kinc_keyboard_show(); +} + +void ui_submit_text_edit() { + current->changed = strcmp(current->submit_text_handle->text, current->text_to_submit) != 0; + current->submit_text_handle->changed = current->changed; + strcpy(current->submit_text_handle->text, current->text_to_submit); + current->submit_text_handle = NULL; + current->text_to_submit[0] = '\0'; + current->text_selected[0] = '\0'; +} + +void ui_deselect_text(ui_t *ui) { + if (ui->text_selected_handle == NULL) { + return; + } + ui->submit_text_handle = ui->text_selected_handle; + strcpy(ui->text_to_submit, ui->text_selected); + ui->text_selected_handle = NULL; + ui->is_typing = false; + if (ui->current_window != NULL) { + ui->current_window->redraws = 2; + } + kinc_keyboard_hide(); + ui->highlight_anchor = ui->cursor_x; + if (ui_on_deselect_text != NULL) { + ui_on_deselect_text(); + } +} + +void ui_remove_char_at(char *str, int at) { + int len = strlen(str); + for (int i = at; i < len; ++i) { + str[i] = str[i + 1]; + } +} + +void ui_remove_chars_at(char *str, int at, int count) { + for (int i = 0; i < count; ++i) { + ui_remove_char_at(str, at); + } +} + +void ui_insert_char_at(char *str, int at, char c) { + int len = strlen(str); + for (int i = len + 1; i > at; --i) { + str[i] = str[i - 1]; + } + str[at] = c; +} + +void ui_insert_chars_at(char *str, int at, char *cs) { + int len = strlen(cs); + for (int i = 0; i < len; ++i) { + ui_insert_char_at(str, at + i, cs[i]); + } +} + +void ui_update_text_edit(int align, bool editable, bool live_update) { + char text[256]; + strcpy(text, current->text_selected); + if (current->is_key_pressed) { // Process input + if (current->key_code == KINC_KEY_LEFT) { // Move cursor + if (current->cursor_x > 0) { + current->cursor_x--; + } + } + else if (current->key_code == KINC_KEY_RIGHT) { + if (current->cursor_x < strlen(text)) { + current->cursor_x++; + } + } + else if (editable && current->key_code == KINC_KEY_BACKSPACE) { // Remove char + if (current->cursor_x > 0 && current->highlight_anchor == current->cursor_x) { + ui_remove_char_at(text, current->cursor_x - 1); + current->cursor_x--; + } + else if (current->highlight_anchor < current->cursor_x) { + int count = current->cursor_x - current->highlight_anchor; + ui_remove_chars_at(text, current->highlight_anchor, count); + current->cursor_x = current->highlight_anchor; + } + else { + int count = current->highlight_anchor - current->cursor_x; + ui_remove_chars_at(text, current->cursor_x, count); + } + } + else if (editable && current->key_code == KINC_KEY_DELETE) { + if (current->highlight_anchor == current->cursor_x) { + ui_remove_char_at(text, current->cursor_x); + } + else if (current->highlight_anchor < current->cursor_x) { + int count = current->cursor_x - current->highlight_anchor; + ui_remove_chars_at(text, current->highlight_anchor, count); + current->cursor_x = current->highlight_anchor; + } + else { + int count = current->highlight_anchor - current->cursor_x; + ui_remove_chars_at(text, current->cursor_x, count); + } + } + else if (current->key_code == KINC_KEY_RETURN) { // Deselect + ui_deselect_text(current); + } + else if (current->key_code == KINC_KEY_ESCAPE) { // Cancel + strcpy(current->text_selected, current->text_selected_handle->text); + ui_deselect_text(current); + } + else if (current->key_code == KINC_KEY_TAB && current->tab_switch_enabled && !current->is_ctrl_down) { // Next field + current->tab_pressed = true; + ui_deselect_text(current); + current->key_code = 0; + } + else if (current->key_code == KINC_KEY_HOME) { + current->cursor_x = 0; + } + else if (current->key_code == KINC_KEY_END) { + current->cursor_x = strlen(text); + } + else if (current->is_ctrl_down && current->is_a_down) { // Select all + current->cursor_x = strlen(text); + current->highlight_anchor = 0; + } + else if (editable && // Write + current->key_code != KINC_KEY_SHIFT && + current->key_code != KINC_KEY_CAPS_LOCK && + current->key_code != KINC_KEY_CONTROL && + current->key_code != KINC_KEY_META && + current->key_code != KINC_KEY_ALT && + current->key_code != KINC_KEY_UP && + current->key_code != KINC_KEY_DOWN && + current->key_char >= 32) { + ui_remove_chars_at(text, current->highlight_anchor, current->cursor_x - current->highlight_anchor); + ui_insert_char_at(text, current->highlight_anchor, current->key_char); + + current->cursor_x = current->cursor_x + 1 > strlen(text) ? strlen(text) : current->cursor_x + 1; + } + bool selecting = current->is_shift_down && (current->key_code == KINC_KEY_LEFT || current->key_code == KINC_KEY_RIGHT || current->key_code == KINC_KEY_SHIFT); + // isCtrlDown && isAltDown is the condition for AltGr was pressed + // AltGr is part of the German keyboard layout and part of key combinations like AltGr + e -> € + if (!selecting && (!current->is_ctrl_down || (current->is_ctrl_down && current->is_alt_down))) { + current->highlight_anchor = current->cursor_x; + } + } + + if (editable && ui_text_to_paste[0] != '\0') { // Process cut copy paste + ui_remove_chars_at(text, current->highlight_anchor, current->cursor_x - current->highlight_anchor); + ui_insert_chars_at(text, current->highlight_anchor, ui_text_to_paste); + current->cursor_x += strlen(ui_text_to_paste); + current->highlight_anchor = current->cursor_x; + ui_text_to_paste[0] = 0; + ui_is_paste = false; + } + if (current->highlight_anchor == current->cursor_x) { + strcpy(ui_text_to_copy, text); // Copy + } + else if (current->highlight_anchor < current->cursor_x) { + int len = current->cursor_x - current->highlight_anchor; + strncpy(ui_text_to_copy, text + current->highlight_anchor, len); + ui_text_to_copy[len] = '\0'; + } + else { + int len = current->highlight_anchor - current->cursor_x; + strncpy(ui_text_to_copy, text + current->cursor_x, len); + ui_text_to_copy[len] = '\0'; + } + if (editable && ui_is_cut) { // Cut + if (current->highlight_anchor == current->cursor_x) { + text[0] = '\0'; + } + else if (current->highlight_anchor < current->cursor_x) { + ui_remove_chars_at(text, current->highlight_anchor, current->cursor_x - current->highlight_anchor); + current->cursor_x = current->highlight_anchor; + } + else { + ui_remove_chars_at(text, current->cursor_x, current->highlight_anchor - current->cursor_x); + } + } + + float off = UI_TEXT_OFFSET(); + float line_height = UI_ELEMENT_H(); + float cursor_height = line_height - current->button_offset_y * 3.0; + // Draw highlight + if (current->highlight_anchor != current->cursor_x) { + float istart = current->cursor_x; + float iend = current->highlight_anchor; + if (current->highlight_anchor < current->cursor_x) { + istart = current->highlight_anchor; + iend = current->cursor_x; + } + + float hlstrw = arm_g2_sub_string_width(current->ops->font->font_, current->font_size, text, istart, iend); + float start_off = arm_g2_sub_string_width(current->ops->font->font_, current->font_size, text, 0, istart); + float hl_start = align == UI_ALIGN_LEFT ? current->_x + start_off + off : current->_x + current->_w - hlstrw - off; + if (align == UI_ALIGN_RIGHT) { + hl_start -= arm_g2_sub_string_width(current->ops->font->font_, current->font_size, text, iend, strlen(text)); + } + arm_g2_set_color(theme->ACCENT_COL); + arm_g2_fill_rect(hl_start, current->_y + current->button_offset_y * 1.5, hlstrw, cursor_height); + } + + // Draw cursor + int str_start = align == UI_ALIGN_LEFT ? 0 : current->cursor_x; + int str_length = align == UI_ALIGN_LEFT ? current->cursor_x : (strlen(text) - current->cursor_x); + float strw = arm_g2_sub_string_width(current->ops->font->font_, current->font_size, text, str_start, str_length); + float cursor_x = align == UI_ALIGN_LEFT ? current->_x + strw + off : current->_x + current->_w - strw - off; + arm_g2_set_color(theme->TEXT_COL); // Cursor + arm_g2_fill_rect(cursor_x, current->_y + current->button_offset_y * 1.5, 1.0 * UI_SCALE(), cursor_height); + + strcpy(current->text_selected, text); + if (live_update && current->text_selected_handle != NULL) { + current->text_selected_handle->changed = strcmp(current->text_selected_handle->text, current->text_selected) != 0; + current->text_selected_handle->text = string_copy(current->text_selected); + } +} + +void ui_set_hovered_tab_name(char *name) { + if (ui_input_in_rect(current->_window_x, current->_window_y, current->_window_w, current->_window_h)) { + strcpy(current->hovered_tab_name, name); + current->hovered_tab_x = current->_window_x; + current->hovered_tab_y = current->_window_y; + current->hovered_tab_w = current->_window_w; + current->hovered_tab_h = current->_window_h; + } +} + +void ui_draw_tabs() { + current->input_x = current->restore_x; + current->input_y = current->restore_y; + if (current->current_window == NULL) { + return; + } + float tab_x = 0.0; + float tab_y = 0.0; + float tab_h_min = UI_BUTTON_H() * 1.1; + float header_h = current->current_window->drag_enabled ? UI_HEADER_DRAG_H() : 0; + float tab_h = (theme->FULL_TABS && current->tab_vertical) ? ((current->_window_h - header_h) / current->tab_count) : tab_h_min; + float orig_y = current->_y; + current->_y = header_h; + current->tab_handle->changed = false; + + if (current->is_ctrl_down && current->is_tab_down) { // Next tab + current->tab_handle->position++; + if (current->tab_handle->position >= current->tab_count) { + current->tab_handle->position = 0; + } + current->tab_handle->changed = true; + current->is_tab_down = false; + } + + if (current->tab_handle->position >= current->tab_count) { + current->tab_handle->position = current->tab_count - 1; + } + + arm_g2_set_color(theme->SEPARATOR_COL); // Tab background + if (current->tab_vertical) { + arm_g2_fill_rect(0, current->_y, UI_ELEMENT_W(), current->_window_h); + } + else { + arm_g2_fill_rect(0, current->_y, current->_window_w, current->button_offset_y + tab_h + 2); + } + + arm_g2_set_color(theme->BUTTON_COL); // Underline tab buttons + if (current->tab_vertical) { + arm_g2_fill_rect(UI_ELEMENT_W(), current->_y, 1, current->_window_h); + } + else { + arm_g2_fill_rect(current->button_offset_y, current->_y + current->button_offset_y + tab_h + 2, current->_window_w - current->button_offset_y * 2.0, 1); + } + + float base_y = current->tab_vertical ? current->_y : current->_y + 2; + bool _enabled = current->enabled; + + for (int i = 0; i < current->tab_count; ++i) { + current->enabled = current->tab_enabled[i]; + current->_x = tab_x; + current->_y = base_y + tab_y; + current->_w = current->tab_vertical ? (UI_ELEMENT_W() - 1 * UI_SCALE()) : + theme->FULL_TABS ? (current->_window_w / current->tab_count) : + (arm_g2_string_width(current->ops->font->font_, current->font_size, current->tab_names[i]) + current->button_offset_y * 2.0 + 18.0 * UI_SCALE()); + bool released = ui_get_released(tab_h); + bool started = ui_get_started(tab_h); + bool pushed = ui_get_pushed(tab_h); + bool hover = ui_get_hover(tab_h); + if (ui_on_tab_drop != NULL) { + if (started) { + current->drag_tab_handle = current->tab_handle; + current->drag_tab_position = i; + } + if (current->drag_tab_handle != NULL && hover && current->input_released) { + ui_on_tab_drop(current->tab_handle, i, current->drag_tab_handle, current->drag_tab_position); + current->tab_handle->position = i; + } + } + if (released) { + ui_handle_t *h = ui_nest(current->tab_handle, current->tab_handle->position); // Restore tab scroll + h->scroll_offset = current->current_window->scroll_offset; + h = ui_nest(current->tab_handle, i); + current->tab_scroll = h->scroll_offset; + current->tab_handle->position = i; // Set new tab + current->current_window->redraws = 3; + current->tab_handle->changed = true; + } + bool selected = current->tab_handle->position == i; + + arm_g2_set_color((pushed || hover) ? theme->HOVER_COL : + current->tab_colors[i] != -1 ? current->tab_colors[i] : + selected ? theme->WINDOW_BG_COL : + theme->SEPARATOR_COL); + if (current->tab_vertical) { + tab_y += tab_h + 1; + } + else { + tab_x += current->_w + 1; + } + // ui_draw_rect(true, current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w, tab_h); // Round corners + arm_g2_fill_rect(current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w, tab_h); + arm_g2_set_color(theme->TEXT_COL); + if (!selected) { + ui_fade_color(0.65); + } + ui_draw_string(current->tab_names[i], theme->TEXT_OFFSET, (tab_h - tab_h_min) / 2.0, (theme->FULL_TABS || !current->tab_vertical) ? UI_ALIGN_CENTER : UI_ALIGN_LEFT, true); + + if (selected) { // Hide underline for active tab + if (current->tab_vertical) { + // Hide underline + // arm_g2_set_color(theme->WINDOW_BG_COL); + // arm_g2_fill_rect(current->_x + current->button_offset_y + current->_w - 1, current->_y + current->button_offset_y - 1, 2, tab_h + current->button_offset_y); + // Highlight + arm_g2_set_color(theme->HIGHLIGHT_COL); + arm_g2_fill_rect(current->_x + current->button_offset_y, current->_y + current->button_offset_y - 1, 2, tab_h + current->button_offset_y); + } + else { + // Hide underline + arm_g2_set_color(theme->WINDOW_BG_COL); + arm_g2_fill_rect(current->_x + current->button_offset_y, current->_y + current->button_offset_y + tab_h, current->_w, 1); + // Highlight + arm_g2_set_color(theme->HIGHLIGHT_COL); + arm_g2_fill_rect(current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w, 2); + } + } + + // Tab separator + if (i < current->tab_count - 1) { + arm_g2_set_color(theme->SEPARATOR_COL - 0x00050505); + if (current->tab_vertical) { + arm_g2_fill_rect(current->_x, current->_y + tab_h, current->_w, 1); + } + else { + arm_g2_fill_rect(current->_x + current->button_offset_y + current->_w, current->_y, 1, tab_h); + } + } + } + + current->enabled = _enabled; + ui_set_hovered_tab_name(current->tab_names[current->tab_handle->position]); + + current->_x = 0; // Restore positions + current->_y = orig_y; + current->_w = !current->current_window->scroll_enabled ? current->_window_w : current->_window_w - UI_SCROLL_W(); +} + +void ui_draw_arrow(bool selected) { + float x = current->_x + current->arrow_offset_x; + float y = current->_y + current->arrow_offset_y; + arm_g2_set_color(theme->TEXT_COL); + if (selected) { + arm_g2_fill_triangle(x, y, + x + UI_ARROW_SIZE(), y, + x + UI_ARROW_SIZE() / 2.0, y + UI_ARROW_SIZE()); + } + else { + arm_g2_fill_triangle(x, y, + x, y + UI_ARROW_SIZE(), + x + UI_ARROW_SIZE(), y + UI_ARROW_SIZE() / 2.0); + } +} + +void ui_draw_tree(bool selected) { + float SIGN_W = 7.0 * UI_SCALE(); + float x = current->_x + current->arrow_offset_x + 1; + float y = current->_y + current->arrow_offset_y + 1; + arm_g2_set_color(theme->TEXT_COL); + if (selected) { + arm_g2_fill_rect(x, y + SIGN_W / 2.0 - 1, SIGN_W, SIGN_W / 8.0); + } + else { + arm_g2_fill_rect(x, y + SIGN_W / 2.0 - 1, SIGN_W, SIGN_W / 8.0); + arm_g2_fill_rect(x + SIGN_W / 2.0 - 1, y, SIGN_W / 8.0, SIGN_W); + } +} + +void ui_draw_check(bool selected, bool hover) { + float x = current->_x + current->check_offset_x; + float y = current->_y + current->check_offset_y; + + arm_g2_set_color(selected ? theme->HIGHLIGHT_COL : theme->PRESSED_COL); + ui_draw_rect(true, x, y, UI_CHECK_SIZE(), UI_CHECK_SIZE()); // Bg + + arm_g2_set_color(hover ? theme->HOVER_COL : theme->BUTTON_COL); + ui_draw_rect(false, x, y, UI_CHECK_SIZE(), UI_CHECK_SIZE()); // Bg + + if (selected) { // Check + arm_g2_set_color(hover ? theme->TEXT_COL : theme->LABEL_COL); + if (!current->enabled) { + ui_fade_color(0.25); + } + int size = UI_CHECK_SELECT_SIZE(); + arm_g2_draw_scaled_render_target(¤t->check_select_image, x + current->check_select_offset_x, y + current->check_select_offset_y, size, size); + } +} + +void ui_draw_radio(bool selected, bool hover) { + float x = current->_x + current->radio_offset_x; + float y = current->_y + current->radio_offset_y; + arm_g2_set_color(selected ? theme->HIGHLIGHT_COL : hover ? theme->HOVER_COL : theme->BUTTON_COL); + arm_g2_draw_render_target(¤t->radio_image, x, y); // Circle bg + + if (selected) { // Check + arm_g2_set_color(theme->LABEL_COL); + if (!current->enabled) { + ui_fade_color(0.25); + } + arm_g2_draw_render_target(¤t->radio_select_image, x + current->radio_select_offset_x, y + current->radio_select_offset_y); // Circle + } +} + +void ui_draw_slider(float value, float from, float to, bool filled, bool hover) { + float x = current->_x + current->button_offset_y; + float y = current->_y + current->button_offset_y; + float w = current->_w - current->button_offset_y * 2.0; + + arm_g2_set_color(theme->PRESSED_COL); + ui_draw_rect(true, x, y, w, UI_BUTTON_H()); // Bg + + if (hover) { + arm_g2_set_color(theme->HOVER_COL); + ui_draw_rect(false, x, y, w, UI_BUTTON_H()); // Bg + } + + arm_g2_set_color(hover ? theme->HOVER_COL : theme->BUTTON_COL); + float offset = (value - from) / (to - from); + float bar_w = 8.0 * UI_SCALE(); // Unfilled bar + float slider_x = filled ? x : x + (w - bar_w) * offset; + slider_x = fmax(fmin(slider_x, x + (w - bar_w)), x); + float slider_w = filled ? w * offset : bar_w; + slider_w = fmax(fmin(slider_w, w), 0); + ui_draw_rect(true, slider_x, y, slider_w, UI_BUTTON_H()); +} + +void ui_set_scale(float factor) { + current->ops->scale_factor = factor; + current->font_size = UI_FONT_SIZE(); + float font_height = arm_g2_font_height(current->ops->font->font_, current->font_size); + current->font_offset_y = (UI_ELEMENT_H() - font_height) / 2.0; // Precalculate offsets + current->arrow_offset_y = (UI_ELEMENT_H() - UI_ARROW_SIZE()) / 2.0; + current->arrow_offset_x = current->arrow_offset_y; + current->title_offset_x = (current->arrow_offset_x * 2.0 + UI_ARROW_SIZE()) / UI_SCALE(); + current->button_offset_y = (UI_ELEMENT_H() - UI_BUTTON_H()) / 2.0; + current->check_offset_y = (UI_ELEMENT_H() - UI_CHECK_SIZE()) / 2.0; + current->check_offset_x = current->check_offset_y; + current->check_select_offset_y = (UI_CHECK_SIZE() - UI_CHECK_SELECT_SIZE()) / 2.0; + current->check_select_offset_x = current->check_select_offset_y; + current->radio_offset_y = (UI_ELEMENT_H() - UI_CHECK_SIZE()) / 2.0; + current->radio_offset_x = current->radio_offset_y; + current->radio_select_offset_y = (UI_CHECK_SIZE() - UI_CHECK_SELECT_SIZE()) / 2.0; + current->radio_select_offset_x = current->radio_select_offset_y; + current->elements_baked = false; +} + +void ui_init(ui_t *ui, ui_options_t *ops) { + assert(ui_instances_count < UI_MAX_INSTANCES); + memset(ui, 0, sizeof(ui_t)); + ui_instances[ui_instances_count++] = ui; + ui->ops = ops; + ui_set_current(ui); + ui_set_scale(ops->scale_factor); + current->enabled = true; + current->scroll_enabled = true; + current->highlight_on_select = true; + current->tab_switch_enabled = true; + current->input_enabled = true; + current->current_ratio = -1; + current->image_scroll_align = true; + current->window_ended = true; + current->restore_x = -1; + current->restore_y = -1; + if (ui_combo_search_handle == NULL) { + ui_combo_search_handle = ui_handle_create(); + gc_root(ui_combo_search_handle); + } +} + +void ui_begin(ui_t *ui) { + ui_set_current(ui); + if (!current->elements_baked) { + ui_bake_elements(); + } + current->changed = false; + current->_x = 0; // Reset cursor + current->_y = 0; + current->_w = 0; + current->_h = 0; +} + +// Sticky region ignores window scrolling +void ui_begin_sticky() { + current->sticky = true; + current->_y -= current->current_window->scroll_offset; + if (current->current_window->scroll_enabled) { + current->_w += UI_SCROLL_W(); // Use full width since there is no scroll bar in sticky region + } +} + +void ui_end_sticky() { + arm_g2_end(); + current->sticky = false; + current->scissor = true; + kinc_g4_scissor(0, current->_y, current->_window_w, current->_window_h - current->_y); + current->window_header_h += current->_y - current->window_header_h; + current->_y += current->current_window->scroll_offset; + current->is_hovered = false; + if (current->current_window->scroll_enabled) { + current->_w -= UI_SCROLL_W(); + } +} + +void ui_end_window(bool bind_global_g) { + ui_handle_t *handle = current->current_window; + if (handle == NULL) return; + if (handle->redraws > 0 || current->is_scrolling) { + if (current->scissor) { + current->scissor = false; + kinc_g4_disable_scissor(); + } + + if (current->tab_count > 0) { + ui_draw_tabs(); + } + + if (handle->drag_enabled) { // Draggable header + arm_g2_set_color(theme->SEPARATOR_COL); + arm_g2_fill_rect(0, 0, current->_window_w, UI_HEADER_DRAG_H()); + } + + float window_size = handle->layout == UI_LAYOUT_VERTICAL ? current->_window_h - current->window_header_h : current->_window_w - current->window_header_w; // Exclude header + float full_size = handle->layout == UI_LAYOUT_VERTICAL ? current->_y - current->window_header_h : current->_x - current->window_header_w; + full_size -= handle->scroll_offset; + + if (full_size < window_size || !current->scroll_enabled) { // Disable scrollbar + handle->scroll_enabled = false; + handle->scroll_offset = 0; + } + else { // Draw window scrollbar if necessary + if (handle->layout == UI_LAYOUT_VERTICAL) { + handle->scroll_enabled = true; + } + if (current->tab_scroll < 0) { // Restore tab + handle->scroll_offset = current->tab_scroll; + current->tab_scroll = 0; + } + float wy = current->_window_y + current->window_header_h; + float amount_to_scroll = full_size - window_size; + float amount_scrolled = -handle->scroll_offset; + float ratio = amount_scrolled / amount_to_scroll; + float bar_h = window_size * fabs(window_size / full_size); + bar_h = fmax(bar_h, UI_ELEMENT_H()); + + float total_scrollable_area = window_size - bar_h; + float e = amount_to_scroll / total_scrollable_area; + float bar_y = total_scrollable_area * ratio + current->window_header_h; + bool bar_focus = ui_input_in_rect(current->_window_x + current->_window_w - UI_SCROLL_W(), bar_y + current->_window_y, UI_SCROLL_W(), bar_h); + + if (handle->scroll_enabled && current->input_started && bar_focus) { // Start scrolling + current->scroll_handle = handle; + current->is_scrolling = true; + } + + float scroll_delta = current->input_wheel_delta; + if (handle->scroll_enabled && ui_touch_scroll && current->input_down && current->input_dy != 0 && current->input_x > current->_window_x + current->window_header_w && current->input_y > current->_window_y + current->window_header_h) { + current->is_scrolling = true; + scroll_delta = -current->input_dy / 20.0; + } + if (handle == current->scroll_handle) { // Scroll + ui_scroll(current->input_dy * e); + } + else if (scroll_delta != 0 && current->combo_selected_handle == NULL && ui_input_in_rect(current->_window_x, wy, current->_window_w, window_size)) { // Wheel + ui_scroll(scroll_delta * UI_ELEMENT_H()); + } + + // Stay in bounds + if (handle->scroll_offset > 0) { + handle->scroll_offset = 0; + } + else if (full_size + handle->scroll_offset < window_size) { + handle->scroll_offset = window_size - full_size; + } + + if (handle->layout == UI_LAYOUT_VERTICAL) { + arm_g2_set_color(theme->BUTTON_COL); // Bar + bool scrollbar_focus = ui_input_in_rect(current->_window_x + current->_window_w - UI_SCROLL_W(), wy, UI_SCROLL_W(), window_size); + float bar_w = (scrollbar_focus || handle == current->scroll_handle) ? UI_SCROLL_W() : UI_SCROLL_MINI_W(); + ui_draw_rect(true, current->_window_w - bar_w - current->scroll_align, bar_y, bar_w, bar_h); + } + } + + handle->last_max_x = current->_x; + handle->last_max_y = current->_y; + if (handle->layout == UI_LAYOUT_VERTICAL) { + handle->last_max_x += current->_window_w; + } + else { + handle->last_max_y += current->_window_h; + } + handle->redraws--; + } + + current->window_ended = true; + + // Draw window texture + if (ui_always_redraw_window || handle->redraws > -4) { + if (bind_global_g) { + arm_g2_restore_render_target(); + } + arm_g2_set_color(0xffffffff); + arm_g2_draw_render_target(&handle->texture, current->_window_x, current->_window_y); + if (handle->redraws <= 0) { + handle->redraws--; + } + } +} + +bool ui_window_dirty(ui_handle_t *handle, int x, int y, int w, int h) { + float wx = x + handle->drag_x; + float wy = y + handle->drag_y; + float input_changed = ui_input_in_rect(wx, wy, w, h) && ui_input_changed(); + return current->always_redraw || current->is_scrolling || input_changed; +} + +bool ui_window(ui_handle_t *handle, int x, int y, int w, int h, bool drag) { + if (handle->texture.width == 0 || w != handle->texture.width || h != handle->texture.height) { + ui_resize(handle, w, h); + } + + if (!current->window_ended) { + ui_end_window(true); // End previous window if necessary + } + current->window_ended = false; + + arm_g2_set_render_target(&handle->texture); + current->current_window = handle; + current->_window_x = x + handle->drag_x; + current->_window_y = y + handle->drag_y; + current->_window_w = w; + current->_window_h = h; + current->window_header_w = 0; + current->window_header_h = 0; + + if (ui_window_dirty(handle, x, y, w, h)) { + handle->redraws = 2; + } + + if (ui_on_border_hover != NULL) { + if (ui_input_in_rect(current->_window_x - 4, current->_window_y, 8, current->_window_h)) { + ui_on_border_hover(handle, 0); + } + else if (ui_input_in_rect(current->_window_x + current->_window_w - 4, current->_window_y, 8, current->_window_h)) { + ui_on_border_hover(handle, 1); + } + else if (ui_input_in_rect(current->_window_x, current->_window_y - 4, current->_window_w, 8)) { + ui_on_border_hover(handle, 2); + } + else if (ui_input_in_rect(current->_window_x, current->_window_y + current->_window_h - 4, current->_window_w, 8)) { + ui_on_border_hover(handle, 3); + } + } + + if (handle->redraws <= 0) { + return false; + } + + if (handle->layout == UI_LAYOUT_VERTICAL) { + current->_x = 0; + current->_y = handle->scroll_offset; + } + else { + current->_x = handle->scroll_offset; + current->_y = 0; + } + if (handle->layout == UI_LAYOUT_HORIZONTAL) w = UI_ELEMENT_W(); + current->_w = !handle->scroll_enabled ? w : w - UI_SCROLL_W(); // Exclude scrollbar if present + current->_h = h; + current->tooltip_text[0] = 0; + current->tooltip_img = NULL; + current->tooltip_rt = NULL; + current->tab_count = 0; + + if (theme->FILL_WINDOW_BG) { + kinc_g4_clear(KINC_G4_CLEAR_COLOR, theme->WINDOW_BG_COL, 0, 0); + } + else { + kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0x00000000, 0, 0); + arm_g2_set_color(theme->WINDOW_BG_COL); + arm_g2_fill_rect(current->_x, current->_y - handle->scroll_offset, handle->last_max_x, handle->last_max_y); + } + + handle->drag_enabled = drag; + if (drag) { + if (current->input_started && ui_input_in_rect(current->_window_x, current->_window_y, current->_window_w, UI_HEADER_DRAG_H())) { + current->drag_handle = handle; + } + else if (current->input_released) { + current->drag_handle = NULL; + } + if (handle == current->drag_handle) { + handle->redraws = 2; + handle->drag_x += current->input_dx; + handle->drag_y += current->input_dy; + } + current->_y += UI_HEADER_DRAG_H(); // Header offset + current->window_header_h += UI_HEADER_DRAG_H(); + } + + return true; +} + +bool ui_button(char *text, int align, char *label/*, kinc_g4_texture_t *icon, int sx, int sy, int sw, int sh*/) { + if (!ui_is_visible(UI_ELEMENT_H())) { + ui_end_element(); + return false; + } + bool released = ui_get_released(UI_ELEMENT_H()); + bool pushed = ui_get_pushed(UI_ELEMENT_H()); + bool hover = ui_get_hover(UI_ELEMENT_H()); + if (released) { + current->changed = true; + } + + arm_g2_set_color(pushed ? theme->PRESSED_COL : + (!theme->FILL_BUTTON_BG && hover) ? theme->HIGHLIGHT_COL : + hover ? theme->HOVER_COL : + theme->BUTTON_COL); + + if (theme->FILL_BUTTON_BG || pushed || hover) { + ui_draw_rect(true, current->_x + current->button_offset_y, current->_y + current->button_offset_y, + current->_w - current->button_offset_y * 2, UI_BUTTON_H()); + } + + arm_g2_set_color(theme->TEXT_COL); + ui_draw_string(text, theme->TEXT_OFFSET, 0, align, true); + if (label != NULL) { + arm_g2_set_color(theme->LABEL_COL); + ui_draw_string(label, theme->TEXT_OFFSET, 0, align == UI_ALIGN_RIGHT ? UI_ALIGN_LEFT : UI_ALIGN_RIGHT, true); + } + + /* + if (icon != NULL) { + arm_g2_set_color(0xffffffff); + if (current->image_invert_y) { + arm_g2_draw_scaled_sub_image(icon, sx, sy, sw, sh, _x + current->button_offset_y, _y - 1 + sh, sw, -sh); + } + else { + arm_g2_draw_scaled_sub_image(icon, sx, sy, sw, sh, _x + current->button_offset_y, _y - 1, sw, sh); + } + } + */ + + ui_end_element(); + return released; +} + +void ui_split_text(char *lines, int align, int bg) { + int count = ui_line_count(lines); + for (int i = 0; i < count; ++i) { + ui_text(ui_extract_line(lines, i), align, bg); + } +} + +int ui_text(char *text, int align, int bg) { + if (ui_line_count(text) > 1) { + ui_split_text(text, align, bg); + return UI_STATE_IDLE; + } + float h = fmax(UI_ELEMENT_H(), arm_g2_font_height(current->ops->font->font_, current->font_size)); + if (!ui_is_visible(h)) { + ui_end_element_of_size(h + UI_ELEMENT_OFFSET()); + return UI_STATE_IDLE; + } + bool started = ui_get_started(h); + bool down = ui_get_pushed(h); + bool released = ui_get_released(h); + bool hover = ui_get_hover(h); + if (bg != 0x0000000) { + arm_g2_set_color(bg); + arm_g2_fill_rect(current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w - current->button_offset_y * 2, UI_BUTTON_H()); + } + arm_g2_set_color(theme->TEXT_COL); + ui_draw_string(text, theme->TEXT_OFFSET, 0, align, true); + + ui_end_element_of_size(h + UI_ELEMENT_OFFSET()); + return started ? UI_STATE_STARTED : released ? UI_STATE_RELEASED : down ? UI_STATE_DOWN : UI_STATE_IDLE; +} + +bool ui_tab(ui_handle_t *handle, char *text, bool vertical, uint32_t color) { + if (current->tab_count == 0) { // First tab + current->tab_handle = handle; + current->tab_vertical = vertical; + current->_w -= current->tab_vertical ? UI_ELEMENT_OFFSET() + UI_ELEMENT_W() - 1 * UI_SCALE() : 0; // Shrink window area by width of vertical tabs + if (vertical) { + current->window_header_w += UI_ELEMENT_W(); + } + else { + current->window_header_h += UI_BUTTON_H() + current->button_offset_y + UI_ELEMENT_OFFSET(); + } + current->restore_x = current->input_x; // Mouse in tab header, disable clicks for tab content + current->restore_y = current->input_y; + if (!vertical && ui_input_in_rect(current->_window_x, current->_window_y, current->_window_w, current->window_header_h)) { + current->input_x = current->input_y = -1; + } + if (vertical) { + current->_x += current->window_header_w + 6; + current->_w -= 6; + } + else { + current->_y += current->window_header_h + 3; + } + } + assert(current->tab_count < 16); + strcpy(current->tab_names[current->tab_count], text); + current->tab_colors[current->tab_count] = color; + current->tab_enabled[current->tab_count] = current->enabled; + current->tab_count++; + return handle->position == current->tab_count - 1; +} + +bool ui_panel(ui_handle_t *handle, char *text, bool is_tree, bool filled) { + if (!ui_is_visible(UI_ELEMENT_H())) { + ui_end_element(); + return handle->selected; + } + if (ui_get_released(UI_ELEMENT_H())) { + handle->selected = !handle->selected; + handle->changed = current->changed = true; + } + + if (is_tree) { + ui_draw_tree(handle->selected); + } + else { + ui_draw_arrow(handle->selected); + } + + arm_g2_set_color(theme->LABEL_COL); // Title + ui_draw_string(text, current->title_offset_x, 0, UI_ALIGN_LEFT, true); + + ui_end_element(); + return handle->selected; +} + +static int image_width(void *image, bool is_rt) { + if (is_rt) { + return ((kinc_g4_render_target_t *)image)->width; + } + else { + return ((kinc_g4_texture_t *)image)->tex_width; + } +} + +static int image_height(void *image, bool is_rt) { + if (is_rt) { + return ((kinc_g4_render_target_t *)image)->height; + } + else { + return ((kinc_g4_texture_t *)image)->tex_height; + } +} + +static void draw_scaled_image(void *image, bool is_rt, float dx, float dy, float dw, float dh) { + if (is_rt) { + arm_g2_draw_scaled_render_target((kinc_g4_render_target_t *)image, dx, dy, dw, dh); + } + else { + arm_g2_draw_scaled_image((kinc_g4_texture_t *)image, dx, dy, dw, dh); + } +} + +static void draw_scaled_sub_image(void *image, bool is_rt, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh) { + if (is_rt) { + arm_g2_draw_scaled_sub_render_target((kinc_g4_render_target_t *)image, sx, sy, sw, sh, dx, dy, dw, dh); + } + else { + arm_g2_draw_scaled_sub_image((kinc_g4_texture_t *)image, sx, sy, sw, sh, dx, dy, dw, dh); + } +} + +int ui_sub_image(/*kinc_g4_texture_t kinc_g4_render_target_t*/ void *image, bool is_rt, uint32_t tint, int h, int sx, int sy, int sw, int sh) { + float iw = (sw > 0 ? sw : image_width(image, is_rt)) * UI_SCALE(); + float ih = (sh > 0 ? sh : image_height(image, is_rt)) * UI_SCALE(); + float w = fmin(iw, current->_w); + float x = current->_x; + float scroll = current->current_window != NULL ? current->current_window->scroll_enabled : false; + float r = current->current_ratio == -1 ? 1.0 : ui_get_ratio(current->ratios->buffer[current->current_ratio], 1); + if (current->image_scroll_align) { // Account for scrollbar size + w = fmin(iw, current->_w - current->button_offset_y * 2.0); + x += current->button_offset_y; + if (!scroll) { + w -= UI_SCROLL_W() * r; + x += UI_SCROLL_W() * r / 2.0; + } + } + else if (scroll) { + w += UI_SCROLL_W() * r; + } + + // Image size + float ratio = h == -1 ? + w / iw : + h / ih; + if (h == -1) { + h = ih * ratio; + } + else { + w = iw * ratio; + } + + if (!ui_is_visible(h)) { + ui_end_element_of_size(h); + return UI_STATE_IDLE; + } + bool started = ui_get_started(h); + bool down = ui_get_pushed(h); + bool released = ui_get_released(h); + bool hover = ui_get_hover(h); + + // Limit input to image width + // if (current->current_ratio == -1 && (started || down || released || hover)) { + // if (current->input_x < current->_window_x + current->_x || current->input_x > current->_window_x + current->_x + w) { + // started = down = released = hover = false; + // } + // } + + arm_g2_set_color(tint); + if (!current->enabled) { + ui_fade_color(0.25); + } + if (sw > 0) { // Source rect specified + if (current->image_invert_y) { + draw_scaled_sub_image(image, is_rt, sx, sy, sw, sh, x, current->_y + h, w, -h); + } + else { + draw_scaled_sub_image(image, is_rt, sx, sy, sw, sh, x, current->_y, w, h); + } + } + else { + if (current->image_invert_y) { + draw_scaled_image(image, is_rt, x, current->_y + h, w, -h); + } + else { + draw_scaled_image(image, is_rt, x, current->_y, w, h); + } + } + + ui_end_element_of_size(h); + return started ? UI_STATE_STARTED : released ? UI_STATE_RELEASED : down ? UI_STATE_DOWN : hover ? UI_STATE_HOVERED : UI_STATE_IDLE; +} + +int ui_image(/*kinc_g4_texture_t kinc_g4_render_target_t*/ void *image, bool is_rt, uint32_t tint, int h) { + return ui_sub_image(image, is_rt, tint, h, 0, 0, image_width(image, is_rt), image_height(image, is_rt)); +} + +char *ui_text_input(ui_handle_t *handle, char *label, int align, bool editable, bool live_update) { + if (!ui_is_visible(UI_ELEMENT_H())) { + ui_end_element(); + return handle->text; + } + + bool hover = ui_get_hover(UI_ELEMENT_H()); + if (hover && ui_on_text_hover != NULL) { + ui_on_text_hover(); + } + arm_g2_set_color(hover ? theme->HOVER_COL : theme->BUTTON_COL); // Text bg + ui_draw_rect(false, current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w - current->button_offset_y * 2, UI_BUTTON_H()); + + bool released = ui_get_released(UI_ELEMENT_H()); + if (current->submit_text_handle == handle && released) { // Keep editing selected text + current->is_typing = true; + current->text_selected_handle = current->submit_text_handle; + current->submit_text_handle = NULL; + ui_set_cursor_to_input(align); + } + bool start_edit = released || current->tab_pressed; + handle->changed = false; + + if (current->text_selected_handle != handle && start_edit) { + ui_start_text_edit(handle, align); + } + if (current->text_selected_handle == handle) { + ui_update_text_edit(align, editable, live_update); + } + if (current->submit_text_handle == handle) { + ui_submit_text_edit(); + } + + if (label[0] != '\0') { + arm_g2_set_color(theme->LABEL_COL); // Label + int label_align = align == UI_ALIGN_RIGHT ? UI_ALIGN_LEFT : UI_ALIGN_RIGHT; + ui_draw_string(label, label_align == UI_ALIGN_LEFT ? theme->TEXT_OFFSET : 0, 0, label_align, true); + } + + arm_g2_set_color(theme->TEXT_COL); // Text + if (current->text_selected_handle != handle) { + ui_draw_string(handle->text, theme->TEXT_OFFSET, 0, align, true); + } + else { + ui_draw_string(current->text_selected, theme->TEXT_OFFSET, 0, align, false); + } + + ui_end_element(); + return handle->text; +} + +bool ui_check(ui_handle_t *handle, char *text, char *label) { + if (!ui_is_visible(UI_ELEMENT_H())) { + ui_end_element(); + return handle->selected; + } + if (ui_get_released(UI_ELEMENT_H())) { + handle->selected = !handle->selected; + handle->changed = current->changed = true; + } + else handle->changed = false; + + bool hover = ui_get_hover(UI_ELEMENT_H()); + ui_draw_check(handle->selected, hover); // Check + + arm_g2_set_color(theme->TEXT_COL); // Text + ui_draw_string(text, current->title_offset_x, 0, UI_ALIGN_LEFT, true); + + if (label[0] != '\0') { + arm_g2_set_color(theme->LABEL_COL); + ui_draw_string(label, theme->TEXT_OFFSET, 0, UI_ALIGN_RIGHT, true); + } + + ui_end_element(); + + return handle->selected; +} + +bool ui_radio(ui_handle_t *handle, int position, char *text, char *label) { + if (!ui_is_visible(UI_ELEMENT_H())) { + ui_end_element(); + return handle->position == position; + } + if (position == 0) { + handle->changed = false; + } + if (ui_get_released(UI_ELEMENT_H())) { + handle->position = position; + handle->changed = current->changed = true; + } + + bool hover = ui_get_hover(UI_ELEMENT_H()); + ui_draw_radio(handle->position == position, hover); // Radio + + arm_g2_set_color(theme->TEXT_COL); // Text + ui_draw_string(text, current->title_offset_x, 0, UI_ALIGN_LEFT, true); + + if (label[0] != '\0') { + arm_g2_set_color(theme->LABEL_COL); + ui_draw_string(label, theme->TEXT_OFFSET, 0, UI_ALIGN_RIGHT, true); + } + + ui_end_element(); + + return handle->position == position; +} + +int ui_combo(ui_handle_t *handle, char_ptr_array_t *texts, char *label, bool show_label, int align, bool search_bar) { + if (!ui_is_visible(UI_ELEMENT_H())) { + ui_end_element(); + return handle->position; + } + if (ui_get_released(UI_ELEMENT_H())) { + if (current->combo_selected_handle == NULL) { + current->input_enabled = false; + current->combo_selected_handle = handle; + current->combo_selected_window = current->current_window; + current->combo_selected_align = align; + current->combo_selected_texts = texts; + current->combo_selected_label = (char *)label; + current->combo_selected_x = current->_x + current->_window_x; + current->combo_selected_y = current->_y + current->_window_y + UI_ELEMENT_H(); + current->combo_selected_w = current->_w; + current->combo_search_bar = search_bar; + for (int i = 0; i < texts->length; ++i) { // Adapt combo list width to combo item width + int w = (int)arm_g2_string_width(current->ops->font->font_, current->font_size, texts->buffer[i]) + 10; + if (current->combo_selected_w < w) { + current->combo_selected_w = w; + } + } + if (current->combo_selected_w > current->_w * 2.0) { + current->combo_selected_w = current->_w * 2.0; + } + if (current->combo_selected_w > current->_w) { + current->combo_selected_w += UI_TEXT_OFFSET(); + } + current->combo_to_submit = handle->position; + current->combo_initial_value = handle->position; + } + } + if (handle == current->combo_selected_handle && (current->is_escape_down || current->input_released_r)) { + handle->position = current->combo_initial_value; + handle->changed = current->changed = true; + current->submit_combo_handle = NULL; + } + else if (handle == current->submit_combo_handle) { + handle->position = current->combo_to_submit; + current->submit_combo_handle = NULL; + handle->changed = current->changed = true; + } + else { + handle->changed = false; + } + + arm_g2_set_color(theme->PRESSED_COL); // Bg + ui_draw_rect(true, current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w - current->button_offset_y * 2, UI_BUTTON_H()); + + bool hover = ui_get_hover(UI_ELEMENT_H()); + arm_g2_set_color(hover ? theme->HOVER_COL : theme->BUTTON_COL); + ui_draw_rect(false, current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w - current->button_offset_y * 2, UI_BUTTON_H()); + + int x = current->_x + current->_w - current->arrow_offset_x - 8; + int y = current->_y + current->arrow_offset_y + 3; + + arm_g2_set_color(theme->HOVER_COL); + // if (handle == current->combo_selected_handle) { + // // Flip arrow when combo is open + // arm_g2_fill_triangle(x, y, x + UI_ARROW_SIZE(), y, x + UI_ARROW_SIZE() / 2.0, y - UI_ARROW_SIZE() / 2.0); + // } + // else { + arm_g2_fill_triangle(x, y, x + UI_ARROW_SIZE(), y, x + UI_ARROW_SIZE() / 2.0, y + UI_ARROW_SIZE() / 2.0); + // } + + if (show_label && label[0] != '\0') { + if (align == UI_ALIGN_LEFT) { + current->_x -= 15; + } + arm_g2_set_color(theme->LABEL_COL); + ui_draw_string(label, theme->TEXT_OFFSET, 0, align == UI_ALIGN_LEFT ? UI_ALIGN_RIGHT : UI_ALIGN_LEFT, true); + if (align == UI_ALIGN_LEFT) { + current->_x += 15; + } + } + + if (align == UI_ALIGN_RIGHT) { + current->_x -= 15; + } + arm_g2_set_color(theme->TEXT_COL); // Value + if (handle->position < texts->length) { + ui_draw_string(texts->buffer[handle->position], theme->TEXT_OFFSET, 0, align, true); + } + if (align == UI_ALIGN_RIGHT) { + current->_x += 15; + } + + ui_end_element(); + return handle->position; +} + +float ui_slider(ui_handle_t *handle, char *text, float from, float to, bool filled, float precision, bool display_value, int align, bool text_edit) { + static char temp[1024]; + if (!ui_is_visible(UI_ELEMENT_H())) { + ui_end_element(); + return handle->value; + } + if (ui_get_started(UI_ELEMENT_H())) { + current->scroll_handle = handle; + current->is_scrolling = true; + current->changed = handle->changed = true; + if (ui_touch_tooltip) { + current->slider_tooltip = true; + current->slider_tooltip_x = current->_x + current->_window_x; + current->slider_tooltip_y = current->_y + current->_window_y; + current->slider_tooltip_w = current->_w; + } + } + else { + handle->changed = false; + } + + #if !defined(KINC_ANDROID) && !defined(KINC_IOS) + if (handle == current->scroll_handle && current->input_dx != 0) { // Scroll + #else + if (handle == current->scroll_handle) { // Scroll + #endif + float range = to - from; + float slider_x = current->_x + current->_window_x + current->button_offset_y; + float slider_w = current->_w - current->button_offset_y * 2; + float step = range / slider_w; + float value = from + (current->input_x - slider_x) * step; + handle->value = round(value * precision) / precision; + if (handle->value < from) { + handle->value = from; // Stay in bounds + } + else if (handle->value > to) { + handle->value = to; + } + handle->changed = current->changed = true; + } + + bool hover = ui_get_hover(UI_ELEMENT_H()); + ui_draw_slider(handle->value, from, to, filled, hover); // Slider + + // Text edit + bool start_edit = (ui_get_released(UI_ELEMENT_H()) || current->tab_pressed) && text_edit; + if (start_edit) { // Mouse did not move + char tmp[256]; + sprintf(tmp, "%.2f", handle->value); + handle->text = string_copy(tmp); + string_strip_trailing_zeros(handle->text); + ui_start_text_edit(handle, UI_ALIGN_LEFT); + handle->changed = current->changed = true; + } + int lalign = align == UI_ALIGN_LEFT ? UI_ALIGN_RIGHT : UI_ALIGN_LEFT; + if (current->text_selected_handle == handle) { + ui_update_text_edit(lalign, true, false); + } + if (current->submit_text_handle == handle) { + ui_submit_text_edit(); + #ifdef WITH_EVAL + handle->value = js_eval(handle->text); + #else + handle->value = atof(handle->text); + #endif + handle->changed = current->changed = true; + } + + arm_g2_set_color(theme->LABEL_COL); // Text + ui_draw_string(text, theme->TEXT_OFFSET, 0, align, true); + + if (display_value) { + arm_g2_set_color(theme->TEXT_COL); // Value + if (current->text_selected_handle != handle) { + sprintf(temp, "%.2f", round(handle->value * precision) / precision); + string_strip_trailing_zeros(temp); + ui_draw_string(temp, theme->TEXT_OFFSET, 0, lalign, true); + } + else { + ui_draw_string(current->text_selected, theme->TEXT_OFFSET, 0, lalign, true); + } + } + + ui_end_element(); + return handle->value; +} + +void ui_separator(int h, bool fill) { + if (!ui_is_visible(UI_ELEMENT_H())) { + current->_y += h * UI_SCALE(); + return; + } + if (fill) { + arm_g2_set_color(theme->SEPARATOR_COL); + arm_g2_fill_rect(current->_x, current->_y, current->_w, h * UI_SCALE()); + } + current->_y += h * UI_SCALE(); +} + +void ui_tooltip(char *text) { + assert(strlen(text) < 512); + strcpy(current->tooltip_text, text); + current->tooltip_y = current->_y + current->_window_y; +} + +void ui_tooltip_image(kinc_g4_texture_t *image, int max_width) { + current->tooltip_img = image; + current->tooltip_img_max_width = max_width; + current->tooltip_invert_y = current->image_invert_y; + current->tooltip_y = current->_y + current->_window_y; +} + +void ui_tooltip_render_target(kinc_g4_render_target_t *image, int max_width) { + current->tooltip_rt = image; + current->tooltip_img_max_width = max_width; + current->tooltip_invert_y = current->image_invert_y; + current->tooltip_y = current->_y + current->_window_y; +} + +void ui_end(bool last) { + if (!current->window_ended) { + ui_end_window(true); + } + ui_draw_combo(true); // Handle active combo + ui_draw_tooltip(true); + current->tab_pressed_handle = NULL; + if (last) { + ui_end_input(); + } +} + +void ui_set_input_position(ui_t *ui, int x, int y) { + ui->input_dx += x - ui->input_x; + ui->input_dy += y - ui->input_y; + ui->input_x = x; + ui->input_y = y; +} + +// Useful for drag and drop operations +char *ui_hovered_tab_name() { + return ui_input_in_rect(current->hovered_tab_x, current->hovered_tab_y, current->hovered_tab_w, current->hovered_tab_h) ? current->hovered_tab_name : ""; +} + +void ui_mouse_down(ui_t *ui, int button, int x, int y) { + if (ui->pen_in_use) { + return; + } + if (button == 0) { + ui->input_started = ui->input_down = true; + } + else { + ui->input_started_r = ui->input_down_r = true; + } + ui->input_started_time = kinc_time(); + #if defined(KINC_ANDROID) || defined(KINC_IOS) + ui_set_input_position(ui, x, y); + #endif + ui->input_started_x = x; + ui->input_started_y = y; +} + +void ui_mouse_move(ui_t *ui, int x, int y, int movement_x, int movement_y) { + #if !defined(KINC_ANDROID) && !defined(KINC_IOS) + ui_set_input_position(ui, x, y); + #endif +} + +void ui_mouse_up(ui_t *ui, int button, int x, int y) { + if (ui->pen_in_use) { + return; + } + + if (ui->touch_hold_activated) { + ui->touch_hold_activated = false; + return; + } + + if (ui->is_scrolling) { // Prevent action when scrolling is active + ui->is_scrolling = false; + ui->scroll_handle = NULL; + ui->slider_tooltip = false; + if (x == ui->input_started_x && y == ui->input_started_y) { // Mouse not moved + if (button == 0) ui->input_released = true; + else ui->input_released_r = true; + } + } + else { + if (button == 0) { + ui->input_released = true; + } + else { + ui->input_released_r = true; + } + } + + if (button == 0) { + ui->input_down = false; + } + else { + ui->input_down_r = false; + } + #if defined(KINC_ANDROID) || defined(KINC_IOS) + ui_set_input_position(ui, x, y); + #endif + ui_deselect_text(ui); +} + +void ui_mouse_wheel(ui_t *ui, int delta) { + ui->input_wheel_delta = delta; +} + +void ui_pen_down(ui_t *ui, int x, int y, float pressure) { + #if defined(KINC_ANDROID) || defined(KINC_IOS) + return; + #endif + + ui_mouse_down(ui, 0, x, y); +} + +void ui_pen_up(ui_t *ui, int x, int y, float pressure) { + #if defined(KINC_ANDROID) || defined(KINC_IOS) + return; + #endif + + if (ui->input_started) { + ui->input_started = false; + ui->pen_in_use = true; + return; + } + ui_mouse_up(ui, 0, x, y); + ui->pen_in_use = true; // On pen release, additional mouse down & up events are fired at once - filter those out +} + +void ui_pen_move(ui_t *ui, int x, int y, float pressure) { + #if defined(KINC_IOS) + // Listen to pen hover if no other input is active + if (pressure == 0.0) { + if (!ui->input_down && !ui->input_down_r) { + ui_set_input_position(ui, x, y); + } + return; + } + #endif + + #if defined(KINC_ANDROID) || defined(KINC_IOS) + return; + #endif + + ui_mouse_move(ui, x, y, 0, 0); +} + +void ui_key_down(ui_t *ui, int key_code) { + ui->key_code = key_code; + ui->is_key_pressed = true; + ui->is_key_down = true; + ui_key_repeat_time = kinc_time() + 0.4; + switch (key_code) { + case KINC_KEY_SHIFT: ui->is_shift_down = true; break; + case KINC_KEY_CONTROL: ui->is_ctrl_down = true; break; + #ifdef KINC_DARWIN + case KINC_KEY_META: ui->is_ctrl_down = true; break; + #endif + case KINC_KEY_ALT: ui->is_alt_down = true; break; + case KINC_KEY_BACKSPACE: ui->is_backspace_down = true; break; + case KINC_KEY_DELETE: ui->is_delete_down = true; break; + case KINC_KEY_ESCAPE: ui->is_escape_down = true; break; + case KINC_KEY_RETURN: ui->is_return_down = true; break; + case KINC_KEY_TAB: ui->is_tab_down = true; break; + case KINC_KEY_A: ui->is_a_down = true; break; + case KINC_KEY_SPACE: ui->key_char = ' '; break; + #ifdef UI_ANDROID_RMB // Detect right mouse button on Android.. + case KINC_KEY_BACK: if (!ui->input_down_r) ui_mouse_down(ui, 1, ui->input_x, ui->input_y); break; + #endif + } +} + +void ui_key_up(ui_t *ui, int key_code) { + ui->is_key_down = false; + switch (key_code) { + case KINC_KEY_SHIFT: ui->is_shift_down = false; break; + case KINC_KEY_CONTROL: ui->is_ctrl_down = false; break; + #ifdef KINC_DARWIN + case KINC_KEY_META: ui->is_ctrl_down = false; break; + #endif + case KINC_KEY_ALT: ui->is_alt_down = false; break; + case KINC_KEY_BACKSPACE: ui->is_backspace_down = false; break; + case KINC_KEY_DELETE: ui->is_delete_down = false; break; + case KINC_KEY_ESCAPE: ui->is_escape_down = false; break; + case KINC_KEY_RETURN: ui->is_return_down = false; break; + case KINC_KEY_TAB: ui->is_tab_down = false; break; + case KINC_KEY_A: ui->is_a_down = false; break; + #ifdef UI_ANDROID_RMB + case KINC_KEY_BACK: ui_mouse_down(ui, 1, ui->input_x, ui->input_y); break; + #endif + } +} + +void ui_key_press(ui_t *ui, unsigned key_char) { + ui->key_char = key_char; + ui->is_key_pressed = true; +} + +#if defined(KINC_ANDROID) || defined(KINC_IOS) +static float ui_pinch_distance = 0.0; +static float ui_pinch_total = 0.0; +static bool ui_pinch_started = false; + +void ui_touch_down(ui_t *ui, int index, int x, int y) { + // Reset movement delta on touch start + if (index == 0) { + ui->input_dx = 0; + ui->input_dy = 0; + ui->input_x = x; + ui->input_y = y; + } + // Two fingers down - right mouse button + else if (index == 1) { + ui->input_down = false; + ui_mouse_down(ui, 1, ui->input_x, ui->input_y); + ui_pinch_started = true; + ui_pinch_total = 0.0; + ui_pinch_distance = 0.0; + } + // Three fingers down - middle mouse button + else if (index == 2) { + ui->input_down_r = false; + ui_mouse_down(ui, 2, ui->input_x, ui->input_y); + } +} + +void ui_touch_up(ui_t *ui, int index, int x, int y) { + if (index == 1) { + ui_mouse_up(ui, 1, ui->input_x, ui->input_y); + } +} + +void ui_touch_move(ui_t *ui, int index, int x, int y) { + if (index == 0) { + ui_set_input_position(ui, x, y); + } + + // Pinch to zoom - mouse wheel + if (index == 1) { + float last_distance = ui_pinch_distance; + float dx = ui->input_x - x; + float dy = ui->input_y - y; + ui_pinch_distance = sqrtf(dx * dx + dy * dy); + ui_pinch_total += last_distance != 0 ? last_distance - ui_pinch_distance : 0; + if (!ui_pinch_started) { + ui->input_wheel_delta = ui_pinch_total / 50; + if (ui->input_wheel_delta != 0) { + ui_pinch_total = 0.0; + } + } + ui_pinch_started = false; + } +} +#endif + +char *ui_copy() { + ui_is_copy = true; + return &ui_text_to_copy[0]; +} + +char *ui_cut() { + ui_is_cut = true; + return ui_copy(); +} + +void ui_paste(char *s) { + ui_is_paste = true; + strcpy(ui_text_to_paste, s); +} + +void ui_theme_default(ui_theme_t *t) { + t->WINDOW_BG_COL = 0xff292929; + t->HOVER_COL = 0xff434343; + t->ACCENT_COL = 0xff606060; + t->BUTTON_COL = 0xff383838; + t->PRESSED_COL = 0xff222222; + t->TEXT_COL = 0xffe8e8e8; + t->LABEL_COL = 0xffc8c8c8; + t->SEPARATOR_COL = 0xff202020; + t->HIGHLIGHT_COL = 0xff205d9c; + t->FONT_SIZE = 13; + t->ELEMENT_W = 100; + t->ELEMENT_H = 24; + t->ELEMENT_OFFSET = 4; + t->ARROW_SIZE = 5; + t->BUTTON_H = 22; + t->CHECK_SIZE = 16; + t->CHECK_SELECT_SIZE = 12; + t->SCROLL_W = 9; + t->SCROLL_MINI_W = 3; + t->TEXT_OFFSET = 8; + t->TAB_W = 6; + t->FILL_WINDOW_BG = true; + t->FILL_BUTTON_BG = true; + t->LINK_STYLE = UI_LINK_STYLE_LINE; + t->FULL_TABS = false; + t->ROUND_CORNERS = true; + t->SHADOWS = true; +} diff --git a/armorcore/sources/iron_ui.h b/armorcore/sources/iron_ui.h new file mode 100644 index 000000000..505c43a2a --- /dev/null +++ b/armorcore/sources/iron_ui.h @@ -0,0 +1,365 @@ + +// Immediate Mode UI Library +// https://github.com/armory3d/armorcore + +#pragma once + +#include +#include +#include +#include "iron_armpack.h" + +#define UI_LAYOUT_VERTICAL 0 +#define UI_LAYOUT_HORIZONTAL 1 +#define UI_ALIGN_LEFT 0 +#define UI_ALIGN_CENTER 1 +#define UI_ALIGN_RIGHT 2 +#define UI_STATE_IDLE 0 +#define UI_STATE_STARTED 1 +#define UI_STATE_DOWN 2 +#define UI_STATE_RELEASED 3 +#define UI_STATE_HOVERED 4 +#define UI_LINK_STYLE_LINE 0 +#define UI_LINK_STYLE_CUBIC_BEZIER 1 + +typedef struct ui_theme { + int WINDOW_BG_COL; + int HOVER_COL; + int ACCENT_COL; + int BUTTON_COL; + int PRESSED_COL; + int TEXT_COL; + int LABEL_COL; + int SEPARATOR_COL; + int HIGHLIGHT_COL; + int FONT_SIZE; + int ELEMENT_W; + int ELEMENT_H; + int ELEMENT_OFFSET; + int ARROW_SIZE; + int BUTTON_H; + int CHECK_SIZE; + int CHECK_SELECT_SIZE; + int SCROLL_W; + int SCROLL_MINI_W; + int TEXT_OFFSET; + int TAB_W; // Indentation + /*bool*/int FILL_WINDOW_BG; + /*bool*/int FILL_BUTTON_BG; + int LINK_STYLE; + /*bool*/int FULL_TABS; // Make tabs take full window width + /*bool*/int ROUND_CORNERS; + /*bool*/int SHADOWS; +} ui_theme_t; + +typedef struct ui_options { + g2_font_t *font; + ui_theme_t *theme; + float scale_factor; + kinc_g4_texture_t *color_wheel; + kinc_g4_texture_t *black_white_gradient; +} ui_options_t; + +typedef struct ui_handle_array { + struct ui_handle **buffer; + int length; + int capacity; +} ui_handle_array_t; + +typedef struct ui_handle { + bool selected; + int position; + uint32_t color; + float value; + char *text; + kinc_g4_render_target_t texture; + int redraws; + float scroll_offset; + bool scroll_enabled; + int layout; + float last_max_x; + float last_max_y; + bool drag_enabled; + int drag_x; + int drag_y; + bool changed; + bool init; + ui_handle_array_t *children; +} ui_handle_t; + +typedef struct ui_text_extract { + char colored[128]; + char uncolored[128]; +} ui_text_extract_t; + +typedef struct ui_coloring { + uint32_t color; + char_ptr_array_t *start; + char *end; + bool separated; +} ui_coloring_t; + +typedef struct ui_coloring_array { + ui_coloring_t **buffer; + int length; + int capacity; +} ui_coloring_array_t; + +typedef struct ui_text_coloring { + ui_coloring_array_t *colorings; + uint32_t default_color; +} ui_text_coloring_t; + +typedef struct ui { + bool is_scrolling; // Use to limit other activities + bool is_typing; + bool enabled; // Current element state + bool is_started; + bool is_pushed; + bool is_hovered; + bool is_released; + bool changed; // Global elements change check + bool image_invert_y; + bool scroll_enabled; + bool always_redraw; // Hurts performance + bool highlight_on_select; // Highlight text edit contents on selection + bool tab_switch_enabled; // Allow switching focus to the next element by pressing tab + ui_text_coloring_t *text_coloring; // Set coloring scheme for ui_draw_string() calls + int window_border_top; + int window_border_bottom; + int window_border_left; + int window_border_right; + char hovered_tab_name[64]; + float hovered_tab_x; + float hovered_tab_y; + float hovered_tab_w; + float hovered_tab_h; + bool touch_hold_activated; + bool slider_tooltip; + float slider_tooltip_x; + float slider_tooltip_y; + float slider_tooltip_w; + bool input_enabled; + float input_x; // Input position + float input_y; + float input_started_x; + float input_started_y; + float input_dx; // Delta + float input_dy; + float input_wheel_delta; + bool input_started; // Buttons + bool input_started_r; + bool input_released; + bool input_released_r; + bool input_down; + bool input_down_r; + bool pen_in_use; + bool is_key_pressed; // Keys + bool is_key_down; + bool is_shift_down; + bool is_ctrl_down; + bool is_alt_down; + bool is_a_down; + bool is_backspace_down; + bool is_delete_down; + bool is_escape_down; + bool is_return_down; + bool is_tab_down; + int key_code; + int key_char; + float input_started_time; + int cursor_x; // Text input + int highlight_anchor; + f32_array_t *ratios; // Splitting rows + int current_ratio; + float x_before_split; + int w_before_split; + + ui_options_t *ops; + int font_size; + float font_offset_y; // Precalculated offsets + float arrow_offset_x; + float arrow_offset_y; + float title_offset_x; + float button_offset_y; + float check_offset_x; + float check_offset_y; + float check_select_offset_x; + float check_select_offset_y; + float radio_offset_x; + float radio_offset_y; + float radio_select_offset_x; + float radio_select_offset_y; + float scroll_align; + bool image_scroll_align; + + float _x; // Cursor(stack) position + float _y; + int _w; + int _h; + + float _window_x; // Window state + float _window_y; + float _window_w; + float _window_h; + ui_handle_t *current_window; + bool window_ended; + ui_handle_t *scroll_handle; // Window or slider being scrolled + ui_handle_t *drag_handle; // Window being dragged + ui_handle_t *drag_tab_handle; // Tab being dragged + int drag_tab_position; + float window_header_w; + float window_header_h; + float restore_x; + float restore_y; + + ui_handle_t *text_selected_handle; + char text_selected[256]; + ui_handle_t *submit_text_handle; + char text_to_submit[256]; + bool tab_pressed; + ui_handle_t *tab_pressed_handle; + ui_handle_t *combo_selected_handle; + ui_handle_t *combo_selected_window; + int combo_selected_align; + char_ptr_array_t *combo_selected_texts; + char *combo_selected_label; + int combo_selected_x; + int combo_selected_y; + int combo_selected_w; + bool combo_search_bar; + ui_handle_t *submit_combo_handle; + int combo_to_submit; + int combo_initial_value; + char tooltip_text[512]; + kinc_g4_texture_t *tooltip_img; + kinc_g4_render_target_t *tooltip_rt; + int tooltip_img_max_width; + bool tooltip_invert_y; + float tooltip_x; + float tooltip_y; + bool tooltip_shown; + bool tooltip_wait; + double tooltip_time; + char tab_names[16][64]; + uint32_t tab_colors[16]; + bool tab_enabled[16]; + int tab_count; // Number of tab calls since window begin + ui_handle_t *tab_handle; + float tab_scroll; + bool tab_vertical; + bool sticky; + bool scissor; + + bool elements_baked; + kinc_g4_render_target_t check_select_image; + kinc_g4_render_target_t radio_image; + kinc_g4_render_target_t radio_select_image; + kinc_g4_render_target_t round_corner_image; + kinc_g4_render_target_t filled_round_corner_image; +} ui_t; + +void ui_init(ui_t *ui, ui_options_t *ops); +void ui_begin(ui_t *ui); +void ui_begin_sticky(); +void ui_end_sticky(); +void ui_begin_region(ui_t *ui, int x, int y, int w); +void ui_end_region(bool last); +bool ui_window(ui_handle_t *handle, int x, int y, int w, int h, bool drag); // Returns true if redraw is needed +bool ui_button(char *text, int align, char *label); +int ui_text(char *text, int align, int bg); +bool ui_tab(ui_handle_t *handle, char *text, bool vertical, uint32_t color); +bool ui_panel(ui_handle_t *handle, char *text, bool is_tree, bool filled); +int ui_sub_image(/*kinc_g4_texture_t kinc_g4_render_target_t*/ void *image, bool is_rt, uint32_t tint, int h, int sx, int sy, int sw, int sh); +int ui_image(/*kinc_g4_texture_t kinc_g4_render_target_t*/ void *image, bool is_rt, uint32_t tint, int h); +char *ui_text_input(ui_handle_t *handle, char *label, int align, bool editable, bool live_update); +bool ui_check(ui_handle_t *handle, char *text, char *label); +bool ui_radio(ui_handle_t *handle, int position, char *text, char *label); +int ui_combo(ui_handle_t *handle, char_ptr_array_t *texts, char *label, bool show_label, int align, bool search_bar); +float ui_slider(ui_handle_t *handle, char *text, float from, float to, bool filled, float precision, bool display_value, int align, bool text_edit); +void ui_row(f32_array_t *ratios); +void ui_separator(int h, bool fill); +void ui_tooltip(char *text); +void ui_tooltip_image(kinc_g4_texture_t *image, int max_width); +void ui_tooltip_render_target(kinc_g4_render_target_t *image, int max_width); +void ui_end(bool last); +void ui_end_window(bool bind_global_g); +char *ui_hovered_tab_name(); +void ui_set_hovered_tab_name(char *name); +void ui_mouse_down(ui_t *ui, int button, int x, int y); // Input events +void ui_mouse_move(ui_t *ui, int x, int y, int movement_x, int movement_y); +void ui_mouse_up(ui_t *ui, int button, int x, int y); +void ui_mouse_wheel(ui_t *ui, int delta); +void ui_pen_down(ui_t *ui, int x, int y, float pressure); +void ui_pen_up(ui_t *ui, int x, int y, float pressure) ; +void ui_pen_move(ui_t *ui, int x, int y, float pressure); +void ui_key_down(ui_t *ui, int key_code); +void ui_key_up(ui_t *ui, int key_code); +void ui_key_press(ui_t *ui, unsigned character); +#if defined(KINC_ANDROID) || defined(KINC_IOS) +void ui_touch_down(ui_t *ui, int index, int x, int y); +void ui_touch_up(ui_t *ui, int index, int x, int y); +void ui_touch_move(ui_t *ui, int index, int x, int y); +#endif +char *ui_copy(); +char *ui_cut(); +void ui_paste(char *s); +void ui_theme_default(ui_theme_t *t); +ui_t *ui_get_current(); +void ui_set_current(ui_t *current); +ui_handle_t *ui_handle_create(); +ui_handle_t *ui_nest(ui_handle_t *handle, int pos); +void ui_set_scale(float factor); + +bool ui_get_hover(float elem_h); +bool ui_get_released(float elem_h); +bool ui_input_in_rect(float x, float y, float w, float h); +void ui_fill(float x, float y, float w, float h, uint32_t color); +void ui_rect(float x, float y, float w, float h, uint32_t color, float strength); +int ui_line_count(char *str); +char *ui_extract_line(char *str, int line); +bool ui_is_visible(float elem_h); +void ui_end_element(); +void ui_end_element_of_size(float element_size); +void ui_end_input(); +void ui_fade_color(float alpha); +void ui_draw_string(char *text, float x_offset, float y_offset, int align, bool truncation); +void ui_draw_shadow(float x, float y, float w, float h); +void ui_draw_rect(bool fill, float x, float y, float w, float h); +void ui_draw_round_bottom(float x, float y, float w); +void ui_start_text_edit(ui_handle_t *handle, int align); +void ui_remove_char_at(char *str, int at); +void ui_remove_chars_at(char *str, int at, int count); +void ui_insert_char_at(char *str, int at, char c); +void ui_insert_chars_at(char *str, int at, char *cs); + +float UI_SCALE(); +float UI_ELEMENT_W(); +float UI_ELEMENT_H(); +float UI_ELEMENT_OFFSET(); +float UI_ARROW_SIZE(); +float UI_BUTTON_H(); +float UI_CHECK_SIZE(); +float UI_CHECK_SELECT_SIZE(); +float UI_FONT_SIZE(); +float UI_SCROLL_W(); +float UI_TEXT_OFFSET(); +float UI_TAB_W(); +float UI_HEADER_DRAG_H(); +float UI_FLASH_SPEED(); +float UI_TOOLTIP_DELAY(); + +#define UI_MAX_INSTANCES 8 +extern ui_t *ui_instances[UI_MAX_INSTANCES]; +extern int ui_instances_count; +extern bool ui_always_redraw_window; +extern bool ui_touch_scroll; +extern bool ui_touch_hold; +extern bool ui_touch_tooltip; +extern bool ui_is_cut; +extern bool ui_is_copy; +extern bool ui_is_paste; +extern void (*ui_on_border_hover)(ui_handle_t *, int); +extern void (*ui_on_text_hover)(void); +extern void (*ui_on_deselect_text)(void); +extern void (*ui_on_tab_drop)(ui_handle_t *, int, ui_handle_t *, int); diff --git a/armorcore/sources/iron_ui_ext.c b/armorcore/sources/iron_ui_ext.c new file mode 100644 index 000000000..b26ebfc5c --- /dev/null +++ b/armorcore/sources/iron_ui_ext.c @@ -0,0 +1,729 @@ +#include "iron_ui_ext.h" +#include +#include +#include +#include +#include +#include +#include "iron_string.h" + +#define MATH_PI 3.14159265358979323846 + +static char data_path[128] = ""; +static char last_path[512]; +static ui_handle_t *wheel_selected_handle = NULL; +static ui_handle_t *gradient_selected_handle = NULL; +static ui_handle_t radio_handle; +static int _ELEMENT_OFFSET = 0; +static int _BUTTON_COL = 0; +static int text_area_selection_start = -1; +bool ui_text_area_line_numbers = false; +bool ui_text_area_scroll_past_end = false; +ui_text_coloring_t *ui_text_area_coloring = NULL; + +float ui_dist(float x1, float y1, float x2, float y2) { + float vx = x1 - x2; + float vy = y1 - y2; + return sqrtf(vx * vx + vy * vy); +} + +float ui_fract(float f) { + return f - (int)f; +} + +float ui_mix(float x, float y, float a) { + return x * (1.0 - a) + y * a; +} + +float ui_clamp(float x, float min_val, float max_val) { + return fmin(fmax(x, min_val), max_val); +} + +float ui_step(float edge, float x) { + return x < edge ? 0.0 : 1.0; +} + +static const float kx = 1.0; +static const float ky = 2.0 / 3.0; +static const float kz = 1.0 / 3.0; +static const float kw = 3.0; +static float ar[] = {0.0, 0.0, 0.0}; +void ui_hsv_to_rgb(float cr, float cg, float cb, float *out) { + float px = fabs(ui_fract(cr + kx) * 6.0 - kw); + float py = fabs(ui_fract(cr + ky) * 6.0 - kw); + float pz = fabs(ui_fract(cr + kz) * 6.0 - kw); + out[0] = cb * ui_mix(kx, ui_clamp(px - kx, 0.0, 1.0), cg); + out[1] = cb * ui_mix(kx, ui_clamp(py - kx, 0.0, 1.0), cg); + out[2] = cb * ui_mix(kx, ui_clamp(pz - kx, 0.0, 1.0), cg); +} + +static const float Kx = 0.0; +static const float Ky = -1.0 / 3.0; +static const float Kz = 2.0 / 3.0; +static const float Kw = -1.0; +static const float e = 1.0e-10; +void ui_rgb_to_hsv(float cr, float cg, float cb, float *out) { + float px = ui_mix(cb, cg, ui_step(cb, cg)); + float py = ui_mix(cg, cb, ui_step(cb, cg)); + float pz = ui_mix(Kw, Kx, ui_step(cb, cg)); + float pw = ui_mix(Kz, Ky, ui_step(cb, cg)); + float qx = ui_mix(px, cr, ui_step(px, cr)); + float qy = ui_mix(py, py, ui_step(px, cr)); + float qz = ui_mix(pw, pz, ui_step(px, cr)); + float qw = ui_mix(cr, px, ui_step(px, cr)); + float d = qx - fmin(qw, qy); + out[0] = fabs(qz + (qw - qy) / (6.0 * d + e)); + out[1] = d / (qx + e); + out[2] = qx; +} + +float ui_float_input(ui_handle_t *handle, char *label, int align, float precision) { + sprintf(handle->text, "%f", round(handle->value * precision) / precision); + char *text = ui_text_input(handle, label, align, true, false); + handle->value = atof(text); + return handle->value; +} + +void ui_init_path(ui_handle_t *handle, const char *system_id) { + strcpy(handle->text, strcmp(system_id, "Windows") == 0 ? "C:\\Users" : "/"); + // %HOMEDRIVE% + %HomePath% + // ~ +} + +char *ui_file_browser(ui_handle_t *handle, bool folders_only) { + ui_t *current = ui_get_current(); + const char *sep = "/"; + + char cmd[64]; + strcpy(cmd, "ls "); + const char *system_id = kinc_system_id(); + if (strcmp(system_id, "Windows") == 0) { + strcpy(cmd, "dir /b "); + if (folders_only) { + strcat(cmd, "/ad "); + } + sep = "\\"; + handle->text = string_replace_all(handle->text, "\\\\", "\\"); + handle->text = string_replace_all(handle->text, "\r", ""); + } + if (handle->text[0] == '\0') { + ui_init_path(handle, system_id); + } + + char save[256]; + strcpy(save, kinc_internal_get_files_location()); + strcat(save, sep); + strcat(save, data_path); + strcat(save, "dir.txt"); + if (strcmp(handle->text, last_path) != 0) { + char str[512]; + strcpy(str, cmd); + strcat(str, "\""); + strcat(str, handle->text); + strcat(str, "\" > \""); + strcat(str, save); + strcat(str, "\""); + } + strcpy(last_path, handle->text); + + kinc_file_reader_t reader; + if (!kinc_file_reader_open(&reader, save, KINC_FILE_TYPE_ASSET)) { + return NULL; + } + int reader_size = (int)kinc_file_reader_size(&reader); + + char str[2048]; // reader_size + kinc_file_reader_read(&reader, str, reader_size); + kinc_file_reader_close(&reader); + + // Up directory + int i1 = strstr(handle->text, "/") - handle->text; + int i2 = strstr(handle->text, "\\") - handle->text; + bool nested = + (i1 > -1 && strlen(handle->text) - 1 > i1) || + (i2 > -1 && strlen(handle->text) - 1 > i2); + handle->changed = false; + if (nested && ui_button("..", UI_ALIGN_LEFT, "")) { + handle->changed = current->changed = true; + handle->text[strrchr(handle->text, sep[0]) - handle->text] = 0; + // Drive root + if (strlen(handle->text) == 2 && handle->text[1] == ':') { + strcat(handle->text, sep); + } + } + + // Directory contents + int count = ui_line_count(str); + for (int i = 0; i < count; ++i) { + char *f = ui_extract_line(str, i); + if (f[0] == '\0' || f[0] == '.') continue; // Skip hidden + if (ui_button(f, UI_ALIGN_LEFT, "")) { + handle->changed = current->changed = true; + if (handle->text[strlen(handle->text) - 1] != sep[0]) { + strcat(handle->text, sep); + } + strcat(handle->text, f); + } + } + + return handle->text; +} + +int ui_inline_radio(ui_handle_t *handle, char_ptr_array_t *texts, int align) { + ui_t *current = ui_get_current(); + + if (!ui_is_visible(UI_ELEMENT_H())) { + ui_end_element(); + return handle->position; + } + float step = current->_w / texts->length; + int hovered = -1; + if (ui_get_hover(UI_ELEMENT_H())) { + int ix = current->input_x - current->_x - current->_window_x; + for (int i = 0; i < texts->length; ++i) { + if (ix < i * step + step) { + hovered = i; + break; + } + } + } + if (ui_get_released(UI_ELEMENT_H())) { + handle->position = hovered; + handle->changed = current->changed = true; + } + else { + handle->changed = false; + } + + for (int i = 0; i < texts->length; ++i) { + if (handle->position == i) { + arm_g2_set_color(current->ops->theme->HIGHLIGHT_COL); + if (!current->enabled) { + ui_fade_color(0.25); + } + ui_draw_rect(true, current->_x + step * i, current->_y + current->button_offset_y, step, UI_BUTTON_H()); + } + else if (hovered == i) { + arm_g2_set_color(current->ops->theme->BUTTON_COL); + if (!current->enabled) { + ui_fade_color(0.25); + } + ui_draw_rect(false, current->_x + step * i, current->_y + current->button_offset_y, step, UI_BUTTON_H()); + } + arm_g2_set_color(current->ops->theme->TEXT_COL); // Text + current->_x += step * i; + float _w = current->_w; + current->_w = (int)step; + ui_draw_string(texts->buffer[i], current->ops->theme->TEXT_OFFSET, 0, align, true); + current->_x -= step * i; + current->_w = _w; + } + ui_end_element(); + return handle->position; +} + +uint8_t ui_color_r(uint32_t color) { + return (color & 0x00ff0000) >> 16; +} + +uint8_t ui_color_g(uint32_t color) { + return (color & 0x0000ff00) >> 8; +} + +uint8_t ui_color_b(uint32_t color) { + return (color & 0x000000ff); +} + +uint8_t ui_color_a(uint32_t color) { + return (color) >> 24; +} + +uint32_t ui_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return (a << 24) | (r << 16) | (g << 8) | b; +} + +int ui_color_wheel(ui_handle_t *handle, bool alpha, float w, float h, bool color_preview, void (*picker)(void *), void *data) { + ui_t *current = ui_get_current(); + if (w < 0) w = current->_w; + float r = ui_color_r(handle->color) / 255.0f; + float g = ui_color_g(handle->color) / 255.0f; + float b = ui_color_b(handle->color) / 255.0f; + ui_rgb_to_hsv(r, g, b, ar); + float chue = ar[0]; + float csat = ar[1]; + float cval = ar[2]; + float calpha = ui_color_a(handle->color) / 255.0f; + + // Wheel + float px = current->_x; + float py = current->_y; + bool scroll = current->current_window != NULL ? current->current_window->scroll_enabled : false; + if (!scroll) { + w -= UI_SCROLL_W(); + px += UI_SCROLL_W() / 2.0; + } + float _x = current->_x; + float _y = current->_y; + float _w = current->_w; + current->_w = (int)(28.0 * UI_SCALE()); + if (picker != NULL && ui_button("P", UI_ALIGN_CENTER, "")) { + (*picker)(data); + current->changed = false; + handle->changed = false; + return handle->color; + } + current->_x = _x; + current->_y = _y; + current->_w = _w; + + uint32_t col = ui_color(round(cval * 255.0f), round(cval * 255.0f), round(cval * 255.0f), 255); + ui_image(current->ops->color_wheel, false, col, -1); + + // Picker + float ph = current->_y - py; + float ox = px + w / 2.0; + float oy = py + ph / 2.0; + float cw = w * 0.7; + float cwh = cw / 2.0; + float cx = ox; + float cy = oy + csat * cwh; // Sat is distance from center + float grad_tx = px + 0.897 * w; + float grad_ty = oy - cwh; + float grad_w = 0.0777 * w; + float grad_h = cw; + // Rotate around origin by hue + float theta = chue * (MATH_PI * 2.0); + float cx2 = cos(theta) * (cx - ox) - sin(theta) * (cy - oy) + ox; + float cy2 = sin(theta) * (cx - ox) + cos(theta) * (cy - oy) + oy; + cx = cx2; + cy = cy2; + + current->_x = px - (scroll ? 0 : UI_SCROLL_W() / 2.0); + current->_y = py; + ui_image(current->ops->black_white_gradient, false, 0xffffffff, -1); + + arm_g2_set_color(0xff000000); + arm_g2_fill_rect(cx - 3.0 * UI_SCALE(), cy - 3.0 * UI_SCALE(), 6.0 * UI_SCALE(), 6.0 * UI_SCALE()); + arm_g2_set_color(0xffffffff); + arm_g2_fill_rect(cx - 2.0 * UI_SCALE(), cy - 2.0 * UI_SCALE(), 4.0 * UI_SCALE(), 4.0 * UI_SCALE()); + + arm_g2_set_color(0xff000000); + arm_g2_fill_rect(grad_tx + grad_w / 2.0 - 3.0 * UI_SCALE(), grad_ty + (1.0 - cval) * grad_h - 3.0 * UI_SCALE(), 6.0 * UI_SCALE(), 6.0 * UI_SCALE()); + arm_g2_set_color(0xffffffff); + arm_g2_fill_rect(grad_tx + grad_w / 2.0 - 2.0 * UI_SCALE(), grad_ty + (1.0 - cval) * grad_h - 2.0 * UI_SCALE(), 4.0 * UI_SCALE(), 4.0 * UI_SCALE()); + + if (alpha) { + ui_handle_t *alpha_handle = ui_nest(handle, 1); + if (alpha_handle->init) { + alpha_handle->value = round(calpha * 100.0) / 100.0; + } + calpha = ui_slider(alpha_handle, "Alpha", 0.0, 1.0, true, 100, true, UI_ALIGN_LEFT, true); + if (alpha_handle->changed) handle->changed = current->changed = true; + } + // Mouse picking for color wheel + float gx = ox + current->_window_x; + float gy = oy + current->_window_y; + if (current->input_started && ui_input_in_rect(gx - cwh, gy - cwh, cw, cw)) { + wheel_selected_handle = handle; + } + if (current->input_released && wheel_selected_handle != NULL) { + wheel_selected_handle = NULL; + handle->changed = current->changed = true; + } + if (current->input_down && wheel_selected_handle == handle) { + csat = fmin(ui_dist(gx, gy, current->input_x, current->input_y), cwh) / cwh; + float angle = atan2(current->input_x - gx, current->input_y - gy); + if (angle < 0) { + angle = MATH_PI + (MATH_PI - fabs(angle)); + } + angle = MATH_PI * 2.0 - angle; + chue = angle / (MATH_PI * 2.0); + handle->changed = current->changed = true; + } + // Mouse picking for cval + if (current->input_started && ui_input_in_rect(grad_tx + current->_window_x, grad_ty + current->_window_y, grad_w, grad_h)) { + gradient_selected_handle = handle; + } + if (current->input_released && gradient_selected_handle != NULL) { + gradient_selected_handle = NULL; + handle->changed = current->changed = true; + } + if (current->input_down && gradient_selected_handle == handle) { + cval = fmax(0.01, fmin(1.0, 1.0 - (current->input_y - grad_ty - current->_window_y) / grad_h)); + handle->changed = current->changed = true; + } + // Save as rgb + ui_hsv_to_rgb(chue, csat, cval, ar); + handle->color = ui_color(round(ar[0] * 255.0), round(ar[1] * 255.0), round(ar[2] * 255.0), round(calpha * 255.0)); + + if (color_preview) { + ui_text("", UI_ALIGN_RIGHT, handle->color); + } + + char *strings[] = {"RGB", "HSV", "Hex"}; + char_ptr_array_t car; + car.buffer = strings; + car.length = 3; + int pos = ui_inline_radio(&radio_handle, &car, UI_ALIGN_LEFT); + + ui_handle_t *h0 = ui_nest(ui_nest(handle, 0), 0); + ui_handle_t *h1 = ui_nest(ui_nest(handle, 0), 1); + ui_handle_t *h2 = ui_nest(ui_nest(handle, 0), 2); + if (pos == 0) { + h0->value = ui_color_r(handle->color) / 255.0f; + float r = ui_slider(h0, "R", 0, 1, true, 100, true, UI_ALIGN_LEFT, true); + h1->value = ui_color_g(handle->color) / 255.0f; + float g = ui_slider(h1, "G", 0, 1, true, 100, true, UI_ALIGN_LEFT, true); + h2->value = ui_color_b(handle->color) / 255.0f; + float b = ui_slider(h2, "B", 0, 1, true, 100, true, UI_ALIGN_LEFT, true); + handle->color = ui_color(r * 255.0, g * 255.0, b * 255.0, 255.0); + } + else if (pos == 1) { + ui_rgb_to_hsv(ui_color_r(handle->color) / 255.0f, ui_color_g(handle->color) / 255.0f, ui_color_b(handle->color) / 255.0f, ar); + h0->value = ar[0]; + h1->value = ar[1]; + h2->value = ar[2]; + float chue = ui_slider(h0, "H", 0, 1, true, 100, true, UI_ALIGN_LEFT, true); + float csat = ui_slider(h1, "S", 0, 1, true, 100, true, UI_ALIGN_LEFT, true); + float cval = ui_slider(h2, "V", 0, 1, true, 100, true, UI_ALIGN_LEFT, true); + ui_hsv_to_rgb(chue, csat, cval, ar); + handle->color = ui_color(ar[0] * 255.0, ar[1] * 255.0, ar[2] * 255.0, 255.0); + } + else if (pos == 2) { + char tmp[16]; + handle->text = tmp; + sprintf(handle->text, "%x", handle->color); + char *hex_code = ui_text_input(handle, "#", UI_ALIGN_LEFT, true, false); + if (strlen(hex_code) >= 1 && hex_code[0] == '#') { // Allow # at the beginning + hex_code = strcpy(hex_code, hex_code + 1); + } + if (strlen(hex_code) == 3) { // 3 digit CSS style values like fa0 --> ffaa00 + hex_code[5] = hex_code[2]; + hex_code[4] = hex_code[2]; + hex_code[3] = hex_code[1]; + hex_code[2] = hex_code[1]; + hex_code[1] = hex_code[0]; + hex_code[6] = '\0'; + } + if (strlen(hex_code) == 4) { // 4 digit CSS style values + hex_code[7] = hex_code[3]; + hex_code[6] = hex_code[3]; + hex_code[5] = hex_code[2]; + hex_code[4] = hex_code[2]; + hex_code[3] = hex_code[1]; + hex_code[2] = hex_code[1]; + hex_code[1] = hex_code[0]; + hex_code[8] = '\0'; + } + if (strlen(hex_code) == 6) { // Make the alpha channel optional + hex_code[7] = hex_code[5]; + hex_code[6] = hex_code[4]; + hex_code[5] = hex_code[3]; + hex_code[4] = hex_code[2]; + hex_code[3] = hex_code[1]; + hex_code[2] = hex_code[0]; + hex_code[0] = 'f'; + hex_code[1] = 'f'; + } + #ifdef _WIN32 + handle->color = _strtoi64(hex_code, NULL, 16); + #else + handle->color = strtol(hex_code, NULL, 16); + #endif + } + if (h0->changed || h1->changed || h2->changed) { + handle->changed = current->changed = true; + } + + // Do not close if user clicks + if (current->input_released && ui_input_in_rect(current->_window_x + px, current->_window_y + py, w, h < 0 ? (current->_y - py) : h) && current->input_released) { + current->changed = true; + } + + return handle->color; +} + +static void scroll_align(ui_t *current, ui_handle_t *handle) { + // Scroll down + if ((handle->position + 1) * UI_ELEMENT_H() + current->current_window->scroll_offset > current->_h - current->window_header_h) { + current->current_window->scroll_offset -= UI_ELEMENT_H(); + } + // Scroll up + else if ((handle->position + 1) * UI_ELEMENT_H() + current->current_window->scroll_offset < current->window_header_h) { + current->current_window->scroll_offset += UI_ELEMENT_H(); + } +} + +static char *right_align_number(char *s, int number, int length) { + sprintf(s, "%d", number); + while (strlen(s) < length) { + for (int i = strlen(s) + 1; i > 0; --i) s[i] = s[i - 1]; + s[0] = ' '; + } + return s; +} + +static void handle_line_select(ui_t *current, ui_handle_t *handle) { + if (current->is_shift_down) { + current->highlight_anchor = 0; + if (text_area_selection_start == -1) { + text_area_selection_start = handle->position; + } + } + else text_area_selection_start = -1; +} + +static int ui_word_count(char *str) { + if (str == NULL || str[0] == '\0') { + return 0; + } + int i = 0; + int count = 1; + while (str[i] != '\0') { + if (str[i] == ' ' || str[i] == '\n') { + count++; + } + i++; + } + return count; +} + +static char temp[128]; + +static char *ui_extract_word(char *str, int word) { + int pos = 0; + int len = strlen(str); + int word_i = 0; + for (int i = 0; i < len; ++i) { + if (str[i] == ' ' || str[i] == '\n') { + word_i++; + continue; + } + if (word_i < word) { + continue; + } + if (word_i > word) { + break; + } + temp[pos++] = str[i]; + } + temp[pos] = 0; + return temp; +} + +static int ui_line_pos(char *str, int line) { + int i = 0; + int current_line = 0; + while (str[i] != '\0' && current_line < line) { + if (str[i] == '\n') { + current_line++; + } + i++; + } + return i; +} + +char *ui_text_area(ui_handle_t *handle, int align, bool editable, char *label, bool word_wrap) { + ui_t *current = ui_get_current(); + handle->text = string_replace_all(handle->text, "\t", " "); + bool selected = current->text_selected_handle == handle; // Text being edited + + char lines[512]; + strcpy(lines, handle->text); + int line_count = ui_line_count(lines); + bool show_label = (line_count == 1 && lines[0] == '\0'); + bool key_pressed = selected && current->is_key_pressed; + current->highlight_on_select = false; + current->tab_switch_enabled = false; + + if (word_wrap && handle->text[0] != '\0') { + bool cursor_set = false; + int cursor_pos = current->cursor_x; + for (int i = 0; i < handle->position; ++i) { + cursor_pos += strlen(ui_extract_line(lines, i)) + 1; // + '\n' + } + int word_count = ui_word_count(lines); + char line[256]; + line[0] = '\0'; + char new_lines[512]; + new_lines[0] = '\0'; + for (int i = 0; i < word_count; ++i) { + char *w = ui_extract_word(lines, i); + float spacew = arm_g2_string_width(current->ops->font->font_, current->font_size, " "); + float wordw = spacew + arm_g2_string_width(current->ops->font->font_, current->font_size, w); + float linew = wordw + arm_g2_string_width(current->ops->font->font_, current->font_size, line); + if (linew > current->_w - 10 && linew > wordw) { + if (new_lines[0] != '\0') strcat(new_lines, "\n"); + strcat(new_lines, line); + line[0] = '\0'; + } + + if (line[0] == '\0') { + strcpy(line, w); + } + else { + strcat(line, " "); + strcat(line, w); + } + + int new_line_count = new_lines[0] == '\0' ? 0 : ui_line_count(new_lines); + int lines_len = new_line_count; + for (int i = 0; i < new_line_count; ++i) { + lines_len += strlen(ui_extract_line(new_lines, i)); + } + + if (selected && !cursor_set && cursor_pos <= lines_len + strlen(line)) { + cursor_set = true; + handle->position = new_line_count; + current->cursor_x = current->highlight_anchor = cursor_pos - lines_len; + } + } + if (new_lines[0] != '\0') { + strcat(new_lines, "\n"); + } + strcat(new_lines, line); + if (selected) { + strcpy(handle->text, ui_extract_line(new_lines, handle->position)); + strcpy(current->text_selected, handle->text); + } + strcpy(lines, new_lines); + } + int cursor_start_x = current->cursor_x; + + if (ui_text_area_line_numbers) { + float _y = current->_y; + int _TEXT_COL = current->ops->theme->TEXT_COL; + current->ops->theme->TEXT_COL = current->ops->theme->BUTTON_COL; + int max_length = ceil(log(line_count + 0.5) / log(10)); // Express log_10 with natural log + char s[64]; + for (int i = 0; i < line_count; ++i) { + ui_text(right_align_number(&s[0], i + 1, max_length), UI_ALIGN_LEFT, 0x00000000); + current->_y -= UI_ELEMENT_OFFSET(); + } + current->ops->theme->TEXT_COL = _TEXT_COL; + current->_y = _y; + + sprintf(s, "%d", line_count); + float numbers_w = (strlen(s) * 16 + 4) * UI_SCALE(); + current->_x += numbers_w; + current->_w -= numbers_w - UI_SCROLL_W(); + } + + arm_g2_set_color(current->ops->theme->SEPARATOR_COL); // Background + ui_draw_rect(true, current->_x + current->button_offset_y, current->_y + current->button_offset_y, current->_w - current->button_offset_y * 2, line_count * UI_ELEMENT_H() - current->button_offset_y * 2); + + ui_text_coloring_t *_text_coloring = current->text_coloring; + current->text_coloring = ui_text_area_coloring; + + if (current->input_started) { + text_area_selection_start = -1; + } + + for (int i = 0; i < line_count; ++i) { // Draw lines + char *line = ui_extract_line(lines, i); + // Text input + if ((!selected && ui_get_hover(UI_ELEMENT_H())) || (selected && i == handle->position)) { + handle->position = i; // Set active line + strcpy(handle->text, line); + current->submit_text_handle = NULL; + ui_text_input(handle, show_label ? label : "", align, editable, false); + if (key_pressed && current->key_code != KINC_KEY_RETURN && current->key_code != KINC_KEY_ESCAPE) { // Edit text + int line_pos = ui_line_pos(lines, i); + ui_remove_chars_at(lines, line_pos, strlen(line)); + strcpy(line, current->text_selected); + ui_insert_chars_at(lines, line_pos, line); + } + } + // Text + else { + if (show_label) { + int TEXT_COL = current->ops->theme->TEXT_COL; + current->ops->theme->TEXT_COL = current->ops->theme->LABEL_COL; + ui_text(label, UI_ALIGN_RIGHT, 0x00000000); + current->ops->theme->TEXT_COL = TEXT_COL; + } + else { + // Multi-line selection highlight + if (text_area_selection_start > -1 && + (i >= text_area_selection_start && i < handle->position) || + (i <= text_area_selection_start && i > handle->position)) { + int line_height = UI_ELEMENT_H(); + int cursor_height = line_height - current->button_offset_y * 3.0; + int linew = arm_g2_string_width(current->ops->font->font_, current->font_size, line); + arm_g2_set_color(current->ops->theme->ACCENT_COL); + arm_g2_fill_rect(current->_x + UI_ELEMENT_OFFSET() * 2.0, current->_y + current->button_offset_y * 1.5, linew, cursor_height); + } + ui_text(line, align, 0x00000000); + } + } + current->_y -= UI_ELEMENT_OFFSET(); + } + current->_y += UI_ELEMENT_OFFSET(); + current->text_coloring = _text_coloring; + + if (ui_text_area_scroll_past_end) { + current->_y += current->_h - current->window_header_h - UI_ELEMENT_H() - UI_ELEMENT_OFFSET(); + } + + if (key_pressed) { + // Move cursor vertically + if (current->key_code == KINC_KEY_DOWN && handle->position < line_count - 1) { + handle_line_select(current, handle); + handle->position++; + scroll_align(current, handle); + } + if (current->key_code == KINC_KEY_UP && handle->position > 0) { + handle_line_select(current, handle); + handle->position--; + scroll_align(current, handle); + } + // New line + if (editable && current->key_code == KINC_KEY_RETURN && !word_wrap) { + handle->position++; + ui_insert_char_at(lines, ui_line_pos(lines, handle->position - 1) + current->cursor_x, '\n'); + ui_start_text_edit(handle, UI_ALIGN_LEFT); + current->cursor_x = current->highlight_anchor = 0; + scroll_align(current, handle); + } + // Delete line + if (editable && current->key_code == KINC_KEY_BACKSPACE && cursor_start_x == 0 && handle->position > 0) { + handle->position--; + current->cursor_x = current->highlight_anchor = strlen(ui_extract_line(lines, handle->position)); + ui_remove_chars_at(lines, ui_line_pos(lines, handle->position + 1) - 1, 1); // Remove '\n' of the previous line + scroll_align(current, handle); + } + strcpy(current->text_selected, ui_extract_line(lines, handle->position)); + } + + current->highlight_on_select = true; + current->tab_switch_enabled = true; + handle->text = string_copy(lines); + return handle->text; +} + +float UI_MENUBAR_H() { + ui_t *current = ui_get_current(); + return UI_BUTTON_H() * 1.1 + 2.0 + current->button_offset_y; +} + +void ui_begin_menu() { + ui_t *current = ui_get_current(); + _ELEMENT_OFFSET = current->ops->theme->ELEMENT_OFFSET; + _BUTTON_COL = current->ops->theme->BUTTON_COL; + current->ops->theme->ELEMENT_OFFSET = 0; + current->ops->theme->BUTTON_COL = current->ops->theme->SEPARATOR_COL; + arm_g2_set_color(current->ops->theme->SEPARATOR_COL); + arm_g2_fill_rect(0, 0, current->_window_w, UI_MENUBAR_H()); +} + +void ui_end_menu() { + ui_t *current = ui_get_current(); + current->ops->theme->ELEMENT_OFFSET = _ELEMENT_OFFSET; + current->ops->theme->BUTTON_COL = _BUTTON_COL; +} + +bool _ui_menu_button(char *text) { + ui_t *current = ui_get_current(); + current->_w = arm_g2_string_width(current->ops->font->font_, current->font_size, text) + 25.0 * UI_SCALE(); + return ui_button(text, UI_ALIGN_CENTER, ""); +} diff --git a/armorcore/sources/iron_ui_ext.h b/armorcore/sources/iron_ui_ext.h new file mode 100644 index 000000000..a85c83958 --- /dev/null +++ b/armorcore/sources/iron_ui_ext.h @@ -0,0 +1,24 @@ +#pragma once + +#include "iron_ui.h" + +float ui_float_input(ui_handle_t *handle, char *label, int align, float precision); +char *ui_file_browser(ui_handle_t *handle, bool folders_only); +int ui_inline_radio(ui_handle_t *handle, char_ptr_array_t *texts, int align); +int ui_color_wheel(ui_handle_t *handle, bool alpha, float w, float h, bool color_preview, void (*picker)(void *), void *data); +char *ui_text_area(ui_handle_t *handle, int align, bool editable, char *label, bool word_wrap); +void ui_begin_menu(); +void ui_end_menu(); +bool _ui_menu_button(char *text); +void ui_hsv_to_rgb(float cr, float cg, float cb, float *out); +void ui_rgb_to_hsv(float cr, float cg, float cb, float *out); + +uint8_t ui_color_r(uint32_t color); +uint8_t ui_color_g(uint32_t color); +uint8_t ui_color_b(uint32_t color); +uint8_t ui_color_a(uint32_t color); +uint32_t ui_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + +extern bool ui_text_area_line_numbers; +extern bool ui_text_area_scroll_past_end; +extern ui_text_coloring_t *ui_text_area_coloring; diff --git a/armorcore/sources/iron_ui_nodes.c b/armorcore/sources/iron_ui_nodes.c new file mode 100644 index 000000000..eabd07bae --- /dev/null +++ b/armorcore/sources/iron_ui_nodes.c @@ -0,0 +1,1578 @@ +#include "iron_ui_nodes.h" + +#include +#include +#include +#include +#include +#include +#include +#include "iron_ui.h" +#include "iron_ui_ext.h" +#include "iron_armpack.h" +#include "iron_json.h" +#include "iron_gc.h" + +static ui_nodes_t *current_nodes = NULL; +static bool ui_nodes_elements_baked = false; +static kinc_g4_render_target_t ui_socket_image; +static bool ui_box_select = false; +static int ui_box_select_x = 0; +static int ui_box_select_y = 0; +static const int ui_max_buttons = 9; +static void (*ui_on_header_released)(ui_node_t *) = NULL; +static void (*ui_nodes_on_node_remove)(ui_node_t *) = NULL; +static int ui_node_id = -1; + +char *ui_clipboard = ""; +char_ptr_array_t *ui_nodes_exclude_remove = NULL; // No removal for listed node types +bool ui_nodes_socket_released = false; +char_ptr_array_t *(*ui_nodes_enum_texts)(char *) = NULL; // Retrieve combo items for buttons of type ENUM +void (*ui_nodes_on_custom_button)(int, char *) = NULL; // Call external function +ui_canvas_control_t *(*ui_nodes_on_canvas_control)(void) = NULL; +void (*ui_nodes_on_canvas_released)(void) = NULL; +void (*ui_nodes_on_socket_released)(int) = NULL; +void (*ui_nodes_on_link_drag)(int, bool) = NULL; + +int ui_popup_x = 0; +int ui_popup_y = 0; +int ui_popup_w = 0; +int ui_popup_h = 0; +void (*ui_popup_commands)(ui_t *, void *, void *) = NULL; +void *ui_popup_data; +void *ui_popup_data2; +int ui_popup_handle_node_id = -1; +int ui_popup_handle_node_socket_id = -1; + +char *ui_tr(char *id/*, map vars*/) { + return id; +} + +void ui_nodes_init(ui_nodes_t *nodes) { + current_nodes = nodes; + memset(current_nodes, 0, sizeof(ui_nodes_t)); + current_nodes->zoom = 1.0; + current_nodes->scale_factor = 1.0; + current_nodes->ELEMENT_H = 25.0; + current_nodes->snap_from_id = -1; + current_nodes->snap_to_id = -1; + current_nodes->link_drag_id = -1; + current_nodes->nodes_selected_id = gc_alloc(sizeof(i32_array_t)); + current_nodes->handle = ui_handle_create(); +} + +float UI_NODES_SCALE() { + return current_nodes->scale_factor * current_nodes->zoom; +} + +float UI_NODES_PAN_X() { + float zoom_pan = (1.0 - current_nodes->zoom) * current_nodes->uiw / 2.5; + return current_nodes->pan_x * UI_NODES_SCALE() + zoom_pan; +} + +float UI_NODES_PAN_Y() { + float zoom_pan = (1.0 - current_nodes->zoom) * current_nodes->uih / 2.5; + return current_nodes->pan_y * UI_NODES_SCALE() + zoom_pan; +} + +float UI_LINE_H() { + return current_nodes->ELEMENT_H * UI_NODES_SCALE(); +} + +float UI_BUTTONS_H(ui_node_t *node) { + float h = 0.0; + for (int i = 0; i < node->buttons->length; ++i) { + ui_node_button_t *but = node->buttons->buffer[i]; + if (strcmp(but->type, "RGBA") == 0) { + h += 102.0 * UI_NODES_SCALE() + UI_LINE_H() * 5.0; // Color wheel + controls + } + else if (strcmp(but->type, "VECTOR") == 0) { + h += UI_LINE_H() * 4.0; + } + else if (strcmp(but->type, "CUSTOM") == 0) { + h += UI_LINE_H() * but->height; + } + else { + h += UI_LINE_H(); + } + } + return h; +} + +float UI_OUTPUTS_H(int sockets_count, int length) { + float h = 0.0; + for (int i = 0; i < (length < 0 ? sockets_count : length); ++i) { + h += UI_LINE_H(); + } + return h; +} + +bool ui_input_linked(ui_node_canvas_t *canvas, int node_id, int i) { + for (int x = 0; x < canvas->links->length; ++x) { + ui_node_link_t *l = canvas->links->buffer[x]; + if (l->to_id == node_id && l->to_socket == i) { + return true; + } + } + return false; +} + +float UI_INPUTS_H(ui_node_canvas_t *canvas, ui_node_socket_t **sockets, int sockets_count, int length) { + float h = 0.0; + for (int i = 0; i < (length < 0 ? sockets_count : length); ++i) { + if (strcmp(sockets[i]->type, "VECTOR") == 0 && sockets[i]->display == 1 && !ui_input_linked(canvas, sockets[i]->node_id, i)) { + h += UI_LINE_H() * 4; + } + else { + h += UI_LINE_H(); + } + } + return h; +} + +float UI_NODE_H(ui_node_canvas_t *canvas, ui_node_t *node) { + return UI_LINE_H() * 1.2 + UI_INPUTS_H(canvas, node->inputs->buffer, node->inputs->length, -1) + UI_OUTPUTS_H(node->outputs->length, -1) + UI_BUTTONS_H(node); +} + +float UI_NODE_W(ui_node_t *node) { + return (node->width != 0 ? node->width : 140.0) * UI_NODES_SCALE(); +} + +float UI_NODE_X(ui_node_t *node) { + return node->x * UI_NODES_SCALE() + UI_NODES_PAN_X(); +} + +float UI_NODE_Y(ui_node_t *node) { + return node->y * UI_NODES_SCALE() + UI_NODES_PAN_Y(); +} + +float UI_INPUT_Y(ui_node_canvas_t *canvas, ui_node_socket_t **sockets, int sockets_count, int pos) { + return UI_LINE_H() * 1.62 + UI_INPUTS_H(canvas, sockets, sockets_count, pos); +} + +float UI_OUTPUT_Y(int sockets_count, int pos) { + return UI_LINE_H() * 1.62 + UI_OUTPUTS_H(sockets_count, pos); +} + +float ui_p(float f) { + return f * UI_NODES_SCALE(); +} + +ui_node_t *ui_get_node(ui_node_array_t *nodes, int id) { + for (int i = 0; i < nodes->length; ++i) { + if (nodes->buffer[i]->id == id) { + return nodes->buffer[i]; + } + } + return NULL; +} + +int ui_get_node_index(ui_node_t **nodes, int nodes_count, int id) { + for (int i = 0; i < nodes_count; ++i) { + if (nodes[i]->id == id) { + return i; + } + } + return -1; +} + +int ui_next_node_id(ui_node_array_t *nodes) { + if (ui_node_id == -1) { + for (int i = 0; i < nodes->length; ++i) { + if (ui_node_id < nodes->buffer[i]->id) { + ui_node_id = nodes->buffer[i]->id; + } + } + } + return ++ui_node_id; +} + +ui_node_link_t *ui_get_link(ui_node_link_array_t *links, int id) { + for (int i = 0; i < links->length; ++i) { + if (links->buffer[i]->id == id) { + return links->buffer[i]; + } + } + return NULL; +} + +int ui_get_link_index(ui_node_link_array_t *links, int id) { + for (int i = 0; i < links->length; ++i) { + if (links->buffer[i]->id == id) { + return i; + } + } + return -1; +} + +int ui_next_link_id(ui_node_link_array_t *links) { + int id = 0; + for (int i = 0; i < links->length; ++i) { + if (links->buffer[i]->id >= id) { + id = links->buffer[i]->id + 1; + } + } + return id; +} + +int ui_get_socket_id(ui_node_array_t *nodes) { + int id = 0; + for (int i = 0; i < nodes->length; ++i) { + ui_node_t *n = nodes->buffer[i]; + for (int j = 0; j < n->inputs->length; ++j) { + if (n->inputs->buffer[j]->id >= id) { + id = n->inputs->buffer[j]->id + 1; + } + } + for (int j = 0; j < n->outputs->length; ++j) { + if (n->outputs->buffer[j]->id >= id) { + id = n->outputs->buffer[j]->id + 1; + } + } + } + return id; +} + +void ui_nodes_bake_elements() { + if (ui_socket_image.width != 0) { + kinc_g4_render_target_destroy(&ui_socket_image); + } + kinc_g4_render_target_init(&ui_socket_image, 24, 24, KINC_G4_RENDER_TARGET_FORMAT_32BIT, 0, 0); + arm_g2_set_render_target(&ui_socket_image); + kinc_g4_clear(KINC_G4_CLEAR_COLOR, 0x00000000, 0, 0); + + arm_g2_set_color(0xff111111); + arm_g2_fill_circle(12, 12, 11, 0); + arm_g2_set_color(0xffffffff); + arm_g2_fill_circle(12, 12, 9, 0); + + arm_g2_restore_render_target(); + ui_nodes_elements_baked = true; +} + +ui_canvas_control_t *ui_on_default_canvas_control() { + ui_t *current = ui_get_current(); + static ui_canvas_control_t c; + c.pan_x = current->input_down_r ? current->input_dx : 0.0; + c.pan_y = current->input_down_r ? current->input_dy : 0.0; + c.zoom = -current->input_wheel_delta / 10.0; + return &c; +} + +void ui_draw_link(float x1, float y1, float x2, float y2, bool highlight) { + ui_t *current = ui_get_current(); + int c1 = current->ops->theme->LABEL_COL; + int c2 = current->ops->theme->ACCENT_COL; + int c = highlight ? c1 : c2; + arm_g2_set_color(ui_color(ui_color_r(c), ui_color_g(c), ui_color_b(c), 210)); + if (current->ops->theme->LINK_STYLE == UI_LINK_STYLE_LINE) { + arm_g2_draw_line(x1, y1, x2, y2, 1.0); + arm_g2_set_color(ui_color(ui_color_r(c), ui_color_g(c), ui_color_b(c), 150)); // AA + arm_g2_draw_line(x1 + 0.5, y1, x2 + 0.5, y2, 1.0); + arm_g2_draw_line(x1 - 0.5, y1, x2 - 0.5, y2, 1.0); + arm_g2_draw_line(x1, y1 + 0.5, x2, y2 + 0.5, 1.0); + arm_g2_draw_line(x1, y1 - 0.5, x2, y2 - 0.5, 1.0); + } + else if (current->ops->theme->LINK_STYLE == UI_LINK_STYLE_CUBIC_BEZIER) { + float x[] = { x1, x1 + fabs(x1 - x2) / 2.0, x2 - fabs(x1 - x2) / 2.0, x2 }; + float y[] = { y1, y1, y2, y2 }; + arm_g2_draw_cubic_bezier(x, y, 30, highlight ? 2.0 : 1.0); + } +} + +static void ui_remove_link_at(ui_node_canvas_t *canvas, int at) { + canvas->links->buffer[at] = NULL; + for (int i = at; i < canvas->links->length - 1; ++i) { + canvas->links->buffer[i] = canvas->links->buffer[i + 1]; + } + canvas->links->length--; +} + +static void ui_remove_node_at(ui_node_canvas_t *canvas, int at) { + canvas->nodes->buffer[at] = NULL; + for (int i = at; i < canvas->nodes->length - 1; ++i) { + canvas->nodes->buffer[i] = canvas->nodes->buffer[i + 1]; + } + canvas->nodes->length--; +} + +void ui_remove_node(ui_node_t *n, ui_node_canvas_t *canvas) { + if (n == NULL) return; + int i = 0; + while (i < canvas->links->length) { + ui_node_link_t *l = canvas->links->buffer[i]; + if (l->from_id == n->id || l->to_id == n->id) { + ui_remove_link_at(canvas, i); + } + else { + i++; + } + } + ui_remove_node_at(canvas, ui_get_node_index(canvas->nodes->buffer, canvas->nodes->length, n->id)); + if (ui_nodes_on_node_remove != NULL) { + (*ui_nodes_on_node_remove)(n); + } +} + +bool ui_is_selected(ui_node_t *node) { + for (int i = 0; i < current_nodes->nodes_selected_id->length; ++i) { + if (current_nodes->nodes_selected_id->buffer[i] == node->id) { + return true; + } + } + return false; +} + +static void remove_from_selection(ui_node_t *node) { + i32_array_remove(current_nodes->nodes_selected_id, node->id); +} + +static void add_to_selection(ui_node_t *node) { + i32_array_push(current_nodes->nodes_selected_id, node->id); +} + +void ui_popup(int x, int y, int w, int h, void (*commands)(ui_t *, void *, void *), void *data, void *data2) { + ui_popup_x = x; + ui_popup_y = y; + ui_popup_w = w; + ui_popup_h = h; + ui_popup_commands = commands; + ui_popup_data = data; + ui_popup_data2 = data2; +} + +static void color_picker_callback(uint32_t color) { + ui_t *current = ui_get_current(); + float *val = (float *)current_nodes->color_picker_callback_data; + val[0] = ui_color_r(color) / 255.0f; + val[1] = ui_color_g(color) / 255.0f; + val[2] = ui_color_b(color) / 255.0f; + current->changed = true; +} + +void ui_color_wheel_picker(void *data) { + current_nodes->color_picker_callback_data = data; + current_nodes->color_picker_callback = &color_picker_callback; +} + +static void rgba_popup_commands(ui_t *ui, void *data, void *data2) { + ui_handle_t *nhandle = (ui_handle_t *)data; + float *val = (float *)data2; + nhandle->color = ui_color(val[0] * 255.0, val[1] * 255.0, val[2] * 255.0, 255.0); + ui_color_wheel(nhandle, false, -1, -1, true, &ui_color_wheel_picker, val); + val[0] = ui_color_r(nhandle->color) / 255.0f; + val[1] = ui_color_g(nhandle->color) / 255.0f; + val[2] = ui_color_b(nhandle->color) / 255.0f; +} + +void ui_nodes_rgba_popup(ui_handle_t *nhandle, float *val, int x, int y) { + ui_t *current = ui_get_current(); + ui_popup(x, y, 140.0 * current_nodes->scale_factor, current->ops->theme->ELEMENT_H * 10.0, &rgba_popup_commands, nhandle, val); +} + +static char_ptr_array_t enum_ar; +static char enum_label[64]; +static char enum_texts_data[64][64]; +static char *enum_texts[64]; + +static char_ptr_array_t temp_ar; +static char temp_label[64]; +static char temp_texts_data[64][64]; +static char *temp_texts[64]; + +void ui_draw_node(ui_node_t *node, ui_node_canvas_t *canvas) { + ui_t *current = ui_get_current(); + float wx = current->_window_x; + float wy = current->_window_y; + float ui_x = current->_x; + float ui_y = current->_y; + float ui_w = current->_w; + float w = UI_NODE_W(node); + float h = UI_NODE_H(canvas, node); + float nx = UI_NODE_X(node); + float ny = UI_NODE_Y(node); + char *text = ui_tr(node->name); + float lineh = UI_LINE_H(); + + // Disallow input if node is overlapped by another node + current_nodes->_input_started = current->input_started; + if (current->input_started) { + for (int i = ui_get_node_index(canvas->nodes->buffer, canvas->nodes->length, node->id) + 1; i < canvas->nodes->length; ++i) { + ui_node_t *n = canvas->nodes->buffer[i]; + if (UI_NODE_X(n) < current->input_x - current->_window_x && UI_NODE_X(n) + UI_NODE_W(n) > current->input_x - current->_window_x && + UI_NODE_Y(n) < current->input_y - current->_window_y && UI_NODE_Y(n) + UI_NODE_H(canvas, n) > current->input_y - current->_window_y) { + current->input_started = false; + break; + } + } + } + + // Shadow + ui_draw_shadow(nx, ny, w, h); + + // Outline + arm_g2_set_color(ui_is_selected(node) ? current->ops->theme->LABEL_COL : current->ops->theme->PRESSED_COL); + ui_draw_rect(true, nx - 1, ny - 1, w + 2, h + 2); + + // Body + arm_g2_set_color(current->ops->theme->WINDOW_BG_COL); + ui_draw_rect(true, nx, ny, w, h); + + // Header line + arm_g2_set_color(node->color); + arm_g2_fill_rect(nx, ny + lineh - ui_p(3), w, ui_p(3)); + + // Title + arm_g2_set_color(current->ops->theme->TEXT_COL); + float textw = arm_g2_string_width(current->ops->font->font_, current->font_size, text); + arm_g2_draw_string(text, nx + ui_p(10), ny + ui_p(6)); + ny += lineh * 0.5; + + // Outputs + for (int i = 0; i < node->outputs->length; ++i) { + ui_node_socket_t *out = node->outputs->buffer[i]; + ny += lineh; + arm_g2_set_color(out->color); + arm_g2_draw_scaled_render_target(&ui_socket_image, nx + w - ui_p(6), ny - ui_p(3), ui_p(12), ui_p(12)); + } + ny -= lineh * node->outputs->length; + arm_g2_set_color(current->ops->theme->LABEL_COL); + for (int i = 0; i < node->outputs->length; ++i) { + ui_node_socket_t *out = node->outputs->buffer[i]; + ny += lineh; + float strw = arm_g2_string_width(current->ops->font->font_, current->font_size, ui_tr(out->name)); + arm_g2_draw_string(ui_tr(out->name), nx + w - strw - ui_p(12), ny - ui_p(3)); + + if (ui_nodes_on_socket_released != NULL && current->input_enabled && (current->input_released || current->input_released_r)) { + if (current->input_x > wx + nx && current->input_x < wx + nx + w && current->input_y > wy + ny && current->input_y < wy + ny + lineh) { + ui_nodes_on_socket_released(out->id); + ui_nodes_socket_released = true; + } + } + } + + // Buttons + ui_handle_t *nhandle = ui_nest(current_nodes->handle, node->id); + ny -= lineh / 3.0; // Fix align + for (int buti = 0; buti < node->buttons->length; ++buti) { + ui_node_button_t *but = node->buttons->buffer[buti]; + + if (strcmp(but->type, "RGBA") == 0) { + ny += lineh; // 18 + 2 separator + current->_x = nx + 1; // Offset for node selection border + current->_y = ny; + current->_w = w; + float *val = node->outputs->buffer[but->output]->default_value->buffer; + nhandle->color = ui_color(val[0] * 255.0, val[1] * 255.0, val[2] * 255.0, 255.0); + ui_color_wheel(nhandle, false, -1, -1, true, &ui_color_wheel_picker, val); + val[0] = ui_color_r(nhandle->color) / 255.0f; + val[1] = ui_color_g(nhandle->color) / 255.0f; + val[2] = ui_color_b(nhandle->color) / 255.0f; + } + else if (strcmp(but->type, "VECTOR") == 0) { + ny += lineh; + current->_x = nx; + current->_y = ny; + current->_w = w; + float min = but->min; + float max = but->max; + float text_off = current->ops->theme->TEXT_OFFSET; + current->ops->theme->TEXT_OFFSET = 6; + ui_text(ui_tr(but->name), UI_ALIGN_LEFT, 0); + float *val = (float *)but->default_value->buffer; + + ui_handle_t *h = ui_nest(nhandle, buti); + ui_handle_t *h0 = ui_nest(h, 0); + if (h0->init) { + h0->value = val[0]; + } + ui_handle_t *h1 = ui_nest(h, 1); + if (h1->init) { + h1->value = val[1]; + } + ui_handle_t *h2 = ui_nest(h, 2); + if (h2->init) { + h2->value = val[2]; + } + + val[0] = ui_slider(h0, "X", min, max, true, 100, true, UI_ALIGN_LEFT, true); + val[1] = ui_slider(h1, "Y", min, max, true, 100, true, UI_ALIGN_LEFT, true); + val[2] = ui_slider(h2, "Z", min, max, true, 100, true, UI_ALIGN_LEFT, true); + current->ops->theme->TEXT_OFFSET = text_off; + if (but->output >= 0) { + node->outputs->buffer[but->output]->default_value->buffer = but->default_value->buffer; + } + ny += lineh * 3.0; + } + else if (strcmp(but->type, "VALUE") == 0) { + ny += lineh; + current->_x = nx; + current->_y = ny; + current->_w = w; + ui_node_socket_t *soc = node->outputs->buffer[but->output]; + float min = but->min; + float max = but->max; + float prec = but->precision; + float text_off = current->ops->theme->TEXT_OFFSET; + current->ops->theme->TEXT_OFFSET = 6; + ui_handle_t *soc_handle = ui_nest(nhandle, buti); + if (soc_handle->init) { + soc_handle->value = ((float *)soc->default_value->buffer)[0]; + } + ((float *)soc->default_value->buffer)[0] = ui_slider(soc_handle, "Value", min, max, true, prec, true, UI_ALIGN_LEFT, true); + current->ops->theme->TEXT_OFFSET = text_off; + } + else if (strcmp(but->type, "STRING") == 0) { + ny += lineh; + current->_x = nx; + current->_y = ny; + current->_w = w; + ui_node_socket_t *soc = but->output >= 0 ? node->outputs->buffer[but->output] : NULL; + ui_handle_t *h = ui_nest(nhandle, buti); + if (h->init) { + h->text = soc != NULL ? soc->default_value->buffer : but->default_value->buffer != NULL ? but->default_value->buffer : ""; + } + but->default_value->buffer = ui_text_input(h, ui_tr(but->name), UI_ALIGN_LEFT, true, false); + but->default_value->length = strlen(but->default_value->buffer) + 1; + if (soc != NULL) { + soc->default_value->buffer = but->default_value->buffer; + } + } + else if (strcmp(but->type, "ENUM") == 0) { + ny += lineh; + current->_x = nx; + current->_y = ny; + current->_w = w; + + ui_handle_t *but_handle = ui_nest(nhandle, buti); + but_handle->position = ((float *)but->default_value->buffer)[0]; + + bool combo_select = current->combo_selected_handle == NULL && ui_get_released(UI_ELEMENT_H()); + char *label = combo_select ? temp_label : enum_label; + char (*texts_data)[64] = combo_select ? temp_texts_data : enum_texts_data; + char **texts = combo_select ? temp_texts : enum_texts; + char_ptr_array_t *ar = combo_select ? &temp_ar : &enum_ar; + + int texts_count = 0; + if (but->data != NULL && but->data->length > 1) { + int wi = 0; + for (int i = 0; i < but->data->length; ++i) { + char c = ((char *)but->data->buffer)[i]; + if (c == '\0') { + texts_data[texts_count][wi] = '\0'; + texts_count++; + break; + } + if (c == '\n') { + texts_data[texts_count][wi] = '\0'; + texts_count++; + wi = 0; + continue; + } + texts_data[texts_count][wi] = c; + wi++; + } + for (int i = 0; i < texts_count; ++i) { + strcpy(texts_data[i], ui_tr(texts_data[i])); + texts[i] = texts_data[i]; + } + + ar->buffer = texts; + ar->length = texts_count; + } + else { + gc_unroot(ar); + ar = (*ui_nodes_enum_texts)(node->type); + gc_root(ar); + } + + strcpy(label, ui_tr(but->name)); + + ((float *)but->default_value->buffer)[0] = ui_combo(but_handle, ar, label, false, UI_ALIGN_LEFT, true); + } + else if (strcmp(but->type, "BOOL") == 0) { + ny += lineh; + current->_x = nx; + current->_y = ny; + current->_w = w; + ui_handle_t *h = ui_nest(nhandle, buti); + if (h->init) { + h->selected = ((float *)but->default_value->buffer)[0]; + } + ((float *)but->default_value->buffer)[0] = ui_check(h, ui_tr(but->name), ""); + } + else if (strcmp(but->type, "CUSTOM") == 0) { // Calls external function for custom button drawing + ny += lineh; + current->_x = nx; + current->_y = ny; + current->_w = w; + ui_nodes_on_custom_button(node->id, but->name); + ny += lineh * (but->height - 1); // but->height specifies vertical button size + } + } + ny += lineh / 3.0; // Fix align + + // Inputs + for (int i = 0; i < node->inputs->length; ++i) { + ui_node_socket_t *inp = node->inputs->buffer[i]; + ny += lineh; + arm_g2_set_color(inp->color); + arm_g2_draw_scaled_render_target(&ui_socket_image, nx - ui_p(6), ny - ui_p(3), ui_p(12), ui_p(12)); + bool is_linked = ui_input_linked(canvas, node->id, i); + if (!is_linked && strcmp(inp->type, "VALUE") == 0) { + current->_x = nx + ui_p(6); + current->_y = ny - ui_p(current_nodes->ELEMENT_H / 3.0); + current->_w = w - ui_p(6); + ui_node_socket_t *soc = inp; + float min = soc->min; + float max = soc->max; + float prec = soc->precision; + float text_off = current->ops->theme->TEXT_OFFSET; + current->ops->theme->TEXT_OFFSET = 6; + + ui_handle_t *_handle = ui_nest(nhandle, ui_max_buttons); + ui_handle_t *soc_handle = ui_nest(_handle, i); + if (soc_handle->init) { + soc_handle->value = ((float *)soc->default_value->buffer)[0]; + } + ((float *)soc->default_value->buffer)[0] = ui_slider(soc_handle, ui_tr(inp->name), min, max, true, prec, true, UI_ALIGN_LEFT, true); + current->ops->theme->TEXT_OFFSET = text_off; + } + else if (!is_linked && strcmp(inp->type, "STRING") == 0) { + current->_x = nx + ui_p(6); + current->_y = ny - ui_p(9); + current->_w = w - ui_p(6); + ui_node_socket_t *soc = inp; + float text_off = current->ops->theme->TEXT_OFFSET; + current->ops->theme->TEXT_OFFSET = 6; + ui_handle_t *_handle = ui_nest(nhandle, ui_max_buttons); + ui_handle_t *h = ui_nest(_handle, i); + if (h->init) { + strcpy(h->text, soc->default_value->buffer); + } + soc->default_value->buffer = ui_text_input(h, ui_tr(inp->name), UI_ALIGN_LEFT, true, false); + current->ops->theme->TEXT_OFFSET = text_off; + } + else if (!is_linked && strcmp(inp->type, "RGBA") == 0) { + arm_g2_set_color(current->ops->theme->LABEL_COL); + arm_g2_draw_string(ui_tr(inp->name), nx + ui_p(12), ny - ui_p(3)); + ui_node_socket_t *soc = inp; + arm_g2_set_color(0xff000000); + arm_g2_fill_rect(nx + w - ui_p(38), ny - ui_p(6), ui_p(36), ui_p(18)); + float *val = (float *)soc->default_value->buffer; + arm_g2_set_color(ui_color(val[0] * 255, val[1] * 255, val[2] * 255, 255)); + float rx = nx + w - ui_p(37); + float ry = ny - ui_p(5); + float rw = ui_p(34); + float rh = ui_p(16); + arm_g2_fill_rect(rx, ry, rw, rh); + float ix = current->input_x - wx; + float iy = current->input_y - wy; + if (current->input_started && ix > rx && iy > ry && ix < rx + rw && iy < ry + rh) { + current_nodes->_input_started = current->input_started = false; + ui_nodes_rgba_popup(nhandle, soc->default_value->buffer, (int)(rx), (int)(ry + UI_ELEMENT_H())); + ui_popup_handle_node_id = node->id; + ui_popup_handle_node_socket_id = soc->id; + } + if (ui_popup_commands != NULL && ui_popup_handle_node_id == node->id && ui_popup_handle_node_socket_id == soc->id) { + // armpack data may have been moved in memory + ui_popup_data = nhandle; + ui_popup_data2 = soc->default_value->buffer; + } + } + else if (!is_linked && strcmp(inp->type, "VECTOR") == 0 && inp->display == 1) { + arm_g2_set_color(current->ops->theme->LABEL_COL); + arm_g2_draw_string(ui_tr(inp->name), nx + ui_p(12), ny - ui_p(3)); + ny += lineh / 2; + current->_x = nx; + current->_y = ny; + current->_w = w; + float min = inp->min; + float max = inp->max; + float text_off = current->ops->theme->TEXT_OFFSET; + current->ops->theme->TEXT_OFFSET = 6; + float *val = (float *)inp->default_value->buffer; + ui_handle_t *h = ui_nest(nhandle, ui_max_buttons); + ui_handle_t *hi = ui_nest(h, i); + ui_handle_t *h0 = ui_nest(hi, 0); + if (h0->init) { + h0->value = val[0]; + } + ui_handle_t *h1 = ui_nest(hi, 1); + if (h1->init) { + h1->value = val[1]; + } + ui_handle_t *h2 = ui_nest(hi, 2); + if (h2->init) { + h2->value = val[2]; + } + val[0] = ui_slider(h0, "X", min, max, true, 100, true, UI_ALIGN_LEFT, true); + val[1] = ui_slider(h1, "Y", min, max, true, 100, true, UI_ALIGN_LEFT, true); + val[2] = ui_slider(h2, "Z", min, max, true, 100, true, UI_ALIGN_LEFT, true); + current->ops->theme->TEXT_OFFSET = text_off; + ny += lineh * 2.5; + } + else { + arm_g2_set_color(current->ops->theme->LABEL_COL); + arm_g2_draw_string(ui_tr(inp->name), nx + ui_p(12), ny - ui_p(3)); + } + if (ui_nodes_on_socket_released != NULL && current->input_enabled && (current->input_released || current->input_released_r)) { + if (current->input_x > wx + nx && current->input_x < wx + nx + w && current->input_y > wy + ny && current->input_y < wy + ny + lineh) { + ui_nodes_on_socket_released(inp->id); + ui_nodes_socket_released = true; + } + } + } + + current->_x = ui_x; + current->_y = ui_y; + current->_w = ui_w; + current->input_started = current_nodes->_input_started; +} + +bool ui_is_node_type_excluded(char *type) { + for (int i = 0; i < ui_nodes_exclude_remove->length; ++i) { + if (strcmp(ui_nodes_exclude_remove->buffer[i], type) == 0) { + return true; + } + } + return false; +} + +void ui_node_canvas(ui_nodes_t *nodes, ui_node_canvas_t *canvas) { + current_nodes = nodes; + ui_t *current = ui_get_current(); + if (!ui_nodes_elements_baked) { + ui_nodes_bake_elements(); + } + if (ui_nodes_exclude_remove == NULL) { + ui_nodes_exclude_remove = gc_alloc(sizeof(char_ptr_array_t)); + gc_root(ui_nodes_exclude_remove); + } + + float wx = current->_window_x; + float wy = current->_window_y; + bool _input_enabled = current->input_enabled; + current->input_enabled = _input_enabled && ui_popup_commands == NULL; + ui_canvas_control_t *controls = ui_nodes_on_canvas_control != NULL ? ui_nodes_on_canvas_control() : ui_on_default_canvas_control(); + ui_nodes_socket_released = false; + + // Pan canvas + if (current->input_enabled && (controls->pan_x != 0.0 || controls->pan_y != 0.0)) { + current_nodes->pan_x += controls->pan_x / UI_NODES_SCALE(); + current_nodes->pan_y += controls->pan_y / UI_NODES_SCALE(); + } + + // Zoom canvas + if (current->input_enabled && controls->zoom != 0.0) { + current_nodes->zoom += controls->zoom; + if (current_nodes->zoom < 0.1) { + current_nodes->zoom = 0.1; + } + else if (current_nodes->zoom > 1.0) { + current_nodes->zoom = 1.0; + } + current_nodes->zoom = round(current_nodes->zoom * 100.0) / 100.0; + current_nodes->uiw = current->_w; + current_nodes->uih = current->_h; + if (ui_touch_scroll) { + // Zoom to finger location + current_nodes->pan_x -= (current->input_x - current->_window_x - current->_window_w / 2.0) * controls->zoom * 5.0 * (1.0 - current_nodes->zoom); + current_nodes->pan_y -= (current->input_y - current->_window_y - current->_window_h / 2.0) * controls->zoom * 5.0 * (1.0 - current_nodes->zoom); + } + } + current_nodes->scale_factor = UI_SCALE(); + current_nodes->ELEMENT_H = current->ops->theme->ELEMENT_H + 2; + ui_set_scale(UI_NODES_SCALE()); // Apply zoomed scale + current->elements_baked = true; + arm_g2_set_font(current->ops->font->font_, current->font_size); + + for (int i = 0; i < canvas->links->length; ++i) { + ui_node_link_t *link = canvas->links->buffer[i]; + ui_node_t *from = ui_get_node(canvas->nodes, link->from_id); + ui_node_t *to = ui_get_node(canvas->nodes, link->to_id); + float from_x = from == NULL ? current->input_x : wx + UI_NODE_X(from) + UI_NODE_W(from); + float from_y = from == NULL ? current->input_y : wy + UI_NODE_Y(from) + UI_OUTPUT_Y(from->outputs->length, link->from_socket); + float to_x = to == NULL ? current->input_x : wx + UI_NODE_X(to); + float to_y = to == NULL ? current->input_y : wy + UI_NODE_Y(to) + UI_INPUT_Y(canvas, to->inputs->buffer, to->inputs->length, link->to_socket) + UI_OUTPUTS_H(to->outputs->length, -1) + UI_BUTTONS_H(to); + + // Cull + float left = to_x > from_x ? from_x : to_x; + float right = to_x > from_x ? to_x : from_x; + float top = to_y > from_y ? from_y : to_y; + float bottom = to_y > from_y ? to_y : from_y; + if (right < 0 || left > wx + current->_window_w || + bottom < 0 || top > wy + current->_window_h) { + continue; + } + + // Snap to nearest socket + if (current_nodes->link_drag_id == link->id) { + if (current_nodes->snap_from_id != -1) { + from_x = current_nodes->snap_x; + from_y = current_nodes->snap_y; + } + if (current_nodes->snap_to_id != -1) { + to_x = current_nodes->snap_x; + to_y = current_nodes->snap_y; + } + current_nodes->snap_from_id = current_nodes->snap_to_id = -1; + + for (int j = 0; j < canvas->nodes->length; ++j) { + ui_node_t *node = canvas->nodes->buffer[j]; + ui_node_socket_t **inps = node->inputs->buffer; + ui_node_socket_t **outs = node->outputs->buffer; + float node_h = UI_NODE_H(canvas, node); + float rx = wx + UI_NODE_X(node) - UI_LINE_H() / 2; + float ry = wy + UI_NODE_Y(node) - UI_LINE_H() / 2; + float rw = UI_NODE_W(node) + UI_LINE_H(); + float rh = node_h + UI_LINE_H(); + if (ui_input_in_rect(rx, ry, rw, rh)) { + if (from == NULL && node->id != to->id) { // Snap to output + for (int k = 0; k < node->outputs->length; ++k) { + float sx = wx + UI_NODE_X(node) + UI_NODE_W(node); + float sy = wy + UI_NODE_Y(node) + UI_OUTPUT_Y(node->outputs->length, k); + float rx = sx - UI_LINE_H() / 2; + float ry = sy - UI_LINE_H() / 2; + if (ui_input_in_rect(rx, ry, UI_LINE_H(), UI_LINE_H())) { + current_nodes->snap_x = sx; + current_nodes->snap_y = sy; + current_nodes->snap_from_id = node->id; + current_nodes->snap_socket = k; + break; + } + } + } + else if (to == NULL && node->id != from->id) { // Snap to input + for (int k = 0; k < node->inputs->length; ++k) { + float sx = wx + UI_NODE_X(node); + float sy = wy + UI_NODE_Y(node) + UI_INPUT_Y(canvas, inps, node->inputs->length, k) + UI_OUTPUTS_H(node->outputs->length, -1) + UI_BUTTONS_H(node); + float rx = sx - UI_LINE_H() / 2.0; + float ry = sy - UI_LINE_H() / 2.0; + if (ui_input_in_rect(rx, ry, UI_LINE_H(), UI_LINE_H())) { + current_nodes->snap_x = sx; + current_nodes->snap_y = sy; + current_nodes->snap_to_id = node->id; + current_nodes->snap_socket = k; + break; + } + } + } + } + } + } + + bool selected = false; + for (int j = 0; j < current_nodes->nodes_selected_id->length; ++j) { + int n_id = current_nodes->nodes_selected_id->buffer[j]; + if (link->from_id == n_id || link->to_id == n_id) { + selected = true; + break; + } + } + + ui_draw_link(from_x - wx, from_y - wy, to_x - wx, to_y - wy, selected); + } + + for (int i = 0; i < canvas->nodes->length; ++i) { + ui_node_t *node = canvas->nodes->buffer[i]; + + // Cull + if (UI_NODE_X(node) > current->_window_w || UI_NODE_X(node) + UI_NODE_W(node) < 0 || + UI_NODE_Y(node) > current->_window_h || UI_NODE_Y(node) + UI_NODE_H(canvas, node) < 0) { + if (!ui_is_selected(node)) { + continue; + } + } + + ui_node_socket_t **inps = node->inputs->buffer; + ui_node_socket_t **outs = node->outputs->buffer; + + // Drag node + float node_h = UI_NODE_H(canvas, node); + if (current->input_enabled && ui_input_in_rect(wx + UI_NODE_X(node) - UI_LINE_H() / 2.0, wy + UI_NODE_Y(node), UI_NODE_W(node) + UI_LINE_H(), UI_LINE_H())) { + if (current->input_started) { + if (current->is_shift_down || current->is_ctrl_down) { + // Add to selection or deselect + if (ui_is_selected(node)) { + remove_from_selection(node); + } + else { + add_to_selection(node); + } + } + else if (current_nodes->nodes_selected_id->length <= 1) { + // Selecting single node, otherwise wait for input release + current_nodes->nodes_selected_id->length = 0; + i32_array_push(current_nodes->nodes_selected_id, node->id); + } + current_nodes->move_on_top = node; // Place selected node on top + current_nodes->nodes_drag = true; + current_nodes->dragged = false; + } + else if (current->input_released && !current->is_shift_down && !current->is_ctrl_down && !current_nodes->dragged) { + // No drag performed, select single node + current_nodes->nodes_selected_id->length = 0; + i32_array_push(current_nodes->nodes_selected_id, node->id); + if (ui_on_header_released != NULL) { + ui_on_header_released(node); + } + } + } + if (current->input_started && ui_input_in_rect(wx + UI_NODE_X(node) - UI_LINE_H() / 2, wy + UI_NODE_Y(node) - UI_LINE_H() / 2, UI_NODE_W(node) + UI_LINE_H(), node_h + UI_LINE_H())) { + // Check sockets + if (current_nodes->link_drag_id == -1) { + for (int j = 0; j < node->outputs->length; ++j) { + float sx = wx + UI_NODE_X(node) + UI_NODE_W(node); + float sy = wy + UI_NODE_Y(node) + UI_OUTPUT_Y(node->outputs->length, j); + if (ui_input_in_rect(sx - UI_LINE_H() / 2.0, sy - UI_LINE_H() / 2.0, UI_LINE_H(), UI_LINE_H())) { + // New link from output + ui_node_link_t *l = (ui_node_link_t *)malloc(sizeof(ui_node_link_t)); // TODO: store at canvas->links without malloc + l->id = ui_next_link_id(canvas->links); + l->from_id = node->id; + l->from_socket = j; + l->to_id = -1; + l->to_socket = -1; + any_array_push(canvas->links, l); + current_nodes->link_drag_id = l->id; + current_nodes->is_new_link = true; + break; + } + } + } + if (current_nodes->link_drag_id == -1) { + for (int j = 0; j < node->inputs->length; ++j) { + float sx = wx + UI_NODE_X(node); + float sy = wy + UI_NODE_Y(node) + UI_INPUT_Y(canvas, inps, node->inputs->length, j) + UI_OUTPUTS_H(node->outputs->length, -1) + UI_BUTTONS_H(node); + if (ui_input_in_rect(sx - UI_LINE_H() / 2.0, sy - UI_LINE_H() / 2.0, UI_LINE_H(), UI_LINE_H())) { + // Already has a link - disconnect + for (int k = 0; k < canvas->links->length; ++k) { + ui_node_link_t *l = canvas->links->buffer[k]; + if (l->to_id == node->id && l->to_socket == j) { + l->to_id = l->to_socket = -1; + current_nodes->link_drag_id = l->id; + current_nodes->is_new_link = false; + break; + } + } + if (current_nodes->link_drag_id != -1) { + break; + } + // New link from input + ui_node_link_t *l = (ui_node_link_t *)malloc(sizeof(ui_node_link_t)); + l->id = ui_next_link_id(canvas->links); + l->from_id = -1; + l->from_socket = -1; + l->to_id = node->id; + l->to_socket = j; + any_array_push(canvas->links, l); + current_nodes->link_drag_id = l->id; + current_nodes->is_new_link = true; + break; + } + } + } + } + else if (current->input_released) { + if (current_nodes->snap_to_id != -1) { // Connect to input + // Force single link per input + for (int j = 0; j < canvas->links->length; ++j) { + ui_node_link_t *l = canvas->links->buffer[j]; + if (l->to_id == current_nodes->snap_to_id && l->to_socket == current_nodes->snap_socket) { + ui_remove_link_at(canvas, ui_get_link_index(canvas->links, l->id)); + break; + } + } + ui_node_link_t *link_drag = ui_get_link(canvas->links, current_nodes->link_drag_id); + link_drag->to_id = current_nodes->snap_to_id; + link_drag->to_socket = current_nodes->snap_socket; + current->changed = true; + } + else if (current_nodes->snap_from_id != -1) { // Connect to output + ui_node_link_t *link_drag = ui_get_link(canvas->links, current_nodes->link_drag_id); + link_drag->from_id = current_nodes->snap_from_id; + link_drag->from_socket = current_nodes->snap_socket; + current->changed = true; + } + else if (current_nodes->link_drag_id != -1) { // Remove dragged link + current->changed = true; + if (ui_nodes_on_link_drag != NULL) { + ui_nodes_on_link_drag(current_nodes->link_drag_id, current_nodes->is_new_link); + } + ui_remove_link_at(canvas, ui_get_link_index(canvas->links, current_nodes->link_drag_id)); + } + current_nodes->snap_to_id = current_nodes->snap_from_id = -1; + current_nodes->link_drag_id = -1; + current_nodes->nodes_drag = false; + } + if (current_nodes->nodes_drag && ui_is_selected(node) && !current->input_down_r) { + if (current->input_dx != 0 || current->input_dy != 0) { + current_nodes->dragged = true; + node->x += current->input_dx / UI_NODES_SCALE(); + node->y += current->input_dy / UI_NODES_SCALE(); + } + } + + ui_draw_node(node, canvas); + } + + if (ui_nodes_on_canvas_released != NULL && current->input_enabled && (current->input_released || current->input_released_r) && !ui_nodes_socket_released) { + ui_nodes_on_canvas_released(); + } + + if (ui_box_select) { + arm_g2_set_color(0x223333dd); + arm_g2_fill_rect(ui_box_select_x, ui_box_select_y, current->input_x - ui_box_select_x - current->_window_x, current->input_y - ui_box_select_y - current->_window_y); + arm_g2_set_color(0x773333dd); + arm_g2_draw_rect(ui_box_select_x, ui_box_select_y, current->input_x - ui_box_select_x - current->_window_x, current->input_y - ui_box_select_y - current->_window_y, 1); + arm_g2_set_color(0xffffffff); + } + if (current->input_enabled && current->input_started && !current->is_alt_down && + current_nodes->link_drag_id == -1 && !current_nodes->nodes_drag && !current->changed && + ui_input_in_rect(current->_window_x, current->_window_y, current->_window_w, current->_window_h)) { + + ui_box_select = true; + ui_box_select_x = current->input_x - current->_window_x; + ui_box_select_y = current->input_y - current->_window_y; + } + else if (ui_box_select && !current->input_down) { + ui_box_select = false; + int left = ui_box_select_x; + int top = ui_box_select_y; + int right = current->input_x - current->_window_x; + int bottom = current->input_y - current->_window_y; + if (left > right) { + int t = left; + left = right; + right = t; + } + if (top > bottom) { + int t = top; + top = bottom; + bottom = t; + } + ui_node_t *nodes[32]; + int nodes_count = 0; + for (int j = 0; j < canvas->nodes->length; ++j) { + ui_node_t *n = canvas->nodes->buffer[j]; + if (UI_NODE_X(n) + UI_NODE_W(n) > left && UI_NODE_X(n) < right && + UI_NODE_Y(n) + UI_NODE_H(canvas, n) > top && UI_NODE_Y(n) < bottom) { + nodes[nodes_count] = n; + nodes_count++; + } + } + if (current->is_shift_down || current->is_ctrl_down) { + for (int j = 0; j < nodes_count; ++j) { + add_to_selection(nodes[j]); + } + } + else { + current_nodes->nodes_selected_id->length = 0; + for (int j = 0; j < nodes_count; ++j) { + add_to_selection(nodes[j]); + } + } + } + + // Place selected node on top + if (current_nodes->move_on_top != NULL) { + int index = ui_get_node_index(canvas->nodes->buffer, canvas->nodes->length, current_nodes->move_on_top->id); + ui_remove_node_at(canvas, index); + any_array_push(canvas->nodes, current_nodes->move_on_top); + current_nodes->move_on_top = NULL; + } + + // Node copy & paste + bool cut_selected = false; + if (ui_is_copy && !current->is_typing) { + ui_node_t *copy_nodes[32]; + int copy_nodes_count = 0; + for (int i = 0; i < current_nodes->nodes_selected_id->length; ++i) { + int id = current_nodes->nodes_selected_id->buffer[i]; + ui_node_t *n = ui_get_node(canvas->nodes, id); + if (ui_is_node_type_excluded(n->type)) { + continue; + } + copy_nodes[copy_nodes_count] = n; + copy_nodes_count++; + } + ui_node_link_t *copy_links[64]; + int copy_links_count = 0; + for (int i = 0; i < canvas->links->length; ++i) { + ui_node_link_t *l = canvas->links->buffer[i]; + ui_node_t *from = NULL; + for (int j = 0; j < current_nodes->nodes_selected_id->length; ++j) { + if (current_nodes->nodes_selected_id->buffer[j] == l->from_id) { + from = ui_get_node(canvas->nodes, l->from_id); + break; + } + } + ui_node_t *to = NULL; + for (int j = 0; j < current_nodes->nodes_selected_id->length; ++j) { + if (current_nodes->nodes_selected_id->buffer[j] == l->to_id) { + to = ui_get_node(canvas->nodes, l->to_id); + break; + } + } + + if (from != NULL && !ui_is_node_type_excluded(from->type) && + to != NULL && !ui_is_node_type_excluded(to->type)) { + copy_links[copy_links_count] = l; + copy_links_count++; + } + } + if (copy_nodes_count > 0) { + ui_node_canvas_t copy_canvas = {}; + copy_canvas.name = canvas->name; + ui_node_array_t nodes = { .buffer = copy_nodes, .length = copy_nodes_count }; + ui_node_link_array_t links = { .buffer = copy_links, .length = copy_links_count }; + copy_canvas.nodes = &nodes; + copy_canvas.links = &links; + gc_unroot(ui_clipboard); + ui_clipboard = ui_node_canvas_to_json(©_canvas); + gc_root(ui_clipboard); + } + cut_selected = ui_is_cut; + ui_is_copy = false; + ui_is_cut = false; + } + + if (ui_is_paste && !current->is_typing) { + ui_is_paste = false; + + bool is_json = ui_clipboard[0] == '{'; + if (is_json) { + + ui_node_canvas_t *paste_canvas = json_parse(ui_clipboard); + gc_root(paste_canvas); // TODO + + // Convert button data from string to u8 array + for (int i = 0; i < paste_canvas->nodes->length; ++i) { + for (int j = 0; j < paste_canvas->nodes->buffer[i]->buttons->length; ++j) { + ui_node_button_t *but = paste_canvas->nodes->buffer[i]->buttons->buffer[j]; + if (but->data != NULL) { + but->data = u8_array_create_from_raw(but->data, strlen(but->data) + 1); + } + } + } + + for (int i = 0; i < paste_canvas->links->length; ++i) { + ui_node_link_t *l = paste_canvas->links->buffer[i]; + // Assign unique link id + l->id = ui_next_link_id(canvas->links); + any_array_push(canvas->links, l); + } + int offset_x = (int)(((int)(current->input_x / UI_SCALE()) * UI_NODES_SCALE() - wx - UI_NODES_PAN_X()) / UI_NODES_SCALE()) - paste_canvas->nodes->buffer[paste_canvas->nodes->length - 1]->x; + int offset_y = (int)(((int)(current->input_y / UI_SCALE()) * UI_NODES_SCALE() - wy - UI_NODES_PAN_Y()) / UI_NODES_SCALE()) - paste_canvas->nodes->buffer[paste_canvas->nodes->length - 1]->y; + for (int i = 0; i < paste_canvas->nodes->length; ++i) { + ui_node_t *n = paste_canvas->nodes->buffer[i]; + // Assign unique node id + int old_id = n->id; + n->id = ui_next_node_id(canvas->nodes); + + for (int j = 0; j < n->inputs->length; ++j) { + ui_node_socket_t *soc = n->inputs->buffer[j]; + soc->id = ui_get_socket_id(canvas->nodes); + soc->node_id = n->id; + } + for (int j = 0; j < n->outputs->length; ++j) { + ui_node_socket_t *soc = n->outputs->buffer[j]; + soc->id = ui_get_socket_id(canvas->nodes); + soc->node_id = n->id; + } + for (int j = 0; j < paste_canvas->links->length; ++j) { + ui_node_link_t *l = paste_canvas->links->buffer[j]; + if (l->from_id == old_id) l->from_id = n->id; + else if (l->to_id == old_id) l->to_id = n->id; + } + n->x += offset_x; + n->y += offset_y; + any_array_push(canvas->nodes, n); + } + current_nodes->nodes_drag = true; + current_nodes->nodes_selected_id->length = 0; + for (int i = 0; i < paste_canvas->nodes->length; ++i) { + i32_array_push(current_nodes->nodes_selected_id, paste_canvas->nodes->buffer[i]->id); + } + current->changed = true; + } + } + + // Select all nodes + if (current->is_ctrl_down && current->key_code == KINC_KEY_A && !current->is_typing) { + current_nodes->nodes_selected_id->length = 0; + for (int i = 0; i < canvas->nodes->length; ++i) { + add_to_selection(canvas->nodes->buffer[i]); + } + } + + // Node removal + bool node_removal = current->input_enabled && (current->is_backspace_down || current->is_delete_down) && !current->is_typing; + if (node_removal || cut_selected) { + int i = current_nodes->nodes_selected_id->length - 1; + while (i >= 0) { + int nid = current_nodes->nodes_selected_id->buffer[i--]; + ui_node_t *n = ui_get_node(canvas->nodes, nid); + if (ui_is_node_type_excluded(n->type)) { + continue; + } + ui_remove_node(n, canvas); + current->changed = true; + } + current_nodes->nodes_selected_id->length = 0; + } + + ui_set_scale(current_nodes->scale_factor); // Restore non-zoomed scale + current->elements_baked = true; + current->input_enabled = _input_enabled; + + if (ui_popup_commands != NULL) { + current->_x = ui_popup_x; + current->_y = ui_popup_y; + current->_w = ui_popup_w; + + ui_draw_shadow(current->_x - 5, current->_y - 5, current->_w + 10, ui_popup_h * UI_SCALE() + 10); + arm_g2_set_color(current->ops->theme->SEPARATOR_COL); + ui_draw_rect(true, current->_x - 5, current->_y - 5, current->_w + 10, ui_popup_h * UI_SCALE() + 10); + (*ui_popup_commands)(current, ui_popup_data, ui_popup_data2); + + bool hide = (current->input_started || current->input_started_r) && (current->input_x - wx < ui_popup_x - 6 || current->input_x - wx > ui_popup_x + ui_popup_w + 6 || current->input_y - wy < ui_popup_y - 6 || current->input_y - wy > ui_popup_y + ui_popup_h * UI_SCALE() + 6); + if (hide || current->is_escape_down) { + ui_popup_commands = NULL; + } + } +} + +void ui_node_canvas_encode(ui_node_canvas_t *canvas) { + // armpack_encode_start(encoded); + armpack_encode_map(3); + armpack_encode_string("name"); + armpack_encode_string(canvas->name); + + armpack_encode_string("nodes"); + armpack_encode_array(canvas->nodes->length); + for (int i = 0; i < canvas->nodes->length; ++i) { + armpack_encode_map(10); + armpack_encode_string("id"); + armpack_encode_i32(canvas->nodes->buffer[i]->id); + armpack_encode_string("name"); + armpack_encode_string(canvas->nodes->buffer[i]->name); + armpack_encode_string("type"); + armpack_encode_string(canvas->nodes->buffer[i]->type); + armpack_encode_string("x"); + armpack_encode_f32(canvas->nodes->buffer[i]->x); + armpack_encode_string("y"); + armpack_encode_f32(canvas->nodes->buffer[i]->y); + armpack_encode_string("color"); + armpack_encode_i32(canvas->nodes->buffer[i]->color); + + armpack_encode_string("inputs"); + armpack_encode_array(canvas->nodes->buffer[i]->inputs->length); + for (int j = 0; j < canvas->nodes->buffer[i]->inputs->length; ++j) { + armpack_encode_map(10); + armpack_encode_string("id"); + armpack_encode_i32(canvas->nodes->buffer[i]->inputs->buffer[j]->id); + armpack_encode_string("node_id"); + armpack_encode_i32(canvas->nodes->buffer[i]->inputs->buffer[j]->node_id); + armpack_encode_string("name"); + armpack_encode_string(canvas->nodes->buffer[i]->inputs->buffer[j]->name); + armpack_encode_string("type"); + armpack_encode_string(canvas->nodes->buffer[i]->inputs->buffer[j]->type); + armpack_encode_string("color"); + armpack_encode_i32(canvas->nodes->buffer[i]->inputs->buffer[j]->color); + armpack_encode_string("default_value"); + armpack_encode_array_f32(canvas->nodes->buffer[i]->inputs->buffer[j]->default_value); + armpack_encode_string("min"); + armpack_encode_f32(canvas->nodes->buffer[i]->inputs->buffer[j]->min); + armpack_encode_string("max"); + armpack_encode_f32(canvas->nodes->buffer[i]->inputs->buffer[j]->max); + armpack_encode_string("precision"); + armpack_encode_f32(canvas->nodes->buffer[i]->inputs->buffer[j]->precision); + armpack_encode_string("display"); + armpack_encode_i32(canvas->nodes->buffer[i]->inputs->buffer[j]->display); + } + + armpack_encode_string("outputs"); + armpack_encode_array(canvas->nodes->buffer[i]->outputs->length); + for (int j = 0; j < canvas->nodes->buffer[i]->outputs->length; ++j) { + armpack_encode_map(10); + armpack_encode_string("id"); + armpack_encode_i32(canvas->nodes->buffer[i]->outputs->buffer[j]->id); + armpack_encode_string("node_id"); + armpack_encode_i32(canvas->nodes->buffer[i]->outputs->buffer[j]->node_id); + armpack_encode_string("name"); + armpack_encode_string(canvas->nodes->buffer[i]->outputs->buffer[j]->name); + armpack_encode_string("type"); + armpack_encode_string(canvas->nodes->buffer[i]->outputs->buffer[j]->type); + armpack_encode_string("color"); + armpack_encode_i32(canvas->nodes->buffer[i]->outputs->buffer[j]->color); + armpack_encode_string("default_value"); + armpack_encode_array_f32(canvas->nodes->buffer[i]->outputs->buffer[j]->default_value); + armpack_encode_string("min"); + armpack_encode_f32(canvas->nodes->buffer[i]->outputs->buffer[j]->min); + armpack_encode_string("max"); + armpack_encode_f32(canvas->nodes->buffer[i]->outputs->buffer[j]->max); + armpack_encode_string("precision"); + armpack_encode_f32(canvas->nodes->buffer[i]->outputs->buffer[j]->precision); + armpack_encode_string("display"); + armpack_encode_i32(canvas->nodes->buffer[i]->outputs->buffer[j]->display); + } + + armpack_encode_string("buttons"); + armpack_encode_array(canvas->nodes->buffer[i]->buttons->length); + for (int j = 0; j < canvas->nodes->buffer[i]->buttons->length; ++j) { + armpack_encode_map(9); + armpack_encode_string("name"); + armpack_encode_string(canvas->nodes->buffer[i]->buttons->buffer[j]->name); + armpack_encode_string("type"); + armpack_encode_string(canvas->nodes->buffer[i]->buttons->buffer[j]->type); + armpack_encode_string("output"); + armpack_encode_i32(canvas->nodes->buffer[i]->buttons->buffer[j]->output); + armpack_encode_string("default_value"); + armpack_encode_array_f32(canvas->nodes->buffer[i]->buttons->buffer[j]->default_value); + armpack_encode_string("data"); + u8_array_t *u8 = canvas->nodes->buffer[i]->buttons->buffer[j]->data; + armpack_encode_array_u8(u8); + armpack_encode_string("min"); + armpack_encode_f32(canvas->nodes->buffer[i]->buttons->buffer[j]->min); + armpack_encode_string("max"); + armpack_encode_f32(canvas->nodes->buffer[i]->buttons->buffer[j]->max); + armpack_encode_string("precision"); + armpack_encode_f32(canvas->nodes->buffer[i]->buttons->buffer[j]->precision); + armpack_encode_string("height"); + armpack_encode_f32(canvas->nodes->buffer[i]->buttons->buffer[j]->height); + } + + armpack_encode_string("width"); + armpack_encode_i32(canvas->nodes->buffer[i]->width); + } + + armpack_encode_string("links"); + armpack_encode_array(canvas->links->length); + for (int i = 0; i < canvas->links->length; ++i) { + armpack_encode_map(5); + armpack_encode_string("id"); + armpack_encode_i32(canvas->links->buffer[i]->id); + armpack_encode_string("from_id"); + armpack_encode_i32(canvas->links->buffer[i]->from_id); + armpack_encode_string("from_socket"); + armpack_encode_i32(canvas->links->buffer[i]->from_socket); + armpack_encode_string("to_id"); + armpack_encode_i32(canvas->links->buffer[i]->to_id); + armpack_encode_string("to_socket"); + armpack_encode_i32(canvas->links->buffer[i]->to_socket); + } +} + +uint32_t ui_node_canvas_encoded_size(ui_node_canvas_t *canvas) { + uint32_t size = 0; + size += armpack_size_map(); + size += armpack_size_string("name"); + size += armpack_size_string(canvas->name); + + size += armpack_size_string("nodes"); + size += armpack_size_array(); + for (int i = 0; i < canvas->nodes->length; ++i) { + size += armpack_size_map(); + size += armpack_size_string("id"); + size += armpack_size_i32(); + size += armpack_size_string("name"); + size += armpack_size_string(canvas->nodes->buffer[i]->name); + size += armpack_size_string("type"); + size += armpack_size_string(canvas->nodes->buffer[i]->type); + size += armpack_size_string("x"); + size += armpack_size_f32(); + size += armpack_size_string("y"); + size += armpack_size_f32(); + size += armpack_size_string("color"); + size += armpack_size_i32(); + + size += armpack_size_string("inputs"); + size += armpack_size_array(); + for (int j = 0; j < canvas->nodes->buffer[i]->inputs->length; ++j) { + size += armpack_size_map(); + size += armpack_size_string("id"); + size += armpack_size_i32(); + size += armpack_size_string("node_id"); + size += armpack_size_i32(); + size += armpack_size_string("name"); + size += armpack_size_string(canvas->nodes->buffer[i]->inputs->buffer[j]->name); + size += armpack_size_string("type"); + size += armpack_size_string(canvas->nodes->buffer[i]->inputs->buffer[j]->type); + size += armpack_size_string("color"); + size += armpack_size_i32(); + size += armpack_size_string("default_value"); + size += armpack_size_array_f32(canvas->nodes->buffer[i]->inputs->buffer[j]->default_value); + size += armpack_size_string("min"); + size += armpack_size_f32(); + size += armpack_size_string("max"); + size += armpack_size_f32(); + size += armpack_size_string("precision"); + size += armpack_size_f32(); + size += armpack_size_string("display"); + size += armpack_size_i32(); + } + + size += armpack_size_string("outputs"); + size += armpack_size_array(); + for (int j = 0; j < canvas->nodes->buffer[i]->outputs->length; ++j) { + size += armpack_size_map(); + size += armpack_size_string("id"); + size += armpack_size_i32(); + size += armpack_size_string("node_id"); + size += armpack_size_i32(); + size += armpack_size_string("name"); + size += armpack_size_string(canvas->nodes->buffer[i]->outputs->buffer[j]->name); + size += armpack_size_string("type"); + size += armpack_size_string(canvas->nodes->buffer[i]->outputs->buffer[j]->type); + size += armpack_size_string("color"); + size += armpack_size_i32(); + size += armpack_size_string("default_value"); + size += armpack_size_array_f32(canvas->nodes->buffer[i]->outputs->buffer[j]->default_value); + size += armpack_size_string("min"); + size += armpack_size_f32(); + size += armpack_size_string("max"); + size += armpack_size_f32(); + size += armpack_size_string("precision"); + size += armpack_size_f32(); + size += armpack_size_string("display"); + size += armpack_size_i32(); + } + + size += armpack_size_string("buttons"); + size += armpack_size_array(); + for (int j = 0; j < canvas->nodes->buffer[i]->buttons->length; ++j) { + size += armpack_size_map(); + size += armpack_size_string("name"); + size += armpack_size_string(canvas->nodes->buffer[i]->buttons->buffer[j]->name); + size += armpack_size_string("type"); + size += armpack_size_string(canvas->nodes->buffer[i]->buttons->buffer[j]->type); + size += armpack_size_string("output"); + size += armpack_size_i32(); + size += armpack_size_string("default_value"); + size += armpack_size_array_f32(canvas->nodes->buffer[i]->buttons->buffer[j]->default_value); + size += armpack_size_string("data"); + u8_array_t *u8 = canvas->nodes->buffer[i]->buttons->buffer[j]->data; + size += armpack_size_array_u8(u8); + size += armpack_size_string("min"); + size += armpack_size_f32(); + size += armpack_size_string("max"); + size += armpack_size_f32(); + size += armpack_size_string("precision"); + size += armpack_size_f32(); + size += armpack_size_string("height"); + size += armpack_size_f32(); + } + + size += armpack_size_string("width"); + size += armpack_size_f32(); + } + + size += armpack_size_string("links"); + size += armpack_size_array(); + for (int i = 0; i < canvas->links->length; ++i) { + size += armpack_size_map(); + size += armpack_size_string("id"); + size += armpack_size_i32(); + size += armpack_size_string("from_id"); + size += armpack_size_i32(); + size += armpack_size_string("from_socket"); + size += armpack_size_i32(); + size += armpack_size_string("to_id"); + size += armpack_size_i32(); + size += armpack_size_string("to_socket"); + size += armpack_size_i32(); + } + + return size; +} + +char *ui_node_canvas_to_json(ui_node_canvas_t *canvas) { + json_encode_begin(); + json_encode_string("name", canvas->name); + + json_encode_begin_array("nodes"); + for (int i = 0; i < canvas->nodes->length; ++i) { + json_encode_begin_object(); + json_encode_i32("id", canvas->nodes->buffer[i]->id); + json_encode_string("name", canvas->nodes->buffer[i]->name); + json_encode_string("type", canvas->nodes->buffer[i]->type); + json_encode_i32("x", canvas->nodes->buffer[i]->x); + json_encode_i32("y", canvas->nodes->buffer[i]->y); + json_encode_i32("color", canvas->nodes->buffer[i]->color); + + json_encode_begin_array("inputs"); + for (int j = 0; j < canvas->nodes->buffer[i]->inputs->length; ++j) { + json_encode_begin_object(); + json_encode_i32("id", canvas->nodes->buffer[i]->inputs->buffer[j]->id); + json_encode_i32("node_id", canvas->nodes->buffer[i]->inputs->buffer[j]->node_id); + json_encode_string("name", canvas->nodes->buffer[i]->inputs->buffer[j]->name); + json_encode_string("type", canvas->nodes->buffer[i]->inputs->buffer[j]->type); + json_encode_i32("color", canvas->nodes->buffer[i]->inputs->buffer[j]->color); + json_encode_f32_array("default_value", canvas->nodes->buffer[i]->inputs->buffer[j]->default_value); + json_encode_f32("min", canvas->nodes->buffer[i]->inputs->buffer[j]->min); + json_encode_f32("max", canvas->nodes->buffer[i]->inputs->buffer[j]->max); + json_encode_f32("precision", canvas->nodes->buffer[i]->inputs->buffer[j]->precision); + json_encode_i32("display", canvas->nodes->buffer[i]->inputs->buffer[j]->display); + json_encode_end_object(); + } + json_encode_end_array(); + + json_encode_begin_array("outputs"); + for (int j = 0; j < canvas->nodes->buffer[i]->outputs->length; ++j) { + json_encode_begin_object(); + json_encode_i32("id", canvas->nodes->buffer[i]->outputs->buffer[j]->id); + json_encode_i32("node_id", canvas->nodes->buffer[i]->outputs->buffer[j]->node_id); + json_encode_string("name", canvas->nodes->buffer[i]->outputs->buffer[j]->name); + json_encode_string("type", canvas->nodes->buffer[i]->outputs->buffer[j]->type); + json_encode_i32("color", canvas->nodes->buffer[i]->outputs->buffer[j]->color); + json_encode_f32_array("default_value", canvas->nodes->buffer[i]->outputs->buffer[j]->default_value); + json_encode_f32("min", canvas->nodes->buffer[i]->outputs->buffer[j]->min); + json_encode_f32("max", canvas->nodes->buffer[i]->outputs->buffer[j]->max); + json_encode_f32("precision", canvas->nodes->buffer[i]->outputs->buffer[j]->precision); + json_encode_i32("display", canvas->nodes->buffer[i]->outputs->buffer[j]->display); + json_encode_end_object(); + } + json_encode_end_array(); + + json_encode_begin_array("buttons"); + for (int j = 0; j < canvas->nodes->buffer[i]->buttons->length; ++j) { + json_encode_begin_object(); + json_encode_string("name", canvas->nodes->buffer[i]->buttons->buffer[j]->name); + json_encode_string("type", canvas->nodes->buffer[i]->buttons->buffer[j]->type); + json_encode_i32("output", canvas->nodes->buffer[i]->buttons->buffer[j]->output); + json_encode_f32_array("default_value", canvas->nodes->buffer[i]->buttons->buffer[j]->default_value); + u8_array_t *data = canvas->nodes->buffer[i]->buttons->buffer[j]->data; + if (data != NULL) { + json_encode_string("data", data->buffer); + } + else { + json_encode_null("data"); + } + json_encode_f32("min", canvas->nodes->buffer[i]->buttons->buffer[j]->min); + json_encode_f32("max", canvas->nodes->buffer[i]->buttons->buffer[j]->max); + json_encode_f32("precision", canvas->nodes->buffer[i]->buttons->buffer[j]->precision); + json_encode_f32("height", canvas->nodes->buffer[i]->buttons->buffer[j]->height); + json_encode_end_object(); + } + json_encode_end_array(); + + json_encode_f32("width", canvas->nodes->buffer[i]->width); + json_encode_end_object(); + } + json_encode_end_array(); + + json_encode_begin_array("links"); + for (int i = 0; i < canvas->links->length; ++i) { + json_encode_begin_object(); + json_encode_i32("id", canvas->links->buffer[i]->id); + json_encode_i32("from_id", canvas->links->buffer[i]->from_id); + json_encode_i32("from_socket", canvas->links->buffer[i]->from_socket); + json_encode_i32("to_id", canvas->links->buffer[i]->to_id); + json_encode_i32("to_socket", canvas->links->buffer[i]->to_socket); + json_encode_end_object(); + } + json_encode_end_array(); + + return json_encode_end(); +} diff --git a/armorcore/sources/iron_ui_nodes.h b/armorcore/sources/iron_ui_nodes.h new file mode 100644 index 000000000..fadcb0f6b --- /dev/null +++ b/armorcore/sources/iron_ui_nodes.h @@ -0,0 +1,154 @@ +#pragma once + +#include "iron_ui.h" +#include "iron_armpack.h" + +typedef struct ui_canvas_control { + float pan_x; + float pan_y; + float zoom; + bool controls_down; +} ui_canvas_control_t; + +typedef struct ui_node_socket { + int id; + int node_id; + char *name; + char *type; + uint32_t color; + f32_array_t *default_value; + float min; + float max; + float precision; + int display; +} ui_node_socket_t; + +typedef struct ui_node_button { + char *name; + char *type; + int output; + f32_array_t *default_value; + u8_array_t *data; + float min; + float max; + float precision; + float height; +} ui_node_button_t; + +typedef struct ui_node_socket_array { + ui_node_socket_t **buffer; + int length; + int capacity; +} ui_node_socket_array_t; + +typedef struct ui_node_button_array { + ui_node_button_t **buffer; + int length; + int capacity; +} ui_node_button_array_t; + +typedef struct ui_node { + int id; + char *name; + char *type; + float x; + float y; + uint32_t color; + ui_node_socket_array_t *inputs; + ui_node_socket_array_t *outputs; + ui_node_button_array_t *buttons; + float width; +} ui_node_t; + +typedef struct ui_node_link { + int id; + int from_id; + int from_socket; + int to_id; + int to_socket; +} ui_node_link_t; + +typedef struct ui_node_array { + ui_node_t **buffer; + int length; + int capacity; +} ui_node_array_t; + +typedef struct ui_node_link_array { + ui_node_link_t **buffer; + int length; + int capacity; +} ui_node_link_array_t; + +typedef struct ui_node_canvas { + char *name; + ui_node_array_t *nodes; + ui_node_link_array_t *links; +} ui_node_canvas_t; + +typedef struct ui_nodes { + bool nodes_drag; + i32_array_t *nodes_selected_id; + float pan_x; + float pan_y; + float zoom; + int uiw; + int uih; + bool _input_started; + void (*color_picker_callback)(uint32_t); + void *color_picker_callback_data; + float scale_factor; + float ELEMENT_H; + bool dragged; + ui_node_t *move_on_top; + int link_drag_id; + bool is_new_link; + int snap_from_id; + int snap_to_id; + int snap_socket; + float snap_x; + float snap_y; + ui_handle_t *handle; +} ui_nodes_t; + +void ui_nodes_init(ui_nodes_t *nodes); +void ui_node_canvas(ui_nodes_t *nodes, ui_node_canvas_t *canvas); +void ui_nodes_rgba_popup(ui_handle_t *nhandle, float *val, int x, int y); + +void ui_remove_node(ui_node_t *n, ui_node_canvas_t *canvas); + +float UI_NODES_SCALE(); +float UI_NODES_PAN_X(); +float UI_NODES_PAN_Y(); +extern char *ui_clipboard; +extern char_ptr_array_t *ui_nodes_exclude_remove; +extern bool ui_nodes_socket_released; +extern char_ptr_array_t *(*ui_nodes_enum_texts)(char *); +extern void (*ui_nodes_on_custom_button)(int, char *); +extern ui_canvas_control_t *(*ui_nodes_on_canvas_control)(void); +extern void (*ui_nodes_on_canvas_released)(void); +extern void (*ui_nodes_on_socket_released)(int); +extern void (*ui_nodes_on_link_drag)(int, bool); + +void ui_node_canvas_encode(ui_node_canvas_t *canvas); +uint32_t ui_node_canvas_encoded_size(ui_node_canvas_t *canvas); +char *ui_node_canvas_to_json(ui_node_canvas_t *canvas); + +float UI_NODE_X(ui_node_t *node); +float UI_NODE_Y(ui_node_t *node); +float UI_NODE_W(ui_node_t *node); +float UI_NODE_H(ui_node_canvas_t *canvas, ui_node_t *node); +float UI_OUTPUT_Y(int sockets_count, int pos); +float UI_INPUT_Y(ui_node_canvas_t *canvas, ui_node_socket_t **sockets, int sockets_count, int pos); +float UI_OUTPUTS_H(int sockets_count, int length); +float UI_BUTTONS_H(ui_node_t *node); +float UI_LINE_H(); +float UI_NODES_PAN_X(); +float UI_NODES_PAN_Y(); + +float ui_p(float f); +int ui_get_socket_id(ui_node_array_t *nodes); +ui_node_link_t *ui_get_link(ui_node_link_array_t *links, int id); +int ui_next_link_id(ui_node_link_array_t *links); +ui_node_t *ui_get_node(ui_node_array_t *nodes, int id); +int ui_next_node_id(ui_node_array_t *nodes); diff --git a/armorcore/sources/iron_vec2.c b/armorcore/sources/iron_vec2.c new file mode 100644 index 000000000..a5b9c04a1 --- /dev/null +++ b/armorcore/sources/iron_vec2.c @@ -0,0 +1,72 @@ +#include "iron_vec2.h" + +#include +#include + +vec2_t vec2_create(float x, float y) { + vec2_t v; + v.x = x; + v.y = y; + return v; +} + +float vec2_len(vec2_t v) { + return sqrtf(v.x * v.x + v.y * v.y); +} + +vec2_t vec2_set_len(vec2_t v, float length) { + float current_length = vec2_len(v); + if (current_length == 0) { + return v; + } + float mul = length / current_length; + v.x *= mul; + v.y *= mul; + return v; +} + +vec2_t vec2_mult(vec2_t v, float f) { + v.x *= f; + v.y *= f; + return v; +} + +vec2_t vec2_add(vec2_t a, vec2_t b) { + a.x += b.x; + a.y += b.y; + return a; +} + +vec2_t vec2_sub(vec2_t a, vec2_t b) { + a.x -= b.x; + a.y -= b.y; + return a; +} + +float vec2_cross(vec2_t a, vec2_t b) { + return a.x * b.y - a.y * b.x; +} + +vec2_t vec2_norm(vec2_t v) { + float n = vec2_len(v); + if (n > 0.0) { + float inv_n = 1.0f / n; + v.x *= inv_n; + v.y *= inv_n; + } + return v; +} + +float vec2_dot(vec2_t a, vec2_t b) { + return a.x * b.x + a.y * b.y; +} + +vec2_t vec2_nan() { + vec2_t v; + v.x = NAN; + return v; +} + +bool vec2_isnan(vec2_t v) { + return isnan(v.x); +} diff --git a/armorcore/sources/iron_vec2.h b/armorcore/sources/iron_vec2.h new file mode 100644 index 000000000..b0585de2f --- /dev/null +++ b/armorcore/sources/iron_vec2.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include "iron_vec4.h" + +vec2_t vec2_create(float x, float y); +float vec2_len(vec2_t v); +vec2_t vec2_set_len(vec2_t v, float length); +vec2_t vec2_mult(vec2_t a, float f); +vec2_t vec2_add(vec2_t a, vec2_t b); +vec2_t vec2_sub(vec2_t a, vec2_t b); +float vec2_cross(vec2_t a, vec2_t b); +vec2_t vec2_norm(vec2_t v); +float vec2_dot(vec2_t a, vec2_t b); +vec2_t vec2_nan(); +bool vec2_isnan(vec2_t v); diff --git a/armorcore/sources/iron_vec3.c b/armorcore/sources/iron_vec3.c new file mode 100644 index 000000000..285d633a0 --- /dev/null +++ b/armorcore/sources/iron_vec3.c @@ -0,0 +1,4 @@ +#include "iron_vec3.h" + +#include +#include diff --git a/armorcore/sources/iron_vec3.h b/armorcore/sources/iron_vec3.h new file mode 100644 index 000000000..68357fb08 --- /dev/null +++ b/armorcore/sources/iron_vec3.h @@ -0,0 +1,3 @@ +#pragma once + +#include diff --git a/armorcore/sources/iron_vec4.c b/armorcore/sources/iron_vec4.c new file mode 100644 index 000000000..eb7af2650 --- /dev/null +++ b/armorcore/sources/iron_vec4.c @@ -0,0 +1,194 @@ +#include "iron_vec4.h" +#include "iron_quat.h" + +#include +#include + +vec4_t vec4_create(float x, float y, float z, float w) { + vec4_t v; + v.x = x; + v.y = y; + v.z = z; + v.w = w; + return v; +} + +vec4_t vec4_cross(vec4_t a, vec4_t b) { + vec4_t v; + v.x = a.y * b.z - a.z * b.y; + v.y = a.z * b.x - a.x * b.z; + v.z = a.x * b.y - a.y * b.x; + return v; +} + +vec4_t vec4_add(vec4_t a, vec4_t b) { + a.x += b.x; + a.y += b.y; + a.z += b.z; + a.w += b.w; + return a; +} + +vec4_t vec4_fadd(vec4_t a, float x, float y, float z, float w) { + a.x += x; + a.y += y; + a.z += z; + a.w += w; + return a; +} + +vec4_t vec4_norm(vec4_t a) { + float n = vec4_len(a); + if (n > 0.0) { + float inv_n = 1.0f / n; + a.x *= inv_n; + a.y *= inv_n; + a.z *= inv_n; + } + return a; +} + +vec4_t vec4_mult(vec4_t a, float f) { + a.x *= f; + a.y *= f; + a.z *= f; + a.w *= f; + return a; +} + +float vec4_dot(vec4_t a, vec4_t b) { + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +vec4_t vec4_clone(vec4_t v) { + return v; +} + +vec4_t vec4_lerp(vec4_t from, vec4_t to, float s) { + vec4_t v; + v.x = from.x + (to.x - from.x) * s; + v.y = from.y + (to.y - from.y) * s; + v.z = from.z + (to.z - from.z) * s; + return v; +} + +vec4_t vec4_apply_proj(vec4_t a, mat4_t m) { + vec4_t v; + float d = 1.0 / (m.m[3] * a.x + m.m[7] * a.y + m.m[11] * a.z + m.m[15]); // Perspective divide + v.x = (m.m[0] * a.x + m.m[4] * a.y + m.m[8] * a.z + m.m[12]) * d; + v.y = (m.m[1] * a.x + m.m[5] * a.y + m.m[9] * a.z + m.m[13]) * d; + v.z = (m.m[2] * a.x + m.m[6] * a.y + m.m[10] * a.z + m.m[14]) * d; + return v; +} + +vec4_t vec4_apply_mat(vec4_t a, mat4_t m) { + vec4_t v; + v.x = m.m[0] * a.x + m.m[4] * a.y + m.m[8] * a.z + m.m[12]; + v.y = m.m[1] * a.x + m.m[5] * a.y + m.m[9] * a.z + m.m[13]; + v.z = m.m[2] * a.x + m.m[6] * a.y + m.m[10] * a.z + m.m[14]; + return v; +} + +vec4_t vec4_apply_mat4(vec4_t a, mat4_t m) { + vec4_t v; + v.x = m.m[0] * a.x + m.m[4] * a.y + m.m[8] * a.z + m.m[12] * a.w; + v.y = m.m[1] * a.x + m.m[5] * a.y + m.m[9] * a.z + m.m[13] * a.w; + v.z = m.m[2] * a.x + m.m[6] * a.y + m.m[10] * a.z + m.m[14] * a.w; + v.w = m.m[3] * a.x + m.m[7] * a.y + m.m[11] * a.z + m.m[15] * a.w; + return v; +} + +vec4_t vec4_apply_axis_angle(vec4_t a, vec4_t axis, float angle) { + quat_t q = quat_from_axis_angle(axis, angle); + return vec4_apply_quat(a, q); +} + +vec4_t vec4_apply_quat(vec4_t a, quat_t q) { + vec4_t v; + float ix = q.w * a.x + q.y * a.z - q.z * a.y; + float iy = q.w * a.y + q.z * a.x - q.x * a.z; + float iz = q.w * a.z + q.x * a.y - q.y * a.x; + float iw = -q.x * a.x - q.y * a.y - q.z * a.z; + v.x = ix * q.w + iw * -q.x + iy * -q.z - iz * -q.y; + v.y = iy * q.w + iw * -q.y + iz * -q.x - ix * -q.z; + v.z = iz * q.w + iw * -q.z + ix * -q.y - iy * -q.x; + return v; +} + +bool vec4_equals(vec4_t a, vec4_t b) { + return a.x == b.x && a.y == b.y && a.z == b.z; +} + +bool vec4_almost_equals(vec4_t a, vec4_t b, float prec) { + return fabs(a.x - b.x) < prec && fabs(a.y - b.y) < prec && fabs(a.z - b.z) < prec; +} + +float vec4_len(vec4_t a) { + return sqrtf(a.x * a.x + a.y * a.y + a.z * a.z); +} + +vec4_t vec4_sub(vec4_t a, vec4_t b) { + a.x -= b.x; + a.y -= b.y; + a.z -= b.z; + return a; +} + +vec4_t vec4_exp(vec4_t a) { + a.x = expf(a.x); + a.y = expf(a.y); + a.z = expf(a.z); + return a; +} + +float vec4_dist(vec4_t v1, vec4_t v2) { + return vec4_fdist(v1.x, v1.y, v1.z, v2.x, v2.y, v2.z); +} + +float vec4_fdist(float v1x, float v1y, float v1z, float v2x, float v2y, float v2z) { + float vx = v1x - v2x; + float vy = v1y - v2y; + float vz = v1z - v2z; + return sqrtf(vx * vx + vy * vy + vz * vz); +} + +vec4_t vec4_reflect(vec4_t a, vec4_t n) { + float d = 2 * vec4_dot(a, n); + a.x -= d * n.x; + a.y -= d * n.y; + a.z -= d * n.z; + return a; +} + +vec4_t vec4_clamp(vec4_t a, float min, float max) { + float l = vec4_len(a); + if (l < min) { + return vec4_mult(vec4_norm(a), min); + } + else if (l > max) { + return vec4_mult(vec4_norm(a), max); + } + return a; +} + +vec4_t vec4_x_axis() { + return vec4_create(1.0, 0.0, 0.0, 1.0); +} + +vec4_t vec4_y_axis() { + return vec4_create(0.0, 1.0, 0.0, 1.0); +} + +vec4_t vec4_z_axis() { + return vec4_create(0.0, 0.0, 1.0, 1.0); +} + +vec4_t vec4_nan() { + vec4_t v; + v.x = NAN; + return v; +} + +bool vec4_isnan(vec4_t v) { + return isnan(v.x); +} diff --git a/armorcore/sources/iron_vec4.h b/armorcore/sources/iron_vec4.h new file mode 100644 index 000000000..d21a9fa8f --- /dev/null +++ b/armorcore/sources/iron_vec4.h @@ -0,0 +1,43 @@ +#pragma once + +#define mat4_t kinc_matrix4x4_t +#define mat3_t kinc_matrix3x3_t +#define quat_t kinc_quaternion_t +#define vec4_t kinc_vector4_t +#define vec3_t kinc_vector3_t +#define vec2_t kinc_vector2_t + +#include +#include +#include + +#include "iron_mat4.h" + +vec4_t vec4_create(float x, float y, float z, float w); +vec4_t vec4_cross(vec4_t a, vec4_t b); +vec4_t vec4_add(vec4_t a, vec4_t b); +vec4_t vec4_fadd(vec4_t a, float x, float y, float z, float w); +vec4_t vec4_norm(vec4_t a); +vec4_t vec4_mult(vec4_t v, float f); +float vec4_dot(vec4_t a, vec4_t b); +vec4_t vec4_clone(vec4_t v); +vec4_t vec4_lerp(vec4_t from, vec4_t to, float s); +vec4_t vec4_apply_proj(vec4_t a, mat4_t m); +vec4_t vec4_apply_mat(vec4_t a, mat4_t m); +vec4_t vec4_apply_mat4(vec4_t a, mat4_t m); +vec4_t vec4_apply_axis_angle(vec4_t a, vec4_t axis, float angle); +vec4_t vec4_apply_quat(vec4_t a, quat_t q); +bool vec4_equals(vec4_t a, vec4_t b); +bool vec4_almost_equals(vec4_t a, vec4_t b, float prec); +float vec4_len(vec4_t a); +vec4_t vec4_sub(vec4_t a, vec4_t b); +vec4_t vec4_exp(vec4_t a); +float vec4_dist(vec4_t v1, vec4_t v2); +float vec4_fdist(float v1x, float v1y, float v1z, float v2x, float v2y, float v2z); +vec4_t vec4_reflect(vec4_t a, vec4_t n); +vec4_t vec4_clamp(vec4_t a, float min, float max); +vec4_t vec4_x_axis(); +vec4_t vec4_y_axis(); +vec4_t vec4_z_axis(); +vec4_t vec4_nan(); +bool vec4_isnan(vec4_t v); diff --git a/armorcore/sources/kinc/audio1/a1unit.c b/armorcore/sources/kinc/audio1/a1unit.c new file mode 100644 index 000000000..16c5d9ec8 --- /dev/null +++ b/armorcore/sources/kinc/audio1/a1unit.c @@ -0,0 +1,25 @@ +#include "audio.h" + +#include + +struct kinc_a1_channel { + kinc_a1_sound_t *sound; + float position; + bool loop; + volatile float volume; + volatile float pitch; +}; + +struct kinc_a1_stream_channel { + kinc_a1_sound_stream_t *stream; + int position; +}; + +struct kinc_internal_video_channel { + struct kinc_internal_video_sound_stream *stream; + int position; +}; + +#include "audio.c.h" +#include "sound.c.h" +#include "soundstream.c.h" diff --git a/armorcore/sources/kinc/audio1/audio.c.h b/armorcore/sources/kinc/audio1/audio.c.h new file mode 100644 index 000000000..b296c8bd2 --- /dev/null +++ b/armorcore/sources/kinc/audio1/audio.c.h @@ -0,0 +1,257 @@ +#include "audio.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include + +static kinc_mutex_t mutex; + +#define CHANNEL_COUNT 16 +static kinc_a1_channel_t channels[CHANNEL_COUNT]; +static kinc_a1_stream_channel_t streamchannels[CHANNEL_COUNT]; +static kinc_internal_video_channel_t videos[CHANNEL_COUNT]; + +static float sampleLinear(int16_t *data, float position) { + int pos1 = (int)position; + int pos2 = (int)(position + 1); + float sample1 = data[pos1] / 32767.0f; + float sample2 = data[pos2] / 32767.0f; + float a = position - pos1; + return sample1 * (1 - a) + sample2 * a; +} + +static void kinc_a2_on_a1_mix(kinc_a2_buffer_t *buffer, uint32_t samples, void *userdata) { + kinc_a1_mix(buffer, samples); +} + +/*float sampleHermite4pt3oX(s16* data, float position) { + float s0 = data[(int)(position - 1)] / 32767.0f; + float s1 = data[(int)(position + 0)] / 32767.0f; + float s2 = data[(int)(position + 1)] / 32767.0f; + float s3 = data[(int)(position + 2)] / 32767.0f; + + float x = position - (int)(position); + + // 4-point, 3rd-order Hermite (x-form) + float c0 = s1; + float c1 = 0.5f * (s2 - s0); + float c2 = s0 - 2.5f * s1 + 2 * s2 - 0.5f * s3; + float c3 = 0.5f * (s3 - s0) + 1.5f * (s1 - s2); + return ((c3 * x + c2) * x + c1) * x + c0; +}*/ + +void kinc_a1_mix(kinc_a2_buffer_t *buffer, uint32_t samples) { + for (uint32_t i = 0; i < samples; ++i) { + float left_value = 0.0f; + float right_value = 0.0f; +#if 0 + __m128 sseSamples[4]; + for (int i = 0; i < channelCount; i += 4) { + s16 data[4]; + for (int i2 = 0; i2 < 4; ++i2) { + if (channels[i + i2].sound != nullptr) { + data[i2] = *(s16*)&channels[i + i2].sound->data[channels[i + i2].position]; + channels[i + i2].position += 2; + if (channels[i + i2].position >= channels[i + i2].sound->size) channels[i + i2].sound = nullptr; + } + else { + data[i2] = 0; + } + } + sseSamples[i / 4] = _mm_set_ps(data[3] / 32767.0f, data[2] / 32767.0f, data[1] / 32767.0f, data[0] / 32767.0f); + } + __m128 a = _mm_add_ps(sseSamples[0], sseSamples[1]); + __m128 b = _mm_add_ps(sseSamples[2], sseSamples[3]); + __m128 c = _mm_add_ps(a, b); + value = c.m128_f32[0] + c.m128_f32[1] + c.m128_f32[2] + c.m128_f32[3]; + value = max(min(value, 1.0f), -1.0f); +#else + kinc_mutex_lock(&mutex); + for (int i = 0; i < CHANNEL_COUNT; ++i) { + if (channels[i].sound != NULL) { + left_value += sampleLinear(channels[i].sound->left, channels[i].position) * channels[i].volume * channels[i].sound->volume; + right_value = kinc_max(kinc_min(right_value, 1.0f), -1.0f); + right_value += sampleLinear(channels[i].sound->right, channels[i].position) * channels[i].volume * channels[i].sound->volume; + left_value = kinc_max(kinc_min(left_value, 1.0f), -1.0f); + + channels[i].position += channels[i].pitch / channels[i].sound->sample_rate_pos; + // channels[i].position += 2; + if (channels[i].position + 1 >= channels[i].sound->size) { + if (channels[i].loop) { + channels[i].position = 0; + } + else { + channels[i].sound = NULL; + } + } + } + } + for (int i = 0; i < CHANNEL_COUNT; ++i) { + if (streamchannels[i].stream != NULL) { + float *samples = kinc_a1_sound_stream_next_frame(streamchannels[i].stream); + left_value += samples[0] * kinc_a1_sound_stream_volume(streamchannels[i].stream); + left_value = kinc_max(kinc_min(left_value, 1.0f), -1.0f); + right_value += samples[1] * kinc_a1_sound_stream_volume(streamchannels[i].stream); + right_value = kinc_max(kinc_min(right_value, 1.0f), -1.0f); + if (kinc_a1_sound_stream_ended(streamchannels[i].stream)) { + streamchannels[i].stream = NULL; + } + } + } + + for (int i = 0; i < CHANNEL_COUNT; ++i) { + if (videos[i].stream != NULL) { + float *samples = kinc_internal_video_sound_stream_next_frame(videos[i].stream); + left_value += samples[0]; + left_value = kinc_max(kinc_min(left_value, 1.0f), -1.0f); + right_value += samples[1]; + right_value = kinc_max(kinc_min(right_value, 1.0f), -1.0f); + if (kinc_internal_video_sound_stream_ended(videos[i].stream)) { + videos[i].stream = NULL; + } + } + } + + kinc_mutex_unlock(&mutex); +#endif + assert(buffer->channel_count >= 2); + buffer->channels[0][buffer->write_location] = left_value; + buffer->channels[1][buffer->write_location] = right_value; + + buffer->write_location += 1; + if (buffer->write_location >= buffer->data_size) { + buffer->write_location = 0; + } + } +} + +void kinc_a1_init(void) { + for (int i = 0; i < CHANNEL_COUNT; ++i) { + channels[i].sound = NULL; + channels[i].position = 0; + } + for (int i = 0; i < CHANNEL_COUNT; ++i) { + streamchannels[i].stream = NULL; + streamchannels[i].position = 0; + } + kinc_mutex_init(&mutex); + + kinc_a2_init(); + kinc_a2_set_callback(kinc_a2_on_a1_mix, NULL); +} + +kinc_a1_channel_t *kinc_a1_play_sound(kinc_a1_sound_t *sound, bool loop, float pitch, bool unique) { + kinc_a1_channel_t *channel = NULL; + kinc_mutex_lock(&mutex); + bool found = false; + for (int i = 0; i < CHANNEL_COUNT; ++i) { + if (channels[i].sound == sound) { + found = true; + break; + } + } + if (!found || !unique) { + for (int i = 0; i < CHANNEL_COUNT; ++i) { + if (channels[i].sound == NULL) { + channels[i].sound = sound; + channels[i].position = 0; + channels[i].loop = loop; + channels[i].pitch = pitch; + channels[i].volume = sound->volume; + channel = &channels[i]; + break; + } + } + } + kinc_mutex_unlock(&mutex); + return channel; +} + +void kinc_a1_stop_sound(kinc_a1_sound_t *sound) { + kinc_mutex_lock(&mutex); + for (int i = 0; i < CHANNEL_COUNT; ++i) { + if (channels[i].sound == sound) { + channels[i].sound = NULL; + channels[i].position = 0; + break; + } + } + kinc_mutex_unlock(&mutex); +} + +void kinc_a1_play_sound_stream(kinc_a1_sound_stream_t *stream) { + kinc_mutex_lock(&mutex); + + for (int i = 0; i < CHANNEL_COUNT; ++i) { + if (streamchannels[i].stream == stream) { + streamchannels[i].stream = NULL; + streamchannels[i].position = 0; + break; + } + } + + for (int i = 0; i < CHANNEL_COUNT; ++i) { + if (streamchannels[i].stream == NULL) { + streamchannels[i].stream = stream; + streamchannels[i].position = 0; + break; + } + } + + kinc_mutex_unlock(&mutex); +} + +void kinc_a1_stop_sound_stream(kinc_a1_sound_stream_t *stream) { + kinc_mutex_lock(&mutex); + for (int i = 0; i < CHANNEL_COUNT; ++i) { + if (streamchannels[i].stream == stream) { + streamchannels[i].stream = NULL; + streamchannels[i].position = 0; + break; + } + } + kinc_mutex_unlock(&mutex); +} + +void kinc_internal_play_video_sound_stream(struct kinc_internal_video_sound_stream *stream) { + kinc_mutex_lock(&mutex); + for (int i = 0; i < CHANNEL_COUNT; ++i) { + if (videos[i].stream == NULL) { + videos[i].stream = stream; + videos[i].position = 0; + break; + } + } + kinc_mutex_unlock(&mutex); +} + +void kinc_internal_stop_video_sound_stream(struct kinc_internal_video_sound_stream *stream) { + kinc_mutex_lock(&mutex); + for (int i = 0; i < CHANNEL_COUNT; ++i) { + if (videos[i].stream == stream) { + videos[i].stream = NULL; + videos[i].position = 0; + break; + } + } + kinc_mutex_unlock(&mutex); +} + +float kinc_a1_channel_get_volume(kinc_a1_channel_t *channel) { + return channel->volume; +} + +void kinc_a1_channel_set_volume(kinc_a1_channel_t *channel, float volume) { + KINC_ATOMIC_EXCHANGE_FLOAT(&channel->volume, volume); +} + +void kinc_a1_channel_set_pitch(kinc_a1_channel_t *channel, float pitch) { + KINC_ATOMIC_EXCHANGE_FLOAT(&channel->pitch, pitch); +} diff --git a/armorcore/sources/kinc/audio1/audio.h b/armorcore/sources/kinc/audio1/audio.h new file mode 100644 index 000000000..035479722 --- /dev/null +++ b/armorcore/sources/kinc/audio1/audio.h @@ -0,0 +1,97 @@ +#pragma once + +#include + +#include "sound.h" +#include "soundstream.h" + +#include + +/*! \file audio.h + \brief Audio1 is a high-level audio-API that lets you directly play audio-files. Depending on the target-system it either sits directly on a high-level + system audio-API or is implemented based on Audio2. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +struct kinc_internal_video_sound_stream; + +struct kinc_a1_channel; +typedef struct kinc_a1_channel kinc_a1_channel_t; + +struct kinc_a1_stream_channel; +typedef struct kinc_a1_stream_channel kinc_a1_stream_channel_t; + +struct kinc_internal_video_channel; +typedef struct kinc_internal_video_channel kinc_internal_video_channel_t; + +///

+/// Initialize the Audio1-API. +/// +void kinc_a1_init(void); + +/// +/// Plays a sound immediately. +/// +/// The sound to play +/// Whether or not to automatically loop the sound +/// Changes the pitch by providing a value that's not 1.0f +/// Makes sure that a sound is not played more than once at the same time +/// A channel object that can be used to control the playing sound. Please be aware that NULL is returned when the maximum number of simultaneously +/// played channels was reached. +kinc_a1_channel_t *kinc_a1_play_sound(kinc_a1_sound_t *sound, bool loop, float pitch, bool unique); + +/// +/// Stops the sound from playing. +/// +/// The sound to stop +void kinc_a1_stop_sound(kinc_a1_sound_t *sound); + +/// +/// Starts playing a sound-stream. +/// +/// The stream to play +void kinc_a1_play_sound_stream(kinc_a1_sound_stream_t *stream); + +/// +/// Stops a sound-stream from playing. +/// +/// The stream to stop. +void kinc_a1_stop_sound_stream(kinc_a1_sound_stream_t *stream); + +/// +/// Gets the current volume of a channel. Only works correctly if the channel is still playing. +/// +/// The channel to get the volume from +/// The volume +float kinc_a1_channel_get_volume(kinc_a1_channel_t *channel); + +/// +/// Sets the current volume of a channel. Only works correctly if the channel is still playing. +/// +/// The channel to set the volume for +/// The volume to set +/// +void kinc_a1_channel_set_volume(kinc_a1_channel_t *channel, float volume); + +void kinc_a1_channel_set_pitch(kinc_a1_channel_t *channel, float pitch); + +/// +/// Mixes audio into the provided buffer. kinc_a1_init sets this as the callback for a2 +/// but you can also call it manually to mix a1-audio with your own audio. To do that, +/// first call kinc_a1_init, then call kinc_a2_set_callback to set it to your own callback +/// and call kinc_a1_mix from within that callback. Please be aware that the callback +/// will run in a separate audio-thread. +/// +/// The audio-buffer to be filled +/// The number of samples to be filled in +void kinc_a1_mix(kinc_a2_buffer_t *buffer, uint32_t samples); + +void kinc_internal_play_video_sound_stream(struct kinc_internal_video_sound_stream *stream); +void kinc_internal_stop_video_sound_stream(struct kinc_internal_video_sound_stream *stream); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/audio1/sound.c.h b/armorcore/sources/kinc/audio1/sound.c.h new file mode 100644 index 000000000..ce8e9a5f3 --- /dev/null +++ b/armorcore/sources/kinc/audio1/sound.c.h @@ -0,0 +1,224 @@ +#include "sound.h" + +#define STB_VORBIS_HEADER_ONLY +#include + +#include +#include +#include + +#include +#include + +struct WaveData { + uint16_t audioFormat; + uint16_t numChannels; + uint32_t sampleRate; + uint32_t bytesPerSecond; + uint16_t bitsPerSample; + uint32_t dataSize; + uint8_t *data; +}; + +static void checkFOURCC(uint8_t **data, const char *fourcc) { + for (int i = 0; i < 4; ++i) { + kinc_affirm(**data == fourcc[i]); + ++*data; + } +} + +static void readFOURCC(uint8_t **data, char *fourcc) { + for (int i = 0; i < 4; ++i) { + fourcc[i] = **data; + ++*data; + } + fourcc[4] = 0; +} + +static void readChunk(uint8_t **data, struct WaveData *wave) { + char fourcc[5]; + readFOURCC(data, fourcc); + uint32_t chunksize = kinc_read_u32le(*data); + *data += 4; + if (strcmp(fourcc, "fmt ") == 0) { + wave->audioFormat = kinc_read_u16le(*data + 0); + wave->numChannels = kinc_read_u16le(*data + 2); + wave->sampleRate = kinc_read_u32le(*data + 4); + wave->bytesPerSecond = kinc_read_u32le(*data + 8); + wave->bitsPerSample = kinc_read_u16le(*data + 14); + *data += chunksize; + } + else if (strcmp(fourcc, "data") == 0) { + wave->dataSize = chunksize; + wave->data = (uint8_t *)malloc(chunksize * sizeof(uint8_t)); + kinc_affirm(wave->data != NULL); + memcpy(wave->data, *data, chunksize); + *data += chunksize; + } + else { + *data += chunksize; + } +} + +static int16_t convert8to16(uint8_t sample) { + return (sample - 127) << 8; +} + +static void splitStereo8(uint8_t *data, int size, int16_t *left, int16_t *right) { + for (int i = 0; i < size; ++i) { + left[i] = convert8to16(data[i * 2 + 0]); + right[i] = convert8to16(data[i * 2 + 1]); + } +} + +static void splitStereo16(int16_t *data, int size, int16_t *left, int16_t *right) { + for (int i = 0; i < size; ++i) { + left[i] = data[i * 2 + 0]; + right[i] = data[i * 2 + 1]; + } +} + +static void splitMono8(uint8_t *data, int size, int16_t *left, int16_t *right) { + for (int i = 0; i < size; ++i) { + left[i] = convert8to16(data[i]); + right[i] = convert8to16(data[i]); + } +} + +static void splitMono16(int16_t *data, int size, int16_t *left, int16_t *right) { + for (int i = 0; i < size; ++i) { + left[i] = data[i]; + right[i] = data[i]; + } +} + +#define MAXIMUM_SOUNDS 1024 +static kinc_a1_sound_t sounds[MAXIMUM_SOUNDS] = {0}; + +static kinc_a1_sound_t *find_sound(void) { + for (int i = 0; i < MAXIMUM_SOUNDS; ++i) { + if (!sounds[i].in_use) { + return &sounds[i]; + } + } + return NULL; +} + +kinc_a1_sound_t *kinc_a1_sound_create(const char *filename) { + kinc_a1_sound_t *sound = find_sound(); + assert(sound != NULL); + sound->in_use = true; + sound->volume = 1.0f; + sound->size = 0; + sound->left = NULL; + sound->right = NULL; + size_t filenameLength = strlen(filename); + uint8_t *data = NULL; + + if (strncmp(&filename[filenameLength - 4], ".ogg", 4) == 0) { + kinc_file_reader_t file; + if (!kinc_file_reader_open(&file, filename, KINC_FILE_TYPE_ASSET)) { + sound->in_use = false; + return NULL; + } + uint8_t *filedata = (uint8_t *)malloc(kinc_file_reader_size(&file)); + kinc_file_reader_read(&file, filedata, kinc_file_reader_size(&file)); + kinc_file_reader_close(&file); + + int channels, sample_rate; + int samples = stb_vorbis_decode_memory(filedata, (int)kinc_file_reader_size(&file), &channels, &sample_rate, (short **)&data); + sound->channel_count = (uint8_t)channels; + sound->samples_per_second = (uint32_t)sample_rate; + sound->size = samples * 2 * sound->channel_count; + sound->bits_per_sample = 16; + free(filedata); + } + else if (strncmp(&filename[filenameLength - 4], ".wav", 4) == 0) { + struct WaveData wave = {0}; + { + kinc_file_reader_t file; + if (!kinc_file_reader_open(&file, filename, KINC_FILE_TYPE_ASSET)) { + sound->in_use = false; + return NULL; + } + uint8_t *filedata = (uint8_t *)malloc(kinc_file_reader_size(&file)); + kinc_file_reader_read(&file, filedata, kinc_file_reader_size(&file)); + kinc_file_reader_close(&file); + uint8_t *data = filedata; + + checkFOURCC(&data, "RIFF"); + uint32_t filesize = kinc_read_u32le(data); + data += 4; + checkFOURCC(&data, "WAVE"); + while (data + 8 - filedata < (intptr_t)filesize) { + readChunk(&data, &wave); + } + + free(filedata); + } + + sound->bits_per_sample = (uint8_t)wave.bitsPerSample; + sound->channel_count = (uint8_t)wave.numChannels; + sound->samples_per_second = wave.sampleRate; + data = wave.data; + sound->size = wave.dataSize; + } + else { + assert(false); + } + + if (sound->channel_count == 1) { + if (sound->bits_per_sample == 8) { + sound->left = (int16_t *)malloc(sound->size * sizeof(int16_t)); + sound->right = (int16_t *)malloc(sound->size * sizeof(int16_t)); + splitMono8(data, sound->size, sound->left, sound->right); + } + else if (sound->bits_per_sample == 16) { + sound->size /= 2; + sound->left = (int16_t *)malloc(sound->size * sizeof(int16_t)); + sound->right = (int16_t *)malloc(sound->size * sizeof(int16_t)); + splitMono16((int16_t *)data, sound->size, sound->left, sound->right); + } + else { + kinc_affirm(false); + } + } + else { + // Left and right channel are in s16 audio stream, alternating. + if (sound->bits_per_sample == 8) { + sound->size /= 2; + sound->left = (int16_t *)malloc(sound->size * sizeof(int16_t)); + sound->right = (int16_t *)malloc(sound->size * sizeof(int16_t)); + splitStereo8(data, sound->size, sound->left, sound->right); + } + else if (sound->bits_per_sample == 16) { + sound->size /= 4; + sound->left = (int16_t *)malloc(sound->size * sizeof(int16_t)); + sound->right = (int16_t *)malloc(sound->size * sizeof(int16_t)); + splitStereo16((int16_t *)data, sound->size, sound->left, sound->right); + } + else { + kinc_affirm(false); + } + } + sound->sample_rate_pos = 44100 / (float)sound->samples_per_second; + free(data); + + return sound; +} + +void kinc_a1_sound_destroy(kinc_a1_sound_t *sound) { + free(sound->left); + free(sound->right); + sound->left = NULL; + sound->right = NULL; + sound->in_use = false; +} + +float kinc_a1_sound_volume(kinc_a1_sound_t *sound) { + return sound->volume; +} + +void kinc_a1_sound_set_volume(kinc_a1_sound_t *sound, float value) { + sound->volume = value; +} diff --git a/armorcore/sources/kinc/audio1/sound.h b/armorcore/sources/kinc/audio1/sound.h new file mode 100644 index 000000000..ebe4af9ec --- /dev/null +++ b/armorcore/sources/kinc/audio1/sound.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include + +#include + +/*! \file sound.h + \brief Sounds are pre-decoded on load and therefore primarily useful for playing sound-effects. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_a1_sound { + uint8_t channel_count; + uint8_t bits_per_sample; + uint32_t samples_per_second; + int16_t *left; + int16_t *right; + int size; + float sample_rate_pos; + float volume; + bool in_use; +} kinc_a1_sound_t; + +/// +/// Create a sound from a wav file. +/// +/// Path to a wav file +/// The newly created sound +kinc_a1_sound_t *kinc_a1_sound_create(const char *filename); + +/// +/// Destroy a sound. +/// +/// The sound to destroy. +void kinc_a1_sound_destroy(kinc_a1_sound_t *sound); + +/// +/// Gets the current volume-multiplicator that's used when playing the sound. +/// +/// The sound to query +/// The volume-multiplicator +float kinc_a1_sound_volume(kinc_a1_sound_t *sound); + +/// +/// Sets the volume-multiplicator that's used when playing the sound. +/// +/// The sound to modify +/// The volume-multiplicator to set +void kinc_a1_sound_set_volume(kinc_a1_sound_t *sound, float value); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/audio1/soundstream.c.h b/armorcore/sources/kinc/audio1/soundstream.c.h new file mode 100644 index 000000000..70399f8cd --- /dev/null +++ b/armorcore/sources/kinc/audio1/soundstream.c.h @@ -0,0 +1,128 @@ +#include "soundstream.h" + +#define STB_VORBIS_HEADER_ONLY +#include + +#include + +#include +#include + +static kinc_a1_sound_stream_t streams[256]; +static int nextStream = 0; +static uint8_t buffer[1024 * 10]; +static int bufferIndex; + +kinc_a1_sound_stream_t *kinc_a1_sound_stream_create(const char *filename, bool looping) { + kinc_a1_sound_stream_t *stream = &streams[nextStream]; + stream->myLooping = looping; + stream->myVolume = 1; + stream->rateDecodedHack = false; + stream->end = false; + kinc_file_reader_t file; + kinc_file_reader_open(&file, filename, KINC_FILE_TYPE_ASSET); + stream->buffer = &buffer[bufferIndex]; + bufferIndex += (int)kinc_file_reader_size(&file); + uint8_t *filecontent = (uint8_t *)malloc(kinc_file_reader_size(&file)); + kinc_file_reader_read(&file, filecontent, kinc_file_reader_size(&file)); + kinc_file_reader_close(&file); + memcpy(stream->buffer, filecontent, kinc_file_reader_size(&file)); + free(filecontent); + stream->vorbis = stb_vorbis_open_memory(buffer, (int)kinc_file_reader_size(&file), NULL, NULL); + if (stream->vorbis != NULL) { + stb_vorbis_info info = stb_vorbis_get_info(stream->vorbis); + stream->chans = info.channels; + stream->rate = info.sample_rate; + } + else { + stream->chans = 2; + stream->rate = 22050; + } + ++nextStream; + return stream; +} + +int kinc_a1_sound_stream_channels(kinc_a1_sound_stream_t *stream) { + return stream->chans; +} + +int kinc_a1_sound_stream_sample_rate(kinc_a1_sound_stream_t *stream) { + return stream->rate; +} + +bool kinc_a1_sound_stream_looping(kinc_a1_sound_stream_t *stream) { + return stream->myLooping; +} + +void kinc_a1_sound_stream_set_looping(kinc_a1_sound_stream_t *stream, bool loop) { + stream->myLooping = loop; +} + +float kinc_a1_sound_stream_volume(kinc_a1_sound_stream_t *stream) { + return stream->myVolume; +} + +void kinc_a1_sound_stream_set_volume(kinc_a1_sound_stream_t *stream, float value) { + stream->myVolume = value; +} + +bool kinc_a1_sound_stream_ended(kinc_a1_sound_stream_t *stream) { + return stream->end; +} + +float kinc_a1_sound_stream_length(kinc_a1_sound_stream_t *stream) { + if (stream->vorbis == NULL) + return 0; + return stb_vorbis_stream_length_in_seconds(stream->vorbis); +} + +float kinc_a1_sound_stream_position(kinc_a1_sound_stream_t *stream) { + if (stream->vorbis == NULL) + return 0; + return stb_vorbis_get_sample_offset(stream->vorbis) / stb_vorbis_stream_length_in_samples(stream->vorbis) * kinc_a1_sound_stream_length(stream); +} + +void kinc_a1_sound_stream_reset(kinc_a1_sound_stream_t *stream) { + if (stream->vorbis != NULL) + stb_vorbis_seek_start(stream->vorbis); + stream->end = false; + stream->rateDecodedHack = false; +} + +float *kinc_a1_sound_stream_next_frame(kinc_a1_sound_stream_t *stream) { + if (stream->vorbis == NULL) { + for (int i = 0; i < stream->chans; ++i) { + stream->samples[i] = 0; + } + return stream->samples; + } + if (stream->rate == 22050) { + if (stream->rateDecodedHack) { + stream->rateDecodedHack = false; + return stream->samples; + } + } + + float left, right; + float *samples_array[2] = {&left, &right}; + int read = stb_vorbis_get_samples_float(stream->vorbis, stream->chans, samples_array, 1); + if (read == 0) { + if (kinc_a1_sound_stream_looping(stream)) { + stb_vorbis_seek_start(stream->vorbis); + stb_vorbis_get_samples_float(stream->vorbis, stream->chans, samples_array, 1); + } + else { + stream->end = true; + for (int i = 0; i < stream->chans; ++i) { + stream->samples[i] = 0; + } + return stream->samples; + } + } + + stream->samples[0] = samples_array[0][0]; + stream->samples[1] = samples_array[1][0]; + + stream->rateDecodedHack = true; + return stream->samples; +} diff --git a/armorcore/sources/kinc/audio1/soundstream.h b/armorcore/sources/kinc/audio1/soundstream.h new file mode 100644 index 000000000..d9bda04ac --- /dev/null +++ b/armorcore/sources/kinc/audio1/soundstream.h @@ -0,0 +1,116 @@ +#pragma once + +#include + +#include +#include + +/*! \file soundstream.h + \brief Sound-Streams are decoded while playing and as such are useful for large audio-files like music or speech. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +struct stb_vorbis; + +typedef struct kinc_a1_sound_stream { + struct stb_vorbis *vorbis; + int chans; + int rate; + bool myLooping; + float myVolume; + bool rateDecodedHack; + bool end; + float samples[2]; + uint8_t *buffer; +} kinc_a1_sound_stream_t; + +/// +/// Create a sound-stream from a wav file. +/// +/// A path to a wav file +/// Defines whether the stream will be looped automatically +/// The newly created sound-stream +kinc_a1_sound_stream_t *kinc_a1_sound_stream_create(const char *filename, bool looping); + +/// +/// Gets the next audio-frame in the stream. +/// +/// The stream to extract the frame from +/// The next sample +float *kinc_a1_sound_stream_next_frame(kinc_a1_sound_stream_t *stream); + +/// +/// Gets the number of audio-channels the stream uses. +/// +/// The stream to extract the number of channels from +/// The number of audio-channels +int kinc_a1_sound_stream_channels(kinc_a1_sound_stream_t *stream); + +/// +/// Gets the sample-rate used by the stream. +/// +/// The stream to extract the sample-rate from +/// The sample-rate of the stream +int kinc_a1_sound_stream_sample_rate(kinc_a1_sound_stream_t *stream); + +/// +/// Returns whether the stream loops automatically. +/// +/// The stream to extract the looping-information from +/// Whether the stream loops +bool kinc_a1_sound_stream_looping(kinc_a1_sound_stream_t *stream); + +/// +/// Changes whether the stream is looped automatically. +/// +/// The stream to change +/// The new loop value to set +void kinc_a1_sound_stream_set_looping(kinc_a1_sound_stream_t *stream, bool loop); + +/// +/// Returns whether the stream finished playing. +/// +/// The stream to query +/// Whether the stream finished playing +bool kinc_a1_sound_stream_ended(kinc_a1_sound_stream_t *stream); + +/// +/// Returns the length of the stream. +/// +/// The stream to query +/// The length of the stream in seconds +float kinc_a1_sound_stream_length(kinc_a1_sound_stream_t *stream); + +/// +/// Returns the current playing-position of the stream. +/// +/// The stream to query +/// The current playing-position in seconds +float kinc_a1_sound_stream_position(kinc_a1_sound_stream_t *stream); + +/// +/// Resets the stream to its start-position. +/// +/// The stream to change +void kinc_a1_sound_stream_reset(kinc_a1_sound_stream_t *stream); + +/// +/// Gets the stream's volume-multiplicator. +/// +/// The stream to query +/// The volume-multiplicator +float kinc_a1_sound_stream_volume(kinc_a1_sound_stream_t *stream); + +/// +/// Sets the stream's volume-multiplicator. +/// +/// The stream to change +/// The volume-multiplicator +void kinc_a1_sound_stream_set_volume(kinc_a1_sound_stream_t *stream, float value); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/audio2/audio.c b/armorcore/sources/kinc/audio2/audio.c new file mode 100644 index 000000000..4da0efb9e --- /dev/null +++ b/armorcore/sources/kinc/audio2/audio.c @@ -0,0 +1,3 @@ +#define KINC_IMPLEMENTATION_AUDIO2 + +#include "audio.h" diff --git a/armorcore/sources/kinc/audio2/audio.h b/armorcore/sources/kinc/audio2/audio.h new file mode 100644 index 000000000..f4ba77e9a --- /dev/null +++ b/armorcore/sources/kinc/audio2/audio.h @@ -0,0 +1,123 @@ +#pragma once + +#include + +#include + +/*! \file audio.h + \brief Audio2 is a low-level audio-API that allows you to directly provide a stream of audio-samples. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#define KINC_A2_MAX_CHANNELS 8 + +typedef struct kinc_a2_buffer { + uint8_t channel_count; + float *channels[KINC_A2_MAX_CHANNELS]; + uint32_t data_size; + uint32_t read_location; + uint32_t write_location; +} kinc_a2_buffer_t; + +/// +/// Initializes the Audio2-API. +/// +void kinc_a2_init(void); + +/// +/// Sets the callback that's used to provide audio-samples. This is the primary method of operation for Audio2. The callback is expected to write the requested +/// number of samples into the ring-buffer. The callback is typically called from the system's audio-thread to minimize audio-latency. +/// +/// The callback to set +/// The user data provided to the callback +void kinc_a2_set_callback(void (*kinc_a2_audio_callback)(kinc_a2_buffer_t *buffer, uint32_t samples, void *userdata), void *userdata); + +/// +/// The current sample-rate of the system. +/// +uint32_t kinc_a2_samples_per_second(void); + +/// +/// Sets a callback that's called when the system's sample-rate changes. +/// +/// The callback to set +/// The user data provided to the callback +/// +void kinc_a2_set_sample_rate_callback(void (*kinc_a2_sample_rate_callback)(void *userdata), void *userdata); + +/// +/// kinc_a2_update should be called every frame. It is required by some systems to pump their audio-loops but on most systems it is a no-op. +/// +void kinc_a2_update(void); + +/// +/// Shuts down the Audio2-API. +/// +void kinc_a2_shutdown(void); + +void kinc_a2_internal_init(void); +bool kinc_a2_internal_callback(kinc_a2_buffer_t *buffer, int samples); +void kinc_a2_internal_sample_rate_callback(void); + +#ifdef KINC_IMPLEMENTATION_AUDIO2 +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#include +#include +#include + +static kinc_mutex_t mutex; + +static void (*a2_callback)(kinc_a2_buffer_t *buffer, uint32_t samples, void *userdata) = NULL; +static void *a2_userdata = NULL; + +void kinc_a2_set_callback(void (*kinc_a2_audio_callback)(kinc_a2_buffer_t *buffer, uint32_t samples, void *userdata), void *userdata) { + kinc_mutex_lock(&mutex); + a2_callback = kinc_a2_audio_callback; + a2_userdata = userdata; + kinc_mutex_unlock(&mutex); +} + +static void (*a2_sample_rate_callback)(void *userdata) = NULL; +static void *a2_sample_rate_userdata = NULL; + +void kinc_a2_set_sample_rate_callback(void (*kinc_a2_sample_rate_callback)(void *userdata), void *userdata) { + kinc_mutex_lock(&mutex); + a2_sample_rate_callback = kinc_a2_sample_rate_callback; + a2_sample_rate_userdata = userdata; + kinc_mutex_unlock(&mutex); +} + +void kinc_a2_internal_init(void) { + kinc_mutex_init(&mutex); +} + +bool kinc_a2_internal_callback(kinc_a2_buffer_t *buffer, int samples) { + kinc_mutex_lock(&mutex); + bool has_callback = a2_callback != NULL; + if (has_callback) { + a2_callback(buffer, samples, a2_userdata); + } + kinc_mutex_unlock(&mutex); + return has_callback; +} + +void kinc_a2_internal_sample_rate_callback(void) { + kinc_mutex_lock(&mutex); + if (a2_sample_rate_callback != NULL) { + a2_sample_rate_callback(a2_sample_rate_userdata); + } + kinc_mutex_unlock(&mutex); +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/color.h b/armorcore/sources/kinc/color.h new file mode 100644 index 000000000..0c1b7e7d3 --- /dev/null +++ b/armorcore/sources/kinc/color.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include + +/*! \file color.h + \brief Provides some utility functionality for handling 32 bit ARGB color values. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/// +/// Splits up an 32 bit ARGB color value into its components. +/// +void kinc_color_components(uint32_t color, float *red, float *green, float *blue, float *alpha); + +#define KINC_COLOR_BLACK 0xff000000 +#define KINC_COLOR_WHITE 0xffffffff +#define KINC_COLOR_RED 0xffff0000 +#define KINC_COLOR_BLUE 0xff0000ff +#define KINC_COLOR_GREEN 0xff00ff00 +#define KINC_COLOR_MAGENTA 0xffff00ff +#define KINC_COLOR_YELLOW 0xffffff00 +#define KINC_COLOR_CYAN 0xff00ffff + +#ifdef KINC_IMPLEMENTATION_ROOT +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +void kinc_color_components(uint32_t color, float *red, float *green, float *blue, float *alpha) { + *alpha = ((color & 0xff000000) >> 24) / 255.0f; + *red = ((color & 0x00ff0000) >> 16) / 255.0f; + *green = ((color & 0x0000ff00) >> 8) / 255.0f; + *blue = (color & 0x000000ff) / 255.0f; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/display.h b/armorcore/sources/kinc/display.h new file mode 100644 index 000000000..bd0aaa112 --- /dev/null +++ b/armorcore/sources/kinc/display.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +#include + +/*! \file display.h + \brief Provides information for the active displays. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_display_mode { + int x; + int y; + int width; + int height; + int pixels_per_inch; + int frequency; + int bits_per_pixel; +} kinc_display_mode_t; + +/// +/// Allows retrieval of display values prior to the kinc_init call. +/// +void kinc_display_init(void); + +/// +/// Retrieves the index of the primary display. +/// +/// The index of the primary display +int kinc_primary_display(void); + +/// +/// Retrieves the number of displays connected to the system. +/// +/// +/// All indices from 0 to kinc_count_displays() - 1 are legal display indices. +/// +/// The number of displays connected to the system +int kinc_count_displays(void); + +/// +/// Checks whether the display index points to an available display. +/// +/// Index of the display to check +/// +/// Returns true if the index points to an available display, +/// false otherwise +/// +bool kinc_display_available(int display_index); + +/// +/// Retrieves the system name of a display. +/// +/// Index of the display to retrieve the name from +/// The system name of the display +const char *kinc_display_name(int display_index); + +/// +/// Retrieves the current mode of a display. +/// +/// Index of the display to retrieve the mode from +/// The current display mode +kinc_display_mode_t kinc_display_current_mode(int display_index); + +/// +/// Retrieves the number of available modes of a display. +/// +/// Index of the display to retrieve the modes count from +/// The number of available modes of the display +int kinc_display_count_available_modes(int display_index); + +/// +/// Retrieves a specific mode of a display. +/// +/// Index of the display to retrieve the mode from +/// Index of the mode to retrieve +/// The display mode +kinc_display_mode_t kinc_display_available_mode(int display_index, int mode_index); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/error.h b/armorcore/sources/kinc/error.h new file mode 100644 index 000000000..60db4b097 --- /dev/null +++ b/armorcore/sources/kinc/error.h @@ -0,0 +1,200 @@ +#pragma once + +#include + +#include + +/*! \file error.h + \brief Contains functionality to stop the program in case of an error and create a user-visible error message. + + The affirm and error functions print an error message and then exit the program. Error messages can be made + visible to the user (unless a console window is active this is only implemented for Windows). +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/// +/// Exits the program when a condition is untrue and shows +/// a generic error message. +/// +/// +/// This is an alternative to assert which also persists in release +/// builds. Use this instead of assert in situations where you want +/// your users to see what's going wrong. +/// This uses Kinc's log and error functionality to make errors +/// visible. +/// +/// +/// Exits the program if condition is false, +/// otherwise does nothing. +/// +void kinc_affirm(bool condition); + +/// +/// Exits the program when a condition is untrue and shows +/// a provided error message. +/// +/// +/// This is equivalent to kinc_affirm() but uses the provided message +/// instead of a generic one. +/// +/// +/// Exits the program if condition is false, +/// otherwise does nothing. +/// +/// +/// The parameter is equivalent to the first printf parameter. +/// +/// +/// The parameter is equivalent to the second printf parameter. +/// +void kinc_affirm_message(bool condition, const char *format, ...); + +/// +/// Equivalent to kinc_affirm_message but uses a va_list parameter. +/// +/// +/// You will need this if you want to provide the parameters using va_start/va_end. +/// +/// +/// Exits the program if condition is false, +/// otherwise does nothing. +/// +/// +/// The parameter is equivalent to the first vprintf parameter. +/// +/// +/// The parameter is equivalent to the second vprintf parameter. +/// +void kinc_affirm_args(bool condition, const char *format, va_list args); + +/// +/// Exits the program and shows a generic error message +/// +/// +/// Mainly this just calls exit(EXIT_FAILURE) but will also use +/// Kore's log function and on Windows show an error message box. +/// +void kinc_error(void); + +/// +/// Exits the program and shows a provided error message. +/// +/// +/// This is equivalent to kinc_error() but uses the provided message +/// instead of a generic one. +/// +/// +/// The parameter is equivalent to the first printf parameter. +/// +/// +/// The parameter is equivalent to the second printf parameter. +/// +void kinc_error_message(const char *format, ...); + +/// +/// Equivalent to kinc_error_message but uses a va_list parameter. +/// +/// +/// You will need this if you want to provide the parameters using va_start/va_end. +/// +/// +/// The parameter is equivalent to the first vprintf parameter. +/// +/// +/// The parameter is equivalent to the second vprintf parameter. +/// +void kinc_error_args(const char *format, va_list args); + +#ifdef KINC_IMPLEMENTATION_ROOT +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#ifndef KINC_IMPLEMENTATION_ROOT +#undef KINC_IMPLEMENTATION +#endif +#include +#ifndef KINC_IMPLEMENTATION_ROOT +#define KINC_IMPLEMENTATION +#endif + +#include + +#ifdef KINC_WINDOWS + +#include +#include +#endif + +void kinc_affirm(bool condition) { + if (!condition) { + kinc_error(); + } +} + +void kinc_affirm_message(bool condition, const char *format, ...) { + if (!condition) { + va_list args; + va_start(args, format); + kinc_error_args(format, args); + va_end(args); + } +} + +void kinc_affirm_args(bool condition, const char *format, va_list args) { + if (!condition) { + kinc_error_args(format, args); + } +} + +void kinc_error(void) { + kinc_error_message("Unknown error"); +} + +void kinc_error_message(const char *format, ...) { + { + va_list args; + va_start(args, format); + kinc_log_args(KINC_LOG_LEVEL_ERROR, format, args); + va_end(args); + } + +#ifdef KINC_WINDOWS + { + va_list args; + va_start(args, format); + wchar_t buffer[4096]; + kinc_microsoft_format(format, args, buffer); + MessageBoxW(NULL, buffer, L"Error", 0); + va_end(args); + } +#endif + +#ifndef KINC_NO_CLIB + exit(EXIT_FAILURE); +#endif +} + +void kinc_error_args(const char *format, va_list args) { + kinc_log_args(KINC_LOG_LEVEL_ERROR, format, args); + +#ifdef KINC_WINDOWS + wchar_t buffer[4096]; + kinc_microsoft_format(format, args, buffer); + MessageBoxW(NULL, buffer, L"Error", 0); +#endif + +#ifndef KINC_NO_CLIB + exit(EXIT_FAILURE); +#endif +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/global.h b/armorcore/sources/kinc/global.h new file mode 100644 index 000000000..d1a0f9fc5 --- /dev/null +++ b/armorcore/sources/kinc/global.h @@ -0,0 +1,62 @@ +#pragma once + +/*! \file global.h + \brief Provides basic functionality that's used all over the place. There's usually no need to manually include this. +*/ + +#include +#include + +#define KINC_LITTLE_ENDIAN + +#ifdef _MSC_VER +#define KINC_INLINE static __forceinline +#else +#define KINC_INLINE static __attribute__((always_inline)) inline +#endif + +#ifdef _MSC_VER +#define KINC_MICROSOFT +#endif + +#if defined(_WIN32) + +#ifndef KINC_CONSOLE +#define KINC_WINDOWS +#endif + +#elif defined(__APPLE__) + +#include + +#if TARGET_OS_IPHONE + +#if !defined(KINC_TVOS) +#define KINC_IOS +#endif + +#define KINC_APPLE_SOC + +#else + +#define KINC_MACOS + +#if defined(__arm64__) +#define KINC_APPLE_SOC +#endif + +#endif + +#define KINC_POSIX + +#elif defined(__linux__) + +#if !defined(KINC_ANDROID) +#define KINC_LINUX +#endif + +#define KINC_POSIX + +#endif + +int kickstart(int argc, char **argv); diff --git a/armorcore/sources/kinc/graphics2/g2.c b/armorcore/sources/kinc/graphics2/g2.c new file mode 100644 index 000000000..30bc4fafa --- /dev/null +++ b/armorcore/sources/kinc/graphics2/g2.c @@ -0,0 +1,1114 @@ +#include "g2.h" +#include "g2_font.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define G2_BUFFER_SIZE 1000 + +static kinc_matrix4x4_t g2_projection_matrix; +static bool g2_bilinear_filter = true; +static uint32_t g2_color = 0; +static arm_g2_font_t *g2_font = NULL; +static int g2_font_size; +static bool g2_is_render_target = false; +static kinc_g4_pipeline_t *g2_last_pipeline = NULL; +static kinc_g4_pipeline_t *g2_custom_pipeline = NULL; +static kinc_matrix3x3_t g2_transform; + +static kinc_g4_vertex_buffer_t image_vertex_buffer; +static kinc_g4_index_buffer_t image_index_buffer; +static kinc_g4_shader_t image_vert_shader; +static kinc_g4_shader_t image_frag_shader; +static kinc_g4_pipeline_t image_pipeline; +static kinc_g4_texture_unit_t image_tex_unit; +static kinc_g4_constant_location_t image_proj_loc; +static float *image_rect_verts = NULL; +static int image_buffer_index = 0; +static int image_buffer_start = 0; +static kinc_g4_texture_t *image_last_texture = NULL; +static kinc_g4_render_target_t *image_last_render_target = NULL; + +static kinc_g4_vertex_buffer_t colored_rect_vertex_buffer; +static kinc_g4_index_buffer_t colored_rect_index_buffer; +static kinc_g4_vertex_buffer_t colored_tris_vertex_buffer; +static kinc_g4_index_buffer_t colored_tris_index_buffer; +static kinc_g4_shader_t colored_vert_shader; +static kinc_g4_shader_t colored_frag_shader; +static kinc_g4_pipeline_t colored_pipeline; +static kinc_g4_constant_location_t colored_proj_loc; +static float *colored_rect_verts = NULL; +static float *colored_tris_verts = NULL; +static int colored_rect_buffer_index = 0; +static int colored_rect_buffer_start = 0; +static int colored_tris_buffer_index = 0; +static int colored_tris_buffer_start = 0; + +static kinc_g4_vertex_buffer_t text_vertex_buffer; +static kinc_g4_index_buffer_t text_index_buffer; +static kinc_g4_shader_t text_vert_shader; +static kinc_g4_shader_t text_frag_shader; +static kinc_g4_pipeline_t text_pipeline; +static kinc_g4_pipeline_t text_pipeline_rt; +static kinc_g4_texture_unit_t text_tex_unit; +static kinc_g4_constant_location_t text_proj_loc; +static float *text_rect_verts = NULL; +static int text_buffer_index = 0; +static int text_buffer_start = 0; +static kinc_g4_texture_t *text_last_texture = NULL; + +static int *g2_font_glyph_blocks = NULL; +static int *g2_font_glyphs = NULL; +static int g2_font_num_glyph_blocks = -1; +static int g2_font_num_glyphs = -1; + +void arm_g2_image_end(void); +void arm_g2_colored_end(void); +void arm_g2_colored_rect_end(bool tris_done); +void arm_g2_colored_tris_end(bool rects_done); +void arm_g2_text_end(void); + +kinc_matrix4x4_t arm_g2_matrix4x4_orthogonal_projection(float left, float right, float bottom, float top, float zn, float zf) { + float tx = -(right + left) / (right - left); + float ty = -(top + bottom) / (top - bottom); + float tz = -(zf + zn) / (zf - zn); + return (kinc_matrix4x4_t) { + 2.0f / (right - left), 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f / (top - bottom), 0.0f, 0.0f, + 0.0f, 0.0f, -2.0f / (zf - zn), 0.0f, + tx, ty, tz, 1.0f + }; +} + +void arm_g2_matrix3x3_multquad(kinc_matrix3x3_t *m, float qx, float qy, float qw, float qh, kinc_vector2_t *out) { + kinc_float32x4_t xx = kinc_float32x4_load(qx, qx, qx + qw, qx + qw); + kinc_float32x4_t yy = kinc_float32x4_load(qy + qh, qy, qy, qy + qh); + + kinc_float32x4_t m00 = kinc_float32x4_load_all(m->m[0]); + kinc_float32x4_t m01 = kinc_float32x4_load_all(m->m[1]); + kinc_float32x4_t m02 = kinc_float32x4_load_all(m->m[2]); + kinc_float32x4_t m10 = kinc_float32x4_load_all(m->m[3]); + kinc_float32x4_t m11 = kinc_float32x4_load_all(m->m[4]); + kinc_float32x4_t m12 = kinc_float32x4_load_all(m->m[5]); + kinc_float32x4_t m20 = kinc_float32x4_load_all(m->m[6]); + kinc_float32x4_t m21 = kinc_float32x4_load_all(m->m[7]); + kinc_float32x4_t m22 = kinc_float32x4_load_all(m->m[8]); + + kinc_float32x4_t w = kinc_float32x4_add(kinc_float32x4_add(kinc_float32x4_mul(m02, xx), kinc_float32x4_mul(m12, yy)), m22); + kinc_float32x4_t px = kinc_float32x4_div(kinc_float32x4_add(kinc_float32x4_add(kinc_float32x4_mul(m00, xx), kinc_float32x4_mul(m10, yy)), m20), w); + kinc_float32x4_t py = kinc_float32x4_div(kinc_float32x4_add(kinc_float32x4_add(kinc_float32x4_mul(m01, xx), kinc_float32x4_mul(m11, yy)), m21), w); + + out[0] = (kinc_vector2_t){kinc_float32x4_get(px, 0), kinc_float32x4_get(py, 0)}; + out[1] = (kinc_vector2_t){kinc_float32x4_get(px, 1), kinc_float32x4_get(py, 1)}; + out[2] = (kinc_vector2_t){kinc_float32x4_get(px, 2), kinc_float32x4_get(py, 2)}; + out[3] = (kinc_vector2_t){kinc_float32x4_get(px, 3), kinc_float32x4_get(py, 3)}; +} + +kinc_vector2_t arm_g2_matrix3x3_multvec(kinc_matrix3x3_t *m, kinc_vector2_t v) { + float w = m->m[2] * v.x + m->m[5] * v.y + m->m[8] * 1.0f; + float x = (m->m[0] * v.x + m->m[3] * v.y + m->m[6] * 1.0f) / w; + float y = (m->m[1] * v.x + m->m[4] * v.y + m->m[7] * 1.0f) / w; + return (kinc_vector2_t){x, y}; +} + +void arm_g2_internal_set_projection_matrix(kinc_g4_render_target_t *target) { + if (target != NULL) { + if (kinc_g4_render_targets_inverted_y()) { + g2_projection_matrix = arm_g2_matrix4x4_orthogonal_projection(0.0f, (float)target->width, 0.0f, (float)target->height, 0.1f, 1000.0f); + } + else { + g2_projection_matrix = arm_g2_matrix4x4_orthogonal_projection(0.0f, (float)target->width, (float)target->height, 0.0f, 0.1f, 1000.0f); + } + } + else { + g2_projection_matrix = arm_g2_matrix4x4_orthogonal_projection(0.0f, (float)kinc_window_width(0), (float)kinc_window_height(0), 0.0f, 0.1f, 1000.0f); + } +} + +void arm_g2_init(void *image_vert, int image_vert_size, void *image_frag, int image_frag_size, void *colored_vert, int colored_vert_size, void *colored_frag, int colored_frag_size, void *text_vert, int text_vert_size, void *text_frag, int text_frag_size) { + arm_g2_internal_set_projection_matrix(NULL); + g2_transform = kinc_matrix3x3_identity(); + + // Image painter + kinc_g4_shader_init(&image_vert_shader, image_vert, image_vert_size, KINC_G4_SHADER_TYPE_VERTEX); + kinc_g4_shader_init(&image_frag_shader, image_frag, image_frag_size, KINC_G4_SHADER_TYPE_FRAGMENT); + + { + kinc_g4_vertex_structure_t structure; + kinc_g4_vertex_structure_init(&structure); + kinc_g4_vertex_structure_add(&structure, "pos", KINC_G4_VERTEX_DATA_FLOAT3); + kinc_g4_vertex_structure_add(&structure, "tex", KINC_G4_VERTEX_DATA_FLOAT2); + kinc_g4_vertex_structure_add(&structure, "col", KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED); + kinc_g4_pipeline_init(&image_pipeline); + image_pipeline.input_layout[0] = &structure; + image_pipeline.input_layout[1] = NULL; + image_pipeline.vertex_shader = &image_vert_shader; + image_pipeline.fragment_shader = &image_frag_shader; + image_pipeline.blend_source = KINC_G4_BLEND_ONE; + image_pipeline.blend_destination = KINC_G4_BLEND_INV_SOURCE_ALPHA; + image_pipeline.alpha_blend_source = KINC_G4_BLEND_ONE; + image_pipeline.alpha_blend_destination = KINC_G4_BLEND_INV_SOURCE_ALPHA; + kinc_g4_pipeline_compile(&image_pipeline); + + image_tex_unit = kinc_g4_pipeline_get_texture_unit(&image_pipeline, "tex"); + image_proj_loc = kinc_g4_pipeline_get_constant_location(&image_pipeline, "P"); + + kinc_g4_vertex_buffer_init(&image_vertex_buffer, G2_BUFFER_SIZE * 4, &structure, KINC_G4_USAGE_DYNAMIC, 0); + image_rect_verts = kinc_g4_vertex_buffer_lock_all(&image_vertex_buffer); + + kinc_g4_index_buffer_init(&image_index_buffer, G2_BUFFER_SIZE * 3 * 2, KINC_G4_INDEX_BUFFER_FORMAT_32BIT, KINC_G4_USAGE_STATIC); + int *indices = kinc_g4_index_buffer_lock_all(&image_index_buffer); + for (int i = 0; i < G2_BUFFER_SIZE; ++i) { + indices[i * 3 * 2 + 0] = i * 4 + 0; + indices[i * 3 * 2 + 1] = i * 4 + 1; + indices[i * 3 * 2 + 2] = i * 4 + 2; + indices[i * 3 * 2 + 3] = i * 4 + 0; + indices[i * 3 * 2 + 4] = i * 4 + 2; + indices[i * 3 * 2 + 5] = i * 4 + 3; + } + kinc_g4_index_buffer_unlock_all(&image_index_buffer); + } + + // Colored painter + kinc_g4_shader_init(&colored_vert_shader, colored_vert, colored_vert_size, KINC_G4_SHADER_TYPE_VERTEX); + kinc_g4_shader_init(&colored_frag_shader, colored_frag, colored_frag_size, KINC_G4_SHADER_TYPE_FRAGMENT); + + { + kinc_g4_vertex_structure_t structure; + kinc_g4_vertex_structure_init(&structure); + kinc_g4_vertex_structure_add(&structure, "pos", KINC_G4_VERTEX_DATA_FLOAT3); + kinc_g4_vertex_structure_add(&structure, "col", KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED); + kinc_g4_pipeline_init(&colored_pipeline); + colored_pipeline.input_layout[0] = &structure; + colored_pipeline.input_layout[1] = NULL; + colored_pipeline.vertex_shader = &colored_vert_shader; + colored_pipeline.fragment_shader = &colored_frag_shader; + // colored_pipeline.blend_source = KINC_G4_BLEND_ONE; + colored_pipeline.blend_source = KINC_G4_BLEND_SOURCE_ALPHA; + colored_pipeline.blend_destination = KINC_G4_BLEND_INV_SOURCE_ALPHA; + colored_pipeline.alpha_blend_source = KINC_G4_BLEND_ONE; + colored_pipeline.alpha_blend_destination = KINC_G4_BLEND_INV_SOURCE_ALPHA; + kinc_g4_pipeline_compile(&colored_pipeline); + + colored_proj_loc = kinc_g4_pipeline_get_constant_location(&colored_pipeline, "P"); + + kinc_g4_vertex_buffer_init(&colored_rect_vertex_buffer, G2_BUFFER_SIZE * 4, &structure, KINC_G4_USAGE_DYNAMIC, 0); + colored_rect_verts = kinc_g4_vertex_buffer_lock_all(&colored_rect_vertex_buffer); + + kinc_g4_index_buffer_init(&colored_rect_index_buffer, G2_BUFFER_SIZE * 3 * 2, KINC_G4_INDEX_BUFFER_FORMAT_32BIT, KINC_G4_USAGE_STATIC); + int *indices = kinc_g4_index_buffer_lock_all(&colored_rect_index_buffer); + for (int i = 0; i < G2_BUFFER_SIZE; ++i) { + indices[i * 3 * 2 + 0] = i * 4 + 0; + indices[i * 3 * 2 + 1] = i * 4 + 1; + indices[i * 3 * 2 + 2] = i * 4 + 2; + indices[i * 3 * 2 + 3] = i * 4 + 0; + indices[i * 3 * 2 + 4] = i * 4 + 2; + indices[i * 3 * 2 + 5] = i * 4 + 3; + } + kinc_g4_index_buffer_unlock_all(&colored_rect_index_buffer); + + kinc_g4_vertex_buffer_init(&colored_tris_vertex_buffer, G2_BUFFER_SIZE * 3, &structure, KINC_G4_USAGE_DYNAMIC, 0); + colored_tris_verts = kinc_g4_vertex_buffer_lock_all(&colored_tris_vertex_buffer); + + kinc_g4_index_buffer_init(&colored_tris_index_buffer, G2_BUFFER_SIZE * 3, KINC_G4_INDEX_BUFFER_FORMAT_32BIT, KINC_G4_USAGE_STATIC); + indices = kinc_g4_index_buffer_lock_all(&colored_tris_index_buffer); + for (int i = 0; i < G2_BUFFER_SIZE; ++i) { + indices[i * 3 + 0] = i * 3 + 0; + indices[i * 3 + 1] = i * 3 + 1; + indices[i * 3 + 2] = i * 3 + 2; + } + kinc_g4_index_buffer_unlock_all(&colored_tris_index_buffer); + } + + // Text painter + kinc_g4_shader_init(&text_vert_shader, text_vert, text_vert_size, KINC_G4_SHADER_TYPE_VERTEX); + kinc_g4_shader_init(&text_frag_shader, text_frag, text_frag_size, KINC_G4_SHADER_TYPE_FRAGMENT); + + { + kinc_g4_vertex_structure_t structure; + kinc_g4_vertex_structure_init(&structure); + kinc_g4_vertex_structure_add(&structure, "pos", KINC_G4_VERTEX_DATA_FLOAT3); + kinc_g4_vertex_structure_add(&structure, "tex", KINC_G4_VERTEX_DATA_FLOAT2); + kinc_g4_vertex_structure_add(&structure, "col", KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED); + + kinc_g4_pipeline_init(&text_pipeline); + text_pipeline.input_layout[0] = &structure; + text_pipeline.input_layout[1] = NULL; + text_pipeline.vertex_shader = &text_vert_shader; + text_pipeline.fragment_shader = &text_frag_shader; + text_pipeline.blend_source = KINC_G4_BLEND_SOURCE_ALPHA; + text_pipeline.blend_destination = KINC_G4_BLEND_INV_SOURCE_ALPHA; + text_pipeline.alpha_blend_source = KINC_G4_BLEND_SOURCE_ALPHA; + text_pipeline.alpha_blend_destination = KINC_G4_BLEND_INV_SOURCE_ALPHA; + kinc_g4_pipeline_compile(&text_pipeline); + text_tex_unit = kinc_g4_pipeline_get_texture_unit(&text_pipeline, "tex"); + text_proj_loc = kinc_g4_pipeline_get_constant_location(&text_pipeline, "P"); + + kinc_g4_pipeline_init(&text_pipeline_rt); + text_pipeline_rt.input_layout[0] = &structure; + text_pipeline_rt.input_layout[1] = NULL; + text_pipeline_rt.vertex_shader = &text_vert_shader; + text_pipeline_rt.fragment_shader = &text_frag_shader; + text_pipeline_rt.blend_source = KINC_G4_BLEND_SOURCE_ALPHA; + text_pipeline_rt.blend_destination = KINC_G4_BLEND_INV_SOURCE_ALPHA; + text_pipeline_rt.alpha_blend_source = KINC_G4_BLEND_ONE; + text_pipeline_rt.alpha_blend_destination = KINC_G4_BLEND_INV_SOURCE_ALPHA; + kinc_g4_pipeline_compile(&text_pipeline_rt); + // text_tex_unit_rt = kinc_g4_pipeline_get_texture_unit(&text_pipeline_rt, "tex"); + // text_proj_loc_rt = kinc_g4_pipeline_get_constant_location(&text_pipeline_rt, "P"); + + kinc_g4_vertex_buffer_init(&text_vertex_buffer, G2_BUFFER_SIZE * 4, &structure, KINC_G4_USAGE_DYNAMIC, 0); + text_rect_verts = kinc_g4_vertex_buffer_lock_all(&text_vertex_buffer); + + kinc_g4_index_buffer_init(&text_index_buffer, G2_BUFFER_SIZE * 3 * 2, KINC_G4_INDEX_BUFFER_FORMAT_32BIT, KINC_G4_USAGE_STATIC); + int *indices = kinc_g4_index_buffer_lock_all(&text_index_buffer); + for (int i = 0; i < G2_BUFFER_SIZE; ++i) { + indices[i * 3 * 2 + 0] = i * 4 + 0; + indices[i * 3 * 2 + 1] = i * 4 + 1; + indices[i * 3 * 2 + 2] = i * 4 + 2; + indices[i * 3 * 2 + 3] = i * 4 + 0; + indices[i * 3 * 2 + 4] = i * 4 + 2; + indices[i * 3 * 2 + 5] = i * 4 + 3; + } + kinc_g4_index_buffer_unlock_all(&text_index_buffer); + } +} + +void arm_g2_begin(void) { + arm_g2_set_color(0xffffffff); +} + +void arm_g2_set_image_rect_verts(float btlx, float btly, float tplx, float tply, float tprx, float tpry, float btrx, float btry) { + int base_idx = (image_buffer_index - image_buffer_start) * 6 * 4; + image_rect_verts[base_idx + 0] = btlx; + image_rect_verts[base_idx + 1] = btly; + image_rect_verts[base_idx + 2] = -5.0f; + + image_rect_verts[base_idx + 6] = tplx; + image_rect_verts[base_idx + 7] = tply; + image_rect_verts[base_idx + 8] = -5.0f; + + image_rect_verts[base_idx + 12] = tprx; + image_rect_verts[base_idx + 13] = tpry; + image_rect_verts[base_idx + 14] = -5.0f; + + image_rect_verts[base_idx + 18] = btrx; + image_rect_verts[base_idx + 19] = btry; + image_rect_verts[base_idx + 20] = -5.0f; +} + +void arm_g2_set_image_rect_tex_coords(float left, float top, float right, float bottom) { + int base_idx = (image_buffer_index - image_buffer_start) * 6 * 4; + image_rect_verts[base_idx + 3] = left; + image_rect_verts[base_idx + 4] = bottom; + + image_rect_verts[base_idx + 9] = left; + image_rect_verts[base_idx + 10] = top; + + image_rect_verts[base_idx + 15] = right; + image_rect_verts[base_idx + 16] = top; + + image_rect_verts[base_idx + 21] = right; + image_rect_verts[base_idx + 22] = bottom; +} + +void arm_g2_set_image_rect_colors(uint32_t color) { + color = (color & 0xff000000) | ((color & 0x00ff0000) >> 16) | (color & 0x0000ff00) | ((color & 0x000000ff) << 16); + int base_idx = (image_buffer_index - image_buffer_start) * 6 * 4; + image_rect_verts[base_idx + 5] = *(float *)&color; + image_rect_verts[base_idx + 11] = *(float *)&color; + image_rect_verts[base_idx + 17] = *(float *)&color; + image_rect_verts[base_idx + 23] = *(float *)&color; +} + +void arm_g2_draw_image_buffer(bool end) { + if (image_buffer_index - image_buffer_start == 0) return; + kinc_g4_vertex_buffer_unlock(&image_vertex_buffer, (image_buffer_index - image_buffer_start) * 4); + kinc_g4_set_pipeline(g2_custom_pipeline != NULL ? g2_custom_pipeline : &image_pipeline); + kinc_g4_set_matrix4(image_proj_loc, &g2_projection_matrix); + kinc_g4_set_vertex_buffer(&image_vertex_buffer); + kinc_g4_set_index_buffer(&image_index_buffer); + if (image_last_texture != NULL) { + kinc_g4_set_texture(image_tex_unit, image_last_texture); + } + else { + kinc_g4_render_target_use_color_as_texture(image_last_render_target, image_tex_unit); + } + kinc_g4_set_texture_addressing(image_tex_unit, KINC_G4_TEXTURE_DIRECTION_U, KINC_G4_TEXTURE_ADDRESSING_CLAMP); + kinc_g4_set_texture_addressing(image_tex_unit, KINC_G4_TEXTURE_DIRECTION_V, KINC_G4_TEXTURE_ADDRESSING_CLAMP); + // kinc_g4_set_texture_mipmap_filter(image_tex_unit, g2_bilinear_filter ? KINC_G4_MIPMAP_FILTER_LINEAR : KINC_G4_MIPMAP_FILTER_NONE); + kinc_g4_set_texture_mipmap_filter(image_tex_unit, KINC_G4_MIPMAP_FILTER_NONE); + kinc_g4_set_texture_minification_filter(image_tex_unit, g2_bilinear_filter ? KINC_G4_TEXTURE_FILTER_LINEAR : KINC_G4_TEXTURE_FILTER_POINT); + kinc_g4_set_texture_magnification_filter(image_tex_unit, g2_bilinear_filter ? KINC_G4_TEXTURE_FILTER_LINEAR : KINC_G4_TEXTURE_FILTER_POINT); + kinc_g4_draw_indexed_vertices_from_to(image_buffer_start * 2 * 3, (image_buffer_index - image_buffer_start) * 2 * 3); + + if (end || image_buffer_index + 1 >= G2_BUFFER_SIZE) { + image_buffer_start = 0; + image_buffer_index = 0; + image_rect_verts = kinc_g4_vertex_buffer_lock(&image_vertex_buffer, 0, G2_BUFFER_SIZE * 4); + } + else { + image_buffer_start = image_buffer_index; + image_rect_verts = kinc_g4_vertex_buffer_lock(&image_vertex_buffer, image_buffer_start * 4, (G2_BUFFER_SIZE - image_buffer_start) * 4); + } +} + +void arm_g2_draw_scaled_sub_image(kinc_g4_texture_t *tex, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh) { + arm_g2_colored_end(); + arm_g2_text_end(); + if (image_buffer_start + image_buffer_index + 1 >= G2_BUFFER_SIZE || (image_last_texture != NULL && tex != image_last_texture) || image_last_render_target != NULL) arm_g2_draw_image_buffer(false); + + arm_g2_set_image_rect_tex_coords(sx / tex->tex_width, sy / tex->tex_height, (sx + sw) / tex->tex_width, (sy + sh) / tex->tex_height); + arm_g2_set_image_rect_colors(g2_color); + kinc_vector2_t p[4]; + arm_g2_matrix3x3_multquad(&g2_transform, dx, dy, dw, dh, p); + arm_g2_set_image_rect_verts(p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y); + + ++image_buffer_index; + image_last_texture = tex; + image_last_render_target = NULL; +} + +void arm_g2_draw_scaled_image(kinc_g4_texture_t *tex, float dx, float dy, float dw, float dh) { + arm_g2_draw_scaled_sub_image(tex, 0, 0, (float)tex->tex_width, (float)tex->tex_height, dx, dy, dw, dh); +} + +void arm_g2_draw_sub_image(kinc_g4_texture_t *tex, float sx, float sy, float sw, float sh, float x, float y) { + arm_g2_draw_scaled_sub_image(tex, sx, sy, sw, sh, x, y, sw, sh); +} + +void arm_g2_draw_image(kinc_g4_texture_t *tex, float x, float y) { + arm_g2_draw_scaled_sub_image(tex, 0, 0, (float)tex->tex_width, (float)tex->tex_height, x, y, (float)tex->tex_width, (float)tex->tex_height); +} + +void arm_g2_draw_scaled_sub_render_target(kinc_g4_render_target_t *rt, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh) { + arm_g2_colored_end(); + arm_g2_text_end(); + if (image_buffer_start + image_buffer_index + 1 >= G2_BUFFER_SIZE || (image_last_render_target != NULL && rt != image_last_render_target) || image_last_texture != NULL) arm_g2_draw_image_buffer(false); + + arm_g2_set_image_rect_tex_coords(sx / rt->width, sy / rt->height, (sx + sw) / rt->width, (sy + sh) / rt->height); + arm_g2_set_image_rect_colors(g2_color); + kinc_vector2_t p[4]; + arm_g2_matrix3x3_multquad(&g2_transform, dx, dy, dw, dh, p); + arm_g2_set_image_rect_verts(p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y); + + ++image_buffer_index; + image_last_render_target = rt; + image_last_texture = NULL; +} + +void arm_g2_draw_scaled_render_target(kinc_g4_render_target_t *rt, float dx, float dy, float dw, float dh) { + arm_g2_draw_scaled_sub_render_target(rt, 0, 0, (float)rt->width, (float)rt->height, dx, dy, dw, dh); +} + +void arm_g2_draw_sub_render_target(kinc_g4_render_target_t *rt, float sx, float sy, float sw, float sh, float x, float y) { + arm_g2_draw_scaled_sub_render_target(rt, sx, sy, sw, sh, x, y, sw, sh); +} + +void arm_g2_draw_render_target(kinc_g4_render_target_t *rt, float x, float y) { + arm_g2_draw_scaled_sub_render_target(rt, 0, 0, (float)rt->width, (float)rt->height, x, y, (float)rt->width, (float)rt->height); +} + +void arm_g2_colored_rect_set_verts(float btlx, float btly, float tplx, float tply, float tprx, float tpry, float btrx, float btry) { + int base_idx = (colored_rect_buffer_index - colored_rect_buffer_start) * 4 * 4; + colored_rect_verts[base_idx + 0] = btlx; + colored_rect_verts[base_idx + 1] = btly; + colored_rect_verts[base_idx + 2] = -5.0f; + + colored_rect_verts[base_idx + 4] = tplx; + colored_rect_verts[base_idx + 5] = tply; + colored_rect_verts[base_idx + 6] = -5.0f; + + colored_rect_verts[base_idx + 8] = tprx; + colored_rect_verts[base_idx + 9] = tpry; + colored_rect_verts[base_idx + 10] = -5.0f; + + colored_rect_verts[base_idx + 12] = btrx; + colored_rect_verts[base_idx + 13] = btry; + colored_rect_verts[base_idx + 14] = -5.0f; +} + +void arm_g2_colored_rect_set_colors(uint32_t color) { + color = (color & 0xff000000) | ((color & 0x00ff0000) >> 16) | (color & 0x0000ff00) | ((color & 0x000000ff) << 16); + int base_idx = (colored_rect_buffer_index - colored_rect_buffer_start) * 4 * 4; + colored_rect_verts[base_idx + 3] = *(float *)&color; + colored_rect_verts[base_idx + 7] = *(float *)&color; + colored_rect_verts[base_idx + 11] = *(float *)&color; + colored_rect_verts[base_idx + 15] = *(float *)&color; +} + +void arm_g2_colored_rect_draw_buffer(bool tris_done) { + if (colored_rect_buffer_index - colored_rect_buffer_start == 0) { + return; + } + + if (!tris_done) arm_g2_colored_tris_end(true); + + kinc_g4_vertex_buffer_unlock(&colored_rect_vertex_buffer, (colored_rect_buffer_index - colored_rect_buffer_start) * 4); + kinc_g4_set_pipeline(g2_custom_pipeline != NULL ? g2_custom_pipeline : &colored_pipeline); + kinc_g4_set_matrix4(colored_proj_loc, &g2_projection_matrix); + kinc_g4_set_vertex_buffer(&colored_rect_vertex_buffer); + kinc_g4_set_index_buffer(&colored_rect_index_buffer); + kinc_g4_draw_indexed_vertices_from_to(colored_rect_buffer_start * 2 * 3, (colored_rect_buffer_index - colored_rect_buffer_start) * 2 * 3); + + if (colored_rect_buffer_index + 1 >= G2_BUFFER_SIZE) { + colored_rect_buffer_start = 0; + colored_rect_buffer_index = 0; + colored_rect_verts = kinc_g4_vertex_buffer_lock(&colored_rect_vertex_buffer, 0, G2_BUFFER_SIZE * 4); + } + else { + colored_rect_buffer_start = colored_rect_buffer_index; + colored_rect_verts = kinc_g4_vertex_buffer_lock(&colored_rect_vertex_buffer, colored_rect_buffer_start * 4, (G2_BUFFER_SIZE - colored_rect_buffer_start) * 4); + } +} + +void arm_g2_colored_tris_set_verts(float x0, float y0, float x1, float y1, float x2, float y2) { + int base_idx = (colored_tris_buffer_index - colored_tris_buffer_start) * 4 * 3; + colored_tris_verts[base_idx + 0] = x0; + colored_tris_verts[base_idx + 1] = y0; + colored_tris_verts[base_idx + 2] = -5.0f; + + colored_tris_verts[base_idx + 4] = x1; + colored_tris_verts[base_idx + 5] = y1; + colored_tris_verts[base_idx + 6] = -5.0f; + + colored_tris_verts[base_idx + 8] = x2; + colored_tris_verts[base_idx + 9] = y2; + colored_tris_verts[base_idx + 10] = -5.0f; +} + +void arm_g2_colored_tris_set_colors(uint32_t color) { + int base_idx = (colored_tris_buffer_index - colored_tris_buffer_start) * 4 * 3; + color = (color & 0xff000000) | ((color & 0x00ff0000) >> 16) | (color & 0x0000ff00) | ((color & 0x000000ff) << 16); + colored_tris_verts[base_idx + 3] = *(float *)&color; + colored_tris_verts[base_idx + 7] = *(float *)&color; + colored_tris_verts[base_idx + 11] = *(float *)&color; +} + +void arm_g2_colored_tris_draw_buffer(bool rect_done) { + if (colored_tris_buffer_index - colored_tris_buffer_start == 0) { + return; + } + + if (!rect_done) arm_g2_colored_rect_end(true); + + kinc_g4_vertex_buffer_unlock(&colored_tris_vertex_buffer, (colored_tris_buffer_index - colored_tris_buffer_start) * 3); + kinc_g4_set_pipeline(g2_custom_pipeline != NULL ? g2_custom_pipeline : &colored_pipeline); + kinc_g4_set_matrix4(colored_proj_loc, &g2_projection_matrix); + kinc_g4_set_vertex_buffer(&colored_tris_vertex_buffer); + kinc_g4_set_index_buffer(&colored_tris_index_buffer); + kinc_g4_draw_indexed_vertices_from_to(colored_tris_buffer_start * 3, (colored_tris_buffer_index - colored_tris_buffer_start) * 3); + + if (colored_tris_buffer_index + 1 >= G2_BUFFER_SIZE) { + colored_tris_buffer_start = 0; + colored_tris_buffer_index = 0; + colored_tris_verts = kinc_g4_vertex_buffer_lock(&colored_tris_vertex_buffer, 0, G2_BUFFER_SIZE * 3); + } + else { + colored_tris_buffer_start = colored_tris_buffer_index; + colored_tris_verts = kinc_g4_vertex_buffer_lock(&colored_tris_vertex_buffer, colored_tris_buffer_start * 3, (G2_BUFFER_SIZE - colored_tris_buffer_start) * 3); + } +} + +void arm_g2_colored_rect_end(bool tris_done) { + if (colored_rect_buffer_index - colored_rect_buffer_start > 0) arm_g2_colored_rect_draw_buffer(tris_done); +} + +void arm_g2_colored_tris_end(bool rects_done) { + if (colored_tris_buffer_index - colored_tris_buffer_start > 0) arm_g2_colored_tris_draw_buffer(rects_done); +} + +void arm_g2_fill_triangle(float x0, float y0, float x1, float y1, float x2, float y2) { + arm_g2_image_end(); + arm_g2_text_end(); + if (colored_rect_buffer_index - colored_rect_buffer_start > 0) arm_g2_colored_rect_draw_buffer(true); + if (colored_tris_buffer_index + 1 >= G2_BUFFER_SIZE) arm_g2_colored_tris_draw_buffer(false); + + arm_g2_colored_tris_set_colors(g2_color); + + kinc_vector2_t p0 = arm_g2_matrix3x3_multvec(&g2_transform, (kinc_vector2_t){x0, y0}); // Bottom-left + kinc_vector2_t p1 = arm_g2_matrix3x3_multvec(&g2_transform, (kinc_vector2_t){x1, y1}); // Top-left + kinc_vector2_t p2 = arm_g2_matrix3x3_multvec(&g2_transform, (kinc_vector2_t){x2, y2}); // Top-right + arm_g2_colored_tris_set_verts(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y); + + ++colored_tris_buffer_index; +} + +void arm_g2_fill_rect(float x, float y, float width, float height) { + arm_g2_image_end(); + arm_g2_text_end(); + if (colored_tris_buffer_index - colored_tris_buffer_start > 0) arm_g2_colored_tris_draw_buffer(true); + if (colored_rect_buffer_index + 1 >= G2_BUFFER_SIZE) arm_g2_colored_rect_draw_buffer(false); + + arm_g2_colored_rect_set_colors(g2_color); + + kinc_vector2_t p[4]; + arm_g2_matrix3x3_multquad(&g2_transform, x, y, width, height, p); + arm_g2_colored_rect_set_verts(p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y); + + ++colored_rect_buffer_index; +} + +void arm_g2_draw_rect(float x, float y, float width, float height, float strength) { + float hs = strength / 2.0f; + arm_g2_fill_rect(x - hs, y - hs, width + strength, strength); // Top + arm_g2_fill_rect(x - hs, y + hs, strength, height - strength); // Left + arm_g2_fill_rect(x - hs, y + height - hs, width + strength, strength); // Bottom + arm_g2_fill_rect(x + width - hs, y + hs, strength, height - strength); // Right +} + +void arm_g2_draw_line(float x0, float y0, float x1, float y1, float strength) { + kinc_vector2_t vec; + if (y1 == y0) { + vec = (kinc_vector2_t){0.0f, -1.0f}; + } + else { + vec = (kinc_vector2_t){1.0f, -(x1 - x0) / (y1 - y0)}; + } + + float current_length = sqrtf(vec.x * vec.x + vec.y * vec.y); + if (current_length != 0) { + float mul = strength / current_length; + vec.x *= mul; + vec.y *= mul; + } + + kinc_vector2_t p0 = (kinc_vector2_t){x0 + 0.5f * vec.x, y0 + 0.5f * vec.y}; + kinc_vector2_t p1 = (kinc_vector2_t){x1 + 0.5f * vec.x, y1 + 0.5f * vec.y}; + kinc_vector2_t p2 = (kinc_vector2_t){p0.x - vec.x, p0.y - vec.y}; + kinc_vector2_t p3 = (kinc_vector2_t){p1.x - vec.x, p1.y - vec.y}; + arm_g2_fill_triangle(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y); + arm_g2_fill_triangle(p2.x, p2.y, p1.x, p1.y, p3.x, p3.y); +} + +void arm_g2_text_set_rect_verts(float btlx, float btly, float tplx, float tply, float tprx, float tpry, float btrx, float btry) { + int base_idx = (text_buffer_index - text_buffer_start) * 6 * 4; + text_rect_verts[base_idx + 0] = btlx; + text_rect_verts[base_idx + 1] = btly; + text_rect_verts[base_idx + 2] = -5.0f; + + text_rect_verts[base_idx + 6] = tplx; + text_rect_verts[base_idx + 7] = tply; + text_rect_verts[base_idx + 8] = -5.0f; + + text_rect_verts[base_idx + 12] = tprx; + text_rect_verts[base_idx + 13] = tpry; + text_rect_verts[base_idx + 14] = -5.0f; + + text_rect_verts[base_idx + 18] = btrx; + text_rect_verts[base_idx + 19] = btry; + text_rect_verts[base_idx + 20] = -5.0f; +} + +void arm_g2_text_set_rect_tex_coords(float left, float top, float right, float bottom) { + int base_idx = (text_buffer_index - text_buffer_start) * 6 * 4; + text_rect_verts[base_idx + 3] = left; + text_rect_verts[base_idx + 4] = bottom; + + text_rect_verts[base_idx + 9] = left; + text_rect_verts[base_idx + 10] = top; + + text_rect_verts[base_idx + 15] = right; + text_rect_verts[base_idx + 16] = top; + + text_rect_verts[base_idx + 21] = right; + text_rect_verts[base_idx + 22] = bottom; +} + +void arm_g2_text_set_rect_colors(uint32_t color) { + color = (color & 0xff000000) | ((color & 0x00ff0000) >> 16) | (color & 0x0000ff00) | ((color & 0x000000ff) << 16); + int base_idx = (text_buffer_index - text_buffer_start) * 6 * 4; + text_rect_verts[base_idx + 5] = *(float *)&color; + text_rect_verts[base_idx + 11] = *(float *)&color; + text_rect_verts[base_idx + 17] = *(float *)&color; + text_rect_verts[base_idx + 23] = *(float *)&color; +} + +void arm_g2_text_draw_buffer(bool end) { + if (text_buffer_index - text_buffer_start == 0) return; + kinc_g4_vertex_buffer_unlock(&text_vertex_buffer, text_buffer_index * 4); + kinc_g4_set_pipeline(g2_custom_pipeline != NULL ? g2_custom_pipeline : g2_is_render_target ? &text_pipeline_rt : &text_pipeline); + kinc_g4_set_matrix4(text_proj_loc, &g2_projection_matrix); + kinc_g4_set_vertex_buffer(&text_vertex_buffer); + kinc_g4_set_index_buffer(&text_index_buffer); + kinc_g4_set_texture(text_tex_unit, text_last_texture); + kinc_g4_set_texture_addressing(text_tex_unit, KINC_G4_TEXTURE_DIRECTION_U, KINC_G4_TEXTURE_ADDRESSING_CLAMP); + kinc_g4_set_texture_addressing(text_tex_unit, KINC_G4_TEXTURE_DIRECTION_V, KINC_G4_TEXTURE_ADDRESSING_CLAMP); + kinc_g4_set_texture_mipmap_filter(text_tex_unit, KINC_G4_MIPMAP_FILTER_NONE); + kinc_g4_set_texture_minification_filter(text_tex_unit, g2_bilinear_filter ? KINC_G4_TEXTURE_FILTER_LINEAR : KINC_G4_TEXTURE_FILTER_POINT); + kinc_g4_set_texture_magnification_filter(text_tex_unit, g2_bilinear_filter ? KINC_G4_TEXTURE_FILTER_LINEAR : KINC_G4_TEXTURE_FILTER_POINT); + kinc_g4_draw_indexed_vertices_from_to(text_buffer_start * 2 * 3, (text_buffer_index - text_buffer_start) * 2 * 3); + + if (end || text_buffer_index + 1 >= G2_BUFFER_SIZE) { + text_buffer_start = 0; + text_buffer_index = 0; + text_rect_verts = kinc_g4_vertex_buffer_lock(&text_vertex_buffer, 0, G2_BUFFER_SIZE * 4); + } + else { + text_buffer_start = text_buffer_index; + text_rect_verts = kinc_g4_vertex_buffer_lock(&text_vertex_buffer, text_buffer_start * 4, (G2_BUFFER_SIZE - text_buffer_start) * 4); + } +} + +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" + +typedef struct arm_g2_font_aligned_quad { + float x0, y0, s0, t0; // Top-left + float x1, y1, s1, t1; // Bottom-right + float xadvance; +} arm_g2_font_aligned_quad_t; + +typedef struct arm_g2_font_image { + float m_size; + stbtt_bakedchar *chars; + kinc_g4_texture_t *tex; + int width, height, first_unused_y; + float baseline, descent, line_gap; +} arm_g2_font_image_t; + +arm_g2_font_image_t *arm_g2_font_get_image_internal(arm_g2_font_t *font, int size) { + for (int i = 0; i < font->m_images_len; ++i) { + if ((int)font->images[i].m_size == size) { + return &(font->images[i]); + } + } + return NULL; +} + +static inline bool arm_g2_prepare_font_load_internal(arm_g2_font_t *font, int size) { + if (arm_g2_font_get_image_internal(font, size) != NULL) return false; // Nothing to do + + // Resize images array if necessary + if (font->m_capacity <= font->m_images_len) { + font->m_capacity = font->m_images_len + 1; + if (font->images == NULL) { + font->images = (arm_g2_font_image_t *)malloc(font->m_capacity * sizeof(arm_g2_font_image_t)); + } + else { + font->images = (arm_g2_font_image_t *)realloc(font->images, font->m_capacity * sizeof(arm_g2_font_image_t)); + } + } + + return true; +} + +static int stbtt_BakeFontBitmapArr(unsigned char *data, int offset, // Font location (use offset=0 for plain .ttf) + float pixel_height, // Height of font in pixels + unsigned char *pixels, int pw, int ph, // Bitmap to be filled in + int* chars, int num_chars, // Characters to bake + stbtt_bakedchar *chardata) { + float scale; + int x, y, bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw * ph); // Background of 0 around pixels + x = y = 1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i = 0; i < num_chars; ++i) { + int advance, lsb, x0, y0, x1, y1, gw, gh; + int g = stbtt_FindGlyphIndex(&f, chars[i]); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale, scale, &x0, &y0, &x1, &y1); + gw = x1 - x0; + gh = y1 - y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // Advance to next row + if (y + gh + 1 >= ph) // Check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x + gw < pw); + STBTT_assert(y + gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels + x + y * pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16)x; + chardata[i].y0 = (stbtt_int16)y; + chardata[i].x1 = (stbtt_int16)(x + gw); + chardata[i].y1 = (stbtt_int16)(y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float)x0; + chardata[i].yoff = (float)y0; + x = x + gw + 1; + if (y + gh + 1 > bottom_y) + bottom_y = y + gh + 1; + } + return bottom_y; +} + +void arm_g2_font_load(arm_g2_font_t *font, int size) { + if (!arm_g2_prepare_font_load_internal(font, size)) return; + + arm_g2_font_image_t *img = &(font->images[font->m_images_len]); + font->m_images_len += 1; + int width = 64; + int height = 32; + stbtt_bakedchar *baked = (stbtt_bakedchar *)malloc(g2_font_num_glyphs * sizeof(stbtt_bakedchar)); + unsigned char *pixels = NULL; + + int status = -1; + while (status <= 0) { + if (height < width) + height *= 2; + else + width *= 2; + if (pixels == NULL) + pixels = (unsigned char *)malloc(width * height); + else + pixels = (unsigned char *)realloc(pixels, width * height); + assert(pixels != NULL); + status = stbtt_BakeFontBitmapArr(font->blob, font->offset, (float)size, pixels, width, height, g2_font_glyphs, g2_font_num_glyphs, baked); + } + + stbtt_fontinfo info; + int ascent, descent, line_gap; + stbtt_InitFont(&info, font->blob, font->offset); + stbtt_GetFontVMetrics(&info, &ascent, &descent, &line_gap); + float scale = stbtt_ScaleForPixelHeight(&info, (float)size); + img->m_size = (float)size; + img->baseline = (float)((int)((float)ascent * scale + 0.5f)); + img->descent = (float)((int)((float)descent * scale + 0.5f)); + img->line_gap = (float)((int)((float)line_gap * scale + 0.5f)); + img->width = width; + img->height = height; + img->chars = baked; + img->first_unused_y = status; + kinc_image_t fontimg; + kinc_image_init_from_bytes(&fontimg, pixels, width, height, KINC_IMAGE_FORMAT_GREY8); + img->tex = (kinc_g4_texture_t *)malloc(sizeof(kinc_g4_texture_t)); + kinc_g4_texture_init_from_image(img->tex, &fontimg); + kinc_image_destroy(&fontimg); + free(pixels); +} + +kinc_g4_texture_t *arm_g2_font_get_texture(arm_g2_font_t *font, int size) { + arm_g2_font_image_t *img = arm_g2_font_get_image_internal(font, size); + return img->tex; +} + +int arm_g2_font_get_char_index_internal(arm_g2_font_image_t *img, int char_index) { + if (g2_font_num_glyphs <= 0) { + return 0; + } + int offset = g2_font_glyph_blocks[0]; + if (char_index < offset) { + return 0; + } + + for (int i = 1; i < g2_font_num_glyph_blocks / 2; ++i) { + int prev_end = g2_font_glyph_blocks[i * 2 - 1]; + int start = g2_font_glyph_blocks[i * 2]; + if (char_index > start - 1) { + offset += start - 1 - prev_end; + } + } + + if (char_index - offset >= g2_font_num_glyphs) { + return 0; + } + return char_index - offset; +} + +bool arm_g2_font_get_baked_quad(arm_g2_font_t *font, int size, arm_g2_font_aligned_quad_t *q, int char_code, float xpos, float ypos) { + arm_g2_font_image_t *img = arm_g2_font_get_image_internal(font, size); + int char_index = arm_g2_font_get_char_index_internal(img, char_code); + if (char_index >= g2_font_num_glyphs) return false; + float ipw = 1.0f / (float)img->width; + float iph = 1.0f / (float)img->height; + stbtt_bakedchar b = img->chars[char_index]; + int round_x = (int)(xpos + b.xoff + 0.5); + int round_y = (int)(ypos + b.yoff + 0.5); + + q->x0 = (float)round_x; + q->y0 = (float)round_y; + q->x1 = (float)round_x + b.x1 - b.x0; + q->y1 = (float)round_y + b.y1 - b.y0; + + q->s0 = b.x0 * ipw; + q->t0 = b.y0 * iph; + q->s1 = b.x1 * ipw; + q->t1 = b.y1 * iph; + + q->xadvance = b.xadvance; + + return true; +} + +void arm_g2_draw_string(const char *text, float x, float y) { + arm_g2_image_end(); + arm_g2_colored_end(); + + arm_g2_font_image_t *img = arm_g2_font_get_image_internal(g2_font, g2_font_size); + kinc_g4_texture_t *tex = img->tex; + + if (text_last_texture != NULL && tex != text_last_texture) arm_g2_text_draw_buffer(false); + text_last_texture = tex; + + float xpos = x; + float ypos = y + img->baseline; + arm_g2_font_aligned_quad_t q; + for (int i = 0; text[i] != 0; ) { + int l = 0; + int codepoint = string_utf8_decode(&text[i], &l); + i += l; + + if (arm_g2_font_get_baked_quad(g2_font, g2_font_size, &q, codepoint, xpos, ypos)) { + if (text_buffer_index + 1 >= G2_BUFFER_SIZE) arm_g2_text_draw_buffer(false); + arm_g2_text_set_rect_colors(g2_color); + arm_g2_text_set_rect_tex_coords(q.s0, q.t0, q.s1, q.t1); + + kinc_vector2_t p[4]; + arm_g2_matrix3x3_multquad(&g2_transform, q.x0, q.y0, q.x1 - q.x0, q.y1 - q.y0, p); + arm_g2_text_set_rect_verts(p[0].x, p[0].y, p[1].x, p[1].y, p[2].x, p[2].y, p[3].x, p[3].y); + + xpos += q.xadvance; + ++text_buffer_index; + } + } +} + +void arm_g2_font_default_glyphs() { + g2_font_num_glyphs = 127 - 32; + g2_font_glyphs = (int *)malloc(g2_font_num_glyphs * sizeof(int)); + for (int i = 32; i < 127; ++i) g2_font_glyphs[i - 32] = i; + g2_font_num_glyph_blocks = 2; + g2_font_glyph_blocks = (int *)malloc(g2_font_num_glyph_blocks * sizeof(int)); + g2_font_glyph_blocks[0] = 32; + g2_font_glyph_blocks[1] = 126; +} + +void arm_g2_font_init(arm_g2_font_t *font, void *blob, int font_index) { + if (g2_font_glyphs == NULL) { + arm_g2_font_default_glyphs(); + } + + font->blob = blob; + font->images = NULL; + font->m_images_len = 0; + font->m_capacity = 0; + font->offset = stbtt_GetFontOffsetForIndex(font->blob, font_index); + if (font->offset == -1) { + font->offset = stbtt_GetFontOffsetForIndex(font->blob, 0); + } +} + +void arm_g2_font_13(arm_g2_font_t *font, void *blob) { + if (g2_font_glyphs == NULL) { + arm_g2_font_default_glyphs(); + } + + font->blob = blob; + font->images = NULL; + font->m_images_len = 0; + font->m_capacity = 0; + font->offset = 0; + arm_g2_prepare_font_load_internal(font, 13); + + arm_g2_font_image_t *img = &(font->images[font->m_images_len]); + font->m_images_len += 1; + img->m_size = 13; + img->baseline = 0; // 10 + img->descent = 0; + img->line_gap = 0; + img->width = 128; + img->height = 128; + img->first_unused_y = 0; + kinc_image_t font_img; + kinc_image_init_from_bytes(&font_img, (void *)g2_font_13_pixels, 128, 128, KINC_IMAGE_FORMAT_GREY8); + img->tex = (kinc_g4_texture_t *)malloc(sizeof(kinc_g4_texture_t)); + kinc_g4_texture_init_from_image(img->tex, &font_img); + kinc_image_destroy(&font_img); + + stbtt_bakedchar *baked = (stbtt_bakedchar *)malloc(95 * sizeof(stbtt_bakedchar)); + for (int i = 0; i < 95; ++i) { + baked[i].x0 = g2_font_13_x0[i]; + baked[i].x1 = g2_font_13_x1[i]; + baked[i].y0 = g2_font_13_y0[i]; + baked[i].y1 = g2_font_13_y1[i]; + baked[i].xoff = g2_font_13_xoff[i]; + baked[i].yoff = g2_font_13_yoff[i]; + baked[i].xadvance = g2_font_13_xadvance[i]; + } + img->chars = baked; + +} + +bool arm_g2_font_has_glyph(int glyph) { + for (int i = 0; i < g2_font_num_glyphs; ++i) { + if (g2_font_glyphs[i] == glyph) { + return true; + } + } + return false; +} + +void arm_g2_font_set_glyphs(int *glyphs, int count) { + free(g2_font_glyphs); + g2_font_num_glyphs = count; + g2_font_glyphs = (int *)malloc(g2_font_num_glyphs * sizeof(int)); + + int blocks = 1; + g2_font_glyphs[0] = glyphs[0]; + int next_char = glyphs[0] + 1; + int pos = 1; + while (pos < count) { + g2_font_glyphs[pos] = glyphs[pos]; + if (glyphs[pos] != next_char) { + ++blocks; + next_char = glyphs[pos] + 1; + } + else { + ++next_char; + } + ++pos; + } + + g2_font_num_glyph_blocks = 2 * blocks; + g2_font_glyph_blocks = (int *)malloc(g2_font_num_glyph_blocks * sizeof(int)); + g2_font_glyph_blocks[0] = glyphs[0]; + next_char = glyphs[0] + 1; + pos = 1; + for (int i = 1; i < count; ++i) { + if (glyphs[i] != next_char) { + g2_font_glyph_blocks[pos * 2 - 1] = glyphs[i - 1]; + g2_font_glyph_blocks[pos * 2] = glyphs[i]; + ++pos; + next_char = glyphs[i] + 1; + } + else { + ++next_char; + } + } + g2_font_glyph_blocks[blocks * 2 - 1] = glyphs[count - 1]; +} + +void arm_g2_font_add_glyph(int glyph) { + // TODO: slow + g2_font_num_glyphs++; + int *font_glyphs = (int *)malloc(g2_font_num_glyphs * sizeof(int)); + for (int i = 0; i < g2_font_num_glyphs - 1; ++i) font_glyphs[i] = g2_font_glyphs[i]; + font_glyphs[g2_font_num_glyphs - 1] = glyph; + arm_g2_font_set_glyphs(font_glyphs, g2_font_num_glyphs); +} + +int arm_g2_font_count(arm_g2_font_t *font) { + return stbtt_GetNumberOfFonts(font->blob); +} + +float arm_g2_font_height(arm_g2_font_t *font, int font_size) { + arm_g2_font_load(font, font_size); + return arm_g2_font_get_image_internal(font, font_size)->m_size; +} + +float arm_g2_font_get_char_width_internal(arm_g2_font_image_t *img, int char_index) { + int i = arm_g2_font_get_char_index_internal(img, char_index); + return img->chars[i].xadvance; +} + +float arm_g2_sub_string_width(arm_g2_font_t *font, int font_size, const char *text, int start, int end) { + arm_g2_font_load(font, font_size); + arm_g2_font_image_t *img = arm_g2_font_get_image_internal(font, font_size); + float width = 0.0; + for (int i = start; i < end; ++i) { + if (text[i] == '\0') break; + width += arm_g2_font_get_char_width_internal(img, text[i]); + } + return width; +} + +float arm_g2_string_width(arm_g2_font_t *font, int font_size, const char *text) { + return arm_g2_sub_string_width(font, font_size, text, 0, (int)strlen(text)); +} + +void arm_g2_image_end(void) { + if (image_buffer_index > 0) arm_g2_draw_image_buffer(true); + image_last_texture = NULL; + image_last_render_target = NULL; +} + +void arm_g2_colored_end(void) { + arm_g2_colored_rect_end(false); + arm_g2_colored_tris_end(false); +} + +void arm_g2_text_end(void) { + if (text_buffer_index > 0) arm_g2_text_draw_buffer(true); + text_last_texture = NULL; +} + +void arm_g2_end(void) { + arm_g2_image_end(); + arm_g2_colored_end(); + arm_g2_text_end(); +} + +void arm_g2_set_color(uint32_t color) { + g2_color = color; +} + +uint32_t arm_g2_get_color() { + return g2_color; +} + +void arm_g2_set_pipeline(kinc_g4_pipeline_t *pipeline) { + if (pipeline == g2_last_pipeline) return; + g2_last_pipeline = pipeline; + arm_g2_end(); // flush + g2_custom_pipeline = pipeline; +} + +void arm_g2_set_transform(kinc_matrix3x3_t *m) { + if (m == NULL) { + g2_transform = kinc_matrix3x3_identity(); + } + else { + for (int i = 0; i < 3 * 3; ++i) g2_transform.m[i] = m->m[i]; + } +} + +void arm_g2_set_font(arm_g2_font_t *font, int size) { + arm_g2_end(); // flush + g2_font = font; + g2_font_size = size; + arm_g2_font_load(font, size); +} + +void arm_g2_set_bilinear_filter(bool bilinear) { + if (g2_bilinear_filter == bilinear) return; + arm_g2_end(); // flush + g2_bilinear_filter = bilinear; +} + +void arm_g2_restore_render_target(void) { + g2_is_render_target = false; + arm_g2_end(); + arm_g2_begin(); + kinc_g4_restore_render_target(); + arm_g2_internal_set_projection_matrix(NULL); +} + +void arm_g2_set_render_target(kinc_g4_render_target_t *target) { + g2_is_render_target = true; + arm_g2_end(); + arm_g2_begin(); + kinc_g4_render_target_t *render_targets[1] = { target }; + kinc_g4_set_render_targets(render_targets, 1); + arm_g2_internal_set_projection_matrix(target); +} diff --git a/armorcore/sources/kinc/graphics2/g2.h b/armorcore/sources/kinc/graphics2/g2.h new file mode 100644 index 000000000..20c036121 --- /dev/null +++ b/armorcore/sources/kinc/graphics2/g2.h @@ -0,0 +1,64 @@ + +// This implementation was adapted from tizilogic's krink and robdangerous' kha, licensed under zlib/libpng license +// https://github.com/tizilogic/krink/blob/main/Sources/krink/graphics2/graphics.h +// https://github.com/Kode/Kha/tree/main/Sources/kha/graphics2 + +#pragma once + +#include +#include +#include +#include +#include +#include + +typedef struct arm_g2_font_image arm_g2_font_image_t; + +typedef struct arm_g2_font { + unsigned char *blob; + arm_g2_font_image_t *images; + size_t m_capacity; + size_t m_images_len; + int offset; +} arm_g2_font_t; + +typedef struct g2_font { + arm_g2_font_t *font_; + void *blob; // buffer_t + void *glyphs; // i32_array_t + int index; +} g2_font_t; + +void arm_g2_init(void *image_vert, int image_vert_size, void *image_frag, int image_frag_size, void *colored_vert, int colored_vert_size, void *colored_frag, int colored_frag_size, void *text_vert, int text_vert_size, void *text_frag, int text_frag_size); +void arm_g2_begin(void); +void arm_g2_draw_scaled_sub_image(kinc_g4_texture_t *tex, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh); +void arm_g2_draw_scaled_image(kinc_g4_texture_t *tex, float dx, float dy, float dw, float dh); +void arm_g2_draw_sub_image(kinc_g4_texture_t *tex, float sx, float sy, float sw, float sh, float x, float y); +void arm_g2_draw_image(kinc_g4_texture_t *tex, float x, float y); +void arm_g2_draw_scaled_sub_render_target(kinc_g4_render_target_t *rt, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh); +void arm_g2_draw_scaled_render_target(kinc_g4_render_target_t *rt, float dx, float dy, float dw, float dh); +void arm_g2_draw_sub_render_target(kinc_g4_render_target_t *rt, float sx, float sy, float sw, float sh, float x, float y); +void arm_g2_draw_render_target(kinc_g4_render_target_t *rt, float x, float y); +void arm_g2_fill_triangle(float x0, float y0, float x1, float y1, float x2, float y2); +void arm_g2_fill_rect(float x, float y, float width, float height); +void arm_g2_draw_rect(float x, float y, float width, float height, float strength); +void arm_g2_draw_line(float x0, float y0, float x1, float y1, float strength); +void arm_g2_draw_string(const char *text, float x, float y); +void arm_g2_end(void); +void arm_g2_set_color(uint32_t color); +uint32_t arm_g2_get_color(); +void arm_g2_set_pipeline(kinc_g4_pipeline_t *pipeline); +void arm_g2_set_transform(kinc_matrix3x3_t *m); +void arm_g2_set_font(arm_g2_font_t *font, int size); +void arm_g2_font_init(arm_g2_font_t *font, void *blob, int font_index); +void arm_g2_font_13(arm_g2_font_t *font, void *blob); +bool arm_g2_font_has_glyph(int glyph); +void arm_g2_font_set_glyphs(int *glyphs, int count); +void arm_g2_font_add_glyph(int glyph); +int arm_g2_font_count(arm_g2_font_t *font); +float arm_g2_font_height(arm_g2_font_t *font, int font_size); +float arm_g2_sub_string_width(arm_g2_font_t *font, int font_size, const char *text, int start, int end); +float arm_g2_string_width(arm_g2_font_t *font, int font_size, const char *text); +void arm_g2_set_bilinear_filter(bool bilinear); +void arm_g2_restore_render_target(void); +void arm_g2_set_render_target(kinc_g4_render_target_t *target); diff --git a/armorcore/sources/kinc/graphics2/g2_ext.c b/armorcore/sources/kinc/graphics2/g2_ext.c new file mode 100644 index 000000000..6c8f1d160 --- /dev/null +++ b/armorcore/sources/kinc/graphics2/g2_ext.c @@ -0,0 +1,124 @@ +#include "g2_ext.h" + +#include +#include "g2.h" +#include "../../iron_vec2.h" + +#define MATH_PI 3.14159265358979323846 + +void arm_g2_fill_circle(float cx, float cy, float radius, int segments) { + if (segments <= 0) { + segments = (int)floor(10 * sqrtf(radius)); + } + + float theta = 2.0 * (float)MATH_PI / segments; + float c = cosf(theta); + float s = sinf(theta); + + float x = radius; + float y = 0.0; + + for (int n = 0; n < segments; ++n) { + float px = x + cx; + float py = y + cy; + + float t = x; + x = c * x - s * y; + y = c * y + s * t; + + arm_g2_fill_triangle(px, py, x + cx, y + cy, cx, cy); + } +} + +void arm_g2_draw_inner_line(float x1, float y1, float x2, float y2, float strength) { + int side = y2 > y1 ? 1 : 0; + if (y2 == y1) { + side = x2 - x1 > 0 ? 1 : 0; + } + + kinc_vector2_t vec; + if (y2 == y1) { + vec = vec2_create(0, -1); + } + else { + vec = vec2_create(1, -(x2 - x1) / (y2 - y1)); + } + vec = vec2_set_len(vec, strength); + kinc_vector2_t p1 = {x1 + side * vec.x, y1 + side * vec.y}; + kinc_vector2_t p2 = {x2 + side * vec.x, y2 + side * vec.y}; + kinc_vector2_t p3 = vec2_sub(p1, vec); + kinc_vector2_t p4 = vec2_sub(p2, vec); + arm_g2_fill_triangle(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); + arm_g2_fill_triangle(p3.x, p3.y, p2.x, p2.y, p4.x, p4.y); +} + +void arm_g2_draw_circle(float cx, float cy, float radius, int segments, float strength) { + radius += strength / 2; + + if (segments <= 0) { + segments = (int)floor(10 * sqrtf(radius)); + } + + float theta = 2 * (float)MATH_PI / segments; + float c = cosf(theta); + float s = sinf(theta); + + float x = radius; + float y = 0.0; + + for (int n = 0; n < segments; ++n) { + float px = x + cx; + float py = y + cy; + + float t = x; + x = c * x - s * y; + y = c * y + s * t; + + arm_g2_draw_inner_line(x + cx, y + cy, px, py, strength); + } +} + +void arm_g2_calculate_cubic_bezier_point(float t, float *x, float *y, float *out) { + float u = 1 - t; + float tt = t * t; + float uu = u * u; + float uuu = uu * u; + float ttt = tt * t; + + // first term + out[0] = uuu * x[0]; + out[1] = uuu * y[0]; + + // second term + out[0] += 3 * uu * t * x[1]; + out[1] += 3 * uu * t * y[1]; + + // third term + out[0] += 3 * u * tt * x[2]; + out[1] += 3 * u * tt * y[2]; + + // fourth term + out[0] += ttt * x[3]; + out[1] += ttt * y[3]; +} + +/** + * Draws a cubic bezier using 4 pairs of points. If the x and y arrays have a length bigger than 4, the additional + * points will be ignored. With a length smaller of 4 a error will occur, there is no check for this. + * You can construct the curves visually in Inkscape with a path using default nodes. + * Provide x and y in the following order: startPoint, controlPoint1, controlPoint2, endPoint + * Reference: http://devmag.org.za/2011/04/05/bzier-curves-a-tutorial/ + */ +void arm_g2_draw_cubic_bezier(float *x, float *y, int segments, float strength) { + float q0[2]; + float q1[2]; + arm_g2_calculate_cubic_bezier_point(0, x, y, q0); + + for (int i = 1; i < (segments + 1); ++i) { + float t = (float)i / segments; + arm_g2_calculate_cubic_bezier_point(t, x, y, q1); + arm_g2_draw_line(q0[0], q0[1], q1[0], q1[1], strength); + q0[0] = q1[0]; + q0[1] = q1[1]; + } +} diff --git a/armorcore/sources/kinc/graphics2/g2_ext.h b/armorcore/sources/kinc/graphics2/g2_ext.h new file mode 100644 index 000000000..a436a8eff --- /dev/null +++ b/armorcore/sources/kinc/graphics2/g2_ext.h @@ -0,0 +1,5 @@ +#pragma once + +void arm_g2_fill_circle(float cx, float cy, float radius, int segments); +void arm_g2_draw_circle(float cx, float cy, float radius, int segments, float strength); +void arm_g2_draw_cubic_bezier(float *x, float *y, int segments, float strength); diff --git a/armorcore/sources/kinc/graphics2/g2_font.h b/armorcore/sources/kinc/graphics2/g2_font.h new file mode 100644 index 000000000..be51e9e99 --- /dev/null +++ b/armorcore/sources/kinc/graphics2/g2_font.h @@ -0,0 +1,1036 @@ +#pragma once + +// Baked font data +static const char g2_font_13_x0[] = {1,2,6,10,18,25,34,42,45,50,55,61,68,71,75,79,85,92,97,104,111,118,1,8,15,22,29,32,35,41,48,55,61,72,81,89,97,105,112,119,1,9,13,20,28,35,45,53,61,69,77,85,93,101,109,1,12,20,28,36,40,46,50,56,62,66,73,80,87,94,101,106,113,120,123,1,8,11,21,28,35,42,49,54,61,66,73,80,90,97,104,111,116,119,1}; +static const char g2_font_13_y0[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,37,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,62}; +static const char g2_font_13_x1[] = {1,5,9,17,24,33,41,44,49,54,60,67,70,74,78,84,91,96,103,110,117,124,7,14,21,28,31,34,40,47,54,60,71,80,88,96,104,111,118,126,8,12,19,27,34,44,52,60,68,76,84,92,100,108,116,11,19,27,35,39,45,49,55,61,65,72,79,86,93,100,105,112,119,122,126,7,10,20,27,34,41,48,53,60,65,72,79,89,96,103,110,115,118,123,8}; +static const char g2_font_13_y1[] = {1,10,5,9,13,11,10,5,13,13,6,8,5,3,4,10,10,9,9,10,9,10,23,22,23,23,21,22,19,18,19,23,25,22,22,23,22,22,22,23,34,34,35,34,34,34,34,35,34,36,34,35,34,35,34,45,45,45,45,49,46,49,42,38,40,44,47,44,47,44,46,46,46,45,48,59,59,56,56,57,59,59,56,57,59,57,56,56,56,59,56,61,60,61,65}; +static const char g2_font_13_xoff[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +static const char g2_font_13_yoff[] = {10,2,1,2,0,1,2,1,1,1,2,3,8,6,8,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,10,1,4,1,4,1,4,1,4,1,2,2,1,1,4,4,4,4,4,4,4,2,4,4,4,4,4,4,1,2,1,5}; +static const float g2_font_13_xadvance[] = {2.74625f,2.85458f,3.54791f,6.83041f,6.22916f,8.125f,6.89541f,1.93375f,3.79166f,3.85666f,4.7775f,6.28875f,2.1775f,3.06041f,2.91958f,4.57166f,6.22916f,6.22916f,6.22916f,6.22916f,6.22916f,6.22916f,6.22916f,6.22916f,6.22916f,6.22916f,2.68666f,2.34541f,5.63875f,6.08833f,5.79583f,5.23791f,9.96125f,7.23666f,6.90625f,7.22041f,7.27458f,6.305f,6.13166f,7.55625f,7.9083f,3.01708f,6.12083f,6.955f,5.96916f,9.685f,7.90833f,7.62666f,6.99833f,7.62666f,6.83041f,6.58125f,6.61916f,7.19333f,7.05791f,9.84208f,6.955f,6.6625f,6.64083f,2.94125f,4.55f,2.94125f,4.63666f,5.005f,3.42875f,6.03416f,6.22375f,5.80666f,6.25625f,5.87708f,3.85125f,6.22375f,6.11f,2.69208f,2.64875f,5.6225f,2.69208f,9.72291f,6.12083f,6.32666f,6.22375f,6.305f,3.75375f,5.72f,3.62375f,6.11541f,5.37333f,8.33625f,5.49791f,5.24875f,5.49791f,3.75375f,2.70291f,3.75375f,7.54541f}; +static const unsigned char g2_font_13_pixels[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 20, 215, 0, 0, 21, 41, 62, 0, 0, 0, 11, 164, 0, 176, + 1, 0, 0, 0, 7, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 49, 191, 205, 94, 0, 0, 0, 36, 30, 0, 0, 0, 38, + 93, 0, 115, 15, 0, 0, 0, 0, 1, 181, 0, 0, 0, 0, 0, 51, + 85, 0, 0, 0, 15, 32, 0, 155, 194, 163, 0, 0, 12, 0, 0, 0, + 0, 0, 173, 20, 0, 0, 65, 181, 198, 99, 0, 0, 0, 22, 112, 194, + 0, 0, 100, 194, 205, 125, 0, 0, 0, 103, 197, 200, 111, 0, 0, 0, + 0, 0, 106, 200, 0, 0, 0, 119, 226, 226, 226, 126, 0, 0, 0, 0, + 0, 0, 20, 239, 0, 0, 66, 124, 190, 0, 0, 0, 58, 139, 10, 189, + 0, 0, 0, 0, 41, 165, 0, 0, 0, 12, 167, 172, 67, 0, 4, 0, + 0, 0, 0, 165, 89, 12, 224, 1, 0, 0, 112, 91, 0, 0, 18, 216, + 31, 0, 52, 187, 1, 0, 0, 95, 57, 195, 69, 63, 0, 0, 0, 96, + 160, 0, 0, 0, 83, 165, 0, 12, 15, 13, 0, 32, 230, 0, 0, 0, + 0, 37, 181, 0, 0, 1, 224, 40, 21, 207, 38, 0, 17, 211, 134, 242, + 0, 33, 213, 18, 12, 211, 52, 0, 34, 213, 13, 10, 215, 29, 0, 0, + 0, 32, 223, 226, 0, 0, 0, 159, 87, 10, 10, 6, 0, 0, 0, 0, + 0, 0, 17, 235, 0, 0, 67, 104, 171, 0, 0, 152, 196, 200, 176, 220, + 111, 0, 0, 70, 207, 235, 130, 5, 0, 91, 98, 10, 177, 0, 165, 21, + 0, 0, 0, 167, 126, 82, 188, 0, 0, 0, 112, 77, 0, 0, 128, 101, + 0, 0, 0, 158, 74, 0, 0, 67, 176, 255, 148, 49, 0, 34, 59, 133, + 182, 59, 49, 0, 152, 107, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, + 0, 134, 84, 0, 0, 41, 195, 0, 0, 140, 101, 0, 1, 0, 15, 242, + 0, 46, 65, 0, 0, 162, 88, 0, 18, 29, 0, 0, 191, 72, 0, 0, + 0, 183, 95, 226, 0, 0, 0, 185, 53, 18, 0, 0, 0, 0, 0, 0, + 0, 0, 14, 232, 0, 0, 24, 32, 56, 0, 0, 29, 172, 62, 126, 110, + 21, 0, 1, 220, 50, 15, 199, 68, 0, 62, 135, 45, 157, 78, 124, 0, + 0, 0, 0, 39, 245, 193, 16, 0, 0, 0, 31, 19, 0, 1, 236, 27, + 0, 0, 0, 65, 182, 0, 0, 5, 194, 142, 134, 0, 0, 106, 183, 210, + 228, 183, 152, 0, 80, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 209, 6, 0, 0, 91, 160, 0, 0, 104, 150, 0, 0, 0, 15, 242, + 0, 0, 0, 0, 32, 216, 16, 0, 0, 0, 98, 164, 171, 6, 0, 0, + 94, 155, 31, 226, 0, 0, 0, 211, 221, 222, 181, 27, 0, 0, 0, 0, + 0, 0, 10, 229, 0, 0, 0, 0, 0, 0, 0, 0, 195, 2, 156, 43, + 0, 0, 6, 234, 20, 0, 70, 67, 0, 1, 101, 136, 39, 184, 6, 0, + 0, 0, 9, 185, 172, 216, 19, 94, 51, 0, 0, 0, 0, 26, 231, 0, + 0, 0, 0, 21, 226, 0, 0, 19, 86, 2, 112, 0, 0, 0, 0, 96, + 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 76, 143, 0, 0, 0, 94, 160, 0, 0, 103, 149, 0, 0, 0, 15, 242, + 0, 0, 0, 12, 202, 70, 0, 0, 0, 0, 88, 149, 194, 21, 0, 23, + 212, 17, 31, 226, 0, 0, 0, 35, 6, 2, 153, 119, 0, 0, 0, 0, + 0, 0, 6, 175, 0, 0, 0, 0, 0, 0, 67, 194, 236, 189, 240, 190, + 54, 0, 0, 127, 214, 92, 5, 0, 0, 0, 0, 0, 141, 66, 90, 95, + 7, 0, 85, 203, 3, 111, 192, 199, 21, 0, 0, 0, 0, 59, 190, 0, + 0, 0, 0, 1, 245, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, + 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 174, 46, 0, 0, 0, 50, 194, 0, 0, 134, 103, 0, 0, 0, 15, 242, + 0, 0, 5, 184, 106, 0, 0, 0, 10, 11, 0, 0, 162, 103, 0, 154, + 214, 165, 176, 245, 162, 0, 3, 15, 0, 0, 75, 180, 0, 0, 0, 0, + 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 51, 146, 6, 193, 0, + 0, 0, 0, 0, 48, 183, 219, 29, 0, 0, 0, 50, 152, 95, 120, 109, + 106, 0, 48, 204, 2, 2, 192, 216, 0, 0, 0, 0, 0, 47, 218, 0, + 0, 0, 0, 10, 246, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, + 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, + 197, 0, 0, 0, 0, 2, 225, 23, 7, 198, 44, 0, 0, 0, 15, 242, + 0, 1, 163, 126, 0, 0, 0, 0, 61, 193, 1, 1, 189, 56, 0, 31, + 43, 43, 69, 231, 42, 0, 5, 215, 23, 0, 141, 113, 0, 0, 0, 0, + 0, 0, 13, 231, 2, 0, 0, 0, 0, 0, 0, 101, 96, 49, 150, 0, + 0, 0, 29, 54, 0, 0, 146, 137, 0, 0, 3, 181, 18, 136, 52, 40, + 150, 0, 0, 133, 205, 221, 188, 193, 114, 0, 0, 0, 0, 13, 254, 18, + 0, 0, 0, 52, 215, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115, + 105, 0, 0, 0, 0, 0, 87, 199, 203, 122, 2, 0, 0, 0, 15, 242, + 0, 79, 252, 209, 209, 209, 172, 0, 0, 144, 205, 203, 143, 0, 0, 0, + 0, 0, 31, 226, 0, 0, 0, 80, 200, 204, 166, 22, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 33, 223, 9, 0, 155, 111, 0, 0, 3, 42, 0, 44, 177, 174, + 55, 0, 0, 0, 2, 11, 0, 0, 0, 0, 0, 0, 0, 0, 205, 71, + 0, 0, 0, 96, 151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 133, + 16, 0, 0, 0, 0, 0, 0, 1, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 127, 224, 221, 174, 19, 0, 0, 0, 0, 0, 0, 3, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 142, + 0, 0, 5, 202, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 67, 142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 169, + 66, 0, 127, 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, + 47, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 88, 183, 127, 0, 0, 122, 210, 210, 210, 215, 163, 0, 0, + 83, 194, 205, 120, 0, 0, 0, 103, 214, 186, 67, 0, 0, 39, 191, 0, + 66, 164, 0, 0, 0, 3, 86, 159, 0, 13, 72, 72, 72, 72, 24, 0, + 62, 156, 44, 0, 0, 0, 0, 8, 156, 220, 177, 37, 0, 0, 0, 3, + 66, 137, 169, 109, 34, 0, 0, 0, 0, 0, 10, 217, 55, 0, 0, 0, + 0, 19, 226, 218, 213, 155, 51, 0, 0, 0, 15, 161, 219, 191, 98, 0, + 0, 19, 226, 218, 201, 139, 18, 0, 0, 19, 226, 218, 218, 218, 190, 0, + 19, 226, 218, 218, 218, 175, 0, 0, 16, 112, 206, 196, 111, 0, 0, 0, + 0, 0, 95, 198, 75, 4, 0, 0, 0, 0, 0, 0, 146, 97, 0, 6, + 226, 29, 11, 211, 42, 0, 45, 225, 26, 38, 226, 9, 0, 4, 42, 0, + 10, 37, 0, 9, 105, 214, 168, 59, 0, 27, 151, 151, 151, 151, 51, 0, + 9, 97, 196, 185, 72, 1, 0, 94, 157, 3, 115, 149, 0, 0, 0, 150, + 125, 43, 7, 50, 174, 41, 0, 0, 0, 0, 93, 221, 154, 0, 0, 0, + 0, 22, 244, 0, 5, 93, 198, 0, 0, 0, 188, 139, 16, 25, 211, 50, + 0, 22, 244, 0, 31, 137, 200, 11, 0, 22, 244, 0, 0, 0, 0, 0, + 22, 244, 0, 0, 0, 0, 0, 0, 170, 139, 17, 21, 201, 67, 0, 0, + 0, 0, 177, 46, 18, 0, 0, 0, 0, 0, 0, 20, 221, 7, 0, 27, + 231, 3, 0, 177, 84, 0, 101, 156, 0, 0, 165, 73, 0, 0, 0, 0, + 0, 0, 0, 150, 221, 45, 0, 0, 0, 1, 7, 7, 7, 7, 2, 0, + 0, 0, 0, 93, 246, 83, 0, 0, 0, 0, 95, 151, 0, 0, 118, 115, + 0, 19, 77, 20, 3, 181, 4, 0, 0, 0, 191, 65, 227, 11, 0, 0, + 0, 22, 244, 0, 0, 31, 242, 4, 0, 31, 248, 10, 0, 0, 78, 94, + 0, 22, 244, 0, 0, 0, 206, 83, 0, 22, 244, 0, 0, 0, 0, 0, + 22, 244, 0, 0, 0, 0, 0, 6, 234, 11, 0, 0, 45, 68, 0, 0, + 0, 6, 226, 174, 203, 180, 18, 0, 0, 0, 0, 126, 124, 0, 0, 0, + 144, 183, 159, 185, 11, 0, 77, 195, 0, 0, 158, 123, 0, 0, 0, 0, + 0, 0, 0, 8, 103, 212, 166, 59, 0, 38, 214, 214, 214, 214, 73, 0, + 11, 104, 204, 180, 67, 0, 0, 0, 0, 25, 216, 35, 0, 0, 193, 0, + 39, 207, 110, 206, 0, 117, 37, 0, 0, 34, 216, 0, 157, 94, 0, 0, + 0, 22, 250, 133, 142, 216, 97, 0, 0, 79, 192, 0, 0, 0, 0, 0, + 0, 22, 244, 0, 0, 0, 130, 140, 0, 22, 249, 126, 126, 126, 47, 0, + 22, 247, 85, 85, 85, 23, 0, 65, 197, 0, 0, 0, 0, 0, 0, 0, + 0, 59, 224, 15, 0, 156, 100, 0, 0, 0, 10, 225, 20, 0, 0, 7, + 180, 149, 130, 197, 29, 0, 9, 203, 177, 166, 231, 101, 0, 0, 12, 0, + 12, 35, 0, 0, 0, 3, 85, 158, 0, 0, 0, 0, 0, 0, 0, 0, + 61, 152, 40, 0, 0, 0, 0, 0, 0, 186, 110, 0, 0, 34, 162, 0, + 174, 56, 6, 198, 0, 88, 75, 0, 0, 131, 126, 0, 64, 192, 0, 0, + 0, 22, 247, 79, 83, 149, 186, 1, 0, 77, 186, 0, 0, 0, 0, 0, + 0, 22, 244, 0, 0, 0, 126, 144, 0, 22, 248, 90, 90, 90, 34, 0, + 22, 250, 132, 132, 132, 35, 0, 78, 195, 0, 26, 215, 222, 175, 0, 0, + 0, 33, 212, 0, 0, 94, 156, 0, 0, 0, 107, 152, 0, 0, 0, 70, + 191, 0, 0, 134, 127, 0, 0, 9, 69, 46, 218, 62, 0, 44, 218, 0, + 67, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 4, 195, 13, 0, 0, 80, 119, 0, + 219, 2, 28, 175, 0, 91, 83, 0, 2, 225, 225, 218, 219, 250, 35, 0, + 0, 22, 244, 0, 0, 0, 223, 47, 0, 13, 234, 4, 0, 0, 57, 76, + 0, 22, 244, 0, 0, 0, 203, 90, 0, 22, 244, 0, 0, 0, 0, 0, + 22, 244, 0, 0, 0, 0, 0, 28, 249, 14, 0, 0, 76, 188, 0, 0, + 0, 0, 207, 48, 1, 163, 85, 0, 0, 3, 219, 40, 0, 0, 0, 35, + 215, 3, 0, 162, 91, 0, 0, 0, 19, 116, 203, 1, 0, 0, 3, 0, + 135, 124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 49, 147, 0, + 210, 1, 89, 169, 0, 150, 14, 0, 70, 195, 0, 0, 0, 134, 132, 0, + 0, 22, 244, 0, 0, 41, 231, 9, 0, 0, 180, 109, 3, 6, 200, 60, + 0, 22, 244, 0, 17, 112, 217, 18, 0, 22, 244, 0, 0, 0, 0, 0, + 22, 244, 0, 0, 0, 0, 0, 0, 193, 134, 10, 0, 96, 188, 0, 0, + 0, 0, 58, 187, 208, 152, 9, 0, 0, 87, 179, 0, 0, 0, 0, 0, + 126, 206, 203, 163, 6, 0, 0, 74, 186, 90, 8, 0, 0, 0, 0, 0, + 76, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 9, 233, 4, 0, 0, 11, 186, 0, + 127, 205, 141, 142, 158, 109, 0, 0, 168, 103, 0, 0, 0, 41, 226, 3, + 0, 22, 253, 217, 216, 187, 82, 0, 0, 0, 30, 143, 210, 204, 124, 0, + 0, 22, 253, 217, 220, 166, 30, 0, 0, 22, 253, 217, 217, 217, 201, 0, + 22, 244, 0, 0, 0, 0, 0, 0, 18, 180, 225, 203, 202, 47, 0, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 153, 88, + 0, 8, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 172, + 150, 83, 77, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 27, 91, 96, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 19, 216, 0, 0, 0, 16, 221, 0, 2, 226, 7, 0, 0, 0, 0, + 0, 176, 60, 0, 19, 216, 0, 0, 16, 195, 73, 0, 19, 217, 0, 0, + 0, 0, 0, 19, 226, 99, 0, 0, 0, 0, 172, 173, 0, 19, 226, 57, + 0, 0, 13, 221, 0, 0, 13, 157, 224, 205, 104, 0, 0, 19, 226, 218, + 218, 170, 82, 0, 0, 0, 18, 162, 226, 202, 96, 0, 0, 20, 226, 218, + 213, 152, 57, 0, 0, 0, 79, 184, 221, 188, 38, 0, 0, 160, 218, 220, + 225, 218, 218, 80, 0, 55, 178, 0, 0, 0, 131, 106, 0, 155, 99, 0, + 0, 0, 85, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 22, 244, 0, 0, 0, 18, 249, 0, 2, 255, 8, 0, 0, 0, 0, + 0, 199, 68, 0, 22, 244, 0, 8, 193, 123, 0, 0, 22, 245, 0, 0, + 0, 0, 0, 22, 248, 208, 0, 0, 0, 37, 247, 195, 0, 22, 255, 213, + 7, 0, 15, 249, 0, 0, 178, 150, 22, 59, 207, 86, 0, 22, 244, 0, + 0, 49, 221, 41, 0, 2, 189, 142, 19, 65, 212, 76, 0, 23, 244, 0, + 5, 79, 223, 3, 0, 16, 228, 34, 2, 90, 222, 0, 0, 0, 0, 52, + 212, 0, 0, 0, 0, 62, 201, 0, 0, 0, 147, 119, 0, 85, 194, 0, + 0, 0, 179, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 22, 244, 0, 0, 0, 18, 249, 0, 2, 255, 8, 0, 0, 0, 0, + 0, 199, 68, 0, 22, 244, 3, 172, 150, 0, 0, 0, 22, 245, 0, 0, + 0, 0, 0, 22, 224, 203, 55, 0, 0, 138, 170, 195, 0, 22, 245, 169, + 132, 0, 15, 249, 0, 26, 248, 11, 0, 0, 100, 186, 0, 22, 244, 0, + 0, 0, 141, 123, 0, 39, 242, 6, 0, 0, 112, 175, 0, 23, 244, 0, + 0, 0, 219, 51, 0, 40, 228, 7, 0, 1, 97, 9, 0, 0, 0, 52, + 212, 0, 0, 0, 0, 62, 201, 0, 0, 0, 147, 119, 0, 8, 237, 29, + 0, 18, 241, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 22, 249, 126, 126, 126, 135, 249, 0, 2, 255, 8, 0, 0, 0, 0, + 0, 199, 68, 0, 22, 244, 149, 210, 3, 0, 0, 0, 22, 245, 0, 0, + 0, 0, 0, 22, 232, 99, 157, 0, 6, 227, 81, 195, 0, 22, 245, 22, + 230, 48, 15, 249, 0, 75, 191, 0, 0, 0, 35, 234, 0, 22, 244, 0, + 0, 37, 214, 48, 0, 88, 180, 0, 0, 0, 47, 223, 0, 23, 244, 10, + 19, 101, 234, 7, 0, 0, 146, 219, 116, 20, 0, 0, 0, 0, 0, 52, + 212, 0, 0, 0, 0, 62, 201, 0, 0, 0, 147, 119, 0, 0, 152, 115, + 0, 100, 169, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 22, 248, 90, 90, 90, 102, 249, 0, 2, 255, 8, 0, 0, 0, 0, + 0, 199, 68, 0, 22, 255, 188, 223, 78, 0, 0, 0, 22, 245, 0, 0, + 0, 0, 0, 22, 240, 11, 229, 15, 87, 166, 67, 195, 0, 22, 245, 0, + 91, 205, 19, 249, 0, 82, 188, 0, 0, 0, 26, 239, 0, 22, 253, 217, + 217, 180, 98, 0, 0, 94, 177, 0, 0, 0, 39, 228, 0, 23, 253, 206, + 213, 240, 31, 0, 0, 0, 0, 36, 152, 237, 107, 0, 0, 0, 0, 52, + 212, 0, 0, 0, 0, 62, 201, 0, 0, 0, 147, 119, 0, 0, 57, 203, + 0, 189, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 22, 244, 0, 0, 0, 18, 249, 0, 2, 255, 8, 0, 31, 16, 0, + 0, 203, 63, 0, 22, 247, 10, 55, 232, 32, 0, 0, 22, 245, 0, 0, + 0, 0, 0, 22, 244, 0, 147, 106, 189, 63, 70, 195, 0, 22, 245, 0, + 0, 179, 136, 249, 0, 33, 245, 7, 0, 0, 90, 192, 0, 22, 244, 0, + 0, 0, 0, 0, 0, 46, 238, 3, 0, 0, 102, 184, 0, 23, 244, 0, + 2, 208, 73, 0, 0, 38, 40, 0, 0, 35, 247, 13, 0, 0, 0, 52, + 212, 0, 0, 0, 0, 48, 214, 0, 0, 0, 161, 106, 0, 0, 1, 217, + 61, 228, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 22, 244, 0, 0, 0, 18, 249, 0, 2, 255, 8, 0, 115, 148, 0, + 27, 232, 7, 0, 22, 244, 0, 0, 109, 202, 7, 0, 22, 245, 0, 0, + 0, 0, 0, 22, 244, 0, 44, 225, 213, 1, 70, 195, 0, 22, 245, 0, + 0, 27, 236, 249, 0, 0, 200, 125, 8, 29, 190, 107, 0, 22, 244, 0, + 0, 0, 0, 0, 0, 3, 209, 116, 6, 34, 199, 97, 0, 23, 244, 0, + 0, 73, 211, 3, 0, 82, 214, 11, 0, 37, 224, 7, 0, 0, 0, 52, + 212, 0, 0, 0, 0, 0, 211, 55, 0, 20, 231, 23, 0, 0, 0, 123, + 221, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 22, 244, 0, 0, 0, 18, 249, 0, 2, 255, 8, 0, 20, 176, 210, + 208, 96, 0, 0, 22, 244, 0, 0, 0, 170, 148, 0, 22, 254, 217, 217, + 217, 151, 0, 22, 244, 0, 0, 195, 111, 0, 70, 195, 0, 22, 245, 0, + 0, 0, 101, 249, 0, 0, 23, 187, 227, 228, 135, 0, 0, 22, 244, 0, + 0, 0, 0, 0, 0, 0, 29, 192, 227, 251, 204, 1, 0, 23, 244, 0, + 0, 0, 191, 100, 0, 1, 123, 227, 206, 198, 81, 0, 0, 0, 0, 52, + 212, 0, 0, 0, 0, 0, 66, 187, 209, 207, 94, 0, 0, 0, 0, 31, + 253, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 9, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 11, 25, 184, 157, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 14, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 30, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 127, 107, 0, 0, 130, 124, 0, 0, 123, 112, 0, 79, 192, 4, 0, + 6, 197, 72, 0, 153, 114, 0, 0, 2, 191, 71, 0, 101, 218, 218, 218, + 220, 226, 7, 0, 1, 3, 3, 0, 139, 77, 0, 0, 0, 0, 3, 3, + 0, 0, 0, 49, 189, 0, 0, 0, 204, 209, 209, 209, 205, 0, 45, 51, + 0, 0, 0, 114, 207, 200, 101, 0, 0, 20, 62, 0, 0, 0, 0, 0, + 0, 87, 204, 200, 105, 0, 0, 0, 0, 0, 0, 44, 38, 0, 0, 74, + 200, 191, 87, 0, 0, 0, 0, 48, 85, 0, 0, 79, 201, 165, 146, 103, + 0, 20, 62, 0, 0, 0, 0, 0, 35, 203, 0, 0, 51, 184, 0, 0, + 0, 85, 176, 0, 0, 211, 204, 0, 0, 193, 68, 0, 1, 189, 117, 0, + 125, 183, 0, 0, 43, 234, 15, 0, 87, 202, 1, 0, 0, 0, 0, 0, + 148, 154, 0, 0, 53, 245, 172, 0, 64, 179, 0, 0, 0, 0, 196, 250, + 25, 0, 0, 154, 223, 57, 0, 0, 0, 0, 0, 0, 0, 0, 24, 209, + 29, 0, 29, 176, 9, 15, 229, 18, 0, 62, 194, 0, 0, 0, 0, 0, + 41, 225, 37, 13, 206, 38, 0, 0, 0, 0, 0, 138, 117, 0, 32, 227, + 30, 19, 216, 17, 0, 0, 97, 205, 96, 0, 15, 232, 45, 40, 205, 120, + 0, 62, 194, 0, 0, 0, 0, 0, 0, 16, 0, 0, 1, 15, 0, 0, + 0, 24, 234, 1, 32, 202, 205, 24, 5, 241, 11, 0, 0, 36, 236, 70, + 236, 32, 0, 0, 0, 155, 129, 4, 216, 63, 0, 0, 0, 0, 0, 70, + 219, 12, 0, 0, 53, 204, 0, 0, 1, 216, 26, 0, 0, 0, 0, 231, + 25, 0, 15, 213, 72, 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, + 33, 0, 0, 25, 92, 139, 230, 57, 0, 62, 194, 0, 0, 0, 0, 0, + 104, 161, 0, 0, 21, 16, 0, 0, 0, 0, 0, 138, 117, 0, 97, 207, + 119, 119, 200, 88, 0, 0, 178, 76, 0, 0, 84, 174, 0, 0, 137, 120, + 0, 62, 194, 0, 0, 0, 0, 0, 34, 186, 0, 0, 46, 174, 0, 0, + 0, 0, 217, 39, 105, 129, 135, 93, 54, 200, 0, 0, 0, 0, 120, 252, + 114, 0, 0, 0, 0, 25, 230, 117, 178, 0, 0, 0, 0, 0, 17, 225, + 59, 0, 0, 0, 53, 204, 0, 0, 0, 123, 120, 0, 0, 0, 0, 231, + 25, 0, 109, 126, 2, 213, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 13, 220, 87, 45, 208, 57, 0, 62, 210, 140, 210, 116, 6, 0, + 96, 150, 0, 0, 0, 0, 0, 0, 77, 201, 168, 181, 117, 0, 100, 193, + 91, 91, 91, 43, 0, 133, 241, 212, 100, 0, 88, 160, 0, 0, 137, 120, + 0, 62, 207, 126, 211, 147, 0, 0, 40, 216, 0, 0, 53, 202, 0, 0, + 0, 0, 155, 98, 177, 54, 63, 163, 111, 138, 0, 0, 0, 0, 112, 254, + 106, 0, 0, 0, 0, 0, 129, 251, 41, 0, 0, 0, 0, 0, 167, 135, + 0, 0, 0, 0, 53, 204, 0, 0, 0, 28, 215, 1, 0, 0, 0, 231, + 25, 0, 8, 4, 0, 9, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 74, 187, 0, 21, 229, 59, 0, 62, 238, 83, 21, 192, 74, 0, + 19, 216, 13, 0, 160, 62, 0, 15, 230, 44, 40, 205, 117, 0, 18, 224, + 18, 0, 58, 18, 0, 0, 191, 65, 0, 0, 19, 224, 19, 21, 191, 120, + 0, 62, 236, 37, 9, 213, 45, 0, 40, 216, 0, 0, 53, 202, 0, 0, + 0, 0, 94, 159, 220, 2, 5, 218, 168, 76, 0, 0, 0, 33, 236, 82, + 236, 29, 0, 0, 0, 0, 51, 215, 0, 0, 0, 0, 0, 88, 207, 6, + 0, 0, 0, 0, 53, 204, 0, 0, 0, 0, 182, 62, 0, 0, 0, 231, + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 184, 214, 167, 210, 92, 0, 62, 194, 0, 0, 105, 145, 0, + 0, 101, 201, 211, 152, 4, 0, 85, 171, 0, 0, 138, 117, 0, 0, 96, + 201, 178, 107, 8, 0, 0, 191, 65, 0, 0, 0, 108, 219, 191, 198, 119, + 0, 62, 194, 0, 0, 162, 93, 0, 40, 216, 0, 0, 53, 202, 0, 0, + 0, 0, 32, 240, 159, 0, 0, 173, 237, 16, 0, 0, 0, 185, 125, 0, + 132, 178, 0, 0, 0, 0, 51, 214, 0, 0, 0, 0, 27, 230, 44, 0, + 0, 0, 0, 0, 53, 204, 0, 0, 0, 0, 83, 160, 0, 0, 0, 231, + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 0, 0, 0, 0, 62, 194, 0, 0, 101, 156, 0, + 0, 0, 1, 9, 0, 0, 0, 93, 158, 0, 0, 138, 117, 0, 0, 0, + 0, 6, 0, 0, 0, 0, 191, 65, 0, 0, 0, 34, 6, 0, 189, 58, + 0, 62, 194, 0, 0, 162, 94, 0, 40, 216, 0, 0, 53, 202, 0, 0, + 0, 0, 0, 225, 85, 0, 0, 101, 207, 0, 0, 0, 94, 212, 6, 0, + 8, 218, 87, 0, 0, 0, 51, 214, 0, 0, 0, 0, 132, 245, 217, 217, + 217, 217, 45, 0, 53, 204, 0, 0, 0, 0, 6, 223, 14, 0, 0, 231, + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 230, 52, 5, 177, 84, 0, + 0, 0, 0, 0, 0, 0, 0, 21, 223, 19, 21, 192, 117, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 191, 65, 0, 0, 8, 204, 184, 183, 180, 1, + 0, 62, 194, 0, 0, 162, 94, 0, 40, 216, 0, 0, 53, 202, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 53, 204, 0, 0, 0, 0, 0, 107, 58, 0, 0, 231, + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 208, 169, 219, 151, 12, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 108, 219, 188, 186, 117, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 191, 65, 0, 0, 0, 4, 51, 32, 0, 0, + 0, 62, 194, 0, 0, 162, 94, 0, 0, 0, 0, 0, 57, 198, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 53, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 231, + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 183, 138, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 53, 210, 28, 0, 0, 0, 0, 0, 0, 0, 32, 234, + 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 67, 3, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 37, 176, 147, 0, 0, 0, 0, 0, 0, 0, 167, 176, + 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 19, 62, 0, 0, 0, 0, 0, 13, 69, 0, 54, 176, 139, 207, 99, + 92, 207, 203, 73, 0, 53, 172, 124, 210, 147, 0, 0, 0, 77, 201, 197, + 100, 4, 0, 53, 174, 142, 210, 116, 6, 0, 0, 80, 200, 169, 155, 100, + 0, 53, 179, 154, 140, 0, 0, 114, 201, 193, 80, 0, 0, 0, 63, 8, + 0, 0, 58, 162, 0, 0, 143, 77, 0, 146, 76, 0, 7, 204, 10, 0, + 141, 75, 0, 85, 151, 0, 10, 203, 3, 0, 95, 153, 0, 52, 192, 7, + 0, 159, 78, 0, 21, 206, 5, 0, 100, 211, 211, 214, 210, 0, 0, 0, + 0, 32, 74, 0, 12, 171, 0, 95, 11, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 195, 0, 0, 0, 0, 0, 40, 216, 0, 63, 233, 66, 13, 220, + 174, 7, 71, 199, 0, 62, 236, 37, 9, 213, 45, 0, 35, 230, 46, 19, + 185, 77, 0, 62, 232, 64, 25, 200, 71, 0, 16, 232, 42, 31, 197, 116, + 0, 62, 241, 96, 3, 0, 37, 217, 5, 33, 194, 4, 0, 0, 225, 30, + 0, 0, 67, 188, 0, 0, 166, 90, 0, 84, 166, 0, 75, 173, 0, 0, + 95, 150, 0, 172, 226, 7, 68, 173, 0, 0, 4, 204, 68, 202, 75, 0, + 0, 99, 170, 0, 97, 163, 0, 0, 0, 0, 1, 175, 121, 0, 0, 0, + 27, 215, 35, 0, 13, 193, 0, 73, 203, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 195, 0, 0, 0, 0, 0, 40, 216, 0, 63, 193, 0, 0, 166, + 93, 0, 9, 247, 0, 62, 194, 0, 0, 162, 93, 0, 101, 171, 0, 0, + 83, 163, 0, 62, 194, 0, 0, 111, 142, 0, 85, 172, 0, 0, 140, 116, + 0, 62, 194, 0, 0, 0, 19, 220, 145, 60, 2, 0, 0, 188, 248, 204, + 42, 0, 67, 188, 0, 0, 166, 90, 0, 8, 226, 9, 157, 82, 0, 0, + 23, 215, 8, 202, 144, 74, 133, 99, 0, 0, 0, 46, 237, 161, 0, 0, + 0, 16, 236, 11, 177, 74, 0, 0, 0, 0, 113, 183, 2, 0, 0, 0, + 102, 148, 0, 0, 13, 193, 0, 0, 215, 35, 0, 0, 0, 0, 0, 0, + 0, 60, 195, 0, 64, 189, 16, 0, 40, 216, 0, 63, 193, 0, 0, 164, + 92, 0, 8, 247, 0, 62, 194, 0, 0, 162, 94, 0, 94, 156, 0, 0, + 77, 191, 0, 62, 194, 0, 0, 105, 154, 0, 94, 158, 0, 0, 140, 116, + 0, 62, 194, 0, 0, 0, 0, 11, 85, 178, 193, 2, 0, 0, 225, 30, + 0, 0, 67, 189, 0, 0, 166, 90, 0, 0, 154, 84, 219, 7, 0, 0, + 0, 202, 105, 134, 62, 153, 196, 26, 0, 0, 0, 38, 240, 150, 0, 0, + 0, 0, 170, 95, 226, 5, 0, 0, 0, 55, 221, 20, 0, 0, 0, 0, + 129, 127, 0, 0, 13, 193, 0, 0, 194, 62, 0, 0, 0, 0, 0, 0, + 0, 60, 195, 54, 221, 41, 0, 0, 40, 216, 0, 63, 193, 0, 0, 164, + 92, 0, 8, 247, 0, 62, 194, 0, 0, 162, 94, 0, 15, 221, 20, 4, + 163, 127, 0, 62, 221, 30, 5, 182, 82, 0, 23, 225, 16, 12, 182, 116, + 0, 62, 194, 0, 0, 0, 74, 147, 0, 5, 236, 19, 0, 0, 225, 30, + 0, 0, 26, 226, 2, 37, 210, 90, 0, 0, 62, 221, 154, 0, 0, 0, + 0, 128, 235, 55, 3, 211, 200, 0, 0, 0, 2, 196, 84, 207, 66, 0, + 0, 0, 78, 232, 152, 0, 0, 0, 18, 220, 59, 0, 0, 0, 0, 0, + 169, 54, 0, 0, 13, 193, 0, 0, 115, 102, 0, 0, 0, 0, 0, 0, + 0, 60, 221, 235, 74, 0, 0, 0, 40, 216, 0, 63, 193, 0, 0, 164, + 92, 0, 8, 247, 0, 62, 194, 0, 0, 162, 94, 0, 0, 91, 198, 216, + 169, 6, 0, 62, 223, 176, 216, 149, 11, 0, 0, 111, 216, 190, 203, 116, + 0, 62, 194, 0, 0, 0, 12, 175, 209, 201, 125, 0, 0, 0, 225, 30, + 0, 0, 0, 152, 218, 191, 205, 90, 0, 0, 1, 223, 63, 0, 0, 0, + 0, 54, 229, 1, 0, 154, 131, 0, 0, 0, 113, 177, 0, 56, 221, 12, + 0, 0, 6, 236, 63, 0, 0, 0, 127, 244, 209, 209, 209, 27, 0, 142, + 188, 0, 0, 0, 13, 193, 0, 0, 6, 228, 80, 0, 0, 0, 0, 0, + 0, 60, 244, 155, 188, 2, 0, 0, 40, 216, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 0, 0, 0, 62, 194, 0, 7, 0, 0, 0, 0, 0, 6, 1, 140, 116, + 0, 0, 0, 0, 0, 0, 0, 0, 10, 3, 0, 0, 0, 0, 218, 37, + 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 19, 215, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 193, 20, 0, 0, 13, 193, 0, 0, 66, 157, 13, 0, 0, 0, 0, 0, + 0, 60, 195, 1, 176, 126, 0, 0, 40, 216, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 62, 194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 116, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 217, + 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 74, 202, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 134, 116, 0, 0, 13, 193, 0, 0, 183, 67, 0, 0, 0, 0, 0, 0, + 0, 60, 195, 0, 17, 220, 64, 0, 40, 216, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 29, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 46, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 118, 134, 0, 0, 13, 193, 0, 0, 200, 51, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 50, 196, 2, 0, 6, 89, 0, 18, 226, 3, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 106, 106, 0, 0, 0, 0, 149, 63, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 6, 68, 18, 0, 1, 42, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 204, 159, 218, 83, 86, 151, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 29, 108, 0, 37, 174, 160, 29, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; diff --git a/armorcore/sources/kinc/graphics2/stb_truetype.h b/armorcore/sources/kinc/graphics2/stb_truetype.h new file mode 100644 index 000000000..bbf2284b1 --- /dev/null +++ b/armorcore/sources/kinc/graphics2/stb_truetype.h @@ -0,0 +1,5077 @@ +// stb_truetype.h - v1.26 - public domain +// authored from 2009-2021 by Sean Barrett / RAD Game Tools +// +// ======================================================================= +// +// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES +// +// This library does no range checking of the offsets found in the file, +// meaning an attacker can use it to read arbitrary memory. +// +// ======================================================================= +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// render glyphs to one-channel SDF bitmaps (signed-distance field/function) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// Dougall Johnson: OpenType / Type 2 font handling +// Daniel Ribeiro Maciel: basic GPOS-based kerning +// +// Misc other: +// Ryan Gordon +// Simon Glass +// github:IntellectualKitty +// Imanol Celaya +// Daniel Ribeiro Maciel +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe +// Cass Everitt Martins Mozeiko github:aloucks +// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam +// Brian Hook Omar Cornut github:vassvik +// Walter van Niftrik Ryan Griege +// David Gow Peter LaValle +// David Given Sergey Popov +// Ivan-Assen Ivanov Giumo X. Clanjor +// Anthony Pesch Higor Euripedes +// Johan Duparc Thomas Fields +// Hou Qiming Derek Vinyard +// Rob Loach Cort Stratton +// Kenney Phillis Jr. Brian Costabile +// Ken Voskuil (kaesve) +// +// VERSION HISTORY +// +// 1.26 (2021-08-28) fix broken rasterizer +// 1.25 (2021-07-11) many fixes +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// variant PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// +// Full history can be found at the end of this file. +// +// LICENSE +// +// See end of file for license information. +// +// USAGE +// +// Include this file in whatever places need to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversampling() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections +// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetFontVMetricsOS2() +// stbtt_GetCodepointKernAdvance() +// +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// DETAILED USAGE: +// +// Scale: +// Select how high you want the font to be, in points or pixels. +// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute +// a scale factor SF that will be used by all other functions. +// +// Baseline: +// You need to select a y-coordinate that is the baseline of where +// your text will appear. Call GetFontBoundingBox to get the baseline-relative +// bounding box for all characters. SF*-y0 will be the distance in pixels +// that the worst-case character could extend above the baseline, so if +// you want the top edge of characters to appear at the top of the +// screen where y=0, then you would set the baseline to SF*-y0. +// +// Current point: +// Set the current point where the first character will appear. The +// first character could extend left of the current point; this is font +// dependent. You can either choose a current point that is the leftmost +// point and hope, or add some padding, or check the bounding box or +// left-side-bearing of the first character to be displayed and set +// the current point based on that. +// +// Displaying a character: +// Compute the bounding box of the character. It will contain signed values +// relative to . I.e. if it returns x0,y0,x1,y1, +// then the character should be displayed in the rectangle from +// to = 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype, e.g. if you don't +//// link with the C runtime library. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #define STBTT_pow(x,y) pow(x,y) + #endif + + #ifndef STBTT_fmod + #include + #define STBTT_fmod(x,y) fmod(x,y) + #endif + + #ifndef STBTT_cos + #include + #define STBTT_cos(x) cos(x) + #define STBTT_acos(x) acos(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// private structure +typedef struct +{ + unsigned char *data; + int cursor; + int size; +} stbtt__buf; + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); +// Query the font vertical metrics without having to create a font first. + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is width * height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint + int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints + int num_chars; + stbtt_packedchar *chardata_for_range; // output + unsigned char h_oversample, v_oversample; // don't set these, they're used internally +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. Note that you can call this multiple +// times within a single PackBegin/PackEnd. + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given +// pack context. The default (no oversampling) is achieved by h_oversample=1 +// and v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts +// +// To use with PackFontRangesGather etc., you must set it before calls +// call to PackFontRangesGatherRects. + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); +// If skip != 0, this tells stb_truetype to skip any codepoints for which +// there is no corresponding glyph. If skip=0, which is the default, then +// codepoints without a glyph recived the font's "missing character" glyph, +// typically an empty box by convention. + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +// Calling these functions in sequence is roughly equivalent to calling +// stbtt_PackFontRanges(). If you more control over the packing of multiple +// fonts, or if you want to pack custom data into a font texture, take a look +// at the source to of stbtt_PackFontRanges() and create a custom version +// using these functions, e.g. call GatherRects multiple times, +// building up a single array of rects, then call PackRects once, +// then call RenderIntoRects repeatedly. This may result in a +// better packing than calling PackFontRanges multiple times +// (or it may not). + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + int skip_missing; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); +// This function will determine the number of fonts in a font file. TrueType +// collection (.ttc) files may contain multiple fonts, while TrueType font +// (.ttf) files only contain one font. The number of fonts can be used for +// indexing with the previous function where the index is between zero and one +// less than the total fonts. If an error occurs, -1 is returned. + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. + +// The following structure is defined publicly so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph + + stbtt__buf cff; // cff font data + stbtt__buf charstrings; // the charstring index + stbtt__buf gsubrs; // global charstring subroutines index + stbtt__buf subrs; // private charstring subroutines index + stbtt__buf fontdicts; // array of font dicts + stbtt__buf fdselect; // map from glyph to fontdict +}; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. +// Returns 0 if the character codepoint is not defined in the font. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); +// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 +// table (specific to MS/Windows TTF files). +// +// Returns 1 on success (table present), 0 on failure. + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + +typedef struct stbtt_kerningentry +{ + int glyph1; // use stbtt_FindGlyphIndex + int glyph2; + int advance; +} stbtt_kerningentry; + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); +// Retrieves a complete list of all of the kerning pairs provided by the font +// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. +// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve, + STBTT_vcubic + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy,cx1,cy1; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of contours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl); +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); +// fills svg with the character's SVG data. +// returns data size or 0 if SVG not found. + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); +// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering +// is performed (see stbtt_PackSetOversampling) + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +// rasterize a shape with quadratic beziers into a bitmap +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into + float flatness_in_pixels, // allowable error of curve in pixels + stbtt_vertex *vertices, // array of vertices defining shape + int num_verts, // number of vertices in above array + float scale_x, float scale_y, // scale applied to input vertices + float shift_x, float shift_y, // translation applied to input vertices + int x_off, int y_off, // another translation applied to input + int invert, // if non-zero, vertically flip shape + void *userdata); // context for to STBTT_MALLOC + +////////////////////////////////////////////////////////////////////////////// +// +// Signed Distance Function (or Field) rendering + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); +// frees the SDF bitmap allocated below + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +// These functions compute a discretized SDF field for a single character, suitable for storing +// in a single-channel texture, sampling with bilinear filtering, and testing against +// larger than some threshold to produce scalable fonts. +// info -- the font +// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap +// glyph/codepoint -- the character to generate the SDF for +// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), +// which allows effects like bit outlines +// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) +// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) +// if positive, > onedge_value is inside; if negative, < onedge_value is inside +// width,height -- output height & width of the SDF bitmap (including padding) +// xoff,yoff -- output origin of the character +// return value -- a 2D array of bytes 0..255, width*height in size +// +// pixel_dist_scale & onedge_value are a scale & bias that allows you to make +// optimal use of the limited 0..255 for your application, trading off precision +// and special effects. SDF values outside the range 0..255 are clamped to 0..255. +// +// Example: +// scale = stbtt_ScaleForPixelHeight(22) +// padding = 5 +// onedge_value = 180 +// pixel_dist_scale = 180/5.0 = 36.0 +// +// This will create an SDF bitmap in which the character is about 22 pixels +// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled +// shape, sample the SDF at each pixel and fill the pixel if the SDF value +// is greater than or equal to 180/255. (You'll actually want to antialias, +// which is beyond the scope of this example.) Additionally, you can compute +// offset outlines (e.g. to stroke the character border inside & outside, +// or only outside). For example, to fill outside the character up to 3 SDF +// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above +// choice of variables maps a range from 5 pixels outside the shape to +// 2 pixels inside the shape to 0..255; this is intended primarily for apply +// outside effects only (the interior range is needed to allow proper +// antialiasing of the font at *smaller* sizes) +// +// The function computes the SDF analytically at each SDF pixel, not by e.g. +// building a higher-res bitmap and approximating it. In theory the quality +// should be as high as possible for an SDF of this size & representation, but +// unclear if this is true in practice (perhaps building a higher-res bitmap +// and computing from that can allow drop-out prevention). +// +// The algorithm has not been optimized at all, so expect it to be slow +// if computing lots of characters or very large sizes. + + + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +////////////////////////////////////////////////////////////////////////// +// +// stbtt__buf helpers to parse data from file +// + +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor++]; +} + +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +static void stbtt__buf_seek(stbtt__buf *b, int o) +{ + STBTT_assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +static void stbtt__buf_skip(stbtt__buf *b, int o) +{ + stbtt__buf_seek(b, b->cursor + o); +} + +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) +{ + stbtt_uint32 v = 0; + int i; + STBTT_assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | stbtt__buf_get8(b); + return v; +} + +static stbtt__buf stbtt__new_buf(const void *p, size_t size) +{ + stbtt__buf r; + STBTT_assert(size < 0x40000000); + r.data = (stbtt_uint8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) +#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) + +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) +{ + stbtt__buf r = stbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = stbtt__buf_get16(b); + if (count) { + offsize = stbtt__buf_get8(b); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(b, offsize * count); + stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); + } + return stbtt__buf_range(b, start, b->cursor - start); +} + +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) +{ + int b0 = stbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; + else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; + else if (b0 == 28) return stbtt__buf_get16(b); + else if (b0 == 29) return stbtt__buf_get32(b); + STBTT_assert(0); + return 0; +} + +static void stbtt__cff_skip_operand(stbtt__buf *b) { + int v, b0 = stbtt__buf_peek8(b); + STBTT_assert(b0 >= 28); + if (b0 == 30) { + stbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = stbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + stbtt__cff_int(b); + } +} + +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) +{ + stbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (stbtt__buf_peek8(b) >= 28) + stbtt__cff_skip_operand(b); + end = b->cursor; + op = stbtt__buf_get8(b); + if (op == 12) op = stbtt__buf_get8(b) | 0x100; + if (op == key) return stbtt__buf_range(b, start, end-start); + } + return stbtt__buf_range(b, 0, 0); +} + +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) +{ + int i; + stbtt__buf operands = stbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = stbtt__cff_int(&operands); +} + +static int stbtt__cff_index_count(stbtt__buf *b) +{ + stbtt__buf_seek(b, 0); + return stbtt__buf_get16(b); +} + +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) +{ + int count, offsize, start, end; + stbtt__buf_seek(&b, 0); + count = stbtt__buf_get16(&b); + offsize = stbtt__buf_get8(&b); + STBTT_assert(i >= 0 && i < count); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(&b, i*offsize); + start = stbtt__buf_get(&b, offsize); + end = stbtt__buf_get(&b, offsize); + return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + // if it's just a font, there's only one valid font + if (stbtt__isfont(font_collection)) + return 1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +// since most people won't use this, find this table the first time it's needed +static int stbtt__get_svg(stbtt_fontinfo *info) +{ + stbtt_uint32 t; + if (info->svg < 0) { + t = stbtt__find_table(info->data, info->fontstart, "SVG "); + if (t) { + stbtt_uint32 offset = ttULONG(info->data + t + 2); + info->svg = t + offset; + } else { + info->svg = 0; + } + } + return info->svg; +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) +{ + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + info->cff = stbtt__new_buf(NULL, 0); + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required + + if (!cmap || !info->head || !info->hhea || !info->hmtx) + return 0; + if (info->glyf) { + // required for truetype + if (!info->loca) return 0; + } else { + // initialization for CFF / Type2 fonts (OTF) + stbtt__buf b, topdict, topdictidx; + stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + stbtt_uint32 cff; + + cff = stbtt__find_table(data, fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = stbtt__new_buf(NULL, 0); + info->fdselect = stbtt__new_buf(NULL, 0); + + // @TODO this should use size from table (not 512MB) + info->cff = stbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + // read the header + stbtt__buf_skip(&b, 2); + stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize + + // @TODO the name INDEX could list multiple fonts, + // but we just use the first one. + stbtt__cff_get_index(&b); // name INDEX + topdictidx = stbtt__cff_get_index(&b); + topdict = stbtt__cff_index_get(topdictidx, 0); + stbtt__cff_get_index(&b); // string INDEX + info->gsubrs = stbtt__cff_get_index(&b); + + stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = stbtt__get_subrs(b, topdict); + + // we only support Type 2 charstrings + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + // looks like a CID font + if (!fdselectoff) return 0; + stbtt__buf_seek(&b, fdarrayoff); + info->fontdicts = stbtt__cff_get_index(&b); + info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); + } + + stbtt__buf_seek(&b, charstrings); + info->charstrings = stbtt__cff_get_index(&b); + } + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + info->svg = -1; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start, last; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + last = ttUSHORT(data + endCount + 2*item); + if (unicode_codepoint < start || unicode_codepoint > last) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + STBTT_assert(!info->cff.size); + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + if (info->cff.size) { + stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + } + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g; + if (info->cff.size) + return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; + g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours < 0) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + stbtt_int32 min_x, max_x, min_y, max_y; + + stbtt_vertex *pvertices; + int num_vertices; +} stbtt__csctx; + +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) +{ + if (c->bounds) { + stbtt__track_vertex(c, x, y); + if (type == STBTT_vcubic) { + stbtt__track_vertex(c, cx, cy); + stbtt__track_vertex(c, cx1, cy1); + } + } else { + stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; + c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; + } + c->num_vertices++; +} + +static void stbtt__csctx_close_shape(stbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) +{ + stbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) +{ + int count = stbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return stbtt__new_buf(NULL, 0); + return stbtt__cff_index_get(idx, n); +} + +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + stbtt__buf_seek(&fdselect, 0); + fmt = stbtt__buf_get8(&fdselect); + if (fmt == 0) { + // untested + stbtt__buf_skip(&fdselect, glyph_index); + fdselector = stbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = stbtt__buf_get16(&fdselect); + start = stbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = stbtt__buf_get8(&fdselect); + end = stbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) stbtt__new_buf(NULL, 0); + return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); +} + +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + stbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define STBTT__CSERR(s) (0) + + // this currently ignores the initial width value, which isn't needed if we have hmtx + b = stbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = stbtt__buf_get8(&b); + switch (b0) { + // @TODO implement hinting + case 0x13: // hintmask + case 0x14: // cntrmask + if (in_header) + maskbits += (sp / 2); // implicit "vstem" + in_header = 0; + stbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: // hstem + case 0x03: // vstem + case 0x12: // hstemhm + case 0x17: // vstemhm + maskbits += (sp / 2); + break; + + case 0x15: // rmoveto + in_header = 0; + if (sp < 2) return STBTT__CSERR("rmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: // vmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("vmoveto stack"); + stbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: // hmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("hmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: // rlineto + if (sp < 2) return STBTT__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical + // starting from a different place. + + case 0x07: // vlineto + if (sp < 1) return STBTT__CSERR("vlineto stack"); + goto vlineto; + case 0x06: // hlineto + if (sp < 1) return STBTT__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + stbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + stbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: // hvcurveto + if (sp < 4) return STBTT__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: // vhcurveto + if (sp < 4) return STBTT__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); + i += 4; + } + break; + + case 0x08: // rrcurveto + if (sp < 6) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: // rcurveline + if (sp < 8) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: // rlinecurve + if (sp < 8) return STBTT__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: // vvcurveto + case 0x1B: // hhcurveto + if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: // callsubr + if (!has_subrs) { + if (info->fdselect.size) + subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + // FALLTHROUGH + case 0x1D: // callgsubr + if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return STBTT__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: // return + if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: // endchar + stbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { // two-byte escape + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = stbtt__buf_get8(&b); + switch (b1) { + // @TODO These "flex" implementations ignore the flex-depth and resolution, + // and always draw beziers. + case 0x22: // hflex + if (sp < 7) return STBTT__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: // flex + if (sp < 13) return STBTT__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + //fd is s[12] + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: // hflex1 + if (sp < 9) return STBTT__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: // flex1 + if (sp < 11) return STBTT__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (STBTT_fabs(dx) > STBTT_fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return STBTT__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && b0 < 32) + return STBTT__CSERR("reserved operator"); + + // push immediate + if (b0 == 255) { + f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; + } else { + stbtt__buf_skip(&b, -1); + f = (float)(stbtt_int16)stbtt__cff_int(&b); + } + if (sp >= 48) return STBTT__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return STBTT__CSERR("no endchar"); + +#undef STBTT__CSERR +} + +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + // runs the charstring twice, once to count and once to output (to avoid realloc) + stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); + stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); + if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); + output_ctx.pvertices = *pvertices; + if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { + STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + stbtt__csctx c = STBTT__CSCTX_INIT(1); + int r = stbtt__run_charstring(info, glyph_index, &c); + if (x0) *x0 = r ? c.min_x : 0; + if (y0) *y0 = r ? c.min_y : 0; + if (x1) *x1 = r ? c.max_x : 0; + if (y1) *y1 = r ? c.max_y : 0; + return r ? c.num_vertices : 0; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + if (!info->cff.size) + return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) +{ + stbtt_uint8 *data = info->data + info->kern; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + return ttUSHORT(data+10); +} + +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) +{ + stbtt_uint8 *data = info->data + info->kern; + int k, length; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + length = ttUSHORT(data+10); + if (table_length < length) + length = table_length; + + for (k = 0; k < length; k++) + { + table[k].glyph1 = ttUSHORT(data+18+(k*6)); + table[k].glyph2 = ttUSHORT(data+20+(k*6)); + table[k].advance = ttSHORT(data+22+(k*6)); + } + + return length; +} + +static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) +{ + stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); + switch (coverageFormat) { + case 1: { + stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); + + // Binary search. + stbtt_int32 l=0, r=glyphCount-1, m; + int straw, needle=glyph; + while (l <= r) { + stbtt_uint8 *glyphArray = coverageTable + 4; + stbtt_uint16 glyphID; + m = (l + r) >> 1; + glyphID = ttUSHORT(glyphArray + 2 * m); + straw = glyphID; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + return m; + } + } + break; + } + + case 2: { + stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); + stbtt_uint8 *rangeArray = coverageTable + 4; + + // Binary search. + stbtt_int32 l=0, r=rangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *rangeRecord; + m = (l + r) >> 1; + rangeRecord = rangeArray + 6 * m; + strawStart = ttUSHORT(rangeRecord); + strawEnd = ttUSHORT(rangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else { + stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); + return startCoverageIndex + glyph - strawStart; + } + } + break; + } + + default: return -1; // unsupported + } + + return -1; +} + +static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) +{ + stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); + switch (classDefFormat) + { + case 1: { + stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); + stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); + stbtt_uint8 *classDef1ValueArray = classDefTable + 6; + + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) + return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); + break; + } + + case 2: { + stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); + stbtt_uint8 *classRangeRecords = classDefTable + 4; + + // Binary search. + stbtt_int32 l=0, r=classRangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *classRangeRecord; + m = (l + r) >> 1; + classRangeRecord = classRangeRecords + 6 * m; + strawStart = ttUSHORT(classRangeRecord); + strawEnd = ttUSHORT(classRangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else + return (stbtt_int32)ttUSHORT(classRangeRecord + 4); + } + break; + } + + default: + return -1; // Unsupported definition type, return an error. + } + + // "All glyphs not assigned to a class fall into class 0". (OpenType spec) + return 0; +} + +// Define to STBTT_assert(x) if you want to break on unimplemented formats. +#define STBTT_GPOS_TODO_assert(x) + +static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint16 lookupListOffset; + stbtt_uint8 *lookupList; + stbtt_uint16 lookupCount; + stbtt_uint8 *data; + stbtt_int32 i, sti; + + if (!info->gpos) return 0; + + data = info->data + info->gpos; + + if (ttUSHORT(data+0) != 1) return 0; // Major version 1 + if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 + + lookupListOffset = ttUSHORT(data+8); + lookupList = data + lookupListOffset; + lookupCount = ttUSHORT(lookupList); + + for (i=0; i= pairSetCount) return 0; + + needle=glyph2; + r=pairValueCount-1; + l=0; + + // Binary search. + while (l <= r) { + stbtt_uint16 secondGlyph; + stbtt_uint8 *pairValue; + m = (l + r) >> 1; + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = ttUSHORT(pairValue); + straw = secondGlyph; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + stbtt_int16 xAdvance = ttSHORT(pairValue + 2); + return xAdvance; + } + } + } else + return 0; + break; + } + + case 2: { + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? + stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); + stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); + int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); + int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); + + stbtt_uint16 class1Count = ttUSHORT(table + 12); + stbtt_uint16 class2Count = ttUSHORT(table + 14); + stbtt_uint8 *class1Records, *class2Records; + stbtt_int16 xAdvance; + + if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed + if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed + + class1Records = table + 16; + class2Records = class1Records + 2 * (glyph1class * class2Count); + xAdvance = ttSHORT(class2Records + 2 * glyph2class); + return xAdvance; + } else + return 0; + break; + } + + default: + return 0; // Unsupported position format + } + } + } + + return 0; +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) +{ + int xAdvance = 0; + + if (info->gpos) + xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); + else if (info->kern) + xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); + + return xAdvance; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) +{ + int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); + if (!tab) + return 0; + if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); + if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); + if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); + return 1; +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) +{ + int i; + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); + + int numEntries = ttUSHORT(svg_doc_list); + stbtt_uint8 *svg_docs = svg_doc_list + 2; + + for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) + return svg_doc; + } + return 0; +} + +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) +{ + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc; + + if (info->svg == 0) + return 0; + + svg_doc = stbtt_FindSVGDoc(info, gl); + if (svg_doc != NULL) { + *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); + return ttULONG(svg_doc + 8); + } else { + return 0; + } +} + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) +{ + return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + if (!z) return z; + + // round dx down to avoid overshooting + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + if (z != NULL) { + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width) +{ + STBTT_assert(top_width >= 0); + STBTT_assert(bottom_width >= 0); + return (top_width + bottom_width) / 2.0f * height; +} + +static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1) +{ + return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0); +} + +static float stbtt__sized_triangle_area(float height, float width) +{ + return height * width / 2; +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = (sy1 - sy0) * e->direction; + STBTT_assert(x >= 0 && x < len); + scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f); + scanline_fill[x] += height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, y_final, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + STBTT_assert(dy >= 0); + STBTT_assert(dx >= 0); + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = y_top + dy * (x1+1 - x0); + + // compute intersection with y axis at x2 + y_final = y_top + dy * (x2 - x0); + + // x1 x_top x2 x_bottom + // y_top +------|-----+------------+------------+--------|---+------------+ + // | | | | | | + // | | | | | | + // sy0 | Txxxxx|............|............|............|............| + // y_crossing | *xxxxx.......|............|............|............| + // | | xxxxx..|............|............|............| + // | | /- xx*xxxx........|............|............| + // | | dy < | xxxxxx..|............|............| + // y_final | | \- | xx*xxx.........|............| + // sy1 | | | | xxxxxB...|............| + // | | | | | | + // | | | | | | + // y_bottom +------------+------------+------------+------------+------------+ + // + // goal is to measure the area covered by '.' in each pixel + + // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057 + // @TODO: maybe test against sy1 rather than y_bottom? + if (y_crossing > y_bottom) + y_crossing = y_bottom; + + sign = e->direction; + + // area of the rectangle covered from sy0..y_crossing + area = sign * (y_crossing-sy0); + + // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing) + scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top); + + // check if final y_crossing is blown up; no test case for this + if (y_final > y_bottom) { + y_final = y_bottom; + dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom + } + + // in second pixel, area covered by line segment found in first pixel + // is always a rectangle 1 wide * the height of that line segment; this + // is exactly what the variable 'area' stores. it also gets a contribution + // from the line segment within it. the THIRD pixel will get the first + // pixel's rectangle contribution, the second pixel's rectangle contribution, + // and its own contribution. the 'own contribution' is the same in every pixel except + // the leftmost and rightmost, a trapezoid that slides down in each pixel. + // the second pixel's contribution to the third pixel will be the + // rectangle 1 wide times the height change in the second pixel, which is dy. + + step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, + // which multiplied by 1-pixel-width is how much pixel area changes for each step in x + // so the area advances by 'step' every time + + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; // area of trapezoid is 1*step/2 + area += step; + } + STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down + STBTT_assert(sy1 > y_final-0.01f); + + // area covered in the last pixel is the rectangle from all the pixels to the left, + // plus the trapezoid filled by the line segment in this pixel all the way to the right edge + scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f); + + // the rest of the line is filled based on the total height of the line segment in this pixel + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + // note though that this does happen some of the time because + // x_top and x_bottom can be extrapolated at the top & bottom of + // the shape and actually lie outside the bounding box + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + // + // the old way of doing this found the intersections with the left & right edges, + // then used some simple logic to produce up to three segments in sorted order + // from top-to-bottom. however, this had a problem: if an x edge was epsilon + // across the x border, then the corresponding y position might not be distinct + // from the other y segment, and it might ignored as an empty segment. to avoid + // that, we need to explicitly produce segments based on x positions. + + // rename variables to clearly-defined pairs + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + float y1 = (x - x0) / dx + y_top; + float y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { // three segments descending down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { // three segments descending down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { // one segment + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + STBTT__NOTUSED(vsubsample); + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + if (z != NULL) { + if (j == 0 && off_y != 0) { + if (z->ey < scan_y_top) { + // this can happen due to subpixel positioning and some kind of fp rounding error i think + z->ey = scan_y_top; + } + } + STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds + // insert at front + z->next = active; + active = z; + } + } + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) STBTT_fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) +{ + // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) // 65536 segments on one curve better be enough! + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count = 0; + int *winding_lengths = NULL; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + spc->skip_missing = 0; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) +{ + spc->skip_missing = skip; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + int missing_glyph_added = 0; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { + rects[k].w = rects[k].h = 0; + } else { + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0) + missing_glyph_added = 1; + } + ++k; + } + } + + return k; +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, + output, + out_w - (prefilter_x - 1), + out_h - (prefilter_y - 1), + out_stride, + scale_x, + scale_y, + shift_x, + shift_y, + glyph); + + if (prefilter_x > 1) + stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); + + if (prefilter_y > 1) + stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); + + *sub_x = stbtt__oversample_shift(prefilter_x); + *sub_y = stbtt__oversample_shift(prefilter_y); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, missing_glyph = -1, return_value = 1; + + // save current values + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed && r->w != 0 && r->h != 0) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + + if (glyph == 0) + missing_glyph = j; + } else if (spc->skip_missing) { + return_value = 0; + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + // restore original values + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + //stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) +{ + int i_ascent, i_descent, i_lineGap; + float scale; + stbtt_fontinfo info; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); + scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); + stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); + *ascent = (float) i_ascent * scale; + *descent = (float) i_descent * scale; + *lineGap = (float) i_lineGap * scale; +} + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// sdf computation +// + +#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) +#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) + +static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) +{ + float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; + float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; + float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; + float roperp = orig[1]*ray[0] - orig[0]*ray[1]; + + float a = q0perp - 2*q1perp + q2perp; + float b = q1perp - q0perp; + float c = q0perp - roperp; + + float s0 = 0., s1 = 0.; + int num_s = 0; + + if (a != 0.0) { + float discr = b*b - a*c; + if (discr > 0.0) { + float rcpna = -1 / a; + float d = (float) STBTT_sqrt(discr); + s0 = (b+d) * rcpna; + s1 = (b-d) * rcpna; + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { + if (num_s == 0) s0 = s1; + ++num_s; + } + } + } else { + // 2*b*s + c = 0 + // s = -c / (2*b) + s0 = c / (-2 * b); + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + } + + if (num_s == 0) + return 0; + else { + float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); + float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; + + float q0d = q0[0]*rayn_x + q0[1]*rayn_y; + float q1d = q1[0]*rayn_x + q1[1]*rayn_y; + float q2d = q2[0]*rayn_x + q2[1]*rayn_y; + float rod = orig[0]*rayn_x + orig[1]*rayn_y; + + float q10d = q1d - q0d; + float q20d = q2d - q0d; + float q0rd = q0d - rod; + + hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; + hits[0][1] = a*s0+b; + + if (num_s > 1) { + hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; + hits[1][1] = a*s1+b; + return 2; + } else { + return 1; + } + } +} + +static int equal(float *a, float *b) +{ + return (a[0] == b[0] && a[1] == b[1]); +} + +static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) +{ + int i; + float orig[2], ray[2] = { 1, 0 }; + float y_frac; + int winding = 0; + + // make sure y never passes through a vertex of the shape + y_frac = (float) STBTT_fmod(y, 1.0f); + if (y_frac < 0.01f) + y += 0.01f; + else if (y_frac > 0.99f) + y -= 0.01f; + + orig[0] = x; + orig[1] = y; + + // test a ray from (-infinity,y) to (x,y) + for (i=0; i < nverts; ++i) { + if (verts[i].type == STBTT_vline) { + int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; + int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } + if (verts[i].type == STBTT_vcurve) { + int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; + int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; + int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; + int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); + int by = STBTT_max(y0,STBTT_max(y1,y2)); + if (y > ay && y < by && x > ax) { + float q0[2],q1[2],q2[2]; + float hits[2][2]; + q0[0] = (float)x0; + q0[1] = (float)y0; + q1[0] = (float)x1; + q1[1] = (float)y1; + q2[0] = (float)x2; + q2[1] = (float)y2; + if (equal(q0,q1) || equal(q1,q2)) { + x0 = (int)verts[i-1].x; + y0 = (int)verts[i-1].y; + x1 = (int)verts[i ].x; + y1 = (int)verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } else { + int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); + if (num_hits >= 1) + if (hits[0][0] < 0) + winding += (hits[0][1] < 0 ? -1 : 1); + if (num_hits >= 2) + if (hits[1][0] < 0) + winding += (hits[1][1] < 0 ? -1 : 1); + } + } + } + } + return winding; +} + +static float stbtt__cuberoot( float x ) +{ + if (x<0) + return -(float) STBTT_pow(-x,1.0f/3.0f); + else + return (float) STBTT_pow( x,1.0f/3.0f); +} + +// x^3 + a*x^2 + b*x + c = 0 +static int stbtt__solve_cubic(float a, float b, float c, float* r) +{ + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; + float p3 = p*p*p; + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative + float m = (float) STBTT_cos(v); + float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); + + //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? + //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); + //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); + return 3; + } +} + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + float scale_x = scale, scale_y = scale; + int ix0,iy0,ix1,iy1; + int w,h; + unsigned char *data; + + if (scale == 0) return NULL; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + // if empty, return NULL + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + // invert for y-downwards bitmaps + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + // coarse culling against bbox + //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && + // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) + dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; + STBTT_assert(i != 0); + if (dist < min_dist) { + // check position along line + // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) + // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) + float dx = x1-x0, dy = y1-y0; + float px = x0-sx, py = y0-sy; + // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy + // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve + float t = -(px*dx + py*dy) / (dx*dx + dy*dy); + if (t >= 0.0f && t <= 1.0f) + min_dist = dist; + } + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; + float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; + float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); + float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); + float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); + float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); + // coarse culling against bbox to avoid computing cubic unnecessarily + if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { + int num=0; + float ax = x1-x0, ay = y1-y0; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float mx = x0 - sx, my = y0 - sy; + float res[3] = {0.f,0.f,0.f}; + float px,py,t,it,dist2; + float a_inv = precompute[i]; + if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula + float a = 3*(ax*bx + ay*by); + float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); + float c = mx*ax+my*ay; + if (a == 0.0) { // if a is 0, it's linear + if (b != 0.0) { + res[num++] = -c/b; + } + } else { + float discriminant = b*b - 4*a*c; + if (discriminant < 0) + num = 0; + else { + float root = (float) STBTT_sqrt(discriminant); + res[0] = (-b - root)/(2*a); + res[1] = (-b + root)/(2*a); + num = 2; // don't bother distinguishing 1-solution case, as code below will still work + } + } + } else { + float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point + float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; + float d = (mx*ax+my*ay) * a_inv; + num = stbtt__solve_cubic(b, c, d, res); + } + dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { + t = res[0], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { + t = res[1], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { + t = res[2], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + } + } + } + if (winding == 0) + min_dist = -min_dist; // if outside the shape, value is negative + val = onedge_value + pixel_dist_scale * min_dist; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; + } + } + STBTT_free(precompute, info->userdata); + STBTT_free(verts, info->userdata); + } + return data; +} + +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, + float pixel_height, unsigned char *pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar *chardata) +{ + return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) +{ + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); +} + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) +{ + return stbtt_GetNumberOfFonts_internal((unsigned char *) data); +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) +{ + return stbtt_InitFont_internal(info, (unsigned char *) data, offset); +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) +{ + return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif // STB_TRUETYPE_IMPLEMENTATION + + +// FULL VERSION HISTORY +// +// 1.25 (2021-07-11) many fixes +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// allow PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/armorcore/sources/kinc/graphics4/compute.h b/armorcore/sources/kinc/graphics4/compute.h new file mode 100644 index 000000000..2e190d675 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/compute.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include +#ifdef KINC_OPENGL +#include +#endif +#include + +/*! \file compute.h + \brief Provides support for running compute-shaders. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +struct kinc_g4_texture; +struct kinc_g4_render_target; + +typedef struct kinc_g4_compute_shader { + kinc_g4_compute_shader_impl impl; +} kinc_g4_compute_shader; + +/// +/// Initialize a compute-shader from system-specific shader-data. +/// +/// The shader-object to initialize +/// A pointer to system-specific shader-data +/// Length of the shader-data in bytes +void kinc_g4_compute_shader_init(kinc_g4_compute_shader *shader, void *source, int length); + +/// +/// Destroy a shader-object +/// +/// The shader-object to destroy +void kinc_g4_compute_shader_destroy(kinc_g4_compute_shader *shader); + +/// +/// Finds the location of a constant/uniform inside of a shader. +/// +/// The shader to look into +/// The constant/uniform-name to look for +/// The found constant-location +kinc_g4_constant_location_t kinc_g4_compute_shader_get_constant_location(kinc_g4_compute_shader *shader, const char *name); + +/// +/// Finds a texture-unit inside of a shader. +/// +/// The shader to look into +/// The texture-name to look for +/// The found texture-unit +kinc_g4_texture_unit_t kinc_g4_compute_shader_get_texture_unit(kinc_g4_compute_shader *shader, const char *name); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics4/constantlocation.h b/armorcore/sources/kinc/graphics4/constantlocation.h new file mode 100644 index 000000000..655b09d58 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/constantlocation.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include +#include + +/*! \file constantlocation.h + \brief Provides the constant_location-struct which is used for setting constants/uniforms in a shader. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_g4_constant_location { + kinc_g4_constant_location_impl_t impl; +} kinc_g4_constant_location_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics4/g4unit.c b/armorcore/sources/kinc/graphics4/g4unit.c new file mode 100644 index 000000000..8404fd740 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/g4unit.c @@ -0,0 +1,4 @@ +#include "graphics.c.h" +#include "pipeline.c.h" +#include "rendertarget.c.h" +#include "vertexbuffer.c.h" diff --git a/armorcore/sources/kinc/graphics4/graphics.c.h b/armorcore/sources/kinc/graphics4/graphics.c.h new file mode 100644 index 000000000..bc55323d9 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/graphics.c.h @@ -0,0 +1,11 @@ +#include "graphics.h" + +static int antialiasing_samples; + +int kinc_g4_antialiasing_samples(void) { + return antialiasing_samples; +} + +void kinc_g4_set_antialiasing_samples(int samples) { + antialiasing_samples = samples; +} diff --git a/armorcore/sources/kinc/graphics4/graphics.h b/armorcore/sources/kinc/graphics4/graphics.h new file mode 100644 index 000000000..49d8de243 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/graphics.h @@ -0,0 +1,418 @@ +#pragma once + +#include + +#include + +#include "constantlocation.h" +#include "pipeline.h" +#include "textureunit.h" + +/*! \file graphics.h + \brief Contains the base G4-functionality. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +struct kinc_g4_compute_shader; +struct kinc_g4_pipeline; +struct kinc_g4_render_target; +struct kinc_g4_texture; + +typedef enum { + KINC_G4_TEXTURE_ADDRESSING_REPEAT, + KINC_G4_TEXTURE_ADDRESSING_MIRROR, + KINC_G4_TEXTURE_ADDRESSING_CLAMP, + KINC_G4_TEXTURE_ADDRESSING_BORDER +} kinc_g4_texture_addressing_t; + +typedef enum { + KINC_G4_TEXTURE_DIRECTION_U, + KINC_G4_TEXTURE_DIRECTION_V, + KINC_G4_TEXTURE_DIRECTION_W +} kinc_g4_texture_direction_t; + +typedef enum { + KINC_G4_TEXTURE_FILTER_POINT, + KINC_G4_TEXTURE_FILTER_LINEAR, + KINC_G4_TEXTURE_FILTER_ANISOTROPIC +} kinc_g4_texture_filter_t; + +typedef enum { + KINC_G4_MIPMAP_FILTER_NONE, + KINC_G4_MIPMAP_FILTER_POINT, + KINC_G4_MIPMAP_FILTER_LINEAR // linear texture filter + linear mip filter -> trilinear filter +} kinc_g4_mipmap_filter_t; + +/// +/// Returns whether instanced rendering (kinc_g4_draw_indexed_vertices_instanced and pals) is supported. +/// +/// Whether instanced rendering is supported +bool kinc_g4_supports_instanced_rendering(void); + +/// +/// Returns whether GPU-compute (the functions in kinc/compute/compute.h) is supported. +/// +/// Whether GPU-compute is supported +bool kinc_g4_supports_compute_shaders(void); + +/// +/// Returns whether blend-constants (see kinc_g4_set_blend_constant and the blending-properties for pipelines) are supported. +/// +/// Whether blend-constants are supported +bool kinc_g4_supports_blend_constants(void); + +/// +/// Returns whether textures are supported which have widths/heights which are not powers of two. +/// +/// Whether non power of two texture-sizes are supported +bool kinc_g4_supports_non_pow2_textures(void); + +/// +/// Returns whether render-targets are upside down. This happens in OpenGL and there is currently no automatic mitigation. +/// +/// Whether render-targets are upside down +bool kinc_g4_render_targets_inverted_y(void); + +/// +/// Returns how many textures can be used at the same time in a fragment-shader. +/// +/// The number of textures +int kinc_g4_max_bound_textures(void); + +/// +/// Kicks off lingering work - may or may not actually do anything depending on the underlying graphics-API. +/// +void kinc_g4_flush(void); + +/// +/// Needs to be called before rendering to a window. Typically called at the start of each frame. +/// +/// The window to render to +void kinc_g4_begin(int window); + +/// +/// Needs to be called after rendering to a window. Typically called at the end of each frame. +/// +/// The window to render to +/// +void kinc_g4_end(int window); + +/// +/// Needs to be called to make the rendered frame visible. Typically called at the very end of each frame. +/// +bool kinc_g4_swap_buffers(void); + +#define KINC_G4_CLEAR_COLOR 1 +#define KINC_G4_CLEAR_DEPTH 2 +#define KINC_G4_CLEAR_STENCIL 4 + +/// +/// Clears the color, depth and/or stencil-components of the current framebuffer or render-target. +/// +/// Defines what components to clear +/// The color-value to clear to in 0xAARRGGBB +/// The depth-value to clear to +/// The stencil-value to clear to +void kinc_g4_clear(unsigned flags, unsigned color, float depth, int stencil); + +/// +/// Sets the viewport which defines the portion of the framebuffer or render-target things are rendered into. By default the viewport is equivalent to the full +/// size of the current render-target or framebuffer. +/// +/// The x-offset of the viewport from the left of the screen in pixels +/// The y-offset of the viewport from the top of the screen in pixels +/// The width of the viewport in pixels +/// The height of the viewport in pixels +void kinc_g4_viewport(int x, int y, int width, int height); + +/// +/// Enables and defines the scissor-rect. When the scissor-rect is enabled, anything that's rendered outside of the scissor-rect will be ignored. +/// +/// The x-offset of the scissor-rect from the left of the screen in pixels +/// The y-offset of the scissor-rect from the top of the screen in pixels +/// The width of the scissor-rect in pixels +/// The height of the scissor-rect in pixels +void kinc_g4_scissor(int x, int y, int width, int height); + +/// +/// Disables the scissor-rect. +/// +void kinc_g4_disable_scissor(void); + +/// +/// Draws the entire content of the currently set index-buffer and vertex-buffer. G4 can only draw triangle-lists using vertex-indices as this is what GPUs tend +/// to be optimized for. +/// +void kinc_g4_draw_indexed_vertices(void); + +/// +/// Draws a part of the content of the currently set index-buffer and vertex-buffer. G4 can only draw triangle-lists using vertex-indices as this is what GPUs +/// tend to be optimized for. +/// +/// The offset into the index-buffer +/// The number of indices to use +void kinc_g4_draw_indexed_vertices_from_to(int start, int count); + +/// +/// Draws a part of the content of the currently set index-buffer and vertex-buffer and additionally applies a general offset into the vertex-buffer. G4 can +/// only draw triangle-lists using vertex-indices as this is what GPUs tend to be optimized for. +/// +/// The offset into the index-buffer +/// The number of indices to use +/// The offset into the vertex-buffer which is added to each index read from the index-buffer +void kinc_g4_draw_indexed_vertices_from_to_from(int start, int count, int vertex_offset); + +void kinc_g4_draw_indexed_vertices_instanced(int instanceCount); + +void kinc_g4_draw_indexed_vertices_instanced_from_to(int instanceCount, int start, int count); + +void kinc_g4_set_texture_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing); + +void kinc_g4_set_texture3d_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing); + +/// +/// Sets the pipeline for the next draw-call. The pipeline defines most rendering-state including the shaders to be used. +/// +/// The pipeline to set +void kinc_g4_set_pipeline(struct kinc_g4_pipeline *pipeline); + +void kinc_g4_set_stencil_reference_value(int value); + +/// +/// Sets the blend constant used for `KINC_G4_BLEND_CONSTANT` or `KINC_G4_INV_BLEND_CONSTANT` +/// +void kinc_g4_set_blend_constant(float r, float g, float b, float a); + + +/// +/// Assigns an integer to a constant/uniform in the currently set pipeline. +/// +/// The location of the constant/uniform to assign the value to +/// The value to assign to the constant/uniform +void kinc_g4_set_int(kinc_g4_constant_location_t location, int value); + +/// +/// Assigns two integers to a constant/uniform in the currently set pipeline. +/// +/// The location of the constant/uniform to assign the values to +/// The value to assign to the first component of the constant/uniform +/// The value to assign to the second component of the constant/uniform +void kinc_g4_set_int2(kinc_g4_constant_location_t location, int value1, int value2); + +/// +/// Assigns three integers to a constant/uniform in the currently set pipeline. +/// +/// The location of the constant/uniform to assign the values to +/// The value to assign to the first component of the constant/uniform +/// The value to assign to the second component of the constant/uniform +/// The value to assign to the third component of the constant/uniform +void kinc_g4_set_int3(kinc_g4_constant_location_t location, int value1, int value2, int value3); + +/// +/// Assigns four integers to a constant/uniform in the currently set pipeline. +/// +/// The location of the constant/uniform to assign the values to +/// The value to assign to the first component of the constant/uniform +/// The value to assign to the second component of the constant/uniform +/// The value to assign to the third component of the constant/uniform +/// The value to assign to the fourth component of the constant/uniform +void kinc_g4_set_int4(kinc_g4_constant_location_t location, int value1, int value2, int value3, int value4); + +/// +/// Assigns a bunch of integers to a constant/uniform in the currently set pipeline. +/// +/// The location of the constant/uniform to assign the values to +/// The values to assign to the constant/uniform +/// The number of values to assign to the constant/uniform +void kinc_g4_set_ints(kinc_g4_constant_location_t location, int *values, int count); + +/// +/// Assigns a float to a constant/uniform in the currently set pipeline. +/// +/// The location of the constant/uniform to assign the value to +/// The value to assign to the constant/uniform +void kinc_g4_set_float(kinc_g4_constant_location_t location, float value); + +/// +/// Assigns two floats to a constant/uniform in the currently set pipeline. +/// +/// The location of the constant/uniform to assign the values to +/// The value to assign to the first constant/uniform +/// The value to assign to the second constant/uniform +void kinc_g4_set_float2(kinc_g4_constant_location_t location, float value1, float value2); + +/// +/// Assigns three floats to a constant/uniform in the currently set pipeline. +/// +/// The location of the constant/uniform to assign the values to +/// The value to assign to the first constant/uniform +/// The value to assign to the second constant/uniform +/// The value to assign to the third constant/uniform +void kinc_g4_set_float3(kinc_g4_constant_location_t location, float value1, float value2, float value3); + +/// +/// Assigns four floats to a constant/uniform in the currently set pipeline. +/// +/// The location of the constant/uniform to assign the values to +/// The value to assign to the first constant/uniform +/// The value to assign to the second constant/uniform +/// The value to assign to the third constant/uniform +/// The value to assign to the fourth constant/uniform +void kinc_g4_set_float4(kinc_g4_constant_location_t location, float value1, float value2, float value3, float value4); + +/// +/// Assigns a bunch of floats to a constant/uniform in the currently set pipeline. +/// +/// The location of the constant/uniform to assign the values to +/// The values to assign to the constant/uniform +/// The number of values to assign to the constant/uniform +void kinc_g4_set_floats(kinc_g4_constant_location_t location, float *values, int count); + +/// +/// Assigns a bool to a constant/uniform in the currently set pipeline. +/// +/// The location of the constant/uniform to assign the value to +/// The value to assign to the constant/uniform +void kinc_g4_set_bool(kinc_g4_constant_location_t location, bool value); + +/// +/// Assigns a 3x3-matrix to a constant/uniform in the currently set pipeline. +/// +/// The location of the constant/uniform to assign the value to +/// The value to assign to the constant/uniform +void kinc_g4_set_matrix3(kinc_g4_constant_location_t location, kinc_matrix3x3_t *value); + +/// +/// Assigns a 4x4-matrix to a constant/uniform in the currently set pipeline. +/// +/// The location of the constant/uniform to assign the value to +/// The value to assign to the constant/uniform +void kinc_g4_set_matrix4(kinc_g4_constant_location_t location, kinc_matrix4x4_t *value); + +/// +/// Set the texture-sampling-mode for upscaled textures. +/// +/// The texture-unit to set the texture-sampling-mode for +/// The mode to set +void kinc_g4_set_texture_magnification_filter(kinc_g4_texture_unit_t unit, kinc_g4_texture_filter_t filter); + +/// +/// Set the texture-sampling-mode for upscaled 3D-textures. +/// +/// The texture-unit to set the texture-sampling-mode for +/// The mode to set +void kinc_g4_set_texture3d_magnification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter); + +/// +/// Set the texture-sampling-mode for downscaled textures. +/// +/// The texture-unit to set the texture-sampling-mode for +/// The mode to set +void kinc_g4_set_texture_minification_filter(kinc_g4_texture_unit_t unit, kinc_g4_texture_filter_t filter); + +/// +/// Set the texture-sampling-mode for downscaled 3D-textures. +/// +/// The texture-unit to set the texture-sampling-mode for +/// The mode to set +void kinc_g4_set_texture3d_minification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter); + +/// +/// Sets the mipmap-sampling-mode which defines whether mipmaps are used at all and if so whether the two neighbouring mipmaps are linearly interpolated. +/// +/// The texture-unit to set the mipmap-sampling-mode for +/// The mode to set +void kinc_g4_set_texture_mipmap_filter(kinc_g4_texture_unit_t unit, kinc_g4_mipmap_filter_t filter); + +/// +/// Sets the mipmap-sampling-mode for a 3D-texture which defines whether mipmaps are used at all and if so whether the two neighbouring mipmaps are linearly +/// interpolated. +/// +/// The texture-unit to set the mipmap-sampling-mode for +/// The mode to set +void kinc_g4_set_texture3d_mipmap_filter(kinc_g4_texture_unit_t texunit, kinc_g4_mipmap_filter_t filter); + +void kinc_g4_set_texture_compare_mode(kinc_g4_texture_unit_t unit, bool enabled); + +void kinc_g4_set_texture_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode); + +void kinc_g4_set_cubemap_compare_mode(kinc_g4_texture_unit_t unit, bool enabled); + +void kinc_g4_set_cubemap_compare_func(kinc_g4_texture_unit_t unit, kinc_g4_compare_mode_t mode); + +void kinc_g4_set_texture_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy); + +void kinc_g4_set_cubemap_max_anisotropy(kinc_g4_texture_unit_t unit, uint16_t max_anisotropy); + +void kinc_g4_set_texture_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp); + +void kinc_g4_set_cubemap_lod(kinc_g4_texture_unit_t unit, float lod_min_clamp, float lod_max_clamp); + +/// +/// Sets the framebuffer (aka the actual contents of the current window) to be the target of any future draw-calls. +/// +void kinc_g4_restore_render_target(void); + +/// +/// Sets the passed render-targets to be the target of any future draw-calls. +/// +/// An array of render-targets +/// The number of render-targets in the render-target-array +void kinc_g4_set_render_targets(struct kinc_g4_render_target **targets, int count); + +void kinc_g4_set_render_target_face(struct kinc_g4_render_target *texture, int face); + +/// +/// Assigns a texture to a texture-unit for sampled access via GLSL's texture. +/// +/// The unit to assign this texture to +/// The texture to assign to the unit +void kinc_g4_set_texture(kinc_g4_texture_unit_t unit, struct kinc_g4_texture *texture); + +/// +/// Assigns a texture to a texture-unit for direct access via GLSL's texelFetch (as +/// opposed to GLSL's texture). The name of this functions is unfortunately based +/// on OpenGL's confusing terminology. +/// +/// The unit to assign this texture to +/// The texture to assign to the unit +void kinc_g4_set_image_texture(kinc_g4_texture_unit_t unit, struct kinc_g4_texture *texture); + +/// +/// Returns the currently used number of samples for hardware-antialiasing. +/// +/// The number of samples +int kinc_g4_antialiasing_samples(void); + +/// +/// Sets the number of samples used for hardware-antialiasing. This typically uses multisampling and typically only works with a few specific numbers of +/// sample-counts - 2 and 4 are pretty safe bets. It also might do nothing at all. +/// +/// The number of samples +void kinc_g4_set_antialiasing_samples(int samples); + +/// +/// Sets a shader for the next compute-run. +/// +/// The shader to use +void kinc_g4_set_compute_shader(struct kinc_g4_compute_shader *shader); + +/// +/// Fire off a compute-run on x * y * z elements. +/// +/// The x-size for the compute-run +/// The y-size for the compute-run +/// The z-size for the compute-run +void kinc_g4_compute(int x, int y, int z); + +void kinc_g4_internal_init(void); +void kinc_g4_internal_init_window(int window, int depth_buffer_bits, int stencil_buffer_bits, bool vsync); +void kinc_g4_internal_destroy_window(int window); +void kinc_g4_internal_destroy(void); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics4/indexbuffer.h b/armorcore/sources/kinc/graphics4/indexbuffer.h new file mode 100644 index 000000000..1d86936f7 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/indexbuffer.h @@ -0,0 +1,84 @@ +#pragma once + +#include + +#include "usage.h" + +#include + +/*! \file indexbuffer.h + \brief Provides functions for setting up and using index-buffers. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kinc_g4_index_buffer_format { KINC_G4_INDEX_BUFFER_FORMAT_32BIT, KINC_G4_INDEX_BUFFER_FORMAT_16BIT } kinc_g4_index_buffer_format_t; + +typedef struct kinc_g4_index_buffer { + kinc_g4_index_buffer_impl_t impl; +} kinc_g4_index_buffer_t; + +/// +/// Initializes an index-buffer. +/// +/// The buffer to initialize +/// The number of indices to allocate for the buffer +/// The integer-format of the buffer +/// A hint for how the buffer will be used +void kinc_g4_index_buffer_init(kinc_g4_index_buffer_t *buffer, int count, kinc_g4_index_buffer_format_t format, kinc_g4_usage_t usage); + +/// +/// Destroys an index-buffer. +/// +/// The buffer to destroy +void kinc_g4_index_buffer_destroy(kinc_g4_index_buffer_t *buffer); + +/// +/// Locks an index-buffer so its contents can be modified. +/// +/// The buffer to lock +/// The contents of the index-buffer in uint32s or uint16s depending on the format provided when initializing +void *kinc_g4_index_buffer_lock_all(kinc_g4_index_buffer_t *buffer); + +/// +/// Locks part of a vertex-buffer to modify its contents. +/// +/// The buffer to lock +/// The index of the first index to lock +/// The number of indices to lock +/// The contents of the index-buffer, starting at start, in uint32s or uint16s depending on the format provided when initializing +void *kinc_g4_index_buffer_lock(kinc_g4_index_buffer_t *buffer, int start, int count); + +/// +/// Unlocks an index-buffer after locking it so the changed buffer-contents can be used. +/// +/// The buffer to unlock +void kinc_g4_index_buffer_unlock_all(kinc_g4_index_buffer_t *buffer); + +/// +/// Unlocks part of an index-buffer after locking so the changed buffer-contents can be used. +/// +/// The buffer to unlock +/// The number of indices to unlock, starting from the start-index from the previous lock-call +void kinc_g4_index_buffer_unlock(kinc_g4_index_buffer_t *buffer, int count); + +/// +/// Returns the number of indices in the buffer. +/// +/// The buffer to query for its number of indices +/// The number of indices +int kinc_g4_index_buffer_count(kinc_g4_index_buffer_t *buffer); + +void kinc_internal_g4_index_buffer_set(kinc_g4_index_buffer_t *buffer); + +/// +/// Sets an index-buffer to be used for the next draw-command. +/// +/// The buffer to use +void kinc_g4_set_index_buffer(kinc_g4_index_buffer_t *buffer); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics4/pipeline.c.h b/armorcore/sources/kinc/graphics4/pipeline.c.h new file mode 100644 index 000000000..35b308ae1 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/pipeline.c.h @@ -0,0 +1,55 @@ +#include "pipeline.h" + +#include + +void kinc_g4_internal_pipeline_set_defaults(kinc_g4_pipeline_t *state) { + for (int i = 0; i < 16; ++i) + state->input_layout[i] = NULL; + state->vertex_shader = NULL; + state->fragment_shader = NULL; + state->geometry_shader = NULL; + state->tessellation_control_shader = NULL; + state->tessellation_evaluation_shader = NULL; + + state->cull_mode = KINC_G4_CULL_NOTHING; + + state->depth_write = false; + state->depth_mode = KINC_G4_COMPARE_ALWAYS; + + state->stencil_front_mode = KINC_G4_COMPARE_ALWAYS; + state->stencil_front_both_pass = KINC_G4_STENCIL_KEEP; + state->stencil_front_depth_fail = KINC_G4_STENCIL_KEEP; + state->stencil_front_fail = KINC_G4_STENCIL_KEEP; + state->stencil_back_mode = KINC_G4_COMPARE_ALWAYS; + state->stencil_back_both_pass = KINC_G4_STENCIL_KEEP; + state->stencil_back_depth_fail = KINC_G4_STENCIL_KEEP; + state->stencil_back_fail = KINC_G4_STENCIL_KEEP; + state->stencil_reference_value = 0; + state->stencil_read_mask = 0xff; + state->stencil_write_mask = 0xff; + + state->blend_source = KINC_G4_BLEND_ONE; + state->blend_destination = KINC_G4_BLEND_ZERO; + state->blend_operation = KINC_G4_BLENDOP_ADD; + state->alpha_blend_source = KINC_G4_BLEND_ONE; + state->alpha_blend_destination = KINC_G4_BLEND_ZERO; + state->alpha_blend_operation = KINC_G4_BLENDOP_ADD; + + for (int i = 0; i < 8; ++i) + state->color_write_mask_red[i] = true; + for (int i = 0; i < 8; ++i) + state->color_write_mask_green[i] = true; + for (int i = 0; i < 8; ++i) + state->color_write_mask_blue[i] = true; + for (int i = 0; i < 8; ++i) + state->color_write_mask_alpha[i] = true; + + state->color_attachment_count = 1; + for (int i = 0; i < 8; ++i) + state->color_attachment[i] = KINC_G4_RENDER_TARGET_FORMAT_32BIT; + + state->depth_attachment_bits = 0; + state->stencil_attachment_bits = 0; + + state->conservative_rasterization = false; +} diff --git a/armorcore/sources/kinc/graphics4/pipeline.h b/armorcore/sources/kinc/graphics4/pipeline.h new file mode 100644 index 000000000..1878cd5e5 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/pipeline.h @@ -0,0 +1,159 @@ +#pragma once + +#include + +#include +#include +#include + +#include + +/*! \file pipeline.h + \brief Provides functions for creating and using pipelines which configure the GPU for rendering. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +struct kinc_g4_vertex_structure; +struct kinc_g4_shader; + +typedef enum { + KINC_G4_BLEND_ONE, + KINC_G4_BLEND_ZERO, + KINC_G4_BLEND_SOURCE_ALPHA, + KINC_G4_BLEND_DEST_ALPHA, + KINC_G4_BLEND_INV_SOURCE_ALPHA, + KINC_G4_BLEND_INV_DEST_ALPHA, + KINC_G4_BLEND_SOURCE_COLOR, + KINC_G4_BLEND_DEST_COLOR, + KINC_G4_BLEND_INV_SOURCE_COLOR, + KINC_G4_BLEND_INV_DEST_COLOR, + KINC_G4_BLEND_CONSTANT, + KINC_G4_BLEND_INV_CONSTANT +} kinc_g4_blending_factor_t; + +typedef enum { + KINC_G4_BLENDOP_ADD, + KINC_G4_BLENDOP_SUBTRACT, + KINC_G4_BLENDOP_REVERSE_SUBTRACT, + KINC_G4_BLENDOP_MIN, + KINC_G4_BLENDOP_MAX +} kinc_g4_blending_operation_t; + +typedef enum { + KINC_G4_COMPARE_ALWAYS, + KINC_G4_COMPARE_NEVER, + KINC_G4_COMPARE_EQUAL, + KINC_G4_COMPARE_NOT_EQUAL, + KINC_G4_COMPARE_LESS, + KINC_G4_COMPARE_LESS_EQUAL, + KINC_G4_COMPARE_GREATER, + KINC_G4_COMPARE_GREATER_EQUAL +} kinc_g4_compare_mode_t; + +typedef enum { KINC_G4_CULL_CLOCKWISE, KINC_G4_CULL_COUNTER_CLOCKWISE, KINC_G4_CULL_NOTHING } kinc_g4_cull_mode_t; + +typedef enum { + KINC_G4_STENCIL_KEEP, + KINC_G4_STENCIL_ZERO, + KINC_G4_STENCIL_REPLACE, + KINC_G4_STENCIL_INCREMENT, + KINC_G4_STENCIL_INCREMENT_WRAP, + KINC_G4_STENCIL_DECREMENT, + KINC_G4_STENCIL_DECREMENT_WRAP, + KINC_G4_STENCIL_INVERT +} kinc_g4_stencil_action_t; + +typedef struct kinc_g4_pipeline { + struct kinc_g4_vertex_structure *input_layout[16]; + struct kinc_g4_shader *vertex_shader; + struct kinc_g4_shader *fragment_shader; + struct kinc_g4_shader *geometry_shader; + struct kinc_g4_shader *tessellation_control_shader; + struct kinc_g4_shader *tessellation_evaluation_shader; + + kinc_g4_cull_mode_t cull_mode; + + bool depth_write; + kinc_g4_compare_mode_t depth_mode; + + kinc_g4_compare_mode_t stencil_front_mode; + kinc_g4_stencil_action_t stencil_front_both_pass; + kinc_g4_stencil_action_t stencil_front_depth_fail; + kinc_g4_stencil_action_t stencil_front_fail; + + kinc_g4_compare_mode_t stencil_back_mode; + kinc_g4_stencil_action_t stencil_back_both_pass; + kinc_g4_stencil_action_t stencil_back_depth_fail; + kinc_g4_stencil_action_t stencil_back_fail; + + int stencil_reference_value; + int stencil_read_mask; + int stencil_write_mask; + + // One, Zero deactivates blending + kinc_g4_blending_factor_t blend_source; + kinc_g4_blending_factor_t blend_destination; + kinc_g4_blending_operation_t blend_operation; + kinc_g4_blending_factor_t alpha_blend_source; + kinc_g4_blending_factor_t alpha_blend_destination; + kinc_g4_blending_operation_t alpha_blend_operation; + + bool color_write_mask_red[8]; // Per render target + bool color_write_mask_green[8]; + bool color_write_mask_blue[8]; + bool color_write_mask_alpha[8]; + + int color_attachment_count; + kinc_g4_render_target_format_t color_attachment[8]; + + int depth_attachment_bits; + int stencil_attachment_bits; + + bool conservative_rasterization; + + kinc_g4_pipeline_impl_t impl; +} kinc_g4_pipeline_t; + +/// +/// Initializes a pipeline. +/// +/// The pipeline to initialize +void kinc_g4_pipeline_init(kinc_g4_pipeline_t *pipeline); + +/// +/// Destroys a pipeline. +/// +/// The pipeline to destroy +void kinc_g4_pipeline_destroy(kinc_g4_pipeline_t *pipeline); + +/// +/// Compiles a pipeline. After a pipeline has been compiled it is finalized. It cannot be compiled again and further changes to the pipeline are ignored. +/// +/// The pipeline to compile +void kinc_g4_pipeline_compile(kinc_g4_pipeline_t *pipeline); + +/// +/// Searches for a constant/uniform and returns a constant-location which can be used to change the constant/uniform. +/// +/// The pipeline to search in +/// The name of the constant/uniform to find +/// The constant-location of the constant/uniform +kinc_g4_constant_location_t kinc_g4_pipeline_get_constant_location(kinc_g4_pipeline_t *pipeline, const char *name); + +/// +/// Searches for a texture-declaration and returns a texture-unit which can be used to assign a texture. +/// +/// The pipeline to search in +/// The name of the texture-declaration to search for +/// The texture-unit of the texture-declaration +kinc_g4_texture_unit_t kinc_g4_pipeline_get_texture_unit(kinc_g4_pipeline_t *pipeline, const char *name); + +void kinc_g4_internal_set_pipeline(kinc_g4_pipeline_t *pipeline); +void kinc_g4_internal_pipeline_set_defaults(kinc_g4_pipeline_t *pipeline); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics4/rendertarget.c.h b/armorcore/sources/kinc/graphics4/rendertarget.c.h new file mode 100644 index 000000000..d19ec5303 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/rendertarget.c.h @@ -0,0 +1,11 @@ +#include "rendertarget.h" + +void kinc_g4_render_target_init(kinc_g4_render_target_t *renderTarget, int width, int height, kinc_g4_render_target_format_t format, int depthBufferBits, + int stencilBufferBits) { + kinc_g4_render_target_init_with_multisampling(renderTarget, width, height, format, depthBufferBits, stencilBufferBits, 1); +} + +void kinc_g4_render_target_init_cube(kinc_g4_render_target_t *renderTarget, int cubeMapSize, kinc_g4_render_target_format_t format, int depthBufferBits, + int stencilBufferBits) { + kinc_g4_render_target_init_cube_with_multisampling(renderTarget, cubeMapSize, format, depthBufferBits, stencilBufferBits, 1); +} \ No newline at end of file diff --git a/armorcore/sources/kinc/graphics4/rendertarget.h b/armorcore/sources/kinc/graphics4/rendertarget.h new file mode 100644 index 000000000..8eab62186 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/rendertarget.h @@ -0,0 +1,135 @@ +#pragma once + +#include + +#include + +#include "textureunit.h" + +#include + +/*! \file rendertarget.h + \brief Provides functions for handling render-targets. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kinc_g4_render_target_format { + KINC_G4_RENDER_TARGET_FORMAT_32BIT, + KINC_G4_RENDER_TARGET_FORMAT_64BIT_FLOAT, + KINC_G4_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT, + KINC_G4_RENDER_TARGET_FORMAT_128BIT_FLOAT, + KINC_G4_RENDER_TARGET_FORMAT_16BIT_DEPTH, + KINC_G4_RENDER_TARGET_FORMAT_8BIT_RED, + KINC_G4_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT +} kinc_g4_render_target_format_t; + +typedef struct kinc_g4_render_target { + int width; + int height; + int texWidth; + int texHeight; + bool isCubeMap; + bool isDepthAttachment; + + kinc_g4_render_target_impl_t impl; +} kinc_g4_render_target_t; + +/// +/// Allocates and initializes a regular render-target. The contents of the render-target are undefined. +/// +/// +/// +/// +/// +/// +/// +void kinc_g4_render_target_init(kinc_g4_render_target_t *renderTarget, int width, int height, kinc_g4_render_target_format_t format, + int depthBufferBits, int stencilBufferBits); + +/// +/// Allocates and initializes a multi-sampled render-target if possible - otherwise it falls back to a regular render-target. The contents of the render-target +/// are undefined. +/// +/// +/// +/// +/// +/// +/// +/// +void kinc_g4_render_target_init_with_multisampling(kinc_g4_render_target_t *renderTarget, int width, int height, + kinc_g4_render_target_format_t format, int depthBufferBits, int stencilBufferBits, + int samples_per_pixel); + +/// +/// Allocates and initializes a render-target-cube-map. The contents of the render-target are undefined. +/// +/// +/// +/// +/// +/// +void kinc_g4_render_target_init_cube(kinc_g4_render_target_t *renderTarget, int cubeMapSize, kinc_g4_render_target_format_t format, + int depthBufferBits, int stencilBufferBits); + +/// +/// Allocates and initializes a multi-sampled render-target-cube-map. Can fall back to a non-multi-sampled cube-map. The contents of the render-target are +/// undefined. +/// +/// +/// +/// +/// +/// +/// +void kinc_g4_render_target_init_cube_with_multisampling(kinc_g4_render_target_t *renderTarget, int cubeMapSize, kinc_g4_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel); + +/// +/// Deallocates and destroys a render-target. +/// +/// The render-target to destroy +void kinc_g4_render_target_destroy(kinc_g4_render_target_t *renderTarget); + +/// +/// Uses the color-component of a render-target as a texture. +/// +/// The render-target to use +/// The texture-unit to assign the render-target to +void kinc_g4_render_target_use_color_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit); + +/// +/// Uses the depth-component of a render-target as a texture. +/// +/// The render-target to use +/// The texture-unit to assign the render-target to +void kinc_g4_render_target_use_depth_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit); + +/// +/// Copies the depth and stencil-components of one render-target into another one. +/// +/// The render-target to copy the data into +/// The render-target from which to copy the data +/// +void kinc_g4_render_target_set_depth_stencil_from(kinc_g4_render_target_t *renderTarget, kinc_g4_render_target_t *source); + +/// +/// Copies out the color-data from a render-target. Beware, this is very slow. +/// +/// The render-target to copy the color-data from +/// A pointer to where the data will be copied to +void kinc_g4_render_target_get_pixels(kinc_g4_render_target_t *renderTarget, uint8_t *data); + +/// +/// Generates the mipmap-chain for a render-target. +/// +/// The render-target to create the mipmaps for +/// The number of mipmap-levels to generate +void kinc_g4_render_target_generate_mipmaps(kinc_g4_render_target_t *renderTarget, int levels); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics4/shader.h b/armorcore/sources/kinc/graphics4/shader.h new file mode 100644 index 000000000..d6857ce06 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/shader.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include + +/*! \file shader.h + \brief Provides functions for creating and destroying shaders. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kinc_g4_shader_type { + KINC_G4_SHADER_TYPE_FRAGMENT, + KINC_G4_SHADER_TYPE_VERTEX, + KINC_G4_SHADER_TYPE_COMPUTE, + KINC_G4_SHADER_TYPE_GEOMETRY, + KINC_G4_SHADER_TYPE_TESSELLATION_CONTROL, + KINC_G4_SHADER_TYPE_TESSELLATION_EVALUATION, + + KINC_G4_SHADER_TYPE_COUNT +} kinc_g4_shader_type_t; + +typedef struct kinc_g4_shader { + kinc_g4_shader_impl_t impl; +} kinc_g4_shader_t; + +/// +/// Initializes a shader based on system-specific shader-data. The system-specific shader-data is usually created per system by the krafix-shader-compiler which +/// is automatically called by kincmake. +/// +/// The shader to initialize +/// The system-specific shader-data +/// The length of the system-specific shader-data in bytes +/// The type of the shader +void kinc_g4_shader_init(kinc_g4_shader_t *shader, const void *data, size_t length, kinc_g4_shader_type_t type); + +/// +/// Initializes a shader from GLSL-source-code. This only works on some platforms and only if KRAFIX_LIBRARY define has been set and the krafix-shader-compiler +/// was compiled in library-mode and linked into the application. +/// +/// The shader to initialize +/// The GLSL-shader-source-code +/// The type of the shader +/// The number of errors the compiler encountered - hopefully it's zero. +int kinc_g4_shader_init_from_source(kinc_g4_shader_t *shader, const char *source, kinc_g4_shader_type_t type); + +/// +/// Destroys a shader. +/// +/// The shader to destroy +void kinc_g4_shader_destroy(kinc_g4_shader_t *shader); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics4/texture.h b/armorcore/sources/kinc/graphics4/texture.h new file mode 100644 index 000000000..48a62543a --- /dev/null +++ b/armorcore/sources/kinc/graphics4/texture.h @@ -0,0 +1,107 @@ +#pragma once + +#include + +#include +#include + +/*! \file texture.h + \brief Provides functions for handling textures. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef kinc_image_t kinc_g4_image_t; + +typedef struct kinc_g4_texture { + int tex_width; + int tex_height; + int tex_depth; + kinc_image_format_t format; + kinc_image_t *image; + kinc_g4_texture_impl_t impl; +} kinc_g4_texture_t; + +/// +/// Allocates and initializes a texture without copying any data into it. +/// +/// The texture to initialize +/// The width of the texture to create +/// The height of the texture to create +/// The format of the texture to create +void kinc_g4_texture_init(kinc_g4_texture_t *texture, int width, int height, kinc_image_format_t format); + +/// +/// Allocates and initializes a 3d-texture without copying any data into it. +/// +/// The texture to initialize +/// The width of the texture to create +/// The height of the texture to create +/// The depth of the texture to create +/// The format of the texture to create +void kinc_g4_texture_init3d(kinc_g4_texture_t *texture, int width, int height, int depth, kinc_image_format_t format); + +/// +/// Allocates and initializes a texture and copies image-data into it. +/// +/// The texture to initialize +/// The image which's data is copied into the texture +void kinc_g4_texture_init_from_image(kinc_g4_texture_t *texture, kinc_image_t *image); + +/// +/// Allocates and initializes a texture and copies image-data into it. +/// +/// The texture to initialize +/// The image which's data is copied into the texture +void kinc_g4_texture_init_from_image3d(kinc_g4_texture_t *texture, kinc_image_t *image); + +/// +/// Deallocates and destroys a texture. +/// +/// The texture to destroy +void kinc_g4_texture_destroy(kinc_g4_texture_t *texture); + +unsigned char *kinc_g4_texture_lock(kinc_g4_texture_t *texture); + +void kinc_g4_texture_unlock(kinc_g4_texture_t *texture); + +/// +/// Clears parts of a texture to a color. +/// +void kinc_g4_texture_clear(kinc_g4_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color); + +/// +/// Generates the mipmap-chain for a texture. +/// +/// The render-target to create the mipmaps for +/// The number of mipmap-levels to generate +void kinc_g4_texture_generate_mipmaps(kinc_g4_texture_t *texture, int levels); + +/// +/// Sets the mipmap for one level of a texture. +/// +/// The texture to set a mipmap-level for +/// The image-data for the mipmap-level to set +/// The mipmap-level to set +void kinc_g4_texture_set_mipmap(kinc_g4_texture_t *texture, kinc_image_t *mipmap, int level); + +/// +/// Returns the stride of the first mipmap-layer of the texture in bytes. +/// +/// The texture to figure out the stride for +/// The stride of the first mipmap-layer in bytes +int kinc_g4_texture_stride(kinc_g4_texture_t *texture); + +#ifdef KINC_ANDROID +void kinc_g4_texture_init_from_id(kinc_g4_texture_t *texture, unsigned texid); +#endif + +#if defined(KINC_IOS) || defined(KINC_MACOS) +void kinc_g4_texture_upload(kinc_g4_texture_t *texture, uint8_t *data, int stride); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics4/textureunit.h b/armorcore/sources/kinc/graphics4/textureunit.h new file mode 100644 index 000000000..9d681599b --- /dev/null +++ b/armorcore/sources/kinc/graphics4/textureunit.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include + +/*! \file textureunit.h + \brief Provides a texture-unit-struct which is used for setting textures in a shader. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_g4_texture_unit { + int stages[KINC_G4_SHADER_TYPE_COUNT]; +} kinc_g4_texture_unit_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics4/usage.h b/armorcore/sources/kinc/graphics4/usage.h new file mode 100644 index 000000000..1cbf7f2f4 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/usage.h @@ -0,0 +1,19 @@ +#pragma once + +/*! \file usage.h + \brief Provides the usage enum. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kinc_g4_usage { + KINC_G4_USAGE_STATIC, + KINC_G4_USAGE_DYNAMIC, + KINC_G4_USAGE_READABLE +} kinc_g4_usage_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics4/vertexbuffer.c.h b/armorcore/sources/kinc/graphics4/vertexbuffer.c.h new file mode 100644 index 000000000..85cda64b1 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/vertexbuffer.c.h @@ -0,0 +1,20 @@ +#include "vertexbuffer.h" + +static void init_vertex_element(kinc_g4_vertex_element_t *element, const char *name, kinc_g4_vertex_data_t data) { + element->name = name; + element->data = data; +} + +void kinc_g4_vertex_structure_init(kinc_g4_vertex_structure_t *structure) { + structure->size = 0; + structure->instanced = false; +} + +void kinc_g4_vertex_structure_add(kinc_g4_vertex_structure_t *structure, const char *name, kinc_g4_vertex_data_t data) { + init_vertex_element(&structure->elements[structure->size++], name, data); +} + +void kinc_g4_set_vertex_buffer(kinc_g4_vertex_buffer_t *buffer) { + kinc_g4_vertex_buffer_t *buffers[1] = {buffer}; + kinc_g4_set_vertex_buffers(buffers, 1); +} diff --git a/armorcore/sources/kinc/graphics4/vertexbuffer.h b/armorcore/sources/kinc/graphics4/vertexbuffer.h new file mode 100644 index 000000000..7045b8209 --- /dev/null +++ b/armorcore/sources/kinc/graphics4/vertexbuffer.h @@ -0,0 +1,101 @@ +#pragma once + +#include + +#include "usage.h" +#include "vertexstructure.h" + +#include + +#include + +/*! \file vertexbuffer.h + \brief Provides functions for setting up and using vertex-buffers. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_g4_vertex_buffer { + kinc_g4_vertex_buffer_impl_t impl; +} kinc_g4_vertex_buffer_t; + +/// +/// Allocate and initialize a vertex-buffer. +/// +/// The buffer to initialize +/// The number of vertices in the buffer +/// The structure of the buffer +/// A hint for how the buffer will be used +/// The step-rate for instanced-rendering - use 0 if instanced-rendering will not be used with this buffer +void kinc_g4_vertex_buffer_init(kinc_g4_vertex_buffer_t *buffer, int count, kinc_g4_vertex_structure_t *structure, kinc_g4_usage_t usage, + int instance_data_step_rate); + +/// +/// Destroys a vertex-buffer. +/// +/// The buffer to destroy +void kinc_g4_vertex_buffer_destroy(kinc_g4_vertex_buffer_t *buffer); + +/// +/// Locks all of a vertex-buffer to modify its contents. +/// +/// The buffer to lock +/// The contents of the buffer +float *kinc_g4_vertex_buffer_lock_all(kinc_g4_vertex_buffer_t *buffer); + +/// +/// Locks part of a vertex-buffer to modify its contents. +/// +/// The buffer to lock +/// The index of the first vertex to lock +/// The number of vertices to lock +/// The contents of the buffer, starting at start +float *kinc_g4_vertex_buffer_lock(kinc_g4_vertex_buffer_t *buffer, int start, int count); + +/// +/// Unlock all of a vertex-buffer so the changed contents can be used. +/// +/// The buffer to unlock +void kinc_g4_vertex_buffer_unlock_all(kinc_g4_vertex_buffer_t *buffer); + +/// +/// Unlocks part of a vertex-buffer so the changed contents can be used. +/// +/// The buffer to unlock +/// The number of vertices to unlock, starting from the start-vertex from the previous lock-call +void kinc_g4_vertex_buffer_unlock(kinc_g4_vertex_buffer_t *buffer, int count); + +/// +/// Returns the number of vertices in a buffer. +/// +/// The buffer to figure out the number of vertices for +/// The number of vertices +int kinc_g4_vertex_buffer_count(kinc_g4_vertex_buffer_t *buffer); + +/// +/// Returns the stride aka the size of one vertex of the buffer in bytes. +/// +/// The buffer to figure out the stride for +/// The stride of the buffer in bytes +int kinc_g4_vertex_buffer_stride(kinc_g4_vertex_buffer_t *buffer); + +int kinc_internal_g4_vertex_buffer_set(kinc_g4_vertex_buffer_t *buffer, int offset); + +/// +/// Sets vertex-buffers for the next draw-call. +/// +/// The buffers to set +/// The number of buffers to set +void kinc_g4_set_vertex_buffers(kinc_g4_vertex_buffer_t **buffers, int count); + +/// +/// Sets a vertex-buffer for the next draw-call. +/// +/// The buffer to set +void kinc_g4_set_vertex_buffer(kinc_g4_vertex_buffer_t *buffer); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics4/vertexstructure.h b/armorcore/sources/kinc/graphics4/vertexstructure.h new file mode 100644 index 000000000..fa605abaa --- /dev/null +++ b/armorcore/sources/kinc/graphics4/vertexstructure.h @@ -0,0 +1,157 @@ +#pragma once + +#include + +#include + +/*! \file vertexstructure.h + \brief Provides functions for setting up the structure of vertices in a vertex-buffer. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kinc_g4_vertex_data { + KINC_G4_VERTEX_DATA_NONE = 0, + KINC_G4_VERTEX_DATA_F32_1X = 1, + KINC_G4_VERTEX_DATA_F32_2X = 2, + KINC_G4_VERTEX_DATA_F32_3X = 3, + KINC_G4_VERTEX_DATA_F32_4X = 4, + KINC_G4_VERTEX_DATA_F32_4X4 = 5, + KINC_G4_VERTEX_DATA_I8_1X = 6, + KINC_G4_VERTEX_DATA_U8_1X = 7, + KINC_G4_VERTEX_DATA_I8_1X_NORMALIZED = 8, + KINC_G4_VERTEX_DATA_U8_1X_NORMALIZED = 9, + KINC_G4_VERTEX_DATA_I8_2X = 10, + KINC_G4_VERTEX_DATA_U8_2X = 11, + KINC_G4_VERTEX_DATA_I8_2X_NORMALIZED = 12, + KINC_G4_VERTEX_DATA_U8_2X_NORMALIZED = 13, + KINC_G4_VERTEX_DATA_I8_4X = 14, + KINC_G4_VERTEX_DATA_U8_4X = 15, + KINC_G4_VERTEX_DATA_I8_4X_NORMALIZED = 16, + KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED = 17, + KINC_G4_VERTEX_DATA_I16_1X = 18, + KINC_G4_VERTEX_DATA_U16_1X = 19, + KINC_G4_VERTEX_DATA_I16_1X_NORMALIZED = 20, + KINC_G4_VERTEX_DATA_U16_1X_NORMALIZED = 21, + KINC_G4_VERTEX_DATA_I16_2X = 22, + KINC_G4_VERTEX_DATA_U16_2X = 23, + KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED = 24, + KINC_G4_VERTEX_DATA_U16_2X_NORMALIZED = 25, + KINC_G4_VERTEX_DATA_I16_4X = 26, + KINC_G4_VERTEX_DATA_U16_4X = 27, + KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED = 28, + KINC_G4_VERTEX_DATA_U16_4X_NORMALIZED = 29, + KINC_G4_VERTEX_DATA_I32_1X = 30, + KINC_G4_VERTEX_DATA_U32_1X = 31, + KINC_G4_VERTEX_DATA_I32_2X = 32, + KINC_G4_VERTEX_DATA_U32_2X = 33, + KINC_G4_VERTEX_DATA_I32_3X = 34, + KINC_G4_VERTEX_DATA_U32_3X = 35, + KINC_G4_VERTEX_DATA_I32_4X = 36, + KINC_G4_VERTEX_DATA_U32_4X = 37, + + // deprecated + KINC_G4_VERTEX_DATA_FLOAT1 = KINC_G4_VERTEX_DATA_F32_1X, + KINC_G4_VERTEX_DATA_FLOAT2 = KINC_G4_VERTEX_DATA_F32_2X, + KINC_G4_VERTEX_DATA_FLOAT3 = KINC_G4_VERTEX_DATA_F32_3X, + KINC_G4_VERTEX_DATA_FLOAT4 = KINC_G4_VERTEX_DATA_F32_4X, + KINC_G4_VERTEX_DATA_FLOAT4X4 = KINC_G4_VERTEX_DATA_F32_4X4, + KINC_G4_VERTEX_DATA_SHORT2_NORM = KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED, + KINC_G4_VERTEX_DATA_SHORT4_NORM = KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED, + KINC_G4_VERTEX_DATA_COLOR = KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED +} kinc_g4_vertex_data_t; + +static inline int kinc_g4_vertex_data_size(kinc_g4_vertex_data_t data) { + switch (data) { + default: + case KINC_G4_VERTEX_DATA_NONE: + return 0; + case KINC_G4_VERTEX_DATA_F32_1X: + return 1 * 4; + case KINC_G4_VERTEX_DATA_F32_2X: + return 2 * 4; + case KINC_G4_VERTEX_DATA_F32_3X: + return 3 * 4; + case KINC_G4_VERTEX_DATA_F32_4X: + return 4 * 4; + case KINC_G4_VERTEX_DATA_F32_4X4: + return 4 * 4 * 4; + case KINC_G4_VERTEX_DATA_I8_1X: + case KINC_G4_VERTEX_DATA_U8_1X: + case KINC_G4_VERTEX_DATA_I8_1X_NORMALIZED: + case KINC_G4_VERTEX_DATA_U8_1X_NORMALIZED: + return 1 * 1; + case KINC_G4_VERTEX_DATA_I8_2X: + case KINC_G4_VERTEX_DATA_U8_2X: + case KINC_G4_VERTEX_DATA_I8_2X_NORMALIZED: + case KINC_G4_VERTEX_DATA_U8_2X_NORMALIZED: + return 2 * 1; + case KINC_G4_VERTEX_DATA_I8_4X: + case KINC_G4_VERTEX_DATA_U8_4X: + case KINC_G4_VERTEX_DATA_I8_4X_NORMALIZED: + case KINC_G4_VERTEX_DATA_U8_4X_NORMALIZED: + return 4 * 1; + case KINC_G4_VERTEX_DATA_I16_1X: + case KINC_G4_VERTEX_DATA_U16_1X: + case KINC_G4_VERTEX_DATA_I16_1X_NORMALIZED: + case KINC_G4_VERTEX_DATA_U16_1X_NORMALIZED: + return 1 * 2; + case KINC_G4_VERTEX_DATA_I16_2X: + case KINC_G4_VERTEX_DATA_U16_2X: + case KINC_G4_VERTEX_DATA_I16_2X_NORMALIZED: + case KINC_G4_VERTEX_DATA_U16_2X_NORMALIZED: + return 2 * 2; + case KINC_G4_VERTEX_DATA_I16_4X: + case KINC_G4_VERTEX_DATA_U16_4X: + case KINC_G4_VERTEX_DATA_I16_4X_NORMALIZED: + case KINC_G4_VERTEX_DATA_U16_4X_NORMALIZED: + return 4 * 2; + case KINC_G4_VERTEX_DATA_I32_1X: + case KINC_G4_VERTEX_DATA_U32_1X: + return 1 * 4; + case KINC_G4_VERTEX_DATA_I32_2X: + case KINC_G4_VERTEX_DATA_U32_2X: + return 2 * 4; + case KINC_G4_VERTEX_DATA_I32_3X: + case KINC_G4_VERTEX_DATA_U32_3X: + return 3 * 4; + case KINC_G4_VERTEX_DATA_I32_4X: + case KINC_G4_VERTEX_DATA_U32_4X: + return 4 * 4; + } +} + +typedef struct kinc_g4_vertex_element { + const char *name; + kinc_g4_vertex_data_t data; +} kinc_g4_vertex_element_t; + +#define KINC_G4_MAX_VERTEX_ELEMENTS 16 + +typedef struct kinc_g4_vertex_structure { + kinc_g4_vertex_element_t elements[KINC_G4_MAX_VERTEX_ELEMENTS]; + int size; + bool instanced; +} kinc_g4_vertex_structure_t; + +/// +/// Initializes a vertex-structure. +/// +/// The structure to initialize +/// +void kinc_g4_vertex_structure_init(kinc_g4_vertex_structure_t *structure); + +/// +/// Adds an element to a vertex-structure. +/// +/// The structure to add an element to +/// The name to use for the new element +/// The type of data to assign for the new element +/// +void kinc_g4_vertex_structure_add(kinc_g4_vertex_structure_t *structure, const char *name, kinc_g4_vertex_data_t data); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/commandlist.c.h b/armorcore/sources/kinc/graphics5/commandlist.c.h new file mode 100644 index 000000000..caa60053b --- /dev/null +++ b/armorcore/sources/kinc/graphics5/commandlist.c.h @@ -0,0 +1 @@ +#include "commandlist.h" diff --git a/armorcore/sources/kinc/graphics5/commandlist.h b/armorcore/sources/kinc/graphics5/commandlist.h new file mode 100644 index 000000000..24ad0837c --- /dev/null +++ b/armorcore/sources/kinc/graphics5/commandlist.h @@ -0,0 +1,333 @@ +#pragma once + +#include + +#include "rendertarget.h" +#include "sampler.h" +#include "texture.h" +#include "textureunit.h" + +#include + +#include + +/*! \file commandlist.h + \brief Contains functions for building command-lists to send commands to the GPU. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#define KINC_G5_CLEAR_COLOR 1 +#define KINC_G5_CLEAR_DEPTH 2 +#define KINC_G5_CLEAR_STENCIL 4 + +struct kinc_g5_compute_shader; +struct kinc_g5_constant_buffer; +struct kinc_g5_index_buffer; +struct kinc_g5_pipeline; +struct kinc_g5_render_target; +struct kinc_g5_texture; +struct kinc_g5_vertex_buffer; +struct kinc_g5_render_target; + +/*typedef enum kinc_g5_render_target_format { + KINC_G5_RENDER_TARGET_FORMAT_32BIT, + KINC_G5_RENDER_TARGET_FORMAT_64BIT_FLOAT, + KINC_G5_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT, + KINC_G5_RENDER_TARGET_FORMAT_128BIT_FLOAT, + KINC_G5_RENDER_TARGET_FORMAT_16BIT_DEPTH, + KINC_G5_RENDER_TARGET_FORMAT_8BIT_RED +} kinc_g5_render_target_format_t;*/ +// typedef kinc_g4_render_target_format_t kinc_g5_render_target_format_t; + +typedef struct kinc_g5_command_list { + CommandList5Impl impl; +} kinc_g5_command_list_t; + +/// +/// Initializes a command-list. +/// +/// The command-list to initialize +void kinc_g5_command_list_init(kinc_g5_command_list_t *list); + +/// +/// Destroys a command-list. +/// +/// The command-list to destroy +void kinc_g5_command_list_destroy(kinc_g5_command_list_t *list); + +/// +/// Starts recording commands in a command-list. +/// +/// The list to use +void kinc_g5_command_list_begin(kinc_g5_command_list_t *list); + +/// +/// Ends recording commands for the list. Has to be called after kinc_g5_command_list_begin and before kinc_g5_command_list_execute. +/// +/// +/// +void kinc_g5_command_list_end(kinc_g5_command_list_t *list); + +/// +/// Records a command to clear the color, depth and/or stencil-components of a render-target. +/// +/// The list to write the command to +/// The render-target to clear +/// Defines what components to clear +/// The color-value to clear to in 0xAARRGGBB +/// The depth-value to clear to +/// The stencil-value to clear to +void kinc_g5_command_list_clear(kinc_g5_command_list_t *list, struct kinc_g5_render_target *render_target, unsigned flags, unsigned color, + float depth, int stencil); + +/// +/// Records a command that prepares a render-target to be used as the current framebuffer. +/// +/// The list to write the command to +/// The render-target to use as the current framebuffer +void kinc_g5_command_list_render_target_to_framebuffer_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget); + +/// +/// Records a command that prepares a render-target for regular render-target-usage after being used as the current framebuffer. +/// +/// The list to write the command to +/// The render-target to use in regular render-target-mode +void kinc_g5_command_list_framebuffer_to_render_target_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget); + +/// +/// Writes a command that prepares a render-target to be rendered to. +/// +/// The list to write the command to +/// The render-target to render to +void kinc_g5_command_list_texture_to_render_target_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget); + +/// +/// Writes a command that prepares a render-target to be used for sampling/reading like a texture. +/// +/// The list to write the command to +/// The render-target to be used like a texture +void kinc_g5_command_list_render_target_to_texture_barrier(kinc_g5_command_list_t *list, struct kinc_g5_render_target *renderTarget); + +/// +/// Writes a command that draws the entire content of the currently set index-buffer and vertex-buffer. G4 can only draw triangle-lists using vertex-indices as +/// this is what GPUs tend to be optimized for. +/// +/// The list to write the command to +void kinc_g5_command_list_draw_indexed_vertices(kinc_g5_command_list_t *list); + +/// +/// Writes a command that draws a part of the content of the currently set index-buffer and vertex-buffer. G4 can only draw triangle-lists using vertex-indices +/// as this is what GPUs tend to be optimized for. +/// +/// The list to write the command to +/// The offset into the index-buffer +/// The number of indices to use +void kinc_g5_command_list_draw_indexed_vertices_from_to(kinc_g5_command_list_t *list, int start, int count); + +/// +/// Writes a command that draws a part of the content of the currently set index-buffer and vertex-buffer and additionally applies a general offset into the +/// vertex-buffer. G4 can only draw triangle-lists using vertex-indices as this is what GPUs tend to be optimized for. +/// +/// The list to write the command to +/// The offset into the index-buffer +/// The number of indices to use +/// The offset into the vertex-buffer which is added to each index read from the index-buffer +void kinc_g5_command_list_draw_indexed_vertices_from_to_from(kinc_g5_command_list_t *list, int start, int count, int vertex_offset); + +void kinc_g5_command_list_draw_indexed_vertices_instanced(kinc_g5_command_list_t *list, int instanceCount); +void kinc_g5_command_list_draw_indexed_vertices_instanced_from_to(kinc_g5_command_list_t *list, int instanceCount, int start, int count); + +/// +/// Writes a command that sets the viewport which defines the portion of the framebuffer or render-target things are rendered into. By default the viewport is +/// equivalent to the full size of the current render-target or framebuffer. +/// +/// The list to write the command to +/// The x-offset of the viewport from the left of the screen in pixels +/// The y-offset of the viewport from the top of the screen in pixels +/// The width of the viewport in pixels +/// The height of the viewport in pixels +void kinc_g5_command_list_viewport(kinc_g5_command_list_t *list, int x, int y, int width, int height); + +/// +/// Writes a command that enables and defines the scissor-rect. When the scissor-rect is enabled, anything that's rendered outside of the scissor-rect will be +/// ignored. +/// +/// The list to write the command to +/// The x-offset of the scissor-rect from the left of the screen in pixels +/// The y-offset of the scissor-rect from the top of the screen in pixels +/// The width of the scissor-rect in pixels +/// The height of the scissor-rect in pixels +void kinc_g5_command_list_scissor(kinc_g5_command_list_t *list, int x, int y, int width, int height); + +/// +/// Writes a command to disable the scissor-rect. +/// +/// The list to write the command to +void kinc_g5_command_list_disable_scissor(kinc_g5_command_list_t *list); + +/// +/// Writes a command to set the pipeline for the next draw-call. The pipeline defines most rendering-state including the shaders to be used. +/// +/// The list to write the command to +/// The pipeline to set +void kinc_g5_command_list_set_pipeline(kinc_g5_command_list_t *list, struct kinc_g5_pipeline *pipeline); + +/// +/// Writes a command to set the compute shader for the next compute-call. +/// +/// The list to write the command to +/// The compute shader to set +void kinc_g5_command_list_set_compute_shader(kinc_g5_command_list_t *list, struct kinc_g5_compute_shader *shader); + +/// +/// Sets the blend constant used for `KINC_G5_BLEND_CONSTANT` or `KINC_G5_INV_BLEND_CONSTANT` +/// +void kinc_g5_command_list_set_blend_constant(kinc_g5_command_list_t *list, float r, float g, float b, float a); + +/// +/// Writes a command which sets vertex-buffers for the next draw-call. +/// +/// The list to write the command to +/// The buffers to set +/// The offset to use for every buffer in number of vertices +/// The number of buffers to set +void kinc_g5_command_list_set_vertex_buffers(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer **buffers, int *offsets, int count); + +/// +/// Writes a command to set an index-buffer to be used for the next draw-command. +/// +/// The list to write the command to +/// The buffer to use +void kinc_g5_command_list_set_index_buffer(kinc_g5_command_list_t *list, struct kinc_g5_index_buffer *buffer); + +/// +/// Writes a command that sets the render-targets to draw into in following draw-calls. +/// +/// The list to write the command to +/// The render-targets to use for following-draw calls +/// The number of render-targets to use +void kinc_g5_command_list_set_render_targets(kinc_g5_command_list_t *list, struct kinc_g5_render_target **targets, int count); + +/// +/// Writes a command to upload an index-buffer that's in main-memory to gpu-memory. Does nothing on unified-memory-systems. +/// +/// The list to write the command to +/// The buffer to upload +void kinc_g5_command_list_upload_index_buffer(kinc_g5_command_list_t *list, struct kinc_g5_index_buffer *buffer); + +/// +/// Writes a command to upload a vertex-buffer that's in main-memory to gpu-memory. Does nothing on unified-memory-systems. +/// +/// The list to write the command to +/// The buffer to upload +void kinc_g5_command_list_upload_vertex_buffer(kinc_g5_command_list_t *list, struct kinc_g5_vertex_buffer *buffer); + +/// +/// Writes a command to upload a texture that's in main-memory to gpu-memory. Does nothing on unified-memory-systems. +/// +/// The list to write the command to +/// The texture to upload +void kinc_g5_command_list_upload_texture(kinc_g5_command_list_t *list, struct kinc_g5_texture *texture); + +/// +/// Writes a command that sets a constant-buffer for the vertex-shader-stage. +/// +/// The list to write the command to +/// The buffer to set +/// The offset into the buffer in bytes to use as the start +/// The size of the buffer to use in bytes starting at the offset +void kinc_g5_command_list_set_vertex_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size); + +/// +/// Writes a command that sets a constant-buffer for the fragment-shader-stage. +/// +/// The list to write the command to +/// The buffer to set +/// The offset into the buffer in bytes to use as the start +/// The size of the buffer to use in bytes starting at the offset +void kinc_g5_command_list_set_fragment_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size); + +/// +/// Writes a command that sets a constant-buffer for the compute-shader-stage. +/// +/// The list to write the command to +/// The buffer to set +/// The offset into the buffer in bytes to use as the start +/// The size of the buffer to use in bytes starting at the offset +void kinc_g5_command_list_set_compute_constant_buffer(kinc_g5_command_list_t *list, struct kinc_g5_constant_buffer *buffer, int offset, size_t size); + +/// +/// Kicks off execution of the commands which have been recorded in the command-list. kinc_g5_command_list_end has to be called beforehand. +/// +/// The command-list to execute +void kinc_g5_command_list_execute(kinc_g5_command_list_t *list); + +/// +/// Waits for execution of the command_list to finish. Make sure the command-list is executing before you wait for it. +/// Also take note that waiting for a command-list to finish executing completely is a very expensive operation. +/// +/// The command-list to execute +void kinc_g5_command_list_wait_for_execution_to_finish(kinc_g5_command_list_t *list); + +/// +/// Writes a command that copies the contents of a render-target into a cpu-side buffer. Beware: This is enormously slow. +/// +/// The list to write the command to +/// The render-target to copy the data from +/// The buffer to copy the data into +void kinc_g5_command_list_get_render_target_pixels(kinc_g5_command_list_t *list, struct kinc_g5_render_target *render_target, uint8_t *data); + +/// +/// Records a command that fires off a compute-run on x * y * z elements. +/// +/// The list to write the command to +/// The x-size for the compute-run +/// The y-size for the compute-run +/// The z-size for the compute-run +void kinc_g5_command_list_compute(kinc_g5_command_list_t *list, int x, int y, int z); + +/// +/// Assigns a texture to a texture-unit for sampled access via GLSL's texture. +/// +/// The list to write the command to +/// The unit to assign this texture to +/// The texture to assign to the unit +void kinc_g5_command_list_set_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture); + +/// +/// Assigns a texture to a texture-unit for direct access via GLSL's texelFetch (as +/// opposed to GLSL's texture). The name of this functions is unfortunately based +/// on OpenGL's confusing terminology. +/// +/// The list to write the command to +/// The unit to assign this texture to +/// The texture to assign to the unit +void kinc_g5_command_list_set_image_texture(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_texture_t *texture); + +/// +/// Uses the color-component of a render-target as a texture. +/// +/// The list to write the command to +/// The texture-unit to assign the render-target to +/// The render-target to use +void kinc_g5_command_list_set_texture_from_render_target(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_render_target_t *target); + +/// +/// Uses the depth-component of a render-target as a texture. +/// +/// The list to write the command to +/// The texture-unit to assign the render-target to +/// The render-target to use +void kinc_g5_command_list_set_texture_from_render_target_depth(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, + kinc_g5_render_target_t *target); + +void kinc_g5_command_list_set_render_target_face(kinc_g5_command_list_t *list, kinc_g5_render_target_t *texture, int face); + +void kinc_g5_command_list_set_sampler(kinc_g5_command_list_t *list, kinc_g5_texture_unit_t unit, kinc_g5_sampler_t *sampler); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/compute.h b/armorcore/sources/kinc/graphics5/compute.h new file mode 100644 index 000000000..a064aeef7 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/compute.h @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include + +#include +#include + +/*! \file compute.h + \brief Provides support for running compute-shaders. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_g5_compute_shader { + kinc_g5_compute_shader_impl impl; +} kinc_g5_compute_shader; + +/// +/// Initialize a compute-shader from system-specific shader-data. +/// +/// The shader-object to initialize +/// A pointer to system-specific shader-data +/// Length of the shader-data in bytes +void kinc_g5_compute_shader_init(kinc_g5_compute_shader *shader, void *source, int length); + +/// +/// Desotry a shader-object +/// +/// The shader-object to destroy +void kinc_g5_compute_shader_destroy(kinc_g5_compute_shader *shader); + +/// +/// Finds the location of a constant/uniform inside of a shader. +/// +/// The shader to look into +/// The constant/uniform-name to look for +/// The found constant-location +kinc_g5_constant_location_t kinc_g5_compute_shader_get_constant_location(kinc_g5_compute_shader *shader, const char *name); + +/// +/// Finds a texture-unit inside of a shader. +/// +/// The shader to look into +/// The texture-name to look for +/// The found texture-unit +kinc_g5_texture_unit_t kinc_g5_compute_shader_get_texture_unit(kinc_g5_compute_shader *shader, const char *name); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/constantbuffer.c.h b/armorcore/sources/kinc/graphics5/constantbuffer.c.h new file mode 100644 index 000000000..1427782b4 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/constantbuffer.c.h @@ -0,0 +1,112 @@ +#include "constantbuffer.h" + +void kinc_g5_constant_buffer_set_int(kinc_g5_constant_buffer_t *buffer, int offset, int value) { + int *ints = (int *)(&buffer->data[offset]); + ints[0] = value; +} + +void kinc_g5_constant_buffer_set_int2(kinc_g5_constant_buffer_t *buffer, int offset, int value1, int value2) { + int *ints = (int *)(&buffer->data[offset]); + ints[0] = value1; + ints[1] = value2; +} + +void kinc_g5_constant_buffer_set_int3(kinc_g5_constant_buffer_t *buffer, int offset, int value1, int value2, int value3) { + int *ints = (int *)(&buffer->data[offset]); + ints[0] = value1; + ints[1] = value2; + ints[2] = value3; +} + +void kinc_g5_constant_buffer_set_int4(kinc_g5_constant_buffer_t *buffer, int offset, int value1, int value2, int value3, int value4) { + int *ints = (int *)(&buffer->data[offset]); + ints[0] = value1; + ints[1] = value2; + ints[2] = value3; + ints[3] = value4; +} + +void kinc_g5_constant_buffer_set_ints(kinc_g5_constant_buffer_t *buffer, int offset, int *values, int count) { + int *ints = (int *)(&buffer->data[offset]); + for (int i = 0; i < count; ++i) { + ints[i] = values[i]; + } +} + +void kinc_g5_constant_buffer_set_float(kinc_g5_constant_buffer_t *buffer, int offset, float value) { + float *floats = (float *)(&buffer->data[offset]); + floats[0] = value; +} + +void kinc_g5_constant_buffer_set_float2(kinc_g5_constant_buffer_t *buffer, int offset, float value1, float value2) { + float *floats = (float *)(&buffer->data[offset]); + floats[0] = value1; + floats[1] = value2; +} + +void kinc_g5_constant_buffer_set_float3(kinc_g5_constant_buffer_t *buffer, int offset, float value1, float value2, float value3) { + float *floats = (float *)(&buffer->data[offset]); + floats[0] = value1; + floats[1] = value2; + floats[2] = value3; +} + +void kinc_g5_constant_buffer_set_float4(kinc_g5_constant_buffer_t *buffer, int offset, float value1, float value2, float value3, float value4) { + float *floats = (float *)(&buffer->data[offset]); + floats[0] = value1; + floats[1] = value2; + floats[2] = value3; + floats[3] = value4; +} + +void kinc_g5_constant_buffer_set_floats(kinc_g5_constant_buffer_t *buffer, int offset, float *values, int count) { + float *floats = (float *)(&buffer->data[offset]); + for (int i = 0; i < count; ++i) { + floats[i] = values[i]; + } +} + +void kinc_g5_constant_buffer_set_bool(kinc_g5_constant_buffer_t *buffer, int offset, bool value) { + int *ints = (int *)(&buffer->data[offset]); + ints[0] = value ? 1 : 0; +} + +static void set_matrix3(uint8_t *constants, int offset, kinc_matrix3x3_t *value) { + float *floats = (float *)(&constants[offset]); + for (int y = 0; y < 3; ++y) { + for (int x = 0; x < 3; ++x) { + floats[x + y * 4] = kinc_matrix3x3_get(value, x, y); + } + } +} + +void kinc_g5_constant_buffer_set_matrix3(kinc_g5_constant_buffer_t *buffer, int offset, kinc_matrix3x3_t *value) { + if (kinc_g5_transposeMat3) { + kinc_matrix3x3_t m = *value; + kinc_matrix3x3_transpose(&m); + set_matrix3(buffer->data, offset, &m); + } + else { + set_matrix3(buffer->data, offset, value); + } +} + +static void set_matrix4(uint8_t *constants, int offset, kinc_matrix4x4_t *value) { + float *floats = (float *)(&constants[offset]); + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 4; ++x) { + floats[x + y * 4] = kinc_matrix4x4_get(value, x, y); + } + } +} + +void kinc_g5_constant_buffer_set_matrix4(kinc_g5_constant_buffer_t *buffer, int offset, kinc_matrix4x4_t *value) { + if (kinc_g5_transposeMat4) { + kinc_matrix4x4_t m = *value; + kinc_matrix4x4_transpose(&m); + set_matrix4(buffer->data, offset, &m); + } + else { + set_matrix4(buffer->data, offset, value); + } +} diff --git a/armorcore/sources/kinc/graphics5/constantbuffer.h b/armorcore/sources/kinc/graphics5/constantbuffer.h new file mode 100644 index 000000000..23f788d02 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/constantbuffer.h @@ -0,0 +1,175 @@ +#pragma once + +#include + +#include + +#include +#include + +/*! \file constantbuffer.h + \brief Provides support for managing buffers of constant-data for shaders. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_g5_constant_buffer { + uint8_t *data; + ConstantBuffer5Impl impl; +} kinc_g5_constant_buffer_t; + +/// +/// Initializes a constant-buffer. +/// +/// The buffer to initialize +/// The size of the constant-data in the buffer in bytes +void kinc_g5_constant_buffer_init(kinc_g5_constant_buffer_t *buffer, int size); + +/// +/// Destroys a buffer. +/// +/// The buffer to destroy +void kinc_g5_constant_buffer_destroy(kinc_g5_constant_buffer_t *buffer); + +/// +/// Locks all of a constant-buffer to modify its contents. +/// +/// The buffer to lock +/// The contents of the buffer +void kinc_g5_constant_buffer_lock_all(kinc_g5_constant_buffer_t *buffer); + +/// +/// Locks part of a constant-buffer to modify its contents. +/// +/// The buffer to lock +/// The offset of where to start the lock in bytes +/// The number of bytes to lock +/// The contents of the buffer, starting at start +void kinc_g5_constant_buffer_lock(kinc_g5_constant_buffer_t *buffer, int start, int count); + +/// +/// Unlocks a constant-buffer so the changed contents can be used. +/// +/// The buffer to unlock +void kinc_g5_constant_buffer_unlock(kinc_g5_constant_buffer_t *buffer); + +/// +/// Figures out the size of the constant-data in the buffer. +/// +/// The buffer to figure out the size for +/// Returns the size of the constant-data in the buffer in bytes +int kinc_g5_constant_buffer_size(kinc_g5_constant_buffer_t *buffer); + +/// +/// Assigns a bool at an offset in a constant-buffer. +/// +/// The offset at which to write the data +/// The value to write into the buffer +void kinc_g5_constant_buffer_set_bool(kinc_g5_constant_buffer_t *buffer, int offset, bool value); + +/// +/// Assigns an integer at an offset in a constant-buffer. +/// +/// The offset at which to write the data +/// The value to assign to the constant/uniform +void kinc_g5_constant_buffer_set_int(kinc_g5_constant_buffer_t *buffer, int offset, int value); + +/// +/// Assigns two integers at an offset in a constant-buffer. +/// +/// The offset at which to write the data +/// The first value to write into the buffer +/// The second value to write into the buffer +void kinc_g5_constant_buffer_set_int2(kinc_g5_constant_buffer_t *buffer, int offset, int value1, int value2); + +/// +/// Assigns three integers at an offset in a constant-buffer. +/// +/// The offset at which to write the data +/// The first value to write into the buffer +/// The second value to write into the buffer +/// The third value to write into the buffer/param> +void kinc_g5_constant_buffer_set_int3(kinc_g5_constant_buffer_t *buffer, int offset, int value1, int value2, int value3); + +/// +/// Assigns four integers at an offset in a constant-buffer. +/// +/// The offset at which to write the data +/// The first value to write into the buffer +/// The second value to write into the buffer +/// The third value to write into the buffer/param> +/// The fourth value to write into the buffer +void kinc_g5_constant_buffer_set_int4(kinc_g5_constant_buffer_t *buffer, int offset, int value1, int value2, int value3, int value4); + +/// +/// Assigns a bunch of integers at an offset in a constant-buffer. +/// +/// The location of the constant/uniform to assign the values to +/// The values to write into the buffer +/// The number of values to write into the buffer +void kinc_g5_constant_buffer_set_ints(kinc_g5_constant_buffer_t *buffer, int offset, int *values, int count); + +/// +/// Assigns a float at an offset in a constant-buffer. +/// +/// The offset at which to write the data +/// The value to write into the buffer +void kinc_g5_constant_buffer_set_float(kinc_g5_constant_buffer_t *buffer, int offset, float value); + +/// +/// Assigns two floats at an offset in a constant-buffer. +/// +/// The offset at which to write the data +/// The first value to write into the buffer +/// The second value to write into the buffer +void kinc_g5_constant_buffer_set_float2(kinc_g5_constant_buffer_t *buffer, int offset, float value1, float value2); + +/// +/// Assigns three floats at an offset in a constant-buffer. +/// +/// The offset at which to write the data +/// The first value to write into the buffer +/// The second value to write into the buffer +/// The third value to write into the buffer/param> +void kinc_g5_constant_buffer_set_float3(kinc_g5_constant_buffer_t *buffer, int offset, float value1, float value2, float value3); + +/// +/// Assigns four floats at an offset in a constant-buffer. +/// +/// The offset at which to write the data +/// The first value to write into the buffer +/// The second value to write into the buffer +/// The third value to write into the buffer/param> +/// The fourth value to write into the buffer +void kinc_g5_constant_buffer_set_float4(kinc_g5_constant_buffer_t *buffer, int offset, float value1, float value2, float value3, float value4); + +/// +/// Assigns a bunch of floats at an offset in a constant-buffer. +/// +/// The location of the constant/uniform to assign the values to +/// The values to write into the buffer +/// The number of values to write into the buffer +void kinc_g5_constant_buffer_set_floats(kinc_g5_constant_buffer_t *buffer, int offset, float *values, int count); + +/// +/// Assigns a 3x3-matrix at an offset in a constant-buffer. +/// +/// The offset at which to write the data +/// The value to write into the buffer +void kinc_g5_constant_buffer_set_matrix3(kinc_g5_constant_buffer_t *buffer, int offset, kinc_matrix3x3_t *value); + +/// +/// Assigns a 4x4-matrix at an offset in a constant-buffer. +/// +/// The offset at which to write the data +/// The value to write into the buffer +void kinc_g5_constant_buffer_set_matrix4(kinc_g5_constant_buffer_t *buffer, int offset, kinc_matrix4x4_t *value); + +extern bool kinc_g5_transposeMat3; +extern bool kinc_g5_transposeMat4; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/constantlocation.h b/armorcore/sources/kinc/graphics5/constantlocation.h new file mode 100644 index 000000000..803317ff0 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/constantlocation.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include + +/*! \file constantlocation.h + \brief Provides the constant_location-struct which is used for setting constants/uniforms in a shader. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_g5_constant_location { + ConstantLocation5Impl impl; +} kinc_g5_constant_location_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/g5unit.c b/armorcore/sources/kinc/graphics5/g5unit.c new file mode 100644 index 000000000..dd67b2bd0 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/g5unit.c @@ -0,0 +1,7 @@ +#include "commandlist.c.h" +#include "constantbuffer.c.h" +#include "graphics.c.h" +#include "pipeline.c.h" +#include "rendertarget.c.h" +#include "sampler.c.h" +#include "texture.c.h" diff --git a/armorcore/sources/kinc/graphics5/graphics.c.h b/armorcore/sources/kinc/graphics5/graphics.c.h new file mode 100644 index 000000000..90eb74563 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/graphics.c.h @@ -0,0 +1,22 @@ +#ifndef OPENGL_1_X + +#include "graphics.h" + +static int samples = 1; + +int kinc_g5_antialiasing_samples(void) { + return samples; +} + +void kinc_g5_set_antialiasing_samples(int samples_) { + samples = samples_; +} + +bool kinc_g5_fullscreen = false; + +// void Graphics5::setVertexBuffer(VertexBuffer& vertexBuffer) { +// VertexBuffer* vertexBuffers[1] = {&vertexBuffer}; +// setVertexBuffers(vertexBuffers, 1); +//} + +#endif diff --git a/armorcore/sources/kinc/graphics5/graphics.h b/armorcore/sources/kinc/graphics5/graphics.h new file mode 100644 index 000000000..4d77d1ce7 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/graphics.h @@ -0,0 +1,111 @@ +#pragma once + +#include + +#include + +#include "rendertarget.h" +#include "shader.h" +#include "vertexstructure.h" + +#include + +#include +#include + +/*! \file graphics.h + \brief Contains the base G5-functionality. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +extern bool kinc_g5_fullscreen; + +/// +/// Returns whether raytracing (see kinc/graphics5/raytrace.h) is supported. +/// +/// Whether raytracing is supported +bool kinc_g5_supports_raytracing(void); + +/// +/// Returns whether instanced rendering (kinc_g5_command_list_draw_indexed_vertices_instanced and pals) is supported. +/// +/// Whether instanced rendering is supported +bool kinc_g5_supports_instanced_rendering(void); + +/// +/// Returns whether GPU-compute (the functions in kinc/compute/compute.h) is supported. +/// +/// Whether GPU-compute is supported +bool kinc_g5_supports_compute_shaders(void); + +/// +/// Returns whether blend-constants (see kinc_g4_set_blend_constant and the blending-properties for pipelines) are supported. +/// +/// Whether blend-constants are supported +bool kinc_g5_supports_blend_constants(void); + +/// +/// Returns whether textures are supported which have widths/heights which are not powers of two. +/// +/// Whether non power of two texture-sizes are supported +bool kinc_g5_supports_non_pow2_textures(void); + +/// +/// Returns whether render-targets are upside down. This happens in OpenGL and there is currently no automatic mitigation. +/// +/// Whether render-targets are upside down +bool kinc_g5_render_targets_inverted_y(void); + +/// +/// Returns how many textures can be used at the same time in a fragment-shader. +/// +/// The number of textures +int kinc_g5_max_bound_textures(void); + +/// +/// I think this does nothing. +/// +void kinc_g5_flush(void); + +/// +/// Returns the currently used number of samples for hardware-antialiasing. +/// +/// The number of samples +int kinc_g5_antialiasing_samples(void); + +/// +/// Sets the number of samples used for hardware-antialiasing. This typically uses multisampling and typically only works with a few specific numbers of +/// sample-counts - 2 and 4 are pretty safe bets. It also might do nothing at all. +/// +/// The number of samples +void kinc_g5_set_antialiasing_samples(int samples); + +/// +/// Needs to be called before rendering to a window. Typically called at the start of each frame. +/// +/// The window to render to +void kinc_g5_begin(kinc_g5_render_target_t *renderTarget, int window); + +/// +/// Needs to be called after rendering to a window. Typically called at the end of each frame. +/// +/// The window to render to +/// +void kinc_g5_end(int window); + +/// +/// Needs to be called to make the rendered frame visible. Typically called at the very end of each frame. +/// +bool kinc_g5_swap_buffers(void); + +void kinc_g5_internal_init(void); +void kinc_g5_internal_init_window(int window, int depth_buffer_bits, int stencil_buffer_bits, bool vsync); +void kinc_g5_internal_destroy_window(int window); +void kinc_g5_internal_destroy(void); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/indexbuffer.h b/armorcore/sources/kinc/graphics5/indexbuffer.h new file mode 100644 index 000000000..3f2c4aebe --- /dev/null +++ b/armorcore/sources/kinc/graphics5/indexbuffer.h @@ -0,0 +1,73 @@ +#pragma once + +#include + +#include + +/*! \file indexbuffer.h + \brief Provides functions for setting up and using index-buffers. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kinc_g5_index_buffer_format { KINC_G5_INDEX_BUFFER_FORMAT_32BIT, KINC_G5_INDEX_BUFFER_FORMAT_16BIT } kinc_g5_index_buffer_format_t; + +typedef struct kinc_g5_index_buffer { + IndexBuffer5Impl impl; +} kinc_g5_index_buffer_t; + +/// +/// Initializes an index-buffer. +/// +/// The buffer to initialize +/// The number of indices to allocate for the buffer +/// When true, the buffer will be uploaded to gpu-memory which will make it faster to use but slower to change +void kinc_g5_index_buffer_init(kinc_g5_index_buffer_t *buffer, int count, kinc_g5_index_buffer_format_t format, bool gpu_memory); + +/// +/// Destroys an index-buffer. +/// +/// The buffer to destroy +void kinc_g5_index_buffer_destroy(kinc_g5_index_buffer_t *buffer); + +/// +/// Locks an index-buffer so its contents can be modified. +/// +/// The buffer to lock +/// The contents of the index-buffer in uint32s or uint16s depending on the format provided when initializing +void *kinc_g5_index_buffer_lock_all(kinc_g5_index_buffer_t *buffer); + +/// +/// Locks part of a vertex-buffer to modify its contents. +/// +/// The buffer to lock +/// The index of the first index to lock +/// The number of indices to lock +/// The contents of the index-buffer, starting at start, in uint32s or uint16s depending on the format provided when initializing +void *kinc_g5_index_buffer_lock(kinc_g5_index_buffer_t *buffer, int start, int count); + +/// +/// Unlocks an index-buffer after locking it so the changed buffer-contents can be used. +/// +/// The buffer to unlock +void kinc_g5_index_buffer_unlock_all(kinc_g5_index_buffer_t *buffer); + +/// +/// Unlocks part of an index-buffer after locking so the changed buffer-contents can be used. +/// +/// The buffer to unlock +/// The number of indices to unlock, starting from the start-index from the previous lock-call +void kinc_g5_index_buffer_unlock(kinc_g5_index_buffer_t *buffer, int count); + +/// +/// Returns the number of indices in the buffer. +/// +/// The buffer to query for its number of indices +/// The number of indices +int kinc_g5_index_buffer_count(kinc_g5_index_buffer_t *buffer); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/pipeline.c.h b/armorcore/sources/kinc/graphics5/pipeline.c.h new file mode 100644 index 000000000..7acd388e0 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/pipeline.c.h @@ -0,0 +1,47 @@ +#include "pipeline.h" + +void kinc_g5_internal_pipeline_init(kinc_g5_pipeline_t *pipe) { + for (int i = 0; i < 16; ++i) + pipe->inputLayout[i] = NULL; + pipe->vertexShader = NULL; + pipe->fragmentShader = NULL; + pipe->geometryShader = NULL; + pipe->tessellationControlShader = NULL; + pipe->tessellationEvaluationShader = NULL; + + pipe->cullMode = KINC_G5_CULL_MODE_NEVER; + + pipe->depthWrite = false; + pipe->depthMode = KINC_G5_COMPARE_MODE_ALWAYS; + + pipe->stencilMode = KINC_G5_COMPARE_MODE_ALWAYS; + pipe->stencilBothPass = KINC_G5_STENCIL_ACTION_KEEP; + pipe->stencilDepthFail = KINC_G5_STENCIL_ACTION_KEEP; + pipe->stencilFail = KINC_G5_STENCIL_ACTION_KEEP; + pipe->stencilReferenceValue = 0; + pipe->stencilReadMask = 0xff; + pipe->stencilWriteMask = 0xff; + + pipe->blend_source = KINC_G5_BLEND_ONE; + pipe->blend_destination = KINC_G5_BLEND_ZERO; + pipe->blend_operation = KINC_G5_BLENDOP_ADD; + pipe->alpha_blend_source = KINC_G5_BLEND_ONE; + pipe->alpha_blend_destination = KINC_G5_BLEND_ZERO; + pipe->alpha_blend_operation = KINC_G5_BLENDOP_ADD; + + for (int i = 0; i < 8; ++i) + pipe->colorWriteMaskRed[i] = true; + for (int i = 0; i < 8; ++i) + pipe->colorWriteMaskGreen[i] = true; + for (int i = 0; i < 8; ++i) + pipe->colorWriteMaskBlue[i] = true; + for (int i = 0; i < 8; ++i) + pipe->colorWriteMaskAlpha[i] = true; + + pipe->colorAttachmentCount = 1; + for (int i = 0; i < 8; ++i) + pipe->colorAttachment[i] = KINC_G5_RENDER_TARGET_FORMAT_32BIT; + + pipe->depthAttachmentBits = 0; + pipe->stencilAttachmentBits = 0; +} diff --git a/armorcore/sources/kinc/graphics5/pipeline.h b/armorcore/sources/kinc/graphics5/pipeline.h new file mode 100644 index 000000000..4fb1b7c6f --- /dev/null +++ b/armorcore/sources/kinc/graphics5/pipeline.h @@ -0,0 +1,154 @@ +#pragma once + +#include + +#include + +#include + +#include "constantlocation.h" +#include "graphics.h" + +/*! \file pipeline.h + \brief Provides functions for creating and using pipelines which configure the GPU for rendering. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +struct kinc_g5_shader; + +// identical to kinc_g4_blending_factor_t +typedef enum { + KINC_G5_BLEND_ONE, + KINC_G5_BLEND_ZERO, + KINC_G5_BLEND_SOURCE_ALPHA, + KINC_G5_BLEND_DEST_ALPHA, + KINC_G5_BLEND_INV_SOURCE_ALPHA, + KINC_G5_BLEND_INV_DEST_ALPHA, + KINC_G5_BLEND_SOURCE_COLOR, + KINC_G5_BLEND_DEST_COLOR, + KINC_G5_BLEND_INV_SOURCE_COLOR, + KINC_G5_BLEND_INV_DEST_COLOR, + KINC_G5_BLEND_CONSTANT, + KINC_G5_BLEND_INV_CONSTANT +} kinc_g5_blending_factor_t; + +// identical to kinc_g4_blending_operation_t +typedef enum { + KINC_G5_BLENDOP_ADD, + KINC_G5_BLENDOP_SUBTRACT, + KINC_G5_BLENDOP_REVERSE_SUBTRACT, + KINC_G5_BLENDOP_MIN, + KINC_G5_BLENDOP_MAX +} kinc_g5_blending_operation_t; + +typedef enum kinc_g5_cull_mode { KINC_G5_CULL_MODE_CLOCKWISE, KINC_G5_CULL_MODE_COUNTERCLOCKWISE, KINC_G5_CULL_MODE_NEVER } kinc_g5_cull_mode_t; + +typedef enum kinc_g5_compare_mode { + KINC_G5_COMPARE_MODE_ALWAYS, + KINC_G5_COMPARE_MODE_NEVER, + KINC_G5_COMPARE_MODE_EQUAL, + KINC_G5_COMPARE_MODE_NOT_EQUAL, + KINC_G5_COMPARE_MODE_LESS, + KINC_G5_COMPARE_MODE_LESS_EQUAL, + KINC_G5_COMPARE_MODE_GREATER, + KINC_G5_COMPARE_MODE_GREATER_EQUAL +} kinc_g5_compare_mode_t; + +typedef enum kinc_g5_stencil_action { + KINC_G5_STENCIL_ACTION_KEEP, + KINC_G5_STENCIL_ACTION_ZERO, + KINC_G5_STENCIL_ACTION_REPLACE, + KINC_G5_STENCIL_ACTION_INCREMENT, + KINC_G5_STENCIL_ACTION_INCREMENT_WRAP, + KINC_G5_STENCIL_ACTION_DECREMENT, + KINC_G5_STENCIL_ACTION_DECREMENT_WRAP, + KINC_G5_STENCIL_ACTION_INVERT +} kinc_g5_stencil_action_t; + +typedef struct kinc_g5_pipeline { + kinc_g5_vertex_structure_t *inputLayout[16]; + struct kinc_g5_shader *vertexShader; + struct kinc_g5_shader *fragmentShader; + struct kinc_g5_shader *geometryShader; + struct kinc_g5_shader *tessellationControlShader; + struct kinc_g5_shader *tessellationEvaluationShader; + + kinc_g5_cull_mode_t cullMode; + + bool depthWrite; + kinc_g5_compare_mode_t depthMode; + + kinc_g5_compare_mode_t stencilMode; + kinc_g5_stencil_action_t stencilBothPass; + kinc_g5_stencil_action_t stencilDepthFail; + kinc_g5_stencil_action_t stencilFail; + int stencilReferenceValue; + int stencilReadMask; + int stencilWriteMask; + + // One, Zero deactivates blending + kinc_g5_blending_factor_t blend_source; + kinc_g5_blending_factor_t blend_destination; + kinc_g5_blending_operation_t blend_operation; + kinc_g5_blending_factor_t alpha_blend_source; + kinc_g5_blending_factor_t alpha_blend_destination; + kinc_g5_blending_operation_t alpha_blend_operation; + + bool colorWriteMaskRed[8]; // Per render target + bool colorWriteMaskGreen[8]; + bool colorWriteMaskBlue[8]; + bool colorWriteMaskAlpha[8]; + + int colorAttachmentCount; + kinc_g5_render_target_format_t colorAttachment[8]; + + int depthAttachmentBits; + int stencilAttachmentBits; + + bool conservativeRasterization; + + PipelineState5Impl impl; +} kinc_g5_pipeline_t; + +/// +/// Initializes a pipeline. +/// +/// The pipeline to initialize +void kinc_g5_pipeline_init(kinc_g5_pipeline_t *pipeline); + +void kinc_g5_internal_pipeline_init(kinc_g5_pipeline_t *pipeline); + +/// +/// Destroys a pipeline. +/// +/// The pipeline to destroy +void kinc_g5_pipeline_destroy(kinc_g5_pipeline_t *pipeline); + +/// +/// Compiles a pipeline. After a pipeline has been compiled it is finalized. It cannot be compiled again and further changes to the pipeline are ignored. +/// +/// The pipeline to compile +void kinc_g5_pipeline_compile(kinc_g5_pipeline_t *pipeline); + +/// +/// Searches for a constant/uniform and returns a constant-location which can be used to change the constant/uniform. +/// +/// The pipeline to search in +/// The name of the constant/uniform to find +/// The constant-location of the constant/uniform +kinc_g5_constant_location_t kinc_g5_pipeline_get_constant_location(kinc_g5_pipeline_t *pipeline, const char *name); + +/// +/// Searches for a texture-declaration and returns a texture-unit which can be used to assign a texture. +/// +/// The pipeline to search in +/// The name of the texture-declaration to search for +/// The texture-unit of the texture-declaration +kinc_g5_texture_unit_t kinc_g5_pipeline_get_texture_unit(kinc_g5_pipeline_t *pipeline, const char *name); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/raytrace.h b/armorcore/sources/kinc/graphics5/raytrace.h new file mode 100644 index 000000000..8078cfaa0 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/raytrace.h @@ -0,0 +1,51 @@ +#pragma once + +/*! \file raytrace.h + \brief Preliminary API, requires some actual D3D12/Vulkan code to fill in + the acceleration-structure and pipeline-details. Also requires manually + compiled shaders. Use with caution. +*/ + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct kinc_g5_command_list; +struct kinc_g5_constant_buffer; +struct kinc_g5_index_buffer; +struct kinc_g5_render_target; +struct kinc_g5_texture; +struct kinc_g5_vertex_buffer; + +typedef struct kinc_raytrace_pipeline { + struct kinc_g5_constant_buffer *_constant_buffer; + kinc_raytrace_pipeline_impl_t impl; +} kinc_raytrace_pipeline_t; + +bool kinc_raytrace_supported(void); +void kinc_raytrace_pipeline_init(kinc_raytrace_pipeline_t *pipeline, struct kinc_g5_command_list *command_list, void *ray_shader, int ray_shader_size, + struct kinc_g5_constant_buffer *constant_buffer); +void kinc_raytrace_pipeline_destroy(kinc_raytrace_pipeline_t *pipeline); + +typedef struct kinc_raytrace_acceleration_structure { + kinc_raytrace_acceleration_structure_impl_t impl; +} kinc_raytrace_acceleration_structure_t; + +void kinc_raytrace_acceleration_structure_init(kinc_raytrace_acceleration_structure_t *accel, struct kinc_g5_command_list *command_list, + struct kinc_g5_vertex_buffer *vb, struct kinc_g5_index_buffer *ib, float scale); +void kinc_raytrace_acceleration_structure_destroy(kinc_raytrace_acceleration_structure_t *accel); +void kinc_raytrace_set_textures(struct kinc_g5_render_target *texpaint0, struct kinc_g5_render_target *texpaint1, struct kinc_g5_render_target *texpaint2, struct kinc_g5_texture *texenv, struct kinc_g5_texture *texsobol, struct kinc_g5_texture *texscramble, struct kinc_g5_texture *texrank); + +void kinc_raytrace_set_acceleration_structure(kinc_raytrace_acceleration_structure_t *accel); +void kinc_raytrace_set_pipeline(kinc_raytrace_pipeline_t *pipeline); +void kinc_raytrace_set_target(struct kinc_g5_render_target *output); +void kinc_raytrace_dispatch_rays(struct kinc_g5_command_list *command_list); +void kinc_raytrace_copy(struct kinc_g5_command_list *command_list, struct kinc_g5_render_target *target, struct kinc_g5_texture *source); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/rendertarget.c.h b/armorcore/sources/kinc/graphics5/rendertarget.c.h new file mode 100644 index 000000000..36b1a39b6 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/rendertarget.c.h @@ -0,0 +1,16 @@ +#include "rendertarget.h" + +void kinc_g5_render_target_init(kinc_g5_render_target_t *render_target, int width, int height, kinc_g5_render_target_format_t format, int depthBufferBits, + int stencilBufferBits) { + kinc_g5_render_target_init_with_multisampling(render_target, width, height, format, depthBufferBits, stencilBufferBits, 1); +} + +void kinc_g5_render_target_init_framebuffer(kinc_g5_render_target_t *render_target, int width, int height, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits) { + kinc_g5_render_target_init_framebuffer_with_multisampling(render_target, width, height, format, depthBufferBits, stencilBufferBits, 1); +} + +void kinc_g5_render_target_init_cube(kinc_g5_render_target_t *render_target, int cubeMapSize, kinc_g5_render_target_format_t format, int depthBufferBits, + int stencilBufferBits) { + kinc_g5_render_target_init_cube_with_multisampling(render_target, cubeMapSize, format, depthBufferBits, stencilBufferBits, 1); +} diff --git a/armorcore/sources/kinc/graphics5/rendertarget.h b/armorcore/sources/kinc/graphics5/rendertarget.h new file mode 100644 index 000000000..0206e18ae --- /dev/null +++ b/armorcore/sources/kinc/graphics5/rendertarget.h @@ -0,0 +1,134 @@ +#pragma once + +#include + +#include "textureunit.h" + +#include + +/*! \file rendertarget.h + \brief Provides functions for handling render-targets. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kinc_g5_render_target_format { + KINC_G5_RENDER_TARGET_FORMAT_32BIT, + KINC_G5_RENDER_TARGET_FORMAT_64BIT_FLOAT, + KINC_G5_RENDER_TARGET_FORMAT_32BIT_RED_FLOAT, + KINC_G5_RENDER_TARGET_FORMAT_128BIT_FLOAT, + KINC_G5_RENDER_TARGET_FORMAT_16BIT_DEPTH, + KINC_G5_RENDER_TARGET_FORMAT_8BIT_RED, + KINC_G5_RENDER_TARGET_FORMAT_16BIT_RED_FLOAT +} kinc_g5_render_target_format_t; + +typedef struct kinc_g5_render_target { + int width; + int height; + int texWidth; + int texHeight; + int framebuffer_index; + bool isCubeMap; + bool isDepthAttachment; + RenderTarget5Impl impl; +} kinc_g5_render_target_t; + +/// +/// Allocates and initializes a regular render-target. The contents of the render-target are undefined. +/// +/// +/// +/// +/// +/// +/// +/// +void kinc_g5_render_target_init(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, int depthBufferBits, + int stencilBufferBits); + +/// +/// Allocates and initializes a regular render-target. Can fall back to a regular render-target. The contents of the render-target are undefined. +/// +/// +/// +/// +/// +/// +/// +/// +/// +void kinc_g5_render_target_init_with_multisampling(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel); + +/// +/// Allocates and initializes a framebuffer. The contents of the framebuffer are undefined. +/// +/// +/// +/// +/// +/// +/// +/// +void kinc_g5_render_target_init_framebuffer(kinc_g5_render_target_t *target, int width, int height, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits); + +/// +/// Allocates and initializes a multisampled framebuffer. Can fall back to a regular framebuffer. The contents of the framebuffer are undefined. +/// +/// +/// +/// +/// +/// +/// +/// +/// +void kinc_g5_render_target_init_framebuffer_with_multisampling(kinc_g5_render_target_t *target, int width, int height, + kinc_g5_render_target_format_t format, int depthBufferBits, int stencilBufferBits, + int samples_per_pixel); + +/// +/// Allocates and initializes a render-target-cube-map. The contents of the render-target are undefined. +/// +/// +/// +/// +/// +/// +/// +void kinc_g5_render_target_init_cube(kinc_g5_render_target_t *target, int cubeMapSize, kinc_g5_render_target_format_t format, int depthBufferBits, + int stencilBufferBits); + +/// +/// Allocates and initializes a multisampled render-target-cube-map. Can fall back to a regular cube-map. The contents of the render-target are undefined. +/// +/// +/// +/// +/// +/// +/// +/// +void kinc_g5_render_target_init_cube_with_multisampling(kinc_g5_render_target_t *target, int cubeMapSize, kinc_g5_render_target_format_t format, + int depthBufferBits, int stencilBufferBits, int samples_per_pixel); + +/// +/// Deallocates and destroys a render-target. +/// +/// The render-target to destroy +void kinc_g5_render_target_destroy(kinc_g5_render_target_t *target); + +/// +/// Copies the depth and stencil-components of one render-target into another one. +/// +/// The render-target to copy the data into +/// The render-target from which to copy the data +/// +void kinc_g5_render_target_set_depth_stencil_from(kinc_g5_render_target_t *target, kinc_g5_render_target_t *source); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/sampler.c.h b/armorcore/sources/kinc/graphics5/sampler.c.h new file mode 100644 index 000000000..fcc20e983 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/sampler.c.h @@ -0,0 +1,19 @@ +#include "sampler.h" + +void kinc_g5_sampler_options_set_defaults(kinc_g5_sampler_options_t *options) { + options->u_addressing = KINC_G5_TEXTURE_ADDRESSING_CLAMP; + options->v_addressing = KINC_G5_TEXTURE_ADDRESSING_CLAMP; + options->w_addressing = KINC_G5_TEXTURE_ADDRESSING_CLAMP; + + options->magnification_filter = KINC_G5_TEXTURE_FILTER_POINT; + options->minification_filter = KINC_G5_TEXTURE_FILTER_POINT; + options->mipmap_filter = KINC_G5_MIPMAP_FILTER_POINT; + + options->lod_min_clamp = 0.0f; + options->lod_max_clamp = 32.0f; + + options->is_comparison = false; + options->compare_mode = KINC_G5_COMPARE_MODE_ALWAYS; + + options->max_anisotropy = 1; +} diff --git a/armorcore/sources/kinc/graphics5/sampler.h b/armorcore/sources/kinc/graphics5/sampler.h new file mode 100644 index 000000000..314ec896f --- /dev/null +++ b/armorcore/sources/kinc/graphics5/sampler.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include + +/*! \file sampler.h + \brief Provides functions for sampler objects. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kinc_g5_texture_addressing { + KINC_G5_TEXTURE_ADDRESSING_REPEAT, + KINC_G5_TEXTURE_ADDRESSING_MIRROR, + KINC_G5_TEXTURE_ADDRESSING_CLAMP, + KINC_G5_TEXTURE_ADDRESSING_BORDER +} kinc_g5_texture_addressing_t; + +typedef enum kinc_g5_texture_filter { + KINC_G5_TEXTURE_FILTER_POINT, + KINC_G5_TEXTURE_FILTER_LINEAR, + KINC_G5_TEXTURE_FILTER_ANISOTROPIC +} kinc_g5_texture_filter_t; + +typedef enum kinc_g5_mipmap_filter { + KINC_G5_MIPMAP_FILTER_NONE, + KINC_G5_MIPMAP_FILTER_POINT, + KINC_G5_MIPMAP_FILTER_LINEAR // linear texture filter + linear mip filter -> trilinear filter +} kinc_g5_mipmap_filter_t; + +typedef struct kinc_g5_sampler_options { + kinc_g5_texture_addressing_t u_addressing; + kinc_g5_texture_addressing_t v_addressing; + kinc_g5_texture_addressing_t w_addressing; + + kinc_g5_texture_filter_t minification_filter; + kinc_g5_texture_filter_t magnification_filter; + kinc_g5_mipmap_filter_t mipmap_filter; + + float lod_min_clamp; + float lod_max_clamp; + + uint16_t max_anisotropy; + + bool is_comparison; + kinc_g5_compare_mode_t compare_mode; +} kinc_g5_sampler_options_t; + +typedef struct kinc_g5_sampler { + kinc_g5_sampler_impl_t impl; +} kinc_g5_sampler_t; + +/// +/// Initializes the passed options-object with the default-options. +/// +/// The options-object for which the default-options will be set +void kinc_g5_sampler_options_set_defaults(kinc_g5_sampler_options_t *options); + +/// +/// Creates a sampler-object. +/// +/// On platforms such as older OpenGL not all sampler attributes may be available. +/// +/// Pointer to the sampler object to initialize +/// Options for the sampler +void kinc_g5_sampler_init(kinc_g5_sampler_t *sampler, const kinc_g5_sampler_options_t *options); + +/// +/// Destroys a sampler-object. +/// +/// The sampler-object to destroy +void kinc_g5_sampler_destroy(kinc_g5_sampler_t *sampler); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/shader.h b/armorcore/sources/kinc/graphics5/shader.h new file mode 100644 index 000000000..6db74d25e --- /dev/null +++ b/armorcore/sources/kinc/graphics5/shader.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include + +#include + +/*! \file shader.h + \brief Provides functions for creating and destroying shaders. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kinc_g5_shader_type { + KINC_G5_SHADER_TYPE_FRAGMENT, + KINC_G5_SHADER_TYPE_VERTEX, + KINC_G5_SHADER_TYPE_COMPUTE, + KINC_G5_SHADER_TYPE_GEOMETRY, + KINC_G5_SHADER_TYPE_TESSELLATION_CONTROL, + KINC_G5_SHADER_TYPE_TESSELLATION_EVALUATION, + + KINC_G5_SHADER_TYPE_COUNT +} kinc_g5_shader_type_t; + +typedef struct kinc_g5_shader { + Shader5Impl impl; +} kinc_g5_shader_t; + +/// +/// Initializes a shader based on system-specific shader-data. The system-specific shader-data is usually created per system by the krafix-shader-compiler which +/// is automatically called by kincmake. +/// +/// The shader to initialize +/// The system-specific shader-data +/// The length of the system-specific shader-data in bytes +/// The type of the shader +void kinc_g5_shader_init(kinc_g5_shader_t *shader, const void *source, size_t length, kinc_g5_shader_type_t type); + +/// +/// Destroys a shader. +/// +/// The shader to destroy +void kinc_g5_shader_destroy(kinc_g5_shader_t *shader); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/texture.c.h b/armorcore/sources/kinc/graphics5/texture.c.h new file mode 100644 index 000000000..3729f4334 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/texture.c.h @@ -0,0 +1,32 @@ +#include "texture.h" + +/* +Graphics5::Texture::Texture(Kore::Reader& reader, const char* format, bool readable) : Image(reader, format, readable) { + _init(format, readable); +} + +Graphics5::Texture::Texture(const char* filename, bool readable) { + FileReader reader(filename); + Image::init(reader, filename, readable); + _init(filename, readable); +} + +Graphics5::Texture::Texture(void* data, int size, const char* format, bool readable) { + BufferReader reader(data, size); + Image::init(reader, format, readable); + _init(format, readable); +} + +Graphics5::Texture::Texture(void* data, int width, int height, int format, bool readable) : Image(data, width, height, Image::Format(format), readable) { + _init("", readable); +} +*/ + +bool kinc_g5_texture_unit_equals(kinc_g5_texture_unit_t *unit1, kinc_g5_texture_unit_t *unit2) { + for (int i = 0; i < KINC_G5_SHADER_TYPE_COUNT; ++i) { + if (unit1->stages[i] != unit2->stages[i]) { + return false; + } + } + return true; +} diff --git a/armorcore/sources/kinc/graphics5/texture.h b/armorcore/sources/kinc/graphics5/texture.h new file mode 100644 index 000000000..4cdd0c300 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/texture.h @@ -0,0 +1,98 @@ +#pragma once + +#include + +#include + +#include "textureunit.h" + +#include + +/*! \file texture.h + \brief Provides functions for handling textures. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_g5_texture { + int texWidth; + int texHeight; + kinc_image_format_t format; + Texture5Impl impl; +} kinc_g5_texture_t; + +/// +/// Allocates and initializes a texture without copying any data into it. +/// +/// The texture to initialize +/// The width of the texture to create +/// The height of the texture to create +/// The format of the texture to create +void kinc_g5_texture_init(kinc_g5_texture_t *texture, int width, int height, kinc_image_format_t format); + +/// +/// Allocates and initializes a 3d-texture without copying any data into it. +/// +/// The texture to initialize +/// The width of the texture to create +/// The height of the texture to create +/// The depth of the texture to create +/// The format of the texture to create +void kinc_g5_texture_init3d(kinc_g5_texture_t *texture, int width, int height, int depth, kinc_image_format_t format); + +/// +/// Allocates and initializes a texture and copies image-data into it. +/// +/// The texture to initialize +/// The image which's data is copied into the texture +void kinc_g5_texture_init_from_image(kinc_g5_texture_t *texture, kinc_image_t *image); + +// void kinc_g5_texture_init_from_encoded_data(kinc_g5_texture_t *texture, void *data, int size, const char *format, bool readable); +// void kinc_g5_texture_init_from_data(kinc_g5_texture_t *texture, void *data, int width, int height, int format, bool readable); + +void kinc_g5_texture_init_non_sampled_access(kinc_g5_texture_t *texture, int width, int height, kinc_image_format_t format); + +/// +/// Deallocates and destroys a texture. +/// +/// The texture to destroy +void kinc_g5_texture_destroy(kinc_g5_texture_t *texture); + +#ifdef KINC_ANDROID +void kinc_g5_texture_init_from_id(kinc_g5_texture_t *texture, unsigned texid); +#endif + +uint8_t *kinc_g5_texture_lock(kinc_g5_texture_t *texture); + +void kinc_g5_texture_unlock(kinc_g5_texture_t *texture); + +/// +/// Clears parts of a texture to a color. +/// +void kinc_g5_texture_clear(kinc_g5_texture_t *texture, int x, int y, int z, int width, int height, int depth, unsigned color); + +#if defined(KINC_IOS) || defined(KINC_MACOS) +void kinc_g5_texture_upload(kinc_g5_texture_t *texture, uint8_t *data); +#endif + +/// +/// Generates the mipmap-chain for a texture. +/// +/// The render-target to create the mipmaps for +/// The number of mipmap-levels to generate +void kinc_g5_texture_generate_mipmaps(kinc_g5_texture_t *texture, int levels); + +void kinc_g5_texture_set_mipmap(kinc_g5_texture_t *texture, kinc_image_t *mipmap, int level); + +/// +/// Returns the stride of the first mipmap-layer of the texture in bytes. +/// +/// The texture to figure out the stride for +/// The stride of the first mipmap-layer in bytes +int kinc_g5_texture_stride(kinc_g5_texture_t *texture); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/textureunit.h b/armorcore/sources/kinc/graphics5/textureunit.h new file mode 100644 index 000000000..91ea16095 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/textureunit.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include +#include + +/*! \file textureunit.h + \brief Provides a texture-unit-struct which is used for setting textures in a shader. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_g5_texture_unit { + int stages[KINC_G5_SHADER_TYPE_COUNT]; +} kinc_g5_texture_unit_t; + +bool kinc_g5_texture_unit_equals(kinc_g5_texture_unit_t *unit1, kinc_g5_texture_unit_t *unit2); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/vertexbuffer.h b/armorcore/sources/kinc/graphics5/vertexbuffer.h new file mode 100644 index 000000000..4fca070bd --- /dev/null +++ b/armorcore/sources/kinc/graphics5/vertexbuffer.h @@ -0,0 +1,85 @@ +#pragma once + +#include + +#include "vertexstructure.h" + +#include + +/*! \file vertexbuffer.h + \brief Provides functions for setting up and using vertex-buffers. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_g5_vertex_buffer { + VertexBuffer5Impl impl; +} kinc_g5_vertex_buffer_t; + +/// +/// Allocate and initialize a vertex-buffer. +/// +/// The buffer to initialize +/// The number of vertices in the buffer +/// The structure of the buffer +/// If true, the vertex-buffer will reside in gpu-memory which will make it slower to update but faster to use +/// The step-rate for instanced-rendering - use 0 if instanced-rendering will not be used with this buffer +void kinc_g5_vertex_buffer_init(kinc_g5_vertex_buffer_t *buffer, int count, kinc_g5_vertex_structure_t *structure, bool gpu_memory, + int instance_data_step_rate); + +/// +/// Destroys a vertex-buffer. +/// +/// The buffer to destroy +void kinc_g5_vertex_buffer_destroy(kinc_g5_vertex_buffer_t *buffer); + +/// +/// Locks all of a vertex-buffer to modify its contents. +/// +/// The buffer to lock +/// The contents of the buffer +float *kinc_g5_vertex_buffer_lock_all(kinc_g5_vertex_buffer_t *buffer); + +/// +/// Locks part of a vertex-buffer to modify its contents. +/// +/// The buffer to lock +/// The index of the first vertex to lock +/// The number of vertices to lock +/// The contents of the buffer, starting at start +float *kinc_g5_vertex_buffer_lock(kinc_g5_vertex_buffer_t *buffer, int start, int count); + +/// +/// Unlock all of a vertex-buffer so the changed contents can be used. +/// +/// The buffer to unlock +void kinc_g5_vertex_buffer_unlock_all(kinc_g5_vertex_buffer_t *buffer); + +/// +/// Unlocks part of a vertex-buffer so the changed contents can be used. +/// +/// The buffer to unlock +/// The number of vertices to unlock, starting from the start-vertex from the previous lock-call +void kinc_g5_vertex_buffer_unlock(kinc_g5_vertex_buffer_t *buffer, int count); + +/// +/// Returns the number of vertices in a buffer. +/// +/// The buffer to figure out the number of vertices for +/// The number of vertices +int kinc_g5_vertex_buffer_count(kinc_g5_vertex_buffer_t *buffer); + +/// +/// Returns the stride aka the size of one vertex of the buffer in bytes. +/// +/// The buffer to figure out the stride for +/// The stride of the buffer in bytes +int kinc_g5_vertex_buffer_stride(kinc_g5_vertex_buffer_t *buffer); + +int kinc_g5_internal_vertex_buffer_set(kinc_g5_vertex_buffer_t *buffer, int offset); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/graphics5/vertexstructure.h b/armorcore/sources/kinc/graphics5/vertexstructure.h new file mode 100644 index 000000000..d6b6b5e76 --- /dev/null +++ b/armorcore/sources/kinc/graphics5/vertexstructure.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include + +/*! \file vertexstructure.h + \brief Provides types for setting up the structure of vertices in a vertex-buffer. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef kinc_g4_vertex_data_t kinc_g5_vertex_data_t; + +// typedef kinc_g4_vertex_attribute_t kinc_g5_vertex_attribute_t; + +typedef kinc_g4_vertex_element_t kinc_g5_vertex_element_t; + +typedef kinc_g4_vertex_structure_t kinc_g5_vertex_structure_t; + +/// +/// Initializes a vertex-structure. +/// +/// The structure to initialize +/// +static inline void kinc_g5_vertex_structure_init(kinc_g5_vertex_structure_t *structure) { + kinc_g4_vertex_structure_init(structure); +} + +/// +/// Adds an element to a vertex-structure. +/// +/// The structure to add an element to +/// The name to use for the new element +/// The type of data to assign for the new element +/// +static inline void kinc_g5_vertex_structure_add(kinc_g5_vertex_structure_t *structure, const char *name, kinc_g5_vertex_data_t data) { + kinc_g4_vertex_structure_add(structure, name, data); +} + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/image.h b/armorcore/sources/kinc/image.h new file mode 100644 index 000000000..3d70c2953 --- /dev/null +++ b/armorcore/sources/kinc/image.h @@ -0,0 +1,750 @@ +#pragma once + +#include + +#include +#include +#include + +/*! \file image.h + \brief Functionality for creating and loading images. Image loading supports PNG, JPEG and the custom K format. K files can contain image data that uses + texture-compression (see kinc_image_compression). +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kinc_image_compression { + KINC_IMAGE_COMPRESSION_NONE, + KINC_IMAGE_COMPRESSION_DXT5, + KINC_IMAGE_COMPRESSION_ASTC, + KINC_IMAGE_COMPRESSION_PVRTC +} kinc_image_compression_t; + +typedef enum kinc_image_format { + KINC_IMAGE_FORMAT_RGBA32, + KINC_IMAGE_FORMAT_GREY8, + KINC_IMAGE_FORMAT_RGB24, + KINC_IMAGE_FORMAT_RGBA128, + KINC_IMAGE_FORMAT_RGBA64, + KINC_IMAGE_FORMAT_A32, + KINC_IMAGE_FORMAT_BGRA32, + KINC_IMAGE_FORMAT_A16 +} kinc_image_format_t; + +typedef struct kinc_image { + int width, height, depth; + kinc_image_format_t format; + unsigned internal_format; + kinc_image_compression_t compression; + void *data; + size_t data_size; +} kinc_image_t; + +typedef struct kinc_image_read_callbacks { + size_t (*read)(void *user_data, void *data, size_t size); + void (*seek)(void *user_data, size_t pos); + size_t (*pos)(void *user_data); + size_t (*size)(void *user_data); +} kinc_image_read_callbacks_t; + +/// +/// Creates a 2D kinc_image in the provided memory. +/// +/// The size that's occupied by the image in memory in bytes +size_t kinc_image_init(kinc_image_t *image, void *memory, int width, int height, kinc_image_format_t format); + +/// +/// Creates a 3D kinc_image in the provided memory. +/// +/// The size that's occupied by the image in memory in bytes +size_t kinc_image_init3d(kinc_image_t *image, void *memory, int width, int height, int depth, kinc_image_format_t format); + +/// +/// Peeks into an image file and figures out the size it will occupy in memory. +/// +/// The memory size in bytes that will be used when loading the image +size_t kinc_image_size_from_file(const char *filename); + +/// +/// Peeks into an image that is loaded via callback functions and figures out the size it will occupy in memory. +/// +/// The memory size in bytes that will be used when loading the image +size_t kinc_image_size_from_callbacks(kinc_image_read_callbacks_t callbacks, void *user_data, const char *format); + +/// +/// Peeks into an image file that resides in memory and figures out the size it will occupy in memory once it is uncompressed. +/// +/// The encoded data +/// The size of the encoded data +/// Something like "png" can help, it also works to just put in the filename +/// The memory size in bytes that will be used when loading the image +size_t kinc_image_size_from_encoded_bytes(void *data, size_t data_size, const char *format_hint); + +/// +/// Loads an image from a file. +/// +/// The memory size in bytes that will be used when loading the image +size_t kinc_image_init_from_file(kinc_image_t *image, void *memory, const char *filename); + +/// +/// Loads an image file using callbacks. +/// +/// The memory size in bytes that will be used when loading the image +size_t kinc_image_init_from_callbacks(kinc_image_t *image, void *memory, kinc_image_read_callbacks_t callbacks, void *user_data, const char *format); + +/// +/// Loads an image file from a memory. +/// +/// The memory size in bytes that will be used when loading the image +size_t kinc_image_init_from_encoded_bytes(kinc_image_t *image, void *memory, void *data, size_t data_size, const char *format); + +/// +/// Creates a 2D image from memory. +/// +void kinc_image_init_from_bytes(kinc_image_t *image, void *data, int width, int height, kinc_image_format_t format); + +/// +/// Creates a 3D image from memory. +/// +void kinc_image_init_from_bytes3d(kinc_image_t *image, void *data, int width, int height, int depth, kinc_image_format_t format); + +/// +/// Destroys an image. This does not free the user-provided memory. +/// +void kinc_image_destroy(kinc_image_t *image); + +/// +/// Gets the color value of a 32 bit pixel. If this doesn't fit the format of the image please use kinc_image_at_raw instead. +/// +/// One 32 bit color value +uint32_t kinc_image_at(kinc_image_t *image, int x, int y); + +/// +/// Gets a pointer to the color-data of one pixel. +/// +/// A pointer to the color-data of the pixel pointed to by x and y +void *kinc_image_at_raw(kinc_image_t *image, int x, int y); + +/// +/// Provides access to the image data. +/// +/// A pointer to the image data +uint8_t *kinc_image_get_pixels(kinc_image_t *image); + +/// +/// Gets the size in bytes of a single pixel for a given image format. +/// +/// The size of one pixel in bytes +int kinc_image_format_sizeof(kinc_image_format_t format); + +#ifdef KINC_IMPLEMENTATION_ROOT +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#include "image.h" +#include + +#ifdef KINC_IMPLEMENTATION_ROOT +#undef KINC_IMPLEMENTATION +#endif +#include +#ifdef KINC_IMPLEMENTATION_ROOT +#define KINC_IMPLEMENTATION +#endif + +#undef KINC_IMPLEMENTATION +#include +#include +#include +#define KINC_IMPLEMENTATION + +#include + +#define BUFFER_SIZE 1024 * 1024 * 4 +static uint8_t buffer[BUFFER_SIZE]; +static size_t buffer_offset = 0; +static uint8_t *last_allocated_pointer = 0; + +static void *buffer_malloc(size_t size) { + uint8_t *current = &buffer[buffer_offset]; + buffer_offset += size + sizeof(size_t); + if (buffer_offset > BUFFER_SIZE) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Not enough memory on image.c Buffer."); + return NULL; + } + memcpy(current, &size, sizeof(*current)); + last_allocated_pointer = current + sizeof(size_t); + return current + sizeof(size_t); +} + +static void *buffer_realloc(void *p, size_t size) { + if (p == NULL) { + return buffer_malloc(size); + } + uint8_t *old_pointer = (uint8_t *)p; + size_t old_size = *(size_t *)(old_pointer - sizeof(size_t)); + if (size <= old_size) { + return old_pointer; + } + else { + if (last_allocated_pointer == old_pointer) { + size_t last_size = &buffer[buffer_offset] - old_pointer; + size_t size_diff = size - last_size; + buffer_offset += size_diff + sizeof(size_t); + return old_pointer; + } + uint8_t *new_pointer = (uint8_t *)buffer_malloc(size); + if (new_pointer == NULL) { + return NULL; + } + memcpy(new_pointer, old_pointer, old_size < size ? old_size : size); + return new_pointer; + } +} + +static void buffer_free(void *p) {} + +#define STBI_MALLOC(sz) buffer_malloc(sz) +#define STBI_REALLOC(p, newsz) buffer_realloc(p, newsz) +#define STBI_FREE(p) buffer_free(p) + +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_STATIC +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wunused-function" +#endif +#include +#include +#include + +#include + +typedef struct { + kinc_image_read_callbacks_t callbacks; + void *user_data; +} read_data; + +// fill 'data' with 'size' bytes. return number of bytes actually read +static int stb_read(void *user, char *data, int size) { + read_data *reader = (read_data *)user; + return (int)reader->callbacks.read(reader->user_data, data, (size_t)size); +} + +// skip the next 'n' bytes, or 'unget' the last -n bytes if negative +static void stb_skip(void *user, int n) { + read_data *reader = (read_data *)user; + reader->callbacks.seek(reader->user_data, reader->callbacks.pos(reader->user_data) + (size_t)n); +} + +// returns nonzero if we are at end of file/data +static int stb_eof(void *user) { + read_data *reader = (read_data *)user; + return reader->callbacks.pos(reader->user_data) == reader->callbacks.size(reader->user_data); +} + +static _Bool endsWith(const char *str, const char *suffix) { + if (str == NULL || suffix == NULL) + return 0; + size_t lenstr = strlen(str); + size_t lensuffix = strlen(suffix); + if (lensuffix > lenstr) + return 0; + return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; +} + +static size_t loadImageSize(kinc_image_read_callbacks_t callbacks, void *user_data, const char *filename) { + if (endsWith(filename, "k")) { + uint8_t data[4]; + callbacks.read(user_data, data, 4); + int width = kinc_read_s32le(data); + callbacks.read(user_data, data, 4); + int height = kinc_read_s32le(data); + + char fourcc[5]; + callbacks.read(user_data, fourcc, 4); + fourcc[4] = 0; + + if (strcmp(fourcc, "LZ4 ") == 0) { + return width * height * 4; + } + else if (strcmp(fourcc, "LZ4F") == 0) { + return width * height * 16; + } + else if (strcmp(fourcc, "ASTC") == 0) { + return width * height * 4; // just an upper bound + } + else if (strcmp(fourcc, "DXT5") == 0) { + return width * height; // just an upper bound + } + else { + kinc_log(KINC_LOG_LEVEL_ERROR, "Unknown fourcc in .k file."); + return 0; + } + } + else if (endsWith(filename, "pvr")) { + uint8_t data[4]; + callbacks.read(user_data, data, 4); // version + callbacks.read(user_data, data, 4); // flags + callbacks.read(user_data, data, 4); // pixelFormat1 + callbacks.read(user_data, data, 4); // colourSpace + callbacks.read(user_data, data, 4); // channelType + callbacks.read(user_data, data, 4); + uint32_t hh = kinc_read_u32le(data); + callbacks.read(user_data, data, 4); + uint32_t ww = kinc_read_u32le(data); + + return ww * hh / 2; + } + else if (endsWith(filename, "hdr")) { + stbi_io_callbacks stbi_callbacks; + stbi_callbacks.eof = stb_eof; + stbi_callbacks.read = stb_read; + stbi_callbacks.skip = stb_skip; + + read_data reader; + reader.callbacks = callbacks; + reader.user_data = user_data; + + int x, y, comp; + stbi_info_from_callbacks(&stbi_callbacks, &reader, &x, &y, &comp); + buffer_offset = 0; + return x * y * 16; + } + else { + stbi_io_callbacks stbi_callbacks; + stbi_callbacks.eof = stb_eof; + stbi_callbacks.read = stb_read; + stbi_callbacks.skip = stb_skip; + + read_data reader; + reader.callbacks = callbacks; + reader.user_data = user_data; + + int x, y, comp; + stbi_info_from_callbacks(&stbi_callbacks, &reader, &x, &y, &comp); + buffer_offset = 0; + return x * y * 4; + } +} + +static bool loadImage(kinc_image_read_callbacks_t callbacks, void *user_data, const char *filename, uint8_t *output, size_t *outputSize, int *width, int *height, + kinc_image_compression_t *compression, kinc_image_format_t *format, unsigned *internalFormat) { + *format = KINC_IMAGE_FORMAT_RGBA32; + if (endsWith(filename, "k")) { + uint8_t data[4]; + callbacks.read(user_data, data, 4); + *width = kinc_read_s32le(data); + callbacks.read(user_data, data, 4); + *height = kinc_read_s32le(data); + + char fourcc[5]; + callbacks.read(user_data, fourcc, 4); + fourcc[4] = 0; + + int compressedSize = (int)callbacks.size(user_data) - 12; + + if (strcmp(fourcc, "LZ4 ") == 0) { + *compression = KINC_IMAGE_COMPRESSION_NONE; + *internalFormat = 0; + *outputSize = (size_t)(*width * *height * 4); + callbacks.read(user_data, buffer, compressedSize); + LZ4_decompress_safe((char *)buffer, (char *)output, compressedSize, (int)*outputSize); + return true; + } + else if (strcmp(fourcc, "LZ4F") == 0) { + *compression = KINC_IMAGE_COMPRESSION_NONE; + *internalFormat = 0; + *outputSize = (size_t)(*width * *height * 16); + callbacks.read(user_data, buffer, compressedSize); + LZ4_decompress_safe((char *)buffer, (char *)output, compressedSize, (int)*outputSize); + *format = KINC_IMAGE_FORMAT_RGBA128; + return true; + } + else if (strcmp(fourcc, "ASTC") == 0) { + *compression = KINC_IMAGE_COMPRESSION_ASTC; + *outputSize = (size_t)(*width * *height * 4); + callbacks.read(user_data, buffer, compressedSize); + *outputSize = LZ4_decompress_safe((char *)buffer, (char *)output, compressedSize, (int)*outputSize); + + uint8_t blockdim_x = 6; + uint8_t blockdim_y = 6; + *internalFormat = (blockdim_x << 8) + blockdim_y; + + return true; + + /*int index = 0; + index += 4; // magic + u8 blockdim_x = astcdata[index++]; + u8 blockdim_y = astcdata[index++]; + ++index; // blockdim_z + internalFormat = (blockdim_x << 8) + blockdim_y; + u8 xsize[4]; + xsize[0] = astcdata[index++]; + xsize[1] = astcdata[index++]; + xsize[2] = astcdata[index++]; + xsize[3] = 0; + this->width = *(unsigned*)&xsize[0]; + u8 ysize[4]; + ysize[0] = astcdata[index++]; + ysize[1] = astcdata[index++]; + ysize[2] = astcdata[index++]; + ysize[3] = 0; + this->height = *(unsigned*)&ysize[0]; + u8 zsize[3]; + zsize[0] = astcdata[index++]; + zsize[1] = astcdata[index++]; + zsize[2] = astcdata[index++]; + u8* all = (u8*)astcdata[index]; + dataSize -= 16; + this->data = new u8[dataSize]; + for (int i = 0; i < dataSize; ++i) { + data[i] = all[16 + i]; + } + free(astcdata);*/ + } + else if (strcmp(fourcc, "DXT5") == 0) { + *compression = KINC_IMAGE_COMPRESSION_DXT5; + *outputSize = (size_t)(*width * *height); + callbacks.read(user_data, buffer, compressedSize); + *outputSize = LZ4_decompress_safe((char *)buffer, (char *)output, compressedSize, (int)*outputSize); + *internalFormat = 0; + return true; + } + else { + kinc_log(KINC_LOG_LEVEL_ERROR, "Unknown fourcc in .k file."); + return false; + } + } + else if (endsWith(filename, "pvr")) { + uint8_t data[4]; + callbacks.read(user_data, data, 4); // version + callbacks.read(user_data, data, 4); // flags + callbacks.read(user_data, data, 4); // pixelFormat1 + callbacks.read(user_data, data, 4); // colourSpace + callbacks.read(user_data, data, 4); // channelType + callbacks.read(user_data, data, 4); + uint32_t hh = kinc_read_u32le(data); + callbacks.read(user_data, data, 4); + uint32_t ww = kinc_read_u32le(data); + callbacks.read(user_data, data, 4); // depth + callbacks.read(user_data, data, 4); // numSurfaces + callbacks.read(user_data, data, 4); // numFaces + callbacks.read(user_data, data, 4); // mipMapCount + callbacks.read(user_data, data, 4); + + callbacks.read(user_data, data, 4); + uint32_t meta1fourcc = kinc_read_u32le(data); + callbacks.read(user_data, data, 4); // meta1key + callbacks.read(user_data, data, 4); // meta1size + callbacks.read(user_data, data, 4); + uint32_t meta1data = kinc_read_u32le(data); + + callbacks.read(user_data, data, 4); + uint32_t meta2fourcc = kinc_read_u32le(data); + callbacks.read(user_data, data, 4); // meta2key + callbacks.read(user_data, data, 4); // meta2size + callbacks.read(user_data, data, 4); + uint32_t meta2data = kinc_read_u32le(data); + + int w = 0; + int h = 0; + + if (meta1fourcc == 0) + w = meta1data; + if (meta1fourcc == 1) + h = meta1data; + if (meta2fourcc == 0) + w = meta2data; + if (meta2fourcc == 1) + h = meta2data; + + *width = w; + *height = h; + *compression = KINC_IMAGE_COMPRESSION_PVRTC; + *internalFormat = 0; + + *outputSize = (size_t)(ww * hh / 2); + callbacks.read(user_data, output, *outputSize); + return true; + } + else if (endsWith(filename, "hdr")) { + *compression = KINC_IMAGE_COMPRESSION_NONE; + *internalFormat = 0; + + stbi_io_callbacks stbi_callbacks; + stbi_callbacks.eof = stb_eof; + stbi_callbacks.read = stb_read; + stbi_callbacks.skip = stb_skip; + + read_data reader; + reader.callbacks = callbacks; + reader.user_data = user_data; + + int comp; + float *uncompressed = stbi_loadf_from_callbacks(&stbi_callbacks, &reader, width, height, &comp, 4); + if (uncompressed == NULL) { + kinc_log(KINC_LOG_LEVEL_ERROR, stbi_failure_reason()); + return false; + } + *outputSize = (size_t)(*width * *height * 16); + memcpy(output, uncompressed, *outputSize); + *format = KINC_IMAGE_FORMAT_RGBA128; + buffer_offset = 0; + return true; + } + else { + *compression = KINC_IMAGE_COMPRESSION_NONE; + *internalFormat = 0; + + stbi_io_callbacks stbi_callbacks; + stbi_callbacks.eof = stb_eof; + stbi_callbacks.read = stb_read; + stbi_callbacks.skip = stb_skip; + + read_data reader; + reader.callbacks = callbacks; + reader.user_data = user_data; + + int comp; + uint8_t *uncompressed = stbi_load_from_callbacks(&stbi_callbacks, &reader, width, height, &comp, 4); + if (uncompressed == NULL) { + kinc_log(KINC_LOG_LEVEL_ERROR, stbi_failure_reason()); + return false; + } + for (int y = 0; y < *height; ++y) { + for (int x = 0; x < *width; ++x) { + float r = uncompressed[y * *width * 4 + x * 4 + 0] / 255.0f; + float g = uncompressed[y * *width * 4 + x * 4 + 1] / 255.0f; + float b = uncompressed[y * *width * 4 + x * 4 + 2] / 255.0f; + float a = uncompressed[y * *width * 4 + x * 4 + 3] / 255.0f; + r *= a; + g *= a; + b *= a; + output[y * *width * 4 + x * 4 + 0] = (uint8_t)kinc_round(r * 255.0f); + output[y * *width * 4 + x * 4 + 1] = (uint8_t)kinc_round(g * 255.0f); + output[y * *width * 4 + x * 4 + 2] = (uint8_t)kinc_round(b * 255.0f); + output[y * *width * 4 + x * 4 + 3] = (uint8_t)kinc_round(a * 255.0f); + } + } + *outputSize = (size_t)(*width * *height * 4); + buffer_offset = 0; + return true; + } +} + +int kinc_image_format_sizeof(kinc_image_format_t format) { + switch (format) { + case KINC_IMAGE_FORMAT_RGBA128: + return 16; + case KINC_IMAGE_FORMAT_RGBA32: + case KINC_IMAGE_FORMAT_BGRA32: + return 4; + case KINC_IMAGE_FORMAT_RGBA64: + return 8; + case KINC_IMAGE_FORMAT_A32: + return 4; + case KINC_IMAGE_FORMAT_A16: + return 2; + case KINC_IMAGE_FORMAT_GREY8: + return 1; + case KINC_IMAGE_FORMAT_RGB24: + return 3; + } + return -1; +} + +// static bool formatIsFloatingPoint(kinc_image_format_t format) { +// return format == KINC_IMAGE_FORMAT_RGBA128 || format == KINC_IMAGE_FORMAT_RGBA64 || format == KINC_IMAGE_FORMAT_A32 || format == KINC_IMAGE_FORMAT_A16; +//} + +size_t kinc_image_init(kinc_image_t *image, void *memory, int width, int height, kinc_image_format_t format) { + return kinc_image_init3d(image, memory, width, height, 1, format); +} + +size_t kinc_image_init3d(kinc_image_t *image, void *memory, int width, int height, int depth, kinc_image_format_t format) { + image->width = width; + image->height = height; + image->depth = depth; + image->format = format; + image->compression = KINC_IMAGE_COMPRESSION_NONE; + image->data = memory; + return width * height * depth * kinc_image_format_sizeof(format); +} + +void kinc_image_init_from_bytes(kinc_image_t *image, void *data, int width, int height, kinc_image_format_t format) { + kinc_image_init_from_bytes3d(image, data, width, height, 1, format); +} + +void kinc_image_init_from_bytes3d(kinc_image_t *image, void *data, int width, int height, int depth, kinc_image_format_t format) { + image->width = width; + image->height = height; + image->depth = depth; + image->format = format; + image->compression = KINC_IMAGE_COMPRESSION_NONE; + image->data = data; +} + +static size_t read_callback(void *user_data, void *data, size_t size) { + return kinc_file_reader_read((kinc_file_reader_t *)user_data, data, size); +} + +static size_t size_callback(void *user_data) { + return kinc_file_reader_size((kinc_file_reader_t *)user_data); +} + +static size_t pos_callback(void *user_data) { + return kinc_file_reader_pos((kinc_file_reader_t *)user_data); +} + +static void seek_callback(void *user_data, size_t pos) { + kinc_file_reader_seek((kinc_file_reader_t *)user_data, pos); +} + +struct kinc_internal_image_memory { + uint8_t *data; + size_t size; + size_t offset; +}; + +static size_t memory_read_callback(void *user_data, void *data, size_t size) { + struct kinc_internal_image_memory *memory = (struct kinc_internal_image_memory *)user_data; + size_t read_size = memory->size - memory->offset < size ? memory->size - memory->offset : size; + memcpy(data, &memory->data[memory->offset], read_size); + memory->offset += read_size; + return read_size; +} + +static size_t memory_size_callback(void *user_data) { + struct kinc_internal_image_memory *memory = (struct kinc_internal_image_memory *)user_data; + return memory->size; +} + +static size_t memory_pos_callback(void *user_data) { + struct kinc_internal_image_memory *memory = (struct kinc_internal_image_memory *)user_data; + return memory->offset; +} + +static void memory_seek_callback(void *user_data, size_t pos) { + struct kinc_internal_image_memory *memory = (struct kinc_internal_image_memory *)user_data; + memory->offset = pos; +} + +size_t kinc_image_size_from_callbacks(kinc_image_read_callbacks_t callbacks, void *user_data, const char *filename) { + return loadImageSize(callbacks, user_data, filename); +} + +size_t kinc_image_size_from_file(const char *filename) { + kinc_file_reader_t reader; + if (kinc_file_reader_open(&reader, filename, KINC_FILE_TYPE_ASSET)) { + kinc_image_read_callbacks_t callbacks; + callbacks.read = read_callback; + callbacks.size = size_callback; + callbacks.pos = pos_callback; + callbacks.seek = seek_callback; + + size_t dataSize = loadImageSize(callbacks, &reader, filename); + kinc_file_reader_close(&reader); + return dataSize; + } + return 0; +} + +size_t kinc_image_size_from_encoded_bytes(void *data, size_t data_size, const char *format) { + kinc_image_read_callbacks_t callbacks; + callbacks.read = memory_read_callback; + callbacks.size = memory_size_callback; + callbacks.pos = memory_pos_callback; + callbacks.seek = memory_seek_callback; + + struct kinc_internal_image_memory image_memory; + image_memory.data = (uint8_t *)data; + image_memory.size = data_size; + image_memory.offset = 0; + + return loadImageSize(callbacks, &image_memory, format); +} + +size_t kinc_image_init_from_callbacks(kinc_image_t *image, void *memory, kinc_image_read_callbacks_t callbacks, void *user_data, const char *filename) { + size_t dataSize = 0; + loadImage(callbacks, user_data, filename, memory, &dataSize, &image->width, &image->height, &image->compression, &image->format, &image->internal_format); + image->data = memory; + image->data_size = dataSize; + return dataSize; +} + +size_t kinc_image_init_from_file(kinc_image_t *image, void *memory, const char *filename) { + kinc_file_reader_t reader; + if (kinc_file_reader_open(&reader, filename, KINC_FILE_TYPE_ASSET)) { + kinc_image_read_callbacks_t callbacks; + callbacks.read = read_callback; + callbacks.size = size_callback; + callbacks.pos = pos_callback; + callbacks.seek = seek_callback; + + size_t dataSize = 0; + loadImage(callbacks, &reader, filename, memory, &dataSize, &image->width, &image->height, &image->compression, &image->format, &image->internal_format); + kinc_file_reader_close(&reader); + image->data = memory; + image->data_size = dataSize; + image->depth = 1; + return dataSize; + } + return 0; +} + +size_t kinc_image_init_from_encoded_bytes(kinc_image_t *image, void *memory, void *data, size_t data_size, const char *format) { + kinc_image_read_callbacks_t callbacks; + callbacks.read = memory_read_callback; + callbacks.size = memory_size_callback; + callbacks.pos = memory_pos_callback; + callbacks.seek = memory_seek_callback; + + struct kinc_internal_image_memory image_memory; + image_memory.data = (uint8_t *)data; + image_memory.size = data_size; + image_memory.offset = 0; + + size_t dataSize = 0; + loadImage(callbacks, &image_memory, format, memory, &dataSize, &image->width, &image->height, &image->compression, &image->format, &image->internal_format); + image->data = memory; + image->data_size = dataSize; + image->depth = 1; + return dataSize; +} + +void kinc_image_destroy(kinc_image_t *image) { + // user has to free the data + image->data = NULL; +} + +uint32_t kinc_image_at(kinc_image_t *image, int x, int y) { + if (image->data == NULL) { + return 0; + } + else { + return *(uint32_t *)&((uint8_t *)image->data)[image->width * kinc_image_format_sizeof(image->format) * y + x * kinc_image_format_sizeof(image->format)]; + } +} + +void *kinc_image_at_raw(kinc_image_t *image, int x, int y) { + if (image->data == NULL) { + return NULL; + } + else { + return &((uint8_t *)image->data)[image->width * kinc_image_format_sizeof(image->format) * y + x * kinc_image_format_sizeof(image->format)]; + } +} + +uint8_t *kinc_image_get_pixels(kinc_image_t *image) { + return (uint8_t *)image->data; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/input/acceleration.h b/armorcore/sources/kinc/input/acceleration.h new file mode 100644 index 000000000..d01069d16 --- /dev/null +++ b/armorcore/sources/kinc/input/acceleration.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +/*! \file acceleration.h + \brief Provides data provided by acceleration-sensors. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/// +/// Sets the acceleration-callback which is called with measured acceleration-data in three dimensions. +/// +/// The callback +void kinc_acceleration_set_callback(void (*value)(float /*x*/, float /*y*/, float /*z*/)); + +void kinc_internal_on_acceleration(float x, float y, float z); + +#ifdef KINC_IMPLEMENTATION_INPUT +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#include +#include + +void (*acceleration_callback)(float /*x*/, float /*y*/, float /*z*/) = NULL; + +void kinc_acceleration_set_callback(void (*value)(float /*x*/, float /*y*/, float /*z*/)) { + acceleration_callback = value; +} + +void kinc_internal_on_acceleration(float x, float y, float z) { + if (acceleration_callback != NULL) { + acceleration_callback(x, y, z); + } +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/input/gamepad.h b/armorcore/sources/kinc/input/gamepad.h new file mode 100644 index 000000000..0f54961a3 --- /dev/null +++ b/armorcore/sources/kinc/input/gamepad.h @@ -0,0 +1,143 @@ +#pragma once + +#include + +/*! \file gamepad.h + \brief Provides gamepad-support. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#define KINC_GAMEPAD_MAX_COUNT 12 + +/// +/// Sets the gamepad-connect-callback which is called when a gamepad is connected. +/// +/// The callback +/// Userdata you will receive back as the 2nd callback parameter +void kinc_gamepad_set_connect_callback(void (*value)(int /*gamepad*/, void * /*userdata*/), void *userdata); + +/// +/// Sets the gamepad-disconnect-callback which is called when a gamepad is disconnected. +/// +/// The callback +/// Userdata you will receive back as the 2nd callback parameter +void kinc_gamepad_set_disconnect_callback(void (*value)(int /*gamepad*/, void * /*userdata*/), void *userdata); + +/// +/// Sets the gamepad-axis-callback which is called with data about changing gamepad-sticks. +/// +/// The callback +/// Userdata you will receive back as the 4th callback parameter +void kinc_gamepad_set_axis_callback(void (*value)(int /*gamepad*/, int /*axis*/, float /*value*/, void * /*userdata*/), void *userdata); + +/// +/// Sets the gamepad-button-callback which is called with data about changing gamepad-buttons. +/// +/// The callback +/// Userdata you will receive back as the 4th callback parameter +void kinc_gamepad_set_button_callback(void (*value)(int /*gamepad*/, int /*button*/, float /*value*/, void * /*userdata*/), void *userdata); + +/// +/// Returns a vendor-name for a gamepad. +/// +/// The index of the gamepad for which to receive the vendor-name +/// The vendor-name +const char *kinc_gamepad_vendor(int gamepad); + +/// +/// Returns a name for a gamepad. +/// +/// The index of the gamepad for which to receive the name +/// The gamepad's name +const char *kinc_gamepad_product_name(int gamepad); + +/// +/// Checks whether a gamepad is connected. +/// +/// The index of the gamepad which's connection will be checked +/// Whether a gamepad is connected for the gamepad-index +bool kinc_gamepad_connected(int gamepad); + +/// +/// Rumbles a gamepad. Careful here because it might just fall off your table. +/// +/// The index of the gamepad to rumble +/// Rumble-strength for the left motor between 0 and 1 +/// Rumble-strength for the right motor between 0 and 1 +void kinc_gamepad_rumble(int gamepad, float left, float right); + +void kinc_internal_gamepad_trigger_connect(int gamepad); +void kinc_internal_gamepad_trigger_disconnect(int gamepad); +void kinc_internal_gamepad_trigger_axis(int gamepad, int axis, float value); +void kinc_internal_gamepad_trigger_button(int gamepad, int button, float value); + +#ifdef KINC_IMPLEMENTATION_INPUT +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#include +#include + +static void (*gamepad_connect_callback)(int /*gamepad*/, void * /*userdata*/) = NULL; +static void *gamepad_connect_callback_userdata = NULL; +static void (*gamepad_disconnect_callback)(int /*gamepad*/, void * /*userdata*/) = NULL; +static void *gamepad_disconnect_callback_userdata = NULL; +static void (*gamepad_axis_callback)(int /*gamepad*/, int /*axis*/, float /*value*/, void * /*userdata*/) = NULL; +static void *gamepad_axis_callback_userdata = NULL; +static void (*gamepad_button_callback)(int /*gamepad*/, int /*button*/, float /*value*/, void * /*userdata*/) = NULL; +static void *gamepad_button_callback_userdata = NULL; + +void kinc_gamepad_set_connect_callback(void (*value)(int /*gamepad*/, void * /*userdata*/), void *userdata) { + gamepad_connect_callback = value; + gamepad_connect_callback_userdata = userdata; +} + +void kinc_gamepad_set_disconnect_callback(void (*value)(int /*gamepad*/, void * /*userdata*/), void *userdata) { + gamepad_disconnect_callback = value; + gamepad_disconnect_callback_userdata = userdata; +} + +void kinc_gamepad_set_axis_callback(void (*value)(int /*gamepad*/, int /*axis*/, float /*value*/, void * /*userdata*/), void *userdata) { + gamepad_axis_callback = value; + gamepad_axis_callback_userdata = userdata; +} + +void kinc_gamepad_set_button_callback(void (*value)(int /*gamepad*/, int /*button*/, float /*value*/, void * /*userdata*/), void *userdata) { + gamepad_button_callback = value; + gamepad_button_callback_userdata = userdata; +} + +void kinc_internal_gamepad_trigger_connect(int gamepad) { + if (gamepad_connect_callback != NULL) { + gamepad_connect_callback(gamepad, gamepad_connect_callback_userdata); + } +} + +void kinc_internal_gamepad_trigger_disconnect(int gamepad) { + if (gamepad_disconnect_callback != NULL) { + gamepad_disconnect_callback(gamepad, gamepad_disconnect_callback_userdata); + } +} + +void kinc_internal_gamepad_trigger_axis(int gamepad, int axis, float value) { + if (gamepad_axis_callback != NULL) { + gamepad_axis_callback(gamepad, axis, value, gamepad_axis_callback_userdata); + } +} + +void kinc_internal_gamepad_trigger_button(int gamepad, int button, float value) { + if (gamepad_button_callback != NULL) { + gamepad_button_callback(gamepad, button, value, gamepad_button_callback_userdata); + } +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/input/inputunit.c b/armorcore/sources/kinc/input/inputunit.c new file mode 100644 index 000000000..baf13b105 --- /dev/null +++ b/armorcore/sources/kinc/input/inputunit.c @@ -0,0 +1,9 @@ +#define KINC_IMPLEMENTATION_INPUT + +#include "acceleration.h" +#include "gamepad.h" +#include "keyboard.h" +#include "mouse.h" +#include "pen.h" +#include "rotation.h" +#include "surface.h" diff --git a/armorcore/sources/kinc/input/keyboard.h b/armorcore/sources/kinc/input/keyboard.h new file mode 100644 index 000000000..14c907526 --- /dev/null +++ b/armorcore/sources/kinc/input/keyboard.h @@ -0,0 +1,294 @@ +#pragma once + +#include + +/*! \file keyboard.h + \brief Provides keyboard-support. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#define KINC_KEY_UNKNOWN 0 +#define KINC_KEY_BACK 1 // Android +#define KINC_KEY_CANCEL 3 +#define KINC_KEY_HELP 6 +#define KINC_KEY_BACKSPACE 8 +#define KINC_KEY_TAB 9 +#define KINC_KEY_CLEAR 12 +#define KINC_KEY_RETURN 13 +#define KINC_KEY_SHIFT 16 +#define KINC_KEY_CONTROL 17 +#define KINC_KEY_ALT 18 +#define KINC_KEY_PAUSE 19 +#define KINC_KEY_CAPS_LOCK 20 +#define KINC_KEY_KANA 21 +#define KINC_KEY_HANGUL 21 +#define KINC_KEY_EISU 22 +#define KINC_KEY_JUNJA 23 +#define KINC_KEY_FINAL 24 +#define KINC_KEY_HANJA 25 +#define KINC_KEY_KANJI 25 +#define KINC_KEY_ESCAPE 27 +#define KINC_KEY_CONVERT 28 +#define KINC_KEY_NON_CONVERT 29 +#define KINC_KEY_ACCEPT 30 +#define KINC_KEY_MODE_CHANGE 31 +#define KINC_KEY_SPACE 32 +#define KINC_KEY_PAGE_UP 33 +#define KINC_KEY_PAGE_DOWN 34 +#define KINC_KEY_END 35 +#define KINC_KEY_HOME 36 +#define KINC_KEY_LEFT 37 +#define KINC_KEY_UP 38 +#define KINC_KEY_RIGHT 39 +#define KINC_KEY_DOWN 40 +#define KINC_KEY_SELECT 41 +#define KINC_KEY_PRINT 42 +#define KINC_KEY_EXECUTE 43 +#define KINC_KEY_PRINT_SCREEN 44 +#define KINC_KEY_INSERT 45 +#define KINC_KEY_DELETE 46 +#define KINC_KEY_0 48 +#define KINC_KEY_1 49 +#define KINC_KEY_2 50 +#define KINC_KEY_3 51 +#define KINC_KEY_4 52 +#define KINC_KEY_5 53 +#define KINC_KEY_6 54 +#define KINC_KEY_7 55 +#define KINC_KEY_8 56 +#define KINC_KEY_9 57 +#define KINC_KEY_COLON 58 +#define KINC_KEY_SEMICOLON 59 +#define KINC_KEY_LESS_THAN 60 +#define KINC_KEY_EQUALS 61 +#define KINC_KEY_GREATER_THAN 62 +#define KINC_KEY_QUESTIONMARK 63 +#define KINC_KEY_AT 64 +#define KINC_KEY_A 65 +#define KINC_KEY_B 66 +#define KINC_KEY_C 67 +#define KINC_KEY_D 68 +#define KINC_KEY_E 69 +#define KINC_KEY_F 70 +#define KINC_KEY_G 71 +#define KINC_KEY_H 72 +#define KINC_KEY_I 73 +#define KINC_KEY_J 74 +#define KINC_KEY_K 75 +#define KINC_KEY_L 76 +#define KINC_KEY_M 77 +#define KINC_KEY_N 78 +#define KINC_KEY_O 79 +#define KINC_KEY_P 80 +#define KINC_KEY_Q 81 +#define KINC_KEY_R 82 +#define KINC_KEY_S 83 +#define KINC_KEY_T 84 +#define KINC_KEY_U 85 +#define KINC_KEY_V 86 +#define KINC_KEY_W 87 +#define KINC_KEY_X 88 +#define KINC_KEY_Y 89 +#define KINC_KEY_Z 90 +#define KINC_KEY_WIN 91 +#define KINC_KEY_CONTEXT_MENU 93 +#define KINC_KEY_SLEEP 95 +#define KINC_KEY_NUMPAD_0 96 +#define KINC_KEY_NUMPAD_1 97 +#define KINC_KEY_NUMPAD_2 98 +#define KINC_KEY_NUMPAD_3 99 +#define KINC_KEY_NUMPAD_4 100 +#define KINC_KEY_NUMPAD_5 101 +#define KINC_KEY_NUMPAD_6 102 +#define KINC_KEY_NUMPAD_7 103 +#define KINC_KEY_NUMPAD_8 104 +#define KINC_KEY_NUMPAD_9 105 +#define KINC_KEY_MULTIPLY 106 +#define KINC_KEY_ADD 107 +#define KINC_KEY_SEPARATOR 108 +#define KINC_KEY_SUBTRACT 109 +#define KINC_KEY_DECIMAL 110 +#define KINC_KEY_DIVIDE 111 +#define KINC_KEY_F1 112 +#define KINC_KEY_F2 113 +#define KINC_KEY_F3 114 +#define KINC_KEY_F4 115 +#define KINC_KEY_F5 116 +#define KINC_KEY_F6 117 +#define KINC_KEY_F7 118 +#define KINC_KEY_F8 119 +#define KINC_KEY_F9 120 +#define KINC_KEY_F10 121 +#define KINC_KEY_F11 122 +#define KINC_KEY_F12 123 +#define KINC_KEY_F13 124 +#define KINC_KEY_F14 125 +#define KINC_KEY_F15 126 +#define KINC_KEY_F16 127 +#define KINC_KEY_F17 128 +#define KINC_KEY_F18 129 +#define KINC_KEY_F19 130 +#define KINC_KEY_F20 131 +#define KINC_KEY_F21 132 +#define KINC_KEY_F22 133 +#define KINC_KEY_F23 134 +#define KINC_KEY_F24 135 +#define KINC_KEY_NUM_LOCK 144 +#define KINC_KEY_SCROLL_LOCK 145 +#define KINC_KEY_WIN_OEM_FJ_JISHO 146 +#define KINC_KEY_WIN_OEM_FJ_MASSHOU 147 +#define KINC_KEY_WIN_OEM_FJ_TOUROKU 148 +#define KINC_KEY_WIN_OEM_FJ_LOYA 149 +#define KINC_KEY_WIN_OEM_FJ_ROYA 150 +#define KINC_KEY_CIRCUMFLEX 160 +#define KINC_KEY_EXCLAMATION 161 +#define KINC_KEY_DOUBLE_QUOTE 162 +#define KINC_KEY_HASH 163 +#define KINC_KEY_DOLLAR 164 +#define KINC_KEY_PERCENT 165 +#define KINC_KEY_AMPERSAND 166 +#define KINC_KEY_UNDERSCORE 167 +#define KINC_KEY_OPEN_PAREN 168 +#define KINC_KEY_CLOSE_PAREN 169 +#define KINC_KEY_ASTERISK 170 +#define KINC_KEY_PLUS 171 +#define KINC_KEY_PIPE 172 +#define KINC_KEY_HYPHEN_MINUS 173 +#define KINC_KEY_OPEN_CURLY_BRACKET 174 +#define KINC_KEY_CLOSE_CURLY_BRACKET 175 +#define KINC_KEY_TILDE 176 +#define KINC_KEY_VOLUME_MUTE 181 +#define KINC_KEY_VOLUME_DOWN 182 +#define KINC_KEY_VOLUME_UP 183 +#define KINC_KEY_COMMA 188 +#define KINC_KEY_PERIOD 190 +#define KINC_KEY_SLASH 191 +#define KINC_KEY_BACK_QUOTE 192 +#define KINC_KEY_OPEN_BRACKET 219 +#define KINC_KEY_BACK_SLASH 220 +#define KINC_KEY_CLOSE_BRACKET 221 +#define KINC_KEY_QUOTE 222 +#define KINC_KEY_META 224 +#define KINC_KEY_ALT_GR 225 +#define KINC_KEY_WIN_ICO_HELP 227 +#define KINC_KEY_WIN_ICO_00 228 +#define KINC_KEY_WIN_ICO_CLEAR 230 +#define KINC_KEY_WIN_OEM_RESET 233 +#define KINC_KEY_WIN_OEM_JUMP 234 +#define KINC_KEY_WIN_OEM_PA1 235 +#define KINC_KEY_WIN_OEM_PA2 236 +#define KINC_KEY_WIN_OEM_PA3 237 +#define KINC_KEY_WIN_OEM_WSCTRL 238 +#define KINC_KEY_WIN_OEM_CUSEL 239 +#define KINC_KEY_WIN_OEM_ATTN 240 +#define KINC_KEY_WIN_OEM_FINISH 241 +#define KINC_KEY_WIN_OEM_COPY 242 +#define KINC_KEY_WIN_OEM_AUTO 243 +#define KINC_KEY_WIN_OEM_ENLW 244 +#define KINC_KEY_WIN_OEM_BACK_TAB 245 +#define KINC_KEY_ATTN 246 +#define KINC_KEY_CRSEL 247 +#define KINC_KEY_EXSEL 248 +#define KINC_KEY_EREOF 249 +#define KINC_KEY_PLAY 250 +#define KINC_KEY_ZOOM 251 +#define KINC_KEY_PA1 253 +#define KINC_KEY_WIN_OEM_CLEAR 254 + +/// +/// Show the keyboard if the system is using a software-keyboard. +/// +void kinc_keyboard_show(void); + +/// +/// Hide the keyboard if the system is using a software-keyboard. +/// +void kinc_keyboard_hide(void); + +/// +/// Figure out whether the keyboard is currently shown if the system is using a software-keyboard. +/// +/// Whether the keyboard is currently shown +bool kinc_keyboard_active(void); + +/// +/// Sets the keyboard-key-down-callback which is called with a key-code when a key goes down. Do not use this for text-input, that's what the key-press-callback +/// is here for. +/// +/// The callback +void kinc_keyboard_set_key_down_callback(void (*value)(int /*key_code*/, void * /*data*/), void *data); + +/// +/// Sets the keyboard-key-up-callback which is called with a key-code when a key goes up. Do not use this for text-input, that's what the key-press-callback is +/// here for. +/// +/// The callback +void kinc_keyboard_set_key_up_callback(void (*value)(int /*key_code*/, void * /*data*/), void *data); + +/// +/// Sets the keyboard-key-press-callback which is called when the system decides that a character came in via the keyboard. Use this for text-input. +/// +/// The callback +void kinc_keyboard_set_key_press_callback(void (*value)(unsigned /*character*/, void * /*data*/), void *data); + +void kinc_internal_keyboard_trigger_key_down(int key_code); +void kinc_internal_keyboard_trigger_key_up(int key_code); +void kinc_internal_keyboard_trigger_key_press(unsigned character); + +#ifdef KINC_IMPLEMENTATION_INPUT +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#include +#include + +static void (*keyboard_key_down_callback)(int /*key_code*/, void * /*data*/) = NULL; +static void *keyboard_key_down_callback_data = NULL; +static void (*keyboard_key_up_callback)(int /*key_code*/, void * /*data*/) = NULL; +static void *keyboard_key_up_callback_data = NULL; +static void (*keyboard_key_press_callback)(unsigned /*character*/, void * /*data*/) = NULL; +static void *keyboard_key_press_callback_data = NULL; + +void kinc_keyboard_set_key_down_callback(void (*value)(int /*key_code*/, void * /*data*/), void *data) { + keyboard_key_down_callback = value; + keyboard_key_down_callback_data = data; +} + +void kinc_keyboard_set_key_up_callback(void (*value)(int /*key_code*/, void * /*data*/), void *data) { + keyboard_key_up_callback = value; + keyboard_key_up_callback_data = data; +} + +void kinc_keyboard_set_key_press_callback(void (*value)(unsigned /*character*/, void * /*data*/), void *data) { + keyboard_key_press_callback = value; + keyboard_key_press_callback_data = data; +} + +void kinc_internal_keyboard_trigger_key_down(int key_code) { + if (keyboard_key_down_callback != NULL) { + keyboard_key_down_callback(key_code, keyboard_key_down_callback_data); + } +} + +void kinc_internal_keyboard_trigger_key_up(int key_code) { + if (keyboard_key_up_callback != NULL) { + keyboard_key_up_callback(key_code, keyboard_key_up_callback_data); + } +} + +void kinc_internal_keyboard_trigger_key_press(unsigned character) { + if (keyboard_key_press_callback != NULL) { + keyboard_key_press_callback(character, keyboard_key_press_callback_data); + } +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/input/mouse.h b/armorcore/sources/kinc/input/mouse.h new file mode 100644 index 000000000..6a4b3772c --- /dev/null +++ b/armorcore/sources/kinc/input/mouse.h @@ -0,0 +1,297 @@ +#pragma once + +#include + +#include + +/*! \file mouse.h + \brief Provides mouse-support. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#define KINC_MOUSE_LEFT 0 +#define KINC_MOUSE_RIGHT 1 +#define KINC_MOUSE_MIDDLE 2 +// eg backward sidebutton +#define KINC_MOUSE_EXTRA1 3 +// eg forward sidebutton +#define KINC_MOUSE_EXTRA2 4 + +/// +/// Sets the mouse-press-callback which is called when a mouse-button is pressed. +/// The button callback argument will usually contain one of the KINC_MOUSE_* values, +/// but can contain higher values on certain platforms when mice with a lot of buttons are used. +/// +/// The callback +void kinc_mouse_set_press_callback(void (*value)(int /*window*/, int /*button*/, int /*x*/, int /*y*/, void * /*data*/), void *data); + +/// +/// Sets the mouse-release-callback which is called when a mouse-button is released. +/// The button callback argument will usually contain one of the KINC_MOUSE_* values, +/// but can contain higher values on certain platforms when mice with a lot of buttons are used. +/// +/// The callback +void kinc_mouse_set_release_callback(void (*value)(int /*window*/, int /*button*/, int /*x*/, int /*y*/, void * /*data*/), void *data); + +/// +/// Sets the mouse-move-callback which is called when the mouse is moved. +/// +/// The callback +void kinc_mouse_set_move_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, int /*movement_x*/, int /*movement_y*/, void * /*data*/), + void *data); + +/// +/// Sets the mouse-scroll-callback which is called when the mouse wheel is spinning. +/// +/// The callback +void kinc_mouse_set_scroll_callback(void (*value)(int /*window*/, int /*delta*/, void * /*data*/), void *data); + +/// +/// Sets the mouse-enter-window-callback which is called when the mouse-cursor enters the application-window. +/// +/// The callback +void kinc_mouse_set_enter_window_callback(void (*value)(int /*window*/, void * /*data*/), void *data); + +/// +/// Sets the mouse-leave-window-callback which is called when the mouse-cursor leaves the application-window. +/// +/// The callback +void kinc_mouse_set_leave_window_callback(void (*value)(int /*window*/, void * /*data*/), void *data); + +/// +/// Figures out whether mouse-locking is supported. +/// +/// Whether mouse-locking is supported +bool kinc_mouse_can_lock(void); + +/// +/// Figures out whether the mouse is currently locked. +/// +/// Whether the mouse is currently locked +bool kinc_mouse_is_locked(void); + +/// +/// Locks the mouse to a window. +/// +/// The window to lock the mouse to +void kinc_mouse_lock(int window); + +/// +/// Unlocks the mouse. +/// +/// +/// +void kinc_mouse_unlock(void); + +/// +/// Change the cursor-image to something that's semi-randomly based on the provided int. +/// +/// Defines what the cursor is changed to - somehow +void kinc_mouse_set_cursor(int cursor); + +/// +/// Shows the mouse-cursor. +/// +void kinc_mouse_show(void); + +/// +/// Hides the mouse-cursor. +/// +void kinc_mouse_hide(void); + +/// +/// Manually sets the mouse-cursor-position. +/// +/// The window to place the cursor in +/// The x-position inside the window to place the cursor at +/// The y-position inside the window to place the cursor at +void kinc_mouse_set_position(int window, int x, int y); + +/// +/// Gets the current mouse-position relative to a window. +/// +/// The window to base the returned position on +/// A pointer where the cursor's x-position is put into +/// A pointer where the cursor's y-position is put into +void kinc_mouse_get_position(int window, int *x, int *y); + +void kinc_internal_mouse_trigger_press(int window, int button, int x, int y); +void kinc_internal_mouse_trigger_release(int window, int button, int x, int y); +void kinc_internal_mouse_trigger_move(int window, int x, int y); +void kinc_internal_mouse_trigger_scroll(int window, int delta); +void kinc_internal_mouse_trigger_enter_window(int window); +void kinc_internal_mouse_trigger_leave_window(int window); +void kinc_internal_mouse_lock(int window); +void kinc_internal_mouse_unlock(void); +void kinc_internal_mouse_window_activated(int window); +void kinc_internal_mouse_window_deactivated(int window); + +#ifdef KINC_IMPLEMENTATION_INPUT +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#undef KINC_IMPLEMENTATION +#include +#define KINC_IMPLEMENTATION + +#include +#include + +static void (*mouse_press_callback)(int /*window*/, int /*button*/, int /*x*/, int /*y*/, void * /*data*/) = NULL; +static void *mouse_press_callback_data = NULL; +static void (*mouse_release_callback)(int /*window*/, int /*button*/, int /*x*/, int /*y*/, void * /*data*/) = NULL; +static void *mouse_release_callback_data = NULL; +static void (*mouse_move_callback)(int /*window*/, int /*x*/, int /*y*/, int /*movementX*/, int /*movementY*/, void * /*data*/) = NULL; +static void *mouse_move_callback_data = NULL; +static void (*mouse_scroll_callback)(int /*window*/, int /*delta*/, void * /*data*/) = NULL; +static void *mouse_scroll_callback_data = NULL; +static void (*mouse_enter_window_callback)(int /*window*/, void * /*data*/) = NULL; +static void *mouse_enter_window_callback_data = NULL; +static void (*mouse_leave_window_callback)(int /*window*/, void * /*data*/) = NULL; +static void *mouse_leave_window_callback_data = NULL; + +void kinc_mouse_set_press_callback(void (*value)(int /*window*/, int /*button*/, int /*x*/, int /*y*/, void * /*data*/), void *data) { + mouse_press_callback = value; + mouse_press_callback_data = data; +} + +void kinc_mouse_set_release_callback(void (*value)(int /*window*/, int /*button*/, int /*x*/, int /*y*/, void * /*data*/), void *data) { + mouse_release_callback = value; + mouse_release_callback_data = data; +} + +void kinc_mouse_set_move_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, int /*movement_x*/, int /*movement_y*/, void * /*data*/), void *data) { + mouse_move_callback = value; + mouse_move_callback_data = data; +} + +void kinc_mouse_set_scroll_callback(void (*value)(int /*window*/, int /*delta*/, void * /*data*/), void *data) { + mouse_scroll_callback = value; + mouse_scroll_callback_data = data; +} + +void kinc_mouse_set_enter_window_callback(void (*value)(int /*window*/, void * /*data*/), void *data) { + mouse_enter_window_callback = value; + mouse_enter_window_callback_data = data; +} + +void kinc_mouse_set_leave_window_callback(void (*value)(int /*window*/, void * /*data*/), void *data) { + mouse_leave_window_callback = value; + mouse_leave_window_callback_data = data; +} + +void kinc_internal_mouse_trigger_release(int window, int button, int x, int y) { + if (mouse_release_callback != NULL) { + mouse_release_callback(window, button, x, y, mouse_release_callback_data); + } +} + +void kinc_internal_mouse_trigger_scroll(int window, int delta) { + if (mouse_scroll_callback != NULL) { + mouse_scroll_callback(window, delta, mouse_scroll_callback_data); + } +} + +void kinc_internal_mouse_trigger_enter_window(int window) { + if (mouse_enter_window_callback != NULL) { + mouse_enter_window_callback(window, mouse_enter_window_callback_data); + } +} + +void kinc_internal_mouse_trigger_leave_window(int window) { + if (mouse_leave_window_callback != NULL) { + mouse_leave_window_callback(window, mouse_leave_window_callback_data); + } +} + +void kinc_internal_mouse_window_activated(int window) { + if (kinc_mouse_is_locked()) { + kinc_internal_mouse_lock(window); + } +} +void kinc_internal_mouse_window_deactivated(int window) { + if (kinc_mouse_is_locked()) { + kinc_internal_mouse_unlock(); + } +} + +// TODO: handle state per window +static bool moved = false; +static bool locked = false; +static int preLockWindow = 0; +static int preLockX = 0; +static int preLockY = 0; +static int centerX = 0; +static int centerY = 0; +static int lastX = 0; +static int lastY = 0; + +void kinc_internal_mouse_trigger_press(int window, int button, int x, int y) { + lastX = x; + lastY = y; + if (mouse_press_callback != NULL) { + mouse_press_callback(window, button, x, y, mouse_press_callback_data); + } +} + +void kinc_internal_mouse_trigger_move(int window, int x, int y) { + int movementX = 0; + int movementY = 0; + if (kinc_mouse_is_locked()) { + movementX = x - centerX; + movementY = y - centerY; + if (movementX != 0 || movementY != 0) { + kinc_mouse_set_position(window, centerX, centerY); + x = centerX; + y = centerY; + } + } + else if (moved) { + movementX = x - lastX; + movementY = y - lastY; + } + moved = true; + + lastX = x; + lastY = y; + if (mouse_move_callback != NULL && (movementX != 0 || movementY != 0)) { + mouse_move_callback(window, x, y, movementX, movementY, mouse_move_callback_data); + } +} + +bool kinc_mouse_is_locked(void) { + return locked; +} + +void kinc_mouse_lock(int window) { + if (!kinc_mouse_can_lock()) { + return; + } + locked = true; + kinc_internal_mouse_lock(window); + kinc_mouse_get_position(window, &preLockX, &preLockY); + centerX = kinc_window_width(window) / 2; + centerY = kinc_window_height(window) / 2; + kinc_mouse_set_position(window, centerX, centerY); +} + +void kinc_mouse_unlock(void) { + if (!kinc_mouse_can_lock()) { + return; + } + moved = false; + locked = false; + kinc_internal_mouse_unlock(); + kinc_mouse_set_position(preLockWindow, preLockX, preLockY); +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/input/pen.h b/armorcore/sources/kinc/input/pen.h new file mode 100644 index 000000000..70dd9cb7d --- /dev/null +++ b/armorcore/sources/kinc/input/pen.h @@ -0,0 +1,138 @@ +#pragma once + +#include + +/*! \file pen.h + \brief Provides pen-support. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/// +/// Sets the pen-press-callback which is called when the pen is touching the drawing-surface. +/// +/// The callback +void kinc_pen_set_press_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)); + +/// +/// Sets the pen-move-callback which is called when the pen is moved. +/// +/// The callback +void kinc_pen_set_move_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)); + +/// +/// Sets the pen-release-callback which is called when the pen is moved away from the drawing-surface. +/// +/// The callback +void kinc_pen_set_release_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)); + +/// +/// Sets the eraser-press-callback which is called when the pen is touching the drawing-surface in eraser-mode. +/// +/// The callback +void kinc_eraser_set_press_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)); + +/// +/// Sets the eraser-move-callback which is called when the pen is moved while in eraser-mode. +/// +/// The callback +void kinc_eraser_set_move_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)); + +/// +/// Sets the eraser-release-callback which is called when the pen is moved away from the drawing-surface when in eraser-mode. +/// +/// The callback +void kinc_eraser_set_release_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)); + +void kinc_internal_pen_trigger_move(int window, int x, int y, float pressure); +void kinc_internal_pen_trigger_press(int window, int x, int y, float pressure); +void kinc_internal_pen_trigger_release(int window, int x, int y, float pressure); + +void kinc_internal_eraser_trigger_move(int window, int x, int y, float pressure); +void kinc_internal_eraser_trigger_press(int window, int x, int y, float pressure); +void kinc_internal_eraser_trigger_release(int window, int x, int y, float pressure); + +#ifdef KINC_IMPLEMENTATION_INPUT +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#include +#include + +static void (*pen_press_callback)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/) = NULL; +static void (*pen_move_callback)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/) = NULL; +static void (*pen_release_callback)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/) = NULL; + +static void (*eraser_press_callback)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/) = NULL; +static void (*eraser_move_callback)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/) = NULL; +static void (*eraser_release_callback)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/) = NULL; + +void kinc_pen_set_press_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)) { + pen_press_callback = value; +} + +void kinc_pen_set_move_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)) { + pen_move_callback = value; +} + +void kinc_pen_set_release_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)) { + pen_release_callback = value; +} + +void kinc_eraser_set_press_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)) { + eraser_press_callback = value; +} + +void kinc_eraser_set_move_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)) { + eraser_move_callback = value; +} + +void kinc_eraser_set_release_callback(void (*value)(int /*window*/, int /*x*/, int /*y*/, float /*pressure*/)) { + eraser_release_callback = value; +} + +void kinc_internal_pen_trigger_press(int window, int x, int y, float pressure) { + if (pen_press_callback != NULL) { + pen_press_callback(window, x, y, pressure); + } +} + +void kinc_internal_pen_trigger_move(int window, int x, int y, float pressure) { + if (pen_move_callback != NULL) { + pen_move_callback(window, x, y, pressure); + } +} + +void kinc_internal_pen_trigger_release(int window, int x, int y, float pressure) { + if (pen_release_callback != NULL) { + pen_release_callback(window, x, y, pressure); + } +} + +void kinc_internal_eraser_trigger_press(int window, int x, int y, float pressure) { + if (eraser_press_callback != NULL) { + eraser_press_callback(window, x, y, pressure); + } +} + +void kinc_internal_eraser_trigger_move(int window, int x, int y, float pressure) { + if (eraser_move_callback != NULL) { + eraser_move_callback(window, x, y, pressure); + } +} + +void kinc_internal_eraser_trigger_release(int window, int x, int y, float pressure) { + if (eraser_release_callback != NULL) { + eraser_release_callback(window, x, y, pressure); + } +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/input/rotation.h b/armorcore/sources/kinc/input/rotation.h new file mode 100644 index 000000000..bfab44715 --- /dev/null +++ b/armorcore/sources/kinc/input/rotation.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +/*! \file rotation.h + \brief Provides support for rotation-sensors. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/// +/// Sets the rotation-callback which is called when the device is rotated. Act quickly when this is called for a desktop-system. +/// +/// The callback +void kinc_rotation_set_callback(void (*value)(float /*x*/, float /*y*/, float /*z*/)); + +void kinc_internal_on_rotation(float x, float y, float z); + +#ifdef KINC_IMPLEMENTATION_INPUT +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#include +#include + +static void (*rotation_callback)(float /*x*/, float /*y*/, float /*z*/) = NULL; + +void kinc_rotation_set_callback(void (*value)(float /*x*/, float /*y*/, float /*z*/)) { + rotation_callback = value; +} + +void kinc_internal_on_rotation(float x, float y, float z) { + if (rotation_callback != NULL) { + rotation_callback(x, y, z); + } +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/input/surface.h b/armorcore/sources/kinc/input/surface.h new file mode 100644 index 000000000..2cd692b48 --- /dev/null +++ b/armorcore/sources/kinc/input/surface.h @@ -0,0 +1,82 @@ +#pragma once + +#include + +/*! \file surface.h + \brief Provides touch-support. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/// +/// Sets the surface-touch-start-callback which is called when a finger-touch is detected. +/// +/// The callback +void kinc_surface_set_touch_start_callback(void (*value)(int /*index*/, int /*x*/, int /*y*/)); + +/// +/// Sets the surface-move-callback which is called when a finger is moving around. +/// +/// The callback +void kinc_surface_set_move_callback(void (*value)(int /*index*/, int /*x*/, int /*y*/)); + +/// +/// Sets the surface-touch-end-callback which is called when a finger disappears. This is usually not a medical emergency. +/// +/// The callback +void kinc_surface_set_touch_end_callback(void (*value)(int /*index*/, int /*x*/, int /*y*/)); + +void kinc_internal_surface_trigger_touch_start(int index, int x, int y); +void kinc_internal_surface_trigger_move(int index, int x, int y); +void kinc_internal_surface_trigger_touch_end(int index, int x, int y); + +#ifdef KINC_IMPLEMENTATION_INPUT +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#include +#include + +static void (*surface_touch_start_callback)(int /*index*/, int /*x*/, int /*y*/) = NULL; +static void (*surface_move_callback)(int /*index*/, int /*x*/, int /*y*/) = NULL; +static void (*surface_touch_end_callback)(int /*index*/, int /*x*/, int /*y*/) = NULL; + +void kinc_surface_set_touch_start_callback(void (*value)(int /*index*/, int /*x*/, int /*y*/)) { + surface_touch_start_callback = value; +} + +void kinc_surface_set_move_callback(void (*value)(int /*index*/, int /*x*/, int /*y*/)) { + surface_move_callback = value; +} + +void kinc_surface_set_touch_end_callback(void (*value)(int /*index*/, int /*x*/, int /*y*/)) { + surface_touch_end_callback = value; +} + +void kinc_internal_surface_trigger_touch_start(int index, int x, int y) { + if (surface_touch_start_callback != NULL) { + surface_touch_start_callback(index, x, y); + } +} + +void kinc_internal_surface_trigger_move(int index, int x, int y) { + if (surface_move_callback != NULL) { + surface_move_callback(index, x, y); + } +} + +void kinc_internal_surface_trigger_touch_end(int index, int x, int y) { + if (surface_touch_end_callback != NULL) { + surface_touch_end_callback(index, x, y); + } +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/io/filereader.h b/armorcore/sources/kinc/io/filereader.h new file mode 100644 index 000000000..ae6f55d56 --- /dev/null +++ b/armorcore/sources/kinc/io/filereader.h @@ -0,0 +1,586 @@ +#pragma once + +#include + +#include +#include +#include + +/*! \file filereader.h + \brief Provides an API very similar to fread and friends but handles the intricacies of where files are actually hidden on each supported system. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef KINC_DEBUGDIR +#define KINC_DEBUGDIR "out" +#endif + +#ifdef KINC_ANDROID +struct AAsset; +struct __sFILE; +typedef struct __sFILE FILE; +#endif + +#define KINC_FILE_TYPE_ASSET 0 +#define KINC_FILE_TYPE_SAVE 1 + +typedef struct kinc_file_reader { + void *data; // A file handle or a more complex structure + size_t size; + size_t offset; // Needed by some implementations + + bool (*close)(struct kinc_file_reader *reader); + size_t (*read)(struct kinc_file_reader *reader, void *data, size_t size); + size_t (*pos)(struct kinc_file_reader *reader); + bool (*seek)(struct kinc_file_reader *reader, size_t pos); +} kinc_file_reader_t; + +/// +/// Opens a file for reading. +/// +/// The reader to initialize for reading +/// A filepath to identify a file +/// Looks for a regular file (KINC_FILE_TYPE_ASSET) or a save-file (KINC_FILE_TYPE_SAVE) +/// Whether the file could be opened +bool kinc_file_reader_open(kinc_file_reader_t *reader, const char *filepath, int type); + +/// +/// Opens a memory area for reading using the file reader API. +/// +/// The reader to initialize for reading +/// A pointer to the memory area to read +/// The size of the memory area +/// This function always returns true +bool kinc_file_reader_from_memory(kinc_file_reader_t *reader, void *data, size_t size); + +/// +/// Registers a file reader callback. +/// +/// The function to call when opening a file +void kinc_file_reader_set_callback(bool (*callback)(kinc_file_reader_t *reader, const char *filename, int type)); + +/// +/// Closes a file. +/// +/// The file to close +/// Whether the file could be closed +bool kinc_file_reader_close(kinc_file_reader_t *reader); + +/// +/// Reads data from a file starting from the current reading-position and increases the reading-position accordingly. +/// +/// The reader to read from +/// A pointer to write the data to +/// The amount of data to read in bytes +/// The number of bytes that were read - can be less than size if there is not enough data in the file +size_t kinc_file_reader_read(kinc_file_reader_t *reader, void *data, size_t size); + +/// +/// Figures out the size of a file. +/// +/// The reader which's file-size to figure out +/// The size in bytes +size_t kinc_file_reader_size(kinc_file_reader_t *reader); + +/// +/// Figures out the current reading-position in the file. +/// +/// The reader which's reading-position to figure out +/// The current reading-position +size_t kinc_file_reader_pos(kinc_file_reader_t *reader); + +/// +/// Sets the reading-position manually. +/// +/// The reader which's reading-position to set +/// The reading-position to set +/// Whether the reading position could be set +bool kinc_file_reader_seek(kinc_file_reader_t *reader, size_t pos); + +/// +/// Interprets four bytes starting at the provided pointer as a little-endian float. +/// +float kinc_read_f32le(uint8_t *data); + +/// +/// Interprets four bytes starting at the provided pointer as a big-endian float. +/// +float kinc_read_f32be(uint8_t *data); + +/// +/// Interprets eight bytes starting at the provided pointer as a little-endian uint64. +/// +uint64_t kinc_read_u64le(uint8_t *data); + +/// +/// Interprets eight bytes starting at the provided pointer as a big-endian uint64. +/// +uint64_t kinc_read_u64be(uint8_t *data); + +/// +/// Interprets eight bytes starting at the provided pointer as a little-endian int64. +/// +int64_t kinc_read_s64le(uint8_t *data); + +/// +/// Interprets eight bytes starting at the provided pointer as a big-endian int64. +/// +int64_t kinc_read_s64be(uint8_t *data); + +/// +/// Interprets four bytes starting at the provided pointer as a little-endian uint32. +/// +uint32_t kinc_read_u32le(uint8_t *data); + +/// +/// Interprets four bytes starting at the provided pointer as a big-endian uint32. +/// +uint32_t kinc_read_u32be(uint8_t *data); + +/// +/// Interprets four bytes starting at the provided pointer as a little-endian int32. +/// +int32_t kinc_read_s32le(uint8_t *data); + +/// +/// Interprets four bytes starting at the provided pointer as a big-endian int32. +/// +int32_t kinc_read_s32be(uint8_t *data); + +/// +/// Interprets two bytes starting at the provided pointer as a little-endian uint16. +/// +uint16_t kinc_read_u16le(uint8_t *data); + +/// +/// Interprets two bytes starting at the provided pointer as a big-endian uint16. +/// +uint16_t kinc_read_u16be(uint8_t *data); + +/// +/// Interprets two bytes starting at the provided pointer as a little-endian int16. +/// +int16_t kinc_read_s16le(uint8_t *data); + +/// +/// Interprets two bytes starting at the provided pointer as a big-endian int16. +/// +int16_t kinc_read_s16be(uint8_t *data); + +/// +/// Interprets one byte starting at the provided pointer as a uint8. +/// +uint8_t kinc_read_u8(uint8_t *data); + +/// +/// Interprets one byte starting at the provided pointer as an int8. +/// +int8_t kinc_read_s8(uint8_t *data); + +void kinc_internal_set_files_location(char *dir); +char *kinc_internal_get_files_location(void); +bool kinc_internal_file_reader_callback(kinc_file_reader_t *reader, const char *filename, int type); +bool kinc_internal_file_reader_open(kinc_file_reader_t *reader, const char *filename, int type); + +#ifdef KINC_IMPLEMENTATION_IO +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#include "filereader.h" + +#undef KINC_IMPLEMENTATION +#include +#define KINC_IMPLEMENTATION + +#ifdef KINC_ANDROID +#include +#endif +#include +#include +#include +#ifdef KINC_WINDOWS +#include +#include +#endif + +static bool memory_close_callback(kinc_file_reader_t *reader) { + return true; +} + +static size_t memory_read_callback(kinc_file_reader_t *reader, void *data, size_t size) { + size_t read_size = reader->size - reader->offset < size ? reader->size - reader->offset : size; + memcpy(data, (uint8_t *)reader->data + reader->offset, read_size); + reader->offset += read_size; + return read_size; +} + +static size_t memory_pos_callback(kinc_file_reader_t *reader) { + return reader->offset; +} + +static bool memory_seek_callback(kinc_file_reader_t *reader, size_t pos) { + reader->offset = pos; + return true; +} + +bool kinc_file_reader_from_memory(kinc_file_reader_t *reader, void *data, size_t size) +{ + memset(reader, 0, sizeof(kinc_file_reader_t)); + reader->data = data; + reader->size = size; + reader->read = memory_read_callback; + reader->pos = memory_pos_callback; + reader->seek = memory_seek_callback; + reader->close = memory_close_callback; + return true; +} + +#ifdef KINC_IOS +const char *iphonegetresourcepath(void); +#endif + +#ifdef KINC_MACOS +const char *macgetresourcepath(void); +#endif + +#if defined(KINC_WINDOWS) +#include +#endif + +#ifdef KINC_RASPBERRY_PI +#define KINC_LINUX +#endif + +static char *fileslocation = NULL; +static bool (*file_reader_callback)(kinc_file_reader_t *reader, const char *filename, int type) = NULL; +#ifdef KINC_WINDOWS +static wchar_t wfilepath[1001]; +#endif + +void kinc_internal_set_files_location(char *dir) { + fileslocation = dir; +} + +char *kinc_internal_get_files_location(void) { + return fileslocation; +} + +bool kinc_internal_file_reader_callback(kinc_file_reader_t *reader, const char *filename, int type) { + return file_reader_callback ? file_reader_callback(reader, filename, type) : false; +} + +#if defined(KINC_WINDOWS) +static size_t kinc_libc_file_reader_read(kinc_file_reader_t *reader, void *data, size_t size) { + DWORD readBytes = 0; + if (ReadFile(reader->data, data, (DWORD)size, &readBytes, NULL)) { + return (size_t)readBytes; + } + else { + return 0; + } +} + +static bool kinc_libc_file_reader_seek(kinc_file_reader_t *reader, size_t pos) { + // TODO: make this 64-bit compliant + SetFilePointer(reader->data, (LONG)pos, NULL, FILE_BEGIN); + return true; +} + +static bool kinc_libc_file_reader_close(kinc_file_reader_t *reader) { + CloseHandle(reader->data); + return true; +} + +static size_t kinc_libc_file_reader_pos(kinc_file_reader_t *reader) { + // TODO: make this 64-bit compliant + return (size_t)SetFilePointer(reader->data, 0, NULL, FILE_CURRENT); +} +#else +static size_t kinc_libc_file_reader_read(kinc_file_reader_t *reader, void *data, size_t size) { + return fread(data, 1, size, (FILE *)reader->data); +} + +static bool kinc_libc_file_reader_seek(kinc_file_reader_t *reader, size_t pos) { + fseek((FILE *)reader->data, pos, SEEK_SET); + return true; +} + +static bool kinc_libc_file_reader_close(kinc_file_reader_t *reader) { + if (reader->data != NULL) { + fclose((FILE *)reader->data); + reader->data = NULL; + } + return true; +} + +static size_t kinc_libc_file_reader_pos(kinc_file_reader_t *reader) { + return ftell((FILE *)reader->data); +} +#endif + +bool kinc_internal_file_reader_open(kinc_file_reader_t *reader, const char *filename, int type) { + char filepath[1001]; +#ifdef KINC_IOS + strcpy(filepath, type == KINC_FILE_TYPE_SAVE ? kinc_internal_save_path() : iphonegetresourcepath()); + if (type != KINC_FILE_TYPE_SAVE) { + strcat(filepath, "/"); + strcat(filepath, KINC_DEBUGDIR); + strcat(filepath, "/"); + } + + strcat(filepath, filename); +#endif +#ifdef KINC_MACOS + strcpy(filepath, type == KINC_FILE_TYPE_SAVE ? kinc_internal_save_path() : macgetresourcepath()); + if (type != KINC_FILE_TYPE_SAVE) { + strcat(filepath, "/"); + strcat(filepath, KINC_DEBUGDIR); + strcat(filepath, "/"); + } + strcat(filepath, filename); +#endif +#ifdef KINC_WINDOWS + if (type == KINC_FILE_TYPE_SAVE) { + strcpy(filepath, kinc_internal_save_path()); + strcat(filepath, filename); + } + else { + strcpy(filepath, filename); + } + size_t filepathlength = strlen(filepath); + for (size_t i = 0; i < filepathlength; ++i) + if (filepath[i] == '/') + filepath[i] = '\\'; +#endif +#if defined(KINC_LINUX) || defined(KINC_ANDROID) + if (type == KINC_FILE_TYPE_SAVE) { + strcpy(filepath, kinc_internal_save_path()); + strcat(filepath, filename); + } + else { + strcpy(filepath, filename); + } +#endif +#ifdef KINC_WASM + strcpy(filepath, filename); +#endif +#ifdef KINC_EMSCRIPTEN + strcpy(filepath, KINC_DEBUGDIR); + strcat(filepath, "/"); + strcat(filepath, filename); +#endif + +#ifdef KINC_WINDOWS + // Drive letter or network + bool isAbsolute = (filename[1] == ':' && filename[2] == '\\') || (filename[0] == '\\' && filename[1] == '\\'); +#else + bool isAbsolute = filename[0] == '/'; +#endif + + if (isAbsolute) { + strcpy(filepath, filename); + } + else if (fileslocation != NULL && type != KINC_FILE_TYPE_SAVE) { + strcpy(filepath, fileslocation); + strcat(filepath, "/"); + strcat(filepath, filename); + } + +#ifdef KINC_WINDOWS + MultiByteToWideChar(CP_UTF8, 0, filepath, -1, wfilepath, 1000); + reader->data = CreateFileW(wfilepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (reader->data == INVALID_HANDLE_VALUE) { + return false; + } +#else + reader->data = fopen(filepath, "rb"); + if (reader->data == NULL) { + return false; + } +#endif + +#ifdef KINC_WINDOWS + // TODO: make this 64-bit compliant + reader->size = (size_t)GetFileSize(reader->data, NULL); +#else + fseek((FILE *)reader->data, 0, SEEK_END); + reader->size = ftell((FILE *)reader->data); + fseek((FILE *)reader->data, 0, SEEK_SET); +#endif + + reader->read = kinc_libc_file_reader_read; + reader->seek = kinc_libc_file_reader_seek; + reader->close = kinc_libc_file_reader_close; + reader->pos = kinc_libc_file_reader_pos; + + return true; +} + +#if !defined(KINC_ANDROID) && !defined(KINC_CONSOLE) +bool kinc_file_reader_open(kinc_file_reader_t *reader, const char *filename, int type) { + memset(reader, 0, sizeof(*reader)); + return kinc_internal_file_reader_callback(reader, filename, type) || + kinc_internal_file_reader_open(reader, filename, type); +} +#endif + +void kinc_file_reader_set_callback(bool (*callback)(kinc_file_reader_t *reader, const char *filename, int type)) { + file_reader_callback = callback; +} + +size_t kinc_file_reader_read(kinc_file_reader_t *reader, void *data, size_t size) { + return reader->read(reader, data, size); +} + +bool kinc_file_reader_seek(kinc_file_reader_t *reader, size_t pos) { + return reader->seek(reader, pos); +} + +bool kinc_file_reader_close(kinc_file_reader_t *reader) { + return reader->close(reader); +} + +size_t kinc_file_reader_pos(kinc_file_reader_t *reader) { + return reader->pos(reader); +} + +size_t kinc_file_reader_size(kinc_file_reader_t *reader) { + return reader->size; +} + +float kinc_read_f32le(uint8_t *data) { +#ifdef KINC_LITTLE_ENDIAN // speed optimization + return *(float *)data; +#else // works on all architectures + int i = (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + return *(float *)&i; +#endif +} + +float kinc_read_f32be(uint8_t *data) { +#ifdef KINC_BIG_ENDIAN // speed optimization + return *(float *)data; +#else // works on all architectures + int i = (data[3] << 0) | (data[2] << 8) | (data[1] << 16) | (data[0] << 24); + return *(float *)&i; +#endif +} + +uint64_t kinc_read_u64le(uint8_t *data) { +#ifdef KINC_LITTLE_ENDIAN + return *(uint64_t *)data; +#else + return ((uint64_t)data[0] << 0) | ((uint64_t)data[1] << 8) | ((uint64_t)data[2] << 16) | ((uint64_t)data[3] << 24) | ((uint64_t)data[4] << 32) | + ((uint64_t)data[5] << 40) | ((uint64_t)data[6] << 48) | ((uint64_t)data[7] << 56); +#endif +} + +uint64_t kinc_read_u64be(uint8_t *data) { +#ifdef KINC_BIG_ENDIAN + return *(uint64_t *)data; +#else + return ((uint64_t)data[7] << 0) | ((uint64_t)data[6] << 8) | ((uint64_t)data[5] << 16) | ((uint64_t)data[4] << 24) | ((uint64_t)data[3] << 32) | + ((uint64_t)data[2] << 40) | ((uint64_t)data[1] << 48) | ((uint64_t)data[0] << 56); +#endif +} + +int64_t kinc_read_s64le(uint8_t *data) { +#ifdef KINC_LITTLE_ENDIAN + return *(int64_t *)data; +#else + return ((int64_t)data[0] << 0) | ((int64_t)data[1] << 8) | ((int64_t)data[2] << 16) | ((int64_t)data[3] << 24) | ((int64_t)data[4] << 32) | + ((int64_t)data[5] << 40) | ((int64_t)data[6] << 48) | ((int64_t)data[7] << 56); +#endif +} + +int64_t kinc_read_s64be(uint8_t *data) { +#ifdef KINC_BIG_ENDIAN + return *(int64_t *)data; +#else + return ((int64_t)data[7] << 0) | ((int64_t)data[6] << 8) | ((int64_t)data[5] << 16) | ((int64_t)data[4] << 24) | ((int64_t)data[3] << 32) | + ((int64_t)data[2] << 40) | ((int64_t)data[1] << 48) | ((int64_t)data[0] << 56); +#endif +} + +uint32_t kinc_read_u32le(uint8_t *data) { +#ifdef KINC_LITTLE_ENDIAN + return *(uint32_t *)data; +#else + return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); +#endif +} + +uint32_t kinc_read_u32be(uint8_t *data) { +#ifdef KINC_BIG_ENDIAN + return *(uint32_t *)data; +#else + return (data[3] << 0) | (data[2] << 8) | (data[1] << 16) | (data[0] << 24); +#endif +} + +int32_t kinc_read_s32le(uint8_t *data) { +#ifdef KINC_LITTLE_ENDIAN + return *(int32_t *)data; +#else + return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); +#endif +} + +int32_t kinc_read_s32be(uint8_t *data) { +#ifdef KINC_BIG_ENDIAN + return *(int32_t *)data; +#else + return (data[3] << 0) | (data[2] << 8) | (data[1] << 16) | (data[0] << 24); +#endif +} + +uint16_t kinc_read_u16le(uint8_t *data) { +#ifdef KINC_LITTLE_ENDIAN + return *(uint16_t *)data; +#else + return (data[0] << 0) | (data[1] << 8); +#endif +} + +uint16_t kinc_read_u16be(uint8_t *data) { +#ifdef KINC_BIG_ENDIAN + return *(uint16_t *)data; +#else + return (data[1] << 0) | (data[0] << 8); +#endif +} + +int16_t kinc_read_s16le(uint8_t *data) { +#ifdef KINC_LITTLE_ENDIAN + return *(int16_t *)data; +#else + return (data[0] << 0) | (data[1] << 8); +#endif +} + +int16_t kinc_read_s16be(uint8_t *data) { +#ifdef KINC_BIG_ENDIAN + return *(int16_t *)data; +#else + return (data[1] << 0) | (data[0] << 8); +#endif +} + +uint8_t kinc_read_u8(uint8_t *data) { + return *data; +} + +int8_t kinc_read_s8(uint8_t *data) { + return *(int8_t *)data; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/io/filewriter.h b/armorcore/sources/kinc/io/filewriter.h new file mode 100644 index 000000000..dd1d5f019 --- /dev/null +++ b/armorcore/sources/kinc/io/filewriter.h @@ -0,0 +1,132 @@ +#pragma once + +#include + +#include + +/*! \file filewriter.h + \brief Provides an API very similar to fwrite and friends but uses a directory that can actually used for persistent file storage. This can later be read + using the kinc_file_reader-functions and KINC_FILE_TYPE_SAVE. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_file_writer { + void *file; + const char *filename; + bool mounted; +} kinc_file_writer_t; + +/// +/// Opens a file for writing. +/// +/// The writer to initialize for writing +/// A filepath to identify a file +/// Whether the file could be opened +bool kinc_file_writer_open(kinc_file_writer_t *writer, const char *filepath); + +/// +/// Writes data to a file starting from the current writing-position and increases the writing-position accordingly. +/// +/// The writer to write to +/// A pointer to read the data from +/// The amount of data to write in bytes +void kinc_file_writer_write(kinc_file_writer_t *writer, void *data, int size); + +/// +/// Closes a file. +/// +/// The file to close +void kinc_file_writer_close(kinc_file_writer_t *writer); + +#ifdef KINC_IMPLEMENTATION_IO +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#if !defined(KINC_CONSOLE) + +#include "filewriter.h" + +#undef KINC_IMPLEMENTATION +#include +#include +#include +#define KINC_IMPLEMENTATION + +#include +#include + +#if defined(KINC_WINDOWS) +#include +#endif + +#if defined(KINC_PS4) || defined(KINC_SWITCH) +#define MOUNT_SAVES +bool mountSaveData(bool); +void unmountSaveData(); +#endif + +bool kinc_file_writer_open(kinc_file_writer_t *writer, const char *filepath) { + writer->file = NULL; + writer->mounted = false; +#ifdef MOUNT_SAVES + if (!mountSaveData(true)) { + return false; + } + writer->mounted = true; +#endif + char path[1001]; + strcpy(path, kinc_internal_save_path()); + strcat(path, filepath); + +#ifdef KINC_WINDOWS + wchar_t wpath[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, MAX_PATH); + writer->file = CreateFileW(wpath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); +#else + writer->file = fopen(path, "wb"); +#endif + if (writer->file == NULL) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Could not open file %s.", filepath); + return false; + } + return true; +} + +void kinc_file_writer_close(kinc_file_writer_t *writer) { + if (writer->file != NULL) { +#ifdef KINC_WINDOWS + CloseHandle(writer->file); +#else + fclose((FILE *)writer->file); +#endif + writer->file = NULL; + } +#ifdef MOUNT_SAVES + if (writer->mounted) { + writer->mounted = false; + unmountSaveData(); + } +#endif +} + +void kinc_file_writer_write(kinc_file_writer_t *writer, void *data, int size) { +#ifdef KINC_WINDOWS + DWORD written = 0; + WriteFile(writer->file, data, (DWORD)size, &written, NULL); +#else + fwrite(data, 1, size, (FILE *)writer->file); +#endif +} + +#endif + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/io/iounit.c b/armorcore/sources/kinc/io/iounit.c new file mode 100644 index 000000000..877ef7ef1 --- /dev/null +++ b/armorcore/sources/kinc/io/iounit.c @@ -0,0 +1,4 @@ +#define KINC_IMPLEMENTATION_IO + +#include "filereader.h" +#include "filewriter.h" diff --git a/armorcore/sources/kinc/libs/lz4x.h b/armorcore/sources/kinc/libs/lz4x.h new file mode 100644 index 000000000..0878e09a6 --- /dev/null +++ b/armorcore/sources/kinc/libs/lz4x.h @@ -0,0 +1,299 @@ +/* + +LZ4X - An optimized LZ4 compressor + +Written and placed in the public domain by Ilya Muravyov + +*/ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#define _CRT_DISABLE_PERFCRIT_LOCKS + +#include +#include +#include +#include + +typedef unsigned char U8; +typedef unsigned short U16; +typedef unsigned int U32; + +#define LZ4_MAGIC 0x184C2102 +#define BLOCK_SIZE (8<<20) // 8 MB +#define PADDING_LITERALS 5 + +#define WINDOW_BITS 16 +#define WINDOW_SIZE (1<(b))?(a):(b)) + +#define LOAD_16(p) (*(const U16*)(&g_buf[p])) +#define LOAD_32(p) (*(const U32*)(&g_buf[p])) +#define STORE_16(p, x) (*(U16*)(&g_buf[p])=(x)) +#define COPY_32(d, s) (*(U32*)(&g_buf[d])=LOAD_32(s)) + +#define HASH_BITS 18 +#define HASH_SIZE (1<>(32-HASH_BITS)) + +static size_t kread(void* dst, size_t size, const char* src, size_t* offset, size_t compressedSize) { + size_t realSize = MIN(size, compressedSize - *offset); + memcpy(dst, &src[*offset], realSize); + *offset += realSize; + return realSize; +} + +static size_t kwrite(void* src, size_t size, char* dst, size_t* offset, int maxOutputSize) { + size_t realSize = MIN(size, maxOutputSize - *offset); + memcpy(&dst[*offset], src, size); + *offset += realSize; + return realSize; +} + +static inline void wild_copy(int d, int s, int n) { + COPY_32(d, s); + COPY_32(d+4, s+4); + + for (int i=8; i 0) { + for (int i=0; i=MAX(12-PADDING_LITERALS, MIN_MATCH)) + { + const int limit=MAX(p-WINDOW_SIZE, NIL); + int chain_len=max_chain; + + int s=head[HASH_32(p)]; + while (s>limit) + { + if (g_buf[s+best_len]==g_buf[p+best_len] && LOAD_32(s)==LOAD_32(p)) + { + int len=MIN_MATCH; + while (lenbest_len) + { + best_len=len; + dist=p-s; + + if (len==max_match) + break; + } + } + + if (--chain_len==0) + break; + + s=tail[s&WINDOW_MASK]; + } + } + + if (best_len>=MIN_MATCH) + { + int len=best_len-MIN_MATCH; + const int nib=MIN(len, 15); + + if (pp!=p) + { + const int run=p-pp; + if (run>=15) + { + g_buf[op++]=(15<<4)+nib; + + int j=run-15; + for (; j>=255; j-=255) + g_buf[op++]=255; + g_buf[op++]=j; + } + else + g_buf[op++]=(run<<4)+nib; + + wild_copy(op, pp, run); + op+=run; + } + else + g_buf[op++]=nib; + + STORE_16(op, dist); + op+=2; + + if (len>=15) + { + len-=15; + for (; len>=255; len-=255) + g_buf[op++]=255; + g_buf[op++]=len; + } + + pp=p+best_len; + + while (p=15) + { + g_buf[op++]=15<<4; + + int j=run-15; + for (; j>=255; j-=255) + g_buf[op++]=255; + g_buf[op++]=j; + } + else + g_buf[op++]=run<<4; + + wild_copy(op, pp, run); + op+=run; + } + + int comp_len=op-BLOCK_SIZE; + + kwrite(&comp_len, sizeof(comp_len), dest, &write_offset, maxDestSize); + kwrite(&g_buf[BLOCK_SIZE], comp_len, dest, &write_offset, maxDestSize); + } + + return write_offset; +} + +int LZ4_compress_bound(int inputSize) { + return inputSize + (inputSize / 255) + 16; +} + +size_t LZ4_compress_default(const char *source, char *dest, int sourceSize, int maxDestSize) { + return LZ4_compress(source, sourceSize, dest, WINDOW_SIZE, maxDestSize); +} + +int LZ4_decompress_safe(const char *source, char *buf, int compressedSize, int maxOutputSize) { + size_t read_offset = 0; + size_t write_offset = 0; + int comp_len; + while (kread(&comp_len, sizeof(comp_len), source, &read_offset, compressedSize)>0) + { + if (comp_len<2 || comp_len>(BLOCK_SIZE+EXCESS) + || kread(&g_buf[BLOCK_SIZE], comp_len, source, &read_offset, compressedSize)!=comp_len) + return -1; + + int p=0; + + int ip=BLOCK_SIZE; + const int ip_end=ip+comp_len; + + for (;;) + { + const int token=g_buf[ip++]; + if (token>=16) + { + int run=token>>4; + if (run==15) + { + for (;;) + { + const int c=g_buf[ip++]; + run+=c; + if (c!=255) + break; + } + } + if ((p+run)>BLOCK_SIZE) + return -1; + + wild_copy(p, ip, run); + p+=run; + ip+=run; + if (ip>=ip_end) + break; + } + + int s=p-LOAD_16(ip); + ip+=2; + if (s<0) + return -1; + + int len=(token&15)+MIN_MATCH; + if (len==(15+MIN_MATCH)) + { + for (;;) + { + const int c=g_buf[ip++]; + len+=c; + if (c!=255) + break; + } + } + if ((p+len)>BLOCK_SIZE) + return -1; + + if ((p-s)>=4) + { + wild_copy(p, s, len); + p+=len; + } + else + { + while (len--!=0) + g_buf[p++]=g_buf[s++]; + } + } + + if (kwrite(g_buf, p, buf, &write_offset, maxOutputSize)!=p) + { + return -1; + } + } + + return 0; +} diff --git a/armorcore/sources/kinc/libs/neon_mathfun.h b/armorcore/sources/kinc/libs/neon_mathfun.h new file mode 100644 index 000000000..f51a61e17 --- /dev/null +++ b/armorcore/sources/kinc/libs/neon_mathfun.h @@ -0,0 +1,301 @@ +/* NEON implementation of sin, cos, exp and log + + Inspired by Intel Approximate Math library, and based on the + corresponding algorithms of the cephes math library +*/ + +/* Copyright (C) 2011 Julien Pommier + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + (this is the zlib license) +*/ + +#include + +typedef float32x4_t v4sf; // vector of 4 float +typedef uint32x4_t v4su; // vector of 4 uint32 +typedef int32x4_t v4si; // vector of 4 uint32 + +#define c_inv_mant_mask ~0x7f800000u +#define c_cephes_SQRTHF 0.707106781186547524 +#define c_cephes_log_p0 7.0376836292E-2 +#define c_cephes_log_p1 - 1.1514610310E-1 +#define c_cephes_log_p2 1.1676998740E-1 +#define c_cephes_log_p3 - 1.2420140846E-1 +#define c_cephes_log_p4 + 1.4249322787E-1 +#define c_cephes_log_p5 - 1.6668057665E-1 +#define c_cephes_log_p6 + 2.0000714765E-1 +#define c_cephes_log_p7 - 2.4999993993E-1 +#define c_cephes_log_p8 + 3.3333331174E-1 +#define c_cephes_log_q1 -2.12194440e-4 +#define c_cephes_log_q2 0.693359375 + +/* natural logarithm computed for 4 simultaneous float + return NaN for x <= 0 +*/ +v4sf log_ps(v4sf x) { + v4sf one = vdupq_n_f32(1); + + x = vmaxq_f32(x, vdupq_n_f32(0)); /* force flush to zero on denormal values */ + v4su invalid_mask = vcleq_f32(x, vdupq_n_f32(0)); + + v4si ux = vreinterpretq_s32_f32(x); + + v4si emm0 = vshrq_n_s32(ux, 23); + + /* keep only the fractional part */ + ux = vandq_s32(ux, vdupq_n_s32(c_inv_mant_mask)); + ux = vorrq_s32(ux, vreinterpretq_s32_f32(vdupq_n_f32(0.5f))); + x = vreinterpretq_f32_s32(ux); + + emm0 = vsubq_s32(emm0, vdupq_n_s32(0x7f)); + v4sf e = vcvtq_f32_s32(emm0); + + e = vaddq_f32(e, one); + + /* part2: + if( x < SQRTHF ) { + e -= 1; + x = x + x - 1.0; + } else { x = x - 1.0; } + */ + v4su mask = vcltq_f32(x, vdupq_n_f32(c_cephes_SQRTHF)); + v4sf tmp = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(x), mask)); + x = vsubq_f32(x, one); + e = vsubq_f32(e, vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(one), mask))); + x = vaddq_f32(x, tmp); + + v4sf z = vmulq_f32(x,x); + + v4sf y = vdupq_n_f32(c_cephes_log_p0); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p1)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p2)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p3)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p4)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p5)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p6)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p7)); + y = vmulq_f32(y, x); + y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p8)); + y = vmulq_f32(y, x); + + y = vmulq_f32(y, z); + + + tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q1)); + y = vaddq_f32(y, tmp); + + + tmp = vmulq_f32(z, vdupq_n_f32(0.5f)); + y = vsubq_f32(y, tmp); + + tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q2)); + x = vaddq_f32(x, y); + x = vaddq_f32(x, tmp); + x = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(x), invalid_mask)); // negative arg will be NAN + return x; +} + +#define c_exp_hi 88.3762626647949f +#define c_exp_lo -88.3762626647949f + +#define c_cephes_LOG2EF 1.44269504088896341 +#define c_cephes_exp_C1 0.693359375 +#define c_cephes_exp_C2 -2.12194440e-4 + +#define c_cephes_exp_p0 1.9875691500E-4 +#define c_cephes_exp_p1 1.3981999507E-3 +#define c_cephes_exp_p2 8.3334519073E-3 +#define c_cephes_exp_p3 4.1665795894E-2 +#define c_cephes_exp_p4 1.6666665459E-1 +#define c_cephes_exp_p5 5.0000001201E-1 + +/* exp() computed for 4 float at once */ +v4sf exp_ps(v4sf x) { + v4sf tmp, fx; + + v4sf one = vdupq_n_f32(1); + x = vminq_f32(x, vdupq_n_f32(c_exp_hi)); + x = vmaxq_f32(x, vdupq_n_f32(c_exp_lo)); + + /* express exp(x) as exp(g + n*log(2)) */ + fx = vmlaq_f32(vdupq_n_f32(0.5f), x, vdupq_n_f32(c_cephes_LOG2EF)); + + /* perform a floorf */ + tmp = vcvtq_f32_s32(vcvtq_s32_f32(fx)); + + /* if greater, substract 1 */ + v4su mask = vcgtq_f32(tmp, fx); + mask = vandq_u32(mask, vreinterpretq_u32_f32(one)); + + + fx = vsubq_f32(tmp, vreinterpretq_f32_u32(mask)); + + tmp = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C1)); + v4sf z = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C2)); + x = vsubq_f32(x, tmp); + x = vsubq_f32(x, z); + + static const float cephes_exp_p[6] = { c_cephes_exp_p0, c_cephes_exp_p1, c_cephes_exp_p2, c_cephes_exp_p3, c_cephes_exp_p4, c_cephes_exp_p5 }; + v4sf y = vld1q_dup_f32(cephes_exp_p+0); + v4sf c1 = vld1q_dup_f32(cephes_exp_p+1); + v4sf c2 = vld1q_dup_f32(cephes_exp_p+2); + v4sf c3 = vld1q_dup_f32(cephes_exp_p+3); + v4sf c4 = vld1q_dup_f32(cephes_exp_p+4); + v4sf c5 = vld1q_dup_f32(cephes_exp_p+5); + + y = vmulq_f32(y, x); + z = vmulq_f32(x,x); + y = vaddq_f32(y, c1); + y = vmulq_f32(y, x); + y = vaddq_f32(y, c2); + y = vmulq_f32(y, x); + y = vaddq_f32(y, c3); + y = vmulq_f32(y, x); + y = vaddq_f32(y, c4); + y = vmulq_f32(y, x); + y = vaddq_f32(y, c5); + + y = vmulq_f32(y, z); + y = vaddq_f32(y, x); + y = vaddq_f32(y, one); + + /* build 2^n */ + int32x4_t mm; + mm = vcvtq_s32_f32(fx); + mm = vaddq_s32(mm, vdupq_n_s32(0x7f)); + mm = vshlq_n_s32(mm, 23); + v4sf pow2n = vreinterpretq_f32_s32(mm); + + y = vmulq_f32(y, pow2n); + return y; +} + +#define c_minus_cephes_DP1 -0.78515625 +#define c_minus_cephes_DP2 -2.4187564849853515625e-4 +#define c_minus_cephes_DP3 -3.77489497744594108e-8 +#define c_sincof_p0 -1.9515295891E-4 +#define c_sincof_p1 8.3321608736E-3 +#define c_sincof_p2 -1.6666654611E-1 +#define c_coscof_p0 2.443315711809948E-005 +#define c_coscof_p1 -1.388731625493765E-003 +#define c_coscof_p2 4.166664568298827E-002 +#define c_cephes_FOPI 1.27323954473516 // 4 / M_PI + +/* evaluation of 4 sines & cosines at once. + + The code is the exact rewriting of the cephes sinf function. + Precision is excellent as long as x < 8192 (I did not bother to + take into account the special handling they have for greater values + -- it does not return garbage for arguments over 8192, though, but + the extra precision is missing). + + Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the + surprising but correct result. + + Note also that when you compute sin(x), cos(x) is available at + almost no extra price so both sin_ps and cos_ps make use of + sincos_ps.. + */ +void sincos_ps(v4sf x, v4sf *ysin, v4sf *ycos) { // any x + v4sf xmm1, xmm2, xmm3, y; + + v4su emm2; + + v4su sign_mask_sin, sign_mask_cos; + sign_mask_sin = vcltq_f32(x, vdupq_n_f32(0)); + x = vabsq_f32(x); + + /* scale by 4/Pi */ + y = vmulq_f32(x, vdupq_n_f32(c_cephes_FOPI)); + + /* store the integer part of y in mm0 */ + emm2 = vcvtq_u32_f32(y); + /* j=(j+1) & (~1) (see the cephes sources) */ + emm2 = vaddq_u32(emm2, vdupq_n_u32(1)); + emm2 = vandq_u32(emm2, vdupq_n_u32(~1)); + y = vcvtq_f32_u32(emm2); + + /* get the polynom selection mask + there is one polynom for 0 <= x <= Pi/4 + and another one for Pi/4 + +/* yes I know, the top of this file is quite ugly */ + +#ifdef _MSC_VER /* visual c++ */ +# define ALIGN16_BEG __declspec(align(16)) +# define ALIGN16_END +#else /* gcc or icc */ +# define ALIGN16_BEG +# define ALIGN16_END __attribute__((aligned(16))) +#endif + +/* __m128 is ugly to write */ +typedef __m128 v4sf; // vector of 4 float (sse1) + +#ifdef USE_SSE2 +# include +typedef __m128i v4si; // vector of 4 int (sse2) +#else +typedef __m64 v2si; // vector of 2 int (mmx) +#endif + +/* declare some SSE constants -- why can't I figure a better way to do that? */ +#define _PS_CONST(Name, Val) \ + static const ALIGN16_BEG float _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val } +#define _PI32_CONST(Name, Val) \ + static const ALIGN16_BEG int _pi32_##Name[4] ALIGN16_END = { Val, Val, Val, Val } +#define _PS_CONST_TYPE(Name, Type, Val) \ + static const ALIGN16_BEG Type _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val } + +_PS_CONST(1 , 1.0f); +_PS_CONST(0p5, 0.5f); +/* the smallest non denormalized float number */ +_PS_CONST_TYPE(min_norm_pos, int, 0x00800000); +_PS_CONST_TYPE(mant_mask, int, 0x7f800000); +_PS_CONST_TYPE(inv_mant_mask, int, ~0x7f800000); + +_PS_CONST_TYPE(sign_mask, int, (int)0x80000000); +_PS_CONST_TYPE(inv_sign_mask, int, ~0x80000000); + +_PI32_CONST(1, 1); +_PI32_CONST(inv1, ~1); +_PI32_CONST(2, 2); +_PI32_CONST(4, 4); +_PI32_CONST(0x7f, 0x7f); + +_PS_CONST(cephes_SQRTHF, 0.707106781186547524); +_PS_CONST(cephes_log_p0, 7.0376836292E-2); +_PS_CONST(cephes_log_p1, - 1.1514610310E-1); +_PS_CONST(cephes_log_p2, 1.1676998740E-1); +_PS_CONST(cephes_log_p3, - 1.2420140846E-1); +_PS_CONST(cephes_log_p4, + 1.4249322787E-1); +_PS_CONST(cephes_log_p5, - 1.6668057665E-1); +_PS_CONST(cephes_log_p6, + 2.0000714765E-1); +_PS_CONST(cephes_log_p7, - 2.4999993993E-1); +_PS_CONST(cephes_log_p8, + 3.3333331174E-1); +_PS_CONST(cephes_log_q1, -2.12194440e-4); +_PS_CONST(cephes_log_q2, 0.693359375); + +#ifndef USE_SSE2 +typedef union xmm_mm_union { + __m128 xmm; + __m64 mm[2]; +} xmm_mm_union; + +#define COPY_XMM_TO_MM(xmm_, mm0_, mm1_) { \ + xmm_mm_union u; u.xmm = xmm_; \ + mm0_ = u.mm[0]; \ + mm1_ = u.mm[1]; \ +} + +#define COPY_MM_TO_XMM(mm0_, mm1_, xmm_) { \ + xmm_mm_union u; u.mm[0]=mm0_; u.mm[1]=mm1_; xmm_ = u.xmm; \ + } + +#endif // USE_SSE2 + +/* natural logarithm computed for 4 simultaneous float + return NaN for x <= 0 +*/ +v4sf log_ps(v4sf x) { +#ifdef USE_SSE2 + v4si emm0; +#else + v2si mm0, mm1; +#endif + v4sf one = *(v4sf*)_ps_1; + + v4sf invalid_mask = _mm_cmple_ps(x, _mm_setzero_ps()); + + x = _mm_max_ps(x, *(v4sf*)_ps_min_norm_pos); /* cut off denormalized stuff */ + +#ifndef USE_SSE2 + /* part 1: x = frexpf(x, &e); */ + COPY_XMM_TO_MM(x, mm0, mm1); + mm0 = _mm_srli_pi32(mm0, 23); + mm1 = _mm_srli_pi32(mm1, 23); +#else + emm0 = _mm_srli_epi32(_mm_castps_si128(x), 23); +#endif + /* keep only the fractional part */ + x = _mm_and_ps(x, *(v4sf*)_ps_inv_mant_mask); + x = _mm_or_ps(x, *(v4sf*)_ps_0p5); + +#ifndef USE_SSE2 + /* now e=mm0:mm1 contain the really base-2 exponent */ + mm0 = _mm_sub_pi32(mm0, *(v2si*)_pi32_0x7f); + mm1 = _mm_sub_pi32(mm1, *(v2si*)_pi32_0x7f); + v4sf e = _mm_cvtpi32x2_ps(mm0, mm1); + _mm_empty(); /* bye bye mmx */ +#else + emm0 = _mm_sub_epi32(emm0, *(v4si*)_pi32_0x7f); + v4sf e = _mm_cvtepi32_ps(emm0); +#endif + + e = _mm_add_ps(e, one); + + /* part2: + if( x < SQRTHF ) { + e -= 1; + x = x + x - 1.0; + } else { x = x - 1.0; } + */ + v4sf mask = _mm_cmplt_ps(x, *(v4sf*)_ps_cephes_SQRTHF); + v4sf tmp = _mm_and_ps(x, mask); + x = _mm_sub_ps(x, one); + e = _mm_sub_ps(e, _mm_and_ps(one, mask)); + x = _mm_add_ps(x, tmp); + + + v4sf z = _mm_mul_ps(x,x); + + v4sf y = *(v4sf*)_ps_cephes_log_p0; + y = _mm_mul_ps(y, x); + y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p1); + y = _mm_mul_ps(y, x); + y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p2); + y = _mm_mul_ps(y, x); + y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p3); + y = _mm_mul_ps(y, x); + y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p4); + y = _mm_mul_ps(y, x); + y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p5); + y = _mm_mul_ps(y, x); + y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p6); + y = _mm_mul_ps(y, x); + y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p7); + y = _mm_mul_ps(y, x); + y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p8); + y = _mm_mul_ps(y, x); + + y = _mm_mul_ps(y, z); + + + tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q1); + y = _mm_add_ps(y, tmp); + + + tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5); + y = _mm_sub_ps(y, tmp); + + tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q2); + x = _mm_add_ps(x, y); + x = _mm_add_ps(x, tmp); + x = _mm_or_ps(x, invalid_mask); // negative arg will be NAN + return x; +} + +_PS_CONST(exp_hi, 88.3762626647949f); +_PS_CONST(exp_lo, -88.3762626647949f); + +_PS_CONST(cephes_LOG2EF, 1.44269504088896341); +_PS_CONST(cephes_exp_C1, 0.693359375); +_PS_CONST(cephes_exp_C2, -2.12194440e-4); + +_PS_CONST(cephes_exp_p0, 1.9875691500E-4); +_PS_CONST(cephes_exp_p1, 1.3981999507E-3); +_PS_CONST(cephes_exp_p2, 8.3334519073E-3); +_PS_CONST(cephes_exp_p3, 4.1665795894E-2); +_PS_CONST(cephes_exp_p4, 1.6666665459E-1); +_PS_CONST(cephes_exp_p5, 5.0000001201E-1); + +v4sf exp_ps(v4sf x) { + v4sf tmp = _mm_setzero_ps(), fx; +#ifdef USE_SSE2 + v4si emm0; +#else + v2si mm0, mm1; +#endif + v4sf one = *(v4sf*)_ps_1; + + x = _mm_min_ps(x, *(v4sf*)_ps_exp_hi); + x = _mm_max_ps(x, *(v4sf*)_ps_exp_lo); + + /* express exp(x) as exp(g + n*log(2)) */ + fx = _mm_mul_ps(x, *(v4sf*)_ps_cephes_LOG2EF); + fx = _mm_add_ps(fx, *(v4sf*)_ps_0p5); + + /* how to perform a floorf with SSE: just below */ +#ifndef USE_SSE2 + /* step 1 : cast to int */ + tmp = _mm_movehl_ps(tmp, fx); + mm0 = _mm_cvttps_pi32(fx); + mm1 = _mm_cvttps_pi32(tmp); + /* step 2 : cast back to float */ + tmp = _mm_cvtpi32x2_ps(mm0, mm1); +#else + emm0 = _mm_cvttps_epi32(fx); + tmp = _mm_cvtepi32_ps(emm0); +#endif + /* if greater, substract 1 */ + v4sf mask = _mm_cmpgt_ps(tmp, fx); + mask = _mm_and_ps(mask, one); + fx = _mm_sub_ps(tmp, mask); + + tmp = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C1); + v4sf z = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C2); + x = _mm_sub_ps(x, tmp); + x = _mm_sub_ps(x, z); + + z = _mm_mul_ps(x,x); + + v4sf y = *(v4sf*)_ps_cephes_exp_p0; + y = _mm_mul_ps(y, x); + y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p1); + y = _mm_mul_ps(y, x); + y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p2); + y = _mm_mul_ps(y, x); + y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p3); + y = _mm_mul_ps(y, x); + y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p4); + y = _mm_mul_ps(y, x); + y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p5); + y = _mm_mul_ps(y, z); + y = _mm_add_ps(y, x); + y = _mm_add_ps(y, one); + + /* build 2^n */ +#ifndef USE_SSE2 + z = _mm_movehl_ps(z, fx); + mm0 = _mm_cvttps_pi32(fx); + mm1 = _mm_cvttps_pi32(z); + mm0 = _mm_add_pi32(mm0, *(v2si*)_pi32_0x7f); + mm1 = _mm_add_pi32(mm1, *(v2si*)_pi32_0x7f); + mm0 = _mm_slli_pi32(mm0, 23); + mm1 = _mm_slli_pi32(mm1, 23); + + v4sf pow2n; + COPY_MM_TO_XMM(mm0, mm1, pow2n); + _mm_empty(); +#else + emm0 = _mm_cvttps_epi32(fx); + emm0 = _mm_add_epi32(emm0, *(v4si*)_pi32_0x7f); + emm0 = _mm_slli_epi32(emm0, 23); + v4sf pow2n = _mm_castsi128_ps(emm0); +#endif + y = _mm_mul_ps(y, pow2n); + return y; +} + +_PS_CONST(minus_cephes_DP1, -0.78515625); +_PS_CONST(minus_cephes_DP2, -2.4187564849853515625e-4); +_PS_CONST(minus_cephes_DP3, -3.77489497744594108e-8); +_PS_CONST(sincof_p0, -1.9515295891E-4); +_PS_CONST(sincof_p1, 8.3321608736E-3); +_PS_CONST(sincof_p2, -1.6666654611E-1); +_PS_CONST(coscof_p0, 2.443315711809948E-005); +_PS_CONST(coscof_p1, -1.388731625493765E-003); +_PS_CONST(coscof_p2, 4.166664568298827E-002); +_PS_CONST(cephes_FOPI, 1.27323954473516); // 4 / M_PI + + +/* evaluation of 4 sines at onces, using only SSE1+MMX intrinsics so + it runs also on old athlons XPs and the pentium III of your grand + mother. + + The code is the exact rewriting of the cephes sinf function. + Precision is excellent as long as x < 8192 (I did not bother to + take into account the special handling they have for greater values + -- it does not return garbage for arguments over 8192, though, but + the extra precision is missing). + + Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the + surprising but correct result. + + Performance is also surprisingly good, 1.33 times faster than the + macos vsinf SSE2 function, and 1.5 times faster than the + __vrs4_sinf of amd's ACML (which is only available in 64 bits). Not + too bad for an SSE1 function (with no special tuning) ! + However the latter libraries probably have a much better handling of NaN, + Inf, denormalized and other special arguments.. + + On my core 1 duo, the execution of this function takes approximately 95 cycles. + + From what I have observed on the experiments with Intel AMath lib, switching to an + SSE2 version would improve the perf by only 10%. + + Since it is based on SSE intrinsics, it has to be compiled at -O2 to + deliver full speed. +*/ +v4sf sin_ps(v4sf x) { // any x + v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, sign_bit, y; + +#ifdef USE_SSE2 + v4si emm0, emm2; +#else + v2si mm0, mm1, mm2, mm3; +#endif + sign_bit = x; + /* take the absolute value */ + x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask); + /* extract the sign bit (upper one) */ + sign_bit = _mm_and_ps(sign_bit, *(v4sf*)_ps_sign_mask); + + /* scale by 4/Pi */ + y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI); + +#ifdef USE_SSE2 + /* store the integer part of y in mm0 */ + emm2 = _mm_cvttps_epi32(y); + /* j=(j+1) & (~1) (see the cephes sources) */ + emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1); + emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1); + y = _mm_cvtepi32_ps(emm2); + + /* get the swap sign flag */ + emm0 = _mm_and_si128(emm2, *(v4si*)_pi32_4); + emm0 = _mm_slli_epi32(emm0, 29); + /* get the polynom selection mask + there is one polynom for 0 <= x <= Pi/4 + and another one for Pi/4 +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#ifndef KINC_NO_CLIB +#define STBI_HAS_LROTL +#endif +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 8) { + STBI_ASSERT(ri.bits_per_channel == 16); + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 16) { + STBI_ASSERT(ri.bits_per_channel == 8); + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) + c = stbi__zreceive(a,3)+3; + else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[288] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + //Originally this was idata_limit *= 2. Changed to append a constant size on realloc instead of doubleing everytime. + //This saves some space on the image.c buffer. + idata_limit += 1 << 16; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth < 8) + ri->bits_per_channel = 8; + else + ri->bits_per_channel = p->depth; + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1; z >>= 1; } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v >= 0 && v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - 14 - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - 14 - info.hsz) >> 2; + } + + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - 14 - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispoase of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + if (delays) { + *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) *comp = info.ma ? 4 : 3; + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + (void) stbi__get32be(s); + (void) stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/armorcore/sources/kinc/libs/stb_sprintf.h b/armorcore/sources/kinc/libs/stb_sprintf.h new file mode 100644 index 000000000..ca432a6bc --- /dev/null +++ b/armorcore/sources/kinc/libs/stb_sprintf.h @@ -0,0 +1,1906 @@ +// stb_sprintf - v1.10 - public domain snprintf() implementation +// originally by Jeff Roberts / RAD Game Tools, 2015/10/20 +// http://github.com/nothings/stb +// +// allowed types: sc uidBboXx p AaGgEef n +// lengths : hh h ll j z t I64 I32 I +// +// Contributors: +// Fabian "ryg" Giesen (reformatting) +// github:aganm (attribute format) +// +// Contributors (bugfixes): +// github:d26435 +// github:trex78 +// github:account-login +// Jari Komppa (SI suffixes) +// Rohit Nirmal +// Marcin Wojdyr +// Leonard Ritter +// Stefano Zanotti +// Adam Allison +// Arvid Gerstmann +// Markus Kolb +// +// LICENSE: +// +// See end of file for license information. + +#ifndef STB_SPRINTF_H_INCLUDE +#define STB_SPRINTF_H_INCLUDE + +/* +Single file sprintf replacement. + +Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. +Hereby placed in public domain. + +This is a full sprintf replacement that supports everything that +the C runtime sprintfs support, including float/double, 64-bit integers, +hex floats, field parameters (%*.*d stuff), length reads backs, etc. + +Why would you need this if sprintf already exists? Well, first off, +it's *much* faster (see below). It's also much smaller than the CRT +versions code-space-wise. We've also added some simple improvements +that are super handy (commas in thousands, callbacks at buffer full, +for example). Finally, the format strings for MSVC and GCC differ +for 64-bit integers (among other small things), so this lets you use +the same format strings in cross platform code. + +It uses the standard single file trick of being both the header file +and the source itself. If you just include it normally, you just get +the header file function definitions. To get the code, you include +it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. + +It only uses va_args macros from the C runtime to do it's work. It +does cast doubles to S64s and shifts and divides U64s, which does +drag in CRT code on most platforms. + +It compiles to roughly 8K with float support, and 4K without. +As a comparison, when using MSVC static libs, calling sprintf drags +in 16K. + +API: +==== +int stbsp_sprintf( char * buf, char const * fmt, ... ) +int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) + Convert an arg list into a buffer. stbsp_snprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) +int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) + Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) + typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); + Convert into a buffer, calling back every STB_SPRINTF_MIN chars. + Your callback can then copy the chars out, print them or whatever. + This function is actually the workhorse for everything else. + The buffer you pass in must hold at least STB_SPRINTF_MIN characters. + // you return the next buffer to use or 0 to stop converting + +void stbsp_set_separators( char comma, char period ) + Set the comma and period characters to use. + +FLOATS/DOUBLES: +=============== +This code uses a internal float->ascii conversion method that uses +doubles with error correction (double-doubles, for ~105 bits of +precision). This conversion is round-trip perfect - that is, an atof +of the values output here will give you the bit-exact double back. + +One difference is that our insignificant digits will be different than +with MSVC or GCC (but they don't match each other either). We also +don't attempt to find the minimum length matching float (pre-MSVC15 +doesn't either). + +If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT +and you'll save 4K of code space. + +64-BIT INTS: +============ +This library also supports 64-bit integers and you can use MSVC style or +GCC style indicators (%I64d or %lld). It supports the C99 specifiers +for size_t and ptr_diff_t (%jd %zd) as well. + +EXTRAS: +======= +Like some GCCs, for integers and floats, you can use a ' (single quote) +specifier and commas will be inserted on the thousands: "%'d" on 12345 +would print 12,345. + +For integers and floats, you can use a "$" specifier and the number +will be converted to float and then divided to get kilo, mega, giga or +tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is +"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn +2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three +$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the +suffix, add "_" specifier: "%_$d" -> "2.53M". + +In addition to octal and hexadecimal conversions, you can print +integers in binary: "%b" for 256 would print 100. + +PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): +=================================================================== +"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) +"%24d" across all 32-bit ints (4.5x/4.2x faster) +"%x" across all 32-bit ints (4.5x/3.8x faster) +"%08x" across all 32-bit ints (4.3x/3.8x faster) +"%f" across e-10 to e+10 floats (7.3x/6.0x faster) +"%e" across e-10 to e+10 floats (8.1x/6.0x faster) +"%g" across e-10 to e+10 floats (10.0x/7.1x faster) +"%f" for values near e-300 (7.9x/6.5x faster) +"%f" for values near e+300 (10.0x/9.1x faster) +"%e" for values near e-300 (10.1x/7.0x faster) +"%e" for values near e+300 (9.2x/6.0x faster) +"%.320f" for values near e-300 (12.6x/11.2x faster) +"%a" for random values (8.6x/4.3x faster) +"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) +"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) +"%s%s%s" for 64 char strings (7.1x/7.3x faster) +"...512 char string..." ( 35.0x/32.5x faster!) +*/ + +#if defined(__clang__) + #if defined(__has_feature) && defined(__has_attribute) + #if __has_feature(address_sanitizer) + #if __has_attribute(__no_sanitize__) + #define STBSP__ASAN __attribute__((__no_sanitize__("address"))) + #elif __has_attribute(__no_sanitize_address__) + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #elif __has_attribute(__no_address_safety_analysis__) + #define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) + #endif + #endif + #endif +#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #endif +#endif + +#ifndef STBSP__ASAN +#define STBSP__ASAN +#endif + +#ifdef STB_SPRINTF_STATIC +#define STBSP__PUBLICDEC static +#define STBSP__PUBLICDEF static STBSP__ASAN +#else +#ifdef __cplusplus +#define STBSP__PUBLICDEC extern "C" +#define STBSP__PUBLICDEF extern "C" STBSP__ASAN +#else +#define STBSP__PUBLICDEC extern +#define STBSP__PUBLICDEF STBSP__ASAN +#endif +#endif + +#if defined(__has_attribute) + #if __has_attribute(format) + #define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) + #endif +#endif + +#ifndef STBSP__ATTRIBUTE_FORMAT +#define STBSP__ATTRIBUTE_FORMAT(fmt,va) +#endif + +#ifdef _MSC_VER +#define STBSP__NOTUSED(v) (void)(v) +#else +#define STBSP__NOTUSED(v) (void)sizeof(v) +#endif + +#include // for va_arg(), va_list() +#include // size_t, ptrdiff_t + +#ifndef STB_SPRINTF_MIN +#define STB_SPRINTF_MIN 512 // how many characters per callback +#endif +typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); + +#ifndef STB_SPRINTF_DECORATE +#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names +#endif + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); + +#endif // STB_SPRINTF_H_INCLUDE + +#ifdef STB_SPRINTF_IMPLEMENTATION + +#define stbsp__uint32 unsigned int +#define stbsp__int32 signed int + +#ifdef _MSC_VER +#define stbsp__uint64 unsigned __int64 +#define stbsp__int64 signed __int64 +#else +#define stbsp__uint64 unsigned long long +#define stbsp__int64 signed long long +#endif +#define stbsp__uint16 unsigned short + +#ifndef stbsp__uintptr +#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) +#define stbsp__uintptr stbsp__uint64 +#else +#define stbsp__uintptr stbsp__uint32 +#endif +#endif + +#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define STB_SPRINTF_MSVC_MODE +#endif +#endif + +#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses +#define STBSP__UNALIGNED(code) +#else +#define STBSP__UNALIGNED(code) code +#endif + +#ifndef STB_SPRINTF_NOFLOAT +// internal float utility functions +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); +#define STBSP__SPECIAL 0x7000 +#endif + +static char stbsp__period = '.'; +static char stbsp__comma = ','; +static struct +{ + short temp; // force next field to be 2-byte aligned + char pair[201]; +} stbsp__digitpair = +{ + 0, + "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899" +}; + +STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) +{ + stbsp__period = pperiod; + stbsp__comma = pcomma; +} + +#define STBSP__LEFTJUST 1 +#define STBSP__LEADINGPLUS 2 +#define STBSP__LEADINGSPACE 4 +#define STBSP__LEADING_0X 8 +#define STBSP__LEADINGZERO 16 +#define STBSP__INTMAX 32 +#define STBSP__TRIPLET_COMMA 64 +#define STBSP__NEGATIVE 128 +#define STBSP__METRIC_SUFFIX 256 +#define STBSP__HALFWIDTH 512 +#define STBSP__METRIC_NOSPACE 1024 +#define STBSP__METRIC_1024 2048 +#define STBSP__METRIC_JEDEC 4096 + +static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) +{ + sign[0] = 0; + if (fl & STBSP__NEGATIVE) { + sign[0] = 1; + sign[1] = '-'; + } else if (fl & STBSP__LEADINGSPACE) { + sign[0] = 1; + sign[1] = ' '; + } else if (fl & STBSP__LEADINGPLUS) { + sign[0] = 1; + sign[1] = '+'; + } +} + +static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) +{ + char const * sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((stbsp__uintptr)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (stbsp__uint32)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (stbsp__uint32)(sn - s); +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) +{ + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + char *bf; + char const *f; + int tlen = 0; + + bf = buf; + f = fmt; + for (;;) { + stbsp__int32 fw, pr, tz; + stbsp__uint32 fl; + + // macros for the callback buffer stuff + #define stbsp__chk_cb_bufL(bytes) \ + { \ + int len = (int)(bf - buf); \ + if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ + tlen += len; \ + if (0 == (bf = buf = callback(buf, user, len))) \ + goto done; \ + } \ + } + #define stbsp__chk_cb_buf(bytes) \ + { \ + if (callback) { \ + stbsp__chk_cb_bufL(bytes); \ + } \ + } + #define stbsp__flush_cb() \ + { \ + stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ + } // flush if there is even one byte in the buffer + #define stbsp__cb_buf_clamp(cl, v) \ + cl = v; \ + if (callback) { \ + int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ + if (cl > lg) \ + cl = lg; \ + } + + // fast copy everything up to the next % (or end of string) + for (;;) { + while (((stbsp__uintptr)f) & 3) { + schk1: + if (f[0] == '%') + goto scandd; + schk2: + if (f[0] == 0) + goto endfmt; + stbsp__chk_cb_buf(1); + *bf++ = f[0]; + ++f; + } + for (;;) { + // Check if the next 4 bytes contain %(0x25) or end of string. + // Using the 'hasless' trick: + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + stbsp__uint32 v, c; + v = *(stbsp__uint32 *)f; + c = (~v) & 0x80808080; + if (((v ^ 0x25252525) - 0x01010101) & c) + goto schk1; + if ((v - 0x01010101) & c) + goto schk2; + if (callback) + if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) + goto schk1; + #ifdef STB_SPRINTF_NOUNALIGNED + if(((stbsp__uintptr)bf) & 3) { + bf[0] = f[0]; + bf[1] = f[1]; + bf[2] = f[2]; + bf[3] = f[3]; + } else + #endif + { + *(stbsp__uint32 *)bf = v; + } + bf += 4; + f += 4; + } + } + scandd: + + ++f; + + // ok, we have a percent, read the modifiers first + fw = 0; + pr = -1; + fl = 0; + tz = 0; + + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + fl |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + fl |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + fl |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + fl |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + fl |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have kilo marker (none->kilo->kibi->jedec) + case '$': + if (fl & STBSP__METRIC_SUFFIX) { + if (fl & STBSP__METRIC_1024) { + fl |= STBSP__METRIC_JEDEC; + } else { + fl |= STBSP__METRIC_1024; + } + } else { + fl |= STBSP__METRIC_SUFFIX; + } + ++f; + continue; + // if we don't want space between metric suffix and number + case '_': + fl |= STBSP__METRIC_NOSPACE; + ++f; + continue; + // if we have leading zero + case '0': + fl |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: goto flags_done; + } + } + flags_done: + + // get the field width + if (f[0] == '*') { + fw = va_arg(va, stbsp__uint32); + ++f; + } else { + while ((f[0] >= '0') && (f[0] <= '9')) { + fw = fw * 10 + f[0] - '0'; + f++; + } + } + // get the precision + if (f[0] == '.') { + ++f; + if (f[0] == '*') { + pr = va_arg(va, stbsp__uint32); + ++f; + } else { + pr = 0; + while ((f[0] >= '0') && (f[0] <= '9')) { + pr = pr * 10 + f[0] - '0'; + f++; + } + } + } + + // handle integer size overrides + switch (f[0]) { + // are we halfwidth? + case 'h': + fl |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH + break; + // are we 64-bit (unix style) + case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + fl |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + fl |= STBSP__INTMAX; + f += 3; + } else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } else { + fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); + ++f; + } + break; + default: break; + } + + // handle each replacement + switch (f[0]) { + #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 + char num[STBSP__NUMSZ]; + char lead[8]; + char tail[8]; + char *s; + char const *h; + stbsp__uint32 l, n, cs; + stbsp__uint64 n64; +#ifndef STB_SPRINTF_NOFLOAT + double fv; +#endif + stbsp__int32 dp; + char const *sn; + + case 's': + // get the string + s = va_arg(va, char *); + if (s == 0) + s = (char *)"null"; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + // copy the string in + goto scopy; + + case 'c': // char + // get the character + s = num + STBSP__NUMSZ - 1; + *s = (char)va_arg(va, int); + l = 1; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 'n': // weird write-bytes specifier + { + int *d = va_arg(va, int *); + *d = tlen + (int)(bf - buf); + } break; + +#ifdef STB_SPRINTF_NOFLOAT + case 'A': // float + case 'a': // hex float + case 'G': // float + case 'g': // float + case 'E': // float + case 'e': // float + case 'f': // float + va_arg(va, double); // eat it + s = (char *)"No float"; + l = 8; + lead[0] = 0; + tail[0] = 0; + pr = 0; + cs = 0; + STBSP__NOTUSED(dp); + goto scopy; +#else + case 'A': // hex float + case 'a': // hex float + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) + fl |= STBSP__NEGATIVE; + + s = num + 64; + + stbsp__lead_sign(fl, lead); + + if (dp == -1023) + dp = (n64) ? -1022 : 0; + else + n64 |= (((stbsp__uint64)1) << 52); + n64 <<= (64 - 56); + if (pr < 15) + n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); +// add leading chars + +#ifdef STB_SPRINTF_MSVC_MODE + *s++ = '0'; + *s++ = 'x'; +#else + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; +#endif + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (pr) + *s++ = stbsp__period; + sn = s; + + // print the bits + n = pr; + if (n > 13) + n = 13; + if (pr > (stbsp__int32)n) + tz = pr - n; + pr = 0; + while (n--) { + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + } + + // print the expo + tail[1] = h[17]; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; + n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + + dp = (int)(s - sn); + l = (int)(s - (num + 64)); + s = num + 64; + cs = 1 + (3 << 24); + goto scopy; + + case 'G': // float + case 'g': // float + h = (f[0] == 'G') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; + else if (pr == 0) + pr = 1; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) + fl |= STBSP__NEGATIVE; + + // clamp the precision and delete extra zeros after clamp + n = pr; + if (l > (stbsp__uint32)pr) + l = pr; + while ((l > 1) && (pr) && (sn[l - 1] == '0')) { + --pr; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (stbsp__int32)n)) { + if (pr > (stbsp__int32)l) + pr = l - 1; + else if (pr) + --pr; // when using %e, there is one digit before the decimal + goto doexpfromg; + } + // this is the insane action to get the pr to match %g semantics for %f + if (dp > 0) { + pr = (dp < (stbsp__int32)l) ? l - dp : 0; + } else { + pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); + } + goto dofloatfromg; + + case 'E': // float + case 'e': // float + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) + fl |= STBSP__NEGATIVE; + doexpfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (pr) + *s++ = stbsp__period; + + // handle after decimal + if ((l - 1) > (stbsp__uint32)pr) + l = pr + 1; + for (n = 1; n < l; n++) + *s++ = sn[n]; + // trailing zeros + tz = pr - (l - 1); + pr = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; +#ifdef STB_SPRINTF_MSVC_MODE + n = 5; +#else + n = (dp >= 100) ? 5 : 4; +#endif + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + cs = 1 + (3 << 24); // how many tens + goto flt_lead; + + case 'f': // float + fv = va_arg(va, double); + doafloat: + // do kilos + if (fl & STBSP__METRIC_SUFFIX) { + double divisor; + divisor = 1000.0f; + if (fl & STBSP__METRIC_1024) + divisor = 1024.0; + while (fl < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + fl += 0x1000000; + } + } + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) + fl |= STBSP__NEGATIVE; + dofloatfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + stbsp__int32 i; + // handle 0.000*000xxxx + *s++ = '0'; + if (pr) + *s++ = stbsp__period; + n = -dp; + if ((stbsp__int32)n > pr) + n = pr; + i = n; + while (i) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + i -= 4; + } + while (i) { + *s++ = '0'; + --i; + } + if ((stbsp__int32)(l + n) > pr) + l = pr - n; + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = pr - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } else { + cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; + if ((stbsp__uint32)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= l) + break; + } + } + if (n < (stbsp__uint32)dp) { + n = dp - n; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (n) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --n; + } + while (n >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + n -= 4; + } + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = '0'; + --n; + } + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) { + *s++ = stbsp__period; + tz = pr; + } + } else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= (stbsp__uint32)dp) + break; + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) + *s++ = stbsp__period; + if ((l - dp) > (stbsp__uint32)pr) + l = pr + dp; + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = pr - (l - dp); + } + } + pr = 0; + + // handle k,m,g,t + if (fl & STBSP__METRIC_SUFFIX) { + char idx; + idx = 1; + if (fl & STBSP__METRIC_NOSPACE) + idx = 0; + tail[0] = idx; + tail[1] = ' '; + { + if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. + if (fl & STBSP__METRIC_1024) + tail[idx + 1] = "_KMGT"[fl >> 24]; + else + tail[idx + 1] = "_kMGT"[fl >> 24]; + idx++; + // If printing kibits and not in jedec, add the 'i'. + if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { + tail[idx + 1] = 'i'; + idx++; + } + tail[0] = idx; + } + } + }; + + flt_lead: + // get the length that we copied + l = (stbsp__uint32)(s - (num + 64)); + s = num + 64; + goto scopy; +#endif + + case 'B': // upper binary + case 'b': // lower binary + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[0xb]; + } + l = (8 << 4) | (1 << 8); + goto radixnum; + + case 'o': // octal + h = hexu; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + + case 'p': // pointer + fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + pr = sizeof(void *) * 2; + fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros + // fall through - to X + + case 'X': // upper hex + case 'x': // lower hex + h = (f[0] == 'X') ? hexu : hex; + l = (4 << 4) | (4 << 8); + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + radixnum: + // get the number + if (fl & STBSP__INTMAX) + n64 = va_arg(va, stbsp__uint64); + else + n64 = va_arg(va, stbsp__uint32); + + s = num + STBSP__NUMSZ; + dp = 0; + // clear tail, and clear leading if value is zero + tail[0] = 0; + if (n64 == 0) { + lead[0] = 0; + if (pr == 0) { + l = 0; + cs = 0; + goto scopy; + } + } + // convert to string + for (;;) { + *--s = h[n64 & ((1 << (l >> 8)) - 1)]; + n64 >>= (l >> 8); + if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) + break; + if (fl & STBSP__TRIPLET_COMMA) { + ++l; + if ((l & 15) == ((l >> 4) & 15)) { + l &= ~15; + *--s = stbsp__comma; + } + } + }; + // get the tens and the comma pos + cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + // copy it + goto scopy; + + case 'u': // unsigned + case 'i': + case 'd': // integer + // get the integer and abs it + if (fl & STBSP__INTMAX) { + stbsp__int64 i64 = va_arg(va, stbsp__int64); + n64 = (stbsp__uint64)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (stbsp__uint64)-i64; + fl |= STBSP__NEGATIVE; + } + } else { + stbsp__int32 i = va_arg(va, stbsp__int32); + n64 = (stbsp__uint32)i; + if ((f[0] != 'u') && (i < 0)) { + n64 = (stbsp__uint32)-i; + fl |= STBSP__NEGATIVE; + } + } + +#ifndef STB_SPRINTF_NOFLOAT + if (fl & STBSP__METRIC_SUFFIX) { + if (n64 < 1024) + pr = 0; + else if (pr == -1) + pr = 1; + fv = (double)(stbsp__int64)n64; + goto doafloat; + } +#endif + + // convert to string + s = num + STBSP__NUMSZ; + l = 0; + + for (;;) { + // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) + char *o = s - 8; + if (n64 >= 100000000) { + n = (stbsp__uint32)(n64 % 100000000); + n64 /= 100000000; + } else { + n = (stbsp__uint32)n64; + n64 = 0; + } + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + do { + s -= 2; + *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + } while (n); + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = (char)(n % 10) + '0'; + n /= 10; + } + } + if (n64 == 0) { + if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) + ++s; + break; + } + while (s != o) + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = '0'; + } + } + + tail[0] = 0; + stbsp__lead_sign(fl, lead); + + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + if (l == 0) { + *--s = '0'; + l = 1; + } + cs = l + (3 << 24); + if (pr < 0) + pr = 0; + + scopy: + // get fw=leading/trailing space, pr=leading zeros + if (pr < (stbsp__int32)l) + pr = l; + n = pr + lead[0] + tail[0] + tz; + if (fw < (stbsp__int32)n) + fw = n; + fw -= n; + pr -= l; + + // handle right justify and leading zeros + if ((fl & STBSP__LEFTJUST) == 0) { + if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr + { + pr = (fw > pr) ? fw : pr; + fw = 0; + } else { + fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas + } + } + + // copy the spaces and/or zeros + if (fw + pr) { + stbsp__int32 i; + stbsp__uint32 c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((fl & STBSP__LEFTJUST) == 0) + while (fw > 0) { + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = ' '; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leader + sn = lead + 1; + while (lead[0]) { + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leading zeros + c = cs >> 24; + cs &= 0xffffff; + cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; + while (pr > 0) { + stbsp__cb_buf_clamp(i, pr); + pr -= i; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + } + while (i) { + if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { + cs = 0; + *bf++ = stbsp__comma; + } else + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + } + + // copy leader if there is still one + sn = lead + 1; + while (lead[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy the string + n = l; + while (n) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, n); + n -= i; + STBSP__UNALIGNED(while (i >= 4) { + *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; + bf += 4; + s += 4; + i -= 4; + }) + while (i) { + *bf++ = *s++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy trailing zeros + while (tz) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tz); + tz -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy tail if there is one + sn = tail + 1; + while (tail[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tail[0]); + tail[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // handle the left justify + if (fl & STBSP__LEFTJUST) + if (fw > 0) { + while (fw) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i--) + *bf++ = ' '; + stbsp__chk_cb_buf(1); + } + } + break; + + default: // unknown, just copy code + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + fw = fl = 0; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + } + ++f; + } +endfmt: + + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); + +done: + return tlen + (int)(bf - buf); +} + +// cleanup +#undef STBSP__LEFTJUST +#undef STBSP__LEADINGPLUS +#undef STBSP__LEADINGSPACE +#undef STBSP__LEADING_0X +#undef STBSP__LEADINGZERO +#undef STBSP__INTMAX +#undef STBSP__TRIPLET_COMMA +#undef STBSP__NEGATIVE +#undef STBSP__METRIC_SUFFIX +#undef STBSP__NUMSZ +#undef stbsp__chk_cb_bufL +#undef stbsp__chk_cb_buf +#undef stbsp__flush_cb +#undef stbsp__cb_buf_clamp + +// ============================================================================ +// wrapper functions + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); + va_end(va); + return result; +} + +typedef struct stbsp__context { + char *buf; + int count; + int length; + char tmp[STB_SPRINTF_MIN]; +} stbsp__context; + +static char *stbsp__clamp_callback(const char *buf, void *user, int len) +{ + stbsp__context *c = (stbsp__context *)user; + c->length += len; + + if (len > c->count) + len = c->count; + + if (len) { + if (buf != c->buf) { + const char *s, *se; + char *d; + d = c->buf; + s = buf; + se = buf + len; + do { + *d++ = *s++; + } while (s < se); + } + c->buf += len; + c->count -= len; + } + + if (c->count <= 0) + return c->tmp; + return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can +} + +static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) +{ + stbsp__context * c = (stbsp__context*)user; + (void) sizeof(buf); + + c->length += len; + return c->tmp; // go direct into buffer if you can +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) +{ + stbsp__context c; + + if ( (count == 0) && !buf ) + { + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); + } + else + { + int l; + + c.buf = buf; + c.count = count; + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); + + // zero-terminate + l = (int)( c.buf - buf ); + if ( l >= count ) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } + + return c.length; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + + result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); + va_end(va); + + return result; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) +{ + return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); +} + +// ======================================================================= +// low level float utility functions + +#ifndef STB_SPRINTF_NOFLOAT + +// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) +#define STBSP__COPYFP(dest, src) \ + { \ + int cn; \ + for (cn = 0; cn < 8; cn++) \ + ((char *)&dest)[cn] = ((char *)&src)[cn]; \ + } + +// get float info +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) +{ + double d; + stbsp__int64 b = 0; + + // load value and round at the frac_digits + d = value; + + STBSP__COPYFP(b, d); + + *bits = b & ((((stbsp__uint64)1) << 52) - 1); + *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); + + return (stbsp__int32)((stbsp__uint64) b >> 63); +} + +static double const stbsp__bot[23] = { + 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, + 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 +}; +static double const stbsp__negbot[22] = { + 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, + 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 +}; +static double const stbsp__negboterr[22] = { + -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, + 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, + -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, + 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 +}; +static double const stbsp__top[13] = { + 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 +}; +static double const stbsp__negtop[13] = { + 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 +}; +static double const stbsp__toperr[13] = { + 8388608, + 6.8601809640529717e+028, + -7.253143638152921e+052, + -4.3377296974619174e+075, + -1.5559416129466825e+098, + -3.2841562489204913e+121, + -3.7745893248228135e+144, + -1.7356668416969134e+167, + -3.8893577551088374e+190, + -9.9566444326005119e+213, + 6.3641293062232429e+236, + -5.2069140800249813e+259, + -5.2504760255204387e+282 +}; +static double const stbsp__negtoperr[13] = { + 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, + -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, + 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, + 8.0970921678014997e-317 +}; + +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000U +}; +#define stbsp__tento19th ((stbsp__uint64)1000000000000000000) +#else +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL +}; +#define stbsp__tento19th (1000000000000000000ULL) +#endif + +#define stbsp__ddmulthi(oh, ol, xh, yh) \ + { \ + double ahi = 0, alo, bhi = 0, blo; \ + stbsp__int64 bt; \ + oh = xh * yh; \ + STBSP__COPYFP(bt, xh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(ahi, bt); \ + alo = xh - ahi; \ + STBSP__COPYFP(bt, yh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(bhi, bt); \ + blo = yh - bhi; \ + ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ + } + +#define stbsp__ddtoS64(ob, xh, xl) \ + { \ + double ahi = 0, alo, vh, t; \ + ob = (stbsp__int64)xh; \ + vh = (double)ob; \ + ahi = (xh - vh); \ + t = (ahi - xh); \ + alo = (xh - (ahi - t)) - (vh + t); \ + ob += (stbsp__int64)(ahi + alo + xl); \ + } + +#define stbsp__ddrenorm(oh, ol) \ + { \ + double s; \ + s = oh + ol; \ + ol = ol - (s - oh); \ + oh = s; \ + } + +#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); + +#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); + +static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 +{ + double ph, pl; + if ((power >= 0) && (power <= 22)) { + stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); + } else { + stbsp__int32 e, et, eb; + double p2h, p2l; + + e = power; + if (power < 0) + e = -e; + et = (e * 0x2c9) >> 14; /* %23 */ + if (et > 13) + et = 13; + eb = e - (et * 23); + + ph = d; + pl = 0.0; + if (power < 0) { + if (eb) { + --eb; + stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); + stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); + ph = p2h; + pl = p2l; + } + } else { + if (eb) { + e = eb; + if (eb > 22) + eb = 22; + e -= eb; + stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); + if (e) { + stbsp__ddrenorm(ph, pl); + stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); + stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); + ph = p2h; + pl = p2l; + } + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); + ph = p2h; + pl = p2l; + } + } + } + stbsp__ddrenorm(ph, pl); + *ohi = ph; + *olo = pl; +} + +// given a float value, returns the significant bits in bits, and the position of the +// decimal point in decimal_pos. +/-INF and NAN are specified by special values +// returned in the decimal_pos parameter. +// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) +{ + double d; + stbsp__int64 bits = 0; + stbsp__int32 expo, e, ng, tens; + + d = value; + STBSP__COPYFP(bits, d); + expo = (stbsp__int32)((bits >> 52) & 2047); + ng = (stbsp__int32)((stbsp__uint64) bits >> 63); + if (ng) + d = -d; + + if (expo == 2047) // is nan or inf? + { + *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; + *decimal_pos = STBSP__SPECIAL; + *len = 3; + return ng; + } + + if (expo == 0) // is zero or denormal + { + if (((stbsp__uint64) bits << 1) == 0) // do zero + { + *decimal_pos = 1; + *start = out; + out[0] = '0'; + *len = 1; + return ng; + } + // find the right expo for denormals + { + stbsp__int64 v = ((stbsp__uint64)1) << 51; + while ((bits & v) == 0) { + --expo; + v >>= 1; + } + } + } + + // find the decimal exponent as well as the decimal bits of the value + { + double ph, pl; + + // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 + tens = expo - 1023; + tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); + + // move the significant bits into position and stick them into an int + stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); + + // get full as much precision from double-double as possible + stbsp__ddtoS64(bits, ph, pl); + + // check if we undershot + if (((stbsp__uint64)bits) >= stbsp__tento19th) + ++tens; + } + + // now do the rounding in integer land + frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); + if ((frac_digits < 24)) { + stbsp__uint32 dg = 1; + if ((stbsp__uint64)bits >= stbsp__powten[9]) + dg = 10; + while ((stbsp__uint64)bits >= stbsp__powten[dg]) { + ++dg; + if (dg == 20) + goto noround; + } + if (frac_digits < dg) { + stbsp__uint64 r; + // add 0.5 at the right position and round + e = dg - frac_digits; + if ((stbsp__uint32)e >= 24) + goto noround; + r = stbsp__powten[e]; + bits = bits + (r / 2); + if ((stbsp__uint64)bits >= stbsp__powten[dg]) + ++tens; + bits /= r; + } + noround:; + } + + // kill long trailing runs of zeros + if (bits) { + stbsp__uint32 n; + for (;;) { + if (bits <= 0xffffffff) + break; + if (bits % 1000) + goto donez; + bits /= 1000; + } + n = (stbsp__uint32)bits; + while ((n % 1000) == 0) + n /= 1000; + bits = n; + donez:; + } + + // convert to string + out += 64; + e = 0; + for (;;) { + stbsp__uint32 n; + char *o = out - 8; + // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) + if (bits >= 100000000) { + n = (stbsp__uint32)(bits % 100000000); + bits /= 100000000; + } else { + n = (stbsp__uint32)bits; + bits = 0; + } + while (n) { + out -= 2; + *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + e += 2; + } + if (bits == 0) { + if ((e) && (out[0] == '0')) { + ++out; + --e; + } + break; + } + while (out != o) { + *--out = '0'; + ++e; + } + } + + *decimal_pos = tens; + *start = out; + *len = e; + return ng; +} + +#undef stbsp__ddmulthi +#undef stbsp__ddrenorm +#undef stbsp__ddmultlo +#undef stbsp__ddmultlos +#undef STBSP__SPECIAL +#undef STBSP__COPYFP + +#endif // STB_SPRINTF_NOFLOAT + +// clean up +#undef stbsp__uint16 +#undef stbsp__uint32 +#undef stbsp__int32 +#undef stbsp__uint64 +#undef stbsp__int64 +#undef STBSP__UNALIGNED + +#endif // STB_SPRINTF_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/armorcore/sources/kinc/libs/stb_vorbis.c b/armorcore/sources/kinc/libs/stb_vorbis.c new file mode 100644 index 000000000..05047621e --- /dev/null +++ b/armorcore/sources/kinc/libs/stb_vorbis.c @@ -0,0 +1,5520 @@ +// Ogg Vorbis audio decoder - v1.17 - public domain +// http://nothings.org/stb_vorbis/ +// +// Original version written by Sean Barrett in 2007. +// +// Originally sponsored by RAD Game Tools. Seeking implementation +// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker, +// Elias Software, Aras Pranckevicius, and Sean Barrett. +// +// LICENSE +// +// See end of file for license information. +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Feature contributors: +// Dougall Johnson (sample-exact seeking) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster alxprd@github +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// Phillip Bennefall Rohit Thiago Goulart +// manxorist@github saga musix github:infatum +// Timur Gagiev Maxwell Koo +// +// Partial history: +// 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure) +// 1.16 - 2019-03-04 - fix warnings +// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found +// 1.14 - 2018-02-11 - delete bogus dealloca usage +// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) +// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files +// 1.11 - 2017-07-23 - fix MinGW compilation +// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory +// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version +// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame +// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const +// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) +// some crash fixes when out of memory or with corrupt files +// fix some inappropriately signed shifts +// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant +// 1.04 - 2014-08-27 - fix missing const-correct case in API +// 1.03 - 2014-08-07 - warning fixes +// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// +// See end of file for full version history. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#define STB_VORBIS_NO_STDIO + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wcomma" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char * datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + const stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, + const unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Moreover, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern int stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0) + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of *samples* per channel. +// Note that for interleaved data, you pass in the number of shorts (the +// size of your array), but the return value is the number of samples per +// channel, not the total number of samples. +// +// The data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed, + VORBIS_ogg_skeleton_not_supported +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef STB_VORBIS_HEADER_ONLY + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +#ifdef STB_VORBIS_CODEBOOK_SHORTS +#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #define STB_VORBIS_NO_STDIO +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT + #include + #include + #include + #include + + // find definition of alloca if it's not in stdlib.h: + #if defined(_MSC_VER) || defined(__MINGW32__) + #include + #endif + #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) + #include + #endif + +#else // STB_VORBIS_NO_CRT + #define NULL 0 + #define malloc(s) 0 + #define free(s) ((void) 0) + #define realloc(s) 0 +#endif // STB_VORBIS_NO_CRT + +#include + +#ifdef __MINGW32__ + // eff you mingw: + // "fixed": + // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/ + // "no that broke the build, reverted, who cares about C": + // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/ + #ifdef __forceinline + #undef __forceinline + #endif + #define __forceinline + #define alloca __builtin_alloca +#elif !defined(_MSC_VER) + #if __GNUC__ + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + +#if defined(__FreeBSD__) && !defined(KINC_PS4) && !defined(KINC_PS5) + #ifdef alloca + #undef alloca + #endif + #define alloca allocm +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#if 0 +#include +#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) +#else +#define CHECK(f) ((void) 0) +#endif + +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) + + +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +typedef float codetype; + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #else + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #endif + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} Codebook; + +typedef struct +{ + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; + +typedef struct +{ + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; +} Floor1; + +typedef union +{ + Floor0 floor0; + Floor1 floor1; +} Floor; + +typedef struct +{ + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; + +typedef struct +{ + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; + +typedef struct +{ + uint16 coupling_steps; + MappingChannel *chan; + uint8 submaps; + uint8 submap_floor[15]; // varies + uint8 submap_residue[15]; // varies +} Mapping; + +typedef struct +{ + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; + +typedef struct +{ + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page +} CRCscan; + +typedef struct +{ + uint32 page_start, page_end; + uint32 last_decoded_sample; +} ProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + uint32 f_start; + int close_on_free; +#endif + + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; + + uint32 stream_len; + + uint8 push_mode; + + uint32 first_audio_page_offset; + + ProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies + + uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + uint16 *bit_reverse[2]; + + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; +}; + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define IS_PUSH_MODE(f) TRUE +#else + #define IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis vorb; + +static int error(vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) +#define temp_free(f,p) 0 +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *setup_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; +} + +static void setup_free(vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack + free(p); +} + +static void *setup_temp_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); +} + +static void setup_temp_free(vorb *f, void *p, int sz) +{ + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+3)&~3; + return; + } + free(p); +} + +#define CRC32_POLY 0x04c11db7 // from spec + +static uint32 crc_table[256]; +static void crc32_init(void) +{ + int i,j; + uint32 s; + for(i=0; i < 256; i++) { + for (s=(uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); + crc_table[i] = s; + } +} + +static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) +{ + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int ilog(int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + if (n < 0) return 0; // signed n returns 0 + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else return 30 + log2_4[n >> 30]; +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float float32_unpack(uint32 x) +{ + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) +{ + int i,k,m=0; + uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1U << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { return FALSE; } + res = available[z]; + assert(z >= 0 && z < 32); + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propagate availability up the tree + if (z != len[i]) { + assert(len[i] >= 0 && len[i] < 32); + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void compute_accelerated_huffman(Codebook *c) +{ + int i, len; + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif + +static int STBV_CDECL uint32_compare(const void *p, const void *q) +{ + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; + return x < y ? -1 : x > y; +} + +static int include_in_sort(Codebook *c, uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int vorbis_validate(uint8 *data) +{ + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +static int lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + if (pow((float) r+1, dim) <= entries) + return -1; + if ((int) floor(pow((float) r, dim)) > entries) + return -1; + return r; +} + +// called twice per file +static void compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void compute_bitreverse(int n, uint16 *rev) +{ + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int init_blocksize(vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; +} + +static void neighbors(uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + uint16 x,id; +} stbv__floor_ordering; + +static int STBV_CDECL point_compare(const void *p, const void *q) +{ + stbv__floor_ordering *a = (stbv__floor_ordering *) p; + stbv__floor_ordering *b = (stbv__floor_ordering *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define USE_MEMORY(z) TRUE +#else + #define USE_MEMORY(z) ((z)->stream) +#endif + +static uint8 get8(vorb *z) +{ + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif +} + +static uint32 get32(vorb *f) +{ + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += (uint32) get8(f) << 24; + return x; +} + +static int getn(vorb *z, uint8 *data, int n) +{ + if (USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif +} + +static void skip(vorb *z, int n) +{ + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif +} + + +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int capture_pattern(vorb *f) +{ + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; + return TRUE; +} + +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 + +static int start_page_no_capturepattern(vorb *f) +{ + uint32 loc0,loc1,n; + // stream structure version + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + ProbedPage p; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + p.page_start = f->first_audio_page_offset; + p.page_end = p.page_start + len; + p.last_decoded_sample = loc0; + f->p_first = p; + } + f->next_seg = 0; + return TRUE; +} + +static int start_page(vorb *f) +{ + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); +} + +static int start_packet(vorb *f) +{ + while (f->next_seg == -1) { + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int maybe_start_packet(vorb *f) +{ + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); +} + +static int next_segment(vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define EOP (-1) +#define INVALID_BITS (-1) + +static int get8_packet_raw(vorb *f) +{ + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); +} + +static int get8_packet(vorb *f) +{ + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static uint32 get_bits(vorb *f, int n) +{ + uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + if (f->valid_bits < 0) return 0; + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +static __forceinline void prep_huffman(vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = get8_packet_raw(f); + if (z == EOP) return; + f->acc += (unsigned) z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5 +}; + +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) +{ + int i; + prep_huffman(f); + + if (c->codewords == NULL && c->sorted_codewords == NULL) + return -1; + + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = codebook_decode_scalar_raw(f,c); \ + } + +#else + +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); + +#endif + +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) DECODE(var,f,c) +#endif + + + + + + +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_BASE(c) (0) + +static int codebook_decode_start(vorb *f, Codebook *c) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) +{ + int i,z = codebook_decode_start(f,c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) +{ + int i,z = codebook_decode_start(f,c); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +static int predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define LINE_OP(a,b) a *= b +#else +#define LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +#endif + +static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + if (x < x1) { + LINE_OP(output[x], inverse_db_table[y&255]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y&255]); + } + } +} + +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +// n is 1/2 of the blocksize -- +// specification: "Correct per-vector decode length is [n]/2" +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) +{ + int i,j,pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + unsigned int actual_size = rtype == 2 ? n*2 : n; + unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); + unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + CHECK(f); + + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch == 1) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = 0, p_inter = z; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = 0; + p_inter = z; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + CHECK(f); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks+r->classbook; + int temp; + DECODE(temp,f,c); + if (temp == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + CHECK(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + temp_free(f,part_classdata); + #else + temp_free(f,classifications); + #endif + temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#elif 0 +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + buffer[i] = acc; + } +} + +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +static __forceinline void iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + + k00 = z[-0] - z[-8]; + k11 = z[-1] - z[-9]; + z[-0] = z[-0] + z[-8]; + z[-1] = z[-1] + z[-9]; + z[-8] = k00; + z[-9] = k11 ; + + k00 = z[ -2] - z[-10]; + k11 = z[ -3] - z[-11]; + z[ -2] = z[ -2] + z[-10]; + z[ -3] = z[ -3] + z[-11]; + z[-10] = (k00+k11) * A2; + z[-11] = (k11-k00) * A2; + + k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k11 = z[ -5] - z[-13]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[-12] = k11; + z[-13] = k00; + + k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation + k11 = z[ -7] - z[-15]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-14] = (k00+k11) * A2; + z[-15] = (k00-k11) * A2; + + iter_54(z); + iter_54(z-8); + z -= 16; + } +} + +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propagates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + temp_free(f,buf2); + temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *get_window(vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef int16 YTYPE; +#else +typedef int YTYPE; +#endif +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + draw_line(target, lx,ly, hx,hy, n2); + CHECK(f); + lx = hx, ly = hy; + } + } + if (lx < n2) { + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); + CHECK(f); + } + } + return TRUE; +} + +// The meaning of "left" and "right" +// +// For a given frame: +// we compute samples from 0..n +// window_center is n/2 +// we'll window and mix the samples from left_start to left_end with data from the previous frame +// all of the samples from left_end to right_start can be output without mixing; however, +// this interval is 0-length except when transitioning between short and long frames +// all of the samples from right_start to right_end need to be mixed with the next frame, +// which we don't have, so those get saved in a buffer +// frame N's right_end-right_start, the number of samples to mix with the next frame, +// has to be the same as frame N+1's left_end-left_start (which they are by +// construction) + +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f,1); + next = get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + + return TRUE; +} + +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + Mapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + +// WINDOWING + + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + CHECK(f); + + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + CHECK(f); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + + CHECK(f); +// RESIDUE DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + CHECK(f); + +// INVERSE COUPLING + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + CHECK(f); + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + CHECK(f); + for (i=0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + CHECK(f); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = -n2; // start of first frame is positioned for discard + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet; + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + (right_end-left_start)) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; // this doesn't seem right, but has no ill effect on my test files + if (*len > right_end) *len = right_end; // this should never happen + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + CHECK(f); + + return TRUE; +} + +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = get_window(f, n); + if (w == NULL) return 0; + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static int vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left, res; + res = vorbis_decode_packet(f, &len, &left, &right); + if (res) + vorbis_finish_frame(f, len, left, right); + return res; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int is_whole_packet_present(stb_vorbis *f, int end_page) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (end_page) + if (s < f->segment_count-1) return error(f, VORBIS_invalid_stream); + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (end_page) + if (s < n-1) return error(f, VORBIS_invalid_stream); + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int start_decoder(vorb *f) +{ + uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + + if (!start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) { + // check for the Ogg skeleton fishead identifying header to refine our error + if (f->segments[0] == 64 && + getn(f, header, 6) && + header[0] == 'f' && + header[1] == 'i' && + header[2] == 's' && + header[3] == 'h' && + header[4] == 'e' && + header[5] == 'a' && + get8(f) == 'd' && + get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported); + else + return error(f, VORBIS_invalid_first_page); + } + + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { + int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f, TRUE)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f,8) + 1; + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total=0; + uint8 *lengths; + Codebook *c = f->codebooks+i; + CHECK(f); + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); + + if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); + + if (c->sparse) + lengths = (uint8 *) setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + + if (!lengths) return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (current_length >= 32) return error(f, VORBIS_invalid_setup); + if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f,1) : 1; + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + if (lengths[j] == 32) + return error(f, VORBIS_invalid_setup); + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + CHECK(f); + if (!c->sparse) { + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) setup_temp_free(f, values, 0); + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + CHECK(f); + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); + if (c->lookup_type == 1) { + int values = lookup1_values(c->entries, c->dimensions); + if (values < 0) return error(f, VORBIS_invalid_setup); + c->lookup_values = (uint32) values; + } else { + c->lookup_values = c->entries * c->dimensions; + } + if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + float last=0; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + unsigned int z = sparse ? c->sorted_values[j] : j; + unsigned int div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + float val = mults[off]; + val = mults[off]*c->delta_value + c->minimum_value + last; + c->multiplicands[j*c->dimensions + k] = val; + if (c->sequence_p) + last = val; + if (k+1 < c->dimensions) { + if (div > UINT_MAX / (unsigned int) c->lookup_values) { + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + return error(f, VORBIS_invalid_setup); + } + div *= c->lookup_values; + } + } + } + c->lookup_type = 2; + } + else +#endif + { + float last=0; + CHECK(f); + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + for (j=0; j < (int) c->lookup_values; ++j) { + float val = mults[j] * c->delta_value + c->minimum_value + last; + c->multiplicands[j] = val; + if (c->sequence_p) + last = val; + } + } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + skip:; +#endif + setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); + + CHECK(f); + } + CHECK(f); + } + + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6)+1; + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return error(f, VORBIS_outofmem); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); + } else { + stbv__floor_ordering p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].id = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values-1; ++j) + if (p[j].x == p[j+1].x) + return error(f, VORBIS_invalid_setup); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (uint8) p[j].id; + // precompute the neighbors + for (j=2; j < g->values; ++j) { + int low,hi; + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6)+1; + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i=0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + if (r->end < r->begin) return error(f, VORBIS_invalid_setup); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); + for (j=0; j < r->classifications; ++j) { + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = get_bits(f,6)+1; + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return error(f, VORBIS_outofmem); + if (get_bits(f,1)) + m->submaps = get_bits(f,4)+1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup); + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); + m->chan[k].angle = get_bits(f, ilog(f->channels-1)); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); + } + + flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); + memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); + #endif + } + + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + unsigned int actual_size = f->blocksize_1 / 2; + unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; + unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + // maximum reasonable partition size is f->blocksize_1 + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + f->first_decode = TRUE; + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return error(f, VORBIS_outofmem); + } + + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + + return TRUE; +} + +static void vorbis_deinit(stb_vorbis *p) +{ + int i,j; + if (p->residue_config) { + for (i=0; i < p->residue_count; ++i) { + Residue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + } + + if (p->codebooks) { + CHECK(p); + for (i=0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + if (p->mapping) { + for (i=0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + } + CHECK(p); + for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + setup_free(p, p->floor_buffers[i]); + #endif + setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + vorbis_deinit(p); + setup_free(p,p); +} + +static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, ogg_page_header, 4)) { + int j,len; + uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + const uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); + } + + f->stream = (uint8 *) data; + f->stream_end = (uint8 *) data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!is_whole_packet_present(f, FALSE)) { + *samples = 0; + return 0; + } + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return (int) (f->stream - data); +} + +stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = (int) (f->stream - data); + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); + #ifndef STB_VORBIS_NO_STDIO + return (unsigned int) (ftell(f->f) - f->f_start); + #endif +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } +} + + +#define SAMPLE_unknown 0xffffffff + +// seeking is implemented with a binary search, which narrows down the range to +// 64K, before using a linear search (because finding the synchronization +// pattern can be expensive, and the chance we'd find the end page again is +// relatively high for small ranges) +// +// two initial interpolation-style probes are used at the start of the search +// to try to bound either side of the binary search sensibly, while still +// working in O(log n) time if they fail. + +static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) +{ + uint8 header[27], lacing[255]; + int i,len; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + getn(f, header, 27); + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + getn(f, lacing, header[26]); + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; +} + +// rarely used function to seek back to the preceding page while finding the +// start of a packet +static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +{ + unsigned int previous_safe, end; + + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + + while (vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + set_file_offset(f, end); + } + + return 0; +} + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wconditional-uninitialized" +#endif + +// implements the search logic for finding a page and starting decoding. if +// the function succeeds, current_loc_valid will be true and current_loc will +// be less than or equal to the provided sample number (the closer the +// better). +static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) +{ + ProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + uint32 delta, stream_length, padding; + double offset, bytes_per_sample; + int probe = 0; + + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) return error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); + + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + sample_number = 0; + else + sample_number -= padding; + + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + set_file_offset(f, left.page_end); + if (!get_seek_page_info(f, &left)) goto error; + } + + right = f->p_last; + assert(right.last_decoded_sample != ~0U); + + // starting from the start is handled differently + if (sample_number <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) + return 1; + return 0; + } + + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) error = 8000; + if (error < 0 && error > -8000) error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + set_file_offset(f, (unsigned int) offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + set_file_offset(f, left.page_end + (delta / 2) - 32768); + } + + if (!vorbis_find_page(f, NULL, NULL)) goto error; + } + + for (;;) { + if (!get_seek_page_info(f, &mid)) goto error; + if (mid.last_decoded_sample != ~0U) break; + // (untested) no frames end on this page + set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough. + if (mid.page_start == right.page_start) + break; + + if (sample_number < mid.last_decoded_sample) + right = mid; + else + left = mid; + + ++probe; + } + + // seek back to start of the last packet + page_start = left.page_start; + set_file_offset(f, page_start); + if (!start_page(f)) return error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); + + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i-1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!go_to_page_before(f, page_start)) + goto error; + + page_start = stb_vorbis_get_file_offset(f); + if (!start_page(f)) goto error; + end_pos = f->segment_count - 1; + } + + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; + + for (i = 0; i < start_seg_with_known_loc; i++) + skip(f, f->segments[i]); + + // start decoding (optimizable - this frame is generally discarded) + if (!vorbis_pump_first_frame(f)) + return 0; + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + +error: + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return error(f, VORBIS_seek_failed); +} + +// the same as vorbis_decode_initial, but without advancing +static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + int bits_read, bytes_read; + + if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; + + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + ilog(f->mode_count-1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; + + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; + + return 1; +} + +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + uint32 max_frame_samples; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + // fast page-level search + if (!seek_to_sample_coarse(f, sample_number)) + return 0; + + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); + + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + maybe_start_packet(f); + flush_packet(f); + } + } + // the next frame will start with the sample + assert(f->current_loc == sample_number); + return 1; +} + +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; + + if (sample_number != f->current_loc) { + int n; + uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } + + return 1; +} + +int stb_vorbis_seek_start(stb_vorbis *f) +{ + if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + return vorbis_pump_first_frame(f); +} + +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + unsigned int last; + uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + previous_safe = last_page_loc+1; + last_page_loc = stb_vorbis_get_file_offset(f); + } + + set_file_offset(f, last_page_loc); + + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; +} + +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = (uint32) ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = (unsigned int) ftell(file); + fseek(file, 0, SEEK_END); + len = (unsigned int) (ftell(file) - start); + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) +{ + FILE *f; +#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) + if (0 != fopen_s(&f, filename, "rb")) + f = NULL; +#else + f = fopen(filename, "rb"); +#endif + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (data == NULL) return NULL; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + len; + p.stream_start = (uint8 *) p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + if (error) *error = VORBIS__no_error; + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 + +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) + +static int8 channel_position[7][6] = +{ + { 0 }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + int i; + } float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define FASTDEF(x) float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() +#else + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) +#endif + +static void copy_samples(short *dest, float *src, int len) +{ + int i; + check_endianness(); + for (i=0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } +} + +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } +} + +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // NO_STDIO + +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // STB_VORBIS_NO_INTEGER_CONVERSION + +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} +#endif // STB_VORBIS_NO_PULLDATA_API + +/* Version history + 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223 + found with Mayhem by ForAllSecure + 1.16 - 2019-03-04 - fix warnings + 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found + 1.14 - 2018-02-11 - delete bogus dealloca usage + 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) + 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files + 1.11 - 2017-07-23 - fix MinGW compilation + 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory + 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version + 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; + avoid discarding last frame of audio data + 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API + some more crash fixes when out of memory or with corrupt files + 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) + some crash fixes when out of memory or with corrupt files + 1.05 - 2015-04-19 - don't define __forceinline if it's redundant + 1.04 - 2014-08-27 - fix missing const-correct case in API + 1.03 - 2014-08-07 - Warning fixes + 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float + 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + +#endif // STB_VORBIS_HEADER_ONLY + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/armorcore/sources/kinc/license.txt b/armorcore/sources/kinc/license.txt new file mode 100644 index 000000000..4d2ae2a58 --- /dev/null +++ b/armorcore/sources/kinc/license.txt @@ -0,0 +1,19 @@ +Copyright (c) 2024 the Kinc Development Team + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. diff --git a/armorcore/sources/kinc/log.h b/armorcore/sources/kinc/log.h new file mode 100644 index 000000000..4e56a8352 --- /dev/null +++ b/armorcore/sources/kinc/log.h @@ -0,0 +1,137 @@ +#pragma once + +#include + +#include + +/*! \file log.h + \brief Contains basic logging functionality. + + Logging functionality is similar to plain printf but provides some system-specific bonuses. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/// +/// Pass this to kinc_log or kinc_log_args +/// +/// +/// When used on Android the log level is converted to the equivalent +/// Android logging level. It is currently ignored on all other targets. +/// +typedef enum { KINC_LOG_LEVEL_INFO, KINC_LOG_LEVEL_WARNING, KINC_LOG_LEVEL_ERROR } kinc_log_level_t; + +/// +/// Logging function similar to printf including some system-specific bonuses +/// +/// +/// On most systems this is equivalent to printf. +/// On Windows it works with utf-8 strings (like printf does on any other target) +/// and also prints to the debug console in IDEs. +/// On Android this uses the android logging functions and also passes the logging level. +/// +/// +/// The logLevel is ignored on all targets but Android where it is converted +/// to the equivalent Android log level +/// +/// The parameter is equivalent to the first printf parameter. +/// The parameter is equivalent to the second printf parameter. +void kinc_log(kinc_log_level_t log_level, const char *format, ...); + +/// +/// Equivalent to kinc_log but uses a va_list parameter +/// +/// +/// You will need this if you want to log parameters using va_start/va_end. +/// +/// +/// The logLevel is ignored on all targets but Android where it is converted +/// to the equivalent Android log level +/// +/// The parameter is equivalent to the first vprintf parameter. +/// The parameter is equivalent to the second vprintf parameter. +void kinc_log_args(kinc_log_level_t log_level, const char *format, va_list args); + +#ifdef KINC_IMPLEMENTATION_ROOT +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#include +#include + +#ifdef KINC_IMPLEMENTATION_ROOT +#undef KINC_IMPLEMENTATION +#endif + +#ifdef KINC_MICROSOFT +#include +#include +#endif + +#ifdef KINC_ANDROID +#include +#endif + +void kinc_log(kinc_log_level_t level, const char *format, ...) { + va_list args; + va_start(args, format); + kinc_log_args(level, format, args); + va_end(args); +} + +#define UTF8 + +void kinc_log_args(kinc_log_level_t level, const char *format, va_list args) { +#ifdef KINC_ANDROID + va_list args_android_copy; + va_copy(args_android_copy, args); + switch (level) { + case KINC_LOG_LEVEL_INFO: + __android_log_vprint(ANDROID_LOG_INFO, "Kinc", format, args_android_copy); + break; + case KINC_LOG_LEVEL_WARNING: + __android_log_vprint(ANDROID_LOG_WARN, "Kinc", format, args_android_copy); + break; + case KINC_LOG_LEVEL_ERROR: + __android_log_vprint(ANDROID_LOG_ERROR, "Kinc", format, args_android_copy); + break; + } + va_end(args_android_copy); +#endif +#ifdef KINC_MICROSOFT +#ifdef UTF8 + wchar_t buffer[4096]; + kinc_microsoft_format(format, args, buffer); + wcscat(buffer, L"\r\n"); + OutputDebugStringW(buffer); +#ifdef KINC_WINDOWS + DWORD written; + WriteConsoleW(GetStdHandle(level == KINC_LOG_LEVEL_INFO ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE), buffer, (DWORD)wcslen(buffer), &written, NULL); +#endif +#else + char buffer[4096]; + vsnprintf(buffer, 4090, format, args); + strcat(buffer, "\r\n"); + OutputDebugStringA(buffer); +#ifdef KINC_WINDOWS + DWORD written; + WriteConsoleA(GetStdHandle(level == KINC_LOG_LEVEL_INFO ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE), buffer, (DWORD)strlen(buffer), &written, NULL); +#endif +#endif +#else + char buffer[4096]; + vsnprintf(buffer, 4090, format, args); + strcat(buffer, "\n"); + fprintf(level == KINC_LOG_LEVEL_INFO ? stdout : stderr, "%s", buffer); +#endif +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/math/core.h b/armorcore/sources/kinc/math/core.h new file mode 100644 index 000000000..00719481c --- /dev/null +++ b/armorcore/sources/kinc/math/core.h @@ -0,0 +1,70 @@ +#pragma once + +#include + +/*! \file core.h + \brief Just a few very simple additions to math.h + the C-lib. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#define KINC_PI 3.141592654 +#define KINC_TAU 6.283185307 + +float kinc_cot(float x); +float kinc_round(float value); +float kinc_abs(float value); +float kinc_min(float a, float b); +float kinc_max(float a, float b); +int kinc_mini(int a, int b); +int kinc_maxi(int a, int b); +float kinc_clamp(float value, float minValue, float maxValue); + +#ifdef KINC_IMPLEMENTATION_MATH +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#include + +float kinc_cot(float x) { + return cosf(x) / sinf(x); +} + +float kinc_round(float value) { + return floorf(value + 0.5f); +} + +float kinc_abs(float value) { + return value < 0 ? -value : value; +} + +float kinc_min(float a, float b) { + return a > b ? b : a; +} + +float kinc_max(float a, float b) { + return a > b ? a : b; +} + +int kinc_mini(int a, int b) { + return a > b ? b : a; +} + +int kinc_maxi(int a, int b) { + return a > b ? a : b; +} + +float kinc_clamp(float value, float minValue, float maxValue) { + return kinc_max(minValue, kinc_min(maxValue, value)); +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/math/mathunit.c b/armorcore/sources/kinc/math/mathunit.c new file mode 100644 index 000000000..c183b226e --- /dev/null +++ b/armorcore/sources/kinc/math/mathunit.c @@ -0,0 +1,5 @@ +#define KINC_IMPLEMENTATION_MATH + +#include "core.h" +#include "matrix.h" +#include "random.h" diff --git a/armorcore/sources/kinc/math/matrix.h b/armorcore/sources/kinc/math/matrix.h new file mode 100644 index 000000000..3df876abc --- /dev/null +++ b/armorcore/sources/kinc/math/matrix.h @@ -0,0 +1,227 @@ +#pragma once + +#include "vector.h" + +/*! \file matrix.h + \brief Provides basic matrix types and a few functions which create transformation-matrices. This only provides functionality which is needed elsewhere in + Kinc - if you need more, look up how transformation-matrices work and add some functions to your own project. Alternatively the Kore/C++-API also provides a + more complete matrix-API. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef union kinc_matrix3x3 { + float m[3 * 3]; + struct { + float m00; + float m01; + float m02; + float m10; + float m11; + float m12; + float m20; + float m21; + float m22; + }; +} kinc_matrix3x3_t; + +float kinc_matrix3x3_get(kinc_matrix3x3_t *matrix, int x, int y); +void kinc_matrix3x3_set(kinc_matrix3x3_t *matrix, int x, int y, float value); +void kinc_matrix3x3_transpose(kinc_matrix3x3_t *matrix); +kinc_matrix3x3_t kinc_matrix3x3_identity(void); +kinc_matrix3x3_t kinc_matrix3x3_rotation_x(float alpha); +kinc_matrix3x3_t kinc_matrix3x3_rotation_y(float alpha); +kinc_matrix3x3_t kinc_matrix3x3_rotation_z(float alpha); +kinc_matrix3x3_t kinc_matrix3x3_translation(float x, float y); +kinc_matrix3x3_t kinc_matrix3x3_multiply(kinc_matrix3x3_t *a, kinc_matrix3x3_t *b); +kinc_vector3_t kinc_matrix3x3_multiply_vector(kinc_matrix3x3_t *a, kinc_vector3_t b); + +typedef union kinc_matrix4x4 { + float m[4 * 4]; + struct { + float m00; + float m01; + float m02; + float m03; + float m10; + float m11; + float m12; + float m13; + float m20; + float m21; + float m22; + float m23; + float m30; + float m31; + float m32; + float m33; + }; +} kinc_matrix4x4_t; + +float kinc_matrix4x4_get(kinc_matrix4x4_t *matrix, int x, int y); +void kinc_matrix4x4_set(kinc_matrix4x4_t *matrix, int x, int y, float value); +void kinc_matrix4x4_transpose(kinc_matrix4x4_t *matrix); +kinc_matrix4x4_t kinc_matrix4x4_multiply(kinc_matrix4x4_t *a, kinc_matrix4x4_t *b); + +#ifdef KINC_IMPLEMENTATION_MATH +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#ifdef KINC_IMPLEMENTATION_MATH +#undef KINC_IMPLEMENTATION +#endif +#include +#include +#ifdef KINC_IMPLEMENTATION_MATH +#define KINC_IMPLEMENTATION +#endif + +#include + +float kinc_matrix3x3_get(kinc_matrix3x3_t *matrix, int x, int y) { + return matrix->m[x * 3 + y]; +} + +void kinc_matrix3x3_set(kinc_matrix3x3_t *matrix, int x, int y, float value) { + matrix->m[x * 3 + y] = value; +} + +void kinc_matrix3x3_transpose(kinc_matrix3x3_t *matrix) { + kinc_matrix3x3_t transposed; + for (int y = 0; y < 3; ++y) { + for (int x = 0; x < 3; ++x) { + kinc_matrix3x3_set(&transposed, x, y, kinc_matrix3x3_get(matrix, y, x)); + } + } + memcpy(matrix->m, transposed.m, sizeof(transposed.m)); +} + +kinc_matrix3x3_t kinc_matrix3x3_identity(void) { + kinc_matrix3x3_t m; + memset(m.m, 0, sizeof(m.m)); + for (unsigned x = 0; x < 3; ++x) { + kinc_matrix3x3_set(&m, x, x, 1.0f); + } + return m; +} + +kinc_matrix3x3_t kinc_matrix3x3_rotation_x(float alpha) { + kinc_matrix3x3_t m = kinc_matrix3x3_identity(); + float ca = cosf(alpha); + float sa = sinf(alpha); + kinc_matrix3x3_set(&m, 1, 1, ca); + kinc_matrix3x3_set(&m, 2, 1, -sa); + kinc_matrix3x3_set(&m, 1, 2, sa); + kinc_matrix3x3_set(&m, 2, 2, ca); + return m; +} + +kinc_matrix3x3_t kinc_matrix3x3_rotation_y(float alpha) { + kinc_matrix3x3_t m = kinc_matrix3x3_identity(); + float ca = cosf(alpha); + float sa = sinf(alpha); + kinc_matrix3x3_set(&m, 0, 0, ca); + kinc_matrix3x3_set(&m, 2, 0, sa); + kinc_matrix3x3_set(&m, 0, 2, -sa); + kinc_matrix3x3_set(&m, 2, 2, ca); + return m; +} + +kinc_matrix3x3_t kinc_matrix3x3_rotation_z(float alpha) { + kinc_matrix3x3_t m = kinc_matrix3x3_identity(); + float ca = cosf(alpha); + float sa = sinf(alpha); + kinc_matrix3x3_set(&m, 0, 0, ca); + kinc_matrix3x3_set(&m, 1, 0, -sa); + kinc_matrix3x3_set(&m, 0, 1, sa); + kinc_matrix3x3_set(&m, 1, 1, ca); + return m; +} + +kinc_matrix3x3_t kinc_matrix3x3_translation(float x, float y) { + kinc_matrix3x3_t m = kinc_matrix3x3_identity(); + kinc_matrix3x3_set(&m, 2, 0, x); + kinc_matrix3x3_set(&m, 2, 1, y); + return m; +} + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wconditional-uninitialized" +#endif + +kinc_matrix3x3_t kinc_matrix3x3_multiply(kinc_matrix3x3_t *a, kinc_matrix3x3_t *b) { + kinc_matrix3x3_t result; + for (unsigned x = 0; x < 3; ++x) { + for (unsigned y = 0; y < 3; ++y) { + float t = kinc_matrix3x3_get(a, 0, y) * kinc_matrix3x3_get(b, x, 0); + for (unsigned i = 1; i < 3; ++i) { + t += kinc_matrix3x3_get(a, i, y) * kinc_matrix3x3_get(b, x, i); + } + kinc_matrix3x3_set(&result, x, y, t); + } + } + return result; +} + +static float vector3_get(kinc_vector3_t vec, int index) { + float *values = (float *)&vec; + return values[index]; +} + +static void vector3_set(kinc_vector3_t *vec, int index, float value) { + float *values = (float *)vec; + values[index] = value; +} + +kinc_vector3_t kinc_matrix3x3_multiply_vector(kinc_matrix3x3_t *a, kinc_vector3_t b) { + kinc_vector3_t product; + for (unsigned y = 0; y < 3; ++y) { + float t = 0; + for (unsigned x = 0; x < 3; ++x) { + t += kinc_matrix3x3_get(a, x, y) * vector3_get(b, x); + } + vector3_set(&product, y, t); + } + return product; +} + +float kinc_matrix4x4_get(kinc_matrix4x4_t *matrix, int x, int y) { + return matrix->m[x * 4 + y]; +} + +void kinc_matrix4x4_set(kinc_matrix4x4_t *matrix, int x, int y, float value) { + matrix->m[x * 4 + y] = value; +} + +void kinc_matrix4x4_transpose(kinc_matrix4x4_t *matrix) { + kinc_matrix4x4_t transposed; + for (int y = 0; y < 4; ++y) { + for (int x = 0; x < 4; ++x) { + kinc_matrix4x4_set(&transposed, x, y, kinc_matrix4x4_get(matrix, y, x)); + } + } + memcpy(matrix->m, transposed.m, sizeof(transposed.m)); +} + +kinc_matrix4x4_t kinc_matrix4x4_multiply(kinc_matrix4x4_t *a, kinc_matrix4x4_t *b) { + kinc_matrix4x4_t result; + for (unsigned x = 0; x < 4; ++x) + for (unsigned y = 0; y < 4; ++y) { + float t = kinc_matrix4x4_get(a, 0, y) * kinc_matrix4x4_get(b, x, 0); + for (unsigned i = 1; i < 4; ++i) { + t += kinc_matrix4x4_get(a, i, y) * kinc_matrix4x4_get(b, x, i); + } + kinc_matrix4x4_set(&result, x, y, t); + } + return result; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/math/quaternion.h b/armorcore/sources/kinc/math/quaternion.h new file mode 100644 index 000000000..15f45b349 --- /dev/null +++ b/armorcore/sources/kinc/math/quaternion.h @@ -0,0 +1,22 @@ +#pragma once + +#include "core.h" + +/*! \file quaternion.h + \brief Provides a basic quaternion type. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_quaternion { + float x; + float y; + float z; + float w; +} kinc_quaternion_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/math/random.h b/armorcore/sources/kinc/math/random.h new file mode 100644 index 000000000..5c9fe6b25 --- /dev/null +++ b/armorcore/sources/kinc/math/random.h @@ -0,0 +1,98 @@ +#pragma once + +#include + +/*! \file random.h + \brief Generates values which are kind of random. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/// +/// Initializes the randomizer with a seed. This is optional but helpful. +/// +/// A value which should ideally be pretty random +void kinc_random_init(int64_t seed); + +/// +/// Returns a random value. +/// +/// A random value +int64_t kinc_random_get(void); + +/// +/// Returns a value between 0 and max (both inclusive). +/// +/// A random value +int64_t kinc_random_get_max(int64_t max); + +/// +/// Returns a value between min and max (both inclusive). +/// +/// A random value +int64_t kinc_random_get_in(int64_t min, int64_t max); + +#ifdef KINC_IMPLEMENTATION_MATH +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#include +#include + +// xoshiro256** 1.0 + +static inline uint64_t rotl(const uint64_t x, int k) { + return (x << k) | (x >> (64 - k)); +} + +static uint64_t s[4] = {1, 2, 3, 4}; + +uint64_t next(void) { + const uint64_t result = rotl(s[1] * 5, 7) * 9; + + const uint64_t t = s[1] << 17; + + s[2] ^= s[0]; + s[3] ^= s[1]; + s[1] ^= s[2]; + s[0] ^= s[3]; + + s[2] ^= t; + + s[3] = rotl(s[3], 45); + + return result; +} + +void kinc_random_init(int64_t seed) { + s[0] = (uint64_t)seed; + s[1] = 2; + s[2] = 3; + s[3] = 4; + s[1] = next(); + s[2] = next(); + s[3] = next(); +} + +int64_t kinc_random_get(void) { + return (int64_t)next(); +} + +int64_t kinc_random_get_max(int64_t max) { + return kinc_random_get() % (max + 1); +} + +int64_t kinc_random_get_in(int64_t min, int64_t max) { + int64_t value = kinc_random_get(); + return (value < -LLONG_MAX ? LLONG_MAX : llabs(value)) % (max + 1 - min) + min; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/math/vector.h b/armorcore/sources/kinc/math/vector.h new file mode 100644 index 000000000..bdcb3619d --- /dev/null +++ b/armorcore/sources/kinc/math/vector.h @@ -0,0 +1,33 @@ +#pragma once + +#include "core.h" + +/*! \file vector.h + \brief Provides basic vector types. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_vector2 { + float x; + float y; +} kinc_vector2_t; + +typedef struct kinc_vector3 { + float x; + float y; + float z; +} kinc_vector3_t; + +typedef struct kinc_vector4 { + float x; + float y; + float z; + float w; +} kinc_vector4_t; + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/network/http.h b/armorcore/sources/kinc/network/http.h new file mode 100644 index 000000000..58e1804ef --- /dev/null +++ b/armorcore/sources/kinc/network/http.h @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include + +/*! \file http.h + \brief Provides a simple http-API. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#define KINC_HTTP_GET 0 +#define KINC_HTTP_POST 1 +#define KINC_HTTP_PUT 2 +#define KINC_HTTP_DELETE 3 + +typedef void (*kinc_http_callback_t)(int error, int response, const char *body, void *callbackdata); + +/// +/// Fires off an http request. +/// +void kinc_http_request(const char *url, const char *path, const char *data, int port, bool secure, int method, const char *header, + kinc_http_callback_t callback, void *callbackdata); + +#ifdef KINC_IMPLEMENTATION_NETWORK +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#if !defined KINC_MACOS && !defined KINC_IOS && !defined KINC_WINDOWS + +#include + +void kinc_http_request(const char *url, const char *path, const char *data, int port, bool secure, int method, const char *header, + kinc_http_callback_t callback, void *callbackdata) { + assert(false); // not implemented for the current system, please send a pull-request +} + +#endif + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/network/networkunit.c b/armorcore/sources/kinc/network/networkunit.c new file mode 100644 index 000000000..64ff61c5e --- /dev/null +++ b/armorcore/sources/kinc/network/networkunit.c @@ -0,0 +1,4 @@ +#define KINC_IMPLEMENTATION_NETWORK + +#include "http.h" +#include "socket.h" diff --git a/armorcore/sources/kinc/network/socket.h b/armorcore/sources/kinc/network/socket.h new file mode 100644 index 000000000..511505f21 --- /dev/null +++ b/armorcore/sources/kinc/network/socket.h @@ -0,0 +1,665 @@ +#pragma once + +#include + +/*! \file socket.h + \brief Provides low-level network-communication via UDP or TCP-sockets. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum kinc_socket_protocol { KINC_SOCKET_PROTOCOL_UDP, KINC_SOCKET_PROTOCOL_TCP } kinc_socket_protocol_t; + +typedef enum kinc_socket_family { KINC_SOCKET_FAMILY_IP4, KINC_SOCKET_FAMILY_IP6 } kinc_socket_family_t; + +#ifdef KINC_MICROSOFT +#if defined(_WIN64) +typedef unsigned __int64 UINT_PTR, *PUINT_PTR; +#else +#if !defined _W64 +#define _W64 +#endif +typedef _W64 unsigned int UINT_PTR, *PUINT_PTR; +#endif +typedef UINT_PTR SOCKET; +#endif + +typedef struct kinc_socket { +#ifdef KINC_MICROSOFT + SOCKET handle; +#else + int handle; +#endif + uint32_t host; + uint32_t port; + kinc_socket_protocol_t protocol; + kinc_socket_family_t family; + bool connected; +} kinc_socket_t; + +typedef struct kinc_socket_options { + bool non_blocking; + bool broadcast; + bool tcp_no_delay; +} kinc_socket_options_t; + +/// +/// Initializes a socket-options-object to the default options +/// +/// The new default options +void kinc_socket_options_set_defaults(kinc_socket_options_t *options); + +/// +/// Initializes a socket-object. To set the host and port use kinc_socket_set. +/// Host will be localhost +/// Port will be 8080 +/// Family will be IPv4 +/// Protocol will be TCP +/// +/// The socket to initialize +void kinc_socket_init(kinc_socket_t *socket); + +/// +/// Sets the sockets properties. +/// +/// The socket-object to use +/// The host to use as IP or URL +/// The port to use +/// The IP-family to use +/// The protocol to use +/// Whether the socket was set correctly +bool kinc_socket_set(kinc_socket_t *socket, const char *host, int port, kinc_socket_family_t family, kinc_socket_protocol_t protocol); + +/// +/// Destroys a socket-object. +/// +/// The socket to destroy +void kinc_socket_destroy(kinc_socket_t *socket); + +/// +/// Opens a socket-connection. +/// +/// The socket-object to use +/// The protocol to use +/// The port to use +/// The options to use +/// Whether the socket-connection could be opened +bool kinc_socket_open(kinc_socket_t *socket, kinc_socket_options_t *options); + +/// +/// For use with non-blocking sockets to try to see if we are connected. +/// +/// The socket-object to use +/// The amount of time in seconds the select function will timeout. +/// Check if the socket is ready to be read from. +/// Check if the socket is ready to be written to. +/// Whether the socket-connection can read or write or checks both. +bool kinc_socket_select(kinc_socket_t *socket, uint32_t waittime, bool read, bool write); + +/*Typically these are server actions.*/ +bool kinc_socket_bind(kinc_socket_t *socket); +bool kinc_socket_listen(kinc_socket_t *socket, int backlog); +bool kinc_socket_accept(kinc_socket_t *socket, kinc_socket_t *new_socket, unsigned *remote_address, unsigned *remote_port); + +/*Typically this is a client action.*/ +bool kinc_socket_connect(kinc_socket_t *socket); + +int kinc_socket_send(kinc_socket_t *socket, const uint8_t *data, int size); +int kinc_socket_send_address(kinc_socket_t *socket, unsigned address, int port, const uint8_t *data, int size); +int kinc_socket_send_url(kinc_socket_t *socket, const char *url, int port, const uint8_t *data, int size); +int kinc_socket_receive(kinc_socket_t *socket, uint8_t *data, int maxSize, unsigned *from_address, unsigned *from_port); + +/// +/// Resolves a DNS-entry to an IP and returns its integer representation. +/// +/// +/// +/// +unsigned kinc_url_to_int(const char *url, int port); + +#ifdef KINC_IMPLEMENTATION_NETWORK +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#undef KINC_IMPLEMENTATION +#include +#include +#define KINC_IMPLEMENTATION + +#include +#include +#include +#include + +#if defined(KINC_WINDOWS) + +// Windows 7 +#define WINVER 0x0601 +#define _WIN32_WINNT 0x0601 + +#define NOATOM +#define NOCLIPBOARD +#define NOCOLOR +#define NOCOMM +#define NOCTLMGR +#define NODEFERWINDOWPOS +#define NODRAWTEXT +#define NOGDI +#define NOGDICAPMASKS +#define NOHELP +#define NOICONS +#define NOKANJI +#define NOKEYSTATES +#define NOMB +#define NOMCX +#define NOMEMMGR +#define NOMENUS +#define NOMETAFILE +#define NOMINMAX +#define NOMSG +#define NONLS +#define NOOPENFILE +#define NOPROFILER +#define NORASTEROPS +#define NOSCROLL +#define NOSERVICE +#define NOSHOWWINDOW +#define NOSOUND +#define NOSYSCOMMANDS +#define NOSYSMETRICS +#define NOTEXTMETRIC +#define NOUSER +#define NOVIRTUALKEYCODES +#define NOWH +#define NOWINMESSAGES +#define NOWINOFFSETS +#define NOWINSTYLES +#define WIN32_LEAN_AND_MEAN + +#include +#include +#elif defined(KINC_POSIX) || defined(KINC_EMSCRIPTEN) +#include // for inet_addr() +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(KINC_EMSCRIPTEN) +#include +#include +#include +#include + +static EMSCRIPTEN_WEBSOCKET_T bridgeSocket = 0; +#elif defined(KINC_POSIX) +#include +#endif + +static int counter = 0; + +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) +// Important: Must be cleaned with freeaddrinfo(address) later if the result is 0 in order to prevent memory leaks +static int resolveAddress(const char *url, int port, struct addrinfo **result) { + struct addrinfo hints = {0}; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + + char serv[6]; + sprintf(serv, "%u", port); + + return getaddrinfo(url, serv, &hints, result); +} +#endif + +bool kinc_socket_bind(kinc_socket_t *sock) { +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) + struct sockaddr_in address; + address.sin_family = sock->family == KINC_SOCKET_FAMILY_IP4 ? AF_INET : AF_INET6; + address.sin_addr.s_addr = sock->host; + address.sin_port = sock->port; + if (bind(sock->handle, (const struct sockaddr *)&address, sizeof(struct sockaddr_in)) < 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not bind socket: %s", strerror(errno)); + return false; + } + return true; +#else + return false; +#endif +} + +void kinc_socket_options_set_defaults(kinc_socket_options_t *options) { + options->non_blocking = true; + options->broadcast = false; + options->tcp_no_delay = false; +} + +void kinc_socket_init(kinc_socket_t *sock) { + sock->handle = 0; + +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) + sock->host = INADDR_ANY; + sock->port = htons((unsigned short)8080); + sock->protocol = KINC_SOCKET_PROTOCOL_TCP; + sock->family = KINC_SOCKET_FAMILY_IP4; +#endif + sock->connected = false; + +#if defined(KINC_WINDOWS) + if (counter == 0) { + WSADATA WsaData; + WSAStartup(MAKEWORD(2, 2), &WsaData); + } +#if defined(KINC_EMSCRIPTEN) + if (!bridgeSocket) { + bridgeSocket = emscripten_init_websocket_to_posix_socket_bridge("ws://localhost:8080"); + // Synchronously wait until connection has been established. + uint16_t readyState = 0; + do { + emscripten_websocket_get_ready_state(bridgeSocket, &readyState); + emscripten_thread_sleep(100); + } while (readyState == 0); + } +#endif +#endif + ++counter; +} + +bool kinc_socket_open(kinc_socket_t *sock, struct kinc_socket_options *options) { +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) + switch (sock->protocol) { + case KINC_SOCKET_PROTOCOL_UDP: + sock->handle = socket(sock->family == KINC_SOCKET_FAMILY_IP4 ? AF_INET : AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + break; + case KINC_SOCKET_PROTOCOL_TCP: + sock->handle = socket(sock->family == KINC_SOCKET_FAMILY_IP4 ? AF_INET : AF_INET6, SOCK_STREAM, IPPROTO_TCP); + break; + default: + kinc_log(KINC_LOG_LEVEL_ERROR, "Unsupported socket protocol."); + return false; + } + + if (sock->handle <= 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not create socket."); +#if defined(KINC_WINDOWS) + int errorCode = WSAGetLastError(); + switch (errorCode) { + case (WSANOTINITIALISED): + kinc_log(KINC_LOG_LEVEL_ERROR, "A successful WSAStartup call must occur before using this function."); + break; + case (WSAENETDOWN): + kinc_log(KINC_LOG_LEVEL_ERROR, "The network subsystem or the associated service provider has failed."); + break; + case (WSAEAFNOSUPPORT): + kinc_log(KINC_LOG_LEVEL_ERROR, + "The specified address family is not supported.For example, an application tried to create a socket for the AF_IRDA address " + "family but an infrared adapter and device driver is not installed on the local computer."); + break; + case (WSAEINPROGRESS): + kinc_log(KINC_LOG_LEVEL_ERROR, + "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function."); + break; + case (WSAEMFILE): + kinc_log(KINC_LOG_LEVEL_ERROR, "No more socket descriptors are available."); + break; + case (WSAEINVAL): + kinc_log(KINC_LOG_LEVEL_ERROR, + "An invalid argument was supplied.This error is returned if the af parameter is set to AF_UNSPEC and the type and protocol " + "parameter are unspecified."); + break; + case (WSAENOBUFS): + kinc_log(KINC_LOG_LEVEL_ERROR, "No buffer space is available.The socket cannot be created."); + break; + case (WSAEPROTONOSUPPORT): + kinc_log(KINC_LOG_LEVEL_ERROR, "The specified protocol is not supported."); + break; + case (WSAEPROTOTYPE): + kinc_log(KINC_LOG_LEVEL_ERROR, "The specified protocol is the wrong type for this socket."); + break; + case (WSAEPROVIDERFAILEDINIT): + kinc_log(KINC_LOG_LEVEL_ERROR, + "The service provider failed to initialize.This error is returned if a layered service provider(LSP) or namespace provider was " + "improperly installed or the provider fails to operate correctly."); + break; + case (WSAESOCKTNOSUPPORT): + kinc_log(KINC_LOG_LEVEL_ERROR, "The specified socket type is not supported in this address family."); + break; + case (WSAEINVALIDPROVIDER): + kinc_log(KINC_LOG_LEVEL_ERROR, "The service provider returned a version other than 2.2."); + break; + case (WSAEINVALIDPROCTABLE): + kinc_log(KINC_LOG_LEVEL_ERROR, "The service provider returned an invalid or incomplete procedure table to the WSPStartup."); + break; + default: + kinc_log(KINC_LOG_LEVEL_ERROR, "Unknown error."); + } +#elif defined(KINC_POSIX) && !defined(KINC_EMSCRIPTEN) + kinc_log(KINC_LOG_LEVEL_ERROR, "%s", strerror(errno)); +#endif + return false; + } +#endif + + if (options) { + if (options->non_blocking) { +#if defined(KINC_WINDOWS) + DWORD value = 1; + if (ioctlsocket(sock->handle, FIONBIO, &value) != 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not set non-blocking mode."); + return false; + } +#elif defined(KINC_POSIX) + int value = 1; + if (fcntl(sock->handle, F_SETFL, O_NONBLOCK, value) == -1) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not set non-blocking mode."); + return false; + } +#endif + } + + if (options->broadcast) { +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) + int value = 1; + if (setsockopt(sock->handle, SOL_SOCKET, SO_BROADCAST, (const char *)&value, sizeof(value)) < 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not set broadcast mode."); + return false; + } +#endif + } + + if (options->tcp_no_delay) { +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) + int value = 1; + if (setsockopt(sock->handle, IPPROTO_TCP, TCP_NODELAY, (const char *)&value, sizeof(value)) != 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not set no-delay mode."); + return false; + } +#endif + } + } + + return true; +} + +void kinc_socket_destroy(kinc_socket_t *sock) { +#if defined(KINC_WINDOWS) + closesocket(sock->handle); +#elif defined(KINC_POSIX) + close(sock->handle); +#endif + + memset(sock, 0, sizeof(kinc_socket_t)); + + --counter; +#if defined(KINC_WINDOWS) + if (counter == 0) { + WSACleanup(); + } +#endif +} + +bool kinc_socket_select(kinc_socket_t *sock, uint32_t waittime, bool read, bool write) { +#if !defined(KINC_EMSCRIPTEN) && (defined(KINC_WINDOWS) || defined(KINC_POSIX)) + fd_set r_fds, w_fds; + struct timeval timeout; + + FD_ZERO(&r_fds); + FD_ZERO(&w_fds); + + FD_SET(sock->handle, &r_fds); + FD_SET(sock->handle, &w_fds); + + timeout.tv_sec = waittime; + timeout.tv_usec = 0; + + if (select(0, &r_fds, &w_fds, NULL, &timeout) < 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "kinc_socket_select didn't work: %s", strerror(errno)); + return false; + } + + if (read && write) { + return FD_ISSET(sock->handle, &w_fds) && FD_ISSET(sock->handle, &r_fds); + } + else if (read) { + return FD_ISSET(sock->handle, &r_fds); + } + else if (write) { + return FD_ISSET(sock->handle, &w_fds); + } + else { + kinc_log(KINC_LOG_LEVEL_ERROR, "Calling kinc_socket_select with both read and write set to false is useless."); + return false; + } +#else + return false; +#endif +} + +bool kinc_socket_set(kinc_socket_t *sock, const char *host, int port, kinc_socket_family_t family, kinc_socket_protocol_t protocol) { +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) + + sock->family = family; + sock->protocol = protocol; + sock->port = htons((unsigned short)port); + + if (host == NULL) + return true; + + if (isdigit(host[0]) || (family == KINC_SOCKET_FAMILY_IP6 && host[4] == ':')) { // Is IPv4 or IPv6 string + struct in_addr addr; + + if (inet_pton(sock->family == KINC_SOCKET_FAMILY_IP4 ? AF_INET : AF_INET6, host, &addr) == 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Invalid %s address: %s\n", sock->family == KINC_SOCKET_FAMILY_IP4 ? "IPv4" : "IPv6", host); + return false; + } + sock->host = addr.s_addr; + return true; + } + else { + struct addrinfo *address = NULL; + int res = resolveAddress(host, port, &address); + if (res != 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not resolve address."); + return false; + } +#if defined(KINC_POSIX) + sock->host = ((struct sockaddr_in *)address->ai_addr)->sin_addr.s_addr; +#else + sock->host = ((struct sockaddr_in *)address->ai_addr)->sin_addr.S_un.S_addr; +#endif + freeaddrinfo(address); + + return true; + } +#else + return false; +#endif +} + +bool kinc_socket_listen(kinc_socket_t *socket, int backlog) { +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) + int res = listen(socket->handle, backlog); + return (res == 0); +#else + return false; +#endif +} + +bool kinc_socket_accept(kinc_socket_t *sock, kinc_socket_t *newSocket, unsigned *remoteAddress, unsigned *remotePort) { +#if defined(KINC_WINDOWS) + typedef int socklen_t; +#endif +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) + struct sockaddr_in addr; + socklen_t addrLength = sizeof(addr); + newSocket->handle = accept(sock->handle, (struct sockaddr *)&addr, &addrLength); + if (newSocket->handle <= 0) { + return false; + } + + newSocket->connected = sock->connected = true; + newSocket->host = addr.sin_addr.s_addr; + newSocket->port = addr.sin_port; + newSocket->family = sock->family; + newSocket->protocol = sock->protocol; + *remoteAddress = ntohl(addr.sin_addr.s_addr); + *remotePort = ntohs(addr.sin_port); + return true; +#else + return false; +#endif +} + +bool kinc_socket_connect(kinc_socket_t *sock) { +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) + struct sockaddr_in addr; + addr.sin_family = sock->family == KINC_SOCKET_FAMILY_IP4 ? AF_INET : AF_INET6; + addr.sin_addr.s_addr = sock->host; + addr.sin_port = sock->port; + + int res = connect(sock->handle, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); + return sock->connected = (res == 0); +#else + return false; +#endif +} + +int kinc_socket_send(kinc_socket_t *sock, const uint8_t *data, int size) { +#if defined(KINC_WINDOWS) + typedef int socklen_t; +#endif +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) + if (sock->protocol == KINC_SOCKET_PROTOCOL_UDP) { + struct sockaddr_in addr; + addr.sin_family = sock->family == KINC_SOCKET_FAMILY_IP4 ? AF_INET : AF_INET6; + addr.sin_addr.s_addr = sock->host; + addr.sin_port = sock->port; + + size_t sent = sendto(sock->handle, (const char *)data, size, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); + if (sent != size) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not send packet."); + return -1; + } + return (int)sent; + } + else { + if (!sock->connected) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Call kinc_sockect_connect/bind before send/recv can be called for TCP sockets."); + return -1; + } + + size_t sent = send(sock->handle, (const char *)data, size, 0); + if (sent != size) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not send packet."); + } + return (int)sent; + } +#else + return 0; +#endif +} + +int kinc_socket_send_address(kinc_socket_t *sock, unsigned address, int port, const uint8_t *data, int size) { +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(address); + addr.sin_port = htons(port); + + size_t sent = sendto(sock->handle, (const char *)data, size, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)); + if (sent != size) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not send packet."); + } + return (int)sent; +#else + return 0; +#endif +} + +int kinc_socket_send_url(kinc_socket_t *sock, const char *url, int port, const uint8_t *data, int size) { +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) + struct addrinfo *address = NULL; + int res = resolveAddress(url, port, &address); + if (res != 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not resolve address."); + return 0; + } + + size_t sent = sendto(sock->handle, (const char *)data, size, 0, address->ai_addr, sizeof(struct sockaddr_in)); + if (sent != size) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not send packet."); + } + freeaddrinfo(address); + return (int)sent; +#else + return 0; +#endif +} + +int kinc_socket_receive(kinc_socket_t *sock, uint8_t *data, int maxSize, unsigned *fromAddress, unsigned *fromPort) { +#if defined(KINC_WINDOWS) + typedef int socklen_t; + typedef int ssize_t; +#endif +#if defined(KINC_WINDOWS) || defined(KINC_POSIX) + + if (sock->protocol == KINC_SOCKET_PROTOCOL_UDP) { + struct sockaddr_in from; + socklen_t fromLength = sizeof(from); + ssize_t bytes = recvfrom(sock->handle, (char *)data, maxSize, 0, (struct sockaddr *)&from, &fromLength); + if (bytes <= 0) { + return (int)bytes; + } + *fromAddress = ntohl(from.sin_addr.s_addr); + *fromPort = ntohs(from.sin_port); + return (int)bytes; + } + else { + + if (!sock->connected) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Call kinc_sockect_connect/bind before send/recv can be called for TCP sockets."); + return -1; + } + ssize_t bytes = recv(sock->handle, (char *)data, maxSize, 0); + *fromAddress = ntohl(sock->host); + *fromPort = ntohs(sock->port); + return (int)bytes; + } +#else + return 0; +#endif +} + +unsigned kinc_url_to_int(const char *url, int port) { +#if defined(KINC_WINDOWS) + struct addrinfo *address = NULL; + int res = resolveAddress(url, port, &address); + if (res != 0) { + kinc_log(KINC_LOG_LEVEL_ERROR, "Could not resolve address."); + return -1; + } + + unsigned fromAddress = ntohl(((struct sockaddr_in *)address->ai_addr)->sin_addr.S_un.S_addr); + freeaddrinfo(address); + + return fromAddress; +#else + return 0; +#endif +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/readme.md b/armorcore/sources/kinc/readme.md new file mode 100644 index 000000000..eab0d56ec --- /dev/null +++ b/armorcore/sources/kinc/readme.md @@ -0,0 +1,2 @@ +Mini-Kinc based on revision 370f433110c7341ecbc319537493c72375662fb5. +Do not modify, create a pull-request at https://github.com/Kode/Kinc instead. diff --git a/armorcore/sources/kinc/rootunit.c b/armorcore/sources/kinc/rootunit.c new file mode 100644 index 000000000..b35143292 --- /dev/null +++ b/armorcore/sources/kinc/rootunit.c @@ -0,0 +1,15 @@ +#define KINC_IMPLEMENTATION_ROOT + +#include "color.h" +#include "error.h" +#include "log.h" + +#include "image.h" +#include "memory.h" +#include "string.h" +#include "system.h" +#include "window.h" + +bool kinc_debugger_attached(void) { + return false; +} diff --git a/armorcore/sources/kinc/simd/float32x4.h b/armorcore/sources/kinc/simd/float32x4.h new file mode 100644 index 000000000..1efa4fe9c --- /dev/null +++ b/armorcore/sources/kinc/simd/float32x4.h @@ -0,0 +1,719 @@ +#pragma once + +#include "types.h" +#include + +/*! \file float32x4.h + \brief Provides 128bit four-element floating point SIMD operations which are mapped to equivalent SSE or Neon operations. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(KINC_SSE) + +static inline kinc_float32x4_t kinc_float32x4_intrin_load(const float *values) { + return _mm_load_ps(values); +} + +static inline kinc_float32x4_t kinc_float32x4_intrin_load_unaligned(const float *values) { + return _mm_loadu_ps(values); +} + +static inline kinc_float32x4_t kinc_float32x4_load(float a, float b, float c, float d) { + return _mm_set_ps(d, c, b, a); +} + +static inline kinc_float32x4_t kinc_float32x4_load_all(float t) { + return _mm_set_ps1(t); +} + +static inline void kinc_float32x4_store(float *destination, kinc_float32x4_t value) { + _mm_store_ps(destination, value); +} + +static inline void kinc_float32x4_store_unaligned(float *destination, kinc_float32x4_t value) { + _mm_storeu_ps(destination, value); +} + +static inline float kinc_float32x4_get(kinc_float32x4_t t, int index) { + union { + __m128 value; + float elements[4]; + } converter; + converter.value = t; + return converter.elements[index]; +} + +static inline kinc_float32x4_t kinc_float32x4_abs(kinc_float32x4_t t) { + __m128 mask = _mm_set_ps1(-0.f); + return _mm_andnot_ps(mask, t); +} + +static inline kinc_float32x4_t kinc_float32x4_add(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_add_ps(a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_div(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_div_ps(a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_mul(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_mul_ps(a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_neg(kinc_float32x4_t t) { + __m128 negative = _mm_set_ps1(-1.0f); + return _mm_mul_ps(t, negative); +} + +static inline kinc_float32x4_t kinc_float32x4_reciprocal_approximation(kinc_float32x4_t t) { + return _mm_rcp_ps(t); +} + +static inline kinc_float32x4_t kinc_float32x4_reciprocal_sqrt_approximation(kinc_float32x4_t t) { + return _mm_rsqrt_ps(t); +} + +static inline kinc_float32x4_t kinc_float32x4_sub(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_sub_ps(a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_sqrt(kinc_float32x4_t t) { + return _mm_sqrt_ps(t); +} + +static inline kinc_float32x4_t kinc_float32x4_max(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_max_ps(a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_min(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_min_ps(a, b); +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmpeq(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_cmpeq_ps(a, b); +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmpge(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_cmpge_ps(a, b); +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmpgt(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_cmpgt_ps(a, b); +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmple(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_cmple_ps(a, b); +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmplt(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_cmplt_ps(a, b); +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmpneq(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_cmpneq_ps(a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_sel(kinc_float32x4_t a, kinc_float32x4_t b, kinc_float32x4_mask_t mask) { + return _mm_xor_ps(b, _mm_and_ps(mask, _mm_xor_ps(a, b))); +} + +static inline kinc_float32x4_t kinc_float32x4_or(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_or_ps(a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_and(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_and_ps(a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_xor(kinc_float32x4_t a, kinc_float32x4_t b) { + return _mm_xor_ps(a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_not(kinc_float32x4_t t) { + __m128 zeroes = _mm_setzero_ps(); + return _mm_xor_ps(t, _mm_cmpeq_ps(zeroes, zeroes)); +} + +#define kinc_float32x4_shuffle_custom(abcd, efgh, left_1, left_2, right_1, right_2) \ + _mm_shuffle_ps((abcd), (efgh), KINC_SHUFFLE_TABLE((left_1), (left_2), (right_1), (right_2))) + +static inline kinc_float32x4_t kinc_float32x4_shuffle_aebf(kinc_float32x4_t abcd, kinc_float32x4_t efgh) { + // aka unpacklo aka zip1 aka interleave low + return _mm_unpacklo_ps(abcd, efgh); +} + +static inline kinc_float32x4_t kinc_float32x4_shuffle_cgdh(kinc_float32x4_t abcd, kinc_float32x4_t efgh) { + // aka unpackhi aka zip2 aka interleave high + return _mm_unpackhi_ps(abcd, efgh); +} + +static inline kinc_float32x4_t kinc_float32x4_shuffle_abef(kinc_float32x4_t abcd, kinc_float32x4_t efgh) { + // aka movelh + return _mm_movelh_ps(abcd, efgh); +} + +static inline kinc_float32x4_t kinc_float32x4_shuffle_ghcd(kinc_float32x4_t abcd, kinc_float32x4_t efgh) { + // aka movehl + return _mm_movehl_ps(abcd, efgh); +} + +#elif defined(KINC_NEON) + +static inline kinc_float32x4_t kinc_float32x4_intrin_load(const float *values) { + return vld1q_f32(values); +} + +static inline kinc_float32x4_t kinc_float32x4_intrin_load_unaligned(const float *values) { + return kinc_float32x4_intrin_load(values); +} + +static inline kinc_float32x4_t kinc_float32x4_load(float a, float b, float c, float d) { + return (kinc_float32x4_t){a, b, c, d}; +} + +static inline kinc_float32x4_t kinc_float32x4_load_all(float t) { + return (kinc_float32x4_t){t, t, t, t}; +} + +static inline void kinc_float32x4_store(float *destination, kinc_float32x4_t value) { + vst1q_f32(destination, value); +} + +static inline void kinc_float32x4_store_unaligned(float *destination, kinc_float32x4_t value) { + kinc_float32x4_store(destination, value); +} + +static inline float kinc_float32x4_get(kinc_float32x4_t t, int index) { + return t[index]; +} + +static inline kinc_float32x4_t kinc_float32x4_abs(kinc_float32x4_t t) { + return vabsq_f32(t); +} + +static inline kinc_float32x4_t kinc_float32x4_add(kinc_float32x4_t a, kinc_float32x4_t b) { + return vaddq_f32(a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_div(kinc_float32x4_t a, kinc_float32x4_t b) { +#if defined(__aarch64__) + return vdivq_f32(a, b); +#else + float32x4_t inv = vrecpeq_f32(b); + float32x4_t restep = vrecpsq_f32(b, inv); + inv = vmulq_f32(restep, inv); + return vmulq_f32(a, inv); +#endif +} + +static inline kinc_float32x4_t kinc_float32x4_mul(kinc_float32x4_t a, kinc_float32x4_t b) { + return vmulq_f32(a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_neg(kinc_float32x4_t t) { + return vnegq_f32(t); +} + +static inline kinc_float32x4_t kinc_float32x4_reciprocal_approximation(kinc_float32x4_t t) { + return vrecpeq_f32(t); +} + +static inline kinc_float32x4_t kinc_float32x4_reciprocal_sqrt_approximation(kinc_float32x4_t t) { + return vrsqrteq_f32(t); +} + +static inline kinc_float32x4_t kinc_float32x4_sub(kinc_float32x4_t a, kinc_float32x4_t b) { + return vsubq_f32(a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_sqrt(kinc_float32x4_t t) { +#if defined(__aarch64__) + return vsqrtq_f32(t); +#else + return vmulq_f32(t, vrsqrteq_f32(t)); +#endif +} + +static inline kinc_float32x4_t kinc_float32x4_max(kinc_float32x4_t a, kinc_float32x4_t b) { + return vmaxq_f32(a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_min(kinc_float32x4_t a, kinc_float32x4_t b) { + return vminq_f32(a, b); +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmpeq(kinc_float32x4_t a, kinc_float32x4_t b) { + return vceqq_f32(a, b); +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmpge(kinc_float32x4_t a, kinc_float32x4_t b) { + return vcgeq_f32(a, b); +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmpgt(kinc_float32x4_t a, kinc_float32x4_t b) { + return vcgtq_f32(a, b); +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmple(kinc_float32x4_t a, kinc_float32x4_t b) { + return vcleq_f32(a, b); +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmplt(kinc_float32x4_t a, kinc_float32x4_t b) { + return vcltq_f32(a, b); +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmpneq(kinc_float32x4_t a, kinc_float32x4_t b) { + return vmvnq_u32(vceqq_f32(a, b)); +} + +static inline kinc_float32x4_t kinc_float32x4_sel(kinc_float32x4_t a, kinc_float32x4_t b, kinc_float32x4_mask_t mask) { + return vbslq_f32(mask, a, b); +} + +static inline kinc_float32x4_t kinc_float32x4_or(kinc_float32x4_t a, kinc_float32x4_t b) { + uint32x4_t acvt = vreinterpretq_u32_f32(a); + uint32x4_t bcvt = vreinterpretq_u32_f32(b); + + return vreinterpretq_f32_u32(vorrq_u32(acvt, bcvt)); +} + +static inline kinc_float32x4_t kinc_float32x4_and(kinc_float32x4_t a, kinc_float32x4_t b) { + uint32x4_t acvt = vreinterpretq_u32_f32(a); + uint32x4_t bcvt = vreinterpretq_u32_f32(b); + + return vreinterpretq_f32_u32(vandq_u32(acvt, bcvt)); +} + +static inline kinc_float32x4_t kinc_float32x4_xor(kinc_float32x4_t a, kinc_float32x4_t b) { + uint32x4_t acvt = vreinterpretq_u32_f32(a); + uint32x4_t bcvt = vreinterpretq_u32_f32(b); + + return vreinterpretq_f32_u32(veorq_u32(acvt, bcvt)); +} + +static inline kinc_float32x4_t kinc_float32x4_not(kinc_float32x4_t t) { + uint32x4_t tcvt = vreinterpretq_u32_f32(t); + + return vreinterpretq_f32_u32(vmvnq_u32(tcvt)); +} + +#define kinc_float32x4_shuffle_custom(abcd, efgh, left_1, left_2, right_1, right_2) \ + (kinc_float32x4_t) { \ + vgetq_lane_f32((abcd), ((left_1)&0x3)), vgetq_lane_f32((abcd), ((left_2)&0x3)), vgetq_lane_f32((efgh), ((right_1)&0x3)), \ + vgetq_lane_f32((efgh), ((right_2)&0x3)) \ + } + +static inline kinc_float32x4_t kinc_float32x4_shuffle_aebf(kinc_float32x4_t abcd, kinc_float32x4_t efgh) { +#if defined(__aarch64__) + + return vzip1q_f32(abcd, efgh); + +#else + + float a = vgetq_lane_f32(abcd, 0); + float b = vgetq_lane_f32(abcd, 1); + float e = vgetq_lane_f32(efgh, 0); + float f = vgetq_lane_f32(efgh, 1); + + return (kinc_float32x4_t){a, e, b, f}; + +#endif +} + +static inline kinc_float32x4_t kinc_float32x4_shuffle_cgdh(kinc_float32x4_t abcd, kinc_float32x4_t efgh) { +#if defined(__aarch64__) + + return vzip2q_f32(abcd, efgh); + +#else + + float c = vgetq_lane_f32(abcd, 2); + float d = vgetq_lane_f32(abcd, 3); + float g = vgetq_lane_f32(efgh, 2); + float h = vgetq_lane_f32(efgh, 3); + + return (kinc_float32x4_t){c, g, d, h}; + +#endif +} + +static inline kinc_float32x4_t kinc_float32x4_shuffle_abef(kinc_float32x4_t abcd, kinc_float32x4_t efgh) { + float32x2_t ab = vget_low_f32(abcd); + float32x2_t ef = vget_low_f32(efgh); + + return vcombine_f32(ab, ef); +} + +static inline kinc_float32x4_t kinc_float32x4_shuffle_ghcd(kinc_float32x4_t abcd, kinc_float32x4_t efgh) { + float32x2_t cd = vget_high_f32(abcd); + float32x2_t gh = vget_high_f32(efgh); + + return vcombine_f32(gh, cd); +} + +#else + +#include + +static inline kinc_float32x4_t kinc_float32x4_intrin_load(const float *values) { + kinc_float32x4_t value; + value.values[0] = values[0]; + value.values[1] = values[1]; + value.values[2] = values[2]; + value.values[3] = values[3]; + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_intrin_load_unaligned(const float *values) { + return kinc_float32x4_intrin_load(values); +} + +static inline kinc_float32x4_t kinc_float32x4_load(float a, float b, float c, float d) { + kinc_float32x4_t value; + value.values[0] = a; + value.values[1] = b; + value.values[2] = c; + value.values[3] = d; + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_load_all(float t) { + kinc_float32x4_t value; + value.values[0] = t; + value.values[1] = t; + value.values[2] = t; + value.values[3] = t; + return value; +} + +static inline void kinc_float32x4_store(float *destination, kinc_float32x4_t value) { + destination[0] = value.values[0]; + destination[1] = value.values[1]; + destination[2] = value.values[2]; + destination[3] = value.values[3]; +} + +static inline void kinc_float32x4_store_unaligned(float *destination, kinc_float32x4_t value) { + kinc_float32x4_store(destination, value); +} + +static inline float kinc_float32x4_get(kinc_float32x4_t t, int index) { + return t.values[index]; +} + +static inline kinc_float32x4_t kinc_float32x4_abs(kinc_float32x4_t t) { + kinc_float32x4_t value; + value.values[0] = kinc_abs(t.values[0]); + value.values[1] = kinc_abs(t.values[1]); + value.values[2] = kinc_abs(t.values[2]); + value.values[3] = kinc_abs(t.values[3]); + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_add(kinc_float32x4_t a, kinc_float32x4_t b) { + kinc_float32x4_t value; + value.values[0] = a.values[0] + b.values[0]; + value.values[1] = a.values[1] + b.values[1]; + value.values[2] = a.values[2] + b.values[2]; + value.values[3] = a.values[3] + b.values[3]; + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_div(kinc_float32x4_t a, kinc_float32x4_t b) { + kinc_float32x4_t value; + value.values[0] = a.values[0] / b.values[0]; + value.values[1] = a.values[1] / b.values[1]; + value.values[2] = a.values[2] / b.values[2]; + value.values[3] = a.values[3] / b.values[3]; + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_mul(kinc_float32x4_t a, kinc_float32x4_t b) { + kinc_float32x4_t value; + value.values[0] = a.values[0] * b.values[0]; + value.values[1] = a.values[1] * b.values[1]; + value.values[2] = a.values[2] * b.values[2]; + value.values[3] = a.values[3] * b.values[3]; + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_neg(kinc_float32x4_t t) { + kinc_float32x4_t value; + value.values[0] = -t.values[0]; + value.values[1] = -t.values[1]; + value.values[2] = -t.values[2]; + value.values[3] = -t.values[3]; + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_reciprocal_approximation(kinc_float32x4_t t) { + kinc_float32x4_t value; + value.values[0] = 1.0f / t.values[0]; + value.values[1] = 1.0f / t.values[1]; + value.values[2] = 1.0f / t.values[2]; + value.values[3] = 1.0f / t.values[3]; + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_reciprocal_sqrt_approximation(kinc_float32x4_t t) { + kinc_float32x4_t value; + value.values[0] = 1.0f / sqrtf(t.values[0]); + value.values[1] = 1.0f / sqrtf(t.values[1]); + value.values[2] = 1.0f / sqrtf(t.values[2]); + value.values[3] = 1.0f / sqrtf(t.values[3]); + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_sub(kinc_float32x4_t a, kinc_float32x4_t b) { + kinc_float32x4_t value; + value.values[0] = a.values[0] - b.values[0]; + value.values[1] = a.values[1] - b.values[1]; + value.values[2] = a.values[2] - b.values[2]; + value.values[3] = a.values[3] - b.values[3]; + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_sqrt(kinc_float32x4_t t) { + kinc_float32x4_t value; + value.values[0] = sqrtf(t.values[0]); + value.values[1] = sqrtf(t.values[1]); + value.values[2] = sqrtf(t.values[2]); + value.values[3] = sqrtf(t.values[3]); + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_max(kinc_float32x4_t a, kinc_float32x4_t b) { + kinc_float32x4_t value; + value.values[0] = kinc_max(a.values[0], b.values[0]); + value.values[1] = kinc_max(a.values[1], b.values[1]); + value.values[2] = kinc_max(a.values[2], b.values[2]); + value.values[3] = kinc_max(a.values[3], b.values[3]); + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_min(kinc_float32x4_t a, kinc_float32x4_t b) { + kinc_float32x4_t value; + value.values[0] = kinc_min(a.values[0], b.values[0]); + value.values[1] = kinc_min(a.values[1], b.values[1]); + value.values[2] = kinc_min(a.values[2], b.values[2]); + value.values[3] = kinc_min(a.values[3], b.values[3]); + return value; +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmpeq(kinc_float32x4_t a, kinc_float32x4_t b) { + uint32_t mask_cvt[4]; + mask_cvt[0] = a.values[0] == b.values[0] ? 0xffffffff : 0; + mask_cvt[1] = a.values[1] == b.values[1] ? 0xffffffff : 0; + mask_cvt[2] = a.values[2] == b.values[2] ? 0xffffffff : 0; + mask_cvt[3] = a.values[3] == b.values[3] ? 0xffffffff : 0; + + kinc_float32x4_mask_t mask; + memcpy(&mask.values[0], &mask_cvt[0], sizeof(mask_cvt)); + + return mask; +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmpge(kinc_float32x4_t a, kinc_float32x4_t b) { + uint32_t mask_cvt[4]; + mask_cvt[0] = a.values[0] >= b.values[0] ? 0xffffffff : 0; + mask_cvt[1] = a.values[1] >= b.values[1] ? 0xffffffff : 0; + mask_cvt[2] = a.values[2] >= b.values[2] ? 0xffffffff : 0; + mask_cvt[3] = a.values[3] >= b.values[3] ? 0xffffffff : 0; + + kinc_float32x4_mask_t mask; + memcpy(&mask.values[0], &mask_cvt[0], sizeof(mask_cvt)); + + return mask; +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmpgt(kinc_float32x4_t a, kinc_float32x4_t b) { + uint32_t mask_cvt[4]; + mask_cvt[0] = a.values[0] > b.values[0] ? 0xffffffff : 0; + mask_cvt[1] = a.values[1] > b.values[1] ? 0xffffffff : 0; + mask_cvt[2] = a.values[2] > b.values[2] ? 0xffffffff : 0; + mask_cvt[3] = a.values[3] > b.values[3] ? 0xffffffff : 0; + + kinc_float32x4_mask_t mask; + memcpy(&mask.values[0], &mask_cvt[0], sizeof(mask_cvt)); + + return mask; +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmple(kinc_float32x4_t a, kinc_float32x4_t b) { + uint32_t mask_cvt[4]; + mask_cvt[0] = a.values[0] <= b.values[0] ? 0xffffffff : 0; + mask_cvt[1] = a.values[1] <= b.values[1] ? 0xffffffff : 0; + mask_cvt[2] = a.values[2] <= b.values[2] ? 0xffffffff : 0; + mask_cvt[3] = a.values[3] <= b.values[3] ? 0xffffffff : 0; + + kinc_float32x4_mask_t mask; + memcpy(&mask.values[0], &mask_cvt[0], sizeof(mask_cvt)); + + return mask; +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmplt(kinc_float32x4_t a, kinc_float32x4_t b) { + uint32_t mask_cvt[4]; + mask_cvt[0] = a.values[0] < b.values[0] ? 0xffffffff : 0; + mask_cvt[1] = a.values[1] < b.values[1] ? 0xffffffff : 0; + mask_cvt[2] = a.values[2] < b.values[2] ? 0xffffffff : 0; + mask_cvt[3] = a.values[3] < b.values[3] ? 0xffffffff : 0; + + kinc_float32x4_mask_t mask; + memcpy(&mask.values[0], &mask_cvt[0], sizeof(mask_cvt)); + + return mask; +} + +static inline kinc_float32x4_mask_t kinc_float32x4_cmpneq(kinc_float32x4_t a, kinc_float32x4_t b) { + uint32_t mask_cvt[4]; + mask_cvt[0] = a.values[0] != b.values[0] ? 0xffffffff : 0; + mask_cvt[1] = a.values[1] != b.values[1] ? 0xffffffff : 0; + mask_cvt[2] = a.values[2] != b.values[2] ? 0xffffffff : 0; + mask_cvt[3] = a.values[3] != b.values[3] ? 0xffffffff : 0; + + kinc_float32x4_mask_t mask; + memcpy(&mask.values[0], &mask_cvt[0], sizeof(mask_cvt)); + + return mask; +} + +static inline kinc_float32x4_t kinc_float32x4_sel(kinc_float32x4_t a, kinc_float32x4_t b, kinc_float32x4_mask_t mask) { + kinc_float32x4_t value; + value.values[0] = mask.values[0] != 0.0f ? a.values[0] : b.values[0]; + value.values[1] = mask.values[1] != 0.0f ? a.values[1] : b.values[1]; + value.values[2] = mask.values[2] != 0.0f ? a.values[2] : b.values[2]; + value.values[3] = mask.values[3] != 0.0f ? a.values[3] : b.values[3]; + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_or(kinc_float32x4_t a, kinc_float32x4_t b) { + uint32_t acvt[4]; + uint32_t bcvt[4]; + memcpy(&acvt[0], &a.values[0], sizeof(a)); + memcpy(&bcvt[0], &b.values[0], sizeof(b)); + + acvt[0] |= bcvt[0]; + acvt[1] |= bcvt[1]; + acvt[2] |= bcvt[2]; + acvt[3] |= bcvt[3]; + + kinc_float32x4_t value; + memcpy(&value.values[0], &acvt[0], sizeof(acvt)); + + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_and(kinc_float32x4_t a, kinc_float32x4_t b) { + uint32_t acvt[4]; + uint32_t bcvt[4]; + memcpy(&acvt[0], &a.values[0], sizeof(a)); + memcpy(&bcvt[0], &b.values[0], sizeof(b)); + + acvt[0] &= bcvt[0]; + acvt[1] &= bcvt[1]; + acvt[2] &= bcvt[2]; + acvt[3] &= bcvt[3]; + + kinc_float32x4_t value; + memcpy(&value.values[0], &acvt[0], sizeof(acvt)); + + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_xor(kinc_float32x4_t a, kinc_float32x4_t b) { + uint32_t acvt[4]; + uint32_t bcvt[4]; + memcpy(&acvt[0], &a.values[0], sizeof(a)); + memcpy(&bcvt[0], &b.values[0], sizeof(b)); + + acvt[0] ^= bcvt[0]; + acvt[1] ^= bcvt[1]; + acvt[2] ^= bcvt[2]; + acvt[3] ^= bcvt[3]; + + kinc_float32x4_t value; + memcpy(&value.values[0], &acvt[0], sizeof(acvt)); + + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_not(kinc_float32x4_t t) { + uint32_t tcvt[4]; + memcpy(&tcvt[0], &t.values[0], sizeof(t)); + + tcvt[0] = ~tcvt[0]; + tcvt[1] = ~tcvt[1]; + tcvt[2] = ~tcvt[2]; + tcvt[3] = ~tcvt[3]; + + kinc_float32x4_t value; + memcpy(&value.values[0], &tcvt[0], sizeof(tcvt)); + + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_shuffle_custom(kinc_float32x4_t abcd, kinc_float32x4_t efgh, const uint32_t left_1, const uint32_t left_2, + const uint32_t right_1, const uint32_t right_2) { + kinc_float32x4_t value; + + value.values[0] = abcd.values[left_1 & 0x3]; + value.values[1] = abcd.values[left_2 & 0x3]; + value.values[2] = efgh.values[right_1 & 0x3]; + value.values[3] = efgh.values[right_2 & 0x3]; + + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_shuffle_aebf(kinc_float32x4_t abcd, kinc_float32x4_t efgh) { + kinc_float32x4_t value; + + value.values[0] = abcd.values[0]; + value.values[1] = efgh.values[0]; + value.values[2] = abcd.values[1]; + value.values[3] = efgh.values[1]; + + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_shuffle_cgdh(kinc_float32x4_t abcd, kinc_float32x4_t efgh) { + kinc_float32x4_t value; + + value.values[0] = abcd.values[2]; + value.values[1] = efgh.values[2]; + value.values[2] = abcd.values[3]; + value.values[3] = efgh.values[3]; + + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_shuffle_abef(kinc_float32x4_t abcd, kinc_float32x4_t efgh) { + kinc_float32x4_t value; + + value.values[0] = abcd.values[0]; + value.values[1] = abcd.values[1]; + value.values[2] = efgh.values[0]; + value.values[3] = efgh.values[1]; + + return value; +} + +static inline kinc_float32x4_t kinc_float32x4_shuffle_ghcd(kinc_float32x4_t abcd, kinc_float32x4_t efgh) { + kinc_float32x4_t value; + + value.values[0] = efgh.values[2]; + value.values[1] = efgh.values[3]; + value.values[2] = abcd.values[2]; + value.values[3] = abcd.values[3]; + + return value; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/simd/int16x8.h b/armorcore/sources/kinc/simd/int16x8.h new file mode 100644 index 000000000..3be9f4fd6 --- /dev/null +++ b/armorcore/sources/kinc/simd/int16x8.h @@ -0,0 +1,463 @@ +#pragma once + +#include "types.h" + +/*! \file int16x8.h + \brief Provides 128bit eight-element signed 16-bit integer SIMD operations which are mapped to equivalent SSE2 or Neon operations. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(KINC_SSE2) + +static inline kinc_int16x8_t kinc_int16x8_intrin_load(const int16_t *values) { + return _mm_load_si128((const kinc_int16x8_t *)values); +} + +static inline kinc_int16x8_t kinc_int16x8_intrin_load_unaligned(const int16_t *values) { + return _mm_loadu_si128((const kinc_int16x8_t *)values); +} + +static inline kinc_int16x8_t kinc_int16x8_load(const int16_t values[8]) { + return _mm_set_epi16(values[7], values[6], values[5], values[4], values[3], values[2], values[1], values[0]); +} + +static inline kinc_int16x8_t kinc_int16x8_load_all(int16_t t) { + return _mm_set1_epi16(t); +} + +static inline void kinc_int16x8_store(int16_t *destination, kinc_int16x8_t value) { + _mm_store_si128((kinc_int16x8_t *)destination, value); +} + +static inline void kinc_int16x8_store_unaligned(int16_t *destination, kinc_int16x8_t value) { + _mm_storeu_si128((kinc_int16x8_t *)destination, value); +} + +static inline int16_t kinc_int16x8_get(kinc_int16x8_t t, int index) { + union { + __m128i value; + int16_t elements[8]; + } converter; + converter.value = t; + return converter.elements[index]; +} + +static inline kinc_int16x8_t kinc_int16x8_add(kinc_int16x8_t a, kinc_int16x8_t b) { + return _mm_add_epi16(a, b); +} + +static inline kinc_int16x8_t kinc_int16x8_sub(kinc_int16x8_t a, kinc_int16x8_t b) { + return _mm_sub_epi16(a, b); +} + +static inline kinc_int16x8_t kinc_int16x8_max(kinc_int16x8_t a, kinc_int16x8_t b) { + __m128i mask = _mm_cmpgt_epi16(a, b); + return _mm_xor_si128(b, _mm_and_si128(mask, _mm_xor_si128(a, b))); +} + +static inline kinc_int16x8_t kinc_int16x8_min(kinc_int16x8_t a, kinc_int16x8_t b) { + __m128i mask = _mm_cmplt_epi16(a, b); + return _mm_xor_si128(b, _mm_and_si128(mask, _mm_xor_si128(a, b))); +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmpeq(kinc_int16x8_t a, kinc_int16x8_t b) { + return _mm_cmpeq_epi16(a, b); +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmpge(kinc_int16x8_t a, kinc_int16x8_t b) { + return _mm_or_si128(_mm_cmpgt_epi16(a, b), _mm_cmpeq_epi16(a, b)); +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmpgt(kinc_int16x8_t a, kinc_int16x8_t b) { + return _mm_cmpgt_epi16(a, b); +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmple(kinc_int16x8_t a, kinc_int16x8_t b) { + return _mm_or_si128(_mm_cmplt_epi16(a, b), _mm_cmpeq_epi16(a, b)); +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmplt(kinc_int16x8_t a, kinc_int16x8_t b) { + return _mm_cmplt_epi16(a, b); +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmpneq(kinc_int16x8_t a, kinc_int16x8_t b) { + return _mm_andnot_si128(_mm_cmpeq_epi16(a, b), _mm_set1_epi32(0xffffffff)); +} + +static inline kinc_int16x8_t kinc_int16x8_sel(kinc_int16x8_t a, kinc_int16x8_t b, kinc_int16x8_mask_t mask) { + return _mm_xor_si128(b, _mm_and_si128(mask, _mm_xor_si128(a, b))); +} + +static inline kinc_int16x8_t kinc_int16x8_or(kinc_int16x8_t a, kinc_int16x8_t b) { + return _mm_or_si128(a, b); +} + +static inline kinc_int16x8_t kinc_int16x8_and(kinc_int16x8_t a, kinc_int16x8_t b) { + return _mm_and_si128(a, b); +} + +static inline kinc_int16x8_t kinc_int16x8_xor(kinc_int16x8_t a, kinc_int16x8_t b) { + return _mm_xor_si128(a, b); +} + +static inline kinc_int16x8_t kinc_int16x8_not(kinc_int16x8_t t) { + return _mm_xor_si128(t, _mm_set1_epi32(0xffffffff)); +} + +#elif defined(KINC_NEON) + +static inline kinc_int16x8_t kinc_int16x8_intrin_load(const int16_t *values) { + return vld1q_s16(values); +} + +static inline kinc_int16x8_t kinc_int16x8_intrin_load_unaligned(const int16_t *values) { + return kinc_int16x8_intrin_load(values); +} + +static inline kinc_int16x8_t kinc_int16x8_load(const int16_t values[8]) { + return (kinc_int16x8_t){values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7]}; +} + +static inline kinc_int16x8_t kinc_int16x8_load_all(int16_t t) { + return (kinc_int16x8_t){t, t, t, t, t, t, t, t}; +} + +static inline void kinc_int16x8_store(int16_t *destination, kinc_int16x8_t value) { + vst1q_s16(destination, value); +} + +static inline void kinc_int16x8_store_unaligned(int16_t *destination, kinc_int16x8_t value) { + kinc_int16x8_store(destination, value); +} + +static inline int16_t kinc_int16x8_get(kinc_int16x8_t t, int index) { + return t[index]; +} + +static inline kinc_int16x8_t kinc_int16x8_add(kinc_int16x8_t a, kinc_int16x8_t b) { + return vaddq_s16(a, b); +} + +static inline kinc_int16x8_t kinc_int16x8_sub(kinc_int16x8_t a, kinc_int16x8_t b) { + return vsubq_s16(a, b); +} + +static inline kinc_int16x8_t kinc_int16x8_max(kinc_int16x8_t a, kinc_int16x8_t b) { + return vmaxq_s16(a, b); +} + +static inline kinc_int16x8_t kinc_int16x8_min(kinc_int16x8_t a, kinc_int16x8_t b) { + return vminq_s16(a, b); +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmpeq(kinc_int16x8_t a, kinc_int16x8_t b) { + return vceqq_s16(a, b); +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmpge(kinc_int16x8_t a, kinc_int16x8_t b) { + return vcgeq_s16(a, b); +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmpgt(kinc_int16x8_t a, kinc_int16x8_t b) { + return vcgtq_s16(a, b); +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmple(kinc_int16x8_t a, kinc_int16x8_t b) { + return vcleq_s16(a, b); +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmplt(kinc_int16x8_t a, kinc_int16x8_t b) { + return vcltq_s16(a, b); +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmpneq(kinc_int16x8_t a, kinc_int16x8_t b) { + return vmvnq_u16(vceqq_s16(a, b)); +} + +static inline kinc_int16x8_t kinc_int16x8_sel(kinc_int16x8_t a, kinc_int16x8_t b, kinc_int16x8_mask_t mask) { + return vbslq_s16(mask, a, b); +} + +static inline kinc_int16x8_t kinc_int16x8_or(kinc_int16x8_t a, kinc_int16x8_t b) { + return vorrq_s16(a, b); +} + +static inline kinc_int16x8_t kinc_int16x8_and(kinc_int16x8_t a, kinc_int16x8_t b) { + return vandq_s16(a, b); +} + +static inline kinc_int16x8_t kinc_int16x8_xor(kinc_int16x8_t a, kinc_int16x8_t b) { + return veorq_s16(a, b); +} + +static inline kinc_int16x8_t kinc_int16x8_not(kinc_int16x8_t t) { + return vmvnq_s16(t); +} + +#else + +static inline kinc_int16x8_t kinc_int16x8_intrin_load(const int16_t *values) { + kinc_int16x8_t value; + value.values[0] = values[0]; + value.values[1] = values[1]; + value.values[2] = values[2]; + value.values[3] = values[3]; + value.values[4] = values[4]; + value.values[5] = values[5]; + value.values[6] = values[6]; + value.values[7] = values[7]; + return value; +} + +static inline kinc_int16x8_t kinc_int16x8_intrin_load_unaligned(const int16_t *values) { + return kinc_int16x8_intrin_load(values); +} + +static inline kinc_int16x8_t kinc_int16x8_load(const int16_t values[8]) { + kinc_int16x8_t value; + value.values[0] = values[0]; + value.values[1] = values[1]; + value.values[2] = values[2]; + value.values[3] = values[3]; + value.values[4] = values[4]; + value.values[5] = values[5]; + value.values[6] = values[6]; + value.values[7] = values[7]; + return value; +} + +static inline kinc_int16x8_t kinc_int16x8_load_all(int16_t t) { + kinc_int16x8_t value; + value.values[0] = t; + value.values[1] = t; + value.values[2] = t; + value.values[3] = t; + value.values[4] = t; + value.values[5] = t; + value.values[6] = t; + value.values[7] = t; + return value; +} + +static inline void kinc_int16x8_store(int16_t *destination, kinc_int16x8_t value) { + destination[0] = value.values[0]; + destination[1] = value.values[1]; + destination[2] = value.values[2]; + destination[3] = value.values[3]; + destination[4] = value.values[4]; + destination[5] = value.values[5]; + destination[6] = value.values[6]; + destination[7] = value.values[7]; +} + +static inline void kinc_int16x8_store_unaligned(int16_t *destination, kinc_int16x8_t value) { + return kinc_int16x8_store(destination, value); +} + +static inline int16_t kinc_int16x8_get(kinc_int16x8_t t, int index) { + return t.values[index]; +} + +static inline kinc_int16x8_t kinc_int16x8_add(kinc_int16x8_t a, kinc_int16x8_t b) { + kinc_int16x8_t value; + value.values[0] = a.values[0] + b.values[0]; + value.values[1] = a.values[1] + b.values[1]; + value.values[2] = a.values[2] + b.values[2]; + value.values[3] = a.values[3] + b.values[3]; + value.values[4] = a.values[4] + b.values[4]; + value.values[5] = a.values[5] + b.values[5]; + value.values[6] = a.values[6] + b.values[6]; + value.values[7] = a.values[7] + b.values[7]; + return value; +} + +static inline kinc_int16x8_t kinc_int16x8_sub(kinc_int16x8_t a, kinc_int16x8_t b) { + kinc_int16x8_t value; + value.values[0] = a.values[0] - b.values[0]; + value.values[1] = a.values[1] - b.values[1]; + value.values[2] = a.values[2] - b.values[2]; + value.values[3] = a.values[3] - b.values[3]; + value.values[4] = a.values[4] - b.values[4]; + value.values[5] = a.values[5] - b.values[5]; + value.values[6] = a.values[6] - b.values[6]; + value.values[7] = a.values[7] - b.values[7]; + return value; +} + +static inline kinc_int16x8_t kinc_int16x8_max(kinc_int16x8_t a, kinc_int16x8_t b) { + kinc_int16x8_t value; + value.values[0] = a.values[0] > b.values[0] ? a.values[0] : b.values[0]; + value.values[1] = a.values[1] > b.values[1] ? a.values[1] : b.values[1]; + value.values[2] = a.values[2] > b.values[2] ? a.values[2] : b.values[2]; + value.values[3] = a.values[3] > b.values[3] ? a.values[3] : b.values[3]; + value.values[4] = a.values[4] > b.values[4] ? a.values[4] : b.values[4]; + value.values[5] = a.values[5] > b.values[5] ? a.values[5] : b.values[5]; + value.values[6] = a.values[6] > b.values[6] ? a.values[6] : b.values[6]; + value.values[7] = a.values[7] > b.values[7] ? a.values[7] : b.values[7]; + return value; +} + +static inline kinc_int16x8_t kinc_int16x8_min(kinc_int16x8_t a, kinc_int16x8_t b) { + kinc_int16x8_t value; + value.values[0] = a.values[0] > b.values[0] ? b.values[0] : a.values[0]; + value.values[1] = a.values[1] > b.values[1] ? b.values[1] : a.values[1]; + value.values[2] = a.values[2] > b.values[2] ? b.values[2] : a.values[2]; + value.values[3] = a.values[3] > b.values[3] ? b.values[3] : a.values[3]; + value.values[4] = a.values[4] > b.values[4] ? b.values[4] : a.values[4]; + value.values[5] = a.values[5] > b.values[5] ? b.values[5] : a.values[5]; + value.values[6] = a.values[6] > b.values[6] ? b.values[6] : a.values[6]; + value.values[7] = a.values[7] > b.values[7] ? b.values[7] : a.values[7]; + return value; +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmpeq(kinc_int16x8_t a, kinc_int16x8_t b) { + kinc_int16x8_mask_t mask; + mask.values[0] = a.values[0] == b.values[0] ? 0xffff : 0; + mask.values[1] = a.values[1] == b.values[1] ? 0xffff : 0; + mask.values[2] = a.values[2] == b.values[2] ? 0xffff : 0; + mask.values[3] = a.values[3] == b.values[3] ? 0xffff : 0; + mask.values[4] = a.values[4] == b.values[4] ? 0xffff : 0; + mask.values[5] = a.values[5] == b.values[5] ? 0xffff : 0; + mask.values[6] = a.values[6] == b.values[6] ? 0xffff : 0; + mask.values[7] = a.values[7] == b.values[7] ? 0xffff : 0; + return mask; +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmpge(kinc_int16x8_t a, kinc_int16x8_t b) { + kinc_int16x8_mask_t mask; + mask.values[0] = a.values[0] >= b.values[0] ? 0xffff : 0; + mask.values[1] = a.values[1] >= b.values[1] ? 0xffff : 0; + mask.values[2] = a.values[2] >= b.values[2] ? 0xffff : 0; + mask.values[3] = a.values[3] >= b.values[3] ? 0xffff : 0; + mask.values[4] = a.values[4] >= b.values[4] ? 0xffff : 0; + mask.values[5] = a.values[5] >= b.values[5] ? 0xffff : 0; + mask.values[6] = a.values[6] >= b.values[6] ? 0xffff : 0; + mask.values[7] = a.values[7] >= b.values[7] ? 0xffff : 0; + return mask; +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmpgt(kinc_int16x8_t a, kinc_int16x8_t b) { + kinc_int16x8_mask_t mask; + mask.values[0] = a.values[0] > b.values[0] ? 0xffff : 0; + mask.values[1] = a.values[1] > b.values[1] ? 0xffff : 0; + mask.values[2] = a.values[2] > b.values[2] ? 0xffff : 0; + mask.values[3] = a.values[3] > b.values[3] ? 0xffff : 0; + mask.values[4] = a.values[4] > b.values[4] ? 0xffff : 0; + mask.values[5] = a.values[5] > b.values[5] ? 0xffff : 0; + mask.values[6] = a.values[6] > b.values[6] ? 0xffff : 0; + mask.values[7] = a.values[7] > b.values[7] ? 0xffff : 0; + return mask; +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmple(kinc_int16x8_t a, kinc_int16x8_t b) { + kinc_int16x8_mask_t mask; + mask.values[0] = a.values[0] <= b.values[0] ? 0xffff : 0; + mask.values[1] = a.values[1] <= b.values[1] ? 0xffff : 0; + mask.values[2] = a.values[2] <= b.values[2] ? 0xffff : 0; + mask.values[3] = a.values[3] <= b.values[3] ? 0xffff : 0; + mask.values[4] = a.values[4] <= b.values[4] ? 0xffff : 0; + mask.values[5] = a.values[5] <= b.values[5] ? 0xffff : 0; + mask.values[6] = a.values[6] <= b.values[6] ? 0xffff : 0; + mask.values[7] = a.values[7] <= b.values[7] ? 0xffff : 0; + return mask; +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmplt(kinc_int16x8_t a, kinc_int16x8_t b) { + kinc_int16x8_mask_t mask; + mask.values[0] = a.values[0] < b.values[0] ? 0xffff : 0; + mask.values[1] = a.values[1] < b.values[1] ? 0xffff : 0; + mask.values[2] = a.values[2] < b.values[2] ? 0xffff : 0; + mask.values[3] = a.values[3] < b.values[3] ? 0xffff : 0; + mask.values[4] = a.values[4] < b.values[4] ? 0xffff : 0; + mask.values[5] = a.values[5] < b.values[5] ? 0xffff : 0; + mask.values[6] = a.values[6] < b.values[6] ? 0xffff : 0; + mask.values[7] = a.values[7] < b.values[7] ? 0xffff : 0; + return mask; +} + +static inline kinc_int16x8_mask_t kinc_int16x8_cmpneq(kinc_int16x8_t a, kinc_int16x8_t b) { + kinc_int16x8_mask_t mask; + mask.values[0] = a.values[0] != b.values[0] ? 0xffff : 0; + mask.values[1] = a.values[1] != b.values[1] ? 0xffff : 0; + mask.values[2] = a.values[2] != b.values[2] ? 0xffff : 0; + mask.values[3] = a.values[3] != b.values[3] ? 0xffff : 0; + mask.values[4] = a.values[4] != b.values[4] ? 0xffff : 0; + mask.values[5] = a.values[5] != b.values[5] ? 0xffff : 0; + mask.values[6] = a.values[6] != b.values[6] ? 0xffff : 0; + mask.values[7] = a.values[7] != b.values[7] ? 0xffff : 0; + return mask; +} + +static inline kinc_int16x8_t kinc_int16x8_sel(kinc_int16x8_t a, kinc_int16x8_t b, kinc_int16x8_mask_t mask) { + kinc_int16x8_t value; + value.values[0] = mask.values[0] != 0 ? a.values[0] : b.values[0]; + value.values[1] = mask.values[1] != 0 ? a.values[1] : b.values[1]; + value.values[2] = mask.values[2] != 0 ? a.values[2] : b.values[2]; + value.values[3] = mask.values[3] != 0 ? a.values[3] : b.values[3]; + value.values[4] = mask.values[4] != 0 ? a.values[4] : b.values[4]; + value.values[5] = mask.values[5] != 0 ? a.values[5] : b.values[5]; + value.values[6] = mask.values[6] != 0 ? a.values[6] : b.values[6]; + value.values[7] = mask.values[7] != 0 ? a.values[7] : b.values[7]; + return value; +} + +static inline kinc_int16x8_t kinc_int16x8_or(kinc_int16x8_t a, kinc_int16x8_t b) { + kinc_int16x8_t value; + value.values[0] = a.values[0] | b.values[0]; + value.values[1] = a.values[1] | b.values[1]; + value.values[2] = a.values[2] | b.values[2]; + value.values[3] = a.values[3] | b.values[3]; + value.values[4] = a.values[4] | b.values[4]; + value.values[5] = a.values[5] | b.values[5]; + value.values[6] = a.values[6] | b.values[6]; + value.values[7] = a.values[7] | b.values[7]; + return value; +} + +static inline kinc_int16x8_t kinc_int16x8_and(kinc_int16x8_t a, kinc_int16x8_t b) { + kinc_int16x8_t value; + value.values[0] = a.values[0] & b.values[0]; + value.values[1] = a.values[1] & b.values[1]; + value.values[2] = a.values[2] & b.values[2]; + value.values[3] = a.values[3] & b.values[3]; + value.values[4] = a.values[4] & b.values[4]; + value.values[5] = a.values[5] & b.values[5]; + value.values[6] = a.values[6] & b.values[6]; + value.values[7] = a.values[7] & b.values[7]; + return value; +} + +static inline kinc_int16x8_t kinc_int16x8_xor(kinc_int16x8_t a, kinc_int16x8_t b) { + kinc_int16x8_t value; + value.values[0] = a.values[0] ^ b.values[0]; + value.values[1] = a.values[1] ^ b.values[1]; + value.values[2] = a.values[2] ^ b.values[2]; + value.values[3] = a.values[3] ^ b.values[3]; + value.values[4] = a.values[4] ^ b.values[4]; + value.values[5] = a.values[5] ^ b.values[5]; + value.values[6] = a.values[6] ^ b.values[6]; + value.values[7] = a.values[7] ^ b.values[7]; + return value; +} + +static inline kinc_int16x8_t kinc_int16x8_not(kinc_int16x8_t t) { + kinc_int16x8_t value; + value.values[0] = ~t.values[0]; + value.values[1] = ~t.values[1]; + value.values[2] = ~t.values[2]; + value.values[3] = ~t.values[3]; + value.values[4] = ~t.values[4]; + value.values[5] = ~t.values[5]; + value.values[6] = ~t.values[6]; + value.values[7] = ~t.values[7]; + return value; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/simd/int32x4.h b/armorcore/sources/kinc/simd/int32x4.h new file mode 100644 index 000000000..56db16918 --- /dev/null +++ b/armorcore/sources/kinc/simd/int32x4.h @@ -0,0 +1,387 @@ +#pragma once + +#include "types.h" + +/*! \file int32x4.h + \brief Provides 128bit four-element signed 32-bit integer SIMD operations which are mapped to equivalent SSE2 or Neon operations. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(KINC_SSE2) + +static inline kinc_int32x4_t kinc_int32x4_intrin_load(const int32_t *values) { + return _mm_load_si128((const kinc_int32x4_t *)values); +} + +static inline kinc_int32x4_t kinc_int32x4_intrin_load_unaligned(const int32_t *values) { + return _mm_loadu_si128((const kinc_int32x4_t *)values); +} + +static inline kinc_int32x4_t kinc_int32x4_load(const int32_t values[4]) { + return _mm_set_epi32(values[3], values[2], values[1], values[0]); +} + +static inline kinc_int32x4_t kinc_int32x4_load_all(int32_t t) { + return _mm_set1_epi32(t); +} + +static inline void kinc_int32x4_store(int32_t *destination, kinc_int32x4_t value) { + _mm_store_si128((kinc_int32x4_t *)destination, value); +} + +static inline void kinc_int32x4_store_unaligned(int32_t *destination, kinc_int32x4_t value) { + _mm_storeu_si128((kinc_int32x4_t *)destination, value); +} + +static inline int32_t kinc_int32x4_get(kinc_int32x4_t t, int index) { + union { + __m128i value; + int32_t elements[4]; + } converter; + converter.value = t; + return converter.elements[index]; +} + +static inline kinc_int32x4_t kinc_int32x4_add(kinc_int32x4_t a, kinc_int32x4_t b) { + return _mm_add_epi32(a, b); +} + +static inline kinc_int32x4_t kinc_int32x4_sub(kinc_int32x4_t a, kinc_int32x4_t b) { + return _mm_sub_epi32(a, b); +} + +static inline kinc_int32x4_t kinc_int32x4_max(kinc_int32x4_t a, kinc_int32x4_t b) { + __m128i mask = _mm_cmpgt_epi32(a, b); + return _mm_xor_si128(b, _mm_and_si128(mask, _mm_xor_si128(a, b))); +} + +static inline kinc_int32x4_t kinc_int32x4_min(kinc_int32x4_t a, kinc_int32x4_t b) { + __m128i mask = _mm_cmplt_epi32(a, b); + return _mm_xor_si128(b, _mm_and_si128(mask, _mm_xor_si128(a, b))); +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmpeq(kinc_int32x4_t a, kinc_int32x4_t b) { + return _mm_cmpeq_epi32(a, b); +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmpge(kinc_int32x4_t a, kinc_int32x4_t b) { + return _mm_or_si128(_mm_cmpgt_epi32(a, b), _mm_cmpeq_epi32(a, b)); +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmpgt(kinc_int32x4_t a, kinc_int32x4_t b) { + return _mm_cmpgt_epi32(a, b); +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmple(kinc_int32x4_t a, kinc_int32x4_t b) { + return _mm_or_si128(_mm_cmplt_epi32(a, b), _mm_cmpeq_epi32(a, b)); +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmplt(kinc_int32x4_t a, kinc_int32x4_t b) { + return _mm_cmplt_epi32(a, b); +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmpneq(kinc_int32x4_t a, kinc_int32x4_t b) { + return _mm_andnot_si128(_mm_cmpeq_epi32(a, b), _mm_set1_epi32(0xffffffff)); +} + +static inline kinc_int32x4_t kinc_int32x4_sel(kinc_int32x4_t a, kinc_int32x4_t b, kinc_int32x4_mask_t mask) { + return _mm_xor_si128(b, _mm_and_si128(mask, _mm_xor_si128(a, b))); +} + +static inline kinc_int32x4_t kinc_int32x4_or(kinc_int32x4_t a, kinc_int32x4_t b) { + return _mm_or_si128(a, b); +} + +static inline kinc_int32x4_t kinc_int32x4_and(kinc_int32x4_t a, kinc_int32x4_t b) { + return _mm_and_si128(a, b); +} + +static inline kinc_int32x4_t kinc_int32x4_xor(kinc_int32x4_t a, kinc_int32x4_t b) { + return _mm_xor_si128(a, b); +} + +static inline kinc_int32x4_t kinc_int32x4_not(kinc_int32x4_t t) { + return _mm_xor_si128(t, _mm_set1_epi32(0xffffffff)); +} + +#elif defined(KINC_NEON) + +static inline kinc_int32x4_t kinc_int32x4_intrin_load(const int32_t *values) { + return vld1q_s32(values); +} + +static inline kinc_int32x4_t kinc_int32x4_intrin_load_unaligned(const int32_t *values) { + return kinc_int32x4_intrin_load(values); +} + +static inline kinc_int32x4_t kinc_int32x4_load(const int32_t values[4]) { + return (kinc_int32x4_t){values[0], values[1], values[2], values[3]}; +} + +static inline kinc_int32x4_t kinc_int32x4_load_all(int32_t t) { + return (kinc_int32x4_t){t, t, t, t}; +} + +static inline void kinc_int32x4_store(int32_t *destination, kinc_int32x4_t value) { + vst1q_s32(destination, value); +} + +static inline void kinc_int32x4_store_unaligned(int32_t *destination, kinc_int32x4_t value) { + kinc_int32x4_store(destination, value); +} + +static inline int32_t kinc_int32x4_get(kinc_int32x4_t t, int index) { + return t[index]; +} + +static inline kinc_int32x4_t kinc_int32x4_add(kinc_int32x4_t a, kinc_int32x4_t b) { + return vaddq_s32(a, b); +} + +static inline kinc_int32x4_t kinc_int32x4_sub(kinc_int32x4_t a, kinc_int32x4_t b) { + return vsubq_s32(a, b); +} + +static inline kinc_int32x4_t kinc_int32x4_max(kinc_int32x4_t a, kinc_int32x4_t b) { + return vmaxq_s32(a, b); +} + +static inline kinc_int32x4_t kinc_int32x4_min(kinc_int32x4_t a, kinc_int32x4_t b) { + return vminq_s32(a, b); +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmpeq(kinc_int32x4_t a, kinc_int32x4_t b) { + return vceqq_s32(a, b); +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmpge(kinc_int32x4_t a, kinc_int32x4_t b) { + return vcgeq_s32(a, b); +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmpgt(kinc_int32x4_t a, kinc_int32x4_t b) { + return vcgtq_s32(a, b); +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmple(kinc_int32x4_t a, kinc_int32x4_t b) { + return vcleq_s32(a, b); +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmplt(kinc_int32x4_t a, kinc_int32x4_t b) { + return vcltq_s32(a, b); +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmpneq(kinc_int32x4_t a, kinc_int32x4_t b) { + return vmvnq_u32(vceqq_s32(a, b)); +} + +static inline kinc_int32x4_t kinc_int32x4_sel(kinc_int32x4_t a, kinc_int32x4_t b, kinc_int32x4_mask_t mask) { + return vbslq_s32(mask, a, b); +} + +static inline kinc_int32x4_t kinc_int32x4_or(kinc_int32x4_t a, kinc_int32x4_t b) { + return vorrq_s32(a, b); +} + +static inline kinc_int32x4_t kinc_int32x4_and(kinc_int32x4_t a, kinc_int32x4_t b) { + return vandq_s32(a, b); +} + +static inline kinc_int32x4_t kinc_int32x4_xor(kinc_int32x4_t a, kinc_int32x4_t b) { + return veorq_s32(a, b); +} + +static inline kinc_int32x4_t kinc_int32x4_not(kinc_int32x4_t t) { + return vmvnq_s32(t); +} + +#else + +static inline kinc_int32x4_t kinc_int32x4_intrin_load(const int32_t *values) { + kinc_int32x4_t value; + value.values[0] = values[0]; + value.values[1] = values[1]; + value.values[2] = values[2]; + value.values[3] = values[3]; + return value; +} + +static inline kinc_int32x4_t kinc_int32x4_intrin_load_unaligned(const int32_t *values) { + return kinc_int32x4_intrin_load(values); +} + +static inline kinc_int32x4_t kinc_int32x4_load(const int32_t values[4]) { + kinc_int32x4_t value; + value.values[0] = values[0]; + value.values[1] = values[1]; + value.values[2] = values[2]; + value.values[3] = values[3]; + return value; +} + +static inline kinc_int32x4_t kinc_int32x4_load_all(int32_t t) { + kinc_int32x4_t value; + value.values[0] = t; + value.values[1] = t; + value.values[2] = t; + value.values[3] = t; + return value; +} + +static inline void kinc_int32x4_store(int32_t *destination, kinc_int32x4_t value) { + destination[0] = value.values[0]; + destination[1] = value.values[1]; + destination[2] = value.values[2]; + destination[3] = value.values[3]; +} + +static inline void kinc_int32x4_store_unaligned(int32_t *destination, kinc_int32x4_t value) { + kinc_int32x4_store(destination, value); +} + +static inline int32_t kinc_int32x4_get(kinc_int32x4_t t, int index) { + return t.values[index]; +} + +static inline kinc_int32x4_t kinc_int32x4_add(kinc_int32x4_t a, kinc_int32x4_t b) { + kinc_int32x4_t value; + value.values[0] = a.values[0] + b.values[0]; + value.values[1] = a.values[1] + b.values[1]; + value.values[2] = a.values[2] + b.values[2]; + value.values[3] = a.values[3] + b.values[3]; + return value; +} + +static inline kinc_int32x4_t kinc_int32x4_sub(kinc_int32x4_t a, kinc_int32x4_t b) { + kinc_int32x4_t value; + value.values[0] = a.values[0] - b.values[0]; + value.values[1] = a.values[1] - b.values[1]; + value.values[2] = a.values[2] - b.values[2]; + value.values[3] = a.values[3] - b.values[3]; + return value; +} + +static inline kinc_int32x4_t kinc_int32x4_max(kinc_int32x4_t a, kinc_int32x4_t b) { + kinc_int32x4_t value; + value.values[0] = a.values[0] > b.values[0] ? a.values[0] : b.values[0]; + value.values[1] = a.values[1] > b.values[1] ? a.values[1] : b.values[1]; + value.values[2] = a.values[2] > b.values[2] ? a.values[2] : b.values[2]; + value.values[3] = a.values[3] > b.values[3] ? a.values[3] : b.values[3]; + return value; +} + +static inline kinc_int32x4_t kinc_int32x4_min(kinc_int32x4_t a, kinc_int32x4_t b) { + kinc_int32x4_t value; + value.values[0] = a.values[0] > b.values[0] ? b.values[0] : a.values[0]; + value.values[1] = a.values[1] > b.values[1] ? b.values[1] : a.values[1]; + value.values[2] = a.values[2] > b.values[2] ? b.values[2] : a.values[2]; + value.values[3] = a.values[3] > b.values[3] ? b.values[3] : a.values[3]; + return value; +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmpeq(kinc_int32x4_t a, kinc_int32x4_t b) { + kinc_int32x4_mask_t mask; + mask.values[0] = a.values[0] == b.values[0] ? 0xffffffff : 0; + mask.values[1] = a.values[1] == b.values[1] ? 0xffffffff : 0; + mask.values[2] = a.values[2] == b.values[2] ? 0xffffffff : 0; + mask.values[3] = a.values[3] == b.values[3] ? 0xffffffff : 0; + return mask; +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmpge(kinc_int32x4_t a, kinc_int32x4_t b) { + kinc_int32x4_mask_t mask; + mask.values[0] = a.values[0] >= b.values[0] ? 0xffffffff : 0; + mask.values[1] = a.values[1] >= b.values[1] ? 0xffffffff : 0; + mask.values[2] = a.values[2] >= b.values[2] ? 0xffffffff : 0; + mask.values[3] = a.values[3] >= b.values[3] ? 0xffffffff : 0; + return mask; +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmpgt(kinc_int32x4_t a, kinc_int32x4_t b) { + kinc_int32x4_mask_t mask; + mask.values[0] = a.values[0] > b.values[0] ? 0xffffffff : 0; + mask.values[1] = a.values[1] > b.values[1] ? 0xffffffff : 0; + mask.values[2] = a.values[2] > b.values[2] ? 0xffffffff : 0; + mask.values[3] = a.values[3] > b.values[3] ? 0xffffffff : 0; + return mask; +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmple(kinc_int32x4_t a, kinc_int32x4_t b) { + kinc_int32x4_mask_t mask; + mask.values[0] = a.values[0] <= b.values[0] ? 0xffffffff : 0; + mask.values[1] = a.values[1] <= b.values[1] ? 0xffffffff : 0; + mask.values[2] = a.values[2] <= b.values[2] ? 0xffffffff : 0; + mask.values[3] = a.values[3] <= b.values[3] ? 0xffffffff : 0; + return mask; +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmplt(kinc_int32x4_t a, kinc_int32x4_t b) { + kinc_int32x4_mask_t mask; + mask.values[0] = a.values[0] < b.values[0] ? 0xffffffff : 0; + mask.values[1] = a.values[1] < b.values[1] ? 0xffffffff : 0; + mask.values[2] = a.values[2] < b.values[2] ? 0xffffffff : 0; + mask.values[3] = a.values[3] < b.values[3] ? 0xffffffff : 0; + return mask; +} + +static inline kinc_int32x4_mask_t kinc_int32x4_cmpneq(kinc_int32x4_t a, kinc_int32x4_t b) { + kinc_int32x4_mask_t mask; + mask.values[0] = a.values[0] != b.values[0] ? 0xffffffff : 0; + mask.values[1] = a.values[1] != b.values[1] ? 0xffffffff : 0; + mask.values[2] = a.values[2] != b.values[2] ? 0xffffffff : 0; + mask.values[3] = a.values[3] != b.values[3] ? 0xffffffff : 0; + return mask; +} + +static inline kinc_int32x4_t kinc_int32x4_sel(kinc_int32x4_t a, kinc_int32x4_t b, kinc_int32x4_mask_t mask) { + kinc_int32x4_t value; + value.values[0] = mask.values[0] != 0 ? a.values[0] : b.values[0]; + value.values[1] = mask.values[1] != 0 ? a.values[1] : b.values[1]; + value.values[2] = mask.values[2] != 0 ? a.values[2] : b.values[2]; + value.values[3] = mask.values[3] != 0 ? a.values[3] : b.values[3]; + return value; +} + +static inline kinc_int32x4_t kinc_int32x4_or(kinc_int32x4_t a, kinc_int32x4_t b) { + kinc_int32x4_t value; + value.values[0] = a.values[0] | b.values[0]; + value.values[1] = a.values[1] | b.values[1]; + value.values[2] = a.values[2] | b.values[2]; + value.values[3] = a.values[3] | b.values[3]; + return value; +} + +static inline kinc_int32x4_t kinc_int32x4_and(kinc_int32x4_t a, kinc_int32x4_t b) { + kinc_int32x4_t value; + value.values[0] = a.values[0] & b.values[0]; + value.values[1] = a.values[1] & b.values[1]; + value.values[2] = a.values[2] & b.values[2]; + value.values[3] = a.values[3] & b.values[3]; + return value; +} + +static inline kinc_int32x4_t kinc_int32x4_xor(kinc_int32x4_t a, kinc_int32x4_t b) { + kinc_int32x4_t value; + value.values[0] = a.values[0] ^ b.values[0]; + value.values[1] = a.values[1] ^ b.values[1]; + value.values[2] = a.values[2] ^ b.values[2]; + value.values[3] = a.values[3] ^ b.values[3]; + return value; +} + +static inline kinc_int32x4_t kinc_int32x4_not(kinc_int32x4_t t) { + kinc_int32x4_t value; + value.values[0] = ~t.values[0]; + value.values[1] = ~t.values[1]; + value.values[2] = ~t.values[2]; + value.values[3] = ~t.values[3]; + return value; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/simd/int8x16.h b/armorcore/sources/kinc/simd/int8x16.h new file mode 100644 index 000000000..91ca1c64c --- /dev/null +++ b/armorcore/sources/kinc/simd/int8x16.h @@ -0,0 +1,620 @@ +#pragma once + +#include "types.h" + +/*! \file int8x16.h + \brief Provides 128bit sixteen-element signed 8-bit integer SIMD operations which are mapped to equivalent SSE2 or Neon operations. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(KINC_SSE2) + +static inline kinc_int8x16_t kinc_int8x16_intrin_load(const int8_t *values) { + return _mm_load_si128((const kinc_int8x16_t *)values); +} + +static inline kinc_int8x16_t kinc_int8x16_intrin_load_unaligned(const int8_t *values) { + return _mm_loadu_si128((const kinc_int8x16_t *)values); +} + +static inline kinc_int8x16_t kinc_int8x16_load(const int8_t values[16]) { + return _mm_set_epi8(values[15], values[14], values[13], values[12], values[11], values[10], values[9], values[8], values[7], values[6], values[5], + values[4], values[3], values[2], values[1], values[0]); +} + +static inline kinc_int8x16_t kinc_int8x16_load_all(int8_t t) { + return _mm_set1_epi8(t); +} + +static inline void kinc_int8x16_store(int8_t *destination, kinc_int8x16_t value) { + _mm_store_si128((kinc_int8x16_t *)destination, value); +} + +static inline void kinc_int8x16_store_unaligned(int8_t *destination, kinc_int8x16_t value) { + _mm_storeu_si128((kinc_int8x16_t *)destination, value); +} + +static inline int8_t kinc_int8x16_get(kinc_int8x16_t t, int index) { + union { + __m128i value; + int8_t elements[16]; + } converter; + converter.value = t; + return converter.elements[index]; +} + +static inline kinc_int8x16_t kinc_int8x16_add(kinc_int8x16_t a, kinc_int8x16_t b) { + return _mm_add_epi8(a, b); +} + +static inline kinc_int8x16_t kinc_int8x16_sub(kinc_int8x16_t a, kinc_int8x16_t b) { + return _mm_sub_epi8(a, b); +} + +static inline kinc_int8x16_t kinc_int8x16_max(kinc_int8x16_t a, kinc_int8x16_t b) { + __m128i mask = _mm_cmpgt_epi8(a, b); + return _mm_xor_si128(b, _mm_and_si128(mask, _mm_xor_si128(a, b))); +} + +static inline kinc_int8x16_t kinc_int8x16_min(kinc_int8x16_t a, kinc_int8x16_t b) { + __m128i mask = _mm_cmplt_epi8(a, b); + return _mm_xor_si128(b, _mm_and_si128(mask, _mm_xor_si128(a, b))); +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmpeq(kinc_int8x16_t a, kinc_int8x16_t b) { + return _mm_cmpeq_epi8(a, b); +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmpge(kinc_int8x16_t a, kinc_int8x16_t b) { + return _mm_or_si128(_mm_cmpgt_epi8(a, b), _mm_cmpeq_epi8(a, b)); +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmpgt(kinc_int8x16_t a, kinc_int8x16_t b) { + return _mm_cmpgt_epi8(a, b); +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmple(kinc_int8x16_t a, kinc_int8x16_t b) { + return _mm_or_si128(_mm_cmplt_epi8(a, b), _mm_cmpeq_epi8(a, b)); +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmplt(kinc_int8x16_t a, kinc_int8x16_t b) { + return _mm_cmplt_epi8(a, b); +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmpneq(kinc_int8x16_t a, kinc_int8x16_t b) { + __m128i mask = _mm_cmpeq_epi8(a, b); + __m128i all = _mm_set1_epi32(0xffffffff); + return _mm_andnot_si128(mask, all); +} + +static inline kinc_int8x16_t kinc_int8x16_sel(kinc_int8x16_t a, kinc_int8x16_t b, kinc_int8x16_mask_t mask) { + return _mm_xor_si128(b, _mm_and_si128(mask, _mm_xor_si128(a, b))); +} + +static inline kinc_int8x16_t kinc_int8x16_or(kinc_int8x16_t a, kinc_int8x16_t b) { + return _mm_or_si128(a, b); +} + +static inline kinc_int8x16_t kinc_int8x16_and(kinc_int8x16_t a, kinc_int8x16_t b) { + return _mm_and_si128(a, b); +} + +static inline kinc_int8x16_t kinc_int8x16_xor(kinc_int8x16_t a, kinc_int8x16_t b) { + return _mm_xor_si128(a, b); +} + +static inline kinc_int8x16_t kinc_int8x16_not(kinc_int8x16_t t) { + __m128i mask = _mm_set1_epi32(0xffffffff); + return _mm_xor_si128(t, mask); +} + +#elif defined(KINC_NEON) + +static inline kinc_int8x16_t kinc_int8x16_intrin_load(const int8_t *values) { + return vld1q_s8(values); +} + +static inline kinc_int8x16_t kinc_int8x16_intrin_load_unaligned(const int8_t *values) { + return kinc_int8x16_intrin_load(values); +} + +static inline kinc_int8x16_t kinc_int8x16_load(const int8_t values[16]) { + return (kinc_int8x16_t){values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], + values[8], values[9], values[10], values[11], values[12], values[13], values[14], values[15]}; +} + +static inline kinc_int8x16_t kinc_int8x16_load_all(int8_t t) { + return (kinc_int8x16_t){t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t}; +} + +static inline void kinc_int8x16_store(int8_t *destination, kinc_int8x16_t value) { + vst1q_s8(destination, value); +} + +static inline void kinc_int8x16_store_unaligned(int8_t *destination, kinc_int8x16_t value) { + kinc_int8x16_store(destination, value); +} + +static inline int8_t kinc_int8x16_get(kinc_int8x16_t t, int index) { + return t[index]; +} + +static inline kinc_int8x16_t kinc_int8x16_add(kinc_int8x16_t a, kinc_int8x16_t b) { + return vaddq_s8(a, b); +} + +static inline kinc_int8x16_t kinc_int8x16_sub(kinc_int8x16_t a, kinc_int8x16_t b) { + return vsubq_s8(a, b); +} + +static inline kinc_int8x16_t kinc_int8x16_max(kinc_int8x16_t a, kinc_int8x16_t b) { + return vmaxq_s8(a, b); +} + +static inline kinc_int8x16_t kinc_int8x16_min(kinc_int8x16_t a, kinc_int8x16_t b) { + return vminq_s8(a, b); +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmpeq(kinc_int8x16_t a, kinc_int8x16_t b) { + return vceqq_s8(a, b); +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmpge(kinc_int8x16_t a, kinc_int8x16_t b) { + return vcgeq_s8(a, b); +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmpgt(kinc_int8x16_t a, kinc_int8x16_t b) { + return vcgtq_s8(a, b); +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmple(kinc_int8x16_t a, kinc_int8x16_t b) { + return vcleq_s8(a, b); +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmplt(kinc_int8x16_t a, kinc_int8x16_t b) { + return vcltq_s8(a, b); +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmpneq(kinc_int8x16_t a, kinc_int8x16_t b) { + return vmvnq_u8(vceqq_s8(a, b)); +} + +static inline kinc_int8x16_t kinc_int8x16_sel(kinc_int8x16_t a, kinc_int8x16_t b, kinc_int8x16_mask_t mask) { + return vbslq_s8(mask, a, b); +} + +static inline kinc_int8x16_t kinc_int8x16_or(kinc_int8x16_t a, kinc_int8x16_t b) { + return vorrq_s8(a, b); +} + +static inline kinc_int8x16_t kinc_int8x16_and(kinc_int8x16_t a, kinc_int8x16_t b) { + return vandq_s8(a, b); +} + +static inline kinc_int8x16_t kinc_int8x16_xor(kinc_int8x16_t a, kinc_int8x16_t b) { + return veorq_s8(a, b); +} + +static inline kinc_int8x16_t kinc_int8x16_not(kinc_int8x16_t t) { + return vmvnq_s8(t); +} + +#else + +static inline kinc_int8x16_t kinc_int8x16_intrin_load(const int8_t *values) { + kinc_int8x16_t value; + value.values[0] = values[0]; + value.values[1] = values[1]; + value.values[2] = values[2]; + value.values[3] = values[3]; + value.values[4] = values[4]; + value.values[5] = values[5]; + value.values[6] = values[6]; + value.values[7] = values[7]; + value.values[8] = values[8]; + value.values[9] = values[9]; + value.values[10] = values[10]; + value.values[11] = values[11]; + value.values[12] = values[12]; + value.values[13] = values[13]; + value.values[14] = values[14]; + value.values[15] = values[15]; + return value; +} + +static inline kinc_int8x16_t kinc_int8x16_intrin_load_unaligned(const int8_t *values) { + return kinc_int8x16_intrin_load(values); +} + +static inline kinc_int8x16_t kinc_int8x16_load(const int8_t values[16]) { + kinc_int8x16_t value; + value.values[0] = values[0]; + value.values[1] = values[1]; + value.values[2] = values[2]; + value.values[3] = values[3]; + value.values[4] = values[4]; + value.values[5] = values[5]; + value.values[6] = values[6]; + value.values[7] = values[7]; + value.values[8] = values[8]; + value.values[9] = values[9]; + value.values[10] = values[10]; + value.values[11] = values[11]; + value.values[12] = values[12]; + value.values[13] = values[13]; + value.values[14] = values[14]; + value.values[15] = values[15]; + return value; +} + +static inline kinc_int8x16_t kinc_int8x16_load_all(int8_t t) { + kinc_int8x16_t value; + value.values[0] = t; + value.values[1] = t; + value.values[2] = t; + value.values[3] = t; + value.values[4] = t; + value.values[5] = t; + value.values[6] = t; + value.values[7] = t; + value.values[8] = t; + value.values[9] = t; + value.values[10] = t; + value.values[11] = t; + value.values[12] = t; + value.values[13] = t; + value.values[14] = t; + value.values[15] = t; + return value; +} + +static inline void kinc_int8x16_store(int8_t *destination, kinc_int8x16_t value) { + destination[0] = value.values[0]; + destination[1] = value.values[1]; + destination[2] = value.values[2]; + destination[3] = value.values[3]; + destination[4] = value.values[4]; + destination[5] = value.values[5]; + destination[6] = value.values[6]; + destination[7] = value.values[7]; + destination[8] = value.values[8]; + destination[9] = value.values[9]; + destination[10] = value.values[10]; + destination[11] = value.values[11]; + destination[12] = value.values[12]; + destination[13] = value.values[13]; + destination[14] = value.values[14]; + destination[15] = value.values[15]; +} + +static inline void kinc_int8x16_store_unaligned(int8_t *destination, kinc_int8x16_t value) { + return kinc_int8x16_store(destination, value); +} + +static inline int8_t kinc_int8x16_get(kinc_int8x16_t t, int index) { + return t.values[index]; +} + +static inline kinc_int8x16_t kinc_int8x16_add(kinc_int8x16_t a, kinc_int8x16_t b) { + kinc_int8x16_t value; + value.values[0] = a.values[0] + b.values[0]; + value.values[1] = a.values[1] + b.values[1]; + value.values[2] = a.values[2] + b.values[2]; + value.values[3] = a.values[3] + b.values[3]; + value.values[4] = a.values[4] + b.values[4]; + value.values[5] = a.values[5] + b.values[5]; + value.values[6] = a.values[6] + b.values[6]; + value.values[7] = a.values[7] + b.values[7]; + value.values[8] = a.values[8] + b.values[8]; + value.values[9] = a.values[9] + b.values[9]; + value.values[10] = a.values[10] + b.values[10]; + value.values[11] = a.values[11] + b.values[11]; + value.values[12] = a.values[12] + b.values[12]; + value.values[13] = a.values[13] + b.values[13]; + value.values[14] = a.values[14] + b.values[14]; + value.values[15] = a.values[15] + b.values[15]; + return value; +} + +static inline kinc_int8x16_t kinc_int8x16_sub(kinc_int8x16_t a, kinc_int8x16_t b) { + kinc_int8x16_t value; + value.values[0] = a.values[0] - b.values[0]; + value.values[1] = a.values[1] - b.values[1]; + value.values[2] = a.values[2] - b.values[2]; + value.values[3] = a.values[3] - b.values[3]; + value.values[4] = a.values[4] - b.values[4]; + value.values[5] = a.values[5] - b.values[5]; + value.values[6] = a.values[6] - b.values[6]; + value.values[7] = a.values[7] - b.values[7]; + value.values[8] = a.values[8] - b.values[8]; + value.values[9] = a.values[9] - b.values[9]; + value.values[10] = a.values[10] - b.values[10]; + value.values[11] = a.values[11] - b.values[11]; + value.values[12] = a.values[12] - b.values[12]; + value.values[13] = a.values[13] - b.values[13]; + value.values[14] = a.values[14] - b.values[14]; + value.values[15] = a.values[15] - b.values[15]; + return value; +} + +static inline kinc_int8x16_t kinc_int8x16_max(kinc_int8x16_t a, kinc_int8x16_t b) { + kinc_int8x16_t value; + value.values[0] = a.values[0] > b.values[0] ? a.values[0] : b.values[0]; + value.values[1] = a.values[1] > b.values[1] ? a.values[1] : b.values[1]; + value.values[2] = a.values[2] > b.values[2] ? a.values[2] : b.values[2]; + value.values[3] = a.values[3] > b.values[3] ? a.values[3] : b.values[3]; + value.values[4] = a.values[4] > b.values[4] ? a.values[4] : b.values[4]; + value.values[5] = a.values[5] > b.values[5] ? a.values[5] : b.values[5]; + value.values[6] = a.values[6] > b.values[6] ? a.values[6] : b.values[6]; + value.values[7] = a.values[7] > b.values[7] ? a.values[7] : b.values[7]; + value.values[8] = a.values[8] > b.values[8] ? a.values[8] : b.values[8]; + value.values[9] = a.values[9] > b.values[9] ? a.values[9] : b.values[9]; + value.values[10] = a.values[10] > b.values[10] ? a.values[10] : b.values[10]; + value.values[11] = a.values[11] > b.values[11] ? a.values[11] : b.values[11]; + value.values[12] = a.values[12] > b.values[12] ? a.values[12] : b.values[12]; + value.values[13] = a.values[13] > b.values[13] ? a.values[13] : b.values[13]; + value.values[14] = a.values[14] > b.values[14] ? a.values[14] : b.values[14]; + value.values[15] = a.values[15] > b.values[15] ? a.values[15] : b.values[15]; + return value; +} + +static inline kinc_int8x16_t kinc_int8x16_min(kinc_int8x16_t a, kinc_int8x16_t b) { + kinc_int8x16_t value; + value.values[0] = a.values[0] > b.values[0] ? b.values[0] : a.values[0]; + value.values[1] = a.values[1] > b.values[1] ? b.values[1] : a.values[1]; + value.values[2] = a.values[2] > b.values[2] ? b.values[2] : a.values[2]; + value.values[3] = a.values[3] > b.values[3] ? b.values[3] : a.values[3]; + value.values[4] = a.values[4] > b.values[4] ? b.values[4] : a.values[4]; + value.values[5] = a.values[5] > b.values[5] ? b.values[5] : a.values[5]; + value.values[6] = a.values[6] > b.values[6] ? b.values[6] : a.values[6]; + value.values[7] = a.values[7] > b.values[7] ? b.values[7] : a.values[7]; + value.values[8] = a.values[8] > b.values[8] ? b.values[8] : a.values[8]; + value.values[9] = a.values[9] > b.values[9] ? b.values[9] : a.values[9]; + value.values[10] = a.values[10] > b.values[10] ? b.values[10] : a.values[10]; + value.values[11] = a.values[11] > b.values[11] ? b.values[11] : a.values[11]; + value.values[12] = a.values[12] > b.values[12] ? b.values[12] : a.values[12]; + value.values[13] = a.values[13] > b.values[13] ? b.values[13] : a.values[13]; + value.values[14] = a.values[14] > b.values[14] ? b.values[14] : a.values[14]; + value.values[15] = a.values[15] > b.values[15] ? b.values[15] : a.values[15]; + return value; +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmpeq(kinc_int8x16_t a, kinc_int8x16_t b) { + kinc_int8x16_mask_t mask; + mask.values[0] = a.values[0] == b.values[0] ? 0xff : 0; + mask.values[1] = a.values[1] == b.values[1] ? 0xff : 0; + mask.values[2] = a.values[2] == b.values[2] ? 0xff : 0; + mask.values[3] = a.values[3] == b.values[3] ? 0xff : 0; + mask.values[4] = a.values[4] == b.values[4] ? 0xff : 0; + mask.values[5] = a.values[5] == b.values[5] ? 0xff : 0; + mask.values[6] = a.values[6] == b.values[6] ? 0xff : 0; + mask.values[7] = a.values[7] == b.values[7] ? 0xff : 0; + mask.values[8] = a.values[8] == b.values[8] ? 0xff : 0; + mask.values[9] = a.values[9] == b.values[9] ? 0xff : 0; + mask.values[10] = a.values[10] == b.values[10] ? 0xff : 0; + mask.values[11] = a.values[11] == b.values[11] ? 0xff : 0; + mask.values[12] = a.values[12] == b.values[12] ? 0xff : 0; + mask.values[13] = a.values[13] == b.values[13] ? 0xff : 0; + mask.values[14] = a.values[14] == b.values[14] ? 0xff : 0; + mask.values[15] = a.values[15] == b.values[15] ? 0xff : 0; + return mask; +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmpge(kinc_int8x16_t a, kinc_int8x16_t b) { + kinc_int8x16_mask_t mask; + mask.values[0] = a.values[0] >= b.values[0] ? 0xff : 0; + mask.values[1] = a.values[1] >= b.values[1] ? 0xff : 0; + mask.values[2] = a.values[2] >= b.values[2] ? 0xff : 0; + mask.values[3] = a.values[3] >= b.values[3] ? 0xff : 0; + mask.values[4] = a.values[4] >= b.values[4] ? 0xff : 0; + mask.values[5] = a.values[5] >= b.values[5] ? 0xff : 0; + mask.values[6] = a.values[6] >= b.values[6] ? 0xff : 0; + mask.values[7] = a.values[7] >= b.values[7] ? 0xff : 0; + mask.values[8] = a.values[8] >= b.values[8] ? 0xff : 0; + mask.values[9] = a.values[9] >= b.values[9] ? 0xff : 0; + mask.values[10] = a.values[10] >= b.values[10] ? 0xff : 0; + mask.values[11] = a.values[11] >= b.values[11] ? 0xff : 0; + mask.values[12] = a.values[12] >= b.values[12] ? 0xff : 0; + mask.values[13] = a.values[13] >= b.values[13] ? 0xff : 0; + mask.values[14] = a.values[14] >= b.values[14] ? 0xff : 0; + mask.values[15] = a.values[15] >= b.values[15] ? 0xff : 0; + return mask; +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmpgt(kinc_int8x16_t a, kinc_int8x16_t b) { + kinc_int8x16_mask_t mask; + mask.values[0] = a.values[0] > b.values[0] ? 0xff : 0; + mask.values[1] = a.values[1] > b.values[1] ? 0xff : 0; + mask.values[2] = a.values[2] > b.values[2] ? 0xff : 0; + mask.values[3] = a.values[3] > b.values[3] ? 0xff : 0; + mask.values[4] = a.values[4] > b.values[4] ? 0xff : 0; + mask.values[5] = a.values[5] > b.values[5] ? 0xff : 0; + mask.values[6] = a.values[6] > b.values[6] ? 0xff : 0; + mask.values[7] = a.values[7] > b.values[7] ? 0xff : 0; + mask.values[8] = a.values[8] > b.values[8] ? 0xff : 0; + mask.values[9] = a.values[9] > b.values[9] ? 0xff : 0; + mask.values[10] = a.values[10] > b.values[10] ? 0xff : 0; + mask.values[11] = a.values[11] > b.values[11] ? 0xff : 0; + mask.values[12] = a.values[12] > b.values[12] ? 0xff : 0; + mask.values[13] = a.values[13] > b.values[13] ? 0xff : 0; + mask.values[14] = a.values[14] > b.values[14] ? 0xff : 0; + mask.values[15] = a.values[15] > b.values[15] ? 0xff : 0; + return mask; +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmple(kinc_int8x16_t a, kinc_int8x16_t b) { + kinc_int8x16_mask_t mask; + mask.values[0] = a.values[0] <= b.values[0] ? 0xff : 0; + mask.values[1] = a.values[1] <= b.values[1] ? 0xff : 0; + mask.values[2] = a.values[2] <= b.values[2] ? 0xff : 0; + mask.values[3] = a.values[3] <= b.values[3] ? 0xff : 0; + mask.values[4] = a.values[4] <= b.values[4] ? 0xff : 0; + mask.values[5] = a.values[5] <= b.values[5] ? 0xff : 0; + mask.values[6] = a.values[6] <= b.values[6] ? 0xff : 0; + mask.values[7] = a.values[7] <= b.values[7] ? 0xff : 0; + mask.values[8] = a.values[8] <= b.values[8] ? 0xff : 0; + mask.values[9] = a.values[9] <= b.values[9] ? 0xff : 0; + mask.values[10] = a.values[10] <= b.values[10] ? 0xff : 0; + mask.values[11] = a.values[11] <= b.values[11] ? 0xff : 0; + mask.values[12] = a.values[12] <= b.values[12] ? 0xff : 0; + mask.values[13] = a.values[13] <= b.values[13] ? 0xff : 0; + mask.values[14] = a.values[14] <= b.values[14] ? 0xff : 0; + mask.values[15] = a.values[15] <= b.values[15] ? 0xff : 0; + return mask; +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmplt(kinc_int8x16_t a, kinc_int8x16_t b) { + kinc_int8x16_mask_t mask; + mask.values[0] = a.values[0] < b.values[0] ? 0xff : 0; + mask.values[1] = a.values[1] < b.values[1] ? 0xff : 0; + mask.values[2] = a.values[2] < b.values[2] ? 0xff : 0; + mask.values[3] = a.values[3] < b.values[3] ? 0xff : 0; + mask.values[4] = a.values[4] < b.values[4] ? 0xff : 0; + mask.values[5] = a.values[5] < b.values[5] ? 0xff : 0; + mask.values[6] = a.values[6] < b.values[6] ? 0xff : 0; + mask.values[7] = a.values[7] < b.values[7] ? 0xff : 0; + mask.values[8] = a.values[8] < b.values[8] ? 0xff : 0; + mask.values[9] = a.values[9] < b.values[9] ? 0xff : 0; + mask.values[10] = a.values[10] < b.values[10] ? 0xff : 0; + mask.values[11] = a.values[11] < b.values[11] ? 0xff : 0; + mask.values[12] = a.values[12] < b.values[12] ? 0xff : 0; + mask.values[13] = a.values[13] < b.values[13] ? 0xff : 0; + mask.values[14] = a.values[14] < b.values[14] ? 0xff : 0; + mask.values[15] = a.values[15] < b.values[15] ? 0xff : 0; + return mask; +} + +static inline kinc_int8x16_mask_t kinc_int8x16_cmpneq(kinc_int8x16_t a, kinc_int8x16_t b) { + kinc_int8x16_mask_t mask; + mask.values[0] = a.values[0] != b.values[0] ? 0xff : 0; + mask.values[1] = a.values[1] != b.values[1] ? 0xff : 0; + mask.values[2] = a.values[2] != b.values[2] ? 0xff : 0; + mask.values[3] = a.values[3] != b.values[3] ? 0xff : 0; + mask.values[4] = a.values[4] != b.values[4] ? 0xff : 0; + mask.values[5] = a.values[5] != b.values[5] ? 0xff : 0; + mask.values[6] = a.values[6] != b.values[6] ? 0xff : 0; + mask.values[7] = a.values[7] != b.values[7] ? 0xff : 0; + mask.values[8] = a.values[8] != b.values[8] ? 0xff : 0; + mask.values[9] = a.values[9] != b.values[9] ? 0xff : 0; + mask.values[10] = a.values[10] != b.values[10] ? 0xff : 0; + mask.values[11] = a.values[11] != b.values[11] ? 0xff : 0; + mask.values[12] = a.values[12] != b.values[12] ? 0xff : 0; + mask.values[13] = a.values[13] != b.values[13] ? 0xff : 0; + mask.values[14] = a.values[14] != b.values[14] ? 0xff : 0; + mask.values[15] = a.values[15] != b.values[15] ? 0xff : 0; + return mask; +} + +static inline kinc_int8x16_t kinc_int8x16_sel(kinc_int8x16_t a, kinc_int8x16_t b, kinc_int8x16_mask_t mask) { + kinc_int8x16_t value; + value.values[0] = mask.values[0] != 0 ? a.values[0] : b.values[0]; + value.values[1] = mask.values[1] != 0 ? a.values[1] : b.values[1]; + value.values[2] = mask.values[2] != 0 ? a.values[2] : b.values[2]; + value.values[3] = mask.values[3] != 0 ? a.values[3] : b.values[3]; + value.values[4] = mask.values[4] != 0 ? a.values[4] : b.values[4]; + value.values[5] = mask.values[5] != 0 ? a.values[5] : b.values[5]; + value.values[6] = mask.values[6] != 0 ? a.values[6] : b.values[6]; + value.values[7] = mask.values[7] != 0 ? a.values[7] : b.values[7]; + value.values[8] = mask.values[8] != 0 ? a.values[8] : b.values[8]; + value.values[9] = mask.values[9] != 0 ? a.values[9] : b.values[9]; + value.values[10] = mask.values[10] != 0 ? a.values[10] : b.values[10]; + value.values[11] = mask.values[11] != 0 ? a.values[11] : b.values[11]; + value.values[12] = mask.values[12] != 0 ? a.values[12] : b.values[12]; + value.values[13] = mask.values[13] != 0 ? a.values[13] : b.values[13]; + value.values[14] = mask.values[14] != 0 ? a.values[14] : b.values[14]; + value.values[15] = mask.values[15] != 0 ? a.values[15] : b.values[15]; + return value; +} + +static inline kinc_int8x16_t kinc_int8x16_or(kinc_int8x16_t a, kinc_int8x16_t b) { + kinc_int8x16_t value; + value.values[0] = a.values[0] | b.values[0]; + value.values[1] = a.values[1] | b.values[1]; + value.values[2] = a.values[2] | b.values[2]; + value.values[3] = a.values[3] | b.values[3]; + value.values[4] = a.values[4] | b.values[4]; + value.values[5] = a.values[5] | b.values[5]; + value.values[6] = a.values[6] | b.values[6]; + value.values[7] = a.values[7] | b.values[7]; + value.values[8] = a.values[8] | b.values[8]; + value.values[9] = a.values[9] | b.values[9]; + value.values[10] = a.values[10] | b.values[10]; + value.values[11] = a.values[11] | b.values[11]; + value.values[12] = a.values[12] | b.values[12]; + value.values[13] = a.values[13] | b.values[13]; + value.values[14] = a.values[14] | b.values[14]; + value.values[15] = a.values[15] | b.values[15]; + return value; +} + +static inline kinc_int8x16_t kinc_int8x16_and(kinc_int8x16_t a, kinc_int8x16_t b) { + kinc_int8x16_t value; + value.values[0] = a.values[0] & b.values[0]; + value.values[1] = a.values[1] & b.values[1]; + value.values[2] = a.values[2] & b.values[2]; + value.values[3] = a.values[3] & b.values[3]; + value.values[4] = a.values[4] & b.values[4]; + value.values[5] = a.values[5] & b.values[5]; + value.values[6] = a.values[6] & b.values[6]; + value.values[7] = a.values[7] & b.values[7]; + value.values[8] = a.values[8] & b.values[8]; + value.values[9] = a.values[9] & b.values[9]; + value.values[10] = a.values[10] & b.values[10]; + value.values[11] = a.values[11] & b.values[11]; + value.values[12] = a.values[12] & b.values[12]; + value.values[13] = a.values[13] & b.values[13]; + value.values[14] = a.values[14] & b.values[14]; + value.values[15] = a.values[15] & b.values[15]; + return value; +} + +static inline kinc_int8x16_t kinc_int8x16_xor(kinc_int8x16_t a, kinc_int8x16_t b) { + kinc_int8x16_t value; + value.values[0] = a.values[0] ^ b.values[0]; + value.values[1] = a.values[1] ^ b.values[1]; + value.values[2] = a.values[2] ^ b.values[2]; + value.values[3] = a.values[3] ^ b.values[3]; + value.values[4] = a.values[4] ^ b.values[4]; + value.values[5] = a.values[5] ^ b.values[5]; + value.values[6] = a.values[6] ^ b.values[6]; + value.values[7] = a.values[7] ^ b.values[7]; + value.values[8] = a.values[8] ^ b.values[8]; + value.values[9] = a.values[9] ^ b.values[9]; + value.values[10] = a.values[10] ^ b.values[10]; + value.values[11] = a.values[11] ^ b.values[11]; + value.values[12] = a.values[12] ^ b.values[12]; + value.values[13] = a.values[13] ^ b.values[13]; + value.values[14] = a.values[14] ^ b.values[14]; + value.values[15] = a.values[15] ^ b.values[15]; + return value; +} + +static inline kinc_int8x16_t kinc_int8x16_not(kinc_int8x16_t t) { + kinc_int8x16_t value; + value.values[0] = ~t.values[0]; + value.values[1] = ~t.values[1]; + value.values[2] = ~t.values[2]; + value.values[3] = ~t.values[3]; + value.values[4] = ~t.values[4]; + value.values[5] = ~t.values[5]; + value.values[6] = ~t.values[6]; + value.values[7] = ~t.values[7]; + value.values[8] = ~t.values[8]; + value.values[9] = ~t.values[9]; + value.values[10] = ~t.values[10]; + value.values[11] = ~t.values[11]; + value.values[12] = ~t.values[12]; + value.values[13] = ~t.values[13]; + value.values[14] = ~t.values[14]; + value.values[15] = ~t.values[15]; + return value; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/simd/type_conversions.h b/armorcore/sources/kinc/simd/type_conversions.h new file mode 100644 index 000000000..61bbd5130 --- /dev/null +++ b/armorcore/sources/kinc/simd/type_conversions.h @@ -0,0 +1,800 @@ +#pragma once + +#include "types.h" +#include +#include + +/*! \file type_conversions.h + \brief Provides type casts and type conversions between all 128bit SIMD types +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(KINC_SSE2) + +// Float32x4 ----> Other +static inline kinc_int32x4_t kinc_float32x4_cast_to_int32x4(kinc_float32x4_t t) { + return _mm_castps_si128(t); +} + +static inline kinc_uint32x4_t kinc_float32x4_cast_to_uint32x4(kinc_float32x4_t t) { + return _mm_castps_si128(t); +} + +static inline kinc_int16x8_t kinc_float32x4_cast_to_int16x8(kinc_float32x4_t t) { + return _mm_castps_si128(t); +} + +static inline kinc_uint16x8_t kinc_float32x4_cast_to_uint16x8(kinc_float32x4_t t) { + return _mm_castps_si128(t); +} + +static inline kinc_int8x16_t kinc_float32x4_cast_to_int8x16(kinc_float32x4_t t) { + return _mm_castps_si128(t); +} + +static inline kinc_uint8x16_t kinc_float32x4_cast_to_uint8x16(kinc_float32x4_t t) { + return _mm_castps_si128(t); +} + +// Int32x4 ----> Other +static inline kinc_float32x4_t kinc_int32x4_cast_to_float32x4(kinc_int32x4_t t) { + return _mm_castsi128_ps(t); +} + +static inline kinc_uint32x4_t kinc_int32x4_cast_to_uint32x4(kinc_int32x4_t t) { + // SSE2's m128i is every int type, so we can just return any inbound int type parameter + return t; +} + +static inline kinc_int16x8_t kinc_int32x4_cast_to_int16x8(kinc_int32x4_t t) { + return t; +} + +static inline kinc_uint16x8_t kinc_int32x4_cast_to_uint16x8(kinc_int32x4_t t) { + return t; +} + +static inline kinc_int8x16_t kinc_int32x4_cast_to_int8x16(kinc_int32x4_t t) { + return t; +} + +static inline kinc_uint8x16_t kinc_int32x4_cast_to_uint8x16(kinc_int32x4_t t) { + return t; +} + +// Unsigned Int32x4 ----> Other +static inline kinc_float32x4_t kinc_uint32x4_cast_to_float32x4(kinc_uint32x4_t t) { + return _mm_castsi128_ps(t); +} + +static inline kinc_int32x4_t kinc_uint32x4_cast_to_int32x4(kinc_uint32x4_t t) { + return t; +} + +static inline kinc_int16x8_t kinc_uint32x4_cast_to_int16x8(kinc_uint32x4_t t) { + return t; +} + +static inline kinc_uint16x8_t kinc_uint32x4_cast_to_uint16x8(kinc_uint32x4_t t) { + return t; +} + +static inline kinc_int8x16_t kinc_uint32x4_cast_to_int8x16(kinc_uint32x4_t t) { + return t; +} + +static inline kinc_uint8x16_t kinc_uint32x4_cast_to_uint8x16(kinc_uint32x4_t t) { + return t; +} + +// Int16x8 ----> Other +static inline kinc_float32x4_t kinc_int16x8_cast_to_float32x4(kinc_int16x8_t t) { + return _mm_castsi128_ps(t); +} + +static inline kinc_int32x4_t kinc_int16x8_cast_to_int32x4(kinc_int16x8_t t) { + return t; +} + +static inline kinc_uint32x4_t kinc_int16x8_cast_to_uint32x4(kinc_int16x8_t t) { + return t; +} + +static inline kinc_uint16x8_t kinc_int16x8_cast_to_uint16x8(kinc_int16x8_t t) { + return t; +} + +static inline kinc_int8x16_t kinc_int16x8_cast_to_int8x16(kinc_int16x8_t t) { + return t; +} + +static inline kinc_uint8x16_t kinc_int16x8_cast_to_uint8x16(kinc_int16x8_t t) { + return t; +} + +// Unsigned Int16x8 ----> Other +static inline kinc_float32x4_t kinc_uint16x8_cast_to_float32x4(kinc_uint16x8_t t) { + return _mm_castsi128_ps(t); +} + +static inline kinc_int32x4_t kinc_uint16x8_cast_to_int32x4(kinc_uint16x8_t t) { + return t; +} + +static inline kinc_uint32x4_t kinc_uint16x8_cast_to_uint32x4(kinc_uint16x8_t t) { + return t; +} + +static inline kinc_int16x8_t kinc_uint16x8_cast_to_int16x8(kinc_uint16x8_t t) { + return t; +} + +static inline kinc_int8x16_t kinc_uint16x8_cast_to_int8x16(kinc_uint16x8_t t) { + return t; +} + +static inline kinc_uint8x16_t kinc_uint16x8_cast_to_uint8x16(kinc_uint16x8_t t) { + return t; +} + +// Int8x16 ----> Other +static inline kinc_float32x4_t kinc_int8x16_cast_to_float32x4(kinc_int8x16_t t) { + return _mm_castsi128_ps(t); +} + +static inline kinc_int32x4_t kinc_int8x16_cast_to_int32x4(kinc_int8x16_t t) { + return t; +} + +static inline kinc_uint32x4_t kinc_int8x16_cast_to_uint32x4(kinc_int8x16_t t) { + return t; +} + +static inline kinc_int16x8_t kinc_int8x16_cast_to_int16x8(kinc_int8x16_t t) { + return t; +} + +static inline kinc_uint16x8_t kinc_int8x16_cast_to_uint16x8(kinc_int8x16_t t) { + return t; +} + +static inline kinc_uint8x16_t kinc_int8x16_cast_to_uint8x16(kinc_int8x16_t t) { + return t; +} + +// Unsigned Int8x16 ----> Other +static inline kinc_float32x4_t kinc_uint8x16_cast_to_float32x4(kinc_uint8x16_t t) { + return _mm_castsi128_ps(t); +} + +static inline kinc_int32x4_t kinc_uint8x16_cast_to_int32x4(kinc_uint8x16_t t) { + return t; +} + +static inline kinc_uint32x4_t kinc_uint8x16_cast_to_uint32x4(kinc_uint8x16_t t) { + return t; +} + +static inline kinc_int16x8_t kinc_uint8x16_cast_to_int16x8(kinc_uint8x16_t t) { + return t; +} + +static inline kinc_uint16x8_t kinc_uint8x16_cast_to_uint16x8(kinc_uint8x16_t t) { + return t; +} + +static inline kinc_int8x16_t kinc_uint8x16_cast_to_int8x16(kinc_uint8x16_t t) { + return t; +} + +#elif defined(KINC_SSE) + +// Float32x4 ----> Other +static inline kinc_int32x4_t kinc_float32x4_cast_to_int32x4(kinc_float32x4_t t) { + float extracted[4]; + _mm_storeu_ps(&extracted[0], t); + + kinc_int32x4_t cvt; + memcpy(&cvt.values[0], &extracted[0], sizeof(extracted)); + + return cvt; +} + +static inline kinc_uint32x4_t kinc_float32x4_cast_to_uint32x4(kinc_float32x4_t t) { + float extracted[4]; + _mm_storeu_ps(&extracted[0], t); + + kinc_uint32x4_t cvt; + memcpy(&cvt.values[0], &extracted[0], sizeof(extracted)); + + return cvt; +} + +static inline kinc_int16x8_t kinc_float32x4_cast_to_int16x8(kinc_float32x4_t t) { + float extracted[4]; + _mm_storeu_ps(&extracted[0], t); + + kinc_int16x8_t cvt; + memcpy(&cvt.values[0], &extracted[0], sizeof(extracted)); + + return cvt; +} + +static inline kinc_uint16x8_t kinc_float32x4_cast_to_uint16x8(kinc_float32x4_t t) { + float extracted[4]; + _mm_storeu_ps(&extracted[0], t); + + kinc_uint16x8_t cvt; + memcpy(&cvt.values[0], &extracted[0], sizeof(extracted)); + + return cvt; +} + +static inline kinc_int8x16_t kinc_float32x4_cast_to_int8x16(kinc_float32x4_t t) { + float extracted[4]; + _mm_storeu_ps(&extracted[0], t); + + kinc_int8x16_t cvt; + memcpy(&cvt.values[0], &extracted[0], sizeof(extracted)); + + return cvt; +} + +static inline kinc_uint8x16_t kinc_float32x4_cast_to_uint8x16(kinc_float32x4_t t) { + float extracted[4]; + _mm_storeu_ps(&extracted[0], t); + + kinc_uint8x16_t cvt; + memcpy(&cvt.values[0], &extracted[0], sizeof(extracted)); + + return cvt; +} + +// Int32x4 ----> Other +static inline kinc_float32x4_t kinc_int32x4_cast_to_float32x4(kinc_int32x4_t t) { + float cvt[4]; + memcpy(&cvt[0], &t.values[0], sizeof(t)); + + return _mm_loadu_ps(&cvt[0]); +} + +// Unsigned Int32x4 ----> Other +static inline kinc_float32x4_t kinc_uint32x4_cast_to_float32x4(kinc_uint32x4_t t) { + float cvt[4]; + memcpy(&cvt[0], &t.values[0], sizeof(t)); + + return _mm_loadu_ps(&cvt[0]); +} + +// Int16x8 ----> Other +static inline kinc_float32x4_t kinc_int16x8_cast_to_float32x4(kinc_int16x8_t t) { + float cvt[4]; + memcpy(&cvt[0], &t.values[0], sizeof(t)); + + return _mm_loadu_ps(&cvt[0]); +} + +// Unsigned Int16x8 ----> Other +static inline kinc_float32x4_t kinc_uint16x8_cast_to_float32x4(kinc_uint16x8_t t) { + float cvt[4]; + memcpy(&cvt[0], &t.values[0], sizeof(t)); + + return _mm_loadu_ps(&cvt[0]); +} + +// Int8x16 ----> Other +static inline kinc_float32x4_t kinc_int8x16_cast_to_float32x4(kinc_int8x16_t t) { + float cvt[4]; + memcpy(&cvt[0], &t.values[0], sizeof(t)); + + return _mm_loadu_ps(&cvt[0]); +} + +// Unsigned Int8x16 ----> Other +static inline kinc_float32x4_t kinc_uint8x16_cast_to_float32x4(kinc_uint8x16_t t) { + float cvt[4]; + memcpy(&cvt[0], &t.values[0], sizeof(t)); + + return _mm_loadu_ps(&cvt[0]); +} + +#elif defined(KINC_NEON) + +// Float32x4 ----> Other +static inline kinc_int32x4_t kinc_float32x4_cast_to_int32x4(kinc_float32x4_t t) { + return vreinterpretq_s32_f32(t); +} + +static inline kinc_uint32x4_t kinc_float32x4_cast_to_uint32x4(kinc_float32x4_t t) { + return vreinterpretq_u32_f32(t); +} + +static inline kinc_int16x8_t kinc_float32x4_cast_to_int16x8(kinc_float32x4_t t) { + return vreinterpretq_s16_f32(t); +} + +static inline kinc_uint16x8_t kinc_float32x4_cast_to_uint16x8(kinc_float32x4_t t) { + return vreinterpretq_u16_f32(t); +} + +static inline kinc_int8x16_t kinc_float32x4_cast_to_int8x16(kinc_float32x4_t t) { + return vreinterpretq_s8_f32(t); +} + +static inline kinc_uint8x16_t kinc_float32x4_cast_to_uint8x16(kinc_float32x4_t t) { + return vreinterpretq_u8_f32(t); +} + +// Int32x4 ----> Other +static inline kinc_float32x4_t kinc_int32x4_cast_to_float32x4(kinc_int32x4_t t) { + return vreinterpretq_f32_s32(t); +} + +static inline kinc_uint32x4_t kinc_int32x4_cast_to_uint32x4(kinc_int32x4_t t) { + return vreinterpretq_u32_s32(t); +} + +static inline kinc_int16x8_t kinc_int32x4_cast_to_int16x8(kinc_int32x4_t t) { + return vreinterpretq_s16_s32(t); +} + +static inline kinc_uint16x8_t kinc_int32x4_cast_to_uint16x8(kinc_int32x4_t t) { + return vreinterpretq_u16_s32(t); +} + +static inline kinc_int8x16_t kinc_int32x4_cast_to_int8x16(kinc_int32x4_t t) { + return vreinterpretq_s8_s32(t); +} + +static inline kinc_uint8x16_t kinc_int32x4_cast_to_uint8x16(kinc_int32x4_t t) { + return vreinterpretq_u8_s32(t); +} + +// Unsigned Int32x4 ----> Other +static inline kinc_float32x4_t kinc_uint32x4_cast_to_float32x4(kinc_uint32x4_t t) { + return vreinterpretq_f32_u32(t); +} + +static inline kinc_int32x4_t kinc_uint32x4_cast_to_int32x4(kinc_uint32x4_t t) { + return vreinterpretq_s32_u32(t); +} + +static inline kinc_int16x8_t kinc_uint32x4_cast_to_int16x8(kinc_uint32x4_t t) { + return vreinterpretq_s16_u32(t); +} + +static inline kinc_uint16x8_t kinc_uint32x4_cast_to_uint16x8(kinc_uint32x4_t t) { + return vreinterpretq_u16_u32(t); +} + +static inline kinc_int8x16_t kinc_uint32x4_cast_to_int8x16(kinc_uint32x4_t t) { + return vreinterpretq_s8_u32(t); +} + +static inline kinc_uint8x16_t kinc_uint32x4_cast_to_uint8x16(kinc_uint32x4_t t) { + return vreinterpretq_u8_u32(t); +} + +// Int16x8 ----> Other +static inline kinc_float32x4_t kinc_int16x8_cast_to_float32x4(kinc_int16x8_t t) { + return vreinterpretq_f32_s16(t); +} + +static inline kinc_int32x4_t kinc_int16x8_cast_to_int32x4(kinc_int16x8_t t) { + return vreinterpretq_s32_s16(t); +} + +static inline kinc_uint32x4_t kinc_int16x8_cast_to_uint32x4(kinc_int16x8_t t) { + return vreinterpretq_u32_s16(t); +} + +static inline kinc_uint16x8_t kinc_int16x8_cast_to_uint16x8(kinc_int16x8_t t) { + return vreinterpretq_u16_s16(t); +} + +static inline kinc_int8x16_t kinc_int16x8_cast_to_int8x16(kinc_int16x8_t t) { + return vreinterpretq_s8_s16(t); +} + +static inline kinc_uint8x16_t kinc_int16x8_cast_to_uint8x16(kinc_int16x8_t t) { + return vreinterpretq_u8_s16(t); +} + +// Unsigned Int16x8 ----> Other +static inline kinc_float32x4_t kinc_uint16x8_cast_to_float32x4(kinc_uint16x8_t t) { + return vreinterpretq_f32_u16(t); +} + +static inline kinc_int32x4_t kinc_uint16x8_cast_to_int32x4(kinc_uint16x8_t t) { + return vreinterpretq_s32_u16(t); +} + +static inline kinc_uint32x4_t kinc_uint16x8_cast_to_uint32x4(kinc_uint16x8_t t) { + return vreinterpretq_u32_u16(t); +} + +static inline kinc_int16x8_t kinc_uint16x8_cast_to_int16x8(kinc_uint16x8_t t) { + return vreinterpretq_s16_u16(t); +} + +static inline kinc_int8x16_t kinc_uint16x8_cast_to_int8x16(kinc_uint16x8_t t) { + return vreinterpretq_s8_u16(t); +} + +static inline kinc_uint8x16_t kinc_uint16x8_cast_to_uint8x16(kinc_uint16x8_t t) { + return vreinterpretq_u8_u16(t); +} + +// Int8x16 ----> Other +static inline kinc_float32x4_t kinc_int8x16_cast_to_float32x4(kinc_int8x16_t t) { + return vreinterpretq_f32_s8(t); +} + +static inline kinc_int32x4_t kinc_int8x16_cast_to_int32x4(kinc_int8x16_t t) { + return vreinterpretq_s32_s8(t); +} + +static inline kinc_uint32x4_t kinc_int8x16_cast_to_uint32x4(kinc_int8x16_t t) { + return vreinterpretq_u32_s8(t); +} + +static inline kinc_int16x8_t kinc_int8x16_cast_to_int16x8(kinc_int8x16_t t) { + return vreinterpretq_s16_s8(t); +} + +static inline kinc_uint16x8_t kinc_int8x16_cast_to_uint16x8(kinc_int8x16_t t) { + return vreinterpretq_u16_s8(t); +} + +static inline kinc_uint8x16_t kinc_int8x16_cast_to_uint8x16(kinc_int8x16_t t) { + return vreinterpretq_u8_s8(t); +} + +// Unsigned Int8x16 ----> Other +static inline kinc_float32x4_t kinc_uint8x16_cast_to_float32x4(kinc_uint8x16_t t) { + return vreinterpretq_f32_u8(t); +} + +static inline kinc_int32x4_t kinc_uint8x16_cast_to_int32x4(kinc_uint8x16_t t) { + return vreinterpretq_s32_u8(t); +} + +static inline kinc_uint32x4_t kinc_uint8x16_cast_to_uint32x4(kinc_uint8x16_t t) { + return vreinterpretq_u32_u8(t); +} + +static inline kinc_int16x8_t kinc_uint8x16_cast_to_int16x8(kinc_uint8x16_t t) { + return vreinterpretq_s16_u8(t); +} + +static inline kinc_uint16x8_t kinc_uint8x16_cast_to_uint16x8(kinc_uint8x16_t t) { + return vreinterpretq_u16_u8(t); +} + +static inline kinc_int8x16_t kinc_uint8x16_cast_to_int8x16(kinc_uint8x16_t t) { + return vreinterpretq_s8_u8(t); +} + +// KINC_NOSIMD float fallbacks casts +#else + +// Float32x4 ----> Other +static inline kinc_int32x4_t kinc_float32x4_cast_to_int32x4(kinc_float32x4_t t) { + kinc_int32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint32x4_t kinc_float32x4_cast_to_uint32x4(kinc_float32x4_t t) { + kinc_uint32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_int16x8_t kinc_float32x4_cast_to_int16x8(kinc_float32x4_t t) { + kinc_int16x8_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint16x8_t kinc_float32x4_cast_to_uint16x8(kinc_float32x4_t t) { + kinc_uint16x8_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_int8x16_t kinc_float32x4_cast_to_int8x16(kinc_float32x4_t t) { + kinc_int8x16_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint8x16_t kinc_float32x4_cast_to_uint8x16(kinc_float32x4_t t) { + kinc_uint8x16_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +// Int32x4 ----> Float32x4 +static inline kinc_float32x4_t kinc_int32x4_cast_to_float32x4(kinc_int32x4_t t) { + kinc_float32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +// Unsigned Int32x4 ----> Float32x4 +static inline kinc_float32x4_t kinc_uint32x4_cast_to_float32x4(kinc_uint32x4_t t) { + kinc_float32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +// Int16x8 ----> Float32x4 +static inline kinc_float32x4_t kinc_int16x8_cast_to_float32x4(kinc_int16x8_t t) { + kinc_float32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +// Unsigned Int16x8 ----> Float32x4 +static inline kinc_float32x4_t kinc_uint16x8_cast_to_float32x4(kinc_uint16x8_t t) { + kinc_float32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +// Int8x16 ----> Float32x4 +static inline kinc_float32x4_t kinc_int8x16_cast_to_float32x4(kinc_int8x16_t t) { + kinc_float32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +// Unsigned Int8x16 ----> Float32x4 +static inline kinc_float32x4_t kinc_uint8x16_cast_to_float32x4(kinc_uint8x16_t t) { + kinc_float32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +#endif // KINC_NOSIMD floats + +// Shared signed and unsigned integer vectors for SSE and SIMD-fallback +#if !defined(KINC_SSE2) && (defined(KINC_SSE) || defined(KINC_NOSIMD)) + +// Int32x4 ----> Other +static inline kinc_uint32x4_t kinc_int32x4_cast_to_uint32x4(kinc_int32x4_t t) { + kinc_uint32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_int16x8_t kinc_int32x4_cast_to_int16x8(kinc_int32x4_t t) { + kinc_int16x8_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint16x8_t kinc_int32x4_cast_to_uint16x8(kinc_int32x4_t t) { + kinc_uint16x8_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_int8x16_t kinc_int32x4_cast_to_int8x16(kinc_int32x4_t t) { + kinc_int8x16_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint8x16_t kinc_int32x4_cast_to_uint8x16(kinc_int32x4_t t) { + kinc_uint8x16_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +// Unsigned Int32x4 ----> Other +static inline kinc_int32x4_t kinc_uint32x4_cast_to_int32x4(kinc_uint32x4_t t) { + kinc_int32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_int16x8_t kinc_uint32x4_cast_to_int16x8(kinc_uint32x4_t t) { + kinc_int16x8_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint16x8_t kinc_uint32x4_cast_to_uint16x8(kinc_uint32x4_t t) { + kinc_uint16x8_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_int8x16_t kinc_uint32x4_cast_to_int8x16(kinc_uint32x4_t t) { + kinc_int8x16_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint8x16_t kinc_uint32x4_cast_to_uint8x16(kinc_uint32x4_t t) { + kinc_uint8x16_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +// Int16x8 ----> Other +static inline kinc_int32x4_t kinc_int16x8_cast_to_int32x4(kinc_int16x8_t t) { + kinc_int32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint32x4_t kinc_int16x8_cast_to_uint32x4(kinc_int16x8_t t) { + kinc_uint32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint16x8_t kinc_int16x8_cast_to_uint16x8(kinc_int16x8_t t) { + kinc_uint16x8_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_int8x16_t kinc_int16x8_cast_to_int8x16(kinc_int16x8_t t) { + kinc_int8x16_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint8x16_t kinc_int16x8_cast_to_uint8x16(kinc_int16x8_t t) { + kinc_uint8x16_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +// Unsigned Int16x8 ----> Other +static inline kinc_int32x4_t kinc_uint16x8_cast_to_int32x4(kinc_uint16x8_t t) { + kinc_int32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint32x4_t kinc_uint16x8_cast_to_uint32x4(kinc_uint16x8_t t) { + kinc_uint32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_int16x8_t kinc_uint16x8_cast_to_int16x8(kinc_uint16x8_t t) { + kinc_int16x8_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_int8x16_t kinc_uint16x8_cast_to_int8x16(kinc_uint16x8_t t) { + kinc_int8x16_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint8x16_t kinc_uint16x8_cast_to_uint8x16(kinc_uint16x8_t t) { + kinc_uint8x16_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +// Int8x16 ----> Other +static inline kinc_int32x4_t kinc_int8x16_cast_to_int32x4(kinc_int8x16_t t) { + kinc_int32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint32x4_t kinc_int8x16_cast_to_uint32x4(kinc_int8x16_t t) { + kinc_uint32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_int16x8_t kinc_int8x16_cast_to_int16x8(kinc_int8x16_t t) { + kinc_int16x8_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint16x8_t kinc_int8x16_cast_to_uint16x8(kinc_int8x16_t t) { + kinc_uint16x8_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint8x16_t kinc_int8x16_cast_to_uint8x16(kinc_int8x16_t t) { + kinc_uint8x16_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +// Unsigned Int8x16 ----> Other +static inline kinc_int32x4_t kinc_uint8x16_cast_to_int32x4(kinc_uint8x16_t t) { + kinc_int32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint32x4_t kinc_uint8x16_cast_to_uint32x4(kinc_uint8x16_t t) { + kinc_uint32x4_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_int16x8_t kinc_uint8x16_cast_to_int16x8(kinc_uint8x16_t t) { + kinc_int16x8_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_uint16x8_t kinc_uint8x16_cast_to_uint16x8(kinc_uint8x16_t t) { + kinc_uint16x8_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +static inline kinc_int8x16_t kinc_uint8x16_cast_to_int8x16(kinc_uint8x16_t t) { + kinc_int8x16_t cvt; + memcpy(&cvt.values[0], &t.values[0], sizeof(t)); + + return cvt; +} + +#endif // KINC_SSE || KINC_NOSIMD + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/simd/types.h b/armorcore/sources/kinc/simd/types.h new file mode 100644 index 000000000..9cb8e45c4 --- /dev/null +++ b/armorcore/sources/kinc/simd/types.h @@ -0,0 +1,180 @@ +#pragma once + +#include + +/*! \file types.h + \brief Provides 128bit SIMD types which are mapped to equivalent SSE or Neon types. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +// Any level of AVX Capability (Could be AVX, AVX2, AVX512, etc.) +//(Currently) only used for checking existence of earlier SSE instruction sets +#if defined(__AVX__) +// Unfortunate situation here +// MSVC does not provide compiletime macros for the following instruction sets +// but their existence is implied by AVX and higher +#define KINC_SSE4_2 +#define KINC_SSE4_1 +#define KINC_SSSE3 +#define KINC_SSE3 +#endif + +// SSE2 Capability check +// Note for Windows: +// _M_IX86_FP checks SSE2 and SSE for 32bit Windows programs only, and is unset if not a 32bit program. +// SSE2 and earlier is --guaranteed-- to be active for any 64bit Windows program +#if defined(__SSE2__) || (_M_IX86_FP == 2) || (defined(KINC_WINDOWS) && defined(KINC_64)) +#define KINC_SSE2 +#endif + +// SSE Capability check +#if defined(__SSE__) || _M_IX86_FP == 2 || _M_IX86_FP == 1 || (defined(KINC_WINDOWS) && !defined(__aarch64__)) || \ + (defined(KINC_WINDOWSAPP) && !defined(__aarch64__)) || (defined(KINC_MACOS) && __x86_64) + +#define KINC_SSE +#endif + +// NEON Capability check +#if defined(KINC_IOS) || defined(KINC_SWITCH) || defined(__aarch64__) || defined(KINC_NEON) +#define KINC_NEON +#endif + +// No SIMD Capabilities +#if !defined(KINC_SSE4_2) && !defined(KINC_SSE4_1) && !defined(KINC_SSSE3) && !defined(KINC_SSE3) && !defined(KINC_SSE2) && !defined(KINC_SSE) && \ + !defined(KINC_NEON) + +#define KINC_NOSIMD +#endif + +#define KINC_SHUFFLE_TABLE(LANE_A1, LANE_A2, LANE_B1, LANE_B2) \ + ((((LANE_B2)&0x3) << 6) | (((LANE_B1)&0x3) << 4) | (((LANE_A2)&0x3) << 2) | (((LANE_A1)&0x3) << 0)) + +#if defined(KINC_SSE2) + +// SSE_## related headers include earlier revisions, IE +// SSE2 contains all of SSE +#include + +typedef __m128 kinc_float32x4_t; +typedef __m128 kinc_float32x4_mask_t; + +typedef __m128i kinc_int8x16_t; +typedef __m128i kinc_int8x16_mask_t; +typedef __m128i kinc_uint8x16_t; +typedef __m128i kinc_uint8x16_mask_t; +typedef __m128i kinc_int16x8_t; +typedef __m128i kinc_int16x8_mask_t; +typedef __m128i kinc_uint16x8_t; +typedef __m128i kinc_uint16x8_mask_t; +typedef __m128i kinc_int32x4_t; +typedef __m128i kinc_int32x4_mask_t; +typedef __m128i kinc_uint32x4_t; +typedef __m128i kinc_uint32x4_mask_t; + +#elif defined(KINC_SSE) + +#include + +typedef __m128 kinc_float32x4_t; +typedef __m128 kinc_float32x4_mask_t; + +typedef struct kinc_int8x16 { + int8_t values[16]; +} kinc_int8x16_t; + +typedef struct kinc_uint8x16 { + uint8_t values[16]; +} kinc_uint8x16_t; + +typedef struct kinc_int16x8 { + int16_t values[8]; +} kinc_int16x8_t; + +typedef struct kinc_uint16x8 { + uint16_t values[8]; +} kinc_uint16x8_t; + +typedef struct kinc_int32x4 { + int32_t values[4]; +} kinc_int32x4_t; + +typedef struct kinc_uint32x4 { + uint32_t values[4]; +} kinc_uint32x4_t; + +typedef kinc_int8x16_t kinc_int8x16_mask_t; +typedef kinc_uint8x16_t kinc_uint8x16_mask_t; +typedef kinc_int16x8_t kinc_int16x8_mask_t; +typedef kinc_uint16x8_t kinc_uint16x8_mask_t; +typedef kinc_int32x4_t kinc_int32x4_mask_t; +typedef kinc_uint32x4_t kinc_uint32x4_mask_t; + +#elif defined(KINC_NEON) + +#include + +typedef float32x4_t kinc_float32x4_t; +typedef uint32x4_t kinc_float32x4_mask_t; + +typedef int8x16_t kinc_int8x16_t; +typedef uint8x16_t kinc_int8x16_mask_t; +typedef uint8x16_t kinc_uint8x16_t; +typedef uint8x16_t kinc_uint8x16_mask_t; +typedef int16x8_t kinc_int16x8_t; +typedef uint16x8_t kinc_int16x8_mask_t; +typedef uint16x8_t kinc_uint16x8_t; +typedef uint16x8_t kinc_uint16x8_mask_t; +typedef int32x4_t kinc_int32x4_t; +typedef uint32x4_t kinc_int32x4_mask_t; +typedef uint32x4_t kinc_uint32x4_t; +typedef uint32x4_t kinc_uint32x4_mask_t; + +#elif defined(KINC_NOSIMD) + +#include + +typedef struct kinc_float32x4 { + float values[4]; +} kinc_float32x4_t; + +typedef kinc_float32x4_t kinc_float32x4_mask_t; + +typedef struct kinc_int8x16 { + int8_t values[16]; +} kinc_int8x16_t; + +typedef struct kinc_uint8x16 { + uint8_t values[16]; +} kinc_uint8x16_t; + +typedef struct kinc_int16x8 { + int16_t values[8]; +} kinc_int16x8_t; + +typedef struct kinc_uint16x8 { + uint16_t values[8]; +} kinc_uint16x8_t; + +typedef struct kinc_int32x4 { + int32_t values[4]; +} kinc_int32x4_t; + +typedef struct kinc_uint32x4 { + uint32_t values[4]; +} kinc_uint32x4_t; + +typedef kinc_int8x16_t kinc_int8x16_mask_t; +typedef kinc_uint8x16_t kinc_uint8x16_mask_t; +typedef kinc_int16x8_t kinc_int16x8_mask_t; +typedef kinc_uint16x8_t kinc_uint16x8_mask_t; +typedef kinc_int32x4_t kinc_int32x4_mask_t; +typedef kinc_uint32x4_t kinc_uint32x4_mask_t; + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/simd/uint16x8.h b/armorcore/sources/kinc/simd/uint16x8.h new file mode 100644 index 000000000..02da629cb --- /dev/null +++ b/armorcore/sources/kinc/simd/uint16x8.h @@ -0,0 +1,501 @@ +#pragma once + +#include "types.h" + +/*! \file uint16x8.h + \brief Provides 128bit eight-element unsigned 16-bit integer SIMD operations which are mapped to equivalent SSE2 or Neon operations. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(KINC_SSE2) + +static inline kinc_uint16x8_t kinc_uint16x8_intrin_load(const uint16_t *values) { + return _mm_load_si128((const kinc_uint16x8_t *)values); +} + +static inline kinc_uint16x8_t kinc_uint16x8_intrin_load_unaligned(const uint16_t *values) { + return _mm_loadu_si128((const kinc_uint16x8_t *)values); +} + +static inline kinc_uint16x8_t kinc_uint16x8_load(const uint16_t values[8]) { + return _mm_set_epi16(values[7], values[6], values[5], values[4], values[3], values[2], values[1], values[0]); +} + +static inline kinc_uint16x8_t kinc_uint16x8_load_all(uint16_t t) { + return _mm_set1_epi16(t); +} + +static inline void kinc_uint16x8_store(uint16_t *destination, kinc_uint16x8_t value) { + _mm_store_si128((kinc_uint16x8_t *)destination, value); +} + +static inline void kinc_uint16x8_store_unaligned(uint16_t *destination, kinc_uint16x8_t value) { + _mm_storeu_si128((kinc_uint16x8_t *)destination, value); +} + +static inline uint16_t kinc_uint16x8_get(kinc_uint16x8_t t, int index) { + union { + __m128i value; + uint16_t elements[8]; + } converter; + converter.value = t; + return converter.elements[index]; +} + +static inline kinc_uint16x8_t kinc_uint16x8_add(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return _mm_add_epi16(a, b); +} + +static inline kinc_uint16x8_t kinc_uint16x8_sub(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return _mm_sub_epi16(a, b); +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmpeq(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return _mm_cmpeq_epi16(a, b); +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmpneq(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return _mm_andnot_si128(_mm_cmpeq_epi16(a, b), _mm_set1_epi32(0xffffffff)); +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmpge(kinc_uint16x8_t a, kinc_uint16x8_t b) { + __m128i bias_by = _mm_set1_epi16((uint16_t)0x8000); + return _mm_or_si128(_mm_cmpgt_epi16(_mm_sub_epi16(a, bias_by), _mm_sub_epi16(b, bias_by)), _mm_cmpeq_epi16(a, b)); +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmpgt(kinc_uint16x8_t a, kinc_uint16x8_t b) { + __m128i bias_by = _mm_set1_epi16((uint16_t)0x8000); + return _mm_cmpgt_epi16(_mm_sub_epi16(a, bias_by), _mm_sub_epi16(b, bias_by)); +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmple(kinc_uint16x8_t a, kinc_uint16x8_t b) { + __m128i bias_by = _mm_set1_epi16((uint16_t)0x8000); + return _mm_or_si128(_mm_cmplt_epi16(_mm_sub_epi16(a, bias_by), _mm_sub_epi16(b, bias_by)), _mm_cmpeq_epi16(a, b)); +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmplt(kinc_uint16x8_t a, kinc_uint16x8_t b) { + __m128i bias_by = _mm_set1_epi16((uint16_t)0x8000); + return _mm_cmplt_epi16(_mm_sub_epi16(a, bias_by), _mm_sub_epi16(b, bias_by)); +} + +static inline kinc_uint16x8_t kinc_uint16x8_sel(kinc_uint16x8_t a, kinc_uint16x8_t b, kinc_uint16x8_mask_t mask) { + return _mm_xor_si128(b, _mm_and_si128(mask, _mm_xor_si128(a, b))); +} + +static inline kinc_uint16x8_t kinc_uint16x8_max(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return kinc_uint16x8_sel(a, b, kinc_uint16x8_cmpgt(a, b)); +} + +static inline kinc_uint16x8_t kinc_uint16x8_min(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return kinc_uint16x8_sel(a, b, kinc_uint16x8_cmplt(a, b)); +} + +static inline kinc_uint16x8_t kinc_uint16x8_or(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return _mm_or_si128(a, b); +} + +static inline kinc_uint16x8_t kinc_uint16x8_and(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return _mm_and_si128(a, b); +} + +static inline kinc_uint16x8_t kinc_uint16x8_xor(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return _mm_xor_si128(a, b); +} + +static inline kinc_uint16x8_t kinc_uint16x8_not(kinc_uint16x8_t t) { + return _mm_xor_si128(t, _mm_set1_epi32(0xffffffff)); +} + +#define kinc_uint16x8_shift_left(t, shift) _mm_slli_epi16((t), (shift)) + +#define kinc_uint16x8_shift_right(t, shift) _mm_srli_epi16((t), (shift)) + +#elif defined(KINC_NEON) + +static inline kinc_uint16x8_t kinc_uint16x8_intrin_load(const uint16_t *values) { + return vld1q_u16(values); +} + +static inline kinc_uint16x8_t kinc_uint16x8_intrin_load_unaligned(const uint16_t *values) { + return kinc_uint16x8_intrin_load(values); +} + +static inline kinc_uint16x8_t kinc_uint16x8_load(const uint16_t values[8]) { + return (kinc_uint16x8_t){values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7]}; +} + +static inline kinc_uint16x8_t kinc_uint16x8_load_all(uint16_t t) { + return (kinc_uint16x8_t){t, t, t, t, t, t, t, t}; +} + +static inline void kinc_uint16x8_store(uint16_t *destination, kinc_uint16x8_t value) { + vst1q_u16(destination, value); +} + +static inline void kinc_uint16x8_store_unaligned(uint16_t *destination, kinc_uint16x8_t value) { + kinc_uint16x8_store(destination, value); +} + +static inline uint16_t kinc_uint16x8_get(kinc_uint16x8_t t, int index) { + return t[index]; +} + +static inline kinc_uint16x8_t kinc_uint16x8_add(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return vaddq_u16(a, b); +} + +static inline kinc_uint16x8_t kinc_uint16x8_sub(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return vsubq_u16(a, b); +} + +static inline kinc_uint16x8_t kinc_uint16x8_max(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return vmaxq_u16(a, b); +} + +static inline kinc_uint16x8_t kinc_uint16x8_min(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return vminq_u16(a, b); +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmpeq(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return vceqq_u16(a, b); +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmpneq(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return vmvnq_u16(vceqq_u16(a, b)); +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmpge(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return vcgeq_u16(a, b); +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmpgt(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return vcgtq_u16(a, b); +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmple(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return vcleq_u16(a, b); +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmplt(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return vcltq_u16(a, b); +} + +static inline kinc_uint16x8_t kinc_uint16x8_sel(kinc_uint16x8_t a, kinc_uint16x8_t b, kinc_uint16x8_mask_t mask) { + return vbslq_u16(mask, a, b); +} + +static inline kinc_uint16x8_t kinc_uint16x8_or(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return vorrq_u16(a, b); +} + +static inline kinc_uint16x8_t kinc_uint16x8_and(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return vandq_u16(a, b); +} + +static inline kinc_uint16x8_t kinc_uint16x8_xor(kinc_uint16x8_t a, kinc_uint16x8_t b) { + return veorq_u16(a, b); +} + +static inline kinc_uint16x8_t kinc_uint16x8_not(kinc_uint16x8_t t) { + return vmvnq_u16(t); +} + +#define kinc_uint16x8_shift_left(t, shift) vshlq_n_u16((t), (shift)) + +#define kinc_uint16x8_shift_right(t, shift) vshrq_n_u16((t), (shift)) + +#else + +static inline kinc_uint16x8_t kinc_uint16x8_intrin_load(const uint16_t *values) { + kinc_uint16x8_t value; + value.values[0] = values[0]; + value.values[1] = values[1]; + value.values[2] = values[2]; + value.values[3] = values[3]; + value.values[4] = values[4]; + value.values[5] = values[5]; + value.values[6] = values[6]; + value.values[7] = values[7]; + return value; +} + +static inline kinc_uint16x8_t kinc_uint16x8_intrin_load_unaligned(const uint16_t *values) { + return kinc_uint16x8_intrin_load(values); +} + +static inline kinc_uint16x8_t kinc_uint16x8_load(const uint16_t values[8]) { + kinc_uint16x8_t value; + value.values[0] = values[0]; + value.values[1] = values[1]; + value.values[2] = values[2]; + value.values[3] = values[3]; + value.values[4] = values[4]; + value.values[5] = values[5]; + value.values[6] = values[6]; + value.values[7] = values[7]; + return value; +} + +static inline kinc_uint16x8_t kinc_uint16x8_load_all(uint16_t t) { + kinc_uint16x8_t value; + value.values[0] = t; + value.values[1] = t; + value.values[2] = t; + value.values[3] = t; + value.values[4] = t; + value.values[5] = t; + value.values[6] = t; + value.values[7] = t; + return value; +} + +static inline void kinc_uint16x8_store(uint16_t *destination, kinc_uint16x8_t value) { + destination[0] = value.values[0]; + destination[1] = value.values[1]; + destination[2] = value.values[2]; + destination[3] = value.values[3]; + destination[4] = value.values[4]; + destination[5] = value.values[5]; + destination[6] = value.values[6]; + destination[7] = value.values[7]; +} + +static inline void kinc_uint16x8_store_unaligned(uint16_t *destination, kinc_uint16x8_t value) { + kinc_uint16x8_store(destination, value); +} + +static inline uint16_t kinc_uint16x8_get(kinc_uint16x8_t t, int index) { + return t.values[index]; +} + +static inline kinc_uint16x8_t kinc_uint16x8_add(kinc_uint16x8_t a, kinc_uint16x8_t b) { + kinc_uint16x8_t value; + value.values[0] = a.values[0] + b.values[0]; + value.values[1] = a.values[1] + b.values[1]; + value.values[2] = a.values[2] + b.values[2]; + value.values[3] = a.values[3] + b.values[3]; + value.values[4] = a.values[4] + b.values[4]; + value.values[5] = a.values[5] + b.values[5]; + value.values[6] = a.values[6] + b.values[6]; + value.values[7] = a.values[7] + b.values[7]; + return value; +} + +static inline kinc_uint16x8_t kinc_uint16x8_sub(kinc_uint16x8_t a, kinc_uint16x8_t b) { + kinc_uint16x8_t value; + value.values[0] = a.values[0] - b.values[0]; + value.values[1] = a.values[1] - b.values[1]; + value.values[2] = a.values[2] - b.values[2]; + value.values[3] = a.values[3] - b.values[3]; + value.values[4] = a.values[4] - b.values[4]; + value.values[5] = a.values[5] - b.values[5]; + value.values[6] = a.values[6] - b.values[6]; + value.values[7] = a.values[7] - b.values[7]; + return value; +} + +static inline kinc_uint16x8_t kinc_uint16x8_max(kinc_uint16x8_t a, kinc_uint16x8_t b) { + kinc_uint16x8_t value; + value.values[0] = a.values[0] > b.values[0] ? a.values[0] : b.values[0]; + value.values[1] = a.values[1] > b.values[1] ? a.values[1] : b.values[1]; + value.values[2] = a.values[2] > b.values[2] ? a.values[2] : b.values[2]; + value.values[3] = a.values[3] > b.values[3] ? a.values[3] : b.values[3]; + value.values[4] = a.values[4] > b.values[4] ? a.values[4] : b.values[4]; + value.values[5] = a.values[5] > b.values[5] ? a.values[5] : b.values[5]; + value.values[6] = a.values[6] > b.values[6] ? a.values[6] : b.values[6]; + value.values[7] = a.values[7] > b.values[7] ? a.values[7] : b.values[7]; + return value; +} + +static inline kinc_uint16x8_t kinc_uint16x8_min(kinc_uint16x8_t a, kinc_uint16x8_t b) { + kinc_uint16x8_t value; + value.values[0] = a.values[0] > b.values[0] ? b.values[0] : a.values[0]; + value.values[1] = a.values[1] > b.values[1] ? b.values[1] : a.values[1]; + value.values[2] = a.values[2] > b.values[2] ? b.values[2] : a.values[2]; + value.values[3] = a.values[3] > b.values[3] ? b.values[3] : a.values[3]; + value.values[4] = a.values[4] > b.values[4] ? b.values[4] : a.values[4]; + value.values[5] = a.values[5] > b.values[5] ? b.values[5] : a.values[5]; + value.values[6] = a.values[6] > b.values[6] ? b.values[6] : a.values[6]; + value.values[7] = a.values[7] > b.values[7] ? b.values[7] : a.values[7]; + return value; +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmpeq(kinc_uint16x8_t a, kinc_uint16x8_t b) { + kinc_uint16x8_mask_t mask; + mask.values[0] = a.values[0] == b.values[0] ? 0xffff : 0; + mask.values[1] = a.values[1] == b.values[1] ? 0xffff : 0; + mask.values[2] = a.values[2] == b.values[2] ? 0xffff : 0; + mask.values[3] = a.values[3] == b.values[3] ? 0xffff : 0; + mask.values[4] = a.values[4] == b.values[4] ? 0xffff : 0; + mask.values[5] = a.values[5] == b.values[5] ? 0xffff : 0; + mask.values[6] = a.values[6] == b.values[6] ? 0xffff : 0; + mask.values[7] = a.values[7] == b.values[7] ? 0xffff : 0; + return mask; +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmpneq(kinc_uint16x8_t a, kinc_uint16x8_t b) { + kinc_uint16x8_mask_t mask; + mask.values[0] = a.values[0] != b.values[0] ? 0xffff : 0; + mask.values[1] = a.values[1] != b.values[1] ? 0xffff : 0; + mask.values[2] = a.values[2] != b.values[2] ? 0xffff : 0; + mask.values[3] = a.values[3] != b.values[3] ? 0xffff : 0; + mask.values[4] = a.values[4] != b.values[4] ? 0xffff : 0; + mask.values[5] = a.values[5] != b.values[5] ? 0xffff : 0; + mask.values[6] = a.values[6] != b.values[6] ? 0xffff : 0; + mask.values[7] = a.values[7] != b.values[7] ? 0xffff : 0; + return mask; +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmpge(kinc_uint16x8_t a, kinc_uint16x8_t b) { + kinc_uint16x8_mask_t mask; + mask.values[0] = a.values[0] >= b.values[0] ? 0xffff : 0; + mask.values[1] = a.values[1] >= b.values[1] ? 0xffff : 0; + mask.values[2] = a.values[2] >= b.values[2] ? 0xffff : 0; + mask.values[3] = a.values[3] >= b.values[3] ? 0xffff : 0; + mask.values[4] = a.values[4] >= b.values[4] ? 0xffff : 0; + mask.values[5] = a.values[5] >= b.values[5] ? 0xffff : 0; + mask.values[6] = a.values[6] >= b.values[6] ? 0xffff : 0; + mask.values[7] = a.values[7] >= b.values[7] ? 0xffff : 0; + return mask; +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmpgt(kinc_uint16x8_t a, kinc_uint16x8_t b) { + kinc_uint16x8_mask_t mask; + mask.values[0] = a.values[0] > b.values[0] ? 0xffff : 0; + mask.values[1] = a.values[1] > b.values[1] ? 0xffff : 0; + mask.values[2] = a.values[2] > b.values[2] ? 0xffff : 0; + mask.values[3] = a.values[3] > b.values[3] ? 0xffff : 0; + mask.values[4] = a.values[4] > b.values[4] ? 0xffff : 0; + mask.values[5] = a.values[5] > b.values[5] ? 0xffff : 0; + mask.values[6] = a.values[6] > b.values[6] ? 0xffff : 0; + mask.values[7] = a.values[7] > b.values[7] ? 0xffff : 0; + return mask; +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmple(kinc_uint16x8_t a, kinc_uint16x8_t b) { + kinc_uint16x8_mask_t mask; + mask.values[0] = a.values[0] <= b.values[0] ? 0xffff : 0; + mask.values[1] = a.values[1] <= b.values[1] ? 0xffff : 0; + mask.values[2] = a.values[2] <= b.values[2] ? 0xffff : 0; + mask.values[3] = a.values[3] <= b.values[3] ? 0xffff : 0; + mask.values[4] = a.values[4] <= b.values[4] ? 0xffff : 0; + mask.values[5] = a.values[5] <= b.values[5] ? 0xffff : 0; + mask.values[6] = a.values[6] <= b.values[6] ? 0xffff : 0; + mask.values[7] = a.values[7] <= b.values[7] ? 0xffff : 0; + return mask; +} + +static inline kinc_uint16x8_mask_t kinc_uint16x8_cmplt(kinc_uint16x8_t a, kinc_uint16x8_t b) { + kinc_uint16x8_mask_t mask; + mask.values[0] = a.values[0] < b.values[0] ? 0xffff : 0; + mask.values[1] = a.values[1] < b.values[1] ? 0xffff : 0; + mask.values[2] = a.values[2] < b.values[2] ? 0xffff : 0; + mask.values[3] = a.values[3] < b.values[3] ? 0xffff : 0; + mask.values[4] = a.values[4] < b.values[4] ? 0xffff : 0; + mask.values[5] = a.values[5] < b.values[5] ? 0xffff : 0; + mask.values[6] = a.values[6] < b.values[6] ? 0xffff : 0; + mask.values[7] = a.values[7] < b.values[7] ? 0xffff : 0; + return mask; +} + +static inline kinc_uint16x8_t kinc_uint16x8_sel(kinc_uint16x8_t a, kinc_uint16x8_t b, kinc_uint16x8_mask_t mask) { + kinc_uint16x8_t value; + value.values[0] = mask.values[0] != 0 ? a.values[0] : b.values[0]; + value.values[1] = mask.values[1] != 0 ? a.values[1] : b.values[1]; + value.values[2] = mask.values[2] != 0 ? a.values[2] : b.values[2]; + value.values[3] = mask.values[3] != 0 ? a.values[3] : b.values[3]; + value.values[4] = mask.values[4] != 0 ? a.values[4] : b.values[4]; + value.values[5] = mask.values[5] != 0 ? a.values[5] : b.values[5]; + value.values[6] = mask.values[6] != 0 ? a.values[6] : b.values[6]; + value.values[7] = mask.values[7] != 0 ? a.values[7] : b.values[7]; + return value; +} + +static inline kinc_uint16x8_t kinc_uint16x8_or(kinc_uint16x8_t a, kinc_uint16x8_t b) { + kinc_uint16x8_t value; + value.values[0] = a.values[0] | b.values[0]; + value.values[1] = a.values[1] | b.values[1]; + value.values[2] = a.values[2] | b.values[2]; + value.values[3] = a.values[3] | b.values[3]; + value.values[4] = a.values[4] | b.values[4]; + value.values[5] = a.values[5] | b.values[5]; + value.values[6] = a.values[6] | b.values[6]; + value.values[7] = a.values[7] | b.values[7]; + return value; +} + +static inline kinc_uint16x8_t kinc_uint16x8_and(kinc_uint16x8_t a, kinc_uint16x8_t b) { + kinc_uint16x8_t value; + value.values[0] = a.values[0] & b.values[0]; + value.values[1] = a.values[1] & b.values[1]; + value.values[2] = a.values[2] & b.values[2]; + value.values[3] = a.values[3] & b.values[3]; + value.values[4] = a.values[4] & b.values[4]; + value.values[5] = a.values[5] & b.values[5]; + value.values[6] = a.values[6] & b.values[6]; + value.values[7] = a.values[7] & b.values[7]; + return value; +} + +static inline kinc_uint16x8_t kinc_uint16x8_xor(kinc_uint16x8_t a, kinc_uint16x8_t b) { + kinc_uint16x8_t value; + value.values[0] = a.values[0] ^ b.values[0]; + value.values[1] = a.values[1] ^ b.values[1]; + value.values[2] = a.values[2] ^ b.values[2]; + value.values[3] = a.values[3] ^ b.values[3]; + value.values[4] = a.values[4] ^ b.values[4]; + value.values[5] = a.values[5] ^ b.values[5]; + value.values[6] = a.values[6] ^ b.values[6]; + value.values[7] = a.values[7] ^ b.values[7]; + return value; +} + +static inline kinc_uint16x8_t kinc_uint16x8_not(kinc_uint16x8_t t) { + kinc_uint16x8_t value; + value.values[0] = ~t.values[0]; + value.values[1] = ~t.values[1]; + value.values[2] = ~t.values[2]; + value.values[3] = ~t.values[3]; + value.values[4] = ~t.values[4]; + value.values[5] = ~t.values[5]; + value.values[6] = ~t.values[6]; + value.values[7] = ~t.values[7]; + return value; +} + +static inline kinc_uint16x8_t kinc_uint16x8_shift_left(kinc_uint16x8_t t, const int shift) { + kinc_uint16x8_t value; + value.values[0] = t.values[0] << shift; + value.values[1] = t.values[1] << shift; + value.values[2] = t.values[2] << shift; + value.values[3] = t.values[3] << shift; + value.values[4] = t.values[4] << shift; + value.values[5] = t.values[5] << shift; + value.values[6] = t.values[6] << shift; + value.values[7] = t.values[7] << shift; + + return value; +} + +static inline kinc_uint16x8_t kinc_uint16x8_shift_right(kinc_uint16x8_t t, const int shift) { + kinc_uint16x8_t value; + value.values[0] = t.values[0] >> shift; + value.values[1] = t.values[1] >> shift; + value.values[2] = t.values[2] >> shift; + value.values[3] = t.values[3] >> shift; + value.values[4] = t.values[4] >> shift; + value.values[5] = t.values[5] >> shift; + value.values[6] = t.values[6] >> shift; + value.values[7] = t.values[7] >> shift; + + return value; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/simd/uint32x4.h b/armorcore/sources/kinc/simd/uint32x4.h new file mode 100644 index 000000000..82b37a8ed --- /dev/null +++ b/armorcore/sources/kinc/simd/uint32x4.h @@ -0,0 +1,417 @@ +#pragma once + +#include "types.h" + +/*! \file uint32x4.h + \brief Provides 128bit four-element unsigned 32-bit integer SIMD operations which are mapped to equivalent SSE2 or Neon operations. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(KINC_SSE2) + +static inline kinc_uint32x4_t kinc_uint32x4_intrin_load(const uint32_t *values) { + return _mm_load_si128((const kinc_uint32x4_t *)values); +} + +static inline kinc_uint32x4_t kinc_uint32x4_intrin_load_unaligned(const uint32_t *values) { + return _mm_loadu_si128((const kinc_uint32x4_t *)values); +} + +static inline kinc_uint32x4_t kinc_uint32x4_load(const uint32_t values[4]) { + return _mm_set_epi32(values[3], values[2], values[1], values[0]); +} + +static inline kinc_uint32x4_t kinc_uint32x4_load_all(uint32_t t) { + return _mm_set1_epi32(t); +} + +static inline void kinc_uint32x4_store(uint32_t *destination, kinc_uint32x4_t value) { + _mm_store_si128((kinc_uint32x4_t *)destination, value); +} + +static inline void kinc_uint32x4_store_unaligned(uint32_t *destination, kinc_uint32x4_t value) { + _mm_storeu_si128((kinc_uint32x4_t *)destination, value); +} + +static inline uint32_t kinc_uint32x4_get(kinc_uint32x4_t t, int index) { + union { + __m128i value; + uint32_t elements[4]; + } converter; + converter.value = t; + return converter.elements[index]; +} + +static inline kinc_uint32x4_t kinc_uint32x4_add(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return _mm_add_epi32(a, b); +} + +static inline kinc_uint32x4_t kinc_uint32x4_sub(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return _mm_sub_epi32(a, b); +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmpeq(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return _mm_cmpeq_epi32(a, b); +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmpneq(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return _mm_andnot_si128(_mm_cmpeq_epi32(a, b), _mm_set1_epi32(0xffffffff)); +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmpge(kinc_uint32x4_t a, kinc_uint32x4_t b) { + __m128i bias_by = _mm_set1_epi32((uint32_t)0x80000000); + return _mm_or_si128(_mm_cmpgt_epi32(_mm_sub_epi32(a, bias_by), _mm_sub_epi32(b, bias_by)), _mm_cmpeq_epi32(a, b)); +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmpgt(kinc_uint32x4_t a, kinc_uint32x4_t b) { + __m128i bias_by = _mm_set1_epi32((uint32_t)0x80000000); + return _mm_cmpgt_epi32(_mm_sub_epi32(a, bias_by), _mm_sub_epi32(b, bias_by)); +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmple(kinc_uint32x4_t a, kinc_uint32x4_t b) { + __m128i bias_by = _mm_set1_epi32((uint32_t)0x80000000); + return _mm_or_si128(_mm_cmplt_epi32(_mm_sub_epi32(a, bias_by), _mm_sub_epi32(b, bias_by)), _mm_cmpeq_epi32(a, b)); +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmplt(kinc_uint32x4_t a, kinc_uint32x4_t b) { + __m128i bias_by = _mm_set1_epi32((uint32_t)0x80000000); + return _mm_cmplt_epi32(_mm_sub_epi32(a, bias_by), _mm_sub_epi32(b, bias_by)); +} + +static inline kinc_uint32x4_t kinc_uint32x4_sel(kinc_uint32x4_t a, kinc_uint32x4_t b, kinc_uint32x4_mask_t mask) { + return _mm_xor_si128(b, _mm_and_si128(mask, _mm_xor_si128(a, b))); +} + +static inline kinc_uint32x4_t kinc_uint32x4_max(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return kinc_uint32x4_sel(a, b, kinc_uint32x4_cmpgt(a, b)); +} + +static inline kinc_uint32x4_t kinc_uint32x4_min(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return kinc_uint32x4_sel(a, b, kinc_uint32x4_cmplt(a, b)); +} + +static inline kinc_uint32x4_t kinc_uint32x4_or(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return _mm_or_si128(a, b); +} + +static inline kinc_uint32x4_t kinc_uint32x4_and(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return _mm_and_si128(a, b); +} + +static inline kinc_uint32x4_t kinc_uint32x4_xor(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return _mm_xor_si128(a, b); +} + +static inline kinc_uint32x4_t kinc_uint32x4_not(kinc_uint32x4_t t) { + return _mm_xor_si128(t, _mm_set1_epi32(0xffffffff)); +} + +#define kinc_uint32x4_shift_left(t, shift) _mm_slli_epi32((t), (shift)) + +#define kinc_uint32x4_shift_right(t, shift) _mm_srli_epi32((t), (shift)) + +#elif defined(KINC_NEON) + +static inline kinc_uint32x4_t kinc_uint32x4_intrin_load(const uint32_t *values) { + return vld1q_u32(values); +} + +static inline kinc_uint32x4_t kinc_uint32x4_intrin_load_unaligned(const uint32_t *values) { + return kinc_uint32x4_intrin_load(values); +} + +static inline kinc_uint32x4_t kinc_uint32x4_load(const uint32_t values[4]) { + return (kinc_uint32x4_t){values[0], values[1], values[2], values[3]}; +} + +static inline kinc_uint32x4_t kinc_uint32x4_load_all(uint32_t t) { + return (kinc_uint32x4_t){t, t, t, t}; +} + +static inline void kinc_uint32x4_store(uint32_t *destination, kinc_uint32x4_t value) { + vst1q_u32(destination, value); +} + +static inline void kinc_uint32x4_store_unaligned(uint32_t *destination, kinc_uint32x4_t value) { + kinc_uint32x4_store(destination, value); +} + +static inline uint32_t kinc_uint32x4_get(kinc_uint32x4_t t, int index) { + return t[index]; +} + +static inline kinc_uint32x4_t kinc_uint32x4_add(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return vaddq_u32(a, b); +} + +static inline kinc_uint32x4_t kinc_uint32x4_sub(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return vsubq_u32(a, b); +} + +static inline kinc_uint32x4_t kinc_uint32x4_max(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return vmaxq_u32(a, b); +} + +static inline kinc_uint32x4_t kinc_uint32x4_min(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return vminq_u32(a, b); +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmpeq(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return vceqq_u32(a, b); +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmpneq(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return vmvnq_u32(vceqq_u32(a, b)); +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmpge(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return vcgeq_u32(a, b); +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmpgt(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return vcgtq_u32(a, b); +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmple(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return vcleq_u32(a, b); +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmplt(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return vcltq_u32(a, b); +} + +static inline kinc_uint32x4_t kinc_uint32x4_sel(kinc_uint32x4_t a, kinc_uint32x4_t b, kinc_uint32x4_mask_t mask) { + return vbslq_u32(mask, a, b); +} + +static inline kinc_uint32x4_t kinc_uint32x4_or(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return vorrq_u32(a, b); +} + +static inline kinc_uint32x4_t kinc_uint32x4_and(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return vandq_u32(a, b); +} + +static inline kinc_uint32x4_t kinc_uint32x4_xor(kinc_uint32x4_t a, kinc_uint32x4_t b) { + return veorq_u32(a, b); +} + +static inline kinc_uint32x4_t kinc_uint32x4_not(kinc_uint32x4_t t) { + return vmvnq_u32(t); +} + +#define kinc_uint32x4_shift_left(t, shift) vshlq_n_u32((t), (shift)) + +#define kinc_uint32x4_shift_right(t, shift) vshrq_n_u32((t), (shift)) + +#else + +static inline kinc_uint32x4_t kinc_uint32x4_intrin_load(const uint32_t *values) { + kinc_uint32x4_t value; + value.values[0] = values[0]; + value.values[1] = values[1]; + value.values[2] = values[2]; + value.values[3] = values[3]; + return value; +} + +static inline kinc_uint32x4_t kinc_uint32x4_intrin_load_unaligned(const uint32_t *values) { + return kinc_uint32x4_intrin_load(values); +} + +static inline kinc_uint32x4_t kinc_uint32x4_load(const uint32_t values[4]) { + kinc_uint32x4_t value; + value.values[0] = values[0]; + value.values[1] = values[1]; + value.values[2] = values[2]; + value.values[3] = values[3]; + return value; +} + +static inline kinc_uint32x4_t kinc_uint32x4_load_all(uint32_t t) { + kinc_uint32x4_t value; + value.values[0] = t; + value.values[1] = t; + value.values[2] = t; + value.values[3] = t; + return value; +} + +static inline void kinc_uint32x4_store(uint32_t *destination, kinc_uint32x4_t value) { + destination[0] = value.values[0]; + destination[1] = value.values[1]; + destination[2] = value.values[2]; + destination[3] = value.values[3]; +} + +static inline void kinc_uint32x4_store_unaligned(uint32_t *destination, kinc_uint32x4_t value) { + kinc_uint32x4_store(destination, value); +} + +static inline uint32_t kinc_uint32x4_get(kinc_uint32x4_t t, int index) { + return t.values[index]; +} + +static inline kinc_uint32x4_t kinc_uint32x4_add(kinc_uint32x4_t a, kinc_uint32x4_t b) { + kinc_uint32x4_t value; + value.values[0] = a.values[0] + b.values[0]; + value.values[1] = a.values[1] + b.values[1]; + value.values[2] = a.values[2] + b.values[2]; + value.values[3] = a.values[3] + b.values[3]; + return value; +} + +static inline kinc_uint32x4_t kinc_uint32x4_sub(kinc_uint32x4_t a, kinc_uint32x4_t b) { + kinc_uint32x4_t value; + value.values[0] = a.values[0] - b.values[0]; + value.values[1] = a.values[1] - b.values[1]; + value.values[2] = a.values[2] - b.values[2]; + value.values[3] = a.values[3] - b.values[3]; + return value; +} + +static inline kinc_uint32x4_t kinc_uint32x4_max(kinc_uint32x4_t a, kinc_uint32x4_t b) { + kinc_uint32x4_t value; + value.values[0] = a.values[0] > b.values[0] ? a.values[0] : b.values[0]; + value.values[1] = a.values[1] > b.values[1] ? a.values[1] : b.values[1]; + value.values[2] = a.values[2] > b.values[2] ? a.values[2] : b.values[2]; + value.values[3] = a.values[3] > b.values[3] ? a.values[3] : b.values[3]; + return value; +} + +static inline kinc_uint32x4_t kinc_uint32x4_min(kinc_uint32x4_t a, kinc_uint32x4_t b) { + kinc_uint32x4_t value; + value.values[0] = a.values[0] > b.values[0] ? b.values[0] : a.values[0]; + value.values[1] = a.values[1] > b.values[1] ? b.values[1] : a.values[1]; + value.values[2] = a.values[2] > b.values[2] ? b.values[2] : a.values[2]; + value.values[3] = a.values[3] > b.values[3] ? b.values[3] : a.values[3]; + return value; +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmpeq(kinc_uint32x4_t a, kinc_uint32x4_t b) { + kinc_uint32x4_mask_t mask; + mask.values[0] = a.values[0] == b.values[0] ? 0xffffffff : 0; + mask.values[1] = a.values[1] == b.values[1] ? 0xffffffff : 0; + mask.values[2] = a.values[2] == b.values[2] ? 0xffffffff : 0; + mask.values[3] = a.values[3] == b.values[3] ? 0xffffffff : 0; + return mask; +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmpneq(kinc_uint32x4_t a, kinc_uint32x4_t b) { + kinc_uint32x4_mask_t mask; + mask.values[0] = a.values[0] != b.values[0] ? 0xffffffff : 0; + mask.values[1] = a.values[1] != b.values[1] ? 0xffffffff : 0; + mask.values[2] = a.values[2] != b.values[2] ? 0xffffffff : 0; + mask.values[3] = a.values[3] != b.values[3] ? 0xffffffff : 0; + return mask; +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmpge(kinc_uint32x4_t a, kinc_uint32x4_t b) { + kinc_uint32x4_mask_t mask; + mask.values[0] = a.values[0] >= b.values[0] ? 0xffffffff : 0; + mask.values[1] = a.values[1] >= b.values[1] ? 0xffffffff : 0; + mask.values[2] = a.values[2] >= b.values[2] ? 0xffffffff : 0; + mask.values[3] = a.values[3] >= b.values[3] ? 0xffffffff : 0; + return mask; +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmpgt(kinc_uint32x4_t a, kinc_uint32x4_t b) { + kinc_uint32x4_mask_t mask; + mask.values[0] = a.values[0] > b.values[0] ? 0xffffffff : 0; + mask.values[1] = a.values[1] > b.values[1] ? 0xffffffff : 0; + mask.values[2] = a.values[2] > b.values[2] ? 0xffffffff : 0; + mask.values[3] = a.values[3] > b.values[3] ? 0xffffffff : 0; + return mask; +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmple(kinc_uint32x4_t a, kinc_uint32x4_t b) { + kinc_uint32x4_mask_t mask; + mask.values[0] = a.values[0] <= b.values[0] ? 0xffffffff : 0; + mask.values[1] = a.values[1] <= b.values[1] ? 0xffffffff : 0; + mask.values[2] = a.values[2] <= b.values[2] ? 0xffffffff : 0; + mask.values[3] = a.values[3] <= b.values[3] ? 0xffffffff : 0; + return mask; +} + +static inline kinc_uint32x4_mask_t kinc_uint32x4_cmplt(kinc_uint32x4_t a, kinc_uint32x4_t b) { + kinc_uint32x4_mask_t mask; + mask.values[0] = a.values[0] < b.values[0] ? 0xffffffff : 0; + mask.values[1] = a.values[1] < b.values[1] ? 0xffffffff : 0; + mask.values[2] = a.values[2] < b.values[2] ? 0xffffffff : 0; + mask.values[3] = a.values[3] < b.values[3] ? 0xffffffff : 0; + return mask; +} + +static inline kinc_uint32x4_t kinc_uint32x4_sel(kinc_uint32x4_t a, kinc_uint32x4_t b, kinc_uint32x4_mask_t mask) { + kinc_uint32x4_t value; + value.values[0] = mask.values[0] != 0 ? a.values[0] : b.values[0]; + value.values[1] = mask.values[1] != 0 ? a.values[1] : b.values[1]; + value.values[2] = mask.values[2] != 0 ? a.values[2] : b.values[2]; + value.values[3] = mask.values[3] != 0 ? a.values[3] : b.values[3]; + return value; +} + +static inline kinc_uint32x4_t kinc_uint32x4_or(kinc_uint32x4_t a, kinc_uint32x4_t b) { + kinc_uint32x4_t value; + value.values[0] = a.values[0] | b.values[0]; + value.values[1] = a.values[1] | b.values[1]; + value.values[2] = a.values[2] | b.values[2]; + value.values[3] = a.values[3] | b.values[3]; + return value; +} + +static inline kinc_uint32x4_t kinc_uint32x4_and(kinc_uint32x4_t a, kinc_uint32x4_t b) { + kinc_uint32x4_t value; + value.values[0] = a.values[0] & b.values[0]; + value.values[1] = a.values[1] & b.values[1]; + value.values[2] = a.values[2] & b.values[2]; + value.values[3] = a.values[3] & b.values[3]; + return value; +} + +static inline kinc_uint32x4_t kinc_uint32x4_xor(kinc_uint32x4_t a, kinc_uint32x4_t b) { + kinc_uint32x4_t value; + value.values[0] = a.values[0] ^ b.values[0]; + value.values[1] = a.values[1] ^ b.values[1]; + value.values[2] = a.values[2] ^ b.values[2]; + value.values[3] = a.values[3] ^ b.values[3]; + return value; +} + +static inline kinc_uint32x4_t kinc_uint32x4_not(kinc_uint32x4_t t) { + kinc_uint32x4_t value; + value.values[0] = ~t.values[0]; + value.values[1] = ~t.values[1]; + value.values[2] = ~t.values[2]; + value.values[3] = ~t.values[3]; + return value; +} + +static inline kinc_uint32x4_t kinc_uint32x4_shift_left(kinc_uint32x4_t t, const int shift) { + kinc_uint32x4_t value; + value.values[0] = t.values[0] << shift; + value.values[1] = t.values[1] << shift; + value.values[2] = t.values[2] << shift; + value.values[3] = t.values[3] << shift; + + return value; +} + +static inline kinc_uint32x4_t kinc_uint32x4_shift_right(kinc_uint32x4_t t, const int shift) { + kinc_uint32x4_t value; + value.values[0] = t.values[0] >> shift; + value.values[1] = t.values[1] >> shift; + value.values[2] = t.values[2] >> shift; + value.values[3] = t.values[3] >> shift; + + return value; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/simd/uint8x16.h b/armorcore/sources/kinc/simd/uint8x16.h new file mode 100644 index 000000000..c737b4d9f --- /dev/null +++ b/armorcore/sources/kinc/simd/uint8x16.h @@ -0,0 +1,615 @@ +#pragma once + +#include "types.h" + +/*! \file int8x16.h + \brief Provides 128bit sixteen-element unsigned 8-bit integer SIMD operations which are mapped to equivalent SSE2 or Neon operations. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(KINC_SSE2) + +static inline kinc_uint8x16_t kinc_uint8x16_intrin_load(const uint8_t *values) { + return _mm_load_si128((const kinc_uint8x16_t *)values); +} + +static inline kinc_uint8x16_t kinc_uint8x16_intrin_load_unaligned(const uint8_t *values) { + return _mm_loadu_si128((const kinc_uint8x16_t *)values); +} + +static inline kinc_uint8x16_t kinc_uint8x16_load(const uint8_t values[16]) { + return _mm_set_epi8(values[15], values[14], values[13], values[12], values[11], values[10], values[9], values[8], values[7], values[6], values[5], + values[4], values[3], values[2], values[1], values[0]); +} + +static inline kinc_uint8x16_t kinc_uint8x16_load_all(uint8_t t) { + return _mm_set1_epi8(t); +} + +static inline void kinc_uint8x16_store(uint8_t *destination, kinc_uint8x16_t value) { + _mm_store_si128((kinc_uint8x16_t *)destination, value); +} + +static inline void kinc_uint8x16_store_unaligned(uint8_t *destination, kinc_uint8x16_t value) { + _mm_storeu_si128((kinc_uint8x16_t *)destination, value); +} + +static inline uint8_t kinc_uint8x16_get(kinc_uint8x16_t t, int index) { + union { + __m128i value; + uint8_t elements[16]; + } converter; + converter.value = t; + return converter.elements[index]; +} + +static inline kinc_uint8x16_t kinc_uint8x16_add(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return _mm_add_epi8(a, b); +} + +static inline kinc_uint8x16_t kinc_uint8x16_sub(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return _mm_sub_epi8(a, b); +} + +static inline kinc_uint8x16_t kinc_uint8x16_max(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return _mm_max_epu8(a, b); +} + +static inline kinc_uint8x16_t kinc_uint8x16_min(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return _mm_min_epu8(a, b); +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmpeq(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return _mm_cmpeq_epi8(a, b); +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmpge(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return _mm_cmpeq_epi8(_mm_max_epu8(a, b), a); +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmpgt(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return _mm_xor_si128(_mm_cmpeq_epi8(_mm_max_epu8(b, a), b), _mm_set1_epi8(-1)); +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmple(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return _mm_cmpeq_epi8(_mm_max_epu8(b, a), b); +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmplt(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return kinc_uint8x16_cmpgt(b, a); +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmpneq(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return _mm_andnot_si128(_mm_cmpeq_epi8(a, b), _mm_set1_epi32(0xffffffff)); +} + +static inline kinc_uint8x16_t kinc_uint8x16_sel(kinc_uint8x16_t a, kinc_uint8x16_t b, kinc_uint8x16_mask_t mask) { + return _mm_xor_si128(b, _mm_and_si128(mask, _mm_xor_si128(a, b))); +} + +static inline kinc_uint8x16_t kinc_uint8x16_or(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return _mm_or_si128(a, b); +} + +static inline kinc_uint8x16_t kinc_uint8x16_and(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return _mm_and_si128(a, b); +} + +static inline kinc_uint8x16_t kinc_uint8x16_xor(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return _mm_xor_si128(a, b); +} + +static inline kinc_uint8x16_t kinc_uint8x16_not(kinc_uint8x16_t t) { + return _mm_xor_si128(t, _mm_set1_epi32(0xffffffff)); +} + +#elif defined(KINC_NEON) + +static inline kinc_uint8x16_t kinc_uint8x16_intrin_load(const uint8_t *values) { + return vld1q_u8(values); +} + +static inline kinc_uint8x16_t kinc_uint8x16_intrin_load_unaligned(const uint8_t *values) { + return kinc_uint8x16_intrin_load(values); +} + +static inline kinc_uint8x16_t kinc_uint8x16_load(const uint8_t values[16]) { + return (kinc_uint8x16_t){values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], + values[8], values[9], values[10], values[11], values[12], values[13], values[14], values[15]}; +} + +static inline kinc_uint8x16_t kinc_uint8x16_load_all(uint8_t t) { + return (kinc_uint8x16_t){t, t, t, t, t, t, t, t, t, t, t, t, t, t, t, t}; +} + +static inline void kinc_uint8x16_store(uint8_t *destination, kinc_uint8x16_t value) { + vst1q_u8(destination, value); +} + +static inline void kinc_uint8x16_store_unaligned(uint8_t *destination, kinc_uint8x16_t value) { + kinc_uint8x16_store(destination, value); +} + +static inline uint8_t kinc_uint8x16_get(kinc_uint8x16_t t, int index) { + return t[index]; +} + +static inline kinc_uint8x16_t kinc_uint8x16_add(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return vaddq_u8(a, b); +} + +static inline kinc_uint8x16_t kinc_uint8x16_sub(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return vsubq_u8(a, b); +} + +static inline kinc_uint8x16_t kinc_uint8x16_max(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return vmaxq_u8(a, b); +} + +static inline kinc_uint8x16_t kinc_uint8x16_min(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return vminq_u8(a, b); +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmpeq(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return vceqq_u8(a, b); +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmpge(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return vcgeq_u8(a, b); +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmpgt(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return vcgtq_u8(a, b); +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmple(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return vcleq_u8(a, b); +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmplt(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return vcltq_u8(a, b); +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmpneq(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return vmvnq_u8(vceqq_u8(a, b)); +} + +static inline kinc_uint8x16_t kinc_uint8x16_sel(kinc_uint8x16_t a, kinc_uint8x16_t b, kinc_uint8x16_mask_t mask) { + return vbslq_u8(mask, a, b); +} + +static inline kinc_uint8x16_t kinc_uint8x16_or(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return vorrq_u8(a, b); +} + +static inline kinc_uint8x16_t kinc_uint8x16_and(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return vandq_u8(a, b); +} + +static inline kinc_uint8x16_t kinc_uint8x16_xor(kinc_uint8x16_t a, kinc_uint8x16_t b) { + return veorq_u8(a, b); +} + +static inline kinc_uint8x16_t kinc_uint8x16_not(kinc_uint8x16_t t) { + return vmvnq_u8(t); +} + +#else + +static inline kinc_uint8x16_t kinc_uint8x16_intrin_load(const uint8_t *values) { + kinc_uint8x16_t value; + value.values[0] = values[0]; + value.values[1] = values[1]; + value.values[2] = values[2]; + value.values[3] = values[3]; + value.values[4] = values[4]; + value.values[5] = values[5]; + value.values[6] = values[6]; + value.values[7] = values[7]; + value.values[8] = values[8]; + value.values[9] = values[9]; + value.values[10] = values[10]; + value.values[11] = values[11]; + value.values[12] = values[12]; + value.values[13] = values[13]; + value.values[14] = values[14]; + value.values[15] = values[15]; + return value; +} + +static inline kinc_uint8x16_t kinc_uint8x16_intrin_load_unaligned(const uint8_t *values) { + return kinc_uint8x16_intrin_load(values); +} + +static inline kinc_uint8x16_t kinc_uint8x16_load(const uint8_t values[16]) { + kinc_uint8x16_t value; + value.values[0] = values[0]; + value.values[1] = values[1]; + value.values[2] = values[2]; + value.values[3] = values[3]; + value.values[4] = values[4]; + value.values[5] = values[5]; + value.values[6] = values[6]; + value.values[7] = values[7]; + value.values[8] = values[8]; + value.values[9] = values[9]; + value.values[10] = values[10]; + value.values[11] = values[11]; + value.values[12] = values[12]; + value.values[13] = values[13]; + value.values[14] = values[14]; + value.values[15] = values[15]; + return value; +} + +static inline kinc_uint8x16_t kinc_uint8x16_load_all(uint8_t t) { + kinc_uint8x16_t value; + value.values[0] = t; + value.values[1] = t; + value.values[2] = t; + value.values[3] = t; + value.values[4] = t; + value.values[5] = t; + value.values[6] = t; + value.values[7] = t; + value.values[8] = t; + value.values[9] = t; + value.values[10] = t; + value.values[11] = t; + value.values[12] = t; + value.values[13] = t; + value.values[14] = t; + value.values[15] = t; + return value; +} + +static inline void kinc_uint8x16_store(uint8_t *destination, kinc_uint8x16_t value) { + destination[0] = value.values[0]; + destination[1] = value.values[1]; + destination[2] = value.values[2]; + destination[3] = value.values[3]; + destination[4] = value.values[4]; + destination[5] = value.values[5]; + destination[6] = value.values[6]; + destination[7] = value.values[7]; + destination[8] = value.values[8]; + destination[9] = value.values[9]; + destination[10] = value.values[10]; + destination[11] = value.values[11]; + destination[12] = value.values[12]; + destination[13] = value.values[13]; + destination[14] = value.values[14]; + destination[15] = value.values[15]; +} + +static inline void kinc_uint8x16_store_unaligned(uint8_t *destination, kinc_uint8x16_t value) { + return kinc_uint8x16_store(destination, value); +} + +static inline uint8_t kinc_uint8x16_get(kinc_uint8x16_t t, int index) { + return t.values[index]; +} + +static inline kinc_uint8x16_t kinc_uint8x16_add(kinc_uint8x16_t a, kinc_uint8x16_t b) { + kinc_uint8x16_t value; + value.values[0] = a.values[0] + b.values[0]; + value.values[1] = a.values[1] + b.values[1]; + value.values[2] = a.values[2] + b.values[2]; + value.values[3] = a.values[3] + b.values[3]; + value.values[4] = a.values[4] + b.values[4]; + value.values[5] = a.values[5] + b.values[5]; + value.values[6] = a.values[6] + b.values[6]; + value.values[7] = a.values[7] + b.values[7]; + value.values[8] = a.values[8] + b.values[8]; + value.values[9] = a.values[9] + b.values[9]; + value.values[10] = a.values[10] + b.values[10]; + value.values[11] = a.values[11] + b.values[11]; + value.values[12] = a.values[12] + b.values[12]; + value.values[13] = a.values[13] + b.values[13]; + value.values[14] = a.values[14] + b.values[14]; + value.values[15] = a.values[15] + b.values[15]; + return value; +} + +static inline kinc_uint8x16_t kinc_uint8x16_sub(kinc_uint8x16_t a, kinc_uint8x16_t b) { + kinc_uint8x16_t value; + value.values[0] = a.values[0] - b.values[0]; + value.values[1] = a.values[1] - b.values[1]; + value.values[2] = a.values[2] - b.values[2]; + value.values[3] = a.values[3] - b.values[3]; + value.values[4] = a.values[4] - b.values[4]; + value.values[5] = a.values[5] - b.values[5]; + value.values[6] = a.values[6] - b.values[6]; + value.values[7] = a.values[7] - b.values[7]; + value.values[8] = a.values[8] - b.values[8]; + value.values[9] = a.values[9] - b.values[9]; + value.values[10] = a.values[10] - b.values[10]; + value.values[11] = a.values[11] - b.values[11]; + value.values[12] = a.values[12] - b.values[12]; + value.values[13] = a.values[13] - b.values[13]; + value.values[14] = a.values[14] - b.values[14]; + value.values[15] = a.values[15] - b.values[15]; + return value; +} + +static inline kinc_uint8x16_t kinc_uint8x16_max(kinc_uint8x16_t a, kinc_uint8x16_t b) { + kinc_uint8x16_t value; + value.values[0] = a.values[0] > b.values[0] ? a.values[0] : b.values[0]; + value.values[1] = a.values[1] > b.values[1] ? a.values[1] : b.values[1]; + value.values[2] = a.values[2] > b.values[2] ? a.values[2] : b.values[2]; + value.values[3] = a.values[3] > b.values[3] ? a.values[3] : b.values[3]; + value.values[4] = a.values[4] > b.values[4] ? a.values[4] : b.values[4]; + value.values[5] = a.values[5] > b.values[5] ? a.values[5] : b.values[5]; + value.values[6] = a.values[6] > b.values[6] ? a.values[6] : b.values[6]; + value.values[7] = a.values[7] > b.values[7] ? a.values[7] : b.values[7]; + value.values[8] = a.values[8] > b.values[8] ? a.values[8] : b.values[8]; + value.values[9] = a.values[9] > b.values[9] ? a.values[9] : b.values[9]; + value.values[10] = a.values[10] > b.values[10] ? a.values[10] : b.values[10]; + value.values[11] = a.values[11] > b.values[11] ? a.values[11] : b.values[11]; + value.values[12] = a.values[12] > b.values[12] ? a.values[12] : b.values[12]; + value.values[13] = a.values[13] > b.values[13] ? a.values[13] : b.values[13]; + value.values[14] = a.values[14] > b.values[14] ? a.values[14] : b.values[14]; + value.values[15] = a.values[15] > b.values[15] ? a.values[15] : b.values[15]; + return value; +} + +static inline kinc_uint8x16_t kinc_uint8x16_min(kinc_uint8x16_t a, kinc_uint8x16_t b) { + kinc_uint8x16_t value; + value.values[0] = a.values[0] > b.values[0] ? b.values[0] : a.values[0]; + value.values[1] = a.values[1] > b.values[1] ? b.values[1] : a.values[1]; + value.values[2] = a.values[2] > b.values[2] ? b.values[2] : a.values[2]; + value.values[3] = a.values[3] > b.values[3] ? b.values[3] : a.values[3]; + value.values[4] = a.values[4] > b.values[4] ? b.values[4] : a.values[4]; + value.values[5] = a.values[5] > b.values[5] ? b.values[5] : a.values[5]; + value.values[6] = a.values[6] > b.values[6] ? b.values[6] : a.values[6]; + value.values[7] = a.values[7] > b.values[7] ? b.values[7] : a.values[7]; + value.values[8] = a.values[8] > b.values[8] ? b.values[8] : a.values[8]; + value.values[9] = a.values[9] > b.values[9] ? b.values[9] : a.values[9]; + value.values[10] = a.values[10] > b.values[10] ? b.values[10] : a.values[10]; + value.values[11] = a.values[11] > b.values[11] ? b.values[11] : a.values[11]; + value.values[12] = a.values[12] > b.values[12] ? b.values[12] : a.values[12]; + value.values[13] = a.values[13] > b.values[13] ? b.values[13] : a.values[13]; + value.values[14] = a.values[14] > b.values[14] ? b.values[14] : a.values[14]; + value.values[15] = a.values[15] > b.values[15] ? b.values[15] : a.values[15]; + return value; +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmpeq(kinc_uint8x16_t a, kinc_uint8x16_t b) { + kinc_uint8x16_mask_t mask; + mask.values[0] = a.values[0] == b.values[0] ? 0xff : 0; + mask.values[1] = a.values[1] == b.values[1] ? 0xff : 0; + mask.values[2] = a.values[2] == b.values[2] ? 0xff : 0; + mask.values[3] = a.values[3] == b.values[3] ? 0xff : 0; + mask.values[4] = a.values[4] == b.values[4] ? 0xff : 0; + mask.values[5] = a.values[5] == b.values[5] ? 0xff : 0; + mask.values[6] = a.values[6] == b.values[6] ? 0xff : 0; + mask.values[7] = a.values[7] == b.values[7] ? 0xff : 0; + mask.values[8] = a.values[8] == b.values[8] ? 0xff : 0; + mask.values[9] = a.values[9] == b.values[9] ? 0xff : 0; + mask.values[10] = a.values[10] == b.values[10] ? 0xff : 0; + mask.values[11] = a.values[11] == b.values[11] ? 0xff : 0; + mask.values[12] = a.values[12] == b.values[12] ? 0xff : 0; + mask.values[13] = a.values[13] == b.values[13] ? 0xff : 0; + mask.values[14] = a.values[14] == b.values[14] ? 0xff : 0; + mask.values[15] = a.values[15] == b.values[15] ? 0xff : 0; + return mask; +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmpge(kinc_uint8x16_t a, kinc_uint8x16_t b) { + kinc_uint8x16_mask_t mask; + mask.values[0] = a.values[0] >= b.values[0] ? 0xff : 0; + mask.values[1] = a.values[1] >= b.values[1] ? 0xff : 0; + mask.values[2] = a.values[2] >= b.values[2] ? 0xff : 0; + mask.values[3] = a.values[3] >= b.values[3] ? 0xff : 0; + mask.values[4] = a.values[4] >= b.values[4] ? 0xff : 0; + mask.values[5] = a.values[5] >= b.values[5] ? 0xff : 0; + mask.values[6] = a.values[6] >= b.values[6] ? 0xff : 0; + mask.values[7] = a.values[7] >= b.values[7] ? 0xff : 0; + mask.values[8] = a.values[8] >= b.values[8] ? 0xff : 0; + mask.values[9] = a.values[9] >= b.values[9] ? 0xff : 0; + mask.values[10] = a.values[10] >= b.values[10] ? 0xff : 0; + mask.values[11] = a.values[11] >= b.values[11] ? 0xff : 0; + mask.values[12] = a.values[12] >= b.values[12] ? 0xff : 0; + mask.values[13] = a.values[13] >= b.values[13] ? 0xff : 0; + mask.values[14] = a.values[14] >= b.values[14] ? 0xff : 0; + mask.values[15] = a.values[15] >= b.values[15] ? 0xff : 0; + return mask; +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmpgt(kinc_uint8x16_t a, kinc_uint8x16_t b) { + kinc_uint8x16_mask_t mask; + mask.values[0] = a.values[0] > b.values[0] ? 0xff : 0; + mask.values[1] = a.values[1] > b.values[1] ? 0xff : 0; + mask.values[2] = a.values[2] > b.values[2] ? 0xff : 0; + mask.values[3] = a.values[3] > b.values[3] ? 0xff : 0; + mask.values[4] = a.values[4] > b.values[4] ? 0xff : 0; + mask.values[5] = a.values[5] > b.values[5] ? 0xff : 0; + mask.values[6] = a.values[6] > b.values[6] ? 0xff : 0; + mask.values[7] = a.values[7] > b.values[7] ? 0xff : 0; + mask.values[8] = a.values[8] > b.values[8] ? 0xff : 0; + mask.values[9] = a.values[9] > b.values[9] ? 0xff : 0; + mask.values[10] = a.values[10] > b.values[10] ? 0xff : 0; + mask.values[11] = a.values[11] > b.values[11] ? 0xff : 0; + mask.values[12] = a.values[12] > b.values[12] ? 0xff : 0; + mask.values[13] = a.values[13] > b.values[13] ? 0xff : 0; + mask.values[14] = a.values[14] > b.values[14] ? 0xff : 0; + mask.values[15] = a.values[15] > b.values[15] ? 0xff : 0; + return mask; +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmple(kinc_uint8x16_t a, kinc_uint8x16_t b) { + kinc_uint8x16_mask_t mask; + mask.values[0] = a.values[0] <= b.values[0] ? 0xff : 0; + mask.values[1] = a.values[1] <= b.values[1] ? 0xff : 0; + mask.values[2] = a.values[2] <= b.values[2] ? 0xff : 0; + mask.values[3] = a.values[3] <= b.values[3] ? 0xff : 0; + mask.values[4] = a.values[4] <= b.values[4] ? 0xff : 0; + mask.values[5] = a.values[5] <= b.values[5] ? 0xff : 0; + mask.values[6] = a.values[6] <= b.values[6] ? 0xff : 0; + mask.values[7] = a.values[7] <= b.values[7] ? 0xff : 0; + mask.values[8] = a.values[8] <= b.values[8] ? 0xff : 0; + mask.values[9] = a.values[9] <= b.values[9] ? 0xff : 0; + mask.values[10] = a.values[10] <= b.values[10] ? 0xff : 0; + mask.values[11] = a.values[11] <= b.values[11] ? 0xff : 0; + mask.values[12] = a.values[12] <= b.values[12] ? 0xff : 0; + mask.values[13] = a.values[13] <= b.values[13] ? 0xff : 0; + mask.values[14] = a.values[14] <= b.values[14] ? 0xff : 0; + mask.values[15] = a.values[15] <= b.values[15] ? 0xff : 0; + return mask; +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmplt(kinc_uint8x16_t a, kinc_uint8x16_t b) { + kinc_uint8x16_mask_t mask; + mask.values[0] = a.values[0] < b.values[0] ? 0xff : 0; + mask.values[1] = a.values[1] < b.values[1] ? 0xff : 0; + mask.values[2] = a.values[2] < b.values[2] ? 0xff : 0; + mask.values[3] = a.values[3] < b.values[3] ? 0xff : 0; + mask.values[4] = a.values[4] < b.values[4] ? 0xff : 0; + mask.values[5] = a.values[5] < b.values[5] ? 0xff : 0; + mask.values[6] = a.values[6] < b.values[6] ? 0xff : 0; + mask.values[7] = a.values[7] < b.values[7] ? 0xff : 0; + mask.values[8] = a.values[8] < b.values[8] ? 0xff : 0; + mask.values[9] = a.values[9] < b.values[9] ? 0xff : 0; + mask.values[10] = a.values[10] < b.values[10] ? 0xff : 0; + mask.values[11] = a.values[11] < b.values[11] ? 0xff : 0; + mask.values[12] = a.values[12] < b.values[12] ? 0xff : 0; + mask.values[13] = a.values[13] < b.values[13] ? 0xff : 0; + mask.values[14] = a.values[14] < b.values[14] ? 0xff : 0; + mask.values[15] = a.values[15] < b.values[15] ? 0xff : 0; + return mask; +} + +static inline kinc_uint8x16_mask_t kinc_uint8x16_cmpneq(kinc_uint8x16_t a, kinc_uint8x16_t b) { + kinc_uint8x16_mask_t mask; + mask.values[0] = a.values[0] != b.values[0] ? 0xff : 0; + mask.values[1] = a.values[1] != b.values[1] ? 0xff : 0; + mask.values[2] = a.values[2] != b.values[2] ? 0xff : 0; + mask.values[3] = a.values[3] != b.values[3] ? 0xff : 0; + mask.values[4] = a.values[4] != b.values[4] ? 0xff : 0; + mask.values[5] = a.values[5] != b.values[5] ? 0xff : 0; + mask.values[6] = a.values[6] != b.values[6] ? 0xff : 0; + mask.values[7] = a.values[7] != b.values[7] ? 0xff : 0; + mask.values[8] = a.values[8] != b.values[8] ? 0xff : 0; + mask.values[9] = a.values[9] != b.values[9] ? 0xff : 0; + mask.values[10] = a.values[10] != b.values[10] ? 0xff : 0; + mask.values[11] = a.values[11] != b.values[11] ? 0xff : 0; + mask.values[12] = a.values[12] != b.values[12] ? 0xff : 0; + mask.values[13] = a.values[13] != b.values[13] ? 0xff : 0; + mask.values[14] = a.values[14] != b.values[14] ? 0xff : 0; + mask.values[15] = a.values[15] != b.values[15] ? 0xff : 0; + return mask; +} + +static inline kinc_uint8x16_t kinc_uint8x16_sel(kinc_uint8x16_t a, kinc_uint8x16_t b, kinc_uint8x16_mask_t mask) { + kinc_uint8x16_t value; + value.values[0] = mask.values[0] != 0 ? a.values[0] : b.values[0]; + value.values[1] = mask.values[1] != 0 ? a.values[1] : b.values[1]; + value.values[2] = mask.values[2] != 0 ? a.values[2] : b.values[2]; + value.values[3] = mask.values[3] != 0 ? a.values[3] : b.values[3]; + value.values[4] = mask.values[4] != 0 ? a.values[4] : b.values[4]; + value.values[5] = mask.values[5] != 0 ? a.values[5] : b.values[5]; + value.values[6] = mask.values[6] != 0 ? a.values[6] : b.values[6]; + value.values[7] = mask.values[7] != 0 ? a.values[7] : b.values[7]; + value.values[8] = mask.values[8] != 0 ? a.values[8] : b.values[8]; + value.values[9] = mask.values[9] != 0 ? a.values[9] : b.values[9]; + value.values[10] = mask.values[10] != 0 ? a.values[10] : b.values[10]; + value.values[11] = mask.values[11] != 0 ? a.values[11] : b.values[11]; + value.values[12] = mask.values[12] != 0 ? a.values[12] : b.values[12]; + value.values[13] = mask.values[13] != 0 ? a.values[13] : b.values[13]; + value.values[14] = mask.values[14] != 0 ? a.values[14] : b.values[14]; + value.values[15] = mask.values[15] != 0 ? a.values[15] : b.values[15]; + return value; +} + +static inline kinc_uint8x16_t kinc_uint8x16_or(kinc_uint8x16_t a, kinc_uint8x16_t b) { + kinc_uint8x16_t value; + value.values[0] = a.values[0] | b.values[0]; + value.values[1] = a.values[1] | b.values[1]; + value.values[2] = a.values[2] | b.values[2]; + value.values[3] = a.values[3] | b.values[3]; + value.values[4] = a.values[4] | b.values[4]; + value.values[5] = a.values[5] | b.values[5]; + value.values[6] = a.values[6] | b.values[6]; + value.values[7] = a.values[7] | b.values[7]; + value.values[8] = a.values[8] | b.values[8]; + value.values[9] = a.values[9] | b.values[9]; + value.values[10] = a.values[10] | b.values[10]; + value.values[11] = a.values[11] | b.values[11]; + value.values[12] = a.values[12] | b.values[12]; + value.values[13] = a.values[13] | b.values[13]; + value.values[14] = a.values[14] | b.values[14]; + value.values[15] = a.values[15] | b.values[15]; + return value; +} + +static inline kinc_uint8x16_t kinc_uint8x16_and(kinc_uint8x16_t a, kinc_uint8x16_t b) { + kinc_uint8x16_t value; + value.values[0] = a.values[0] & b.values[0]; + value.values[1] = a.values[1] & b.values[1]; + value.values[2] = a.values[2] & b.values[2]; + value.values[3] = a.values[3] & b.values[3]; + value.values[4] = a.values[4] & b.values[4]; + value.values[5] = a.values[5] & b.values[5]; + value.values[6] = a.values[6] & b.values[6]; + value.values[7] = a.values[7] & b.values[7]; + value.values[8] = a.values[8] & b.values[8]; + value.values[9] = a.values[9] & b.values[9]; + value.values[10] = a.values[10] & b.values[10]; + value.values[11] = a.values[11] & b.values[11]; + value.values[12] = a.values[12] & b.values[12]; + value.values[13] = a.values[13] & b.values[13]; + value.values[14] = a.values[14] & b.values[14]; + value.values[15] = a.values[15] & b.values[15]; + return value; +} + +static inline kinc_uint8x16_t kinc_uint8x16_xor(kinc_uint8x16_t a, kinc_uint8x16_t b) { + kinc_uint8x16_t value; + value.values[0] = a.values[0] ^ b.values[0]; + value.values[1] = a.values[1] ^ b.values[1]; + value.values[2] = a.values[2] ^ b.values[2]; + value.values[3] = a.values[3] ^ b.values[3]; + value.values[4] = a.values[4] ^ b.values[4]; + value.values[5] = a.values[5] ^ b.values[5]; + value.values[6] = a.values[6] ^ b.values[6]; + value.values[7] = a.values[7] ^ b.values[7]; + value.values[8] = a.values[8] ^ b.values[8]; + value.values[9] = a.values[9] ^ b.values[9]; + value.values[10] = a.values[10] ^ b.values[10]; + value.values[11] = a.values[11] ^ b.values[11]; + value.values[12] = a.values[12] ^ b.values[12]; + value.values[13] = a.values[13] ^ b.values[13]; + value.values[14] = a.values[14] ^ b.values[14]; + value.values[15] = a.values[15] ^ b.values[15]; + return value; +} + +static inline kinc_uint8x16_t kinc_uint8x16_not(kinc_uint8x16_t t) { + kinc_uint8x16_t value; + value.values[0] = ~t.values[0]; + value.values[1] = ~t.values[1]; + value.values[2] = ~t.values[2]; + value.values[3] = ~t.values[3]; + value.values[4] = ~t.values[4]; + value.values[5] = ~t.values[5]; + value.values[6] = ~t.values[6]; + value.values[7] = ~t.values[7]; + value.values[8] = ~t.values[8]; + value.values[9] = ~t.values[9]; + value.values[10] = ~t.values[10]; + value.values[11] = ~t.values[11]; + value.values[12] = ~t.values[12]; + value.values[13] = ~t.values[13]; + value.values[14] = ~t.values[14]; + value.values[15] = ~t.values[15]; + return value; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/system.h b/armorcore/sources/kinc/system.h new file mode 100644 index 000000000..c518e2d6c --- /dev/null +++ b/armorcore/sources/kinc/system.h @@ -0,0 +1,662 @@ +#pragma once + +#include + +#include + +#include +#include +#include + +/*! \file system.h + \brief Provides basic system and application-management functionality which doesn't fit anywhere else. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +struct kinc_window_options; +struct kinc_framebuffer_options; + +/// +/// Initializes a Kinc application and creates an initial window for systems which support windows (systems which do not support windows are treated as if the +/// would provide a single window which cannot change). This has to be called before any other Kinc-function with the exception of the Display-API which can +/// optionally be initialized beforehand using kinc_display_init. +/// +/// The id of the initial window +int kinc_init(const char *name, int width, int height, struct kinc_window_options *win, struct kinc_framebuffer_options *frame); + +/// +/// Returns the current application name as set by kinc_init or kinc_set_application_name. +/// +/// The current name of the application +const char *kinc_application_name(void); + +/// +/// Changes the application-name that was initially set by kinc_init. +/// +/// The new application-name +void kinc_set_application_name(const char *name); + +/// +/// Returns the current width of the initial application-window which is equivalent to calling kinc_window_width(0). +/// +/// The width of the initial window +int kinc_width(void); + +/// +/// Returns the current height of the initial application-window which is equivalent to calling kinc_window_height(0). +/// +/// The height of the initial window +int kinc_height(void); + +/// +/// Instruct the system to load up the provided URL which will usually open it in the system's default browser. +/// +/// The URL to open +void kinc_load_url(const char *url); + +/// +/// Returns an ID representing the current type of target-system. +/// +/// The ID representing the target system +const char *kinc_system_id(void); + +/// +/// Returns the current system-language. +/// +/// The current system-language as a two-letter language code +const char *kinc_language(void); + +/// +/// Vibrates the whole system if supported. This is primarily supported on mobile phones but don't blame us if your computer falls over. +/// +void kinc_vibrate(int milliseconds); + +/// +/// Returns the portion of the screen which can be safely used for important content. This is mostly relevant for TVs which often scale the image by default and +/// thefore cut off some of the content. +/// +/// The safe-zone which can be multiplied with the width or height of the display to convert it to pixels +float kinc_safe_zone(void); + +/// +/// Returns whether the system itself handles configuration of the safe-zone. +/// +/// Whether the safe-zone is handlet by the syste +bool kinc_automatic_safe_zone(void); + +/// +/// Sets the safe-zone for systems which return false for kinc_automatic_safe_zone. +/// +/// The safe-zone for width and height as a ratio of the full display-resolution. +void kinc_set_safe_zone(float value); + +typedef uint64_t kinc_ticks_t; + +/// +/// Returns the frequency of system-timestamps. +/// +/// The frequency of the system's timestamps in 1 / second +double kinc_frequency(void); + +/// +/// Returns a timestamp for right now in a system-specific unit. +/// +/// The current timestamp +kinc_ticks_t kinc_timestamp(void); + +/// +/// Returns the number of proper CPU-cores (not the number of hardware-threads) +/// +/// Number of cores +int kinc_cpu_cores(void); + +/// +/// Returns the number of hardware-threads +/// +/// Number of hardware-threads +int kinc_hardware_threads(void); + +/// +/// Returns the current time. This can also be calculated ala kinc_timestamp() / kinc_frequency() but kinc_time is a little more precise on some systems. +/// +/// The current time in seconds +double kinc_time(void); + +/// +/// Starts Kinc's main-loop. kinc_set_update_callback should be called before kinc_start so the main-loop actually has something to do. +/// +void kinc_start(void); + +/// +/// Stops Kinc's main loop and thereby returns to the function which called kinc_start. +/// +void kinc_stop(void); + +/// +/// Instructs the system to login a user if that is supported. +/// +void kinc_login(void); + +/// +/// Returns true if kinc_login was called and the login-process is still ongoing. +/// +/// Whether a login-process is still in progress +bool kinc_waiting_for_login(void); + +/// +/// Unlocks an achievement or trophy or however you prefer to call it. +/// +/// The id of the achievement/tropy +void kinc_unlock_achievement(int id); + +/// +/// Disallows the system to logout the current user. +/// +void kinc_disallow_user_change(void); + +/// +/// Allows the system to logout the current user. +/// +void kinc_allow_user_change(void); + +/// +/// Instructs the system whether it is allowed to turn off the screen while the application is running. +/// +/// Whether turning off the screen is allowed +void kinc_set_keep_screen_on(bool on); + +/// +/// Tries to halt program-execution in an attached debugger when compiled in debug-mode (aka when NDEBUG is not defined). +/// +KINC_INLINE void kinc_debug_break(void) { +#ifndef NDEBUG +#if defined(_MSC_VER) + __debugbreak(); +#elif defined(__clang__) + __builtin_debugtrap(); +#else +#if defined(__aarch64__) + __asm__ volatile(".inst 0xd4200000"); +#elif defined(__x86_64__) + __asm__ volatile("int $0x03"); +#else + kinc_log(KINC_LOG_LEVEL_WARNING, "Oh no, kinc_debug_break is not implemented for the current compiler and CPU."); +#endif +#endif +#endif +} + +/// +/// Returns whether a debugger is currently attached to the running program. This is not yet working though. +/// +/// Whether a debugger is currently attached +bool kinc_debugger_attached(void); + +/// +/// Copies the provided string to the system's clipboard. +/// +/// The text to be copied into the clipboard +void kinc_copy_to_clipboard(const char *text); + +/// +/// Sets the update-callback which drives the application and is called for every frame. +/// +/// The callback +/// Arbitrary data-pointer that's passed to the callback +void kinc_set_update_callback(void (*callback)(void *), void *data); + +/// +/// Sets a callback which is called whenever the application is brought to the foreground. +/// +/// The foreground-callback +/// Arbitrary data-pointer that's passed to the callback +void kinc_set_foreground_callback(void (*callback)(void *), void *data); + +/// +/// Sets a callback which is called whenever the application was paused and is being resumed. +/// +/// The resume-callback +/// Arbitrary data-pointer that's passed to the callback +void kinc_set_resume_callback(void (*callback)(void *), void *data); + +/// +/// Sets a callback which is called whenever the application is paused. +/// +/// The pause-callback +/// Arbitrary data-pointer that's passed to the callback +void kinc_set_pause_callback(void (*callback)(void *), void *data); + +/// +/// Sets a callback which is called whenever the application is brought to the background. +/// +/// The background-callback +/// Arbitrary data-pointer that's passed to the callback +void kinc_set_background_callback(void (*callback)(void *), void *data); + +/// +/// Sets a callback which is called whenever the application is about to shut down. +/// +/// The shutdown-callback +/// Arbitrary data-pointer that's passed to the callback +void kinc_set_shutdown_callback(void (*callback)(void *), void *data); + +/// +/// Sets a callback which is called when files are dropped on the application-window. +/// +/// The drop-files-callback +/// Arbitrary data-pointer that's passed to the callback +void kinc_set_drop_files_callback(void (*callback)(wchar_t *, void *), void *data); + +/// +/// Sets a callback which is called when the application is instructed to cut, typically via ctrl+x or cmd+x. +/// Kinc does not take ownership of the provided string. +/// +/// The cut-callback +/// Arbitrary data-pointer that's passed to the callback +void kinc_set_cut_callback(char *(*callback)(void *), void *data); + +/// +/// Sets a callback which is called when the application is instructed to copy, typically via ctrl+c or cmd+c. +/// Kinc does not take ownership of the provided string. +/// +/// The copy-callback +/// Arbitrary data-pointer that's passed to the callback +void kinc_set_copy_callback(char *(*callback)(void *), void *data); + +/// +/// Sets a callback which is called when the application is instructed to paste, typically via ctrl+v or cmd+v. +/// The provided string is only valid during the callback-call - copy it if you want to keep it. +/// +/// The paste-callback +/// Arbitrary data-pointer that's passed to the callback +void kinc_set_paste_callback(void (*callback)(char *, void *), void *data); + +/// +/// Sets a callback which is called when a user logs in. +/// +/// The login-callback +/// Arbitrary data-pointer that's passed to the callback +void kinc_set_login_callback(void (*callback)(void *), void *data); + +/// +/// Sets a callback which is called when a user logs out. +/// +/// The logout-callback +/// Arbitrary data-pointer that's passed to the callback +void kinc_set_logout_callback(void (*callback)(void *), void *data); + +bool kinc_internal_frame(void); +const char *kinc_internal_save_path(void); +bool kinc_internal_handle_messages(void); +void kinc_internal_shutdown(void); +void kinc_internal_update_callback(void); +void kinc_internal_foreground_callback(void); +void kinc_internal_resume_callback(void); +void kinc_internal_pause_callback(void); +void kinc_internal_background_callback(void); +void kinc_internal_shutdown_callback(void); +void kinc_internal_drop_files_callback(wchar_t *); +char *kinc_internal_cut_callback(void); +char *kinc_internal_copy_callback(void); +void kinc_internal_paste_callback(char *); +void kinc_internal_login_callback(void); +void kinc_internal_logout_callback(void); + +#ifdef KINC_IMPLEMENTATION_ROOT +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#ifdef KINC_IMPLEMENTATION_ROOT +#undef KINC_IMPLEMENTATION +#endif +#include +#ifdef KINC_IMPLEMENTATION_ROOT +#define KINC_IMPLEMENTATION +#endif + +#undef KINC_IMPLEMENTATION +#include +#include +#define KINC_IMPLEMENTATION + +#include +#include + +#if !defined(KINC_WASM) && !defined(KINC_EMSCRIPTEN) && !defined(KINC_ANDROID) && !defined(KINC_WINDOWS) && !defined(KINC_CONSOLE) +double kinc_time(void) { + return kinc_timestamp() / kinc_frequency(); +} +#endif + +static void (*update_callback)(void *) = NULL; +static void *update_callback_data = NULL; +static void (*foreground_callback)(void *) = NULL; +static void *foreground_callback_data = NULL; +static void (*background_callback)(void *) = NULL; +static void *background_callback_data = NULL; +static void (*pause_callback)(void *) = NULL; +static void *pause_callback_data = NULL; +static void (*resume_callback)(void *) = NULL; +static void *resume_callback_data = NULL; +static void (*shutdown_callback)(void *) = NULL; +static void *shutdown_callback_data = NULL; +static void (*drop_files_callback)(wchar_t *, void *) = NULL; +static void *drop_files_callback_data = NULL; +static char *(*cut_callback)(void *) = NULL; +static void *cut_callback_data = NULL; +static char *(*copy_callback)(void *) = NULL; +static void *copy_callback_data = NULL; +static void (*paste_callback)(char *, void *) = NULL; +static void *paste_callback_data = NULL; +static void (*login_callback)(void *) = NULL; +static void *login_callback_data = NULL; +static void (*logout_callback)(void *) = NULL; +static void *logout_callback_data = NULL; + +#if defined(KINC_IOS) || defined(KINC_MACOS) +bool withAutoreleasepool(bool (*f)(void)); +#endif + +void kinc_set_update_callback(void (*callback)(void *), void *data) { + update_callback = callback; + update_callback_data = data; +} + +void kinc_set_foreground_callback(void (*callback)(void *), void *data) { + foreground_callback = callback; + foreground_callback_data = data; +} + +void kinc_set_resume_callback(void (*callback)(void *), void *data) { + resume_callback = callback; + resume_callback_data = data; +} + +void kinc_set_pause_callback(void (*callback)(void *), void *data) { + pause_callback = callback; + pause_callback_data = data; +} + +void kinc_set_background_callback(void (*callback)(void *), void *data) { + background_callback = callback; + background_callback_data = data; +} + +void kinc_set_shutdown_callback(void (*callback)(void *), void *data) { + shutdown_callback = callback; + shutdown_callback_data = data; +} + +void kinc_set_drop_files_callback(void (*callback)(wchar_t *, void *), void *data) { + drop_files_callback = callback; + drop_files_callback_data = data; +} + +void kinc_set_cut_callback(char *(*callback)(void *), void *data) { + cut_callback = callback; + cut_callback_data = data; +} + +void kinc_set_copy_callback(char *(*callback)(void *), void *data) { + copy_callback = callback; + copy_callback_data = data; +} + +void kinc_set_paste_callback(void (*callback)(char *, void *), void *data) { + paste_callback = callback; + paste_callback_data = data; +} + +void kinc_set_login_callback(void (*callback)(void *), void *data) { + login_callback = callback; + login_callback_data = data; +} + +void kinc_set_logout_callback(void (*callback)(void *), void *data) { + logout_callback = callback; + logout_callback_data = data; +} + +void kinc_internal_update_callback(void) { + if (update_callback != NULL) { + update_callback(update_callback_data); + } +} + +void kinc_internal_foreground_callback(void) { + if (foreground_callback != NULL) { + foreground_callback(foreground_callback_data); + } +} + +void kinc_internal_resume_callback(void) { + if (resume_callback != NULL) { + resume_callback(resume_callback_data); + } +} + +void kinc_internal_pause_callback(void) { + if (pause_callback != NULL) { + pause_callback(pause_callback_data); + } +} + +void kinc_internal_background_callback(void) { + if (background_callback != NULL) { + background_callback(background_callback_data); + } +} + +void kinc_internal_shutdown_callback(void) { + if (shutdown_callback != NULL) { + shutdown_callback(shutdown_callback_data); + } +} + +void kinc_internal_drop_files_callback(wchar_t *filePath) { + if (drop_files_callback != NULL) { + drop_files_callback(filePath, drop_files_callback_data); + } +} + +char *kinc_internal_cut_callback(void) { + if (cut_callback != NULL) { + return cut_callback(cut_callback_data); + } + return NULL; +} + +char *kinc_internal_copy_callback(void) { + if (copy_callback != NULL) { + return copy_callback(copy_callback_data); + } + return NULL; +} + +void kinc_internal_paste_callback(char *value) { + if (paste_callback != NULL) { + paste_callback(value, paste_callback_data); + } +} + +void kinc_internal_login_callback(void) { + if (login_callback != NULL) { + login_callback(login_callback_data); + } +} + +void kinc_internal_logout_callback(void) { + if (logout_callback != NULL) { + logout_callback(logout_callback_data); + } +} + +static bool running = false; +// static bool showWindowFlag = true; +static char application_name[1024] = {"Kinc Application"}; + +const char *kinc_application_name(void) { + return application_name; +} + +void kinc_set_application_name(const char *name) { + strcpy(application_name, name); +} + +void kinc_stop(void) { + running = false; + + // TODO (DK) destroy graphics + windows, but afaik Application::~Application() was never called, so it's the same behavior now as well + + // for (int windowIndex = 0; windowIndex < sizeof(windowIds) / sizeof(int); ++windowIndex) { + // Graphics::destroy(windowIndex); + //} +} + +bool kinc_internal_frame(void) { + kinc_internal_update_callback(); + kinc_internal_handle_messages(); + return running; +} + +void kinc_start(void) { + running = true; + +#if !defined(KINC_WASM) && !defined(KINC_EMSCRIPTEN) + // if (Graphics::hasWindow()) Graphics::swapBuffers(); + +#if defined(KINC_IOS) || defined(KINC_MACOS) + while (withAutoreleasepool(kinc_internal_frame)) { + } +#else + while (kinc_internal_frame()) { + } +#endif + kinc_internal_shutdown(); +#endif +} + +int kinc_width(void) { + return kinc_window_width(0); +} + +int kinc_height(void) { + return kinc_window_height(0); +} + +#ifndef KHA +void kinc_memory_emergency(void) {} +#endif + +#if !defined(KINC_SONY) && !defined(KINC_SWITCH) +static float safe_zone = 0.9f; + +float kinc_safe_zone(void) { +#ifdef KINC_ANDROID + return 1.0f; +#else + return safe_zone; +#endif +} + +bool kinc_automatic_safe_zone(void) { +#ifdef KINC_ANDROID + return true; +#else + return false; +#endif +} + +void kinc_set_safe_zone(float value) { + safe_zone = value; +} +#endif + +#if !defined(KINC_SONY) +bool is_save_load_initialized(void) { + return true; +} + +bool is_ps4_japanese_button_style(void) { + return false; +} + +bool is_save_load_broken(void) { + return false; +} +#endif + +#if !defined(KINC_CONSOLE) + +#define SAVE_RESULT_NONE 0 +#define SAVE_RESULT_SUCCESS 1 +#define SAVE_RESULT_FAILURE 2 +volatile int save_result = SAVE_RESULT_SUCCESS; + +void kinc_disallow_user_change(void) {} + +void kinc_allow_user_change(void) {} + +static uint8_t *current_file = NULL; +static size_t current_file_size = 0; + +bool kinc_save_file_loaded(void) { + return true; +} + +uint8_t *kinc_get_save_file(void) { + return current_file; +} + +size_t kinc_get_save_file_size(void) { + return current_file_size; +} + +void kinc_load_save_file(const char *filename) { + free(current_file); + current_file = NULL; + current_file_size = 0; + + kinc_file_reader_t reader; + if (kinc_file_reader_open(&reader, filename, KINC_FILE_TYPE_SAVE)) { + current_file_size = kinc_file_reader_size(&reader); + current_file = (uint8_t *)malloc(current_file_size); + kinc_file_reader_read(&reader, current_file, current_file_size); + kinc_file_reader_close(&reader); + } +} + +void kinc_save_save_file(const char *filename, uint8_t *data, size_t size) { + kinc_file_writer_t writer; + if (kinc_file_writer_open(&writer, filename)) { + kinc_file_writer_write(&writer, data, (int)size); + kinc_file_writer_close(&writer); + } +} + +bool kinc_save_is_saving(void) { + return false; +} + +bool kinc_waiting_for_login(void) { + return false; +} + +#if !defined(KINC_WINDOWS) && !defined(KINC_LINUX) && !defined(KINC_MACOS) +void kinc_copy_to_clipboard(const char *text) { + kinc_log(KINC_LOG_LEVEL_WARNING, "Oh no, kinc_copy_to_clipboard is not implemented for this system."); +} +#endif + +#endif + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/threads/atomic.h b/armorcore/sources/kinc/threads/atomic.h new file mode 100644 index 000000000..8560d6f77 --- /dev/null +++ b/armorcore/sources/kinc/threads/atomic.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#include + +/*! \file atomic.h + \brief Provides atomics aka interlocked operations. +*/ diff --git a/armorcore/sources/kinc/threads/event.h b/armorcore/sources/kinc/threads/event.h new file mode 100644 index 000000000..f236382a9 --- /dev/null +++ b/armorcore/sources/kinc/threads/event.h @@ -0,0 +1,62 @@ +#pragma once + +#include + +#include + +#include + +/*! \file event.h + \brief An event is a simple threading-object that allows a thread to wait for something to happen on another thread. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_event { + kinc_event_impl_t impl; +} kinc_event_t; + +/// +/// Initializes an event-object. +/// +/// The event to initialize +/// When auto-clear is true, the event is automatically reset to an unsignaled state after a successful wait-operation +void kinc_event_init(kinc_event_t *event, bool auto_clear); + +/// +/// Destroys an event-object. +/// +/// The event to destroy +void kinc_event_destroy(kinc_event_t *event); + +/// +/// Signals an event which allows threads which are waiting for the event to continue. +/// +/// The event to signal +void kinc_event_signal(kinc_event_t *event); + +/// +/// Waits for an event to be signaled. +/// +/// The event to wait for +void kinc_event_wait(kinc_event_t *event); + +/// +/// Waits for an event to be signaled or the provided timeout to run out - whatever happens first. +/// +/// The event to wait for +/// The timeout in seconds after which the function returns if the event hasn't been signaled +/// Whether the event has been signaled +bool kinc_event_try_to_wait(kinc_event_t *event, double timeout); + +/// +/// Resets an event to an unsignaled state. +/// +/// The event to reset +void kinc_event_reset(kinc_event_t *event); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/threads/fiber.h b/armorcore/sources/kinc/threads/fiber.h new file mode 100644 index 000000000..abc8400f7 --- /dev/null +++ b/armorcore/sources/kinc/threads/fiber.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include + +/*! \file fiber.h + \brief The fiber-API is experimental and only supported on a few system. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_fiber { + kinc_fiber_impl_t impl; +} kinc_fiber_t; + +/// +/// Uses the current thread as a fiber. +/// +/// The fiber-object to initialize using the current thread +void kinc_fiber_init_current_thread(kinc_fiber_t *fiber); + +/// +/// Initializes a fiber. +/// +/// The fiber-object to initialize +/// The function to be run in the fiber-context +/// A parameter to be provided to the fiber-function when it starts running +void kinc_fiber_init(kinc_fiber_t *fiber, void (*func)(void *param), void *param); + +/// +/// Destroys a fiber. +/// +/// The fiber to destroy +void kinc_fiber_destroy(kinc_fiber_t *fiber); + +/// +/// Switch the current thread to a different fiber. +/// +/// The fiber to switch to +void kinc_fiber_switch(kinc_fiber_t *fiber); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/threads/mutex.h b/armorcore/sources/kinc/threads/mutex.h new file mode 100644 index 000000000..b32540911 --- /dev/null +++ b/armorcore/sources/kinc/threads/mutex.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +#include + +#include + +/*! \file mutex.h + \brief Provides mutexes which are used to synchronize threads and uber-mutexes which are used to synchronize processes. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_mutex { + kinc_mutex_impl_t impl; +} kinc_mutex_t; + +/// +/// Initializes a mutex-object. +/// +/// The mutex to initialize +void kinc_mutex_init(kinc_mutex_t *mutex); + +/// +/// Destroys a mutex-object. +/// +/// The mutex to destroy +void kinc_mutex_destroy(kinc_mutex_t *mutex); + +/// +/// Locks a mutex. A mutex can only be locked from one thread - when other threads attempt to lock the mutex the function will only return once the mutex has +/// been unlocked. +/// +/// The mutex to lock +void kinc_mutex_lock(kinc_mutex_t *mutex); + +/// +/// Attempts to lock the mutex which will only succeed if no other thread currently holds the lock. +/// +/// The mutex to lock +/// Whether the mutex could be locked +bool kinc_mutex_try_to_lock(kinc_mutex_t *mutex); + +/// +/// Unlocks the mutex which then allows other threads to lock it. +/// +/// The mutex to unlock +void kinc_mutex_unlock(kinc_mutex_t *mutex); + +typedef struct kinc_uber_mutex { + kinc_uber_mutex_impl_t impl; +} kinc_uber_mutex_t; + +/// +/// Initializes an uber-mutex-object. +/// +/// The uber-mutex to initialize +/// A name assigned to the uber-mutex - uber-mutex-creation fails if an uber-mutex of that name already exists +/// Whether the uber-mutex could be created +bool kinc_uber_mutex_init(kinc_uber_mutex_t *mutex, const char *name); + +/// +/// Destroys an uber-mutex-obejct. +/// +/// The uber-mutex to destroy +void kinc_uber_mutex_destroy(kinc_uber_mutex_t *mutex); + +/// +/// Locks an uber-mutex. +/// +/// The uber-mutex to lock +void kinc_uber_mutex_lock(kinc_uber_mutex_t *mutex); + +/// +/// Unlocks an uber-mutex. +/// +/// The uber-mutex to unlock +/// +void kinc_uber_mutex_unlock(kinc_uber_mutex_t *mutex); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/threads/semaphore.h b/armorcore/sources/kinc/threads/semaphore.h new file mode 100644 index 000000000..f415e4926 --- /dev/null +++ b/armorcore/sources/kinc/threads/semaphore.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include + +#include + +/*! \file semaphore.h + \brief A semaphore is a fancier version of an event that includes a counter to control how many threads are allowed to work on a task. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_semaphore { + kinc_semaphore_impl_t impl; +} kinc_semaphore_t; + +/// +/// Initializes a semaphore. +/// +/// The semaphore to initialize +/// The current count of the semaphore +/// The maximum allowed count of the semaphore +void kinc_semaphore_init(kinc_semaphore_t *semaphore, int current, int max); + +/// +/// Destroys a semaphore. +/// +/// The semaphore to destroy +void kinc_semaphore_destroy(kinc_semaphore_t *semaphore); + +/// +/// Increases the current count of the semaphore, therefore allowing more acquires to succeed. +/// +/// The semaphore to increase the count on +/// The amount by which the count will be increased +void kinc_semaphore_release(kinc_semaphore_t *semaphore, int count); + +/// +/// Decreases the count of the semaphore by one. Blocks until it is possible to decrease the count if it already reached zero. +/// +/// The semaphore to acquire +void kinc_semaphore_acquire(kinc_semaphore_t *semaphore); + +/// +/// Attempts to decrease the count of the semaphore by one. +/// +/// The semaphore to acquire +/// The timeout in seconds after which the function returns if the semaphore-count could not be decreased +/// Whether the semaphore-count could be decreased +bool kinc_semaphore_try_to_acquire(kinc_semaphore_t *semaphore, double timeout); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/threads/thread.h b/armorcore/sources/kinc/threads/thread.h new file mode 100644 index 000000000..ae6d7cec5 --- /dev/null +++ b/armorcore/sources/kinc/threads/thread.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +#include + +/*! \file thread.h + \brief Supports the creation and destruction of threads. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_thread { + kinc_thread_impl_t impl; +} kinc_thread_t; + +/// +/// Initializes the threading system. This has to be called before calling any kinc_thread functions. +/// +void kinc_threads_init(void); + +/// +/// Shuts down the threading system. +/// +/// +/// +void kinc_threads_quit(void); + +/// +/// Starts a thread via the provided function. +/// +/// The thread-object to initialize with the new thread +/// The function used to start a new thread +/// A parameter that is passed to the thread-function when the thread starts running +void kinc_thread_init(kinc_thread_t *thread, void (*func)(void *param), void *param); + +/// +/// Waits for the thread to complete execution and then destroys it. +/// +/// The thread to destroy +void kinc_thread_wait_and_destroy(kinc_thread_t *thread); + +/// +/// Attempts to destroy a thread. +/// +/// The thread to destroy +/// Returns if the thread is still running and therefore couldn't be destroyed +bool kinc_thread_try_to_destroy(kinc_thread_t *thread); + +/// +/// Assigns a name to the current thread which will then show up in debuggers and profilers. +/// +/// The name to assign to the thread +void kinc_thread_set_name(const char *name); + +/// +/// Puts the current thread to sleep. +/// +/// How long to sleep +void kinc_thread_sleep(int milliseconds); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/threads/threadlocal.h b/armorcore/sources/kinc/threads/threadlocal.h new file mode 100644 index 000000000..08e5eb030 --- /dev/null +++ b/armorcore/sources/kinc/threads/threadlocal.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include + +/*! \file threadlocal.h + \brief Provides storage-slots for thread-specific data. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_thread_local { + kinc_thread_local_impl_t impl; +} kinc_thread_local_t; + +/// +/// Initializes a thread-specific storage-slot. +/// +/// The storage-slot to initialize +void kinc_thread_local_init(kinc_thread_local_t *local); + +/// +/// Destroys a storage-slot. +/// +/// The storage-slot to destroy +void kinc_thread_local_destroy(kinc_thread_local_t *local); + +/// +/// Gets the data in the storage-slot. +/// +/// The slot to query +void *kinc_thread_local_get(kinc_thread_local_t *local); + +/// +/// Sets the data in the storage-slot. +/// +/// The slot to put the data into +/// The data to put in the slot +void kinc_thread_local_set(kinc_thread_local_t *local, void *data); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/video.h b/armorcore/sources/kinc/video.h new file mode 100644 index 000000000..a19759614 --- /dev/null +++ b/armorcore/sources/kinc/video.h @@ -0,0 +1,102 @@ +#pragma once + +#include + +#include +#include + +/*! \file video.h + \brief Hardware-assisted video decoding support. Actually supported + formats vary per target-system. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_video { + kinc_video_impl_t impl; +} kinc_video_t; + +/// +/// Returns the list of video formats which are supported on the current system. +/// +/// A NULL-terminated list of strings representing the supported video-formats +const char **kinc_video_formats(void); + +/// +/// Prepares a video object based on an encoded video-file. +/// +void kinc_video_init(kinc_video_t *video, const char *filename); + +/// +/// Destroys a video object. +/// +void kinc_video_destroy(kinc_video_t *video); + +/// +/// Starts playing a video. +/// +void kinc_video_play(kinc_video_t *video, bool loop); + +/// +/// Pauses a video. +/// +void kinc_video_pause(kinc_video_t *video); + +/// +/// Stops a video which is equivalent to pausing it and setting the position to 0. +/// +void kinc_video_stop(kinc_video_t *video); + +/// +/// Gets the width of the video in pixels. +/// +/// The width of the video in pixels +int kinc_video_width(kinc_video_t *video); + +/// +/// Gets the height of the video in pixels. +/// +/// The height of the video in pixels +int kinc_video_height(kinc_video_t *video); + +/// +/// Gets the current image of a playing video which can be rendered using any of Kinc's graphics APIs. +/// +/// The current image of a playing video +kinc_g4_texture_t *kinc_video_current_image(kinc_video_t *video); + +/// +/// Gets the duration of the video in seconds. +/// +/// The duration of the video in seconds +double kinc_video_duration(kinc_video_t *video); + +/// +/// Returns the current playback-position of the video in seconds. +/// +/// The current playback-position in seconds +double kinc_video_position(kinc_video_t *video); + +/// +/// Returns whether the video reached its end. +/// +/// the end-state of the video +bool kinc_video_finished(kinc_video_t *video); + +/// +/// Returns whether the video is currently paused. +/// +/// The current pause state of the video +bool kinc_video_paused(kinc_video_t *video); + +/// +/// Call this every frame to update the video. This is not required on all targets but where it's not required the function just does nothing - so please call +/// it. +/// +void kinc_video_update(kinc_video_t *video, double time); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/kinc/window.h b/armorcore/sources/kinc/window.h new file mode 100644 index 000000000..698fc5673 --- /dev/null +++ b/armorcore/sources/kinc/window.h @@ -0,0 +1,215 @@ +#pragma once + +#include + +#include + +/*! \file window.h + \brief Provides functionality for creating and handling windows. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kinc_framebuffer_options { + int frequency; + bool vertical_sync; + int color_bits; + int depth_bits; + int stencil_bits; + int samples_per_pixel; +} kinc_framebuffer_options_t; + +typedef enum { + KINC_WINDOW_MODE_WINDOW, + KINC_WINDOW_MODE_FULLSCREEN, + KINC_WINDOW_MODE_EXCLUSIVE_FULLSCREEN // Only relevant for Windows +} kinc_window_mode_t; + +#define KINC_WINDOW_FEATURE_RESIZEABLE 1 +#define KINC_WINDOW_FEATURE_MINIMIZABLE 2 +#define KINC_WINDOW_FEATURE_MAXIMIZABLE 4 +#define KINC_WINDOW_FEATURE_BORDERLESS 8 +#define KINC_WINDOW_FEATURE_ON_TOP 16 + +typedef struct kinc_window_options { + const char *title; + + int x; + int y; + int width; + int height; + int display_index; + + bool visible; + int window_features; + kinc_window_mode_t mode; +} kinc_window_options_t; + +/// +/// Creates a window. +/// +/// The id of the created window +int kinc_window_create(kinc_window_options_t *win, kinc_framebuffer_options_t *frame); + +/// +/// Closes and destroys a window. +/// +void kinc_window_destroy(int window); + +void kinc_window_options_set_defaults(kinc_window_options_t *win); + +void kinc_framebuffer_options_set_defaults(kinc_framebuffer_options_t *frame); + +/// +/// Counts all windows the program created, including the initial window created by kinc_init. +/// +/// The number of windows +int kinc_count_windows(void); + +/// +/// Resizes a window. +/// +void kinc_window_resize(int window, int width, int height); + +/// +/// Moves a window. +/// +void kinc_window_move(int window, int x, int y); + +/// +/// Switches between window and fullscreen-modes. +/// +void kinc_window_change_mode(int window, kinc_window_mode_t mode); + +/// +/// Applies an or-ed combination of KINC_WINDOW_FEATURE values. +/// +void kinc_window_change_features(int window, int features); + +/// +/// Changes the framebuffer-options of a window. +/// +void kinc_window_change_framebuffer(int window, kinc_framebuffer_options_t *frame); + +/// +/// Returns the x position of a window. +/// +/// The x-position +int kinc_window_x(int window); + +/// +/// Returns the y position of a window. +/// +/// The y-position +int kinc_window_y(int window); + +/// +/// Returns the width of a window. +/// +/// The window-width +int kinc_window_width(int window); + +/// +/// Returns the height of a window. +/// +/// The window-height +int kinc_window_height(int window); + +/// +/// Returns the id of the display the window currently is on. +/// +/// The display-id +int kinc_window_display(int window); + +/// +/// Returns the current window-mode. +/// +/// The window-mode +kinc_window_mode_t kinc_window_get_mode(int window); + +/// +/// Makes the window visible. +/// +void kinc_window_show(int window); + +/// +/// Hides a window. +/// +void kinc_window_hide(int window); + +/// +/// Sets the title of a window. +/// +void kinc_window_set_title(int window, const char *title); + +/// +/// Sets a resize callback that's called whenever the window is resized. +/// +void kinc_window_set_resize_callback(int window, void (*callback)(int x, int y, void *data), void *data); + +/// +/// Sets a PPI callback that's called whenever the window moves to a display that uses a different PPI-setting. +/// +void kinc_window_set_ppi_changed_callback(int window, void (*callback)(int ppi, void *data), void *data); + +/// +/// Sets a close callback that's called when the window is about to close. +/// Returning false from the callback tries to stop the window from closing. +/// +void kinc_window_set_close_callback(int window, bool (*callback)(void *data), void *data); + +/// +/// Returns whether the window is vsynced or not. +/// +/// Whether the window is vsynced or not +bool kinc_window_vsynced(int window); + +void kinc_internal_call_resize_callback(int window, int width, int height); +void kinc_internal_call_ppi_changed_callback(int window, int ppi); +bool kinc_internal_call_close_callback(int window); + +#ifdef KINC_IMPLEMENTATION_ROOT +#define KINC_IMPLEMENTATION +#endif + +#ifdef KINC_IMPLEMENTATION + +#ifdef KINC_IMPLEMENTATION_ROOT +#undef KINC_IMPLEMENTATION +#endif +#include +#ifdef KINC_IMPLEMENTATION_ROOT +#define KINC_IMPLEMENTATION +#endif + +#include + +void kinc_window_options_set_defaults(kinc_window_options_t *win) { + kinc_display_init(); + win->title = NULL; + win->display_index = kinc_primary_display(); + win->mode = KINC_WINDOW_MODE_WINDOW; + win->x = -1; + win->y = -1; + win->width = 800; + win->height = 600; + win->visible = true; + win->window_features = KINC_WINDOW_FEATURE_RESIZEABLE | KINC_WINDOW_FEATURE_MINIMIZABLE | KINC_WINDOW_FEATURE_MAXIMIZABLE; +} + +void kinc_framebuffer_options_set_defaults(kinc_framebuffer_options_t *frame) { + frame->frequency = 60; + frame->vertical_sync = true; + frame->color_bits = 32; + frame->depth_bits = 16; + frame->stencil_bits = 8; + frame->samples_per_pixel = 1; +} + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/libs/dir.c b/armorcore/sources/libs/dir.c new file mode 100644 index 000000000..fe446b7b1 --- /dev/null +++ b/armorcore/sources/libs/dir.c @@ -0,0 +1,94 @@ +// Copyright (c) 2022 the Kongruent development team + +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. + +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: + +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. + +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. + +// 3. This notice may not be removed or altered from any source distribution. + +#include "dir.h" +#include +#include + +#ifdef _WIN32 + +#include + +directory open_dir(const char *dirname) { + char pattern[1024]; + strcpy(pattern, dirname); + strcat(pattern, "\\*"); + + WIN32_FIND_DATAA data; + directory dir; + dir.handle = FindFirstFileA(pattern, &data); + if (dir.handle == INVALID_HANDLE_VALUE) { + return dir; + } + FindNextFileA(dir.handle, &data); + return dir; +} + +file read_next_file(directory *dir) { + WIN32_FIND_DATAA data; + file file; + file.valid = FindNextFileA(dir->handle, &data) != 0; + if (file.valid) { + strcpy(file.name, data.cFileName); + } + else { + file.name[0] = 0; + } + return file; +} + +void close_dir(directory *dir) { + FindClose(dir->handle); +} + +#else + +#include + +#include +#include +#include + +directory open_dir(const char *dirname) { + directory dir; + dir.handle = opendir(dirname); + return dir; +} + +file read_next_file(directory *dir) { + struct dirent *entry = readdir(dir->handle); + + while (entry != NULL && (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)) { + entry = readdir(dir->handle); + } + + file f; + f.valid = entry != NULL; + + if (f.valid) { + strcpy(f.name, entry->d_name); + } + + return f; +} + +void close_dir(directory *dir) {} + +#endif diff --git a/armorcore/sources/libs/dir.h b/armorcore/sources/libs/dir.h new file mode 100644 index 000000000..d6457e8e6 --- /dev/null +++ b/armorcore/sources/libs/dir.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +typedef struct directory { + void *handle; +} directory; + +typedef struct file { + bool valid; + char name[256]; +} file; + +directory open_dir(const char *dirname); +file read_next_file(directory *dir); +void close_dir(directory *dir); diff --git a/armorcore/sources/libs/gc.c b/armorcore/sources/libs/gc.c new file mode 100644 index 000000000..718e45887 --- /dev/null +++ b/armorcore/sources/libs/gc.c @@ -0,0 +1,503 @@ +// MIT License + +// Copyright (c) 2019 Marc Kirchner + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// https://github.com/mkirchner/gc + +#include "gc.h" + +#include +#include +#include +// #include +// #include + +#define PTRSIZE sizeof(char *) + +#define GC_TAG_NONE 0x0 +#define GC_TAG_LEAF 0x1 +#define GC_TAG_MARK 0x2 +#define GC_TAG_ROOT 0x4 +#define GC_TAG_CUT 0x8 + +#if defined(_MSC_VER) && !defined(__clang__) +#define __builtin_frame_address(x) ((void)(x), _AddressOfReturnAddress()) +#endif + +typedef struct gc_allocation { + void *ptr; // mem pointer + size_t size; // allocated size in bytes + int *array_length; // if this alloc is an array + char tag; // the tag for mark-and-sweep + char roots; // number of root users + struct gc_allocation *next; // separate chaining + struct gc_allocation *cut; +} gc_allocation_t; + +typedef struct gc_allocation_map { + size_t capacity; + size_t min_capacity; + double downsize_factor; + double upsize_factor; + double sweep_factor; + size_t sweep_limit; + size_t size; + gc_allocation_t **allocs; +} gc_allocation_map_t; + +typedef struct garbage_collector { + struct gc_allocation_map *allocs; // allocation map + // struct gc_allocation_map *roots; // root map + bool paused; // (temporarily) switch gc on/off + void *bos; // bottom of stack +} garbage_collector_t; + +garbage_collector_t _gc; +garbage_collector_t *gc = &_gc; + +static gc_allocation_t *gc_allocation_new(void *ptr, size_t size) { + gc_allocation_t *a = (gc_allocation_t *)malloc(sizeof(gc_allocation_t)); + a->ptr = ptr; + a->size = size; + a->array_length = NULL; + a->tag = GC_TAG_NONE; + a->roots = 0; + a->next = NULL; + a->cut = NULL; + return a; +} + +static void gc_allocation_delete(gc_allocation_t *a) { + free(a); +} + +static gc_allocation_map_t *gc_allocation_map_new(size_t min_capacity, size_t capacity, + double sweep_factor, double downsize_factor, double upsize_factor) { + gc_allocation_map_t *am = (gc_allocation_map_t *)malloc(sizeof(gc_allocation_map_t)); + am->min_capacity = min_capacity; + am->capacity = capacity; + am->sweep_factor = sweep_factor; + am->sweep_limit = (int)(sweep_factor * am->capacity); + am->downsize_factor = downsize_factor; + am->upsize_factor = upsize_factor; + am->allocs = (gc_allocation_t **)calloc(am->capacity, sizeof(gc_allocation_t *)); + am->size = 0; + return am; +} + +static void gc_allocation_map_delete(gc_allocation_map_t *am) { + // Iterate over the map + gc_allocation_t *alloc; + gc_allocation_t *tmp; + for (size_t i = 0; i < am->capacity; ++i) { + if ((alloc = am->allocs[i])) { + // Make sure to follow the chain inside a bucket + while (alloc) { + tmp = alloc; + alloc = alloc->next; + // free the management structure + gc_allocation_delete(tmp); + } + } + } + free(am->allocs); + free(am); +} + +static size_t gc_hash(void *ptr) { + return ((uintptr_t)ptr) >> 3; +} + +static void gc_allocation_map_resize(gc_allocation_map_t *am, size_t new_capacity) { + if (new_capacity <= am->min_capacity) { + return; + } + // Replaces the existing items array in the hash table + // with a resized one and pushes items into the new, correct buckets + gc_allocation_t **resized_allocs = calloc(new_capacity, sizeof(gc_allocation_t *)); + + for (size_t i = 0; i < am->capacity; ++i) { + gc_allocation_t *alloc = am->allocs[i]; + while (alloc) { + gc_allocation_t *next_alloc = alloc->next; + size_t new_index = gc_hash(alloc->ptr) & (new_capacity - 1); + alloc->next = resized_allocs[new_index]; + resized_allocs[new_index] = alloc; + alloc = next_alloc; + } + } + free(am->allocs); + am->capacity = new_capacity; + am->allocs = resized_allocs; + am->sweep_limit = am->size + am->sweep_factor * (am->capacity - am->size); +} + +static bool gc_allocation_map_resize_to_fit(gc_allocation_map_t *am) { + double load_factor = (double)am->size / (double)am->capacity; + if (load_factor > am->upsize_factor) { + gc_allocation_map_resize(am, am->capacity * 2); + return true; + } + if (load_factor < am->downsize_factor) { + gc_allocation_map_resize(am, am->capacity / 2); + return true; + } + return false; +} + +static gc_allocation_t *gc_allocation_map_get(gc_allocation_map_t *am, void *ptr) { + size_t index = gc_hash(ptr) & (am->capacity - 1); // % am->capacity + gc_allocation_t *cur = am->allocs[index]; + while(cur) { + if (cur->ptr == ptr) { + return cur; + } + cur = cur->next; + } + return NULL; +} + +static gc_allocation_t *gc_allocation_map_put(gc_allocation_map_t *am, gc_allocation_t *alloc) { + size_t index = gc_hash(alloc->ptr) & (am->capacity - 1); + gc_allocation_t *cur = am->allocs[index]; + gc_allocation_t *prev = NULL; + /* Upsert if ptr is already known */ + while (cur != NULL) { + if (cur->ptr == alloc->ptr) { + // found it + alloc->next = cur->next; + if (!prev) { + // position 0 + am->allocs[index] = alloc; + } + else { + // in the list + prev->next = alloc; + } + gc_allocation_delete(cur); + return alloc; + + } + prev = cur; + cur = cur->next; + } + /* Insert at the front of the separate chaining list */ + cur = am->allocs[index]; + alloc->next = cur; + am->allocs[index] = alloc; + am->size++; + void *p = alloc->ptr; + if (gc_allocation_map_resize_to_fit(am)) { + alloc = gc_allocation_map_get(am, p); + } + return alloc; +} + +static void gc_allocation_map_remove(gc_allocation_map_t *am, void *ptr, bool allow_resize) { + // ignores unknown keys + size_t index = gc_hash(ptr) & (am->capacity - 1); + gc_allocation_t *cur = am->allocs[index]; + gc_allocation_t *prev = NULL; + gc_allocation_t *next; + while (cur != NULL) { + next = cur->next; + if (cur->ptr == ptr) { + // found it + if (!prev) { + // first item in list + am->allocs[index] = cur->next; + } + else { + // not the first item in the list + prev->next = cur->next; + } + gc_allocation_delete(cur); + am->size--; + } + else { + // move on + prev = cur; + } + cur = next; + } + if (allow_resize) { + gc_allocation_map_resize_to_fit(am); + } +} + +static void* gc_mcalloc(size_t count, size_t size) { + if (!count) { + return malloc(size); + } + return calloc(count, size); +} + +static void *gc_allocate(size_t count, size_t size) { + /* Allocation logic that generalizes over malloc/calloc */ + /* Check if we reached the high-water mark and need to clean up */ + if (gc->allocs->size > gc->allocs->sweep_limit && !gc->paused) { + size_t freed_mem = _gc_run(); + } + /* With cleanup out of the way, attempt to allocate memory */ + void *ptr = gc_mcalloc(count, size); + size_t alloc_size = count ? count * size : size; + /* If allocation fails, force an out-of-policy run to free some memory and try again. */ + if (!ptr && !gc->paused) { + _gc_run(); + ptr = gc_mcalloc(count, size); + } + /* Start managing the memory we received from the system */ + if (ptr) { + gc_allocation_t *alloc = gc_allocation_map_put(gc->allocs, gc_allocation_new(ptr, alloc_size)); + /* Deal with metadata allocation failure */ + if (alloc) { + ptr = alloc->ptr; + } + else { + /* We failed to allocate the metadata, fail cleanly. */ + free(ptr); + ptr = NULL; + } + } + return ptr; +} + +void _gc_array(void *ptr, int *length) { + gc_allocation_t *alloc = gc_allocation_map_get(gc->allocs, ptr); + if (alloc) { + // alloc->array_length = length; + } +} + +void _gc_leaf(void *ptr) { + gc_allocation_t *alloc = gc_allocation_map_get(gc->allocs, ptr); + if (alloc) { + alloc->tag |= GC_TAG_LEAF; + } +} + +void _gc_root(void *ptr) { + gc_allocation_t *alloc = gc_allocation_map_get(gc->allocs, ptr); + if (alloc) { + alloc->roots++; + alloc->tag |= GC_TAG_ROOT; + } +} + +void _gc_unroot(void *ptr) { + gc_allocation_t *alloc = gc_allocation_map_get(gc->allocs, ptr); + if (alloc) { + alloc->roots--; + if (alloc->roots == 0) { + alloc->tag &= ~GC_TAG_ROOT; + } + } +} + +static inline uint64_t pad(int di, int n) { + return (n - (di % n)) % n; +} + +// Cut out a leaf out of an existing allocation +void *_gc_cut(void *ptr, size_t pos, size_t size) { + size += pad(size, 8); + + // Start + gc_allocation_t *start = gc_allocation_map_get(gc->allocs, ptr); + while (start->cut) { + start = start->cut; + } + size_t start_size = start->size; + pos -= start->ptr - ptr; + start->size = pos; + + // Middle + gc_allocation_t *middle = gc_allocation_new(start->ptr + pos, size); + middle->tag |= GC_TAG_CUT; + middle->tag |= GC_TAG_LEAF; + start->cut = middle; + gc_allocation_map_put(gc->allocs, middle); + + // End + gc_allocation_t *end = gc_allocation_new(start->ptr + pos + size, start_size - size - pos); + end->tag |= GC_TAG_CUT; + middle->cut = end; + gc_allocation_map_put(gc->allocs, end); + + return middle->ptr; +} + +static void gc_mark_alloc(void *ptr) { + gc_allocation_t *alloc = gc_allocation_map_get(gc->allocs, ptr); + /* Mark if alloc exists and is not tagged already, otherwise skip */ + if (alloc && !(alloc->tag & GC_TAG_MARK)) { + alloc->tag |= GC_TAG_MARK; + if (!(alloc->tag & GC_TAG_LEAF)) { // Skip contents + /* Iterate over allocation contents and mark them as well */ + int size = alloc->array_length ? (*alloc->array_length) * PTRSIZE : alloc->size; + + for (char *p = (char *)alloc->ptr; p <= (char *)alloc->ptr + size - PTRSIZE; ++p) { + gc_mark_alloc(*(void **)p); + } + } + } +} + +static void gc_mark_stack() { + void *tos = __builtin_frame_address(0); + void *bos = gc->bos; + /* The stack grows towards smaller memory addresses, hence we scan tos->bos. + * Stop scanning once the distance between tos & bos is too small to hold a valid pointer */ + for (char *p = (char *)tos; p <= (char *)bos - PTRSIZE; ++p) { + gc_mark_alloc(*(void **)p); + } +} + +static void gc_mark_roots() { + for (size_t i = 0; i < gc->allocs->capacity; ++i) { + gc_allocation_t *chunk = gc->allocs->allocs[i]; + while (chunk) { + if (chunk->tag & GC_TAG_ROOT) { + gc_mark_alloc(chunk->ptr); + } + chunk = chunk->next; + } + } +} + +static void gc_mark() { + /* Note: We only look at the stack and the heap, and ignore BSS. */ + /* Scan the heap for roots */ + gc_mark_roots(); + /* Dump registers onto stack and scan the stack */ + void (*volatile _mark_stack)(void) = gc_mark_stack; + jmp_buf ctx; + memset(&ctx, 0, sizeof(jmp_buf)); + setjmp(ctx); + _mark_stack(); +} + +static size_t gc_sweep() { + size_t total = 0; + for (size_t i = 0; i < gc->allocs->capacity; ++i) { + gc_allocation_t *chunk = gc->allocs->allocs[i]; + gc_allocation_t *next = NULL; + /* Iterate over separate chaining */ + while (chunk) { + if (chunk->tag & GC_TAG_MARK) { + /* unmark */ + chunk->tag &= ~GC_TAG_MARK; + chunk = chunk->next; + } + else if (chunk->tag & GC_TAG_CUT) { + chunk = chunk->next; + } + else { + /* no reference to this chunk, hence delete it */ + total += chunk->size; + free(chunk->ptr); + /* and remove it from the bookkeeping */ + next = chunk->next; + gc_allocation_t *cut = chunk->cut; + gc_allocation_map_remove(gc->allocs, chunk->ptr, false); + while (cut) { + gc_allocation_t *next = cut->cut; + gc_allocation_map_remove(gc->allocs, cut->ptr, false); + cut = next; + } + chunk = next; + } + } + } + gc_allocation_map_resize_to_fit(gc->allocs); + return total; +} + +void *_gc_calloc(size_t count, size_t size) { + return gc_allocate(count, size); +} + +void *_gc_realloc(void *p, size_t size) { + gc_allocation_t *alloc = gc_allocation_map_get(gc->allocs, p); + if (p && !alloc) { + // the user passed an unknown pointer + return NULL; + } + void *q = realloc(p, size); + if (!q) { + // realloc failed but p is still valid + return NULL; + } + if (!p) { + // allocation, not reallocation + gc_allocation_t *alloc = gc_allocation_map_put(gc->allocs, gc_allocation_new(q, size)); + return alloc->ptr; + } + if (p == q) { + // successful reallocation w/o copy + alloc->size = size; + } + else { + // successful reallocation w/ copy + gc_allocation_map_remove(gc->allocs, p, true); + gc_allocation_map_put(gc->allocs, gc_allocation_new(q, size)); + } + return q; +} + +void _gc_free(void *ptr) { + gc_allocation_t *alloc = gc_allocation_map_get(gc->allocs, ptr); + if (alloc) { + free(ptr); + gc_allocation_map_remove(gc->allocs, ptr, true); + } +} + +void _gc_start(void *bos) { + gc->paused = false; + gc->bos = bos; + // Create allocation map with no downsizing + // Capacity must be a power of two + gc->allocs = gc_allocation_map_new(1024 * 1024, 1024 * 1024, 0.5, 0.0, 0.8); + // gc->roots = gc_allocation_map_new(1024, 1024, 0.5, 0.0, 0.8); +} + +void _gc_pause() { + gc->paused = true; +} + +void _gc_resume() { + gc->paused = false; +} + +size_t _gc_stop() { + size_t collected = gc_sweep(); + gc_allocation_map_delete(gc->allocs); + return collected; +} + +size_t _gc_run() { + // double t = kinc_time(); + gc_mark(); + size_t collected = gc_sweep(); + // kinc_log(KINC_LOG_LEVEL_INFO, "gc took %fms, freed %db.\n", (kinc_time() - t) * 1000, collected); + return collected; +} diff --git a/armorcore/sources/libs/gc.h b/armorcore/sources/libs/gc.h new file mode 100644 index 000000000..b95b1ff3d --- /dev/null +++ b/armorcore/sources/libs/gc.h @@ -0,0 +1,21 @@ +#pragma once + +// gc - A simple mark and sweep garbage collector for C. + +#include +#include +#include + +void _gc_start(void *bos); +size_t _gc_stop(); +void _gc_pause(); +void _gc_resume(); +size_t _gc_run(); +void *_gc_calloc(size_t count, size_t size); +void _gc_array(void *ptr, int *length); +void _gc_leaf(void *ptr); +void _gc_root(void *ptr); +void _gc_unroot(void *ptr); +void *_gc_cut(void *ptr, size_t pos, size_t size); +void *_gc_realloc(void *ptr, size_t size); +void _gc_free(void *ptr); diff --git a/armorcore/sources/libs/jo_mpeg.h b/armorcore/sources/libs/jo_mpeg.h new file mode 100644 index 000000000..1c07ff4f0 --- /dev/null +++ b/armorcore/sources/libs/jo_mpeg.h @@ -0,0 +1,261 @@ +/* public domain Simple, Minimalistic, No Allocations MPEG writer - http://jonolick.com + * + * "Converted" to C by Wladislav Artsimovich + * I just added a bunch of * and removed a bunch of & ¯\_(ツ)_/¯ + * + * Latest revisions: + * 1.02 (22-03-2017) Fixed AC encoding bug. + * Fixed color space bug (thx r- lyeh!) + * 1.01 (18-10-2016) warning fixes + * 1.00 (25-09-2016) initial release + * + * Basic usage: + * char *frame = new char[width*height*4]; // 4 component. RGBX format, where X is unused + * FILE *fp = fopen("foo.mpg", "wb"); + * jo_write_mpeg(fp, frame, width, height, 60); // frame 0 + * jo_write_mpeg(fp, frame, width, height, 60); // frame 1 + * jo_write_mpeg(fp, frame, width, height, 60); // frame 2 + * ... + * fclose(fp); + * + * Notes: + * Only supports 24, 25, 30, 50, or 60 fps + * + * I don't know if decoders support changing of fps, or dimensions for each frame. + * Movie players *should* support it as the spec allows it, but ... + * + * MPEG-1/2 currently has no active patents as far as I am aware. + * + * http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html + * http://www.cs.cornell.edu/dali/api/mpegvideo-c.html + * */ + +#ifndef JO_INCLUDE_MPEG_H +#define JO_INCLUDE_MPEG_H + +#include + +// To get a header file for this, either cut and paste the header, +// or create jo_mpeg.h, #define JO_MPEG_HEADER_FILE_ONLY, and +// then include jo_mpeg.c from it. + +// Returns false on failure +extern void jo_write_mpeg(FILE *fp, const unsigned char *rgbx, int width, int height, int fps); + +#endif // JO_INCLUDE_MPEG_H + +#ifndef JO_MPEG_HEADER_FILE_ONLY + +#include +#include +#include + +// Huffman tables +static const unsigned char s_jo_HTDC_Y[9][2] = {{4,3}, {0,2}, {1,2}, {5,3}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}}; +static const unsigned char s_jo_HTDC_C[9][2] = {{0,2}, {1,2}, {2,2}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}, {254,8}}; +static const unsigned char s_jo_HTAC[32][40][2] = { +{{6,3},{8,5},{10,6},{12,8},{76,9},{66,9},{20,11},{58,13},{48,13},{38,13},{32,13},{52,14},{50,14},{48,14},{46,14},{62,15},{62,15},{58,15},{56,15},{54,15},{52,15},{50,15},{48,15},{46,15},{44,15},{42,15},{40,15},{38,15},{36,15},{34,15},{32,15},{48,16},{46,16},{44,16},{42,16},{40,16},{38,16},{36,16},{34,16},{32,16},}, +{{6,4},{12,7},{74,9},{24,11},{54,13},{44,14},{42,14},{62,16},{60,16},{58,16},{56,16},{54,16},{52,16},{50,16},{38,17},{36,17},{34,17},{32,17}}, +{{10,5},{8,8},{22,11},{40,13},{40,14}}, +{{14,6},{72,9},{56,13},{38,14}}, +{{12,6},{30,11},{36,13}}, {{14,7},{18,11},{36,14}}, {{10,7},{60,13},{40,17}}, +{{8,7},{42,13}}, {{14,8},{34,13}}, {{10,8},{34,14}}, {{78,9},{32,14}}, {{70,9},{52,17}}, {{68,9},{50,17}}, {{64,9},{48,17}}, {{28,11},{46,17}}, {{26,11},{44,17}}, {{16,11},{42,17}}, +{{62,13}}, {{52,13}}, {{50,13}}, {{46,13}}, {{44,13}}, {{62,14}}, {{60,14}}, {{58,14}}, {{56,14}}, {{54,14}}, {{62,17}}, {{60,17}}, {{58,17}}, {{56,17}}, {{54,17}}, +}; +static const float s_jo_quantTbl[64] = { + 0.015625f,0.005632f,0.005035f,0.004832f,0.004808f,0.005892f,0.007964f,0.013325f, + 0.005632f,0.004061f,0.003135f,0.003193f,0.003338f,0.003955f,0.004898f,0.008828f, + 0.005035f,0.003135f,0.002816f,0.003013f,0.003299f,0.003581f,0.005199f,0.009125f, + 0.004832f,0.003484f,0.003129f,0.003348f,0.003666f,0.003979f,0.005309f,0.009632f, + 0.005682f,0.003466f,0.003543f,0.003666f,0.003906f,0.004546f,0.005774f,0.009439f, + 0.006119f,0.004248f,0.004199f,0.004228f,0.004546f,0.005062f,0.006124f,0.009942f, + 0.008883f,0.006167f,0.006096f,0.005777f,0.006078f,0.006391f,0.007621f,0.012133f, + 0.016780f,0.011263f,0.009907f,0.010139f,0.009849f,0.010297f,0.012133f,0.019785f, +}; +static const unsigned char s_jo_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +typedef struct { + FILE *fp; + int buf, cnt; +} jo_bits_t; + +static void jo_writeBits(jo_bits_t *b, int value, int count) { + b->cnt += count; + b->buf |= value << (24 - b->cnt); + while(b->cnt >= 8) { + unsigned char c = (b->buf >> 16) & 255; + putc(c, b->fp); + b->buf <<= 8; + b->cnt -= 8; + } +} + +static void jo_DCT(float *d0, float *d1, float *d2, float *d3, float *d4, float *d5, float *d6, float *d7) { + float tmp0 = *d0 + *d7; + float tmp7 = *d0 - *d7; + float tmp1 = *d1 + *d6; + float tmp6 = *d1 - *d6; + float tmp2 = *d2 + *d5; + float tmp5 = *d2 - *d5; + float tmp3 = *d3 + *d4; + float tmp4 = *d3 - *d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + *d0 = tmp10 + tmp11; // phase 3 + *d4 = tmp10 - tmp11; + + float z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + *d2 = tmp13 + z1; // phase 5 + *d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + float z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + float z2 = tmp10 * 0.541196100f + z5; // c2-c6 + float z4 = tmp12 * 1.306562965f + z5; // c2+c6 + float z3 = tmp11 * 0.707106781f; // c4 + + float z11 = tmp7 + z3; // phase 5 + float z13 = tmp7 - z3; + + *d5 = z13 + z2; // phase 6 + *d3 = z13 - z2; + *d1 = z11 + z4; + *d7 = z11 - z4; +} + +static int jo_processDU(jo_bits_t *bits, float A[64], const unsigned char htdc[9][2], int DC) { + for(int dataOff=0; dataOff<64; dataOff+=8) { + jo_DCT(&A[dataOff], &A[dataOff+1], &A[dataOff+2], &A[dataOff+3], &A[dataOff+4], &A[dataOff+5], &A[dataOff+6], &A[dataOff+7]); + } + for(int dataOff=0; dataOff<8; ++dataOff) { + jo_DCT(&A[dataOff], &A[dataOff+8], &A[dataOff+16], &A[dataOff+24], &A[dataOff+32], &A[dataOff+40], &A[dataOff+48], &A[dataOff+56]); + } + int Q[64]; + for(int i=0; i<64; ++i) { + float v = A[i]*s_jo_quantTbl[i]; + Q[s_jo_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f)); + } + + DC = Q[0] - DC; + int aDC = DC < 0 ? -DC : DC; + int size = 0; + int tempval = aDC; + while(tempval) { + size++; + tempval >>= 1; + } + jo_writeBits(bits, htdc[size][0], htdc[size][1]); + if(DC < 0) aDC ^= (1 << size) - 1; + jo_writeBits(bits, aDC, size); + + int endpos = 63; + for(; (endpos>0)&&(Q[endpos]==0); --endpos) { /* do nothing */ } + for(int i = 1; i <= endpos;) { + int run = 0; + while (Q[i]==0 && i 127) { + jo_writeBits(bits, 0, 8); + } + code = AC&255; + size = 8; + } + jo_writeBits(bits, code, size); + } + jo_writeBits(bits, 2, 2); + + return Q[0]; +} + +void jo_write_mpeg(FILE *fp, const unsigned char *rgbx, int width, int height, int fps) { + int lastDCY = 128, lastDCCR = 128, lastDCCB = 128; + jo_bits_t bits = {fp}; + + // Sequence Header + fwrite("\x00\x00\x01\xB3", 4, 1, fp); + // 12 bits for width, height + putc((width>>4)&0xFF, fp); + putc(((width&0xF)<<4) | ((height>>8) & 0xF), fp); + putc(height & 0xFF, fp); + // aspect ratio, framerate + if(fps <= 24) putc(0x12, fp); + else if(fps <= 25) putc(0x13, fp); + else if(fps <= 30) putc(0x15, fp); + else if(fps <= 50) putc(0x16, fp); + else putc(0x18, fp); // 60fps + fwrite("\xFF\xFF\xE0\xA0", 4, 1, fp); + + fwrite("\x00\x00\x01\xB8\x80\x08\x00\x40", 8, 1, fp); // GOP header + fwrite("\x00\x00\x01\x00\x00\x0C\x00\x00", 8, 1, fp); // PIC header + fwrite("\x00\x00\x01\x01", 4, 1, fp); // Slice header + jo_writeBits(&bits, 0x10, 6); + + for (int vblock=0; vblock<(height+15)/16; vblock++) { + for (int hblock=0; hblock<(width+15)/16; hblock++) { + jo_writeBits(&bits, 3, 2); + + float Y[256], CBx[256], CRx[256]; + for (int i=0; i<256; ++i) { + int y = vblock*16+(i/16); + int x = hblock*16+(i&15); + x = x >= width ? width-1 : x; + y = y >= height ? height-1 : y; + const unsigned char *c = rgbx + y*width*4+x*4; + float r = c[0], g = c[1], b = c[2]; + Y[i] = (0.299f*r + 0.587f*g + 0.114f*b) * (219.f/255) + 16; + CBx[i] = (-0.299f*r - 0.587f*g + 0.886f*b) * (224.f/255) + 128; + CRx[i] = (0.701f*r - 0.587f*g - 0.114f*b) * (224.f/255) + 128; + } + + // Downsample Cb,Cr (420 format) + float CB[64], CR[64]; + for (int i=0; i<64; ++i) { + int j =(i&7)*2 + (i&56)*4; + CB[i] = (CBx[j] + CBx[j+1] + CBx[j+16] + CBx[j+17]) * 0.25f; + CR[i] = (CRx[j] + CRx[j+1] + CRx[j+16] + CRx[j+17]) * 0.25f; + } + + for (int k1=0; k1<2; ++k1) { + for (int k2=0; k2<2; ++k2) { + float block[64]; + for (int i=0; i<64; i+=8) { + int j = (i&7)+(i&56)*2 + k1*8*16 + k2*8; + memcpy(block+i, Y+j, 8*sizeof(Y[0])); + } + lastDCY = jo_processDU(&bits, block, s_jo_HTDC_Y, lastDCY); + } + } + lastDCCB = jo_processDU(&bits, CB, s_jo_HTDC_C, lastDCCB); + lastDCCR = jo_processDU(&bits, CR, s_jo_HTDC_C, lastDCCR); + } + } + jo_writeBits(&bits, 0, 7); + fwrite("\x00\x00\x01\xb7", 4, 1, fp); // End of Sequence +} +#endif + diff --git a/armorcore/sources/libs/jsmn.h b/armorcore/sources/libs/jsmn.h new file mode 100644 index 000000000..8ac14c1bd --- /dev/null +++ b/armorcore/sources/libs/jsmn.h @@ -0,0 +1,471 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef JSMN_H +#define JSMN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1 << 0, + JSMN_ARRAY = 1 << 1, + JSMN_STRING = 1 << 2, + JSMN_PRIMITIVE = 1 << 3 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct jsmntok { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct jsmn_parser { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + /* Skip starting quote */ + parser->pos++; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ diff --git a/armorcore/sources/libs/miniClib/assert.h b/armorcore/sources/libs/miniClib/assert.h new file mode 100644 index 000000000..0a8f8c411 --- /dev/null +++ b/armorcore/sources/libs/miniClib/assert.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef NDEBUG +#define assert(condition) +#else +static void assert(int condition) {} +#endif diff --git a/armorcore/sources/libs/miniClib/errno.h b/armorcore/sources/libs/miniClib/errno.h new file mode 100644 index 000000000..fe522d1e6 --- /dev/null +++ b/armorcore/sources/libs/miniClib/errno.h @@ -0,0 +1,3 @@ +#pragma once + +#define errno 0 diff --git a/armorcore/sources/libs/miniClib/license.txt b/armorcore/sources/libs/miniClib/license.txt new file mode 100644 index 000000000..4d2ae2a58 --- /dev/null +++ b/armorcore/sources/libs/miniClib/license.txt @@ -0,0 +1,19 @@ +Copyright (c) 2024 the Kinc Development Team + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. diff --git a/armorcore/sources/libs/miniClib/math.c b/armorcore/sources/libs/miniClib/math.c new file mode 100644 index 000000000..6efc3d054 --- /dev/null +++ b/armorcore/sources/libs/miniClib/math.c @@ -0,0 +1,100 @@ +#include "math.h" + +#ifdef KINC_WASM +__attribute__((import_module("imports"), import_name("js_pow"))) float js_pow(float base, float exponent); +__attribute__((import_module("imports"), import_name("js_floor"))) float js_floor(float x); +__attribute__((import_module("imports"), import_name("js_sin"))) float js_sin(float x); +__attribute__((import_module("imports"), import_name("js_cos"))) float js_cos(float x); +__attribute__((import_module("imports"), import_name("js_tan"))) float js_tan(float x); +__attribute__((import_module("imports"), import_name("js_log"))) float js_log(float x); +__attribute__((import_module("imports"), import_name("js_exp"))) float js_exp(float x); +__attribute__((import_module("imports"), import_name("js_sqrt"))) float js_sqrt(float x); +#endif + +double ldexp(double x, int exp) { + return 0.0; +} + +double pow(double base, double exponent) { +#ifdef KINC_WASM + return js_pow(base, exponent); +#endif + return 0.0; +} + +double floor(double x) { +#ifdef KINC_WASM + return js_floor(x); +#endif + return 0.0; +} + +float floorf(float x) { +#ifdef KINC_WASM + return js_floor(x); +#endif + return 0.0f; +} + +double sin(double x) { +#ifdef KINC_WASM + return js_sin(x); +#endif + return 0.0; +} + +float sinf(float x) { +#ifdef KINC_WASM + return js_sin(x); +#endif + return 0.0f; +} + +double cos(double x) { +#ifdef KINC_WASM + return js_cos(x); +#endif + return 0.0; +} + +float cosf(float x) { +#ifdef KINC_WASM + return js_cos(x); +#endif + return 0.0f; +} + +double tan(double x) { +#ifdef KINC_WASM + return js_tan(x); +#endif + return 0.0; +} + +float tanf(float x) { +#ifdef KINC_WASM + return js_tan(x); +#endif + return 0.0f; +} + +double log(double x) { +#ifdef KINC_WASM + return js_log(x); +#endif + return 0.0; +} + +double exp(double x) { +#ifdef KINC_WASM + return js_exp(x); +#endif + return 0.0; +} + +double sqrt(double x) { +#ifdef KINC_WASM + return js_sqrt(x); +#endif + return 0.0; +} diff --git a/armorcore/sources/libs/miniClib/math.h b/armorcore/sources/libs/miniClib/math.h new file mode 100644 index 000000000..5f45ee724 --- /dev/null +++ b/armorcore/sources/libs/miniClib/math.h @@ -0,0 +1,35 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +double ldexp(double x, int exp); + +double pow(double base, double exponent); + +double floor(double x); + +float floorf(float x); + +double sin(double x); + +float sinf(float x); + +double cos(double x); + +float cosf(float x); + +double tan(double x); + +float tanf(float x); + +double log(double x); + +double exp(double x); + +double sqrt(double x); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/libs/miniClib/memory.c b/armorcore/sources/libs/miniClib/memory.c new file mode 100644 index 000000000..ce4d68f60 --- /dev/null +++ b/armorcore/sources/libs/miniClib/memory.c @@ -0,0 +1,68 @@ +#include "stdlib.h" + +#ifdef KINC_WASM +__attribute__((import_module("imports"), import_name("js_fprintf"))) void js_fprintf(const char *format); + +#define HEAP_SIZE 1024 * 1024 * 8 +static unsigned char heap[HEAP_SIZE]; +static size_t heap_top = 4; +#endif + +#ifdef KINC_WASM +__attribute__((export_name("malloc"))) +#endif +void *malloc(size_t size) { +#ifdef KINC_WASM + // Align to 4 bytes to make js typed arrays work + if (size % 4 != 0) { + size += 4 - size % 4; + } + size_t old_top = heap_top; + heap_top += size; + if (heap_top >= HEAP_SIZE) { + js_fprintf("malloc: out of memory"); + } + return &heap[old_top]; +#endif + return NULL; +} + +void *alloca(size_t size) { + return NULL; +} + +void *realloc(void *mem, size_t size) { + return NULL; +} + +void free(void *mem) { + +} + +void *memset(void *ptr, int value, size_t num) { + unsigned char *data = (unsigned char *)ptr; + for (size_t i = 0; i < num; ++i) { + data[i] = (unsigned char)value; + } + return ptr; +} + +void *memcpy(void *destination, const void *source, size_t num) { + unsigned char *s = (unsigned char *)source; + unsigned char *d = (unsigned char *)destination; + for (size_t i = 0; i < num; ++i) { + d[i] = s[i]; + } + return destination; +} + +int memcmp(const void *ptr1, const void *ptr2, size_t num) { + unsigned char *p1 = (unsigned char *)ptr1; + unsigned char *p2 = (unsigned char *)ptr2; + for (size_t i = 0; i < num; ++i) { + if (p1[i] != p2[i]) { + return (int)p1[i] - (int)p2[i]; + } + } + return 0; +} diff --git a/armorcore/sources/libs/miniClib/memory.h b/armorcore/sources/libs/miniClib/memory.h new file mode 100644 index 000000000..d537c33f0 --- /dev/null +++ b/armorcore/sources/libs/miniClib/memory.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/libs/miniClib/stdbool.h b/armorcore/sources/libs/miniClib/stdbool.h new file mode 100644 index 000000000..241e5f6e3 --- /dev/null +++ b/armorcore/sources/libs/miniClib/stdbool.h @@ -0,0 +1,7 @@ +#pragma once + +#ifndef __cplusplus +typedef _Bool bool; +#define true 1 +#define false 0 +#endif diff --git a/armorcore/sources/libs/miniClib/stdio.c b/armorcore/sources/libs/miniClib/stdio.c new file mode 100644 index 000000000..875b4b1a8 --- /dev/null +++ b/armorcore/sources/libs/miniClib/stdio.c @@ -0,0 +1,62 @@ +#include "stdio.h" + +#ifdef KINC_WASM +__attribute__((import_module("imports"), import_name("js_fprintf"))) void js_fprintf(const char *format); +__attribute__((import_module("imports"), import_name("js_fopen"))) FILE *js_fopen(const char *filename); +__attribute__((import_module("imports"), import_name("js_ftell"))) long int js_ftell(FILE *stream); +__attribute__((import_module("imports"), import_name("js_fseek"))) int js_fseek(FILE *stream, long int offset, int origin); +__attribute__((import_module("imports"), import_name("js_fread"))) size_t js_fread(void *ptr, size_t size, size_t count, FILE *stream); +#endif + +FILE *stdout = NULL, *stderr = NULL; + +int fprintf(FILE *stream, const char *format, ...) { +#ifdef KINC_WASM + js_fprintf(format); +#endif + return 0; +} + +int vsnprintf(char *s, size_t n, const char *format, va_list arg) { + return 0; +} + +size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream) { + return 0; +} + +FILE *fopen(const char *filename, const char *mode) { +#ifdef KINC_WASM + return js_fopen(filename); +#endif + return NULL; +} + +int fclose(FILE *stream) { + return 0; +} + +long int ftell(FILE *stream) { +#ifdef KINC_WASM + return js_ftell(stream); +#endif + return 0; +} + +int fseek(FILE *stream, long int offset, int origin) { +#ifdef KINC_WASM + return js_fseek(stream, offset, origin); +#endif + return 0; +} + +size_t fread(void *ptr, size_t size, size_t count, FILE *stream) { +#ifdef KINC_WASM + return js_fread(ptr, size, count, stream); +#endif + return 0; +} + +int fputs(const char *str, FILE *stream) { + return 0; +} diff --git a/armorcore/sources/libs/miniClib/stdio.h b/armorcore/sources/libs/miniClib/stdio.h new file mode 100644 index 000000000..68f412ad6 --- /dev/null +++ b/armorcore/sources/libs/miniClib/stdio.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SEEK_SET 0 +#define SEEK_END 1 + +typedef int FILE; + +extern FILE *stdout, *stderr; + +int fprintf(FILE *stream, const char *format, ...); + +int vsnprintf(char *s, size_t n, const char *format, va_list arg); + +size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream); + +FILE *fopen(const char *filename, const char *mode); + +int fclose(FILE *stream); + +long int ftell(FILE *stream); + +int fseek(FILE *stream, long int offset, int origin); + +size_t fread(void *ptr, size_t size, size_t count, FILE *stream); + +int fputs(const char *str, FILE *stream); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/libs/miniClib/stdlib.c b/armorcore/sources/libs/miniClib/stdlib.c new file mode 100644 index 000000000..08a813a69 --- /dev/null +++ b/armorcore/sources/libs/miniClib/stdlib.c @@ -0,0 +1,21 @@ +#include "stdlib.h" + +void exit(int code) { + exit(code); +} + +long int strtol(const char *str, char **endptr, int base) { + return 0; +} + +int abs(int n) { + return n < 0 ? -n : n; +} + +long long int llabs(long long int n) { + return n < 0 ? -n : n; +} + +void qsort(void *base, size_t num, size_t size, int (*compar)(const void*,const void*)) { + +} diff --git a/armorcore/sources/libs/miniClib/stdlib.h b/armorcore/sources/libs/miniClib/stdlib.h new file mode 100644 index 000000000..671f0af9b --- /dev/null +++ b/armorcore/sources/libs/miniClib/stdlib.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned long size_t; +#define EXIT_FAILURE 1 + +void *malloc(size_t size); + +void *alloca(size_t size); + +void *realloc(void *mem, size_t size); + +void free(void *mem); + +void exit(int code); + +long int strtol(const char *str, char **endptr, int base); + +int abs(int n); + +long long int llabs(long long int n); + +void qsort(void *base, size_t num, size_t size, int (*compar)(const void *, const void *)); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/libs/miniClib/string.c b/armorcore/sources/libs/miniClib/string.c new file mode 100644 index 000000000..16ee1cad6 --- /dev/null +++ b/armorcore/sources/libs/miniClib/string.c @@ -0,0 +1,173 @@ +#include "string.h" + +#include + +size_t strlen(const char *str) { + size_t size = 0; + while (true) { + if (str[size] == 0) { + return size; + } + ++size; + } + return 0; +} + +char *strcpy(char *destination, const char *source) { + for (size_t i = 0;; ++i) { + destination[i] = source[i]; + if (source[i] == 0) { + return destination; + } + } + return destination; +} + +char *strncpy(char *destination, const char *source, size_t num) { + for (size_t i = 0; i < num; ++i) { + destination[i] = source[i]; + if (source[i] == 0) { + return destination; + } + } + return destination; +} + +char *strcat(char *destination, const char *source) { + size_t di = 0; + while (destination[di] != 0) { + ++di; + } + for (size_t si = 0;; ++si) { + destination[di] = source[si]; + if (source[si] == 0) { + return destination; + } + ++di; + } + return destination; +} + +char *strstr(const char *str1, const char *str2) { + for (size_t i1 = 0;; ++i1) { + if (str1[i1] == 0) { + return NULL; + } + for (size_t i2 = 0;; ++i2) { + if (str2[i2] == 0) { + return (char*)&str1[i1]; + } + if (str1[i1 + i2] != str2[i2]) { + break; + } + } + } +} + +int strcmp(const char *str1, const char *str2) { + for (size_t i = 0;; ++i) { + if (str1[i] != str2[i]) { + return str1[i] - str2[i]; + } + if (str1[i] == 0) { + return 0; + } + } +} + +int strncmp(const char *str1, const char *str2, size_t num) { + for (size_t i = 0; i < num; ++i) { + if (str1[i] != str2[i]) { + return str1[i] - str2[i]; + } + if (str1[i] == 0) { + return 0; + } + } + return 0; +} + +size_t wcslen(const wchar_t *str) { + size_t size = 0; + while (true) { + if (str[size] == 0) { + return size; + } + ++size; + } + return 0; +} + +wchar_t *wcscpy(wchar_t *destination, const wchar_t *source) { + for (size_t i = 0;; ++i) { + destination[i] = source[i]; + if (source[i] == 0) { + return destination; + } + } + return destination; +} + +wchar_t *wcsncpy(wchar_t *destination, const wchar_t *source, size_t num) { + for (size_t i = 0; i < num; ++i) { + destination[i] = source[i]; + if (source[i] == 0) { + return destination; + } + } + return destination; +} + +wchar_t *wcscat(wchar_t *destination, const wchar_t *source) { + size_t di = 0; + while (destination[di] != 0) { + ++di; + } + for (size_t si = 0;; ++si) { + destination[di] = source[si]; + if (source[si] == 0) { + return destination; + } + ++di; + } + return destination; +} + +wchar_t *wcsstr(wchar_t *str1, const wchar_t *str2) { + for (size_t i1 = 0;; ++i1) { + if (str1[i1] == 0) { + return NULL; + } + for (size_t i2 = 0;; ++i2) { + if (str2[i2] == 0) { + return &str1[i1]; + } + if (str1[i1 + i2] != str2[i2]) { + break; + } + } + } +} + +int wcscmp(const wchar_t *str1, const wchar_t *str2) { + for (size_t i = 0;; ++i) { + if (str1[i] != str2[i]) { + return str1[i] - str2[i]; + } + if (str1[i] == 0) { + return 0; + } + } +} + +int wcsncmp(const wchar_t *str1, const wchar_t *str2, size_t num) { + for (size_t i = 0; i < num; ++i) { + if (str1[i] != str2[i]) { + return str1[i] - str2[i]; + } + if (str1[i] == 0) { + return 0; + } + } + return 0; +} diff --git a/armorcore/sources/libs/miniClib/string.h b/armorcore/sources/libs/miniClib/string.h new file mode 100644 index 000000000..f8b259bef --- /dev/null +++ b/armorcore/sources/libs/miniClib/string.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void *memset(void *ptr, int value, size_t num); + +void *memcpy(void *destination, const void *source, size_t num); + +int memcmp(const void *ptr1, const void *ptr2, size_t num); + +size_t strlen(const char *str); + +char *strcpy(char *destination, const char *source); + +char *strncpy(char *destination, const char *source, size_t num); + +char *strcat(char *destination, const char *source); + +// built-in in Clang +char *strstr(const char *str1, const char *str2); + +int strcmp(const char *str1, const char *str2); + +int strncmp(const char *str1, const char *str2, size_t num); + +size_t wcslen(const wchar_t *str); + +wchar_t *wcscpy(wchar_t *destination, const wchar_t *source); + +wchar_t *wcsncpy(wchar_t *destination, const wchar_t *source, size_t num); + +wchar_t *wcscat(wchar_t *destination, const wchar_t *source); + +wchar_t *wcsstr(wchar_t *str1, const wchar_t *str2); + +int wcscmp(const wchar_t *str1, const wchar_t *str2); + +int wcsncmp(const wchar_t *str1, const wchar_t *str2, size_t num); + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/libs/miniClib/time.h b/armorcore/sources/libs/miniClib/time.h new file mode 100644 index 000000000..d537c33f0 --- /dev/null +++ b/armorcore/sources/libs/miniClib/time.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __cplusplus +} +#endif diff --git a/armorcore/sources/libs/nfd/LICENSE b/armorcore/sources/libs/nfd/LICENSE new file mode 100644 index 000000000..3ab103c55 --- /dev/null +++ b/armorcore/sources/libs/nfd/LICENSE @@ -0,0 +1,16 @@ +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + diff --git a/armorcore/sources/libs/nfd/common.h b/armorcore/sources/libs/nfd/common.h new file mode 100644 index 000000000..7745d323b --- /dev/null +++ b/armorcore/sources/libs/nfd/common.h @@ -0,0 +1,21 @@ +/* + Native File Dialog + + Internal, common across platforms + + http://www.frogtoss.com/labs + */ + + +#ifndef _NFD_COMMON_H +#define _NFD_COMMON_H + +#define NFD_MAX_STRLEN 256 +#define _NFD_UNUSED(x) ((void)x) + +void *NFDi_Malloc( size_t bytes ); +void NFDi_Free( void *ptr ); +void NFDi_SetError( const char *msg ); +void NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy ); + +#endif diff --git a/armorcore/sources/libs/nfd/nfd.h b/armorcore/sources/libs/nfd/nfd.h new file mode 100644 index 000000000..74c92743f --- /dev/null +++ b/armorcore/sources/libs/nfd/nfd.h @@ -0,0 +1,74 @@ +/* + Native File Dialog + + User API + + http://www.frogtoss.com/labs + */ + + +#ifndef _NFD_H +#define _NFD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* denotes UTF-8 char */ +typedef char nfdchar_t; + +/* opaque data structure -- see NFD_PathSet_* */ +typedef struct { + nfdchar_t *buf; + size_t *indices; /* byte offsets into buf */ + size_t count; /* number of indices into buf */ +}nfdpathset_t; + +typedef enum { + NFD_ERROR, /* programmatic error */ + NFD_OKAY, /* user pressed okay, or successful return */ + NFD_CANCEL /* user pressed cancel */ +}nfdresult_t; + + +/* nfd_.c */ + +/* single file open dialog */ +nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ); + +/* multiple file open dialog */ +nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdpathset_t *outPaths ); + +/* save dialog */ +nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ); + + +/* select folder dialog */ +nfdresult_t NFD_PickFolder( const nfdchar_t *defaultPath, + nfdchar_t **outPath); + +/* nfd_common.c */ + +/* get last error -- set when nfdresult_t returns NFD_ERROR */ +const char *NFD_GetError( void ); +/* get the number of entries stored in pathSet */ +size_t NFD_PathSet_GetCount( const nfdpathset_t *pathSet ); +/* Get the UTF-8 path at offset index */ +nfdchar_t *NFD_PathSet_GetPath( const nfdpathset_t *pathSet, size_t index ); +/* Free the pathSet */ +void NFD_PathSet_Free( nfdpathset_t *pathSet ); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/armorcore/sources/libs/nfd/nfd_cocoa.m b/armorcore/sources/libs/nfd/nfd_cocoa.m new file mode 100644 index 000000000..4fb064197 --- /dev/null +++ b/armorcore/sources/libs/nfd/nfd_cocoa.m @@ -0,0 +1,286 @@ +/* + Native File Dialog + + http://www.frogtoss.com/labs + */ + +#include +#include "nfd.h" +#include "nfd_common.h" + +static NSArray *BuildAllowedFileTypes( const char *filterList ) +{ + // Commas and semicolons are the same thing on this platform + + NSMutableArray *buildFilterList = [[NSMutableArray alloc] init]; + + char typebuf[NFD_MAX_STRLEN] = {0}; + + size_t filterListLen = strlen(filterList); + char *p_typebuf = typebuf; + for ( size_t i = 0; i < filterListLen+1; ++i ) + { + if ( filterList[i] == ',' || filterList[i] == ';' || filterList[i] == '\0' ) + { + if (filterList[i] != '\0') + ++p_typebuf; + *p_typebuf = '\0'; + + NSString *thisType = [NSString stringWithUTF8String: typebuf]; + [buildFilterList addObject:thisType]; + p_typebuf = typebuf; + *p_typebuf = '\0'; + } + else + { + *p_typebuf = filterList[i]; + ++p_typebuf; + + } + } + + NSArray *returnArray = [NSArray arrayWithArray:buildFilterList]; + +// [buildFilterList release]; + return returnArray; +} + +static void AddFilterListToDialog( NSSavePanel *dialog, const char *filterList ) +{ + if ( !filterList || strlen(filterList) == 0 ) + return; + + NSArray *allowedFileTypes = BuildAllowedFileTypes( filterList ); + if ( [allowedFileTypes count] != 0 ) + { + [dialog setAllowedFileTypes:allowedFileTypes]; + } +} + +static void SetDefaultPath( NSSavePanel *dialog, const nfdchar_t *defaultPath ) +{ + if ( !defaultPath || strlen(defaultPath) == 0 ) + return; + + NSString *defaultPathString = [NSString stringWithUTF8String: defaultPath]; + NSURL *url = [NSURL fileURLWithPath:defaultPathString isDirectory:YES]; + [dialog setDirectoryURL:url]; +} + + +/* fixme: pathset should be pathSet */ +static nfdresult_t AllocPathSet( NSArray *urls, nfdpathset_t *pathset ) +{ + assert(pathset); + assert([urls count]); + + pathset->count = (size_t)[urls count]; + pathset->indices = NFDi_Malloc( sizeof(size_t)*pathset->count ); + if ( !pathset->indices ) + { + return NFD_ERROR; + } + + // count the total space needed for buf + size_t bufsize = 0; + for ( NSURL *url in urls ) + { + NSString *path = [url path]; + bufsize += [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; + } + + pathset->buf = NFDi_Malloc( sizeof(nfdchar_t) * bufsize ); + if ( !pathset->buf ) + { + return NFD_ERROR; + } + + // fill buf + nfdchar_t *p_buf = pathset->buf; + size_t count = 0; + for ( NSURL *url in urls ) + { + NSString *path = [url path]; + const nfdchar_t *utf8Path = [path UTF8String]; + size_t byteLen = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; + memcpy( p_buf, utf8Path, byteLen ); + + ptrdiff_t index = p_buf - pathset->buf; + assert( index >= 0 ); + pathset->indices[count] = (size_t)index; + + p_buf += byteLen; + ++count; + } + + return NFD_OKAY; +} + +/* public */ + + +nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ +// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; + NSOpenPanel *dialog = [NSOpenPanel openPanel]; + [dialog setAllowsMultipleSelection:NO]; + + // Build the filter list + AddFilterListToDialog(dialog, filterList); + + // Set the starting directory + SetDefaultPath(dialog, defaultPath); + + nfdresult_t nfdResult = NFD_CANCEL; + if ( [dialog runModal] == NSModalResponseOK ) + { + NSURL *url = [dialog URL]; + const char *utf8Path = [[url path] UTF8String]; + + // byte count, not char count + size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path); + + *outPath = NFDi_Malloc( len+1 ); + if ( !*outPath ) + { +// [pool release]; + [keyWindow makeKeyAndOrderFront:nil]; + return NFD_ERROR; + } + memcpy( *outPath, utf8Path, len+1 ); /* copy null term */ + nfdResult = NFD_OKAY; + } +// [pool release]; + + [keyWindow makeKeyAndOrderFront:nil]; + return nfdResult; +} + + +nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdpathset_t *outPaths ) +{ +// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; + + NSOpenPanel *dialog = [NSOpenPanel openPanel]; + [dialog setAllowsMultipleSelection:YES]; + + // Build the fiter list. + AddFilterListToDialog(dialog, filterList); + + // Set the starting directory + SetDefaultPath(dialog, defaultPath); + + nfdresult_t nfdResult = NFD_CANCEL; + if ( [dialog runModal] == NSModalResponseOK ) + { + NSArray *urls = [dialog URLs]; + + if ( [urls count] == 0 ) + { +// [pool release]; + [keyWindow makeKeyAndOrderFront:nil]; + return NFD_CANCEL; + } + + if ( AllocPathSet( urls, outPaths ) == NFD_ERROR ) + { +// [pool release]; + [keyWindow makeKeyAndOrderFront:nil]; + return NFD_ERROR; + } + + nfdResult = NFD_OKAY; + } +// [pool release]; + + [keyWindow makeKeyAndOrderFront:nil]; + return nfdResult; +} + + +nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ +// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; + + NSSavePanel *dialog = [NSSavePanel savePanel]; + [dialog setExtensionHidden:NO]; + + // Build the filter list. + AddFilterListToDialog(dialog, filterList); + + // Set the starting directory + SetDefaultPath(dialog, defaultPath); + + nfdresult_t nfdResult = NFD_CANCEL; + if ( [dialog runModal] == NSModalResponseOK ) + { + NSURL *url = [dialog URL]; + const char *utf8Path = [[url path] UTF8String]; + + size_t byteLen = [url.path lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1; + + *outPath = NFDi_Malloc( byteLen ); + if ( !*outPath ) + { +// [pool release]; + [keyWindow makeKeyAndOrderFront:nil]; + return NFD_ERROR; + } + memcpy( *outPath, utf8Path, byteLen ); + nfdResult = NFD_OKAY; + } + +// [pool release]; + [keyWindow makeKeyAndOrderFront:nil]; + return nfdResult; +} + +nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath, + nfdchar_t **outPath) +{ +// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSWindow *keyWindow = [[NSApplication sharedApplication] keyWindow]; + NSOpenPanel *dialog = [NSOpenPanel openPanel]; + [dialog setAllowsMultipleSelection:NO]; + [dialog setCanChooseDirectories:YES]; + [dialog setCanCreateDirectories:YES]; + [dialog setCanChooseFiles:NO]; + + // Set the starting directory + SetDefaultPath(dialog, defaultPath); + + nfdresult_t nfdResult = NFD_CANCEL; + if ( [dialog runModal] == NSModalResponseOK ) + { + NSURL *url = [dialog URL]; + const char *utf8Path = [[url path] UTF8String]; + + // byte count, not char count + size_t len = strlen(utf8Path);//NFDi_UTF8_Strlen(utf8Path); + + *outPath = NFDi_Malloc( len+1 ); + if ( !*outPath ) + { +// [pool release]; + [keyWindow makeKeyAndOrderFront:nil]; + return NFD_ERROR; + } + memcpy( *outPath, utf8Path, len+1 ); /* copy null term */ + nfdResult = NFD_OKAY; + } +// [pool release]; + + [keyWindow makeKeyAndOrderFront:nil]; + return nfdResult; +} diff --git a/armorcore/sources/libs/nfd/nfd_common.c b/armorcore/sources/libs/nfd/nfd_common.c new file mode 100644 index 000000000..55517f5dd --- /dev/null +++ b/armorcore/sources/libs/nfd/nfd_common.c @@ -0,0 +1,142 @@ +/* + Native File Dialog + + http://www.frogtoss.com/labs + */ + +#include +#include +#include +#include "nfd_common.h" + +static char g_errorstr[NFD_MAX_STRLEN] = {0}; + +/* public routines */ + +const char *NFD_GetError( void ) +{ + return g_errorstr; +} + +size_t NFD_PathSet_GetCount( const nfdpathset_t *pathset ) +{ + assert(pathset); + return pathset->count; +} + +nfdchar_t *NFD_PathSet_GetPath( const nfdpathset_t *pathset, size_t num ) +{ + assert(pathset); + assert(num < pathset->count); + + return pathset->buf + pathset->indices[num]; +} + +void NFD_PathSet_Free( nfdpathset_t *pathset ) +{ + assert(pathset); + NFDi_Free( pathset->indices ); + NFDi_Free( pathset->buf ); +} + +/* internal routines */ + +void *NFDi_Malloc( size_t bytes ) +{ + void *ptr = malloc(bytes); + if ( !ptr ) + NFDi_SetError("NFDi_Malloc failed."); + + return ptr; +} + +void NFDi_Free( void *ptr ) +{ + assert(ptr); + free(ptr); +} + +void NFDi_SetError( const char *msg ) +{ + int bTruncate = NFDi_SafeStrncpy( g_errorstr, msg, NFD_MAX_STRLEN ); + assert( !bTruncate ); _NFD_UNUSED(bTruncate); +} + + +int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy ) +{ + size_t n = maxCopy; + char *d = dst; + + assert( src ); + assert( dst ); + + while ( n > 0 && *src != '\0' ) + { + *d++ = *src++; + --n; + } + + /* Truncation case - + terminate string and return true */ + if ( n == 0 ) + { + dst[maxCopy-1] = '\0'; + return 1; + } + + /* No truncation. Append a single NULL and return. */ + *d = '\0'; + return 0; +} + + +/* adapted from microutf8 */ +int32_t NFDi_UTF8_Strlen( const nfdchar_t *str ) +{ + /* This function doesn't properly check validity of UTF-8 character + sequence, it is supposed to use only with valid UTF-8 strings. */ + + int32_t character_count = 0; + int32_t i = 0; /* Counter used to iterate over string. */ + nfdchar_t maybe_bom[4]; + + /* If there is UTF-8 BOM ignore it. */ + if (strlen(str) > 2) + { + strncpy(maybe_bom, str, 3); + maybe_bom[3] = 0; + if (strcmp(maybe_bom, (nfdchar_t*)NFD_UTF8_BOM) == 0) + i += 3; + } + + while(str[i]) + { + if (str[i] >> 7 == 0) + { + /* If bit pattern begins with 0 we have ascii character. */ + ++character_count; + } + else if (str[i] >> 6 == 3) + { + /* If bit pattern begins with 11 it is beginning of UTF-8 byte sequence. */ + ++character_count; + } + else if (str[i] >> 6 == 2) + ; /* If bit pattern begins with 10 it is middle of utf-8 byte sequence. */ + else + { + /* In any other case this is not valid UTF-8. */ + return -1; + } + ++i; + } + + return character_count; +} + +int NFDi_IsFilterSegmentChar( char ch ) +{ + return (ch==','||ch==';'||ch=='\0'); +} + diff --git a/armorcore/sources/libs/nfd/nfd_common.h b/armorcore/sources/libs/nfd/nfd_common.h new file mode 100644 index 000000000..1a9ab1625 --- /dev/null +++ b/armorcore/sources/libs/nfd/nfd_common.h @@ -0,0 +1,39 @@ +/* + Native File Dialog + + Internal, common across platforms + + http://www.frogtoss.com/labs + */ + + +#ifndef _NFD_COMMON_H +#define _NFD_COMMON_H + +#include "nfd.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NFD_MAX_STRLEN 256 +#define _NFD_UNUSED(x) ((void)x) + +#define NFD_UTF8_BOM "\xEF\xBB\xBF" + + +void *NFDi_Malloc( size_t bytes ); +void NFDi_Free( void *ptr ); +void NFDi_SetError( const char *msg ); +int NFDi_SafeStrncpy( char *dst, const char *src, size_t maxCopy ); +int32_t NFDi_UTF8_Strlen( const nfdchar_t *str ); +int NFDi_IsFilterSegmentChar( char ch ); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/armorcore/sources/libs/nfd/nfd_gtk.c b/armorcore/sources/libs/nfd/nfd_gtk.c new file mode 100644 index 000000000..6fa2123aa --- /dev/null +++ b/armorcore/sources/libs/nfd/nfd_gtk.c @@ -0,0 +1,379 @@ +/* + Native File Dialog + + http://www.frogtoss.com/labs +*/ + +#include +#include +#include +#include +#include "nfd.h" +#include "nfd_common.h" + + +const char INIT_FAIL_MSG[] = "gtk_init_check failed to initilaize GTK+"; + + +static void AddTypeToFilterName( const char *typebuf, char *filterName, size_t bufsize ) +{ + const char SEP[] = ", "; + + size_t len = strlen(filterName); + if ( len != 0 ) + { + strncat( filterName, SEP, bufsize - len - 1 ); + len += strlen(SEP); + } + + strncat( filterName, typebuf, bufsize - len - 1 ); +} + +static void AddFiltersToDialog( GtkWidget *dialog, const char *filterList ) +{ + GtkFileFilter *filter; + char typebuf[NFD_MAX_STRLEN] = {0}; + const char *p_filterList = filterList; + char *p_typebuf = typebuf; + char filterName[NFD_MAX_STRLEN] = {0}; + + if ( !filterList || strlen(filterList) == 0 ) + return; + + filter = gtk_file_filter_new(); + while ( 1 ) + { + + if ( NFDi_IsFilterSegmentChar(*p_filterList) ) + { + char typebufWildcard[NFD_MAX_STRLEN]; + /* add another type to the filter */ + assert( strlen(typebuf) > 0 ); + assert( strlen(typebuf) < NFD_MAX_STRLEN-1 ); + + snprintf( typebufWildcard, NFD_MAX_STRLEN, "*.%s", typebuf ); + AddTypeToFilterName( typebuf, filterName, NFD_MAX_STRLEN ); + + gtk_file_filter_add_pattern( filter, typebufWildcard ); + + p_typebuf = typebuf; + memset( typebuf, 0, sizeof(char) * NFD_MAX_STRLEN ); + } + + if ( *p_filterList == ';' || *p_filterList == '\0' ) + { + /* end of filter -- add it to the dialog */ + + gtk_file_filter_set_name( filter, filterName ); + gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter ); + + filterName[0] = '\0'; + + if ( *p_filterList == '\0' ) + break; + + filter = gtk_file_filter_new(); + } + + if ( !NFDi_IsFilterSegmentChar( *p_filterList ) ) + { + *p_typebuf = *p_filterList; + p_typebuf++; + } + + p_filterList++; + } + + /* always append a wildcard option to the end*/ + + filter = gtk_file_filter_new(); + gtk_file_filter_set_name( filter, "*.*" ); + gtk_file_filter_add_pattern( filter, "*" ); + gtk_file_chooser_add_filter( GTK_FILE_CHOOSER(dialog), filter ); +} + +static void SetDefaultPath( GtkWidget *dialog, const char *defaultPath ) +{ + if ( !defaultPath || strlen(defaultPath) == 0 ) + return; + + /* GTK+ manual recommends not specifically setting the default path. + We do it anyway in order to be consistent across platforms. + + If consistency with the native OS is preferred, this is the line + to comment out. -ml */ + gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), defaultPath ); +} + +static nfdresult_t AllocPathSet( GSList *fileList, nfdpathset_t *pathSet ) +{ + size_t bufSize = 0; + GSList *node; + nfdchar_t *p_buf; + size_t count = 0; + + assert(fileList); + assert(pathSet); + + pathSet->count = (size_t)g_slist_length( fileList ); + assert( pathSet->count > 0 ); + + pathSet->indices = NFDi_Malloc( sizeof(size_t)*pathSet->count ); + if ( !pathSet->indices ) + { + return NFD_ERROR; + } + + /* count the total space needed for buf */ + for ( node = fileList; node; node = node->next ) + { + assert(node->data); + bufSize += strlen( (const gchar*)node->data ) + 1; + } + + pathSet->buf = NFDi_Malloc( sizeof(nfdchar_t) * bufSize ); + + /* fill buf */ + p_buf = pathSet->buf; + for ( node = fileList; node; node = node->next ) + { + nfdchar_t *path = (nfdchar_t*)(node->data); + size_t byteLen = strlen(path)+1; + ptrdiff_t index; + + memcpy( p_buf, path, byteLen ); + g_free(node->data); + + index = p_buf - pathSet->buf; + assert( index >= 0 ); + pathSet->indices[count] = (size_t)index; + + p_buf += byteLen; + ++count; + } + + g_slist_free( fileList ); + + return NFD_OKAY; +} + +static void WaitForCleanup(void) +{ + while (gtk_events_pending()) + gtk_main_iteration(); +} + +/* public */ + +nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ + GtkWidget *dialog; + nfdresult_t result; + + if ( !gtk_init_check( NULL, NULL ) ) + { + NFDi_SetError(INIT_FAIL_MSG); + return NFD_ERROR; + } + + dialog = gtk_file_chooser_dialog_new( "Open File", + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, + NULL ); + + /* Build the filter list */ + AddFiltersToDialog(dialog, filterList); + + /* Set the default path */ + SetDefaultPath(dialog, defaultPath); + + result = NFD_CANCEL; + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + char *filename; + + filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); + + { + size_t len = strlen(filename); + *outPath = NFDi_Malloc( len + 1 ); + memcpy( *outPath, filename, len + 1 ); + if ( !*outPath ) + { + g_free( filename ); + gtk_widget_destroy(dialog); + return NFD_ERROR; + } + } + g_free( filename ); + + result = NFD_OKAY; + } + + WaitForCleanup(); + gtk_widget_destroy(dialog); + WaitForCleanup(); + + return result; +} + + +nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdpathset_t *outPaths ) +{ + GtkWidget *dialog; + nfdresult_t result; + + if ( !gtk_init_check( NULL, NULL ) ) + { + NFDi_SetError(INIT_FAIL_MSG); + return NFD_ERROR; + } + + dialog = gtk_file_chooser_dialog_new( "Open Files", + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, + NULL ); + gtk_file_chooser_set_select_multiple( GTK_FILE_CHOOSER(dialog), TRUE ); + + /* Build the filter list */ + AddFiltersToDialog(dialog, filterList); + + /* Set the default path */ + SetDefaultPath(dialog, defaultPath); + + result = NFD_CANCEL; + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + GSList *fileList = gtk_file_chooser_get_filenames( GTK_FILE_CHOOSER(dialog) ); + if ( AllocPathSet( fileList, outPaths ) == NFD_ERROR ) + { + gtk_widget_destroy(dialog); + return NFD_ERROR; + } + + result = NFD_OKAY; + } + + WaitForCleanup(); + gtk_widget_destroy(dialog); + WaitForCleanup(); + + return result; +} + +nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ + GtkWidget *dialog; + nfdresult_t result; + + if ( !gtk_init_check( NULL, NULL ) ) + { + NFDi_SetError(INIT_FAIL_MSG); + return NFD_ERROR; + } + + dialog = gtk_file_chooser_dialog_new( "Save File", + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Save", GTK_RESPONSE_ACCEPT, + NULL ); + gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE ); + + /* Build the filter list */ + AddFiltersToDialog(dialog, filterList); + + /* Set the default path */ + SetDefaultPath(dialog, defaultPath); + + result = NFD_CANCEL; + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + char *filename; + filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); + + { + size_t len = strlen(filename); + *outPath = NFDi_Malloc( len + 1 ); + memcpy( *outPath, filename, len + 1 ); + if ( !*outPath ) + { + g_free( filename ); + gtk_widget_destroy(dialog); + return NFD_ERROR; + } + } + g_free(filename); + + result = NFD_OKAY; + } + + WaitForCleanup(); + gtk_widget_destroy(dialog); + WaitForCleanup(); + + return result; +} + +nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath, + nfdchar_t **outPath) +{ + GtkWidget *dialog; + nfdresult_t result; + + if (!gtk_init_check(NULL, NULL)) + { + NFDi_SetError(INIT_FAIL_MSG); + return NFD_ERROR; + } + + dialog = gtk_file_chooser_dialog_new( "Select folder", + NULL, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Select", GTK_RESPONSE_ACCEPT, + NULL ); + gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(dialog), TRUE ); + + + /* Set the default path */ + SetDefaultPath(dialog, defaultPath); + + result = NFD_CANCEL; + if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + char *filename; + filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) ); + + { + size_t len = strlen(filename); + *outPath = NFDi_Malloc( len + 1 ); + memcpy( *outPath, filename, len + 1 ); + if ( !*outPath ) + { + g_free( filename ); + gtk_widget_destroy(dialog); + return NFD_ERROR; + } + } + g_free(filename); + + result = NFD_OKAY; + } + + WaitForCleanup(); + gtk_widget_destroy(dialog); + WaitForCleanup(); + + return result; +} diff --git a/armorcore/sources/libs/nfd/nfd_win.cpp b/armorcore/sources/libs/nfd/nfd_win.cpp new file mode 100644 index 000000000..949da2b53 --- /dev/null +++ b/armorcore/sources/libs/nfd/nfd_win.cpp @@ -0,0 +1,762 @@ +/* + Native File Dialog + + http://www.frogtoss.com/labs + */ + + +#ifdef __MINGW32__ +// Explicitly setting NTDDI version, this is necessary for the MinGW compiler +#define NTDDI_VERSION NTDDI_VISTA +#define _WIN32_WINNT _WIN32_WINNT_VISTA +#endif + +#define _CRTDBG_MAP_ALLOC +#include +#include + +/* only locally define UNICODE in this compilation unit */ +#ifndef UNICODE +#define UNICODE +#endif + +#include +#include +#include +#include +#include +#include "nfd_common.h" + + +#define COM_INITFLAGS ::COINIT_APARTMENTTHREADED | ::COINIT_DISABLE_OLE1DDE + +static BOOL COMIsInitialized(HRESULT coResult) +{ + if (coResult == RPC_E_CHANGED_MODE) + { + // If COM was previously initialized with different init flags, + // NFD still needs to operate. Eat this warning. + return TRUE; + } + + return SUCCEEDED(coResult); +} + +static HRESULT COMInit(void) +{ + return ::CoInitializeEx(NULL, COM_INITFLAGS); +} + +static void COMUninit(HRESULT coResult) +{ + // do not uninitialize if RPC_E_CHANGED_MODE occurred -- this + // case does not refcount COM. + if (SUCCEEDED(coResult)) + ::CoUninitialize(); +} + +// allocs the space in outPath -- call free() +static void CopyWCharToNFDChar( const wchar_t *inStr, nfdchar_t **outStr ) +{ + int inStrCharacterCount = static_cast(wcslen(inStr)); + int bytesNeeded = WideCharToMultiByte( CP_UTF8, 0, + inStr, inStrCharacterCount, + NULL, 0, NULL, NULL ); + assert( bytesNeeded ); + bytesNeeded += 1; + + *outStr = (nfdchar_t*)NFDi_Malloc( bytesNeeded ); + if ( !*outStr ) + return; + + int bytesWritten = WideCharToMultiByte( CP_UTF8, 0, + inStr, -1, + *outStr, bytesNeeded, + NULL, NULL ); + assert( bytesWritten ); _NFD_UNUSED( bytesWritten ); +} + +/* includes NULL terminator byte in return */ +static size_t GetUTF8ByteCountForWChar( const wchar_t *str ) +{ + size_t bytesNeeded = WideCharToMultiByte( CP_UTF8, 0, + str, -1, + NULL, 0, NULL, NULL ); + assert( bytesNeeded ); + return bytesNeeded+1; +} + +// write to outPtr -- no free() necessary. +static int CopyWCharToExistingNFDCharBuffer( const wchar_t *inStr, nfdchar_t *outPtr ) +{ + int bytesNeeded = static_cast(GetUTF8ByteCountForWChar( inStr )); + + /* invocation copies null term */ + int bytesWritten = WideCharToMultiByte( CP_UTF8, 0, + inStr, -1, + outPtr, bytesNeeded, + NULL, 0 ); + assert( bytesWritten ); + + return bytesWritten; + +} + + +// allocs the space in outStr -- call free() +static void CopyNFDCharToWChar( const nfdchar_t *inStr, wchar_t **outStr ) +{ + int inStrByteCount = static_cast(strlen(inStr)); + int charsNeeded = MultiByteToWideChar(CP_UTF8, 0, + inStr, inStrByteCount, + NULL, 0 ); + assert( charsNeeded ); + assert( !*outStr ); + charsNeeded += 1; // terminator + + *outStr = (wchar_t*)NFDi_Malloc( charsNeeded * sizeof(wchar_t) ); + if ( !*outStr ) + return; + + int ret = MultiByteToWideChar(CP_UTF8, 0, + inStr, inStrByteCount, + *outStr, charsNeeded); + (*outStr)[charsNeeded-1] = '\0'; + +#ifdef _DEBUG + int inStrCharacterCount = static_cast(NFDi_UTF8_Strlen(inStr)); + assert( ret == inStrCharacterCount ); +#else + _NFD_UNUSED(ret); +#endif +} + + +/* ext is in format "jpg", no wildcards or separators */ +static int AppendExtensionToSpecBuf( const char *ext, char *specBuf, size_t specBufLen ) +{ + const char SEP[] = ";"; + assert( specBufLen > strlen(ext)+3 ); + + if ( strlen(specBuf) > 0 ) + { + strncat( specBuf, SEP, specBufLen - strlen(specBuf) - 1 ); + specBufLen += strlen(SEP); + } + + char extWildcard[NFD_MAX_STRLEN]; + int bytesWritten = sprintf_s( extWildcard, NFD_MAX_STRLEN, "*.%s", ext ); + assert( bytesWritten == (int)(strlen(ext)+2) ); + _NFD_UNUSED(bytesWritten); + + strncat( specBuf, extWildcard, specBufLen - strlen(specBuf) - 1 ); + + return NFD_OKAY; +} + +static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const char *filterList ) +{ + const wchar_t WILDCARD[] = L"*.*"; + + if ( !filterList || strlen(filterList) == 0 ) + return NFD_OKAY; + + // Count rows to alloc + UINT filterCount = 1; /* guaranteed to have one filter on a correct, non-empty parse */ + const char *p_filterList; + for ( p_filterList = filterList; *p_filterList; ++p_filterList ) + { + if ( *p_filterList == ';' ) + ++filterCount; + } + + assert(filterCount); + if ( !filterCount ) + { + NFDi_SetError("Error parsing filters."); + return NFD_ERROR; + } + + /* filterCount plus 1 because we hardcode the *.* wildcard after the while loop */ + COMDLG_FILTERSPEC *specList = (COMDLG_FILTERSPEC*)NFDi_Malloc( sizeof(COMDLG_FILTERSPEC) * ((size_t)filterCount + 1) ); + if ( !specList ) + { + return NFD_ERROR; + } + for (UINT i = 0; i < filterCount+1; ++i ) + { + specList[i].pszName = NULL; + specList[i].pszSpec = NULL; + } + + size_t specIdx = 0; + p_filterList = filterList; + char typebuf[NFD_MAX_STRLEN] = {0}; /* one per comma or semicolon */ + char *p_typebuf = typebuf; + + char specbuf[NFD_MAX_STRLEN] = {0}; /* one per semicolon */ + + while ( 1 ) + { + if ( NFDi_IsFilterSegmentChar(*p_filterList) ) + { + /* append a type to the specbuf (pending filter) */ + AppendExtensionToSpecBuf( typebuf, specbuf, NFD_MAX_STRLEN ); + + p_typebuf = typebuf; + memset( typebuf, 0, sizeof(char)*NFD_MAX_STRLEN ); + } + + if ( *p_filterList == ';' || *p_filterList == '\0' ) + { + /* end of filter -- add it to specList */ + + CopyNFDCharToWChar( specbuf, (wchar_t**)&specList[specIdx].pszName ); + CopyNFDCharToWChar( specbuf, (wchar_t**)&specList[specIdx].pszSpec ); + + memset( specbuf, 0, sizeof(char)*NFD_MAX_STRLEN ); + ++specIdx; + if ( specIdx == filterCount ) + break; + } + + if ( !NFDi_IsFilterSegmentChar( *p_filterList )) + { + *p_typebuf = *p_filterList; + ++p_typebuf; + } + + ++p_filterList; + } + + /* Add wildcard */ + specList[specIdx].pszSpec = WILDCARD; + specList[specIdx].pszName = WILDCARD; + + fileOpenDialog->SetFileTypes( filterCount+1, specList ); + + /* free speclist */ + for ( size_t i = 0; i < filterCount; ++i ) + { + NFDi_Free( (void*)specList[i].pszSpec ); + } + NFDi_Free( specList ); + + return NFD_OKAY; +} + +static nfdresult_t AllocPathSet( IShellItemArray *shellItems, nfdpathset_t *pathSet ) +{ + const char ERRORMSG[] = "Error allocating pathset."; + + assert(shellItems); + assert(pathSet); + + // How many items in shellItems? + DWORD numShellItems; + HRESULT result = shellItems->GetCount(&numShellItems); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError(ERRORMSG); + return NFD_ERROR; + } + + pathSet->count = static_cast(numShellItems); + assert( pathSet->count > 0 ); + + pathSet->indices = (size_t*)NFDi_Malloc( sizeof(size_t)*pathSet->count ); + if ( !pathSet->indices ) + { + return NFD_ERROR; + } + + /* count the total bytes needed for buf */ + size_t bufSize = 0; + for ( DWORD i = 0; i < numShellItems; ++i ) + { + ::IShellItem *shellItem; + result = shellItems->GetItemAt(i, &shellItem); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError(ERRORMSG); + return NFD_ERROR; + } + + // Confirm SFGAO_FILESYSTEM is true for this shellitem, or ignore it. + SFGAOF attribs; + result = shellItem->GetAttributes( SFGAO_FILESYSTEM, &attribs ); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError(ERRORMSG); + return NFD_ERROR; + } + if ( !(attribs & SFGAO_FILESYSTEM) ) + continue; + + LPWSTR name; + shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name); + + // Calculate length of name with UTF-8 encoding + bufSize += GetUTF8ByteCountForWChar( name ); + + CoTaskMemFree(name); + } + + assert(bufSize); + + pathSet->buf = (nfdchar_t*)NFDi_Malloc( sizeof(nfdchar_t) * bufSize ); + memset( pathSet->buf, 0, sizeof(nfdchar_t) * bufSize ); + + /* fill buf */ + nfdchar_t *p_buf = pathSet->buf; + for (DWORD i = 0; i < numShellItems; ++i ) + { + ::IShellItem *shellItem; + result = shellItems->GetItemAt(i, &shellItem); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError(ERRORMSG); + return NFD_ERROR; + } + + // Confirm SFGAO_FILESYSTEM is true for this shellitem, or ignore it. + SFGAOF attribs; + result = shellItem->GetAttributes( SFGAO_FILESYSTEM, &attribs ); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError(ERRORMSG); + return NFD_ERROR; + } + if ( !(attribs & SFGAO_FILESYSTEM) ) + continue; + + LPWSTR name; + shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name); + + int bytesWritten = CopyWCharToExistingNFDCharBuffer(name, p_buf); + CoTaskMemFree(name); + + ptrdiff_t index = p_buf - pathSet->buf; + assert( index >= 0 ); + pathSet->indices[i] = static_cast(index); + + p_buf += bytesWritten; + } + + return NFD_OKAY; +} + + +static nfdresult_t SetDefaultPath( IFileDialog *dialog, const char *defaultPath ) +{ + if ( !defaultPath || strlen(defaultPath) == 0 ) + return NFD_OKAY; + + wchar_t *defaultPathW = {0}; + CopyNFDCharToWChar( defaultPath, &defaultPathW ); + + IShellItem *folder; + HRESULT result = SHCreateItemFromParsingName( defaultPathW, NULL, IID_PPV_ARGS(&folder) ); + + // Valid non results. + if ( result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || result == HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE) ) + { + NFDi_Free( defaultPathW ); + return NFD_OKAY; + } + + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Error creating ShellItem"); + NFDi_Free( defaultPathW ); + return NFD_ERROR; + } + + // Could also call SetDefaultFolder(), but this guarantees defaultPath -- more consistency across API. + dialog->SetFolder( folder ); + + NFDi_Free( defaultPathW ); + folder->Release(); + + return NFD_OKAY; +} + +/* public */ + + +nfdresult_t NFD_OpenDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ + nfdresult_t nfdResult = NFD_ERROR; + + + HRESULT coResult = COMInit(); + if (!COMIsInitialized(coResult)) + { + NFDi_SetError("Could not initialize COM."); + return nfdResult; + } + + // Create dialog + ::IFileOpenDialog *fileOpenDialog(NULL); + HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL, + CLSCTX_ALL, ::IID_IFileOpenDialog, + reinterpret_cast(&fileOpenDialog) ); + + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not create dialog."); + goto end; + } + + // Build the filter list + if ( !AddFiltersToDialog( fileOpenDialog, filterList ) ) + { + goto end; + } + + // Set the default path + if ( !SetDefaultPath( fileOpenDialog, defaultPath ) ) + { + goto end; + } + + // Show the dialog. + result = fileOpenDialog->Show(NULL); + if ( SUCCEEDED(result) ) + { + // Get the file name + ::IShellItem *shellItem(NULL); + result = fileOpenDialog->GetResult(&shellItem); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not get shell item from dialog."); + goto end; + } + wchar_t *filePath(NULL); + result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not get file path for selected."); + shellItem->Release(); + goto end; + } + + CopyWCharToNFDChar( filePath, outPath ); + CoTaskMemFree(filePath); + if ( !*outPath ) + { + /* error is malloc-based, error message would be redundant */ + shellItem->Release(); + goto end; + } + + nfdResult = NFD_OKAY; + shellItem->Release(); + } + else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) + { + nfdResult = NFD_CANCEL; + } + else + { + NFDi_SetError("File dialog box show failed."); + nfdResult = NFD_ERROR; + } + +end: + if (fileOpenDialog) + fileOpenDialog->Release(); + + COMUninit(coResult); + + return nfdResult; +} + +nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdpathset_t *outPaths ) +{ + nfdresult_t nfdResult = NFD_ERROR; + + + HRESULT coResult = COMInit(); + if (!COMIsInitialized(coResult)) + { + NFDi_SetError("Could not initialize COM."); + return nfdResult; + } + + // Create dialog + ::IFileOpenDialog *fileOpenDialog(NULL); + HRESULT result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL, + CLSCTX_ALL, ::IID_IFileOpenDialog, + reinterpret_cast(&fileOpenDialog) ); + + if ( !SUCCEEDED(result) ) + { + fileOpenDialog = NULL; + NFDi_SetError("Could not create dialog."); + goto end; + } + + // Build the filter list + if ( !AddFiltersToDialog( fileOpenDialog, filterList ) ) + { + goto end; + } + + // Set the default path + if ( !SetDefaultPath( fileOpenDialog, defaultPath ) ) + { + goto end; + } + + // Set a flag for multiple options + DWORD dwFlags; + result = fileOpenDialog->GetOptions(&dwFlags); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not get options."); + goto end; + } + result = fileOpenDialog->SetOptions(dwFlags | FOS_ALLOWMULTISELECT); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not set options."); + goto end; + } + + // Show the dialog. + result = fileOpenDialog->Show(NULL); + if ( SUCCEEDED(result) ) + { + IShellItemArray *shellItems; + result = fileOpenDialog->GetResults( &shellItems ); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not get shell items."); + goto end; + } + + if ( AllocPathSet( shellItems, outPaths ) == NFD_ERROR ) + { + shellItems->Release(); + goto end; + } + + shellItems->Release(); + nfdResult = NFD_OKAY; + } + else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) + { + nfdResult = NFD_CANCEL; + } + else + { + NFDi_SetError("File dialog box show failed."); + nfdResult = NFD_ERROR; + } + +end: + if ( fileOpenDialog ) + fileOpenDialog->Release(); + + COMUninit(coResult); + + return nfdResult; +} + +nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList, + const nfdchar_t *defaultPath, + nfdchar_t **outPath ) +{ + nfdresult_t nfdResult = NFD_ERROR; + + HRESULT coResult = COMInit(); + if (!COMIsInitialized(coResult)) + { + NFDi_SetError("Could not initialize COM."); + return nfdResult; + } + + // Create dialog + ::IFileSaveDialog *fileSaveDialog(NULL); + HRESULT result = ::CoCreateInstance(::CLSID_FileSaveDialog, NULL, + CLSCTX_ALL, ::IID_IFileSaveDialog, + reinterpret_cast(&fileSaveDialog) ); + + if ( !SUCCEEDED(result) ) + { + fileSaveDialog = NULL; + NFDi_SetError("Could not create dialog."); + goto end; + } + + // Build the filter list + if ( !AddFiltersToDialog( fileSaveDialog, filterList ) ) + { + goto end; + } + + // Set the default path + if ( !SetDefaultPath( fileSaveDialog, defaultPath ) ) + { + goto end; + } + + // Show the dialog. + result = fileSaveDialog->Show(NULL); + if ( SUCCEEDED(result) ) + { + // Get the file name + ::IShellItem *shellItem; + result = fileSaveDialog->GetResult(&shellItem); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not get shell item from dialog."); + goto end; + } + wchar_t *filePath(NULL); + result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath); + if ( !SUCCEEDED(result) ) + { + shellItem->Release(); + NFDi_SetError("Could not get file path for selected."); + goto end; + } + + CopyWCharToNFDChar( filePath, outPath ); + CoTaskMemFree(filePath); + if ( !*outPath ) + { + /* error is malloc-based, error message would be redundant */ + shellItem->Release(); + goto end; + } + + nfdResult = NFD_OKAY; + shellItem->Release(); + } + else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) + { + nfdResult = NFD_CANCEL; + } + else + { + NFDi_SetError("File dialog box show failed."); + nfdResult = NFD_ERROR; + } + +end: + if ( fileSaveDialog ) + fileSaveDialog->Release(); + + COMUninit(coResult); + + return nfdResult; +} + + + +nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath, + nfdchar_t **outPath) +{ + nfdresult_t nfdResult = NFD_ERROR; + DWORD dwOptions = 0; + + HRESULT coResult = COMInit(); + if (!COMIsInitialized(coResult)) + { + NFDi_SetError("CoInitializeEx failed."); + return nfdResult; + } + + // Create dialog + ::IFileOpenDialog *fileDialog(NULL); + HRESULT result = CoCreateInstance(CLSID_FileOpenDialog, + NULL, + CLSCTX_ALL, + IID_PPV_ARGS(&fileDialog)); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("CoCreateInstance for CLSID_FileOpenDialog failed."); + goto end; + } + + // Set the default path + if (SetDefaultPath(fileDialog, defaultPath) != NFD_OKAY) + { + NFDi_SetError("SetDefaultPath failed."); + goto end; + } + + // Get the dialogs options + if (!SUCCEEDED(fileDialog->GetOptions(&dwOptions))) + { + NFDi_SetError("GetOptions for IFileDialog failed."); + goto end; + } + + // Add in FOS_PICKFOLDERS which hides files and only allows selection of folders + if (!SUCCEEDED(fileDialog->SetOptions(dwOptions | FOS_PICKFOLDERS))) + { + NFDi_SetError("SetOptions for IFileDialog failed."); + goto end; + } + + // Show the dialog to the user + result = fileDialog->Show(NULL); + if ( SUCCEEDED(result) ) + { + // Get the folder name + ::IShellItem *shellItem(NULL); + + result = fileDialog->GetResult(&shellItem); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("Could not get file path for selected."); + shellItem->Release(); + goto end; + } + + wchar_t *path = NULL; + result = shellItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &path); + if ( !SUCCEEDED(result) ) + { + NFDi_SetError("GetDisplayName for IShellItem failed."); + shellItem->Release(); + goto end; + } + + CopyWCharToNFDChar(path, outPath); + CoTaskMemFree(path); + if ( !*outPath ) + { + shellItem->Release(); + goto end; + } + + nfdResult = NFD_OKAY; + shellItem->Release(); + } + else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) ) + { + nfdResult = NFD_CANCEL; + } + else + { + NFDi_SetError("Show for IFileDialog failed."); + nfdResult = NFD_ERROR; + } + + end: + + if (fileDialog) + fileDialog->Release(); + + COMUninit(coResult); + + return nfdResult; +} diff --git a/armorcore/sources/libs/nfd/simple_exec.h b/armorcore/sources/libs/nfd/simple_exec.h new file mode 100644 index 000000000..6308dfe0f --- /dev/null +++ b/armorcore/sources/libs/nfd/simple_exec.h @@ -0,0 +1,218 @@ +// copied from: https://github.com/wheybags/simple_exec/blob/5a74c507c4ce1b2bb166177ead4cca7cfa23cb35/simple_exec.h + +// simple_exec.h, single header library to run external programs + retrieve their status code and output (unix only for now) +// +// do this: +// #define SIMPLE_EXEC_IMPLEMENTATION +// before you include this file in *one* C or C++ file to create the implementation. +// i.e. it should look like this: +// #define SIMPLE_EXEC_IMPLEMENTATION +// #include "simple_exec.h" + +#ifndef SIMPLE_EXEC_H +#define SIMPLE_EXEC_H + +int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...); +int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs); + +#endif // SIMPLE_EXEC_H + +#ifdef SIMPLE_EXEC_IMPLEMENTATION + +#include +#include +#include +#include +#include +#include +#include +#include + +#define release_assert(exp) { if (!(exp)) { abort(); } } + +enum PIPE_FILE_DESCRIPTORS +{ + READ_FD = 0, + WRITE_FD = 1 +}; + +enum RUN_COMMAND_ERROR +{ + COMMAND_RAN_OK = 0, + COMMAND_NOT_FOUND = 1 +}; + +int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs) +{ + // adapted from: https://stackoverflow.com/a/479103 + + int bufferSize = 256; + char buffer[bufferSize + 1]; + + int dataReadFromChildDefaultSize = bufferSize * 5; + int dataReadFromChildSize = dataReadFromChildDefaultSize; + int dataReadFromChildUsed = 0; + char* dataReadFromChild = (char*)malloc(dataReadFromChildSize); + + + int parentToChild[2]; + release_assert(pipe(parentToChild) == 0); + + int childToParent[2]; + release_assert(pipe(childToParent) == 0); + + int errPipe[2]; + release_assert(pipe(errPipe) == 0); + + pid_t pid; + switch( pid = fork() ) + { + case -1: + { + release_assert(0 && "Fork failed"); + break; + } + + case 0: // child + { + release_assert(dup2(parentToChild[READ_FD ], STDIN_FILENO ) != -1); + release_assert(dup2(childToParent[WRITE_FD], STDOUT_FILENO) != -1); + + if(includeStdErr) + { + release_assert(dup2(childToParent[WRITE_FD], STDERR_FILENO) != -1); + } + else + { + int devNull = open("/dev/null", O_WRONLY); + release_assert(dup2(devNull, STDERR_FILENO) != -1); + } + + // unused + release_assert(close(parentToChild[WRITE_FD]) == 0); + release_assert(close(childToParent[READ_FD ]) == 0); + release_assert(close(errPipe[READ_FD]) == 0); + + const char* command = allArgs[0]; + execvp(command, allArgs); + + char err = 1; + ssize_t result = write(errPipe[WRITE_FD], &err, 1); + release_assert(result != -1); + + close(errPipe[WRITE_FD]); + close(parentToChild[READ_FD]); + close(childToParent[WRITE_FD]); + + exit(0); + } + + + default: // parent + { + // unused + release_assert(close(parentToChild[READ_FD]) == 0); + release_assert(close(childToParent[WRITE_FD]) == 0); + release_assert(close(errPipe[WRITE_FD]) == 0); + + while(1) + { + ssize_t bytesRead = 0; + switch(bytesRead = read(childToParent[READ_FD], buffer, bufferSize)) + { + case 0: // End-of-File, or non-blocking read. + { + int status = 0; + release_assert(waitpid(pid, &status, 0) == pid); + + // done with these now + release_assert(close(parentToChild[WRITE_FD]) == 0); + release_assert(close(childToParent[READ_FD]) == 0); + + char errChar = 0; + ssize_t result = read(errPipe[READ_FD], &errChar, 1); + release_assert(result != -1); + close(errPipe[READ_FD]); + + if(errChar) + { + free(dataReadFromChild); + return COMMAND_NOT_FOUND; + } + + // free any un-needed memory with realloc + add a null terminator for convenience + dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildUsed + 1); + dataReadFromChild[dataReadFromChildUsed] = '\0'; + + if(stdOut != NULL) + *stdOut = dataReadFromChild; + else + free(dataReadFromChild); + + if(stdOutByteCount != NULL) + *stdOutByteCount = dataReadFromChildUsed; + if(returnCode != NULL) + *returnCode = WEXITSTATUS(status); + + return COMMAND_RAN_OK; + } + case -1: + { + release_assert(0 && "read() failed"); + break; + } + + default: + { + if(dataReadFromChildUsed + bytesRead + 1 >= dataReadFromChildSize) + { + dataReadFromChildSize += dataReadFromChildDefaultSize; + dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildSize); + } + + memcpy(dataReadFromChild + dataReadFromChildUsed, buffer, bytesRead); + dataReadFromChildUsed += bytesRead; + break; + } + } + } + } + } +} + +int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...) +{ + va_list vl; + va_start(vl, command); + + char* currArg = NULL; + + int allArgsInitialSize = 16; + int allArgsSize = allArgsInitialSize; + char** allArgs = (char**)malloc(sizeof(char*) * allArgsSize); + allArgs[0] = command; + + int i = 1; + do + { + currArg = va_arg(vl, char*); + allArgs[i] = currArg; + + i++; + + if(i >= allArgsSize) + { + allArgsSize += allArgsInitialSize; + allArgs = (char**)realloc(allArgs, sizeof(char*) * allArgsSize); + } + + } while(currArg != NULL); + + va_end(vl); + + int retval = runCommandArray(stdOut, stdOutByteCount, returnCode, includeStdErr, allArgs); + free(allArgs); + return retval; +} + +#endif //SIMPLE_EXEC_IMPLEMENTATION diff --git a/armorcore/sources/libs/quickjs/LICENSE b/armorcore/sources/libs/quickjs/LICENSE new file mode 100644 index 000000000..7d87e3ca5 --- /dev/null +++ b/armorcore/sources/libs/quickjs/LICENSE @@ -0,0 +1,24 @@ +The MIT License (MIT) + +Copyright (c) 2017-2021 Fabrice Bellard +Copyright (c) 2017-2021 Charlie Gordon +Copyright (c) 2023 Ben Noordhuis +Copyright (c) 2023 Saúl Ibarra Corretgé + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/armorcore/sources/libs/quickjs/VERSION b/armorcore/sources/libs/quickjs/VERSION new file mode 100644 index 000000000..2c31d8e7b --- /dev/null +++ b/armorcore/sources/libs/quickjs/VERSION @@ -0,0 +1,2 @@ +a9594573092ce1543b2776b6d1766275989291c0 +https://github.com/quickjs-ng/quickjs diff --git a/armorcore/sources/libs/quickjs/cutils.c b/armorcore/sources/libs/quickjs/cutils.c new file mode 100644 index 000000000..56982dfa7 --- /dev/null +++ b/armorcore/sources/libs/quickjs/cutils.c @@ -0,0 +1,1407 @@ +/* + * C utilities + * + * Copyright (c) 2017 Fabrice Bellard + * Copyright (c) 2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif + +#include "cutils.h" + +#undef NANOSEC +#define NANOSEC ((uint64_t) 1e9) + +#pragma GCC visibility push(default) + +void pstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + if (buf_size <= 0) + return; + + for(;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = c; + } + *q = '\0'; +} + +/* strcat and truncate. */ +char *pstrcat(char *buf, int buf_size, const char *s) +{ + int len; + len = strlen(buf); + if (len < buf_size) + pstrcpy(buf + len, buf_size - len, s); + return buf; +} + +int strstart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (*p != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +int has_suffix(const char *str, const char *suffix) +{ + size_t len = strlen(str); + size_t slen = strlen(suffix); + return (len >= slen && !memcmp(str + len - slen, suffix, slen)); +} + +/* Dynamic buffer package */ + +static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func) +{ + memset(s, 0, sizeof(*s)); + if (!realloc_func) + realloc_func = dbuf_default_realloc; + s->opaque = opaque; + s->realloc_func = realloc_func; +} + +void dbuf_init(DynBuf *s) +{ + dbuf_init2(s, NULL, NULL); +} + +/* return < 0 if error */ +int dbuf_realloc(DynBuf *s, size_t new_size) +{ + size_t size; + uint8_t *new_buf; + if (new_size > s->allocated_size) { + if (s->error) + return -1; + size = s->allocated_size * 3 / 2; + if (size > new_size) + new_size = size; + new_buf = s->realloc_func(s->opaque, s->buf, new_size); + if (!new_buf) { + s->error = TRUE; + return -1; + } + s->buf = new_buf; + s->allocated_size = new_size; + } + return 0; +} + +int dbuf_write(DynBuf *s, size_t offset, const void *data, size_t len) +{ + size_t end; + end = offset + len; + if (dbuf_realloc(s, end)) + return -1; + memcpy(s->buf + offset, data, len); + if (end > s->size) + s->size = end; + return 0; +} + +int dbuf_put(DynBuf *s, const void *data, size_t len) +{ + if (unlikely((s->size + len) > s->allocated_size)) { + if (dbuf_realloc(s, s->size + len)) + return -1; + } + if (len > 0) { + memcpy(s->buf + s->size, data, len); + s->size += len; + } + return 0; +} + +int dbuf_put_self(DynBuf *s, size_t offset, size_t len) +{ + if (unlikely((s->size + len) > s->allocated_size)) { + if (dbuf_realloc(s, s->size + len)) + return -1; + } + memcpy(s->buf + s->size, s->buf + offset, len); + s->size += len; + return 0; +} + +int dbuf_putc(DynBuf *s, uint8_t c) +{ + return dbuf_put(s, &c, 1); +} + +int dbuf_putstr(DynBuf *s, const char *str) +{ + return dbuf_put(s, (const uint8_t *)str, strlen(str)); +} + +int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, + const char *fmt, ...) +{ + va_list ap; + char buf[128]; + int len; + + va_start(ap, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if (len < sizeof(buf)) { + /* fast case */ + return dbuf_put(s, (uint8_t *)buf, len); + } else { + if (dbuf_realloc(s, s->size + len + 1)) + return -1; + va_start(ap, fmt); + vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size, + fmt, ap); + va_end(ap); + s->size += len; + } + return 0; +} + +void dbuf_free(DynBuf *s) +{ + /* we test s->buf as a fail safe to avoid crashing if dbuf_free() + is called twice */ + if (s->buf) { + s->realloc_func(s->opaque, s->buf, 0); + } + memset(s, 0, sizeof(*s)); +} + +/*--- UTF-8 utility functions --*/ + +/* Note: only encode valid codepoints (0x0000..0x10FFFF). + At most UTF8_CHAR_LEN_MAX bytes are output. */ + +/* Compute the number of bytes of the UTF-8 encoding for a codepoint + `c` is a code-point. + Returns the number of bytes. If a codepoint is beyond 0x10FFFF the + return value is 3 as the codepoint would be encoded as 0xFFFD. + */ +size_t utf8_encode_len(uint32_t c) +{ + if (c < 0x80) + return 1; + if (c < 0x800) + return 2; + if (c < 0x10000) + return 3; + if (c < 0x110000) + return 4; + return 3; +} + +/* Encode a codepoint in UTF-8 + `buf` points to an array of at least `UTF8_CHAR_LEN_MAX` bytes + `c` is a code-point. + Returns the number of bytes. If a codepoint is beyond 0x10FFFF the + return value is 3 and the codepoint is encoded as 0xFFFD. + No null byte is stored after the encoded bytes. + Return value is in range 1..4 + */ +size_t utf8_encode(uint8_t *buf, uint32_t c) +{ + if (c < 0x80) { + buf[0] = c; + return 1; + } + if (c < 0x800) { + buf[0] = (c >> 6) | 0xC0; + buf[1] = (c & 0x3F) | 0x80; + return 2; + } + if (c < 0x10000) { + buf[0] = (c >> 12) | 0xE0; + buf[1] = ((c >> 6) & 0x3F) | 0x80; + buf[2] = (c & 0x3F) | 0x80; + return 3; + } + if (c < 0x110000) { + buf[0] = (c >> 18) | 0xF0; + buf[1] = ((c >> 12) & 0x3F) | 0x80; + buf[2] = ((c >> 6) & 0x3F) | 0x80; + buf[3] = (c & 0x3F) | 0x80; + return 4; + } + buf[0] = (0xFFFD >> 12) | 0xE0; + buf[1] = ((0xFFFD >> 6) & 0x3F) | 0x80; + buf[2] = (0xFFFD & 0x3F) | 0x80; + return 3; +} + +/* Decode a single code point from a UTF-8 encoded array of bytes + `p` is a valid pointer to an array of bytes + `pp` is a valid pointer to a `const uint8_t *` to store a pointer + to the byte following the current sequence. + Return the code point at `p`, in the range `0..0x10FFFF` + Return 0xFFFD on error. Only a single byte is consumed in this case + The maximum length for a UTF-8 byte sequence is 4 bytes. + This implements the algorithm specified in whatwg.org, except it accepts + UTF-8 encoded surrogates as JavaScript allows them in strings. + The source string is assumed to have at least UTF8_CHAR_LEN_MAX bytes + or be null terminated. + If `p[0]` is '\0', the return value is `0` and the byte is consumed. + cf: https://encoding.spec.whatwg.org/#utf-8-encoder + */ +uint32_t utf8_decode(const uint8_t *p, const uint8_t **pp) +{ + uint32_t c; + uint8_t lower, upper; + + c = *p++; + if (c < 0x80) { + *pp = p; + return c; + } + switch(c) { + case 0xC2: case 0xC3: + case 0xC4: case 0xC5: case 0xC6: case 0xC7: + case 0xC8: case 0xC9: case 0xCA: case 0xCB: + case 0xCC: case 0xCD: case 0xCE: case 0xCF: + case 0xD0: case 0xD1: case 0xD2: case 0xD3: + case 0xD4: case 0xD5: case 0xD6: case 0xD7: + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + if (*p >= 0x80 && *p <= 0xBF) { + *pp = p + 1; + return ((c - 0xC0) << 6) + (*p - 0x80); + } + // otherwise encoding error + break; + case 0xE0: + lower = 0xA0; /* reject invalid encoding */ + goto need2; + case 0xE1: case 0xE2: case 0xE3: + case 0xE4: case 0xE5: case 0xE6: case 0xE7: + case 0xE8: case 0xE9: case 0xEA: case 0xEB: + case 0xEC: case 0xED: case 0xEE: case 0xEF: + lower = 0x80; + need2: + if (*p >= lower && *p <= 0xBF && p[1] >= 0x80 && p[1] <= 0xBF) { + *pp = p + 2; + return ((c - 0xE0) << 12) + ((*p - 0x80) << 6) + (p[1] - 0x80); + } + // otherwise encoding error + break; + case 0xF0: + lower = 0x90; /* reject invalid encoding */ + upper = 0xBF; + goto need3; + case 0xF4: + lower = 0x80; + upper = 0x8F; /* reject values above 0x10FFFF */ + goto need3; + case 0xF1: case 0xF2: case 0xF3: + lower = 0x80; + upper = 0xBF; + need3: + if (*p >= lower && *p <= upper && p[1] >= 0x80 && p[1] <= 0xBF + && p[2] >= 0x80 && p[2] <= 0xBF) { + *pp = p + 3; + return ((c - 0xF0) << 18) + ((*p - 0x80) << 12) + + ((p[1] - 0x80) << 6) + (p[2] - 0x80); + } + // otherwise encoding error + break; + default: + // invalid lead byte + break; + } + *pp = p; + return 0xFFFD; +} + +uint32_t utf8_decode_len(const uint8_t *p, size_t max_len, const uint8_t **pp) { + switch (max_len) { + case 0: + *pp = p; + return 0xFFFD; + case 1: + if (*p < 0x80) + goto good; + break; + case 2: + if (*p < 0xE0) + goto good; + break; + case 3: + if (*p < 0xF0) + goto good; + break; + default: + good: + return utf8_decode(p, pp); + } + *pp = p + 1; + return 0xFFFD; +} + +/* Scan a UTF-8 encoded buffer for content type + `buf` is a valid pointer to a UTF-8 encoded string + `len` is the number of bytes to scan + `plen` points to a `size_t` variable to receive the number of units + Return value is a mask of bits. + - `UTF8_PLAIN_ASCII`: return value for 7-bit ASCII plain text + - `UTF8_NON_ASCII`: bit for non ASCII code points (8-bit or more) + - `UTF8_HAS_16BIT`: bit for 16-bit code points + - `UTF8_HAS_NON_BMP1`: bit for non-BMP1 code points, needs UTF-16 surrogate pairs + - `UTF8_HAS_ERRORS`: bit for encoding errors + */ +int utf8_scan(const char *buf, size_t buf_len, size_t *plen) +{ + const uint8_t *p, *p_end, *p_next; + size_t i, len; + int kind; + uint8_t cbits; + + kind = UTF8_PLAIN_ASCII; + cbits = 0; + len = buf_len; + // TODO: handle more than 1 byte at a time + for (i = 0; i < buf_len; i++) + cbits |= buf[i]; + if (cbits >= 0x80) { + p = (const uint8_t *)buf; + p_end = p + buf_len; + kind = UTF8_NON_ASCII; + len = 0; + while (p < p_end) { + len++; + if (*p++ >= 0x80) { + /* parse UTF-8 sequence, check for encoding error */ + uint32_t c = utf8_decode_len(p - 1, p_end - (p - 1), &p_next); + if (p_next == p) + kind |= UTF8_HAS_ERRORS; + p = p_next; + if (c > 0xFF) { + kind |= UTF8_HAS_16BIT; + if (c > 0xFFFF) { + len++; + kind |= UTF8_HAS_NON_BMP1; + } + } + } + } + } + *plen = len; + return kind; +} + +/* Decode a string encoded in UTF-8 into an array of bytes + `src` points to the source string. It is assumed to be correctly encoded + and only contains code points below 0x800 + `src_len` is the length of the source string + `dest` points to the destination array, it can be null if `dest_len` is `0` + `dest_len` is the length of the destination array. A null + terminator is stored at the end of the array unless `dest_len` is `0`. + */ +size_t utf8_decode_buf8(uint8_t *dest, size_t dest_len, const char *src, size_t src_len) +{ + const uint8_t *p, *p_end; + size_t i; + + p = (const uint8_t *)src; + p_end = p + src_len; + for (i = 0; p < p_end; i++) { + uint32_t c = *p++; + if (c >= 0xC0) + c = (c << 6) + *p++ - ((0xC0 << 6) + 0x80); + if (i < dest_len) + dest[i] = c; + } + if (i < dest_len) + dest[i] = '\0'; + else if (dest_len > 0) + dest[dest_len - 1] = '\0'; + return i; +} + +/* Decode a string encoded in UTF-8 into an array of 16-bit words + `src` points to the source string. It is assumed to be correctly encoded. + `src_len` is the length of the source string + `dest` points to the destination array, it can be null if `dest_len` is `0` + `dest_len` is the length of the destination array. No null terminator is + stored at the end of the array. + */ +size_t utf8_decode_buf16(uint16_t *dest, size_t dest_len, const char *src, size_t src_len) +{ + const uint8_t *p, *p_end; + size_t i; + + p = (const uint8_t *)src; + p_end = p + src_len; + for (i = 0; p < p_end; i++) { + uint32_t c = *p++; + if (c >= 0x80) { + /* parse utf-8 sequence */ + c = utf8_decode_len(p - 1, p_end - (p - 1), &p); + /* encoding errors are converted as 0xFFFD and use a single byte */ + if (c > 0xFFFF) { + if (i < dest_len) + dest[i] = get_hi_surrogate(c); + i++; + c = get_lo_surrogate(c); + } + } + if (i < dest_len) + dest[i] = c; + } + return i; +} + +/* Encode a buffer of 8-bit bytes as a UTF-8 encoded string + `src` points to the source buffer. + `src_len` is the length of the source buffer + `dest` points to the destination array, it can be null if `dest_len` is `0` + `dest_len` is the length in bytes of the destination array. A null + terminator is stored at the end of the array unless `dest_len` is `0`. + */ +size_t utf8_encode_buf8(char *dest, size_t dest_len, const uint8_t *src, size_t src_len) +{ + size_t i, j; + uint32_t c; + + for (i = j = 0; i < src_len; i++) { + c = src[i]; + if (c < 0x80) { + if (j + 1 >= dest_len) + goto overflow; + dest[j++] = c; + } else { + if (j + 2 >= dest_len) + goto overflow; + dest[j++] = (c >> 6) | 0xC0; + dest[j++] = (c & 0x3F) | 0x80; + } + } + if (j < dest_len) + dest[j] = '\0'; + return j; + +overflow: + if (j < dest_len) + dest[j] = '\0'; + while (i < src_len) + j += 1 + (src[i++] >= 0x80); + return j; +} + +/* Encode a buffer of 16-bit code points as a UTF-8 encoded string + `src` points to the source buffer. + `src_len` is the length of the source buffer + `dest` points to the destination array, it can be null if `dest_len` is `0` + `dest_len` is the length in bytes of the destination array. A null + terminator is stored at the end of the array unless `dest_len` is `0`. + */ +size_t utf8_encode_buf16(char *dest, size_t dest_len, const uint16_t *src, size_t src_len) +{ + size_t i, j; + uint32_t c; + + for (i = j = 0; i < src_len;) { + c = src[i++]; + if (c < 0x80) { + if (j + 1 >= dest_len) + goto overflow; + dest[j++] = c; + } else { + if (is_hi_surrogate(c) && i < src_len && is_lo_surrogate(src[i])) + c = from_surrogate(c, src[i++]); + if (j + utf8_encode_len(c) >= dest_len) + goto overflow; + j += utf8_encode((uint8_t *)dest + j, c); + } + } + if (j < dest_len) + dest[j] = '\0'; + return j; + +overflow: + i -= 1 + (c > 0xFFFF); + if (j < dest_len) + dest[j] = '\0'; + while (i < src_len) { + c = src[i++]; + if (c < 0x80) { + j++; + } else { + if (is_hi_surrogate(c) && i < src_len && is_lo_surrogate(src[i])) + c = from_surrogate(c, src[i++]); + j += utf8_encode_len(c); + } + } + return j; +} + +/*--- integer to string conversions --*/ + +/* All conversion functions: + - require a destination array `buf` of sufficient length + - write the string representation at the beginning of `buf` + - null terminate the string + - return the string length + */ + +/* 2 <= base <= 36 */ +char const digits36[36] = "0123456789abcdefghijklmnopqrstuvwxyz"; + +#define USE_SPECIAL_RADIX_10 1 // special case base 10 radix conversions +#define USE_SINGLE_CASE_FAST 1 // special case single digit numbers + +/* using u32toa_shift variant */ + +#define gen_digit(buf, c) if (is_be()) \ + buf = (buf >> 8) | ((uint64_t)(c) << ((sizeof(buf) - 1) * 8)); \ + else \ + buf = (buf << 8) | (c) + +size_t u7toa_shift(char dest[minimum_length(8)], uint32_t n) +{ + size_t len = 1; + uint64_t buf = 0; + while (n >= 10) { + uint32_t quo = n % 10; + n /= 10; + gen_digit(buf, '0' + quo); + len++; + } + gen_digit(buf, '0' + n); + memcpy(dest, &buf, sizeof buf); + return len; +} + +size_t u07toa_shift(char dest[minimum_length(8)], uint32_t n, size_t len) +{ + size_t i; + dest += len; + dest[7] = '\0'; + for (i = 7; i-- > 1;) { + uint32_t quo = n % 10; + n /= 10; + dest[i] = (char)('0' + quo); + } + dest[i] = (char)('0' + n); + return len + 7; +} + +size_t u32toa(char buf[minimum_length(11)], uint32_t n) +{ +#ifdef USE_SINGLE_CASE_FAST /* 10% */ + if (n < 10) { + buf[0] = (char)('0' + n); + buf[1] = '\0'; + return 1; + } +#endif +#define TEN_POW_7 10000000 + if (n >= TEN_POW_7) { + uint32_t quo = n / TEN_POW_7; + n %= TEN_POW_7; + size_t len = u7toa_shift(buf, quo); + return u07toa_shift(buf, n, len); + } + return u7toa_shift(buf, n); +} + +size_t u64toa(char buf[minimum_length(21)], uint64_t n) +{ + if (likely(n < 0x100000000)) + return u32toa(buf, n); + + size_t len; + if (n >= TEN_POW_7) { + uint64_t n1 = n / TEN_POW_7; + n %= TEN_POW_7; + if (n1 >= TEN_POW_7) { + uint32_t quo = n1 / TEN_POW_7; + n1 %= TEN_POW_7; + len = u7toa_shift(buf, quo); + len = u07toa_shift(buf, n1, len); + } else { + len = u7toa_shift(buf, n1); + } + return u07toa_shift(buf, n, len); + } + return u7toa_shift(buf, n); +} + +size_t i32toa(char buf[minimum_length(12)], int32_t n) +{ + if (likely(n >= 0)) + return u32toa(buf, n); + + buf[0] = '-'; + return 1 + u32toa(buf + 1, -(uint32_t)n); +} + +size_t i64toa(char buf[minimum_length(22)], int64_t n) +{ + if (likely(n >= 0)) + return u64toa(buf, n); + + buf[0] = '-'; + return 1 + u64toa(buf + 1, -(uint64_t)n); +} + +/* using u32toa_radix_length variant */ + +static uint8_t const radix_shift[64] = { + 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +size_t u32toa_radix(char buf[minimum_length(33)], uint32_t n, unsigned base) +{ + int shift; + +#ifdef USE_SPECIAL_RADIX_10 + if (likely(base == 10)) + return u32toa(buf, n); +#endif + if (n < base) { + buf[0] = digits36[n]; + buf[1] = '\0'; + return 1; + } + shift = radix_shift[base & 63]; + if (shift) { + uint32_t mask = (1 << shift) - 1; + size_t len = (32 - clz32(n) + shift - 1) / shift; + size_t last = n & mask; + char *end = buf + len; + n >>= shift; + *end-- = '\0'; + *end-- = digits36[last]; + while (n >= base) { + size_t quo = n & mask; + n >>= shift; + *end-- = digits36[quo]; + } + *end = digits36[n]; + return len; + } else { + size_t len = 2; + size_t last = n % base; + n /= base; + uint32_t nbase = base; + while (n >= nbase) { + nbase *= base; + len++; + } + char *end = buf + len; + *end-- = '\0'; + *end-- = digits36[last]; + while (n >= base) { + size_t quo = n % base; + n /= base; + *end-- = digits36[quo]; + } + *end = digits36[n]; + return len; + } +} + +size_t u64toa_radix(char buf[minimum_length(65)], uint64_t n, unsigned base) +{ + int shift; + +#ifdef USE_SPECIAL_RADIX_10 + if (likely(base == 10)) + return u64toa(buf, n); +#endif + shift = radix_shift[base & 63]; + if (shift) { + if (n < base) { + buf[0] = digits36[n]; + buf[1] = '\0'; + return 1; + } + uint64_t mask = (1 << shift) - 1; + size_t len = (64 - clz64(n) + shift - 1) / shift; + size_t last = n & mask; + char *end = buf + len; + n >>= shift; + *end-- = '\0'; + *end-- = digits36[last]; + while (n >= base) { + size_t quo = n & mask; + n >>= shift; + *end-- = digits36[quo]; + } + *end = digits36[n]; + return len; + } else { + if (likely(n < 0x100000000)) + return u32toa_radix(buf, n, base); + size_t last = n % base; + n /= base; + uint64_t nbase = base; + size_t len = 2; + while (n >= nbase) { + nbase *= base; + len++; + } + char *end = buf + len; + *end-- = '\0'; + *end-- = digits36[last]; + while (n >= base) { + size_t quo = n % base; + n /= base; + *end-- = digits36[quo]; + } + *end = digits36[n]; + return len; + } +} + +size_t i32toa_radix(char buf[minimum_length(34)], int32_t n, unsigned int base) +{ + if (likely(n >= 0)) + return u32toa_radix(buf, n, base); + + buf[0] = '-'; + return 1 + u32toa_radix(buf + 1, -(uint32_t)n, base); +} + +size_t i64toa_radix(char buf[minimum_length(66)], int64_t n, unsigned int base) +{ + if (likely(n >= 0)) + return u64toa_radix(buf, n, base); + + buf[0] = '-'; + return 1 + u64toa_radix(buf + 1, -(uint64_t)n, base); +} + +#undef gen_digit +#undef TEN_POW_7 +#undef USE_SPECIAL_RADIX_10 +#undef USE_SINGLE_CASE_FAST + +/*---- sorting with opaque argument ----*/ + +typedef void (*exchange_f)(void *a, void *b, size_t size); +typedef int (*cmp_f)(const void *, const void *, void *opaque); + +static void exchange_bytes(void *a, void *b, size_t size) { + uint8_t *ap = (uint8_t *)a; + uint8_t *bp = (uint8_t *)b; + + while (size-- != 0) { + uint8_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_byte(void *a, void *b, size_t size) { + uint8_t *ap = (uint8_t *)a; + uint8_t *bp = (uint8_t *)b; + uint8_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int16s(void *a, void *b, size_t size) { + uint16_t *ap = (uint16_t *)a; + uint16_t *bp = (uint16_t *)b; + + for (size /= sizeof(uint16_t); size-- != 0;) { + uint16_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_int16(void *a, void *b, size_t size) { + uint16_t *ap = (uint16_t *)a; + uint16_t *bp = (uint16_t *)b; + uint16_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int32s(void *a, void *b, size_t size) { + uint32_t *ap = (uint32_t *)a; + uint32_t *bp = (uint32_t *)b; + + for (size /= sizeof(uint32_t); size-- != 0;) { + uint32_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_int32(void *a, void *b, size_t size) { + uint32_t *ap = (uint32_t *)a; + uint32_t *bp = (uint32_t *)b; + uint32_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int64s(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + + for (size /= sizeof(uint64_t); size-- != 0;) { + uint64_t t = *ap; + *ap++ = *bp; + *bp++ = t; + } +} + +static void exchange_one_int64(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + uint64_t t = *ap; + *ap = *bp; + *bp = t; +} + +static void exchange_int128s(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + + for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) { + uint64_t t = ap[0]; + uint64_t u = ap[1]; + ap[0] = bp[0]; + ap[1] = bp[1]; + bp[0] = t; + bp[1] = u; + } +} + +static void exchange_one_int128(void *a, void *b, size_t size) { + uint64_t *ap = (uint64_t *)a; + uint64_t *bp = (uint64_t *)b; + uint64_t t = ap[0]; + uint64_t u = ap[1]; + ap[0] = bp[0]; + ap[1] = bp[1]; + bp[0] = t; + bp[1] = u; +} + +static inline exchange_f exchange_func(const void *base, size_t size) { + switch (((uintptr_t)base | (uintptr_t)size) & 15) { + case 0: + if (size == sizeof(uint64_t) * 2) + return exchange_one_int128; + else + return exchange_int128s; + case 8: + if (size == sizeof(uint64_t)) + return exchange_one_int64; + else + return exchange_int64s; + case 4: + case 12: + if (size == sizeof(uint32_t)) + return exchange_one_int32; + else + return exchange_int32s; + case 2: + case 6: + case 10: + case 14: + if (size == sizeof(uint16_t)) + return exchange_one_int16; + else + return exchange_int16s; + default: + if (size == 1) + return exchange_one_byte; + else + return exchange_bytes; + } +} + +static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) +{ + uint8_t *basep = (uint8_t *)base; + size_t i, n, c, r; + exchange_f swap = exchange_func(base, size); + + if (nmemb > 1) { + i = (nmemb / 2) * size; + n = nmemb * size; + + while (i > 0) { + i -= size; + for (r = i; (c = r * 2 + size) < n; r = c) { + if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0) + c += size; + if (cmp(basep + r, basep + c, opaque) > 0) + break; + swap(basep + r, basep + c, size); + } + } + for (i = n - size; i > 0; i -= size) { + swap(basep, basep + i, size); + + for (r = 0; (c = r * 2 + size) < i; r = c) { + if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0) + c += size; + if (cmp(basep + r, basep + c, opaque) > 0) + break; + swap(basep + r, basep + c, size); + } + } + } +} + +static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque) +{ + return cmp(a, b, opaque) < 0 ? + (cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) : + (cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c )); +} + +/* pointer based version with local stack and insertion sort threshhold */ +void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque) +{ + struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack; + uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m; + size_t m4, i, lt, gt, span, span2; + int c, depth; + exchange_f swap = exchange_func(base, size); + exchange_f swap_block = exchange_func(base, size | 128); + + if (nmemb < 2 || size <= 0) + return; + + sp->base = (uint8_t *)base; + sp->count = nmemb; + sp->depth = 0; + sp++; + + while (sp > stack) { + sp--; + ptr = sp->base; + nmemb = sp->count; + depth = sp->depth; + + while (nmemb > 6) { + if (++depth > 50) { + /* depth check to ensure worst case logarithmic time */ + heapsortx(ptr, nmemb, size, cmp, opaque); + nmemb = 0; + break; + } + /* select median of 3 from 1/4, 1/2, 3/4 positions */ + /* should use median of 5 or 9? */ + m4 = (nmemb >> 2) * size; + m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque); + swap(ptr, m, size); /* move the pivot to the start or the array */ + i = lt = 1; + pi = plt = ptr + size; + gt = nmemb; + pj = pgt = top = ptr + nmemb * size; + for (;;) { + while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) { + if (c == 0) { + swap(plt, pi, size); + lt++; + plt += size; + } + i++; + pi += size; + } + while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) { + if (c == 0) { + gt--; + pgt -= size; + swap(pgt, pj, size); + } + } + if (pi >= pj) + break; + swap(pi, pj, size); + i++; + pi += size; + } + /* array has 4 parts: + * from 0 to lt excluded: elements identical to pivot + * from lt to pi excluded: elements smaller than pivot + * from pi to gt excluded: elements greater than pivot + * from gt to n excluded: elements identical to pivot + */ + /* move elements identical to pivot in the middle of the array: */ + /* swap values in ranges [0..lt[ and [i-lt..i[ + swapping the smallest span between lt and i-lt is sufficient + */ + span = plt - ptr; + span2 = pi - plt; + lt = i - lt; + if (span > span2) + span = span2; + swap_block(ptr, pi - span, span); + /* swap values in ranges [gt..top[ and [i..top-(top-gt)[ + swapping the smallest span between top-gt and gt-i is sufficient + */ + span = top - pgt; + span2 = pgt - pi; + pgt = top - span2; + gt = nmemb - (gt - i); + if (span > span2) + span = span2; + swap_block(pi, top - span, span); + + /* now array has 3 parts: + * from 0 to lt excluded: elements smaller than pivot + * from lt to gt excluded: elements identical to pivot + * from gt to n excluded: elements greater than pivot + */ + /* stack the larger segment and keep processing the smaller one + to minimize stack use for pathological distributions */ + if (lt > nmemb - gt) { + sp->base = ptr; + sp->count = lt; + sp->depth = depth; + sp++; + ptr = pgt; + nmemb -= gt; + } else { + sp->base = pgt; + sp->count = nmemb - gt; + sp->depth = depth; + sp++; + nmemb = lt; + } + } + /* Use insertion sort for small fragments */ + for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) { + for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size) + swap(pj, pj - size, size); + } + } +} + +/*---- Portable time functions ----*/ + +#if defined(_MSC_VER) + // From: https://stackoverflow.com/a/26085827 +static int gettimeofday_msvc(struct timeval *tp, struct timezone *tzp) +{ + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + + return 0; +} + +uint64_t js__hrtime_ns(void) { + LARGE_INTEGER counter, frequency; + double scaled_freq; + double result; + + if (!QueryPerformanceFrequency(&frequency)) + abort(); + assert(frequency.QuadPart != 0); + + if (!QueryPerformanceCounter(&counter)) + abort(); + assert(counter.QuadPart != 0); + + /* Because we have no guarantee about the order of magnitude of the + * performance counter interval, integer math could cause this computation + * to overflow. Therefore we resort to floating point math. + */ + scaled_freq = (double) frequency.QuadPart / NANOSEC; + result = (double) counter.QuadPart / scaled_freq; + return (uint64_t) result; +} +#else +uint64_t js__hrtime_ns(void) { + struct timespec t; + + if (clock_gettime(CLOCK_MONOTONIC, &t)) + abort(); + + return t.tv_sec * NANOSEC + t.tv_nsec; +} +#endif + +int64_t js__gettimeofday_us(void) { + struct timeval tv; +#if defined(_MSC_VER) + gettimeofday_msvc(&tv, NULL); +#else + gettimeofday(&tv, NULL); +#endif + return ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec; +} + +/*--- Cross-platform threading APIs. ----*/ + +#if !defined(EMSCRIPTEN) && !defined(__wasi__) + +#if defined(_WIN32) +typedef void (*js__once_cb)(void); + +typedef struct { + js__once_cb callback; +} js__once_data_t; + +static BOOL WINAPI js__once_inner(INIT_ONCE *once, void *param, void **context) { + js__once_data_t *data = param; + + data->callback(); + + return TRUE; +} + +void js_once(js_once_t *guard, js__once_cb callback) { + js__once_data_t data = { .callback = callback }; + InitOnceExecuteOnce(guard, js__once_inner, (void*) &data, NULL); +} + +void js_mutex_init(js_mutex_t *mutex) { + InitializeCriticalSection(mutex); +} + +void js_mutex_destroy(js_mutex_t *mutex) { + DeleteCriticalSection(mutex); +} + +void js_mutex_lock(js_mutex_t *mutex) { + EnterCriticalSection(mutex); +} + +void js_mutex_unlock(js_mutex_t *mutex) { + LeaveCriticalSection(mutex); +} + +void js_cond_init(js_cond_t *cond) { + InitializeConditionVariable(cond); +} + +void js_cond_destroy(js_cond_t *cond) { + /* nothing to do */ + (void) cond; +} + +void js_cond_signal(js_cond_t *cond) { + WakeConditionVariable(cond); +} + +void js_cond_broadcast(js_cond_t *cond) { + WakeAllConditionVariable(cond); +} + +void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex) { + if (!SleepConditionVariableCS(cond, mutex, INFINITE)) + abort(); +} + +int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) { + if (SleepConditionVariableCS(cond, mutex, (DWORD)(timeout / 1e6))) + return 0; + if (GetLastError() != ERROR_TIMEOUT) + abort(); + return -1; +} + +#else /* !defined(_WIN32) */ + +void js_once(js_once_t *guard, void (*callback)(void)) { + if (pthread_once(guard, callback)) + abort(); +} + +void js_mutex_init(js_mutex_t *mutex) { + if (pthread_mutex_init(mutex, NULL)) + abort(); +} + +void js_mutex_destroy(js_mutex_t *mutex) { + if (pthread_mutex_destroy(mutex)) + abort(); +} + +void js_mutex_lock(js_mutex_t *mutex) { + if (pthread_mutex_lock(mutex)) + abort(); +} + +void js_mutex_unlock(js_mutex_t *mutex) { + if (pthread_mutex_unlock(mutex)) + abort(); +} + +void js_cond_init(js_cond_t *cond) { +#if defined(__APPLE__) && defined(__MACH__) + if (pthread_cond_init(cond, NULL)) + abort(); +#else + pthread_condattr_t attr; + + if (pthread_condattr_init(&attr)) + abort(); + + if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) + abort(); + + if (pthread_cond_init(cond, &attr)) + abort(); + + if (pthread_condattr_destroy(&attr)) + abort(); +#endif +} + +void js_cond_destroy(js_cond_t *cond) { +#if defined(__APPLE__) && defined(__MACH__) + /* It has been reported that destroying condition variables that have been + * signalled but not waited on can sometimes result in application crashes. + * See https://codereview.chromium.org/1323293005. + */ + pthread_mutex_t mutex; + struct timespec ts; + int err; + + if (pthread_mutex_init(&mutex, NULL)) + abort(); + + if (pthread_mutex_lock(&mutex)) + abort(); + + ts.tv_sec = 0; + ts.tv_nsec = 1; + + err = pthread_cond_timedwait_relative_np(cond, &mutex, &ts); + if (err != 0 && err != ETIMEDOUT) + abort(); + + if (pthread_mutex_unlock(&mutex)) + abort(); + + if (pthread_mutex_destroy(&mutex)) + abort(); +#endif /* defined(__APPLE__) && defined(__MACH__) */ + + if (pthread_cond_destroy(cond)) + abort(); +} + +void js_cond_signal(js_cond_t *cond) { + if (pthread_cond_signal(cond)) + abort(); +} + +void js_cond_broadcast(js_cond_t *cond) { + if (pthread_cond_broadcast(cond)) + abort(); +} + +void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex) { +#if defined(__APPLE__) && defined(__MACH__) + int r; + + errno = 0; + r = pthread_cond_wait(cond, mutex); + + /* Workaround for a bug in OS X at least up to 13.6 + * See https://github.com/libuv/libuv/issues/4165 + */ + if (r == EINVAL && errno == EBUSY) + return; + if (r) + abort(); +#else + if (pthread_cond_wait(cond, mutex)) + abort(); +#endif +} + +int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout) { + int r; + struct timespec ts; + +#if !defined(__APPLE__) + timeout += js__hrtime_ns(); +#endif + + ts.tv_sec = timeout / NANOSEC; + ts.tv_nsec = timeout % NANOSEC; +#if defined(__APPLE__) && defined(__MACH__) + r = pthread_cond_timedwait_relative_np(cond, mutex, &ts); +#else + r = pthread_cond_timedwait(cond, mutex, &ts); +#endif + + if (r == 0) + return 0; + + if (r == ETIMEDOUT) + return -1; + + abort(); + + /* Pacify some compilers. */ + return -1; +} + +#endif + +#endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */ + +#pragma GCC visibility pop diff --git a/armorcore/sources/libs/quickjs/cutils.h b/armorcore/sources/libs/quickjs/cutils.h new file mode 100644 index 000000000..aefb4dd1e --- /dev/null +++ b/armorcore/sources/libs/quickjs/cutils.h @@ -0,0 +1,539 @@ +/* + * C utilities + * + * Copyright (c) 2017 Fabrice Bellard + * Copyright (c) 2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CUTILS_H +#define CUTILS_H + +#include +#include +#include + +#if defined(_WIN32) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) +#include +#include +#define alloca _alloca +#define ssize_t ptrdiff_t +#endif +#if defined(__APPLE__) +#include +#elif defined(__linux__) || defined(__ANDROID__) || defined(__CYGWIN__) +#include +#elif defined(__FreeBSD__) +#include +#endif +#if !defined(_WIN32) +#include +#include +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +# define likely(x) (x) +# define unlikely(x) (x) +# define force_inline __forceinline +# define no_inline __declspec(noinline) +# define __maybe_unused +# define __attribute__(x) +# define __attribute(x) +# include +static void *__builtin_frame_address(unsigned int level) { + return (void *)((char*)_AddressOfReturnAddress() - sizeof(int *) - level * sizeof(int *)); +} +#else +# define likely(x) __builtin_expect(!!(x), 1) +# define unlikely(x) __builtin_expect(!!(x), 0) +# define force_inline inline __attribute__((always_inline)) +# define no_inline __attribute__((noinline)) +# define __maybe_unused __attribute__((unused)) +#endif + +// https://stackoverflow.com/a/6849629 +#undef FORMAT_STRING +#if _MSC_VER >= 1400 +# include +# if _MSC_VER > 1400 +# define FORMAT_STRING(p) _Printf_format_string_ p +# else +# define FORMAT_STRING(p) __format_string p +# endif /* FORMAT_STRING */ +#else +# define FORMAT_STRING(p) p +#endif /* _MSC_VER */ + +#if defined(_MSC_VER) && !defined(__clang__) +#include +#define INF INFINITY +#define NEG_INF -INFINITY +#else +#define INF (1.0/0.0) +#define NEG_INF (-1.0/0.0) +#endif + +#define xglue(x, y) x ## y +#define glue(x, y) xglue(x, y) +#define stringify(s) tostring(s) +#define tostring(s) #s + +#ifndef offsetof +#define offsetof(type, field) ((size_t) &((type *)0)->field) +#endif +#ifndef countof +#define countof(x) (sizeof(x) / sizeof((x)[0])) +#ifndef endof +#define endof(x) ((x) + countof(x)) +#endif +#endif +#ifndef container_of +/* return the pointer of type 'type *' containing 'ptr' as field 'member' */ +#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member))) +#endif + +#if !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define minimum_length(n) static n +#else +#define minimum_length(n) n +#endif + +typedef int BOOL; + +#ifndef FALSE +enum { + FALSE = 0, + TRUE = 1, +}; +#endif + +void pstrcpy(char *buf, int buf_size, const char *str); +char *pstrcat(char *buf, int buf_size, const char *s); +int strstart(const char *str, const char *val, const char **ptr); +int has_suffix(const char *str, const char *suffix); + +static inline uint8_t is_be(void) { + union { + uint16_t a; + uint8_t b; + } u = { 0x100 }; + return u.b; +} + +static inline int max_int(int a, int b) +{ + if (a > b) + return a; + else + return b; +} + +static inline int min_int(int a, int b) +{ + if (a < b) + return a; + else + return b; +} + +static inline uint32_t max_uint32(uint32_t a, uint32_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline uint32_t min_uint32(uint32_t a, uint32_t b) +{ + if (a < b) + return a; + else + return b; +} + +static inline int64_t max_int64(int64_t a, int64_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline int64_t min_int64(int64_t a, int64_t b) +{ + if (a < b) + return a; + else + return b; +} + +/* WARNING: undefined if a = 0 */ +static inline int clz32(unsigned int a) +{ +#if defined(_MSC_VER) && !defined(__clang__) + unsigned long index; + _BitScanReverse(&index, a); + return 31 - index; +#else + return __builtin_clz(a); +#endif +} + +/* WARNING: undefined if a = 0 */ +static inline int clz64(uint64_t a) +{ +#if defined(_MSC_VER) && !defined(__clang__) +#if INTPTR_MAX == INT64_MAX + unsigned long index; + _BitScanReverse64(&index, a); + return 63 - index; +#else + if (a >> 32) + return clz32((unsigned)(a >> 32)); + else + return clz32((unsigned)a) + 32; +#endif +#else + return __builtin_clzll(a); +#endif +} + +/* WARNING: undefined if a = 0 */ +static inline int ctz32(unsigned int a) +{ +#if defined(_MSC_VER) && !defined(__clang__) + unsigned long index; + _BitScanForward(&index, a); + return index; +#else + return __builtin_ctz(a); +#endif +} + +/* WARNING: undefined if a = 0 */ +static inline int ctz64(uint64_t a) +{ +#if defined(_MSC_VER) && !defined(__clang__) + unsigned long index; + _BitScanForward64(&index, a); + return index; +#else + return __builtin_ctzll(a); +#endif +} + +static inline uint64_t get_u64(const uint8_t *tab) +{ + uint64_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline int64_t get_i64(const uint8_t *tab) +{ + int64_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline void put_u64(uint8_t *tab, uint64_t val) +{ + memcpy(tab, &val, sizeof(val)); +} + +static inline uint32_t get_u32(const uint8_t *tab) +{ + uint32_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline int32_t get_i32(const uint8_t *tab) +{ + int32_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline void put_u32(uint8_t *tab, uint32_t val) +{ + memcpy(tab, &val, sizeof(val)); +} + +static inline uint32_t get_u16(const uint8_t *tab) +{ + uint16_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline int32_t get_i16(const uint8_t *tab) +{ + int16_t v; + memcpy(&v, tab, sizeof(v)); + return v; +} + +static inline void put_u16(uint8_t *tab, uint16_t val) +{ + memcpy(tab, &val, sizeof(val)); +} + +static inline uint32_t get_u8(const uint8_t *tab) +{ + return *tab; +} + +static inline int32_t get_i8(const uint8_t *tab) +{ + return (int8_t)*tab; +} + +static inline void put_u8(uint8_t *tab, uint8_t val) +{ + *tab = val; +} + +#ifndef bswap16 +static inline uint16_t bswap16(uint16_t x) +{ + return (x >> 8) | (x << 8); +} +#endif + +#ifndef bswap32 +static inline uint32_t bswap32(uint32_t v) +{ + return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | + ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); +} +#endif + +#ifndef bswap64 +static inline uint64_t bswap64(uint64_t v) +{ + return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | + ((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) | + ((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) | + ((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) | + ((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) | + ((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) | + ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | + ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); +} +#endif + +static inline void inplace_bswap16(uint8_t *tab) { + put_u16(tab, bswap16(get_u16(tab))); +} + +static inline void inplace_bswap32(uint8_t *tab) { + put_u32(tab, bswap32(get_u32(tab))); +} + +/* XXX: should take an extra argument to pass slack information to the caller */ +typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); + +typedef struct DynBuf { + uint8_t *buf; + size_t size; + size_t allocated_size; + BOOL error; /* true if a memory allocation error occurred */ + DynBufReallocFunc *realloc_func; + void *opaque; /* for realloc_func */ +} DynBuf; + +void dbuf_init(DynBuf *s); +void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func); +int dbuf_realloc(DynBuf *s, size_t new_size); +int dbuf_write(DynBuf *s, size_t offset, const void *data, size_t len); +int dbuf_put(DynBuf *s, const void *data, size_t len); +int dbuf_put_self(DynBuf *s, size_t offset, size_t len); +int dbuf_putc(DynBuf *s, uint8_t c); +int dbuf_putstr(DynBuf *s, const char *str); +static inline int dbuf_put_u16(DynBuf *s, uint16_t val) +{ + return dbuf_put(s, (uint8_t *)&val, 2); +} +static inline int dbuf_put_u32(DynBuf *s, uint32_t val) +{ + return dbuf_put(s, (uint8_t *)&val, 4); +} +static inline int dbuf_put_u64(DynBuf *s, uint64_t val) +{ + return dbuf_put(s, (uint8_t *)&val, 8); +} +int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, + FORMAT_STRING(const char *fmt), ...); +void dbuf_free(DynBuf *s); +static inline BOOL dbuf_error(DynBuf *s) { + return s->error; +} +static inline void dbuf_set_error(DynBuf *s) +{ + s->error = TRUE; +} + +/*---- UTF-8 and UTF-16 handling ----*/ + +#define UTF8_CHAR_LEN_MAX 4 + +enum { + UTF8_PLAIN_ASCII = 0, // 7-bit ASCII plain text + UTF8_NON_ASCII = 1, // has non ASCII code points (8-bit or more) + UTF8_HAS_16BIT = 2, // has 16-bit code points + UTF8_HAS_NON_BMP1 = 4, // has non-BMP1 code points, needs UTF-16 surrogate pairs + UTF8_HAS_ERRORS = 8, // has encoding errors +}; +int utf8_scan(const char *buf, size_t len, size_t *plen); +size_t utf8_encode_len(uint32_t c); +size_t utf8_encode(uint8_t *buf, uint32_t c); +uint32_t utf8_decode_len(const uint8_t *p, size_t max_len, const uint8_t **pp); +uint32_t utf8_decode(const uint8_t *p, const uint8_t **pp); +size_t utf8_decode_buf8(uint8_t *dest, size_t dest_len, const char *src, size_t src_len); +size_t utf8_decode_buf16(uint16_t *dest, size_t dest_len, const char *src, size_t src_len); +size_t utf8_encode_buf8(char *dest, size_t dest_len, const uint8_t *src, size_t src_len); +size_t utf8_encode_buf16(char *dest, size_t dest_len, const uint16_t *src, size_t src_len); + +static inline BOOL is_surrogate(uint32_t c) +{ + return (c >> 11) == (0xD800 >> 11); // 0xD800-0xDFFF +} + +static inline BOOL is_hi_surrogate(uint32_t c) +{ + return (c >> 10) == (0xD800 >> 10); // 0xD800-0xDBFF +} + +static inline BOOL is_lo_surrogate(uint32_t c) +{ + return (c >> 10) == (0xDC00 >> 10); // 0xDC00-0xDFFF +} + +static inline uint32_t get_hi_surrogate(uint32_t c) +{ + return (c >> 10) - (0x10000 >> 10) + 0xD800; +} + +static inline uint32_t get_lo_surrogate(uint32_t c) +{ + return (c & 0x3FF) | 0xDC00; +} + +static inline uint32_t from_surrogate(uint32_t hi, uint32_t lo) +{ + return 65536 + 1024 * (hi & 1023) + (lo & 1023); +} + +static inline int from_hex(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else + return -1; +} + +static inline uint8_t is_upper_ascii(uint8_t c) { + return c >= 'A' && c <= 'Z'; +} + +static inline uint8_t to_upper_ascii(uint8_t c) { + return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c; +} + +extern char const digits36[36]; +size_t u32toa(char buf[minimum_length(11)], uint32_t n); +size_t i32toa(char buf[minimum_length(12)], int32_t n); +size_t u64toa(char buf[minimum_length(21)], uint64_t n); +size_t i64toa(char buf[minimum_length(22)], int64_t n); +size_t u32toa_radix(char buf[minimum_length(33)], uint32_t n, unsigned int base); +size_t i32toa_radix(char buf[minimum_length(34)], int32_t n, unsigned base); +size_t u64toa_radix(char buf[minimum_length(65)], uint64_t n, unsigned int base); +size_t i64toa_radix(char buf[minimum_length(66)], int64_t n, unsigned int base); + +void rqsort(void *base, size_t nmemb, size_t size, + int (*cmp)(const void *, const void *, void *), + void *arg); + +int64_t js__gettimeofday_us(void); +uint64_t js__hrtime_ns(void); + +static inline size_t js__malloc_usable_size(const void *ptr) +{ +#if defined(__APPLE__) + return malloc_size(ptr); +#elif defined(_WIN32) + return _msize((void *)ptr); +#elif defined(__linux__) || defined(__ANDROID__) || defined(__CYGWIN__) || defined(__FreeBSD__) + return malloc_usable_size((void *)ptr); +#else + return 0; +#endif +} + +/* Cross-platform threading APIs. */ + +#if !defined(EMSCRIPTEN) && !defined(__wasi__) + +#if defined(_WIN32) +#define JS_ONCE_INIT INIT_ONCE_STATIC_INIT +typedef INIT_ONCE js_once_t; +typedef CRITICAL_SECTION js_mutex_t; +typedef CONDITION_VARIABLE js_cond_t; +#else +#define JS_ONCE_INIT PTHREAD_ONCE_INIT +typedef pthread_once_t js_once_t; +typedef pthread_mutex_t js_mutex_t; +typedef pthread_cond_t js_cond_t; +#endif + +void js_once(js_once_t *guard, void (*callback)(void)); + +void js_mutex_init(js_mutex_t *mutex); +void js_mutex_destroy(js_mutex_t *mutex); +void js_mutex_lock(js_mutex_t *mutex); +void js_mutex_unlock(js_mutex_t *mutex); + +void js_cond_init(js_cond_t *cond); +void js_cond_destroy(js_cond_t *cond); +void js_cond_signal(js_cond_t *cond); +void js_cond_broadcast(js_cond_t *cond); +void js_cond_wait(js_cond_t *cond, js_mutex_t *mutex); +int js_cond_timedwait(js_cond_t *cond, js_mutex_t *mutex, uint64_t timeout); + +#endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */ + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* CUTILS_H */ diff --git a/armorcore/sources/libs/quickjs/dirent_compat.h b/armorcore/sources/libs/quickjs/dirent_compat.h new file mode 100644 index 000000000..077674e45 --- /dev/null +++ b/armorcore/sources/libs/quickjs/dirent_compat.h @@ -0,0 +1,1166 @@ +/* + * Dirent interface for Microsoft Visual Studio + * + * Copyright (C) 1998-2019 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* Hide warnings about unreferenced local functions */ +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +#elif defined(_MSC_VER) +# pragma warning(disable:4505) +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of the file name without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return the maximum size of a file name */ +#define _D_ALLOC_NAMLEN(p) ((PATH_MAX)+1) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX+1]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +/* Multi-byte character version */ +struct dirent { + /* Always zero */ + long d_ino; + + /* File position within stream */ + long d_off; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX+1]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + + +/* Dirent functions */ +static DIR *opendir (const char *dirname); +static _WDIR *_wopendir (const wchar_t *dirname); + +static struct dirent *readdir (DIR *dirp); +static struct _wdirent *_wreaddir (_WDIR *dirp); + +static int readdir_r( + DIR *dirp, struct dirent *entry, struct dirent **result); +static int _wreaddir_r( + _WDIR *dirp, struct _wdirent *entry, struct _wdirent **result); + +static int closedir (DIR *dirp); +static int _wclosedir (_WDIR *dirp); + +static void rewinddir (DIR* dirp); +static void _wrewinddir (_WDIR* dirp); + +static int scandir (const char *dirname, struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)); + +static int alphasort (const struct dirent **a, const struct dirent **b); + +static int versionsort (const struct dirent **a, const struct dirent **b); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp; +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + DWORD n; +#else + /* WinRT */ + size_t n; +#endif + wchar_t *p; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (!dirp) { + return NULL; + } + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* + * Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, 0, NULL, NULL); +#else + /* WinRT */ + n = wcslen (dirname); +#endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt == NULL) { + goto exit_closedir; + } + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume it is an absolute path. + */ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + /* Desktop */ + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); + if (n <= 0) { + goto exit_closedir; + } +#else + /* WinRT */ + wcsncpy_s (dirp->patt, n+1, dirname, n); +#endif + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (!dirent_first (dirp)) { + goto exit_closedir; + } + + /* Success */ + return dirp; + + /* Failure */ +exit_closedir: + _wclosedir (dirp); + return NULL; +} + +/* + * Read next directory entry. + * + * Returns pointer to static directory entry which may be overwritten by + * subsequent calls to _wreaddir(). + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + struct _wdirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) _wreaddir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry. + * + * Returns zero on success. If end of directory stream is reached, then sets + * result to NULL and returns zero. + */ +static int +_wreaddir_r( + _WDIR *dirp, + struct _wdirent *entry, + struct _wdirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n < PATH_MAX && datap->cFileName[n] != 0) { + entry->d_name[n] = datap->cFileName[n]; + n++; + } + entry->d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct _wdirent); + + /* Set result address */ + *result = entry; + + } else { + + /* Return NULL to indicate end of directory */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Release search pattern */ + free (dirp->patt); + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + DWORD error; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + /* Set error code */ + error = GetLastError (); + switch (error) { + case ERROR_ACCESS_DENIED: + /* No read access to directory */ + dirent_set_errno (EACCES); + break; + + case ERROR_DIRECTORY: + /* Directory name is invalid */ + dirent_set_errno (ENOTDIR); + break; + + case ERROR_PATH_NOT_FOUND: + default: + /* Cannot find the file */ + dirent_set_errno (ENOENT); + } + + } + return datap; +} + +/* + * Get next directory entry (internal). + * + * Returns + */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occurred */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (!dirp) { + return NULL; + } + { + int error; + wchar_t wname[PATH_MAX + 1]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s( + &n, wname, PATH_MAX + 1, dirname, PATH_MAX + 1); + if (error) { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + goto exit_free; + } + + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (!dirp->wdirp) { + goto exit_free; + } + + } + + /* Success */ + return dirp; + + /* Failure */ +exit_free: + free (dirp); + return NULL; +} + +/* + * Read next directory entry. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + struct dirent *entry; + + /* + * Read directory entry to buffer. We can safely ignore the return value + * as entry will be set to NULL in case of error. + */ + (void) readdir_r (dirp, &dirp->ent, &entry); + + /* Return pointer to statically allocated directory entry */ + return entry; +} + +/* + * Read next directory entry into called-allocated buffer. + * + * Returns zero on success. If the end of directory stream is reached, then + * sets result to NULL and returns zero. + */ +static int +readdir_r( + DIR *dirp, + struct dirent *entry, + struct dirent **result) +{ + WIN32_FIND_DATAW *datap; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, datap->cFileName, PATH_MAX + 1); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, entry->d_name, PATH_MAX + 1, + datap->cAlternateFileName, PATH_MAX + 1); + } + + if (!error) { + DWORD attr; + + /* Length of file name excluding zero terminator */ + entry->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entry->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entry->d_type = DT_DIR; + } else { + entry->d_type = DT_REG; + } + + /* Reset dummy fields */ + entry->d_ino = 0; + entry->d_off = 0; + entry->d_reclen = sizeof (struct dirent); + + } else { + + /* + * Cannot convert file name to multi-byte string so construct + * an erroneous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entry->d_name[0] = '?'; + entry->d_name[1] = '\0'; + entry->d_namlen = 1; + entry->d_type = DT_UNKNOWN; + entry->d_ino = 0; + entry->d_off = -1; + entry->d_reclen = 0; + + } + + /* Return pointer to directory entry */ + *result = entry; + + } else { + + /* No more directory entries */ + *result = NULL; + + } + + return /*OK*/0; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* + * Scan directory for entries. + */ +static int +scandir( + const char *dirname, + struct dirent ***namelist, + int (*filter)(const struct dirent*), + int (*compare)(const struct dirent**, const struct dirent**)) +{ + struct dirent **files = NULL; + size_t size = 0; + size_t allocated = 0; + const size_t init_size = 1; + DIR *dir = NULL; + struct dirent *entry; + struct dirent *tmp = NULL; + size_t i; + int result = 0; + + /* Open directory stream */ + dir = opendir (dirname); + if (dir) { + + /* Read directory entries to memory */ + while (1) { + + /* Enlarge pointer table to make room for another pointer */ + if (size >= allocated) { + void *p; + size_t num_entries; + + /* Compute number of entries in the enlarged pointer table */ + if (size < init_size) { + /* Allocate initial pointer table */ + num_entries = init_size; + } else { + /* Double the size */ + num_entries = size * 2; + } + + /* Allocate first pointer table or enlarge existing table */ + p = realloc (files, sizeof (void*) * num_entries); + if (p != NULL) { + /* Got the memory */ + files = (dirent**) p; + allocated = num_entries; + } else { + /* Out of memory */ + result = -1; + break; + } + + } + + /* Allocate room for temporary directory entry */ + if (tmp == NULL) { + tmp = (struct dirent*) malloc (sizeof (struct dirent)); + if (tmp == NULL) { + /* Cannot allocate temporary directory entry */ + result = -1; + break; + } + } + + /* Read directory entry to temporary area */ + if (readdir_r (dir, tmp, &entry) == /*OK*/0) { + + /* Did we get an entry? */ + if (entry != NULL) { + int pass; + + /* Determine whether to include the entry in result */ + if (filter) { + /* Let the filter function decide */ + pass = filter (tmp); + } else { + /* No filter function, include everything */ + pass = 1; + } + + if (pass) { + /* Store the temporary entry to pointer table */ + files[size++] = tmp; + tmp = NULL; + + /* Keep up with the number of files */ + result++; + } + + } else { + + /* + * End of directory stream reached => sort entries and + * exit. + */ + qsort (files, size, sizeof (void*), + (int (*) (const void*, const void*)) compare); + break; + + } + + } else { + /* Error reading directory entry */ + result = /*Error*/ -1; + break; + } + + } + + } else { + /* Cannot open directory */ + result = /*Error*/ -1; + } + + /* Release temporary directory entry */ + free (tmp); + + /* Release allocated memory on error */ + if (result < 0) { + for (i = 0; i < size; i++) { + free (files[i]); + } + free (files); + files = NULL; + } + + /* Close directory stream */ + if (dir) { + closedir (dir); + } + + /* Pass pointer table to caller */ + if (namelist) { + *namelist = files; + } + return result; +} + +/* Alphabetical sorting */ +static int +alphasort( + const struct dirent **a, const struct dirent **b) +{ + return strcoll ((*a)->d_name, (*b)->d_name); +} + +/* Sort versions */ +static int +versionsort( + const struct dirent **a, const struct dirent **b) +{ + /* FIXME: implement strverscmp and use that */ + return alphasort (a, b); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resulting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ \ No newline at end of file diff --git a/armorcore/sources/libs/quickjs/libbf.c b/armorcore/sources/libs/quickjs/libbf.c new file mode 100644 index 000000000..5d48db622 --- /dev/null +++ b/armorcore/sources/libs/quickjs/libbf.c @@ -0,0 +1,8417 @@ +/* + * Tiny arbitrary precision floating point library + * + * Copyright (c) 2017-2021 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include + +#ifdef __AVX2__ +#include +#endif + +#include "cutils.h" +#include "libbf.h" + +/* enable it to check the multiplication result */ +//#define USE_MUL_CHECK +/* enable it to use FFT/NTT multiplication */ +#define USE_FFT_MUL +/* enable decimal floating point support */ +#define USE_BF_DEC + +//#define inline __attribute__((always_inline)) + +#ifdef __AVX2__ +#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */ +#else +#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */ +#endif + +/* XXX: adjust */ +#define DIVNORM_LARGE_THRESHOLD 50 +#define UDIV1NORM_THRESHOLD 3 + +#if LIMB_BITS == 64 +#define FMT_LIMB1 "%" PRIx64 +#define FMT_LIMB "%016" PRIx64 +#define PRId_LIMB PRId64 +#define PRIu_LIMB PRIu64 + +#else + +#define FMT_LIMB1 "%x" +#define FMT_LIMB "%08x" +#define PRId_LIMB "d" +#define PRIu_LIMB "u" + +#endif + +typedef intptr_t mp_size_t; + +typedef int bf_op2_func_t(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); + +#ifdef USE_FFT_MUL + +#define FFT_MUL_R_OVERLAP_A (1 << 0) +#define FFT_MUL_R_OVERLAP_B (1 << 1) +#define FFT_MUL_R_NORESIZE (1 << 2) + +static no_inline int fft_mul(bf_context_t *s, + bf_t *res, limb_t *a_tab, limb_t a_len, + limb_t *b_tab, limb_t b_len, int mul_flags); +static void fft_clear_cache(bf_context_t *s); +#endif +#ifdef USE_BF_DEC +static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos); +#endif + + +/* could leading zeros */ +static inline int clz(limb_t a) +{ + if (a == 0) { + return LIMB_BITS; + } else { +#if LIMB_BITS == 64 + return clz64(a); +#else + return clz32(a); +#endif + } +} + +static inline int ctz(limb_t a) +{ + if (a == 0) { + return LIMB_BITS; + } else { +#if LIMB_BITS == 64 + return ctz64(a); +#else + return ctz32(a); +#endif + } +} + +static inline int ceil_log2(limb_t a) +{ + if (a <= 1) + return 0; + else + return LIMB_BITS - clz(a - 1); +} + +/* b must be >= 1 */ +static inline slimb_t ceil_div(slimb_t a, slimb_t b) +{ + if (a >= 0) + return (a + b - 1) / b; + else + return a / b; +} + +/* b must be >= 1 */ +static inline slimb_t floor_div(slimb_t a, slimb_t b) +{ + if (a >= 0) { + return a / b; + } else { + return (a - b + 1) / b; + } +} + +/* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */ +static inline limb_t smod(slimb_t a, slimb_t b) +{ + a = a % (slimb_t)b; + if (a < 0) + a += b; + return a; +} + +/* signed addition with saturation */ +static inline slimb_t sat_add(slimb_t a, slimb_t b) +{ + slimb_t r; + r = a + b; + /* overflow ? */ + if (((a ^ r) & (b ^ r)) < 0) + r = (a >> (LIMB_BITS - 1)) ^ (((limb_t)1 << (LIMB_BITS - 1)) - 1); + return r; +} + +static inline __maybe_unused limb_t shrd(limb_t low, limb_t high, long shift) +{ + if (shift != 0) + low = (low >> shift) | (high << (LIMB_BITS - shift)); + return low; +} + +static inline __maybe_unused limb_t shld(limb_t a1, limb_t a0, long shift) +{ + if (shift != 0) + return (a1 << shift) | (a0 >> (LIMB_BITS - shift)); + else + return a1; +} + +#define malloc(s) malloc_is_forbidden(s) +#define free(p) free_is_forbidden(p) +#define realloc(p, s) realloc_is_forbidden(p, s) + +void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, + void *realloc_opaque) +{ + memset(s, 0, sizeof(*s)); + s->realloc_func = realloc_func; + s->realloc_opaque = realloc_opaque; +} + +void bf_context_end(bf_context_t *s) +{ + bf_clear_cache(s); +} + +void bf_init(bf_context_t *s, bf_t *r) +{ + r->ctx = s; + r->sign = 0; + r->expn = BF_EXP_ZERO; + r->len = 0; + r->tab = NULL; +} + +/* return 0 if OK, -1 if alloc error */ +int bf_resize(bf_t *r, limb_t len) +{ + limb_t *tab; + + if (len != r->len) { + tab = bf_realloc(r->ctx, r->tab, len * sizeof(limb_t)); + if (!tab && len != 0) + return -1; + r->tab = tab; + r->len = len; + } + return 0; +} + +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set_ui(bf_t *r, uint64_t a) +{ + r->sign = 0; + if (a == 0) { + r->expn = BF_EXP_ZERO; + bf_resize(r, 0); /* cannot fail */ + } +#if LIMB_BITS == 32 + else if (a <= 0xffffffff) +#else + else +#endif + { + int shift; + if (bf_resize(r, 1)) + goto fail; + shift = clz(a); + r->tab[0] = a << shift; + r->expn = LIMB_BITS - shift; + } +#if LIMB_BITS == 32 + else { + uint32_t a1, a0; + int shift; + if (bf_resize(r, 2)) + goto fail; + a0 = a; + a1 = a >> 32; + shift = clz(a1); + r->tab[0] = a0 << shift; + r->tab[1] = shld(a1, a0, shift); + r->expn = 2 * LIMB_BITS - shift; + } +#endif + return 0; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set_si(bf_t *r, int64_t a) +{ + int ret; + + if (a < 0) { + ret = bf_set_ui(r, -a); + r->sign = 1; + } else { + ret = bf_set_ui(r, a); + } + return ret; +} + +void bf_set_nan(bf_t *r) +{ + bf_resize(r, 0); /* cannot fail */ + r->expn = BF_EXP_NAN; + r->sign = 0; +} + +void bf_set_zero(bf_t *r, int is_neg) +{ + bf_resize(r, 0); /* cannot fail */ + r->expn = BF_EXP_ZERO; + r->sign = is_neg; +} + +void bf_set_inf(bf_t *r, int is_neg) +{ + bf_resize(r, 0); /* cannot fail */ + r->expn = BF_EXP_INF; + r->sign = is_neg; +} + +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set(bf_t *r, const bf_t *a) +{ + if (r == a) + return 0; + if (bf_resize(r, a->len)) { + bf_set_nan(r); + return BF_ST_MEM_ERROR; + } + r->sign = a->sign; + r->expn = a->expn; + if (a->len > 0) + memcpy(r->tab, a->tab, a->len * sizeof(limb_t)); + return 0; +} + +/* equivalent to bf_set(r, a); bf_delete(a) */ +void bf_move(bf_t *r, bf_t *a) +{ + bf_context_t *s = r->ctx; + if (r == a) + return; + bf_free(s, r->tab); + *r = *a; +} + +static limb_t get_limbz(const bf_t *a, limb_t idx) +{ + if (idx >= a->len) + return 0; + else + return a->tab[idx]; +} + +/* get LIMB_BITS at bit position 'pos' in tab */ +static inline limb_t get_bits(const limb_t *tab, limb_t len, slimb_t pos) +{ + limb_t i, a0, a1; + int p; + + i = pos >> LIMB_LOG2_BITS; + p = pos & (LIMB_BITS - 1); + if (i < len) + a0 = tab[i]; + else + a0 = 0; + if (p == 0) { + return a0; + } else { + i++; + if (i < len) + a1 = tab[i]; + else + a1 = 0; + return (a0 >> p) | (a1 << (LIMB_BITS - p)); + } +} + +static inline limb_t get_bit(const limb_t *tab, limb_t len, slimb_t pos) +{ + slimb_t i; + i = pos >> LIMB_LOG2_BITS; + if (i < 0 || i >= len) + return 0; + return (tab[i] >> (pos & (LIMB_BITS - 1))) & 1; +} + +static inline limb_t limb_mask(int start, int last) +{ + limb_t v; + int n; + n = last - start + 1; + if (n == LIMB_BITS) + v = -1; + else + v = (((limb_t)1 << n) - 1) << start; + return v; +} + +static limb_t mp_scan_nz(const limb_t *tab, mp_size_t n) +{ + mp_size_t i; + for(i = 0; i < n; i++) { + if (tab[i] != 0) + return 1; + } + return 0; +} + +/* return != 0 if one bit between 0 and bit_pos inclusive is not zero. */ +static inline limb_t scan_bit_nz(const bf_t *r, slimb_t bit_pos) +{ + slimb_t pos; + limb_t v; + + pos = bit_pos >> LIMB_LOG2_BITS; + if (pos < 0) + return 0; + v = r->tab[pos] & limb_mask(0, bit_pos & (LIMB_BITS - 1)); + if (v != 0) + return 1; + pos--; + while (pos >= 0) { + if (r->tab[pos] != 0) + return 1; + pos--; + } + return 0; +} + +/* return the addend for rounding. Note that prec can be <= 0 (for + BF_FLAG_RADPNT_PREC) */ +static int bf_get_rnd_add(int *pret, const bf_t *r, limb_t l, + slimb_t prec, int rnd_mode) +{ + int add_one, inexact; + limb_t bit1, bit0; + + if (rnd_mode == BF_RNDF) { + bit0 = 1; /* faithful rounding does not honor the INEXACT flag */ + } else { + /* starting limb for bit 'prec + 1' */ + bit0 = scan_bit_nz(r, l * LIMB_BITS - 1 - bf_max(0, prec + 1)); + } + + /* get the bit at 'prec' */ + bit1 = get_bit(r->tab, l, l * LIMB_BITS - 1 - prec); + inexact = (bit1 | bit0) != 0; + + add_one = 0; + switch(rnd_mode) { + case BF_RNDZ: + break; + case BF_RNDN: + if (bit1) { + if (bit0) { + add_one = 1; + } else { + /* round to even */ + add_one = + get_bit(r->tab, l, l * LIMB_BITS - 1 - (prec - 1)); + } + } + break; + case BF_RNDD: + case BF_RNDU: + if (r->sign == (rnd_mode == BF_RNDD)) + add_one = inexact; + break; + case BF_RNDA: + add_one = inexact; + break; + case BF_RNDNA: + case BF_RNDF: + add_one = bit1; + break; + default: + abort(); + } + + if (inexact) + *pret |= BF_ST_INEXACT; + return add_one; +} + +static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags) +{ + slimb_t i, l, e_max; + int rnd_mode; + + rnd_mode = flags & BF_RND_MASK; + if (prec == BF_PREC_INF || + rnd_mode == BF_RNDN || + rnd_mode == BF_RNDNA || + rnd_mode == BF_RNDA || + (rnd_mode == BF_RNDD && sign == 1) || + (rnd_mode == BF_RNDU && sign == 0)) { + bf_set_inf(r, sign); + } else { + /* set to maximum finite number */ + l = (prec + LIMB_BITS - 1) / LIMB_BITS; + if (bf_resize(r, l)) { + bf_set_nan(r); + return BF_ST_MEM_ERROR; + } + r->tab[0] = limb_mask((-prec) & (LIMB_BITS - 1), + LIMB_BITS - 1); + for(i = 1; i < l; i++) + r->tab[i] = (limb_t)-1; + e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + r->expn = e_max; + r->sign = sign; + } + return BF_ST_OVERFLOW | BF_ST_INEXACT; +} + +/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is + assumed to have length 'l' (1 <= l <= r->len). Note: 'prec1' can be + infinite (BF_PREC_INF). 'ret' is 0 or BF_ST_INEXACT if the result + is known to be inexact. Can fail with BF_ST_MEM_ERROR in case of + overflow not returning infinity. */ +static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l, + int ret) +{ + limb_t v, a; + int shift, add_one, rnd_mode; + slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; + + /* e_min and e_max are computed to match the IEEE 754 conventions */ + e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + e_min = -e_range + 3; + e_max = e_range; + + if (flags & BF_FLAG_RADPNT_PREC) { + /* 'prec' is the precision after the radix point */ + if (prec1 != BF_PREC_INF) + prec = r->expn + prec1; + else + prec = prec1; + } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { + /* restrict the precision in case of potentially subnormal + result */ + assert(prec1 != BF_PREC_INF); + prec = prec1 - (e_min - r->expn); + } else { + prec = prec1; + } + + /* round to prec bits */ + rnd_mode = flags & BF_RND_MASK; + add_one = bf_get_rnd_add(&ret, r, l, prec, rnd_mode); + + if (prec <= 0) { + if (add_one) { + bf_resize(r, 1); /* cannot fail */ + r->tab[0] = (limb_t)1 << (LIMB_BITS - 1); + r->expn += 1 - prec; + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + return ret; + } else { + goto underflow; + } + } else if (add_one) { + limb_t carry; + + /* add one starting at digit 'prec - 1' */ + bit_pos = l * LIMB_BITS - 1 - (prec - 1); + pos = bit_pos >> LIMB_LOG2_BITS; + carry = (limb_t)1 << (bit_pos & (LIMB_BITS - 1)); + + for(i = pos; i < l; i++) { + v = r->tab[i] + carry; + carry = (v < carry); + r->tab[i] = v; + if (carry == 0) + break; + } + if (carry) { + /* shift right by one digit */ + v = 1; + for(i = l - 1; i >= pos; i--) { + a = r->tab[i]; + r->tab[i] = (a >> 1) | (v << (LIMB_BITS - 1)); + v = a; + } + r->expn++; + } + } + + /* check underflow */ + if (unlikely(r->expn < e_min)) { + if (flags & BF_FLAG_SUBNORMAL) { + /* if inexact, also set the underflow flag */ + if (ret & BF_ST_INEXACT) + ret |= BF_ST_UNDERFLOW; + } else { + underflow: + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + bf_set_zero(r, r->sign); + return ret; + } + } + + /* check overflow */ + if (unlikely(r->expn > e_max)) + return bf_set_overflow(r, r->sign, prec1, flags); + + /* keep the bits starting at 'prec - 1' */ + bit_pos = l * LIMB_BITS - 1 - (prec - 1); + i = bit_pos >> LIMB_LOG2_BITS; + if (i >= 0) { + shift = bit_pos & (LIMB_BITS - 1); + if (shift != 0) + r->tab[i] &= limb_mask(shift, LIMB_BITS - 1); + } else { + i = 0; + } + /* remove trailing zeros */ + while (r->tab[i] == 0) + i++; + if (i > 0) { + l -= i; + memmove(r->tab, r->tab + i, l * sizeof(limb_t)); + } + bf_resize(r, l); /* cannot fail */ + return ret; +} + +/* 'r' must be a finite number. */ +int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags) +{ + limb_t l, v, a; + int shift, ret; + slimb_t i; + + // bf_print_str("bf_renorm", r); + l = r->len; + while (l > 0 && r->tab[l - 1] == 0) + l--; + if (l == 0) { + /* zero */ + r->expn = BF_EXP_ZERO; + bf_resize(r, 0); /* cannot fail */ + ret = 0; + } else { + r->expn -= (r->len - l) * LIMB_BITS; + /* shift to have the MSB set to '1' */ + v = r->tab[l - 1]; + shift = clz(v); + if (shift != 0) { + v = 0; + for(i = 0; i < l; i++) { + a = r->tab[i]; + r->tab[i] = (a << shift) | (v >> (LIMB_BITS - shift)); + v = a; + } + r->expn -= shift; + } + ret = __bf_round(r, prec1, flags, l, 0); + } + // bf_print_str("r_final", r); + return ret; +} + +/* return true if rounding can be done at precision 'prec' assuming + the exact result r is such that |r-a| <= 2^(EXP(a)-k). */ +/* XXX: check the case where the exponent would be incremented by the + rounding */ +int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k) +{ + BOOL is_rndn; + slimb_t bit_pos, n; + limb_t bit; + + if (a->expn == BF_EXP_INF || a->expn == BF_EXP_NAN) + return FALSE; + if (rnd_mode == BF_RNDF) { + return (k >= (prec + 1)); + } + if (a->expn == BF_EXP_ZERO) + return FALSE; + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); + if (k < (prec + 2)) + return FALSE; + bit_pos = a->len * LIMB_BITS - 1 - prec; + n = k - prec; + /* bit pattern for RNDN or RNDNA: 0111.. or 1000... + for other rounding modes: 000... or 111... + */ + bit = get_bit(a->tab, a->len, bit_pos); + bit_pos--; + n--; + bit ^= is_rndn; + /* XXX: slow, but a few iterations on average */ + while (n != 0) { + if (get_bit(a->tab, a->len, bit_pos) != bit) + return TRUE; + bit_pos--; + n--; + } + return FALSE; +} + +/* Cannot fail with BF_ST_MEM_ERROR. */ +int bf_round(bf_t *r, limb_t prec, bf_flags_t flags) +{ + if (r->len == 0) + return 0; + return __bf_round(r, prec, flags, r->len, 0); +} + +/* for debugging */ +static __maybe_unused void dump_limbs(const char *str, const limb_t *tab, limb_t n) +{ + limb_t i; + printf("%s: len=%" PRId_LIMB "\n", str, n); + for(i = 0; i < n; i++) { + printf("%" PRId_LIMB ": " FMT_LIMB "\n", + i, tab[i]); + } +} + +void mp_print_str(const char *str, const limb_t *tab, limb_t n) +{ + slimb_t i; + printf("%s= 0x", str); + for(i = n - 1; i >= 0; i--) { + if (i != (n - 1)) + printf("_"); + printf(FMT_LIMB, tab[i]); + } + printf("\n"); +} + +static __maybe_unused void mp_print_str_h(const char *str, + const limb_t *tab, limb_t n, + limb_t high) +{ + slimb_t i; + printf("%s= 0x", str); + printf(FMT_LIMB, high); + for(i = n - 1; i >= 0; i--) { + printf("_"); + printf(FMT_LIMB, tab[i]); + } + printf("\n"); +} + +/* for debugging */ +void bf_print_str(const char *str, const bf_t *a) +{ + slimb_t i; + printf("%s=", str); + + if (a->expn == BF_EXP_NAN) { + printf("NaN"); + } else { + if (a->sign) + putchar('-'); + if (a->expn == BF_EXP_ZERO) { + putchar('0'); + } else if (a->expn == BF_EXP_INF) { + printf("Inf"); + } else { + printf("0x0."); + for(i = a->len - 1; i >= 0; i--) + printf(FMT_LIMB, a->tab[i]); + printf("p%" PRId_LIMB, a->expn); + } + } + printf("\n"); +} + +/* compare the absolute value of 'a' and 'b'. Return < 0 if a < b, 0 + if a = b and > 0 otherwise. */ +int bf_cmpu(const bf_t *a, const bf_t *b) +{ + slimb_t i; + limb_t len, v1, v2; + + if (a->expn != b->expn) { + if (a->expn < b->expn) + return -1; + else + return 1; + } + len = bf_max(a->len, b->len); + for(i = len - 1; i >= 0; i--) { + v1 = get_limbz(a, a->len - len + i); + v2 = get_limbz(b, b->len - len + i); + if (v1 != v2) { + if (v1 < v2) + return -1; + else + return 1; + } + } + return 0; +} + +/* Full order: -0 < 0, NaN == NaN and NaN is larger than all other numbers */ +int bf_cmp_full(const bf_t *a, const bf_t *b) +{ + int res; + + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + if (a->expn == b->expn) + res = 0; + else if (a->expn == BF_EXP_NAN) + res = 1; + else + res = -1; + } else if (a->sign != b->sign) { + res = 1 - 2 * a->sign; + } else { + res = bf_cmpu(a, b); + if (a->sign) + res = -res; + } + return res; +} + +/* Standard floating point comparison: return 2 if one of the operands + is NaN (unordered) or -1, 0, 1 depending on the ordering assuming + -0 == +0 */ +int bf_cmp(const bf_t *a, const bf_t *b) +{ + int res; + + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + res = 2; + } else if (a->sign != b->sign) { + if (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) + res = 0; + else + res = 1 - 2 * a->sign; + } else { + res = bf_cmpu(a, b); + if (a->sign) + res = -res; + } + return res; +} + +/* Compute the number of bits 'n' matching the pattern: + a= X1000..0 + b= X0111..1 + + When computing a-b, the result will have at least n leading zero + bits. + + Precondition: a > b and a.expn - b.expn = 0 or 1 +*/ +static limb_t count_cancelled_bits(const bf_t *a, const bf_t *b) +{ + slimb_t bit_offset, b_offset, n; + int p, p1; + limb_t v1, v2, mask; + + bit_offset = a->len * LIMB_BITS - 1; + b_offset = (b->len - a->len) * LIMB_BITS - (LIMB_BITS - 1) + + a->expn - b->expn; + n = 0; + + /* first search the equals bits */ + for(;;) { + v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); + v2 = get_bits(b->tab, b->len, bit_offset + b_offset); + // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); + if (v1 != v2) + break; + n += LIMB_BITS; + bit_offset -= LIMB_BITS; + } + /* find the position of the first different bit */ + p = clz(v1 ^ v2) + 1; + n += p; + /* then search for '0' in a and '1' in b */ + p = LIMB_BITS - p; + if (p > 0) { + /* search in the trailing p bits of v1 and v2 */ + mask = limb_mask(0, p - 1); + p1 = bf_min(clz(v1 & mask), clz((~v2) & mask)) - (LIMB_BITS - p); + n += p1; + if (p1 != p) + goto done; + } + bit_offset -= LIMB_BITS; + for(;;) { + v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); + v2 = get_bits(b->tab, b->len, bit_offset + b_offset); + // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); + if (v1 != 0 || v2 != -1) { + /* different: count the matching bits */ + p1 = bf_min(clz(v1), clz(~v2)); + n += p1; + break; + } + n += LIMB_BITS; + bit_offset -= LIMB_BITS; + } + done: + return n; +} + +static int bf_add_internal(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int b_neg) +{ + const bf_t *tmp; + int is_sub, ret, cmp_res, a_sign, b_sign; + + a_sign = a->sign; + b_sign = b->sign ^ b_neg; + is_sub = a_sign ^ b_sign; + cmp_res = bf_cmpu(a, b); + if (cmp_res < 0) { + tmp = a; + a = b; + b = tmp; + a_sign = b_sign; /* b_sign is never used later */ + } + /* abs(a) >= abs(b) */ + if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { + /* zero result */ + bf_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); + ret = 0; + } else if (a->len == 0 || b->len == 0) { + ret = 0; + if (a->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN) { + /* at least one operand is NaN */ + bf_set_nan(r); + } else if (b->expn == BF_EXP_INF && is_sub) { + /* infinities with different signs */ + bf_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bf_set_inf(r, a_sign); + } + } else { + /* at least one zero and not subtract */ + bf_set(r, a); + r->sign = a_sign; + goto renorm; + } + } else { + slimb_t d, a_offset, b_bit_offset, i, cancelled_bits; + limb_t carry, v1, v2, u, r_len, carry1, precl, tot_len, z, sub_mask; + + r->sign = a_sign; + r->expn = a->expn; + d = a->expn - b->expn; + /* must add more precision for the leading cancelled bits in + subtraction */ + if (is_sub) { + if (d <= 1) + cancelled_bits = count_cancelled_bits(a, b); + else + cancelled_bits = 1; + } else { + cancelled_bits = 0; + } + + /* add two extra bits for rounding */ + precl = (cancelled_bits + prec + 2 + LIMB_BITS - 1) / LIMB_BITS; + tot_len = bf_max(a->len, b->len + (d + LIMB_BITS - 1) / LIMB_BITS); + r_len = bf_min(precl, tot_len); + if (bf_resize(r, r_len)) + goto fail; + a_offset = a->len - r_len; + b_bit_offset = (b->len - r_len) * LIMB_BITS + d; + + /* compute the bits before for the rounding */ + carry = is_sub; + z = 0; + sub_mask = -is_sub; + i = r_len - tot_len; + while (i < 0) { + slimb_t ap, bp; + BOOL inflag; + + ap = a_offset + i; + bp = b_bit_offset + i * LIMB_BITS; + inflag = FALSE; + if (ap >= 0 && ap < a->len) { + v1 = a->tab[ap]; + inflag = TRUE; + } else { + v1 = 0; + } + if (bp + LIMB_BITS > 0 && bp < (slimb_t)(b->len * LIMB_BITS)) { + v2 = get_bits(b->tab, b->len, bp); + inflag = TRUE; + } else { + v2 = 0; + } + if (!inflag) { + /* outside 'a' and 'b': go directly to the next value + inside a or b so that the running time does not + depend on the exponent difference */ + i = 0; + if (ap < 0) + i = bf_min(i, -a_offset); + /* b_bit_offset + i * LIMB_BITS + LIMB_BITS >= 1 + equivalent to + i >= ceil(-b_bit_offset + 1 - LIMB_BITS) / LIMB_BITS) + */ + if (bp + LIMB_BITS <= 0) + i = bf_min(i, (-b_bit_offset) >> LIMB_LOG2_BITS); + } else { + i++; + } + v2 ^= sub_mask; + u = v1 + v2; + carry1 = u < v1; + u += carry; + carry = (u < carry) | carry1; + z |= u; + } + /* and the result */ + for(i = 0; i < r_len; i++) { + v1 = get_limbz(a, a_offset + i); + v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS); + v2 ^= sub_mask; + u = v1 + v2; + carry1 = u < v1; + u += carry; + carry = (u < carry) | carry1; + r->tab[i] = u; + } + /* set the extra bits for the rounding */ + r->tab[0] |= (z != 0); + + /* carry is only possible in add case */ + if (!is_sub && carry) { + if (bf_resize(r, r_len + 1)) + goto fail; + r->tab[r_len] = 1; + r->expn += LIMB_BITS; + } + renorm: + ret = bf_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +static int __bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_add_internal(r, a, b, prec, flags, 0); +} + +static int __bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_add_internal(r, a, b, prec, flags, 1); +} + +limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, + limb_t n, limb_t carry) +{ + slimb_t i; + limb_t k, a, v, k1; + + k = carry; + for(i=0;i v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} + +/* compute 0 - op2 */ +static limb_t mp_neg(limb_t *res, const limb_t *op2, mp_size_t n, limb_t carry) +{ + int i; + limb_t k, a, v, k1; + + k = carry; + for(i=0;i v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} + +limb_t mp_sub_ui(limb_t *tab, limb_t b, mp_size_t n) +{ + mp_size_t i; + limb_t k, a, v; + + k=b; + for(i=0;i v; + tab[i] = a; + if (k == 0) + break; + } + return k; +} + +/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). + 1 <= shift <= LIMB_BITS - 1 */ +static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n, + int shift, limb_t high) +{ + mp_size_t i; + limb_t l, a; + + assert(shift >= 1 && shift < LIMB_BITS); + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift)); + l = a; + } + return l & (((limb_t)1 << shift) - 1); +} + +/* tabr[] = taba[] * b + l. Return the high carry */ +static limb_t mp_mul1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b, limb_t l) +{ + limb_t i; + dlimb_t t; + + for(i = 0; i < n; i++) { + t = (dlimb_t)taba[i] * (dlimb_t)b + l; + tabr[i] = t; + l = t >> LIMB_BITS; + } + return l; +} + +/* tabr[] += taba[] * b, return the high word. */ +static limb_t mp_add_mul1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b) +{ + limb_t i, l; + dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = (dlimb_t)taba[i] * (dlimb_t)b + l + tabr[i]; + tabr[i] = t; + l = t >> LIMB_BITS; + } + return l; +} + +/* size of the result : op1_size + op2_size. */ +static void mp_mul_basecase(limb_t *result, + const limb_t *op1, limb_t op1_size, + const limb_t *op2, limb_t op2_size) +{ + limb_t i, r; + + result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0); + for(i=1;i= FFT_MUL_THRESHOLD)) { + bf_t r_s, *r = &r_s; + r->tab = result; + /* XXX: optimize memory usage in API */ + if (fft_mul(s, r, (limb_t *)op1, op1_size, + (limb_t *)op2, op2_size, FFT_MUL_R_NORESIZE)) + return -1; + } else +#endif + { + mp_mul_basecase(result, op1, op1_size, op2, op2_size); + } + return 0; +} + +/* tabr[] -= taba[] * b. Return the value to substract to the high + word. */ +static limb_t mp_sub_mul1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b) +{ + limb_t i, l; + dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = tabr[i] - (dlimb_t)taba[i] * (dlimb_t)b - l; + tabr[i] = t; + l = -(t >> LIMB_BITS); + } + return l; +} + +/* WARNING: d must be >= 2^(LIMB_BITS-1) */ +static inline limb_t udiv1norm_init(limb_t d) +{ + limb_t a0, a1; + a1 = -d - 1; + a0 = -1; + return (((dlimb_t)a1 << LIMB_BITS) | a0) / d; +} + +/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0 + / d' with 0 <= a1 < d. */ +static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0, + limb_t d, limb_t d_inv) +{ + limb_t n1m, n_adj, q, r, ah; + dlimb_t a; + n1m = ((slimb_t)a0 >> (LIMB_BITS - 1)); + n_adj = a0 + (n1m & d); + a = (dlimb_t)d_inv * (a1 - n1m) + n_adj; + q = (a >> LIMB_BITS) + a1; + /* compute a - q * r and update q so that the remainder is\ + between 0 and d - 1 */ + a = ((dlimb_t)a1 << LIMB_BITS) | a0; + a = a - (dlimb_t)q * d - d; + ah = a >> LIMB_BITS; + q += 1 + ah; + r = (limb_t)a + (ah & d); + *pr = r; + return q; +} + +/* b must be >= 1 << (LIMB_BITS - 1) */ +static limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b, limb_t r) +{ + slimb_t i; + + if (n >= UDIV1NORM_THRESHOLD) { + limb_t b_inv; + b_inv = udiv1norm_init(b); + for(i = n - 1; i >= 0; i--) { + tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); + } + } else { + dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((dlimb_t)r << LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; + } + } + return r; +} + +static int mp_divnorm_large(bf_context_t *s, + limb_t *tabq, limb_t *taba, limb_t na, + const limb_t *tabb, limb_t nb); + +/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb + - 1] must be >= 1 << (LIMB_BITS - 1). na - nb must be >= 0. 'taba' + is modified and contains the remainder (nb limbs). tabq[0..na-nb] + contains the quotient with tabq[na - nb] <= 1. */ +static int mp_divnorm(bf_context_t *s, limb_t *tabq, limb_t *taba, limb_t na, + const limb_t *tabb, limb_t nb) +{ + limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r; + slimb_t i, j; + + b1 = tabb[nb - 1]; + if (nb == 1) { + taba[0] = mp_div1norm(tabq, taba, na, b1, 0); + return 0; + } + n = na - nb; + if (bf_min(n, nb) >= DIVNORM_LARGE_THRESHOLD) { + return mp_divnorm_large(s, tabq, taba, na, tabb, nb); + } + + if (n >= UDIV1NORM_THRESHOLD) + b1_inv = udiv1norm_init(b1); + else + b1_inv = 0; + + /* first iteration: the quotient is only 0 or 1 */ + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[n + j] != tabb[j]) { + if (taba[n + j] < tabb[j]) + q = 0; + break; + } + } + tabq[n] = q; + if (q) { + mp_sub(taba + n, taba + n, tabb, nb, 0); + } + + for(i = n - 1; i >= 0; i--) { + if (unlikely(taba[i + nb] >= b1)) { + q = -1; + } else if (b1_inv) { + q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv); + } else { + dlimb_t al; + al = ((dlimb_t)taba[i + nb] << LIMB_BITS) | taba[i + nb - 1]; + q = al / b1; + r = al % b1; + } + r = mp_sub_mul1(taba + i, tabb, nb, q); + + v = taba[i + nb]; + a = v - r; + c = (a > v); + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = mp_add(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == 0) { + break; + } + } + } + } + tabq[i] = q; + } + return 0; +} + +/* compute r=B^(2*n)/a such as a*r < B^(2*n) < a*r + 2 with n >= 1. 'a' + has n limbs with a[n-1] >= B/2 and 'r' has n+1 limbs with r[n] = 1. + + See Modern Computer Arithmetic by Richard P. Brent and Paul + Zimmermann, algorithm 3.5 */ +int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n) +{ + mp_size_t l, h, k, i; + limb_t *tabxh, *tabt, c, *tabu; + + if (n <= 2) { + /* return ceil(B^(2*n)/a) - 1 */ + /* XXX: could avoid allocation */ + tabu = bf_malloc(s, sizeof(limb_t) * (2 * n + 1)); + tabt = bf_malloc(s, sizeof(limb_t) * (n + 2)); + if (!tabt || !tabu) + goto fail; + for(i = 0; i < 2 * n; i++) + tabu[i] = 0; + tabu[2 * n] = 1; + if (mp_divnorm(s, tabt, tabu, 2 * n + 1, taba, n)) + goto fail; + for(i = 0; i < n + 1; i++) + tabr[i] = tabt[i]; + if (mp_scan_nz(tabu, n) == 0) { + /* only happens for a=B^n/2 */ + mp_sub_ui(tabr, 1, n + 1); + } + } else { + l = (n - 1) / 2; + h = n - l; + /* n=2p -> l=p-1, h = p + 1, k = p + 3 + n=2p+1-> l=p, h = p + 1; k = p + 2 + */ + tabt = bf_malloc(s, sizeof(limb_t) * (n + h + 1)); + tabu = bf_malloc(s, sizeof(limb_t) * (n + 2 * h - l + 2)); + if (!tabt || !tabu) + goto fail; + tabxh = tabr + l; + if (mp_recip(s, tabxh, taba + l, h)) + goto fail; + if (mp_mul(s, tabt, taba, n, tabxh, h + 1)) /* n + h + 1 limbs */ + goto fail; + while (tabt[n + h] != 0) { + mp_sub_ui(tabxh, 1, h + 1); + c = mp_sub(tabt, tabt, taba, n, 0); + mp_sub_ui(tabt + n, c, h + 1); + } + /* T = B^(n+h) - T */ + mp_neg(tabt, tabt, n + h + 1, 0); + tabt[n + h]++; + if (mp_mul(s, tabu, tabt + l, n + h + 1 - l, tabxh, h + 1)) + goto fail; + /* n + 2*h - l + 2 limbs */ + k = 2 * h - l; + for(i = 0; i < l; i++) + tabr[i] = tabu[i + k]; + mp_add(tabr + l, tabr + l, tabu + 2 * h, h, 0); + } + bf_free(s, tabt); + bf_free(s, tabu); + return 0; + fail: + bf_free(s, tabt); + bf_free(s, tabu); + return -1; +} + +/* return -1, 0 or 1 */ +static int mp_cmp(const limb_t *taba, const limb_t *tabb, mp_size_t n) +{ + mp_size_t i; + for(i = n - 1; i >= 0; i--) { + if (taba[i] != tabb[i]) { + if (taba[i] < tabb[i]) + return -1; + else + return 1; + } + } + return 0; +} + +//#define DEBUG_DIVNORM_LARGE +//#define DEBUG_DIVNORM_LARGE2 + +/* subquadratic divnorm */ +static int mp_divnorm_large(bf_context_t *s, + limb_t *tabq, limb_t *taba, limb_t na, + const limb_t *tabb, limb_t nb) +{ + limb_t *tabb_inv, nq, *tabt, i, n; + nq = na - nb; +#ifdef DEBUG_DIVNORM_LARGE + printf("na=%d nb=%d nq=%d\n", (int)na, (int)nb, (int)nq); + mp_print_str("a", taba, na); + mp_print_str("b", tabb, nb); +#endif + assert(nq >= 1); + n = nq; + if (nq < nb) + n++; + tabb_inv = bf_malloc(s, sizeof(limb_t) * (n + 1)); + tabt = bf_malloc(s, sizeof(limb_t) * 2 * (n + 1)); + if (!tabb_inv || !tabt) + goto fail; + + if (n >= nb) { + for(i = 0; i < n - nb; i++) + tabt[i] = 0; + for(i = 0; i < nb; i++) + tabt[i + n - nb] = tabb[i]; + } else { + /* truncate B: need to increment it so that the approximate + inverse is smaller that the exact inverse */ + for(i = 0; i < n; i++) + tabt[i] = tabb[i + nb - n]; + if (mp_add_ui(tabt, 1, n)) { + /* tabt = B^n : tabb_inv = B^n */ + memset(tabb_inv, 0, n * sizeof(limb_t)); + tabb_inv[n] = 1; + goto recip_done; + } + } + if (mp_recip(s, tabb_inv, tabt, n)) + goto fail; + recip_done: + /* Q=A*B^-1 */ + if (mp_mul(s, tabt, tabb_inv, n + 1, taba + na - (n + 1), n + 1)) + goto fail; + + for(i = 0; i < nq + 1; i++) + tabq[i] = tabt[i + 2 * (n + 1) - (nq + 1)]; +#ifdef DEBUG_DIVNORM_LARGE + mp_print_str("q", tabq, nq + 1); +#endif + + bf_free(s, tabt); + bf_free(s, tabb_inv); + tabb_inv = NULL; + + /* R=A-B*Q */ + tabt = bf_malloc(s, sizeof(limb_t) * (na + 1)); + if (!tabt) + goto fail; + if (mp_mul(s, tabt, tabq, nq + 1, tabb, nb)) + goto fail; + /* we add one more limb for the result */ + mp_sub(taba, taba, tabt, nb + 1, 0); + bf_free(s, tabt); + /* the approximated quotient is smaller than than the exact one, + hence we may have to increment it */ +#ifdef DEBUG_DIVNORM_LARGE2 + int cnt = 0; + static int cnt_max; +#endif + for(;;) { + if (taba[nb] == 0 && mp_cmp(taba, tabb, nb) < 0) + break; + taba[nb] -= mp_sub(taba, taba, tabb, nb, 0); + mp_add_ui(tabq, 1, nq + 1); +#ifdef DEBUG_DIVNORM_LARGE2 + cnt++; +#endif + } +#ifdef DEBUG_DIVNORM_LARGE2 + if (cnt > cnt_max) { + cnt_max = cnt; + printf("\ncnt=%d nq=%d nb=%d\n", cnt_max, (int)nq, (int)nb); + } +#endif + return 0; + fail: + bf_free(s, tabb_inv); + bf_free(s, tabt); + return -1; +} + +int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + int ret, r_sign; + + if (a->len < b->len) { + const bf_t *tmp = a; + a = b; + b = tmp; + } + r_sign = a->sign ^ b->sign; + /* here b->len <= a->len */ + if (b->len == 0) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bf_set_nan(r); + ret = 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { + if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || + (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { + bf_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bf_set_inf(r, r_sign); + ret = 0; + } + } else { + bf_set_zero(r, r_sign); + ret = 0; + } + } else { + bf_t tmp, *r1 = NULL; + limb_t a_len, b_len, precl; + limb_t *a_tab, *b_tab; + + a_len = a->len; + b_len = b->len; + + if ((flags & BF_RND_MASK) == BF_RNDF) { + /* faithful rounding does not require using the full inputs */ + precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS; + a_len = bf_min(a_len, precl); + b_len = bf_min(b_len, precl); + } + a_tab = a->tab + a->len - a_len; + b_tab = b->tab + b->len - b_len; + +#ifdef USE_FFT_MUL + if (b_len >= FFT_MUL_THRESHOLD) { + int mul_flags = 0; + if (r == a) + mul_flags |= FFT_MUL_R_OVERLAP_A; + if (r == b) + mul_flags |= FFT_MUL_R_OVERLAP_B; + if (fft_mul(r->ctx, r, a_tab, a_len, b_tab, b_len, mul_flags)) + goto fail; + } else +#endif + { + if (r == a || r == b) { + bf_init(r->ctx, &tmp); + r1 = r; + r = &tmp; + } + if (bf_resize(r, a_len + b_len)) { + fail: + bf_set_nan(r); + ret = BF_ST_MEM_ERROR; + goto done; + } + mp_mul_basecase(r->tab, a_tab, a_len, b_tab, b_len); + } + r->sign = r_sign; + r->expn = a->expn + b->expn; + ret = bf_normalize_and_round(r, prec, flags); + done: + if (r == &tmp) + bf_move(r1, &tmp); + } + return ret; +} + +/* multiply 'r' by 2^e */ +int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags) +{ + slimb_t e_max; + if (r->len == 0) + return 0; + e_max = ((limb_t)1 << BF_EXT_EXP_BITS_MAX) - 1; + e = bf_max(e, -e_max); + e = bf_min(e, e_max); + r->expn += e; + return __bf_round(r, prec, flags, r->len, 0); +} + +/* Return e such as a=m*2^e with m odd integer. return 0 if a is zero, + Infinite or Nan. */ +slimb_t bf_get_exp_min(const bf_t *a) +{ + slimb_t i; + limb_t v; + int k; + + for(i = 0; i < a->len; i++) { + v = a->tab[i]; + if (v != 0) { + k = ctz(v); + return a->expn - (a->len - i) * LIMB_BITS + k; + } + } + return 0; +} + +/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the + integer defined as floor(a/b) and r = a - q * b. */ +static void bf_tdivremu(bf_t *q, bf_t *r, + const bf_t *a, const bf_t *b) +{ + if (bf_cmpu(a, b) < 0) { + bf_set_ui(q, 0); + bf_set(r, a); + } else { + bf_div(q, a, b, bf_max(a->expn - b->expn + 1, 2), BF_RNDZ); + bf_rint(q, BF_RNDZ); + bf_mul(r, q, b, BF_PREC_INF, BF_RNDZ); + bf_sub(r, a, r, BF_PREC_INF, BF_RNDZ); + } +} + +static int __bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + int ret, r_sign; + limb_t n, nb, precl; + + r_sign = a->sign ^ b->sign; + if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else if (a->expn == BF_EXP_INF) { + bf_set_inf(r, r_sign); + return 0; + } else { + bf_set_zero(r, r_sign); + return 0; + } + } else if (a->expn == BF_EXP_ZERO) { + if (b->expn == BF_EXP_ZERO) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, r_sign); + return 0; + } + } else if (b->expn == BF_EXP_ZERO) { + bf_set_inf(r, r_sign); + return BF_ST_DIVIDE_ZERO; + } + + /* number of limbs of the quotient (2 extra bits for rounding) */ + precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS; + nb = b->len; + n = bf_max(a->len, precl); + + { + limb_t *taba, na; + slimb_t d; + + na = n + nb; + taba = bf_malloc(s, (na + 1) * sizeof(limb_t)); + if (!taba) + goto fail; + d = na - a->len; + memset(taba, 0, d * sizeof(limb_t)); + memcpy(taba + d, a->tab, a->len * sizeof(limb_t)); + if (bf_resize(r, n + 1)) + goto fail1; + if (mp_divnorm(s, r->tab, taba, na, b->tab, nb)) { + fail1: + bf_free(s, taba); + goto fail; + } + /* see if non zero remainder */ + if (mp_scan_nz(taba, nb)) + r->tab[0] |= 1; + bf_free(r->ctx, taba); + r->expn = a->expn - b->expn + LIMB_BITS; + r->sign = r_sign; + ret = bf_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +/* division and remainder. + + rnd_mode is the rounding mode for the quotient. The additional + rounding mode BF_RND_EUCLIDIAN is supported. + + 'q' is an integer. 'r' is rounded with prec and flags (prec can be + BF_PREC_INF). +*/ +int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode) +{ + bf_t a1_s, *a1 = &a1_s; + bf_t b1_s, *b1 = &b1_s; + int q_sign, ret; + BOOL is_ceil, is_rndn; + + assert(q != a && q != b); + assert(r != a && r != b); + assert(q != r); + + if (a->len == 0 || b->len == 0) { + bf_set_zero(q, 0); + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set(r, a); + return bf_round(r, prec, flags); + } + } + + q_sign = a->sign ^ b->sign; + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); + switch(rnd_mode) { + default: + case BF_RNDZ: + case BF_RNDN: + case BF_RNDNA: + is_ceil = FALSE; + break; + case BF_RNDD: + is_ceil = q_sign; + break; + case BF_RNDU: + is_ceil = q_sign ^ 1; + break; + case BF_RNDA: + is_ceil = TRUE; + break; + case BF_DIVREM_EUCLIDIAN: + is_ceil = a->sign; + break; + } + + a1->expn = a->expn; + a1->tab = a->tab; + a1->len = a->len; + a1->sign = 0; + + b1->expn = b->expn; + b1->tab = b->tab; + b1->len = b->len; + b1->sign = 0; + + /* XXX: could improve to avoid having a large 'q' */ + bf_tdivremu(q, r, a1, b1); + if (bf_is_nan(q) || bf_is_nan(r)) + goto fail; + + if (r->len != 0) { + if (is_rndn) { + int res; + b1->expn--; + res = bf_cmpu(r, b1); + b1->expn++; + if (res > 0 || + (res == 0 && + (rnd_mode == BF_RNDNA || + get_bit(q->tab, q->len, q->len * LIMB_BITS - q->expn)))) { + goto do_sub_r; + } + } else if (is_ceil) { + do_sub_r: + ret = bf_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ); + ret |= bf_sub(r, r, b1, BF_PREC_INF, BF_RNDZ); + if (ret & BF_ST_MEM_ERROR) + goto fail; + } + } + + r->sign ^= a->sign; + q->sign = q_sign; + return bf_round(r, prec, flags); + fail: + bf_set_nan(q); + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode) +{ + bf_t q_s, *q = &q_s; + int ret; + + bf_init(r->ctx, q); + ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); + bf_delete(q); + return ret; +} + +static inline int bf_get_limb(slimb_t *pres, const bf_t *a, int flags) +{ +#if LIMB_BITS == 32 + return bf_get_int32(pres, a, flags); +#else + return bf_get_int64(pres, a, flags); +#endif +} + +int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode) +{ + bf_t q_s, *q = &q_s; + int ret; + + bf_init(r->ctx, q); + ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); + bf_get_limb(pq, q, BF_GET_INT_MOD); + bf_delete(q); + return ret; +} + +static __maybe_unused inline limb_t mul_mod(limb_t a, limb_t b, limb_t m) +{ + dlimb_t t; + t = (dlimb_t)a * (dlimb_t)b; + return t % m; +} + +#if defined(USE_MUL_CHECK) +static limb_t mp_mod1(const limb_t *tab, limb_t n, limb_t m, limb_t r) +{ + slimb_t i; + dlimb_t t; + + for(i = n - 1; i >= 0; i--) { + t = ((dlimb_t)r << LIMB_BITS) | tab[i]; + r = t % m; + } + return r; +} +#endif + +static const uint16_t sqrt_table[192] = { +128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,157,158,159,160,160,161,162,163,163,164,165,166,167,167,168,169,170,170,171,172,173,173,174,175,176,176,177,178,178,179,180,181,181,182,183,183,184,185,185,186,187,187,188,189,189,190,191,192,192,193,193,194,195,195,196,197,197,198,199,199,200,201,201,202,203,203,204,204,205,206,206,207,208,208,209,209,210,211,211,212,212,213,214,214,215,215,216,217,217,218,218,219,219,220,221,221,222,222,223,224,224,225,225,226,226,227,227,228,229,229,230,230,231,231,232,232,233,234,234,235,235,236,236,237,237,238,238,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,253,254,254,255, +}; + +/* a >= 2^(LIMB_BITS - 2). Return (s, r) with s=floor(sqrt(a)) and + r=a-s^2. 0 <= r <= 2 * s */ +static limb_t mp_sqrtrem1(limb_t *pr, limb_t a) +{ + limb_t s1, r1, s, r, q, u, num; + + /* use a table for the 16 -> 8 bit sqrt */ + s1 = sqrt_table[(a >> (LIMB_BITS - 8)) - 64]; + r1 = (a >> (LIMB_BITS - 16)) - s1 * s1; + if (r1 > 2 * s1) { + r1 -= 2 * s1 + 1; + s1++; + } + + /* one iteration to get a 32 -> 16 bit sqrt */ + num = (r1 << 8) | ((a >> (LIMB_BITS - 32 + 8)) & 0xff); + q = num / (2 * s1); /* q <= 2^8 */ + u = num % (2 * s1); + s = (s1 << 8) + q; + r = (u << 8) | ((a >> (LIMB_BITS - 32)) & 0xff); + r -= q * q; + if ((slimb_t)r < 0) { + s--; + r += 2 * s + 1; + } + +#if LIMB_BITS == 64 + s1 = s; + r1 = r; + /* one more iteration for 64 -> 32 bit sqrt */ + num = (r1 << 16) | ((a >> (LIMB_BITS - 64 + 16)) & 0xffff); + q = num / (2 * s1); /* q <= 2^16 */ + u = num % (2 * s1); + s = (s1 << 16) + q; + r = (u << 16) | ((a >> (LIMB_BITS - 64)) & 0xffff); + r -= q * q; + if ((slimb_t)r < 0) { + s--; + r += 2 * s + 1; + } +#endif + *pr = r; + return s; +} + +/* return floor(sqrt(a)) */ +limb_t bf_isqrt(limb_t a) +{ + limb_t s, r; + int k; + + if (a == 0) + return 0; + k = clz(a) & ~1; + s = mp_sqrtrem1(&r, a << k); + s >>= (k >> 1); + return s; +} + +static limb_t mp_sqrtrem2(limb_t *tabs, limb_t *taba) +{ + limb_t s1, r1, s, q, u, a0, a1; + dlimb_t r, num; + int l; + + a0 = taba[0]; + a1 = taba[1]; + s1 = mp_sqrtrem1(&r1, a1); + l = LIMB_BITS / 2; + num = ((dlimb_t)r1 << l) | (a0 >> l); + q = num / (2 * s1); + u = num % (2 * s1); + s = (s1 << l) + q; + r = ((dlimb_t)u << l) | (a0 & (((limb_t)1 << l) - 1)); + if (unlikely((q >> l) != 0)) + r -= (dlimb_t)1 << LIMB_BITS; /* special case when q=2^l */ + else + r -= q * q; + if ((slimb_t)(r >> LIMB_BITS) < 0) { + s--; + r += 2 * (dlimb_t)s + 1; + } + tabs[0] = s; + taba[0] = r; + return r >> LIMB_BITS; +} + +//#define DEBUG_SQRTREM + +/* tmp_buf must contain (n / 2 + 1 limbs). *prh contains the highest + limb of the remainder. */ +static int mp_sqrtrem_rec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n, + limb_t *tmp_buf, limb_t *prh) +{ + limb_t l, h, rh, ql, qh, c, i; + + if (n == 1) { + *prh = mp_sqrtrem2(tabs, taba); + return 0; + } +#ifdef DEBUG_SQRTREM + mp_print_str("a", taba, 2 * n); +#endif + l = n / 2; + h = n - l; + if (mp_sqrtrem_rec(s, tabs + l, taba + 2 * l, h, tmp_buf, &qh)) + return -1; +#ifdef DEBUG_SQRTREM + mp_print_str("s1", tabs + l, h); + mp_print_str_h("r1", taba + 2 * l, h, qh); + mp_print_str_h("r2", taba + l, n, qh); +#endif + + /* the remainder is in taba + 2 * l. Its high bit is in qh */ + if (qh) { + mp_sub(taba + 2 * l, taba + 2 * l, tabs + l, h, 0); + } + /* instead of dividing by 2*s, divide by s (which is normalized) + and update q and r */ + if (mp_divnorm(s, tmp_buf, taba + l, n, tabs + l, h)) + return -1; + qh += tmp_buf[l]; + for(i = 0; i < l; i++) + tabs[i] = tmp_buf[i]; + ql = mp_shr(tabs, tabs, l, 1, qh & 1); + qh = qh >> 1; /* 0 or 1 */ + if (ql) + rh = mp_add(taba + l, taba + l, tabs + l, h, 0); + else + rh = 0; +#ifdef DEBUG_SQRTREM + mp_print_str_h("q", tabs, l, qh); + mp_print_str_h("u", taba + l, h, rh); +#endif + + mp_add_ui(tabs + l, qh, h); +#ifdef DEBUG_SQRTREM + mp_print_str_h("s2", tabs, n, sh); +#endif + + /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */ + /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */ + if (qh) { + c = qh; + } else { + if (mp_mul(s, taba + n, tabs, l, tabs, l)) + return -1; + c = mp_sub(taba, taba, taba + n, 2 * l, 0); + } + rh -= mp_sub_ui(taba + 2 * l, c, n - 2 * l); + if ((slimb_t)rh < 0) { + mp_sub_ui(tabs, 1, n); + rh += mp_add_mul1(taba, tabs, n, 2); + rh += mp_add_ui(taba, 1, n); + } + *prh = rh; + return 0; +} + +/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= 2 ^ (LIMB_BITS + - 2). Return (s, r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 + * s. tabs has n limbs. r is returned in the lower n limbs of + taba. Its r[n] is the returned value of the function. */ +/* Algorithm from the article "Karatsuba Square Root" by Paul Zimmermann and + inspirated from its GMP implementation */ +int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n) +{ + limb_t tmp_buf1[8]; + limb_t *tmp_buf; + mp_size_t n2; + int ret; + n2 = n / 2 + 1; + if (n2 <= countof(tmp_buf1)) { + tmp_buf = tmp_buf1; + } else { + tmp_buf = bf_malloc(s, sizeof(limb_t) * n2); + if (!tmp_buf) + return -1; + } + ret = mp_sqrtrem_rec(s, tabs, taba, n, tmp_buf, taba + n); + if (tmp_buf != tmp_buf1) + bf_free(s, tmp_buf); + return ret; +} + +/* Integer square root with remainder. 'a' must be an integer. r = + floor(sqrt(a)) and rem = a - r^2. BF_ST_INEXACT is set if the result + is inexact. 'rem' can be NULL if the remainder is not needed. */ +int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a) +{ + int ret; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (a->expn == BF_EXP_INF && a->sign) { + goto invalid_op; + } else { + bf_set(r, a); + } + if (rem1) + bf_set_ui(rem1, 0); + ret = 0; + } else if (a->sign) { + invalid_op: + bf_set_nan(r); + if (rem1) + bf_set_ui(rem1, 0); + ret = BF_ST_INVALID_OP; + } else { + bf_t rem_s, *rem; + + bf_sqrt(r, a, (a->expn + 1) / 2, BF_RNDZ); + bf_rint(r, BF_RNDZ); + /* see if the result is exact by computing the remainder */ + if (rem1) { + rem = rem1; + } else { + rem = &rem_s; + bf_init(r->ctx, rem); + } + /* XXX: could avoid recomputing the remainder */ + bf_mul(rem, r, r, BF_PREC_INF, BF_RNDZ); + bf_neg(rem); + bf_add(rem, rem, a, BF_PREC_INF, BF_RNDZ); + if (bf_is_nan(rem)) { + ret = BF_ST_MEM_ERROR; + goto done; + } + if (rem->len != 0) { + ret = BF_ST_INEXACT; + } else { + ret = 0; + } + done: + if (!rem1) + bf_delete(rem); + } + return ret; +} + +int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = a->ctx; + int ret; + + assert(r != a); + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (a->expn == BF_EXP_INF && a->sign) { + goto invalid_op; + } else { + bf_set(r, a); + } + ret = 0; + } else if (a->sign) { + invalid_op: + bf_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + limb_t *a1; + slimb_t n, n1; + limb_t res; + + /* convert the mantissa to an integer with at least 2 * + prec + 4 bits */ + n = (2 * (prec + 2) + 2 * LIMB_BITS - 1) / (2 * LIMB_BITS); + if (bf_resize(r, n)) + goto fail; + a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); + if (!a1) + goto fail; + n1 = bf_min(2 * n, a->len); + memset(a1, 0, (2 * n - n1) * sizeof(limb_t)); + memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t)); + if (a->expn & 1) { + res = mp_shr(a1, a1, 2 * n, 1, 0); + } else { + res = 0; + } + if (mp_sqrtrem(s, r->tab, a1, n)) { + bf_free(s, a1); + goto fail; + } + if (!res) { + res = mp_scan_nz(a1, n + 1); + } + bf_free(s, a1); + if (!res) { + res = mp_scan_nz(a->tab, a->len - n1); + } + if (res != 0) + r->tab[0] |= 1; + r->sign = 0; + r->expn = (a->expn + 1) >> 1; + ret = bf_round(r, prec, flags); + } + return ret; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +static no_inline int bf_op2(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, bf_op2_func_t *func) +{ + bf_t tmp; + int ret; + + if (r == a || r == b) { + bf_init(r->ctx, &tmp); + ret = func(&tmp, a, b, prec, flags); + bf_move(r, &tmp); + } else { + ret = func(r, a, b, prec, flags); + } + return ret; +} + +int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2(r, a, b, prec, flags, __bf_add); +} + +int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2(r, a, b, prec, flags, __bf_sub); +} + +int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2(r, a, b, prec, flags, __bf_div); +} + +int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, + bf_flags_t flags) +{ + bf_t b; + int ret; + bf_init(r->ctx, &b); + ret = bf_set_ui(&b, b1); + ret |= bf_mul(r, a, &b, prec, flags); + bf_delete(&b); + return ret; +} + +int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bf_t b; + int ret; + bf_init(r->ctx, &b); + ret = bf_set_si(&b, b1); + ret |= bf_mul(r, a, &b, prec, flags); + bf_delete(&b); + return ret; +} + +int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bf_t b; + int ret; + + bf_init(r->ctx, &b); + ret = bf_set_si(&b, b1); + ret |= bf_add(r, a, &b, prec, flags); + bf_delete(&b); + return ret; +} + +static int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec, + bf_flags_t flags) +{ + int ret, n_bits, i; + + assert(r != a); + if (b == 0) + return bf_set_ui(r, 1); + ret = bf_set(r, a); + n_bits = LIMB_BITS - clz(b); + for(i = n_bits - 2; i >= 0; i--) { + ret |= bf_mul(r, r, r, prec, flags); + if ((b >> i) & 1) + ret |= bf_mul(r, r, a, prec, flags); + } + return ret; +} + +static int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b, + limb_t prec, bf_flags_t flags) +{ + bf_t a; + int ret; + + if (a1 == 10 && b <= LIMB_DIGITS) { + /* use precomputed powers. We do not round at this point + because we expect the caller to do it */ + ret = bf_set_ui(r, mp_pow_dec[b]); + } else { + bf_init(r->ctx, &a); + ret = bf_set_ui(&a, a1); + ret |= bf_pow_ui(r, &a, b, prec, flags); + bf_delete(&a); + } + return ret; +} + +/* convert to integer (infinite precision) */ +int bf_rint(bf_t *r, int rnd_mode) +{ + return bf_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); +} + +/* logical operations */ +#define BF_LOGIC_OR 0 +#define BF_LOGIC_XOR 1 +#define BF_LOGIC_AND 2 + +static inline limb_t bf_logic_op1(limb_t a, limb_t b, int op) +{ + switch(op) { + case BF_LOGIC_OR: + return a | b; + case BF_LOGIC_XOR: + return a ^ b; + default: + case BF_LOGIC_AND: + return a & b; + } +} + +static int bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op) +{ + bf_t b1_s, a1_s, *a, *b; + limb_t a_sign, b_sign, r_sign; + slimb_t l, i, a_bit_offset, b_bit_offset; + limb_t v1, v2, v1_mask, v2_mask, r_mask; + int ret; + + assert(r != a1 && r != b1); + + if (a1->expn <= 0) + a_sign = 0; /* minus zero is considered as positive */ + else + a_sign = a1->sign; + + if (b1->expn <= 0) + b_sign = 0; /* minus zero is considered as positive */ + else + b_sign = b1->sign; + + if (a_sign) { + a = &a1_s; + bf_init(r->ctx, a); + if (bf_add_si(a, a1, 1, BF_PREC_INF, BF_RNDZ)) { + b = NULL; + goto fail; + } + } else { + a = (bf_t *)a1; + } + + if (b_sign) { + b = &b1_s; + bf_init(r->ctx, b); + if (bf_add_si(b, b1, 1, BF_PREC_INF, BF_RNDZ)) + goto fail; + } else { + b = (bf_t *)b1; + } + + r_sign = bf_logic_op1(a_sign, b_sign, op); + if (op == BF_LOGIC_AND && r_sign == 0) { + /* no need to compute extra zeros for and */ + if (a_sign == 0 && b_sign == 0) + l = bf_min(a->expn, b->expn); + else if (a_sign == 0) + l = a->expn; + else + l = b->expn; + } else { + l = bf_max(a->expn, b->expn); + } + /* Note: a or b can be zero */ + l = (bf_max(l, 1) + LIMB_BITS - 1) / LIMB_BITS; + if (bf_resize(r, l)) + goto fail; + a_bit_offset = a->len * LIMB_BITS - a->expn; + b_bit_offset = b->len * LIMB_BITS - b->expn; + v1_mask = -a_sign; + v2_mask = -b_sign; + r_mask = -r_sign; + for(i = 0; i < l; i++) { + v1 = get_bits(a->tab, a->len, a_bit_offset + i * LIMB_BITS) ^ v1_mask; + v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS) ^ v2_mask; + r->tab[i] = bf_logic_op1(v1, v2, op) ^ r_mask; + } + r->expn = l * LIMB_BITS; + r->sign = r_sign; + bf_normalize_and_round(r, BF_PREC_INF, BF_RNDZ); /* cannot fail */ + if (r_sign) { + if (bf_add_si(r, r, -1, BF_PREC_INF, BF_RNDZ)) + goto fail; + } + ret = 0; + done: + if (a == &a1_s) + bf_delete(a); + if (b == &b1_s) + bf_delete(b); + return ret; + fail: + bf_set_nan(r); + ret = BF_ST_MEM_ERROR; + goto done; +} + +/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ +int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b) +{ + return bf_logic_op(r, a, b, BF_LOGIC_OR); +} + +/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ +int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b) +{ + return bf_logic_op(r, a, b, BF_LOGIC_XOR); +} + +/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ +int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b) +{ + return bf_logic_op(r, a, b, BF_LOGIC_AND); +} + +/* conversion between fixed size types */ + +typedef union { + double d; + uint64_t u; +} Float64Union; + +int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode) +{ + Float64Union u; + int e, ret; + uint64_t m; + + ret = 0; + if (a->expn == BF_EXP_NAN) { + u.u = 0x7ff8000000000000; /* quiet nan */ + } else { + bf_t b_s, *b = &b_s; + + bf_init(a->ctx, b); + bf_set(b, a); + if (bf_is_finite(b)) { + ret = bf_round(b, 53, rnd_mode | BF_FLAG_SUBNORMAL | bf_set_exp_bits(11)); + } + if (b->expn == BF_EXP_INF) { + e = (1 << 11) - 1; + m = 0; + } else if (b->expn == BF_EXP_ZERO) { + e = 0; + m = 0; + } else { + e = b->expn + 1023 - 1; +#if LIMB_BITS == 32 + if (b->len == 2) { + m = ((uint64_t)b->tab[1] << 32) | b->tab[0]; + } else { + m = ((uint64_t)b->tab[0] << 32); + } +#else + m = b->tab[0]; +#endif + if (e <= 0) { + /* subnormal */ + m = m >> (12 - e); + e = 0; + } else { + m = (m << 1) >> 12; + } + } + u.u = m | ((uint64_t)e << 52) | ((uint64_t)b->sign << 63); + bf_delete(b); + } + *pres = u.d; + return ret; +} + +int bf_set_float64(bf_t *a, double d) +{ + Float64Union u; + uint64_t m; + int shift, e, sgn; + + u.d = d; + sgn = u.u >> 63; + e = (u.u >> 52) & ((1 << 11) - 1); + m = u.u & (((uint64_t)1 << 52) - 1); + if (e == ((1 << 11) - 1)) { + if (m != 0) { + bf_set_nan(a); + } else { + bf_set_inf(a, sgn); + } + } else if (e == 0) { + if (m == 0) { + bf_set_zero(a, sgn); + } else { + /* subnormal number */ + m <<= 12; + shift = clz64(m); + m <<= shift; + e = -shift; + goto norm; + } + } else { + m = (m << 11) | ((uint64_t)1 << 63); + norm: + a->expn = e - 1023 + 1; +#if LIMB_BITS == 32 + if (bf_resize(a, 2)) + goto fail; + a->tab[0] = m; + a->tab[1] = m >> 32; +#else + if (bf_resize(a, 1)) + goto fail; + a->tab[0] = m; +#endif + a->sign = sgn; + } + return 0; +fail: + bf_set_nan(a); + return BF_ST_MEM_ERROR; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there + is an overflow and 0 otherwise. */ +int bf_get_int32(int *pres, const bf_t *a, int flags) +{ + uint32_t v; + int ret; + if (a->expn >= BF_EXP_INF) { + ret = BF_ST_INVALID_OP; + if (flags & BF_GET_INT_MOD) { + v = 0; + } else if (a->expn == BF_EXP_INF) { + v = (uint32_t)INT32_MAX + a->sign; + } else { + v = INT32_MAX; + } + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->expn <= 31) { + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); + if (a->sign) + v = -v; + ret = 0; + } else if (!(flags & BF_GET_INT_MOD)) { + ret = BF_ST_INVALID_OP; + if (a->sign) { + v = (uint32_t)INT32_MAX + 1; + if (a->expn == 32 && + (a->tab[a->len - 1] >> (LIMB_BITS - 32)) == v) { + ret = 0; + } + } else { + v = INT32_MAX; + } + } else { + v = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn); + if (a->sign) + v = -v; + ret = 0; + } + *pres = v; + return ret; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there + is an overflow and 0 otherwise. */ +int bf_get_int64(int64_t *pres, const bf_t *a, int flags) +{ + uint64_t v; + int ret; + if (a->expn >= BF_EXP_INF) { + ret = BF_ST_INVALID_OP; + if (flags & BF_GET_INT_MOD) { + v = 0; + } else if (a->expn == BF_EXP_INF) { + v = (uint64_t)INT64_MAX + a->sign; + } else { + v = INT64_MAX; + } + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->expn <= 63) { +#if LIMB_BITS == 32 + if (a->expn <= 32) + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); + else + v = (((uint64_t)a->tab[a->len - 1] << 32) | + get_limbz(a, a->len - 2)) >> (64 - a->expn); +#else + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); +#endif + if (a->sign) + v = -v; + ret = 0; + } else if (!(flags & BF_GET_INT_MOD)) { + ret = BF_ST_INVALID_OP; + if (a->sign) { + uint64_t v1; + v = (uint64_t)INT64_MAX + 1; + if (a->expn == 64) { + v1 = a->tab[a->len - 1]; +#if LIMB_BITS == 32 + v1 = (v1 << 32) | get_limbz(a, a->len - 2); +#endif + if (v1 == v) + ret = 0; + } + } else { + v = INT64_MAX; + } + } else { + slimb_t bit_pos = a->len * LIMB_BITS - a->expn; + v = get_bits(a->tab, a->len, bit_pos); +#if LIMB_BITS == 32 + v |= (uint64_t)get_bits(a->tab, a->len, bit_pos + 32) << 32; +#endif + if (a->sign) + v = -v; + ret = 0; + } + *pres = v; + return ret; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there + is an overflow and 0 otherwise. */ +int bf_get_uint64(uint64_t *pres, const bf_t *a) +{ + uint64_t v; + int ret; + if (a->expn == BF_EXP_NAN) { + goto overflow; + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->sign) { + v = 0; + ret = BF_ST_INVALID_OP; + } else if (a->expn <= 64) { +#if LIMB_BITS == 32 + if (a->expn <= 32) + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); + else + v = (((uint64_t)a->tab[a->len - 1] << 32) | + get_limbz(a, a->len - 2)) >> (64 - a->expn); +#else + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); +#endif + ret = 0; + } else { + overflow: + v = UINT64_MAX; + ret = BF_ST_INVALID_OP; + } + *pres = v; + return ret; +} + +/* base conversion from radix */ + +static const uint8_t digits_per_limb_table[BF_RADIX_MAX - 1] = { +#if LIMB_BITS == 32 +32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +#else +64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12, +#endif +}; + +static limb_t get_limb_radix(int radix) +{ + int i, k; + limb_t radixl; + + k = digits_per_limb_table[radix - 2]; + radixl = radix; + for(i = 1; i < k; i++) + radixl *= radix; + return radixl; +} + +/* return != 0 if error */ +static int bf_integer_from_radix_rec(bf_t *r, const limb_t *tab, + limb_t n, int level, limb_t n0, + limb_t radix, bf_t *pow_tab) +{ + int ret; + if (n == 1) { + ret = bf_set_ui(r, tab[0]); + } else { + bf_t T_s, *T = &T_s, *B; + limb_t n1, n2; + + n2 = (((n0 * 2) >> (level + 1)) + 1) / 2; + n1 = n - n2; + // printf("level=%d n0=%ld n1=%ld n2=%ld\n", level, n0, n1, n2); + B = &pow_tab[level]; + if (B->len == 0) { + ret = bf_pow_ui_ui(B, radix, n2, BF_PREC_INF, BF_RNDZ); + if (ret) + return ret; + } + ret = bf_integer_from_radix_rec(r, tab + n2, n1, level + 1, n0, + radix, pow_tab); + if (ret) + return ret; + ret = bf_mul(r, r, B, BF_PREC_INF, BF_RNDZ); + if (ret) + return ret; + bf_init(r->ctx, T); + ret = bf_integer_from_radix_rec(T, tab, n2, level + 1, n0, + radix, pow_tab); + if (!ret) + ret = bf_add(r, r, T, BF_PREC_INF, BF_RNDZ); + bf_delete(T); + } + return ret; + // bf_print_str(" r=", r); +} + +/* return 0 if OK != 0 if memory error */ +static int bf_integer_from_radix(bf_t *r, const limb_t *tab, + limb_t n, limb_t radix) +{ + bf_context_t *s = r->ctx; + int pow_tab_len, i, ret; + limb_t radixl; + bf_t *pow_tab; + + radixl = get_limb_radix(radix); + pow_tab_len = ceil_log2(n) + 2; /* XXX: check */ + pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len); + if (!pow_tab) + return -1; + for(i = 0; i < pow_tab_len; i++) + bf_init(r->ctx, &pow_tab[i]); + ret = bf_integer_from_radix_rec(r, tab, n, 0, n, radixl, pow_tab); + for(i = 0; i < pow_tab_len; i++) { + bf_delete(&pow_tab[i]); + } + bf_free(s, pow_tab); + return ret; +} + +/* compute and round T * radix^expn. */ +int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, + slimb_t expn, limb_t prec, bf_flags_t flags) +{ + int ret, expn_sign, overflow; + slimb_t e, extra_bits, prec1, ziv_extra_bits; + bf_t B_s, *B = &B_s; + + if (T->len == 0) { + return bf_set(r, T); + } else if (expn == 0) { + ret = bf_set(r, T); + ret |= bf_round(r, prec, flags); + return ret; + } + + e = expn; + expn_sign = 0; + if (e < 0) { + e = -e; + expn_sign = 1; + } + bf_init(r->ctx, B); + if (prec == BF_PREC_INF) { + /* infinite precision: only used if the result is known to be exact */ + ret = bf_pow_ui_ui(B, radix, e, BF_PREC_INF, BF_RNDN); + if (expn_sign) { + ret |= bf_div(r, T, B, T->len * LIMB_BITS, BF_RNDN); + } else { + ret |= bf_mul(r, T, B, BF_PREC_INF, BF_RNDN); + } + } else { + ziv_extra_bits = 16; + for(;;) { + prec1 = prec + ziv_extra_bits; + /* XXX: correct overflow/underflow handling */ + /* XXX: rigorous error analysis needed */ + extra_bits = ceil_log2(e) * 2 + 1; + ret = bf_pow_ui_ui(B, radix, e, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); + overflow = !bf_is_finite(B); + /* XXX: if bf_pow_ui_ui returns an exact result, can stop + after the next operation */ + if (expn_sign) + ret |= bf_div(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); + else + ret |= bf_mul(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); + if (ret & BF_ST_MEM_ERROR) + break; + if ((ret & BF_ST_INEXACT) && + !bf_can_round(r, prec, flags & BF_RND_MASK, prec1) && + !overflow) { + /* and more precision and retry */ + ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); + } else { + /* XXX: need to use __bf_round() to pass the inexact + flag for the subnormal case */ + ret = bf_round(r, prec, flags) | (ret & BF_ST_INEXACT); + break; + } + } + } + bf_delete(B); + return ret; +} + +static inline int bf_to_digit(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'Z') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + return c - 'a' + 10; + else + return 36; +} + +/* add a limb at 'pos' and decrement pos. new space is created if + needed. Return 0 if OK, -1 if memory error */ +static int bf_add_limb(bf_t *a, slimb_t *ppos, limb_t v) +{ + slimb_t pos; + pos = *ppos; + if (unlikely(pos < 0)) { + limb_t new_size, d, *new_tab; + new_size = bf_max(a->len + 1, a->len * 3 / 2); + new_tab = bf_realloc(a->ctx, a->tab, sizeof(limb_t) * new_size); + if (!new_tab) + return -1; + a->tab = new_tab; + d = new_size - a->len; + memmove(a->tab + d, a->tab, a->len * sizeof(limb_t)); + a->len = new_size; + pos += d; + } + a->tab[pos--] = v; + *ppos = pos; + return 0; +} + +static int bf_tolower(int c) +{ + if (c >= 'A' && c <= 'Z') + c = c - 'A' + 'a'; + return c; +} + +static int strcasestart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (bf_tolower(*p) != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +static int bf_atof_internal(bf_t *r, slimb_t *pexponent, + const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags, BOOL is_dec) +{ + const char *p, *p_start; + int is_neg, radix_bits, exp_is_neg, ret, digits_per_limb, shift; + limb_t cur_limb; + slimb_t pos, expn, int_len, digit_count; + BOOL has_decpt, is_bin_exp; + bf_t a_s, *a; + + *pexponent = 0; + p = str; + if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 && + strcasestart(p, "nan", &p)) { + bf_set_nan(r); + ret = 0; + goto done; + } + is_neg = 0; + + if (p[0] == '+') { + p++; + p_start = p; + } else if (p[0] == '-') { + is_neg = 1; + p++; + p_start = p; + } else { + p_start = p; + } + if (p[0] == '0') { + if ((p[1] == 'x' || p[1] == 'X') && + (radix == 0 || radix == 16) && + !(flags & BF_ATOF_NO_HEX)) { + radix = 16; + p += 2; + } else if ((p[1] == 'o' || p[1] == 'O') && + radix == 0 && (flags & BF_ATOF_BIN_OCT)) { + p += 2; + radix = 8; + } else if ((p[1] == 'b' || p[1] == 'B') && + radix == 0 && (flags & BF_ATOF_BIN_OCT)) { + p += 2; + radix = 2; + } else { + goto no_prefix; + } + /* there must be a digit after the prefix */ + if (bf_to_digit((uint8_t)*p) >= radix) { + bf_set_nan(r); + ret = 0; + goto done; + } + no_prefix: ; + } else { + if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 && + strcasestart(p, "inf", &p)) { + bf_set_inf(r, is_neg); + ret = 0; + goto done; + } + } + + if (radix == 0) + radix = 10; + if (is_dec) { + assert(radix == 10); + radix_bits = 0; + a = r; + } else if ((radix & (radix - 1)) != 0) { + radix_bits = 0; /* base is not a power of two */ + a = &a_s; + bf_init(r->ctx, a); + } else { + radix_bits = ceil_log2(radix); + a = r; + } + + /* skip leading zeros */ + /* XXX: could also skip zeros after the decimal point */ + while (*p == '0') + p++; + + if (radix_bits) { + shift = digits_per_limb = LIMB_BITS; + } else { + radix_bits = 0; + shift = digits_per_limb = digits_per_limb_table[radix - 2]; + } + cur_limb = 0; + bf_resize(a, 1); + pos = 0; + has_decpt = FALSE; + int_len = digit_count = 0; + for(;;) { + limb_t c; + if (*p == '.' && (p > p_start || bf_to_digit(p[1]) < radix)) { + if (has_decpt) + break; + has_decpt = TRUE; + int_len = digit_count; + p++; + } + c = bf_to_digit(*p); + if (c >= radix) + break; + digit_count++; + p++; + if (radix_bits) { + shift -= radix_bits; + if (shift <= 0) { + cur_limb |= c >> (-shift); + if (bf_add_limb(a, &pos, cur_limb)) + goto mem_error; + if (shift < 0) + cur_limb = c << (LIMB_BITS + shift); + else + cur_limb = 0; + shift += LIMB_BITS; + } else { + cur_limb |= c << shift; + } + } else { + cur_limb = cur_limb * radix + c; + shift--; + if (shift == 0) { + if (bf_add_limb(a, &pos, cur_limb)) + goto mem_error; + shift = digits_per_limb; + cur_limb = 0; + } + } + } + if (!has_decpt) + int_len = digit_count; + + /* add the last limb and pad with zeros */ + if (shift != digits_per_limb) { + if (radix_bits == 0) { + while (shift != 0) { + cur_limb *= radix; + shift--; + } + } + if (bf_add_limb(a, &pos, cur_limb)) { + mem_error: + ret = BF_ST_MEM_ERROR; + if (!radix_bits) + bf_delete(a); + bf_set_nan(r); + goto done; + } + } + + /* reset the next limbs to zero (we prefer to reallocate in the + renormalization) */ + memset(a->tab, 0, (pos + 1) * sizeof(limb_t)); + + if (p == p_start) { + ret = 0; + if (!radix_bits) + bf_delete(a); + bf_set_nan(r); + goto done; + } + + /* parse the exponent, if any */ + expn = 0; + is_bin_exp = FALSE; + if (((radix == 10 && (*p == 'e' || *p == 'E')) || + (radix != 10 && (*p == '@' || + (radix_bits && (*p == 'p' || *p == 'P'))))) && + p > p_start) { + is_bin_exp = (*p == 'p' || *p == 'P'); + p++; + exp_is_neg = 0; + if (*p == '+') { + p++; + } else if (*p == '-') { + exp_is_neg = 1; + p++; + } + for(;;) { + int c; + c = bf_to_digit(*p); + if (c >= 10) + break; + if (unlikely(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) { + /* exponent overflow */ + if (exp_is_neg) { + bf_set_zero(r, is_neg); + ret = BF_ST_UNDERFLOW | BF_ST_INEXACT; + } else { + bf_set_inf(r, is_neg); + ret = BF_ST_OVERFLOW | BF_ST_INEXACT; + } + goto done; + } + p++; + expn = expn * 10 + c; + } + if (exp_is_neg) + expn = -expn; + } + if (is_dec) { + a->expn = expn + int_len; + a->sign = is_neg; + ret = bfdec_normalize_and_round((bfdec_t *)a, prec, flags); + } else if (radix_bits) { + /* XXX: may overflow */ + if (!is_bin_exp) + expn *= radix_bits; + a->expn = expn + (int_len * radix_bits); + a->sign = is_neg; + ret = bf_normalize_and_round(a, prec, flags); + } else { + limb_t l; + pos++; + l = a->len - pos; /* number of limbs */ + if (l == 0) { + bf_set_zero(r, is_neg); + ret = 0; + } else { + bf_t T_s, *T = &T_s; + + expn -= l * digits_per_limb - int_len; + bf_init(r->ctx, T); + if (bf_integer_from_radix(T, a->tab + pos, l, radix)) { + bf_set_nan(r); + ret = BF_ST_MEM_ERROR; + } else { + T->sign = is_neg; + if (flags & BF_ATOF_EXPONENT) { + /* return the exponent */ + *pexponent = expn; + ret = bf_set(r, T); + } else { + ret = bf_mul_pow_radix(r, T, radix, expn, prec, flags); + } + } + bf_delete(T); + } + bf_delete(a); + } + done: + if (pnext) + *pnext = p; + return ret; +} + +/* + Return (status, n, exp). 'status' is the floating point status. 'n' + is the parsed number. + + If (flags & BF_ATOF_EXPONENT) and if the radix is not a power of + two, the parsed number is equal to r * + (*pexponent)^radix. Otherwise *pexponent = 0. +*/ +int bf_atof2(bf_t *r, slimb_t *pexponent, + const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags) +{ + return bf_atof_internal(r, pexponent, str, pnext, radix, prec, flags, + FALSE); +} + +int bf_atof(bf_t *r, const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags) +{ + slimb_t dummy_exp; + return bf_atof_internal(r, &dummy_exp, str, pnext, radix, prec, flags, FALSE); +} + +/* base conversion to radix */ + +#if LIMB_BITS == 64 +#define RADIXL_10 UINT64_C(10000000000000000000) +#else +#define RADIXL_10 UINT64_C(1000000000) +#endif + +static const uint32_t inv_log2_radix[BF_RADIX_MAX - 1][LIMB_BITS / 32 + 1] = { +#if LIMB_BITS == 32 +{ 0x80000000, 0x00000000,}, +{ 0x50c24e60, 0xd4d4f4a7,}, +{ 0x40000000, 0x00000000,}, +{ 0x372068d2, 0x0a1ee5ca,}, +{ 0x3184648d, 0xb8153e7a,}, +{ 0x2d983275, 0x9d5369c4,}, +{ 0x2aaaaaaa, 0xaaaaaaab,}, +{ 0x28612730, 0x6a6a7a54,}, +{ 0x268826a1, 0x3ef3fde6,}, +{ 0x25001383, 0xbac8a744,}, +{ 0x23b46706, 0x82c0c709,}, +{ 0x229729f1, 0xb2c83ded,}, +{ 0x219e7ffd, 0xa5ad572b,}, +{ 0x20c33b88, 0xda7c29ab,}, +{ 0x20000000, 0x00000000,}, +{ 0x1f50b57e, 0xac5884b3,}, +{ 0x1eb22cc6, 0x8aa6e26f,}, +{ 0x1e21e118, 0x0c5daab2,}, +{ 0x1d9dcd21, 0x439834e4,}, +{ 0x1d244c78, 0x367a0d65,}, +{ 0x1cb40589, 0xac173e0c,}, +{ 0x1c4bd95b, 0xa8d72b0d,}, +{ 0x1bead768, 0x98f8ce4c,}, +{ 0x1b903469, 0x050f72e5,}, +{ 0x1b3b433f, 0x2eb06f15,}, +{ 0x1aeb6f75, 0x9c46fc38,}, +{ 0x1aa038eb, 0x0e3bfd17,}, +{ 0x1a593062, 0xb38d8c56,}, +{ 0x1a15f4c3, 0x2b95a2e6,}, +{ 0x19d630dc, 0xcc7ddef9,}, +{ 0x19999999, 0x9999999a,}, +{ 0x195fec80, 0x8a609431,}, +{ 0x1928ee7b, 0x0b4f22f9,}, +{ 0x18f46acf, 0x8c06e318,}, +{ 0x18c23246, 0xdc0a9f3d,}, +#else +{ 0x80000000, 0x00000000, 0x00000000,}, +{ 0x50c24e60, 0xd4d4f4a7, 0x021f57bc,}, +{ 0x40000000, 0x00000000, 0x00000000,}, +{ 0x372068d2, 0x0a1ee5ca, 0x19ea911b,}, +{ 0x3184648d, 0xb8153e7a, 0x7fc2d2e1,}, +{ 0x2d983275, 0x9d5369c4, 0x4dec1661,}, +{ 0x2aaaaaaa, 0xaaaaaaaa, 0xaaaaaaab,}, +{ 0x28612730, 0x6a6a7a53, 0x810fabde,}, +{ 0x268826a1, 0x3ef3fde6, 0x23e2566b,}, +{ 0x25001383, 0xbac8a744, 0x385a3349,}, +{ 0x23b46706, 0x82c0c709, 0x3f891718,}, +{ 0x229729f1, 0xb2c83ded, 0x15fba800,}, +{ 0x219e7ffd, 0xa5ad572a, 0xe169744b,}, +{ 0x20c33b88, 0xda7c29aa, 0x9bddee52,}, +{ 0x20000000, 0x00000000, 0x00000000,}, +{ 0x1f50b57e, 0xac5884b3, 0x70e28eee,}, +{ 0x1eb22cc6, 0x8aa6e26f, 0x06d1a2a2,}, +{ 0x1e21e118, 0x0c5daab1, 0x81b4f4bf,}, +{ 0x1d9dcd21, 0x439834e3, 0x81667575,}, +{ 0x1d244c78, 0x367a0d64, 0xc8204d6d,}, +{ 0x1cb40589, 0xac173e0c, 0x3b7b16ba,}, +{ 0x1c4bd95b, 0xa8d72b0d, 0x5879f25a,}, +{ 0x1bead768, 0x98f8ce4c, 0x66cc2858,}, +{ 0x1b903469, 0x050f72e5, 0x0cf5488e,}, +{ 0x1b3b433f, 0x2eb06f14, 0x8c89719c,}, +{ 0x1aeb6f75, 0x9c46fc37, 0xab5fc7e9,}, +{ 0x1aa038eb, 0x0e3bfd17, 0x1bd62080,}, +{ 0x1a593062, 0xb38d8c56, 0x7998ab45,}, +{ 0x1a15f4c3, 0x2b95a2e6, 0x46aed6a0,}, +{ 0x19d630dc, 0xcc7ddef9, 0x5aadd61b,}, +{ 0x19999999, 0x99999999, 0x9999999a,}, +{ 0x195fec80, 0x8a609430, 0xe1106014,}, +{ 0x1928ee7b, 0x0b4f22f9, 0x5f69791d,}, +{ 0x18f46acf, 0x8c06e318, 0x4d2aeb2c,}, +{ 0x18c23246, 0xdc0a9f3d, 0x3fe16970,}, +#endif +}; + +static const limb_t log2_radix[BF_RADIX_MAX - 1] = { +#if LIMB_BITS == 32 +0x20000000, +0x32b80347, +0x40000000, +0x4a4d3c26, +0x52b80347, +0x59d5d9fd, +0x60000000, +0x6570068e, +0x6a4d3c26, +0x6eb3a9f0, +0x72b80347, +0x766a008e, +0x79d5d9fd, +0x7d053f6d, +0x80000000, +0x82cc7edf, +0x8570068e, +0x87ef05ae, +0x8a4d3c26, +0x8c8ddd45, +0x8eb3a9f0, +0x90c10501, +0x92b80347, +0x949a784c, +0x966a008e, +0x982809d6, +0x99d5d9fd, +0x9b74948f, +0x9d053f6d, +0x9e88c6b3, +0xa0000000, +0xa16bad37, +0xa2cc7edf, +0xa4231623, +0xa570068e, +#else +0x2000000000000000, +0x32b803473f7ad0f4, +0x4000000000000000, +0x4a4d3c25e68dc57f, +0x52b803473f7ad0f4, +0x59d5d9fd5010b366, +0x6000000000000000, +0x6570068e7ef5a1e8, +0x6a4d3c25e68dc57f, +0x6eb3a9f01975077f, +0x72b803473f7ad0f4, +0x766a008e4788cbcd, +0x79d5d9fd5010b366, +0x7d053f6d26089673, +0x8000000000000000, +0x82cc7edf592262d0, +0x8570068e7ef5a1e8, +0x87ef05ae409a0289, +0x8a4d3c25e68dc57f, +0x8c8ddd448f8b845a, +0x8eb3a9f01975077f, +0x90c10500d63aa659, +0x92b803473f7ad0f4, +0x949a784bcd1b8afe, +0x966a008e4788cbcd, +0x982809d5be7072dc, +0x99d5d9fd5010b366, +0x9b74948f5532da4b, +0x9d053f6d26089673, +0x9e88c6b3626a72aa, +0xa000000000000000, +0xa16bad3758efd873, +0xa2cc7edf592262d0, +0xa4231623369e78e6, +0xa570068e7ef5a1e8, +#endif +}; + +/* compute floor(a*b) or ceil(a*b) with b = log2(radix) or + b=1/log2(radix). For is_inv = 0, strict accuracy is not guaranteed + when radix is not a power of two. */ +slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, + int is_ceil1) +{ + int is_neg; + limb_t a; + BOOL is_ceil; + + is_ceil = is_ceil1; + a = a1; + if (a1 < 0) { + a = -a; + is_neg = 1; + } else { + is_neg = 0; + } + is_ceil ^= is_neg; + if ((radix & (radix - 1)) == 0) { + int radix_bits; + /* radix is a power of two */ + radix_bits = ceil_log2(radix); + if (is_inv) { + if (is_ceil) + a += radix_bits - 1; + a = a / radix_bits; + } else { + a = a * radix_bits; + } + } else { + const uint32_t *tab; + limb_t b0, b1; + dlimb_t t; + + if (is_inv) { + tab = inv_log2_radix[radix - 2]; +#if LIMB_BITS == 32 + b1 = tab[0]; + b0 = tab[1]; +#else + b1 = ((limb_t)tab[0] << 32) | tab[1]; + b0 = (limb_t)tab[2] << 32; +#endif + t = (dlimb_t)b0 * (dlimb_t)a; + t = (dlimb_t)b1 * (dlimb_t)a + (t >> LIMB_BITS); + a = t >> (LIMB_BITS - 1); + } else { + b0 = log2_radix[radix - 2]; + t = (dlimb_t)b0 * (dlimb_t)a; + a = t >> (LIMB_BITS - 3); + } + /* a = floor(result) and 'result' cannot be an integer */ + a += is_ceil; + } + if (is_neg) + a = -a; + return a; +} + +/* 'n' is the number of output limbs */ +static int bf_integer_to_radix_rec(bf_t *pow_tab, + limb_t *out, const bf_t *a, limb_t n, + int level, limb_t n0, limb_t radixl, + unsigned int radixl_bits) +{ + limb_t n1, n2, q_prec; + int ret; + + assert(n >= 1); + if (n == 1) { + out[0] = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn); + } else if (n == 2) { + dlimb_t t; + slimb_t pos; + pos = a->len * LIMB_BITS - a->expn; + t = ((dlimb_t)get_bits(a->tab, a->len, pos + LIMB_BITS) << LIMB_BITS) | + get_bits(a->tab, a->len, pos); + if (likely(radixl == RADIXL_10)) { + /* use division by a constant when possible */ + out[0] = t % RADIXL_10; + out[1] = t / RADIXL_10; + } else { + out[0] = t % radixl; + out[1] = t / radixl; + } + } else { + bf_t Q, R, *B, *B_inv; + int q_add; + bf_init(a->ctx, &Q); + bf_init(a->ctx, &R); + n2 = (((n0 * 2) >> (level + 1)) + 1) / 2; + n1 = n - n2; + B = &pow_tab[2 * level]; + B_inv = &pow_tab[2 * level + 1]; + ret = 0; + if (B->len == 0) { + /* compute BASE^n2 */ + ret |= bf_pow_ui_ui(B, radixl, n2, BF_PREC_INF, BF_RNDZ); + /* we use enough bits for the maximum possible 'n1' value, + i.e. n2 + 1 */ + ret |= bf_set_ui(&R, 1); + ret |= bf_div(B_inv, &R, B, (n2 + 1) * radixl_bits + 2, BF_RNDN); + } + // printf("%d: n1=% " PRId64 " n2=%" PRId64 "\n", level, n1, n2); + q_prec = n1 * radixl_bits; + ret |= bf_mul(&Q, a, B_inv, q_prec, BF_RNDN); + ret |= bf_rint(&Q, BF_RNDZ); + + ret |= bf_mul(&R, &Q, B, BF_PREC_INF, BF_RNDZ); + ret |= bf_sub(&R, a, &R, BF_PREC_INF, BF_RNDZ); + + if (ret & BF_ST_MEM_ERROR) + goto fail; + /* adjust if necessary */ + q_add = 0; + while (R.sign && R.len != 0) { + if (bf_add(&R, &R, B, BF_PREC_INF, BF_RNDZ)) + goto fail; + q_add--; + } + while (bf_cmpu(&R, B) >= 0) { + if (bf_sub(&R, &R, B, BF_PREC_INF, BF_RNDZ)) + goto fail; + q_add++; + } + if (q_add != 0) { + if (bf_add_si(&Q, &Q, q_add, BF_PREC_INF, BF_RNDZ)) + goto fail; + } + if (bf_integer_to_radix_rec(pow_tab, out + n2, &Q, n1, level + 1, n0, + radixl, radixl_bits)) + goto fail; + if (bf_integer_to_radix_rec(pow_tab, out, &R, n2, level + 1, n0, + radixl, radixl_bits)) { + fail: + bf_delete(&Q); + bf_delete(&R); + return -1; + } + bf_delete(&Q); + bf_delete(&R); + } + return 0; +} + +/* return 0 if OK != 0 if memory error */ +static int bf_integer_to_radix(bf_t *r, const bf_t *a, limb_t radixl) +{ + bf_context_t *s = r->ctx; + limb_t r_len; + bf_t *pow_tab; + int i, pow_tab_len, ret; + + r_len = r->len; + pow_tab_len = (ceil_log2(r_len) + 2) * 2; /* XXX: check */ + pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len); + if (!pow_tab) + return -1; + for(i = 0; i < pow_tab_len; i++) + bf_init(r->ctx, &pow_tab[i]); + + ret = bf_integer_to_radix_rec(pow_tab, r->tab, a, r_len, 0, r_len, radixl, + ceil_log2(radixl)); + + for(i = 0; i < pow_tab_len; i++) { + bf_delete(&pow_tab[i]); + } + bf_free(s, pow_tab); + return ret; +} + +/* a must be >= 0. 'P' is the wanted number of digits in radix + 'radix'. 'r' is the mantissa represented as an integer. *pE + contains the exponent. Return != 0 if memory error. */ +static int bf_convert_to_radix(bf_t *r, slimb_t *pE, + const bf_t *a, int radix, + limb_t P, bf_rnd_t rnd_mode, + BOOL is_fixed_exponent) +{ + slimb_t E, e, prec, extra_bits, ziv_extra_bits, prec0; + bf_t B_s, *B = &B_s; + int e_sign, ret, res; + + if (a->len == 0) { + /* zero case */ + *pE = 0; + return bf_set(r, a); + } + + if (is_fixed_exponent) { + E = *pE; + } else { + /* compute the new exponent */ + E = 1 + bf_mul_log2_radix(a->expn - 1, radix, TRUE, FALSE); + } + // bf_print_str("a", a); + // printf("E=%ld P=%ld radix=%d\n", E, P, radix); + + for(;;) { + e = P - E; + e_sign = 0; + if (e < 0) { + e = -e; + e_sign = 1; + } + /* Note: precision for log2(radix) is not critical here */ + prec0 = bf_mul_log2_radix(P, radix, FALSE, TRUE); + ziv_extra_bits = 16; + for(;;) { + prec = prec0 + ziv_extra_bits; + /* XXX: rigorous error analysis needed */ + extra_bits = ceil_log2(e) * 2 + 1; + ret = bf_pow_ui_ui(r, radix, e, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); + if (!e_sign) + ret |= bf_mul(r, r, a, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); + else + ret |= bf_div(r, a, r, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); + if (ret & BF_ST_MEM_ERROR) + return BF_ST_MEM_ERROR; + /* if the result is not exact, check that it can be safely + rounded to an integer */ + if ((ret & BF_ST_INEXACT) && + !bf_can_round(r, r->expn, rnd_mode, prec)) { + /* and more precision and retry */ + ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); + continue; + } else { + ret = bf_rint(r, rnd_mode); + if (ret & BF_ST_MEM_ERROR) + return BF_ST_MEM_ERROR; + break; + } + } + if (is_fixed_exponent) + break; + /* check that the result is < B^P */ + /* XXX: do a fast approximate test first ? */ + bf_init(r->ctx, B); + ret = bf_pow_ui_ui(B, radix, P, BF_PREC_INF, BF_RNDZ); + if (ret) { + bf_delete(B); + return ret; + } + res = bf_cmpu(r, B); + bf_delete(B); + if (res < 0) + break; + /* try a larger exponent */ + E++; + } + *pE = E; + return 0; +} + +static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len) +{ + int digit, i; + + if (radix == 10) { + /* specific case with constant divisor */ + for(i = len - 1; i >= 0; i--) { + digit = (limb_t)n % 10; + n = (limb_t)n / 10; + buf[i] = digit + '0'; + } + } else { + for(i = len - 1; i >= 0; i--) { + digit = (limb_t)n % radix; + n = (limb_t)n / radix; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + buf[i] = digit; + } + } +} + +/* for power of 2 radixes */ +static void limb_to_a2(char *buf, limb_t n, unsigned int radix_bits, int len) +{ + int digit, i; + unsigned int mask; + + mask = (1 << radix_bits) - 1; + for(i = len - 1; i >= 0; i--) { + digit = n & mask; + n >>= radix_bits; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + buf[i] = digit; + } +} + +/* 'a' must be an integer if the is_dec = FALSE or if the radix is not + a power of two. A dot is added before the 'dot_pos' digit. dot_pos + = n_digits does not display the dot. 0 <= dot_pos <= + n_digits. n_digits >= 1. */ +static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits, + limb_t dot_pos, BOOL is_dec) +{ + limb_t i, v, l; + slimb_t pos, pos_incr; + int digits_per_limb, buf_pos, radix_bits, first_buf_pos; + char buf[65]; + bf_t a_s, *a; + + if (is_dec) { + digits_per_limb = LIMB_DIGITS; + a = (bf_t *)a1; + radix_bits = 0; + pos = a->len; + pos_incr = 1; + first_buf_pos = 0; + } else if ((radix & (radix - 1)) == 0) { + a = (bf_t *)a1; + radix_bits = ceil_log2(radix); + digits_per_limb = LIMB_BITS / radix_bits; + pos_incr = digits_per_limb * radix_bits; + /* digits are aligned relative to the radix point */ + pos = a->len * LIMB_BITS + smod(-a->expn, radix_bits); + first_buf_pos = 0; + } else { + limb_t n, radixl; + + digits_per_limb = digits_per_limb_table[radix - 2]; + radixl = get_limb_radix(radix); + a = &a_s; + bf_init(a1->ctx, a); + n = (n_digits + digits_per_limb - 1) / digits_per_limb; + if (bf_resize(a, n)) { + dbuf_set_error(s); + goto done; + } + if (bf_integer_to_radix(a, a1, radixl)) { + dbuf_set_error(s); + goto done; + } + radix_bits = 0; + pos = n; + pos_incr = 1; + first_buf_pos = pos * digits_per_limb - n_digits; + } + buf_pos = digits_per_limb; + i = 0; + while (i < n_digits) { + if (buf_pos == digits_per_limb) { + pos -= pos_incr; + if (radix_bits == 0) { + v = get_limbz(a, pos); + limb_to_a(buf, v, radix, digits_per_limb); + } else { + v = get_bits(a->tab, a->len, pos); + limb_to_a2(buf, v, radix_bits, digits_per_limb); + } + buf_pos = first_buf_pos; + first_buf_pos = 0; + } + if (i < dot_pos) { + l = dot_pos; + } else { + if (i == dot_pos) + dbuf_putc(s, '.'); + l = n_digits; + } + l = bf_min(digits_per_limb - buf_pos, l - i); + dbuf_put(s, (uint8_t *)(buf + buf_pos), l); + buf_pos += l; + i += l; + } + done: + if (a != a1) + bf_delete(a); +} + +static void *bf_dbuf_realloc(void *opaque, void *ptr, size_t size) +{ + bf_context_t *s = opaque; + return bf_realloc(s, ptr, size); +} + +/* return the length in bytes. A trailing '\0' is added */ +static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix, + limb_t prec, bf_flags_t flags, BOOL is_dec) +{ + bf_context_t *ctx = a2->ctx; + DynBuf s_s, *s = &s_s; + int radix_bits; + + // bf_print_str("ftoa", a2); + // printf("radix=%d\n", radix); + dbuf_init2(s, ctx, bf_dbuf_realloc); + if (a2->expn == BF_EXP_NAN) { + dbuf_putstr(s, "NaN"); + } else { + if (a2->sign) + dbuf_putc(s, '-'); + if (a2->expn == BF_EXP_INF) { + if (flags & BF_FTOA_JS_QUIRKS) + dbuf_putstr(s, "Infinity"); + else + dbuf_putstr(s, "Inf"); + } else { + int fmt, ret; + slimb_t n_digits, n, i, n_max, n1; + bf_t a1_s, *a1 = &a1_s; + + if ((radix & (radix - 1)) != 0) + radix_bits = 0; + else + radix_bits = ceil_log2(radix); + + fmt = flags & BF_FTOA_FORMAT_MASK; + bf_init(ctx, a1); + if (fmt == BF_FTOA_FORMAT_FRAC) { + if (is_dec || radix_bits != 0) { + if (bf_set(a1, a2)) + goto fail1; +#ifdef USE_BF_DEC + if (is_dec) { + if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) + goto fail1; + n = a1->expn; + } else +#endif + { + if (bf_round(a1, prec * radix_bits, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) + goto fail1; + n = ceil_div(a1->expn, radix_bits); + } + if (flags & BF_FTOA_ADD_PREFIX) { + if (radix == 16) + dbuf_putstr(s, "0x"); + else if (radix == 8) + dbuf_putstr(s, "0o"); + else if (radix == 2) + dbuf_putstr(s, "0b"); + } + if (a1->expn == BF_EXP_ZERO) { + dbuf_putstr(s, "0"); + if (prec > 0) { + dbuf_putstr(s, "."); + for(i = 0; i < prec; i++) { + dbuf_putc(s, '0'); + } + } + } else { + n_digits = prec + n; + if (n <= 0) { + /* 0.x */ + dbuf_putstr(s, "0."); + for(i = 0; i < -n; i++) { + dbuf_putc(s, '0'); + } + if (n_digits > 0) { + output_digits(s, a1, radix, n_digits, n_digits, is_dec); + } + } else { + output_digits(s, a1, radix, n_digits, n, is_dec); + } + } + } else { + size_t pos, start; + bf_t a_s, *a = &a_s; + + /* make a positive number */ + a->tab = a2->tab; + a->len = a2->len; + a->expn = a2->expn; + a->sign = 0; + + /* one more digit for the rounding */ + n = 1 + bf_mul_log2_radix(bf_max(a->expn, 0), radix, TRUE, TRUE); + n_digits = n + prec; + n1 = n; + if (bf_convert_to_radix(a1, &n1, a, radix, n_digits, + flags & BF_RND_MASK, TRUE)) + goto fail1; + start = s->size; + output_digits(s, a1, radix, n_digits, n, is_dec); + /* remove leading zeros because we allocated one more digit */ + pos = start; + while ((pos + 1) < s->size && s->buf[pos] == '0' && + s->buf[pos + 1] != '.') + pos++; + if (pos > start) { + memmove(s->buf + start, s->buf + pos, s->size - pos); + s->size -= (pos - start); + } + } + } else { +#ifdef USE_BF_DEC + if (is_dec) { + if (bf_set(a1, a2)) + goto fail1; + if (fmt == BF_FTOA_FORMAT_FIXED) { + n_digits = prec; + n_max = n_digits; + if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) + goto fail1; + } else { + /* prec is ignored */ + prec = n_digits = a1->len * LIMB_DIGITS; + /* remove the trailing zero digits */ + while (n_digits > 1 && + get_digit(a1->tab, a1->len, prec - n_digits) == 0) { + n_digits--; + } + n_max = n_digits + 4; + } + n = a1->expn; + } else +#endif + if (radix_bits != 0) { + if (bf_set(a1, a2)) + goto fail1; + if (fmt == BF_FTOA_FORMAT_FIXED) { + slimb_t prec_bits; + n_digits = prec; + n_max = n_digits; + /* align to the radix point */ + prec_bits = prec * radix_bits - + smod(-a1->expn, radix_bits); + if (bf_round(a1, prec_bits, + (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) + goto fail1; + } else { + limb_t digit_mask; + slimb_t pos; + /* position of the digit before the most + significant digit in bits */ + pos = a1->len * LIMB_BITS + + smod(-a1->expn, radix_bits); + n_digits = ceil_div(pos, radix_bits); + /* remove the trailing zero digits */ + digit_mask = ((limb_t)1 << radix_bits) - 1; + while (n_digits > 1 && + (get_bits(a1->tab, a1->len, pos - n_digits * radix_bits) & digit_mask) == 0) { + n_digits--; + } + n_max = n_digits + 4; + } + n = ceil_div(a1->expn, radix_bits); + } else { + bf_t a_s, *a = &a_s; + + /* make a positive number */ + a->tab = a2->tab; + a->len = a2->len; + a->expn = a2->expn; + a->sign = 0; + + if (fmt == BF_FTOA_FORMAT_FIXED) { + n_digits = prec; + n_max = n_digits; + } else { + slimb_t n_digits_max, n_digits_min; + + assert(prec != BF_PREC_INF); + n_digits = 1 + bf_mul_log2_radix(prec, radix, TRUE, TRUE); + /* max number of digits for non exponential + notation. The rational is to have the same rule + as JS i.e. n_max = 21 for 64 bit float in base 10. */ + n_max = n_digits + 4; + if (fmt == BF_FTOA_FORMAT_FREE_MIN) { + bf_t b_s, *b = &b_s; + + /* find the minimum number of digits by + dichotomy. */ + /* XXX: inefficient */ + n_digits_max = n_digits; + n_digits_min = 1; + bf_init(ctx, b); + while (n_digits_min < n_digits_max) { + n_digits = (n_digits_min + n_digits_max) / 2; + if (bf_convert_to_radix(a1, &n, a, radix, n_digits, + flags & BF_RND_MASK, FALSE)) { + bf_delete(b); + goto fail1; + } + /* convert back to a number and compare */ + ret = bf_mul_pow_radix(b, a1, radix, n - n_digits, + prec, + (flags & ~BF_RND_MASK) | + BF_RNDN); + if (ret & BF_ST_MEM_ERROR) { + bf_delete(b); + goto fail1; + } + if (bf_cmpu(b, a) == 0) { + n_digits_max = n_digits; + } else { + n_digits_min = n_digits + 1; + } + } + bf_delete(b); + n_digits = n_digits_max; + } + } + if (bf_convert_to_radix(a1, &n, a, radix, n_digits, + flags & BF_RND_MASK, FALSE)) { + fail1: + bf_delete(a1); + goto fail; + } + } + if (a1->expn == BF_EXP_ZERO && + fmt != BF_FTOA_FORMAT_FIXED && + !(flags & BF_FTOA_FORCE_EXP)) { + /* just output zero */ + dbuf_putstr(s, "0"); + } else { + if (flags & BF_FTOA_ADD_PREFIX) { + if (radix == 16) + dbuf_putstr(s, "0x"); + else if (radix == 8) + dbuf_putstr(s, "0o"); + else if (radix == 2) + dbuf_putstr(s, "0b"); + } + if (a1->expn == BF_EXP_ZERO) + n = 1; + if ((flags & BF_FTOA_FORCE_EXP) || + n <= -6 || n > n_max) { + const char *fmt; + /* exponential notation */ + output_digits(s, a1, radix, n_digits, 1, is_dec); + if (radix_bits != 0 && radix <= 16) { + if (flags & BF_FTOA_JS_QUIRKS) + fmt = "p%+" PRId_LIMB; + else + fmt = "p%" PRId_LIMB; + dbuf_printf(s, fmt, (n - 1) * radix_bits); + } else { + if (flags & BF_FTOA_JS_QUIRKS) + fmt = "%c%+" PRId_LIMB; + else + fmt = "%c%" PRId_LIMB; + dbuf_printf(s, fmt, + radix <= 10 ? 'e' : '@', n - 1); + } + } else if (n <= 0) { + /* 0.x */ + dbuf_putstr(s, "0."); + for(i = 0; i < -n; i++) { + dbuf_putc(s, '0'); + } + output_digits(s, a1, radix, n_digits, n_digits, is_dec); + } else { + if (n_digits <= n) { + /* no dot */ + output_digits(s, a1, radix, n_digits, n_digits, is_dec); + for(i = 0; i < (n - n_digits); i++) + dbuf_putc(s, '0'); + } else { + output_digits(s, a1, radix, n_digits, n, is_dec); + } + } + } + } + bf_delete(a1); + } + } + dbuf_putc(s, '\0'); + if (dbuf_error(s)) + goto fail; + if (plen) + *plen = s->size - 1; + return (char *)s->buf; + fail: + bf_free(ctx, s->buf); + if (plen) + *plen = 0; + return NULL; +} + +char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, + bf_flags_t flags) +{ + return bf_ftoa_internal(plen, a, radix, prec, flags, FALSE); +} + +/***************************************************************/ +/* transcendental functions */ + +/* Note: the algorithm is from MPFR */ +static void bf_const_log2_rec(bf_t *T, bf_t *P, bf_t *Q, limb_t n1, + limb_t n2, BOOL need_P) +{ + bf_context_t *s = T->ctx; + if ((n2 - n1) == 1) { + if (n1 == 0) { + bf_set_ui(P, 3); + } else { + bf_set_ui(P, n1); + P->sign = 1; + } + bf_set_ui(Q, 2 * n1 + 1); + Q->expn += 2; + bf_set(T, P); + } else { + limb_t m; + bf_t T1_s, *T1 = &T1_s; + bf_t P1_s, *P1 = &P1_s; + bf_t Q1_s, *Q1 = &Q1_s; + + m = n1 + ((n2 - n1) >> 1); + bf_const_log2_rec(T, P, Q, n1, m, TRUE); + bf_init(s, T1); + bf_init(s, P1); + bf_init(s, Q1); + bf_const_log2_rec(T1, P1, Q1, m, n2, need_P); + bf_mul(T, T, Q1, BF_PREC_INF, BF_RNDZ); + bf_mul(T1, T1, P, BF_PREC_INF, BF_RNDZ); + bf_add(T, T, T1, BF_PREC_INF, BF_RNDZ); + if (need_P) + bf_mul(P, P, P1, BF_PREC_INF, BF_RNDZ); + bf_mul(Q, Q, Q1, BF_PREC_INF, BF_RNDZ); + bf_delete(T1); + bf_delete(P1); + bf_delete(Q1); + } +} + +/* compute log(2) with faithful rounding at precision 'prec' */ +static void bf_const_log2_internal(bf_t *T, limb_t prec) +{ + limb_t w, N; + bf_t P_s, *P = &P_s; + bf_t Q_s, *Q = &Q_s; + + w = prec + 15; + N = w / 3 + 1; + bf_init(T->ctx, P); + bf_init(T->ctx, Q); + bf_const_log2_rec(T, P, Q, 0, N, FALSE); + bf_div(T, T, Q, prec, BF_RNDN); + bf_delete(P); + bf_delete(Q); +} + +/* PI constant */ + +#define CHUD_A 13591409 +#define CHUD_B 545140134 +#define CHUD_C 640320 +#define CHUD_BITS_PER_TERM 47 + +static void chud_bs(bf_t *P, bf_t *Q, bf_t *G, int64_t a, int64_t b, int need_g, + limb_t prec) +{ + bf_context_t *s = P->ctx; + int64_t c; + + if (a == (b - 1)) { + bf_t T0, T1; + + bf_init(s, &T0); + bf_init(s, &T1); + bf_set_ui(G, 2 * b - 1); + bf_mul_ui(G, G, 6 * b - 1, prec, BF_RNDN); + bf_mul_ui(G, G, 6 * b - 5, prec, BF_RNDN); + bf_set_ui(&T0, CHUD_B); + bf_mul_ui(&T0, &T0, b, prec, BF_RNDN); + bf_set_ui(&T1, CHUD_A); + bf_add(&T0, &T0, &T1, prec, BF_RNDN); + bf_mul(P, G, &T0, prec, BF_RNDN); + P->sign = b & 1; + + bf_set_ui(Q, b); + bf_mul_ui(Q, Q, b, prec, BF_RNDN); + bf_mul_ui(Q, Q, b, prec, BF_RNDN); + bf_mul_ui(Q, Q, (uint64_t)CHUD_C * CHUD_C * CHUD_C / 24, prec, BF_RNDN); + bf_delete(&T0); + bf_delete(&T1); + } else { + bf_t P2, Q2, G2; + + bf_init(s, &P2); + bf_init(s, &Q2); + bf_init(s, &G2); + + c = (a + b) / 2; + chud_bs(P, Q, G, a, c, 1, prec); + chud_bs(&P2, &Q2, &G2, c, b, need_g, prec); + + /* Q = Q1 * Q2 */ + /* G = G1 * G2 */ + /* P = P1 * Q2 + P2 * G1 */ + bf_mul(&P2, &P2, G, prec, BF_RNDN); + if (!need_g) + bf_set_ui(G, 0); + bf_mul(P, P, &Q2, prec, BF_RNDN); + bf_add(P, P, &P2, prec, BF_RNDN); + bf_delete(&P2); + + bf_mul(Q, Q, &Q2, prec, BF_RNDN); + bf_delete(&Q2); + if (need_g) + bf_mul(G, G, &G2, prec, BF_RNDN); + bf_delete(&G2); + } +} + +/* compute Pi with faithful rounding at precision 'prec' using the + Chudnovsky formula */ +static void bf_const_pi_internal(bf_t *Q, limb_t prec) +{ + bf_context_t *s = Q->ctx; + int64_t n, prec1; + bf_t P, G; + + /* number of serie terms */ + n = prec / CHUD_BITS_PER_TERM + 1; + /* XXX: precision analysis */ + prec1 = prec + 32; + + bf_init(s, &P); + bf_init(s, &G); + + chud_bs(&P, Q, &G, 0, n, 0, BF_PREC_INF); + + bf_mul_ui(&G, Q, CHUD_A, prec1, BF_RNDN); + bf_add(&P, &G, &P, prec1, BF_RNDN); + bf_div(Q, Q, &P, prec1, BF_RNDF); + + bf_set_ui(&P, CHUD_C); + bf_sqrt(&G, &P, prec1, BF_RNDF); + bf_mul_ui(&G, &G, (uint64_t)CHUD_C / 12, prec1, BF_RNDF); + bf_mul(Q, Q, &G, prec, BF_RNDN); + bf_delete(&P); + bf_delete(&G); +} + +static int bf_const_get(bf_t *T, limb_t prec, bf_flags_t flags, + BFConstCache *c, + void (*func)(bf_t *res, limb_t prec), int sign) +{ + limb_t ziv_extra_bits, prec1; + + ziv_extra_bits = 32; + for(;;) { + prec1 = prec + ziv_extra_bits; + if (c->prec < prec1) { + if (c->val.len == 0) + bf_init(T->ctx, &c->val); + func(&c->val, prec1); + c->prec = prec1; + } else { + prec1 = c->prec; + } + bf_set(T, &c->val); + T->sign = sign; + if (!bf_can_round(T, prec, flags & BF_RND_MASK, prec1)) { + /* and more precision and retry */ + ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); + } else { + break; + } + } + return bf_round(T, prec, flags); +} + +static void bf_const_free(BFConstCache *c) +{ + bf_delete(&c->val); + memset(c, 0, sizeof(*c)); +} + +int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = T->ctx; + return bf_const_get(T, prec, flags, &s->log2_cache, bf_const_log2_internal, 0); +} + +/* return rounded pi * (1 - 2 * sign) */ +static int bf_const_pi_signed(bf_t *T, int sign, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = T->ctx; + return bf_const_get(T, prec, flags, &s->pi_cache, bf_const_pi_internal, + sign); +} + +int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags) +{ + return bf_const_pi_signed(T, 0, prec, flags); +} + +void bf_clear_cache(bf_context_t *s) +{ +#ifdef USE_FFT_MUL + fft_clear_cache(s); +#endif + bf_const_free(&s->log2_cache); + bf_const_free(&s->pi_cache); +} + +/* ZivFunc should compute the result 'r' with faithful rounding at + precision 'prec'. For efficiency purposes, the final bf_round() + does not need to be done in the function. */ +typedef int ZivFunc(bf_t *r, const bf_t *a, limb_t prec, void *opaque); + +static int bf_ziv_rounding(bf_t *r, const bf_t *a, + limb_t prec, bf_flags_t flags, + ZivFunc *f, void *opaque) +{ + int rnd_mode, ret; + slimb_t prec1, ziv_extra_bits; + + rnd_mode = flags & BF_RND_MASK; + if (rnd_mode == BF_RNDF) { + /* no need to iterate */ + f(r, a, prec, opaque); + ret = 0; + } else { + ziv_extra_bits = 32; + for(;;) { + prec1 = prec + ziv_extra_bits; + ret = f(r, a, prec1, opaque); + if (ret & (BF_ST_OVERFLOW | BF_ST_UNDERFLOW | BF_ST_MEM_ERROR)) { + /* overflow or underflow should never happen because + it indicates the rounding cannot be done correctly, + but we do not catch all the cases */ + return ret; + } + /* if the result is exact, we can stop */ + if (!(ret & BF_ST_INEXACT)) { + ret = 0; + break; + } + if (bf_can_round(r, prec, rnd_mode, prec1)) { + ret = BF_ST_INEXACT; + break; + } + ziv_extra_bits = ziv_extra_bits * 2; + // printf("ziv_extra_bits=%" PRId64 "\n", (int64_t)ziv_extra_bits); + } + } + if (r->len == 0) + return ret; + else + return __bf_round(r, prec, flags, r->len, ret); +} + +/* add (1 - 2*e_sign) * 2^e */ +static int bf_add_epsilon(bf_t *r, const bf_t *a, slimb_t e, int e_sign, + limb_t prec, int flags) +{ + bf_t T_s, *T = &T_s; + int ret; + /* small argument case: result = 1 + epsilon * sign(x) */ + bf_init(a->ctx, T); + bf_set_ui(T, 1); + T->sign = e_sign; + T->expn += e; + ret = bf_add(r, r, T, prec, flags); + bf_delete(T); + return ret; +} + +/* Compute the exponential using faithful rounding at precision 'prec'. + Note: the algorithm is from MPFR */ +static int bf_exp_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + slimb_t n, K, l, i, prec1; + + assert(r != a); + + /* argument reduction: + T = a - n*log(2) with 0 <= T < log(2) and n integer. + */ + bf_init(s, T); + if (a->expn <= -1) { + /* 0 <= abs(a) <= 0.5 */ + if (a->sign) + n = -1; + else + n = 0; + } else { + bf_const_log2(T, LIMB_BITS, BF_RNDZ); + bf_div(T, a, T, LIMB_BITS, BF_RNDD); + bf_get_limb(&n, T, 0); + } + + K = bf_isqrt((prec + 1) / 2); + l = (prec - 1) / K + 1; + /* XXX: precision analysis ? */ + prec1 = prec + (K + 2 * l + 18) + K + 8; + if (a->expn > 0) + prec1 += a->expn; + // printf("n=%ld K=%ld prec1=%ld\n", n, K, prec1); + + bf_const_log2(T, prec1, BF_RNDF); + bf_mul_si(T, T, n, prec1, BF_RNDN); + bf_sub(T, a, T, prec1, BF_RNDN); + + /* reduce the range of T */ + bf_mul_2exp(T, -K, BF_PREC_INF, BF_RNDZ); + + /* Taylor expansion around zero : + 1 + x + x^2/2 + ... + x^n/n! + = (1 + x * (1 + x/2 * (1 + ... (x/n)))) + */ + { + bf_t U_s, *U = &U_s; + + bf_init(s, U); + bf_set_ui(r, 1); + for(i = l ; i >= 1; i--) { + bf_set_ui(U, i); + bf_div(U, T, U, prec1, BF_RNDN); + bf_mul(r, r, U, prec1, BF_RNDN); + bf_add_si(r, r, 1, prec1, BF_RNDN); + } + bf_delete(U); + } + bf_delete(T); + + /* undo the range reduction */ + for(i = 0; i < K; i++) { + bf_mul(r, r, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); + } + + /* undo the argument reduction */ + bf_mul_2exp(r, n, BF_PREC_INF, BF_RNDZ | BF_FLAG_EXT_EXP); + + return BF_ST_INEXACT; +} + +/* crude overflow and underflow tests for exp(a). a_low <= a <= a_high */ +static int check_exp_underflow_overflow(bf_context_t *s, bf_t *r, + const bf_t *a_low, const bf_t *a_high, + limb_t prec, bf_flags_t flags) +{ + bf_t T_s, *T = &T_s; + bf_t log2_s, *log2 = &log2_s; + slimb_t e_min, e_max; + + if (a_high->expn <= 0) + return 0; + + e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + e_min = -e_max + 3; + if (flags & BF_FLAG_SUBNORMAL) + e_min -= (prec - 1); + + bf_init(s, T); + bf_init(s, log2); + bf_const_log2(log2, LIMB_BITS, BF_RNDU); + bf_mul_ui(T, log2, e_max, LIMB_BITS, BF_RNDU); + /* a_low > e_max * log(2) implies exp(a) > e_max */ + if (bf_cmp_lt(T, a_low) > 0) { + /* overflow */ + bf_delete(T); + bf_delete(log2); + return bf_set_overflow(r, 0, prec, flags); + } + /* a_high < (e_min - 2) * log(2) implies exp(a) < (e_min - 2) */ + bf_const_log2(log2, LIMB_BITS, BF_RNDD); + bf_mul_si(T, log2, e_min - 2, LIMB_BITS, BF_RNDD); + if (bf_cmp_lt(a_high, T)) { + int rnd_mode = flags & BF_RND_MASK; + + /* underflow */ + bf_delete(T); + bf_delete(log2); + if (rnd_mode == BF_RNDU) { + /* set the smallest value */ + bf_set_ui(r, 1); + r->expn = e_min; + } else { + bf_set_zero(r, 0); + } + return BF_ST_UNDERFLOW | BF_ST_INEXACT; + } + bf_delete(log2); + bf_delete(T); + return 0; +} + +int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + int ret; + assert(r != a); + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (a->expn == BF_EXP_INF) { + if (a->sign) + bf_set_zero(r, 0); + else + bf_set_inf(r, 0); + } else { + bf_set_ui(r, 1); + } + return 0; + } + + ret = check_exp_underflow_overflow(s, r, a, a, prec, flags); + if (ret) + return ret; + if (a->expn < 0 && (-a->expn) >= (prec + 2)) { + /* small argument case: result = 1 + epsilon * sign(x) */ + bf_set_ui(r, 1); + return bf_add_epsilon(r, r, -(prec + 2), a->sign, prec, flags); + } + + return bf_ziv_rounding(r, a, prec, flags, bf_exp_internal, NULL); +} + +static int bf_log_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + bf_t U_s, *U = &U_s; + bf_t V_s, *V = &V_s; + slimb_t n, prec1, l, i, K; + + assert(r != a); + + bf_init(s, T); + /* argument reduction 1 */ + /* T=a*2^n with 2/3 <= T <= 4/3 */ + { + bf_t U_s, *U = &U_s; + bf_set(T, a); + n = T->expn; + T->expn = 0; + /* U= ~ 2/3 */ + bf_init(s, U); + bf_set_ui(U, 0xaaaaaaaa); + U->expn = 0; + if (bf_cmp_lt(T, U)) { + T->expn++; + n--; + } + bf_delete(U); + } + // printf("n=%ld\n", n); + // bf_print_str("T", T); + + /* XXX: precision analysis */ + /* number of iterations for argument reduction 2 */ + K = bf_isqrt((prec + 1) / 2); + /* order of Taylor expansion */ + l = prec / (2 * K) + 1; + /* precision of the intermediate computations */ + prec1 = prec + K + 2 * l + 32; + + bf_init(s, U); + bf_init(s, V); + + /* Note: cancellation occurs here, so we use more precision (XXX: + reduce the precision by computing the exact cancellation) */ + bf_add_si(T, T, -1, BF_PREC_INF, BF_RNDN); + + /* argument reduction 2 */ + for(i = 0; i < K; i++) { + /* T = T / (1 + sqrt(1 + T)) */ + bf_add_si(U, T, 1, prec1, BF_RNDN); + bf_sqrt(V, U, prec1, BF_RNDF); + bf_add_si(U, V, 1, prec1, BF_RNDN); + bf_div(T, T, U, prec1, BF_RNDN); + } + + { + bf_t Y_s, *Y = &Y_s; + bf_t Y2_s, *Y2 = &Y2_s; + bf_init(s, Y); + bf_init(s, Y2); + + /* compute ln(1+x) = ln((1+y)/(1-y)) with y=x/(2+x) + = y + y^3/3 + ... + y^(2*l + 1) / (2*l+1) + with Y=Y^2 + = y*(1+Y/3+Y^2/5+...) = y*(1+Y*(1/3+Y*(1/5 + ...))) + */ + bf_add_si(Y, T, 2, prec1, BF_RNDN); + bf_div(Y, T, Y, prec1, BF_RNDN); + + bf_mul(Y2, Y, Y, prec1, BF_RNDN); + bf_set_ui(r, 0); + for(i = l; i >= 1; i--) { + bf_set_ui(U, 1); + bf_set_ui(V, 2 * i + 1); + bf_div(U, U, V, prec1, BF_RNDN); + bf_add(r, r, U, prec1, BF_RNDN); + bf_mul(r, r, Y2, prec1, BF_RNDN); + } + bf_add_si(r, r, 1, prec1, BF_RNDN); + bf_mul(r, r, Y, prec1, BF_RNDN); + bf_delete(Y); + bf_delete(Y2); + } + bf_delete(V); + bf_delete(U); + + /* multiplication by 2 for the Taylor expansion and undo the + argument reduction 2*/ + bf_mul_2exp(r, K + 1, BF_PREC_INF, BF_RNDZ); + + /* undo the argument reduction 1 */ + bf_const_log2(T, prec1, BF_RNDF); + bf_mul_si(T, T, n, prec1, BF_RNDN); + bf_add(r, r, T, prec1, BF_RNDN); + + bf_delete(T); + return BF_ST_INEXACT; +} + +int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + + assert(r != a); + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + if (a->sign) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_inf(r, 0); + return 0; + } + } else { + bf_set_inf(r, 1); + return 0; + } + } + if (a->sign) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } + bf_init(s, T); + bf_set_ui(T, 1); + if (bf_cmp_eq(a, T)) { + bf_set_zero(r, 0); + bf_delete(T); + return 0; + } + bf_delete(T); + + return bf_ziv_rounding(r, a, prec, flags, bf_log_internal, NULL); +} + +/* x and y finite and x > 0 */ +static int bf_pow_generic(bf_t *r, const bf_t *x, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + const bf_t *y = opaque; + bf_t T_s, *T = &T_s; + limb_t prec1; + + bf_init(s, T); + /* XXX: proof for the added precision */ + prec1 = prec + 32; + bf_log(T, x, prec1, BF_RNDF | BF_FLAG_EXT_EXP); + bf_mul(T, T, y, prec1, BF_RNDF | BF_FLAG_EXT_EXP); + if (bf_is_nan(T)) + bf_set_nan(r); + else + bf_exp_internal(r, T, prec1, NULL); /* no overflow/underlow test needed */ + bf_delete(T); + return BF_ST_INEXACT; +} + +/* x and y finite, x > 0, y integer and y fits on one limb */ +static int bf_pow_int(bf_t *r, const bf_t *x, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + const bf_t *y = opaque; + bf_t T_s, *T = &T_s; + limb_t prec1; + int ret; + slimb_t y1; + + bf_get_limb(&y1, y, 0); + if (y1 < 0) + y1 = -y1; + /* XXX: proof for the added precision */ + prec1 = prec + ceil_log2(y1) * 2 + 8; + ret = bf_pow_ui(r, x, y1 < 0 ? -y1 : y1, prec1, BF_RNDN | BF_FLAG_EXT_EXP); + if (y->sign) { + bf_init(s, T); + bf_set_ui(T, 1); + ret |= bf_div(r, T, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); + bf_delete(T); + } + return ret; +} + +/* x must be a finite non zero float. Return TRUE if there is a + floating point number r such as x=r^(2^n) and return this floating + point number 'r'. Otherwise return FALSE and r is undefined. */ +static BOOL check_exact_power2n(bf_t *r, const bf_t *x, slimb_t n) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + slimb_t e, i, er; + limb_t v; + + /* x = m*2^e with m odd integer */ + e = bf_get_exp_min(x); + /* fast check on the exponent */ + if (n > (LIMB_BITS - 1)) { + if (e != 0) + return FALSE; + er = 0; + } else { + if ((e & (((limb_t)1 << n) - 1)) != 0) + return FALSE; + er = e >> n; + } + /* every perfect odd square = 1 modulo 8 */ + v = get_bits(x->tab, x->len, x->len * LIMB_BITS - x->expn + e); + if ((v & 7) != 1) + return FALSE; + + bf_init(s, T); + bf_set(T, x); + T->expn -= e; + for(i = 0; i < n; i++) { + if (i != 0) + bf_set(T, r); + if (bf_sqrtrem(r, NULL, T) != 0) + return FALSE; + } + r->expn += er; + return TRUE; +} + +/* prec = BF_PREC_INF is accepted for x and y integers and y >= 0 */ +int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + bf_t ytmp_s; + BOOL y_is_int, y_is_odd; + int r_sign, ret, rnd_mode; + slimb_t y_emin; + + if (x->len == 0 || y->len == 0) { + if (y->expn == BF_EXP_ZERO) { + /* pow(x, 0) = 1 */ + bf_set_ui(r, 1); + } else if (x->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else { + int cmp_x_abs_1; + bf_set_ui(r, 1); + cmp_x_abs_1 = bf_cmpu(x, r); + if (cmp_x_abs_1 == 0 && (flags & BF_POW_JS_QUIRKS) && + (y->expn >= BF_EXP_INF)) { + bf_set_nan(r); + } else if (cmp_x_abs_1 == 0 && + (!x->sign || y->expn != BF_EXP_NAN)) { + /* pow(1, y) = 1 even if y = NaN */ + /* pow(-1, +/-inf) = 1 */ + } else if (y->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (y->expn == BF_EXP_INF) { + if (y->sign == (cmp_x_abs_1 > 0)) { + bf_set_zero(r, 0); + } else { + bf_set_inf(r, 0); + } + } else { + y_emin = bf_get_exp_min(y); + y_is_odd = (y_emin == 0); + if (y->sign == (x->expn == BF_EXP_ZERO)) { + bf_set_inf(r, y_is_odd & x->sign); + if (y->sign) { + /* pow(0, y) with y < 0 */ + return BF_ST_DIVIDE_ZERO; + } + } else { + bf_set_zero(r, y_is_odd & x->sign); + } + } + } + return 0; + } + bf_init(s, T); + bf_set(T, x); + y_emin = bf_get_exp_min(y); + y_is_int = (y_emin >= 0); + rnd_mode = flags & BF_RND_MASK; + if (x->sign) { + if (!y_is_int) { + bf_set_nan(r); + bf_delete(T); + return BF_ST_INVALID_OP; + } + y_is_odd = (y_emin == 0); + r_sign = y_is_odd; + /* change the directed rounding mode if the sign of the result + is changed */ + if (r_sign && (rnd_mode == BF_RNDD || rnd_mode == BF_RNDU)) + flags ^= 1; + bf_neg(T); + } else { + r_sign = 0; + } + + bf_set_ui(r, 1); + if (bf_cmp_eq(T, r)) { + /* abs(x) = 1: nothing more to do */ + ret = 0; + } else { + /* check the overflow/underflow cases */ + { + bf_t al_s, *al = &al_s; + bf_t ah_s, *ah = &ah_s; + limb_t precl = LIMB_BITS; + + bf_init(s, al); + bf_init(s, ah); + /* compute bounds of log(abs(x)) * y with a low precision */ + /* XXX: compute bf_log() once */ + /* XXX: add a fast test before this slow test */ + bf_log(al, T, precl, BF_RNDD); + bf_log(ah, T, precl, BF_RNDU); + bf_mul(al, al, y, precl, BF_RNDD ^ y->sign); + bf_mul(ah, ah, y, precl, BF_RNDU ^ y->sign); + ret = check_exp_underflow_overflow(s, r, al, ah, prec, flags); + bf_delete(al); + bf_delete(ah); + if (ret) + goto done; + } + + if (y_is_int) { + slimb_t T_bits, e; + int_pow: + T_bits = T->expn - bf_get_exp_min(T); + if (T_bits == 1) { + /* pow(2^b, y) = 2^(b*y) */ + bf_mul_si(T, y, T->expn - 1, LIMB_BITS, BF_RNDZ); + bf_get_limb(&e, T, 0); + bf_set_ui(r, 1); + ret = bf_mul_2exp(r, e, prec, flags); + } else if (prec == BF_PREC_INF) { + slimb_t y1; + /* specific case for infinite precision (integer case) */ + bf_get_limb(&y1, y, 0); + assert(!y->sign); + /* x must be an integer, so abs(x) >= 2 */ + if (y1 >= ((slimb_t)1 << BF_EXP_BITS_MAX)) { + bf_delete(T); + return bf_set_overflow(r, 0, BF_PREC_INF, flags); + } + ret = bf_pow_ui(r, T, y1, BF_PREC_INF, BF_RNDZ); + } else { + if (y->expn <= 31) { + /* small enough power: use exponentiation in all cases */ + } else if (y->sign) { + /* cannot be exact */ + goto general_case; + } else { + if (rnd_mode == BF_RNDF) + goto general_case; /* no need to track exact results */ + /* see if the result has a chance to be exact: + if x=a*2^b (a odd), x^y=a^y*2^(b*y) + x^y needs a precision of at least floor_log2(a)*y bits + */ + bf_mul_si(r, y, T_bits - 1, LIMB_BITS, BF_RNDZ); + bf_get_limb(&e, r, 0); + if (prec < e) + goto general_case; + } + ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_int, (void *)y); + } + } else { + if (rnd_mode != BF_RNDF) { + bf_t *y1; + if (y_emin < 0 && check_exact_power2n(r, T, -y_emin)) { + /* the problem is reduced to a power to an integer */ + bf_set(T, r); + y1 = &ytmp_s; + y1->tab = y->tab; + y1->len = y->len; + y1->sign = y->sign; + y1->expn = y->expn - y_emin; + y = y1; + goto int_pow; + } + } + general_case: + ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_generic, (void *)y); + } + } + done: + bf_delete(T); + r->sign = r_sign; + return ret; +} + +/* compute sqrt(-2*x-x^2) to get |sin(x)| from cos(x) - 1. */ +static void bf_sqrt_sin(bf_t *r, const bf_t *x, limb_t prec1) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + bf_init(s, T); + bf_set(T, x); + bf_mul(r, T, T, prec1, BF_RNDN); + bf_mul_2exp(T, 1, BF_PREC_INF, BF_RNDZ); + bf_add(T, T, r, prec1, BF_RNDN); + bf_neg(T); + bf_sqrt(r, T, prec1, BF_RNDF); + bf_delete(T); +} + +static int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec) +{ + bf_context_t *s1 = a->ctx; + bf_t T_s, *T = &T_s; + bf_t U_s, *U = &U_s; + bf_t r_s, *r = &r_s; + slimb_t K, prec1, i, l, mod, prec2; + int is_neg; + + assert(c != a && s != a); + + bf_init(s1, T); + bf_init(s1, U); + bf_init(s1, r); + + /* XXX: precision analysis */ + K = bf_isqrt(prec / 2); + l = prec / (2 * K) + 1; + prec1 = prec + 2 * K + l + 8; + + /* after the modulo reduction, -pi/4 <= T <= pi/4 */ + if (a->expn <= -1) { + /* abs(a) <= 0.25: no modulo reduction needed */ + bf_set(T, a); + mod = 0; + } else { + slimb_t cancel; + cancel = 0; + for(;;) { + prec2 = prec1 + a->expn + cancel; + bf_const_pi(U, prec2, BF_RNDF); + bf_mul_2exp(U, -1, BF_PREC_INF, BF_RNDZ); + bf_remquo(&mod, T, a, U, prec2, BF_RNDN, BF_RNDN); + // printf("T.expn=%ld prec2=%ld\n", T->expn, prec2); + if (mod == 0 || (T->expn != BF_EXP_ZERO && + (T->expn + prec2) >= (prec1 - 1))) + break; + /* increase the number of bits until the precision is good enough */ + cancel = bf_max(-T->expn, (cancel + 1) * 3 / 2); + } + mod &= 3; + } + + is_neg = T->sign; + + /* compute cosm1(x) = cos(x) - 1 */ + bf_mul(T, T, T, prec1, BF_RNDN); + bf_mul_2exp(T, -2 * K, BF_PREC_INF, BF_RNDZ); + + /* Taylor expansion: + -x^2/2 + x^4/4! - x^6/6! + ... + */ + bf_set_ui(r, 1); + for(i = l ; i >= 1; i--) { + bf_set_ui(U, 2 * i - 1); + bf_mul_ui(U, U, 2 * i, BF_PREC_INF, BF_RNDZ); + bf_div(U, T, U, prec1, BF_RNDN); + bf_mul(r, r, U, prec1, BF_RNDN); + bf_neg(r); + if (i != 1) + bf_add_si(r, r, 1, prec1, BF_RNDN); + } + bf_delete(U); + + /* undo argument reduction: + cosm1(2*x)= 2*(2*cosm1(x)+cosm1(x)^2) + */ + for(i = 0; i < K; i++) { + bf_mul(T, r, r, prec1, BF_RNDN); + bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ); + bf_add(r, r, T, prec1, BF_RNDN); + bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ); + } + bf_delete(T); + + if (c) { + if ((mod & 1) == 0) { + bf_add_si(c, r, 1, prec1, BF_RNDN); + } else { + bf_sqrt_sin(c, r, prec1); + c->sign = is_neg ^ 1; + } + c->sign ^= mod >> 1; + } + if (s) { + if ((mod & 1) == 0) { + bf_sqrt_sin(s, r, prec1); + s->sign = is_neg; + } else { + bf_add_si(s, r, 1, prec1, BF_RNDN); + } + s->sign ^= mod >> 1; + } + bf_delete(r); + return BF_ST_INEXACT; +} + +static int bf_cos_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + return bf_sincos(NULL, r, a, prec); +} + +int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_ui(r, 1); + return 0; + } + } + + /* small argument case: result = 1+r(x) with r(x) = -x^2/2 + + O(X^4). We assume r(x) < 2^(2*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = 2 * a->expn - 1; + if (e < -(prec + 2)) { + bf_set_ui(r, 1); + return bf_add_epsilon(r, r, e, 1, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_cos_internal, NULL); +} + +static int bf_sin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + return bf_sincos(r, NULL, a, prec); +} + +int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + + /* small argument case: result = x+r(x) with r(x) = -x^3/6 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 2); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_sin_internal, NULL); +} + +static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + limb_t prec1; + + /* XXX: precision analysis */ + prec1 = prec + 8; + bf_init(s, T); + bf_sincos(r, T, a, prec1); + bf_div(r, r, T, prec1, BF_RNDF); + bf_delete(T); + return BF_ST_INEXACT; +} + +int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + assert(r != a); + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + + /* small argument case: result = x+r(x) with r(x) = x^3/3 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 1); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_tan_internal, NULL); +} + +/* if add_pi2 is true, add pi/2 to the result (used for acos(x) to + avoid cancellation) */ +static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec, + void *opaque) +{ + bf_context_t *s = r->ctx; + BOOL add_pi2 = (BOOL)(intptr_t)opaque; + bf_t T_s, *T = &T_s; + bf_t U_s, *U = &U_s; + bf_t V_s, *V = &V_s; + bf_t X2_s, *X2 = &X2_s; + int cmp_1; + slimb_t prec1, i, K, l; + + /* XXX: precision analysis */ + K = bf_isqrt((prec + 1) / 2); + l = prec / (2 * K) + 1; + prec1 = prec + K + 2 * l + 32; + // printf("prec=%d K=%d l=%d prec1=%d\n", (int)prec, (int)K, (int)l, (int)prec1); + + bf_init(s, T); + cmp_1 = (a->expn >= 1); /* a >= 1 */ + if (cmp_1) { + bf_set_ui(T, 1); + bf_div(T, T, a, prec1, BF_RNDN); + } else { + bf_set(T, a); + } + + /* abs(T) <= 1 */ + + /* argument reduction */ + + bf_init(s, U); + bf_init(s, V); + bf_init(s, X2); + for(i = 0; i < K; i++) { + /* T = T / (1 + sqrt(1 + T^2)) */ + bf_mul(U, T, T, prec1, BF_RNDN); + bf_add_si(U, U, 1, prec1, BF_RNDN); + bf_sqrt(V, U, prec1, BF_RNDN); + bf_add_si(V, V, 1, prec1, BF_RNDN); + bf_div(T, T, V, prec1, BF_RNDN); + } + + /* Taylor series: + x - x^3/3 + ... + (-1)^ l * y^(2*l + 1) / (2*l+1) + */ + bf_mul(X2, T, T, prec1, BF_RNDN); + bf_set_ui(r, 0); + for(i = l; i >= 1; i--) { + bf_set_si(U, 1); + bf_set_ui(V, 2 * i + 1); + bf_div(U, U, V, prec1, BF_RNDN); + bf_neg(r); + bf_add(r, r, U, prec1, BF_RNDN); + bf_mul(r, r, X2, prec1, BF_RNDN); + } + bf_neg(r); + bf_add_si(r, r, 1, prec1, BF_RNDN); + bf_mul(r, r, T, prec1, BF_RNDN); + + /* undo the argument reduction */ + bf_mul_2exp(r, K, BF_PREC_INF, BF_RNDZ); + + bf_delete(U); + bf_delete(V); + bf_delete(X2); + + i = add_pi2; + if (cmp_1 > 0) { + /* undo the inversion : r = sign(a)*PI/2 - r */ + bf_neg(r); + i += 1 - 2 * a->sign; + } + /* add i*(pi/2) with -1 <= i <= 2 */ + if (i != 0) { + bf_const_pi(T, prec1, BF_RNDF); + if (i != 2) + bf_mul_2exp(T, -1, BF_PREC_INF, BF_RNDZ); + T->sign = (i < 0); + bf_add(r, T, r, prec1, BF_RNDN); + } + + bf_delete(T); + return BF_ST_INEXACT; +} + +int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + /* -PI/2 or PI/2 */ + bf_const_pi_signed(r, a->sign, prec, flags); + bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res == 0) { + /* short cut: abs(a) == 1 -> +/-pi/4 */ + bf_const_pi_signed(r, a->sign, prec, flags); + bf_mul_2exp(r, -2, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } + + /* small argument case: result = x+r(x) with r(x) = -x^3/3 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 1); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_atan_internal, (void *)FALSE); +} + +static int bf_atan2_internal(bf_t *r, const bf_t *y, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + const bf_t *x = opaque; + bf_t T_s, *T = &T_s; + limb_t prec1; + int ret; + + if (y->expn == BF_EXP_NAN || x->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } + + /* compute atan(y/x) assumming inf/inf = 1 and 0/0 = 0 */ + bf_init(s, T); + prec1 = prec + 32; + if (y->expn == BF_EXP_INF && x->expn == BF_EXP_INF) { + bf_set_ui(T, 1); + T->sign = y->sign ^ x->sign; + } else if (y->expn == BF_EXP_ZERO && x->expn == BF_EXP_ZERO) { + bf_set_zero(T, y->sign ^ x->sign); + } else { + bf_div(T, y, x, prec1, BF_RNDF); + } + ret = bf_atan(r, T, prec1, BF_RNDF); + + if (x->sign) { + /* if x < 0 (it includes -0), return sign(y)*pi + atan(y/x) */ + bf_const_pi(T, prec1, BF_RNDF); + T->sign = y->sign; + bf_add(r, r, T, prec1, BF_RNDN); + ret |= BF_ST_INEXACT; + } + + bf_delete(T); + return ret; +} + +int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, + limb_t prec, bf_flags_t flags) +{ + return bf_ziv_rounding(r, y, prec, flags, bf_atan2_internal, (void *)x); +} + +static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + BOOL is_acos = (BOOL)(intptr_t)opaque; + bf_t T_s, *T = &T_s; + limb_t prec1, prec2; + + /* asin(x) = atan(x/sqrt(1-x^2)) + acos(x) = pi/2 - asin(x) */ + prec1 = prec + 8; + /* increase the precision in x^2 to compensate the cancellation in + (1-x^2) if x is close to 1 */ + /* XXX: use less precision when possible */ + if (a->expn >= 0) + prec2 = BF_PREC_INF; + else + prec2 = prec1; + bf_init(s, T); + bf_mul(T, a, a, prec2, BF_RNDN); + bf_neg(T); + bf_add_si(T, T, 1, prec2, BF_RNDN); + + bf_sqrt(r, T, prec1, BF_RNDN); + bf_div(T, a, r, prec1, BF_RNDN); + if (is_acos) + bf_neg(T); + bf_atan_internal(r, T, prec1, (void *)(intptr_t)is_acos); + bf_delete(T); + return BF_ST_INEXACT; +} + +int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res > 0) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } + + /* small argument case: result = x+r(x) with r(x) = x^3/6 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 2); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)FALSE); +} + +int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_const_pi(r, prec, flags); + bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } + } + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res > 0) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else if (res == 0 && a->sign == 0) { + bf_set_zero(r, 0); + return 0; + } + + return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)TRUE); +} + +/***************************************************************/ +/* decimal floating point numbers */ + +#ifdef USE_BF_DEC + +#define adddq(r1, r0, a1, a0) \ + do { \ + limb_t __t = r0; \ + r0 += (a0); \ + r1 += (a1) + (r0 < __t); \ + } while (0) + +#define subdq(r1, r0, a1, a0) \ + do { \ + limb_t __t = r0; \ + r0 -= (a0); \ + r1 -= (a1) + (r0 > __t); \ + } while (0) + +#if LIMB_BITS == 64 + +/* Note: we assume __int128 is available */ +/* uint128_t defined in libbf.h */ +#define muldq(r1, r0, a, b) \ + do { \ + uint128_t __t; \ + __t = (uint128_t)(a) * (uint128_t)(b); \ + r0 = __t; \ + r1 = __t >> 64; \ + } while (0) + +#define divdq(q, r, a1, a0, b) \ + do { \ + uint128_t __t; \ + limb_t __b = (b); \ + __t = ((uint128_t)(a1) << 64) | (a0); \ + q = __t / __b; \ + r = __t % __b; \ + } while (0) + +#else + +#define muldq(r1, r0, a, b) \ + do { \ + uint64_t __t; \ + __t = (uint64_t)(a) * (uint64_t)(b); \ + r0 = __t; \ + r1 = __t >> 32; \ + } while (0) + +#define divdq(q, r, a1, a0, b) \ + do { \ + uint64_t __t; \ + limb_t __b = (b); \ + __t = ((uint64_t)(a1) << 32) | (a0); \ + q = __t / __b; \ + r = __t % __b; \ + } while (0) + +#endif /* LIMB_BITS != 64 */ + +#if LIMB_DIGITS == 19 + +/* WARNING: hardcoded for b = 1e19. It is assumed that: + 0 <= a1 < 2^63 */ +#define divdq_base(q, r, a1, a0)\ +do {\ + uint64_t __a0, __a1, __t0, __t1, __b = BF_DEC_BASE; \ + __a0 = a0;\ + __a1 = a1;\ + __t0 = __a1;\ + __t0 = shld(__t0, __a0, 1);\ + muldq(q, __t1, __t0, UINT64_C(17014118346046923173)); \ + muldq(__t1, __t0, q, __b);\ + subdq(__a1, __a0, __t1, __t0);\ + subdq(__a1, __a0, 1, __b * 2); \ + __t0 = (slimb_t)__a1 >> 1; \ + q += 2 + __t0;\ + adddq(__a1, __a0, 0, __b & __t0);\ + q += __a1; \ + __a0 += __b & __a1; \ + r = __a0;\ +} while(0) + +#elif LIMB_DIGITS == 9 + +/* WARNING: hardcoded for b = 1e9. It is assumed that: + 0 <= a1 < 2^29 */ +#define divdq_base(q, r, a1, a0)\ +do {\ + uint32_t __t0, __t1, __b = BF_DEC_BASE; \ + __t0 = a1;\ + __t1 = a0;\ + __t0 = (__t0 << 3) | (__t1 >> (32 - 3)); \ + muldq(q, __t1, __t0, 2305843009U);\ + r = a0 - q * __b;\ + __t1 = (r >= __b);\ + q += __t1;\ + if (__t1)\ + r -= __b;\ +} while(0) + +#endif + +/* fast integer division by a fixed constant */ + +typedef struct FastDivData { + limb_t m1; /* multiplier */ + int8_t shift1; + int8_t shift2; +} FastDivData; + +/* From "Division by Invariant Integers using Multiplication" by + Torborn Granlund and Peter L. Montgomery */ +/* d must be != 0 */ +static inline __maybe_unused void fast_udiv_init(FastDivData *s, limb_t d) +{ + int l; + limb_t q, r, m1; + if (d == 1) + l = 0; + else + l = 64 - clz64(d - 1); + divdq(q, r, ((limb_t)1 << l) - d, 0, d); + (void)r; + m1 = q + 1; + // printf("d=%lu l=%d m1=0x%016lx\n", d, l, m1); + s->m1 = m1; + s->shift1 = l; + if (s->shift1 > 1) + s->shift1 = 1; + s->shift2 = l - 1; + if (s->shift2 < 0) + s->shift2 = 0; +} + +static inline limb_t fast_udiv(limb_t a, const FastDivData *s) +{ + limb_t t0, t1; + muldq(t1, t0, s->m1, a); + t0 = (a - t1) >> s->shift1; + return (t1 + t0) >> s->shift2; +} + +/* contains 10^i */ +const limb_t mp_pow_dec[LIMB_DIGITS + 1] = { + 1U, + 10U, + 100U, + 1000U, + 10000U, + 100000U, + 1000000U, + 10000000U, + 100000000U, + 1000000000U, +#if LIMB_BITS == 64 + 10000000000U, + 100000000000U, + 1000000000000U, + 10000000000000U, + 100000000000000U, + 1000000000000000U, + 10000000000000000U, + 100000000000000000U, + 1000000000000000000U, + 10000000000000000000U, +#endif +}; + +/* precomputed from fast_udiv_init(10^i) */ +static const FastDivData mp_pow_div[LIMB_DIGITS + 1] = { +#if LIMB_BITS == 32 + { 0x00000001, 0, 0 }, + { 0x9999999a, 1, 3 }, + { 0x47ae147b, 1, 6 }, + { 0x0624dd30, 1, 9 }, + { 0xa36e2eb2, 1, 13 }, + { 0x4f8b588f, 1, 16 }, + { 0x0c6f7a0c, 1, 19 }, + { 0xad7f29ac, 1, 23 }, + { 0x5798ee24, 1, 26 }, + { 0x12e0be83, 1, 29 }, +#else + { 0x0000000000000001, 0, 0 }, + { 0x999999999999999a, 1, 3 }, + { 0x47ae147ae147ae15, 1, 6 }, + { 0x0624dd2f1a9fbe77, 1, 9 }, + { 0xa36e2eb1c432ca58, 1, 13 }, + { 0x4f8b588e368f0847, 1, 16 }, + { 0x0c6f7a0b5ed8d36c, 1, 19 }, + { 0xad7f29abcaf48579, 1, 23 }, + { 0x5798ee2308c39dfa, 1, 26 }, + { 0x12e0be826d694b2f, 1, 29 }, + { 0xb7cdfd9d7bdbab7e, 1, 33 }, + { 0x5fd7fe17964955fe, 1, 36 }, + { 0x19799812dea11198, 1, 39 }, + { 0xc25c268497681c27, 1, 43 }, + { 0x6849b86a12b9b01f, 1, 46 }, + { 0x203af9ee756159b3, 1, 49 }, + { 0xcd2b297d889bc2b7, 1, 53 }, + { 0x70ef54646d496893, 1, 56 }, + { 0x2725dd1d243aba0f, 1, 59 }, + { 0xd83c94fb6d2ac34d, 1, 63 }, +#endif +}; + +/* divide by 10^shift with 0 <= shift <= LIMB_DIGITS */ +static inline limb_t fast_shr_dec(limb_t a, int shift) +{ + return fast_udiv(a, &mp_pow_div[shift]); +} + +/* division and remainder by 10^shift */ +#define fast_shr_rem_dec(q, r, a, shift) q = fast_shr_dec(a, shift), r = a - q * mp_pow_dec[shift] + +limb_t mp_add_dec(limb_t *res, const limb_t *op1, const limb_t *op2, + mp_size_t n, limb_t carry) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, a, v; + + k=carry; + for(i=0;i v; + if (k) + a += base; + res[i] = a; + } + return k; +} + +limb_t mp_sub_ui_dec(limb_t *tab, limb_t b, mp_size_t n) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, v, a; + + k=b; + for(i=0;i v; + if (k) + a += base; + tab[i]=a; + if (k == 0) + break; + } + return k; +} + +/* taba[] = taba[] * b + l. 0 <= b, l <= base - 1. Return the high carry */ +limb_t mp_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b, limb_t l) +{ + mp_size_t i; + limb_t t0, t1, r; + + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + divdq_base(l, r, t1, t0); + tabr[i] = r; + } + return l; +} + +/* tabr[] += taba[] * b. 0 <= b <= base - 1. Return the value to add + to the high word */ +limb_t mp_add_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b) +{ + mp_size_t i; + limb_t l, t0, t1, r; + + l = 0; + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + adddq(t1, t0, 0, tabr[i]); + divdq_base(l, r, t1, t0); + tabr[i] = r; + } + return l; +} + +/* tabr[] -= taba[] * b. 0 <= b <= base - 1. Return the value to + substract to the high word. */ +limb_t mp_sub_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t l, t0, t1, r, a, v, c; + + /* XXX: optimize */ + l = 0; + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + divdq_base(l, r, t1, t0); + v = tabr[i]; + a = v - r; + c = a > v; + if (c) + a += base; + /* never bigger than base because r = 0 when l = base - 1 */ + l += c; + tabr[i] = a; + } + return l; +} + +/* size of the result : op1_size + op2_size. */ +void mp_mul_basecase_dec(limb_t *result, + const limb_t *op1, mp_size_t op1_size, + const limb_t *op2, mp_size_t op2_size) +{ + mp_size_t i; + limb_t r; + + result[op1_size] = mp_mul1_dec(result, op1, op1_size, op2[0], 0); + + for(i=1;i> 1; + if (r) + r = base_div2; + for(i = na - 1; i >= 0; i--) { + t0 = taba[i]; + tabr[i] = (t0 >> 1) + r; + r = 0; + if (t0 & 1) + r = base_div2; + } + if (r) + r = 1; + } else +#endif + if (na >= UDIV1NORM_THRESHOLD) { + shift = clz(b); + if (shift == 0) { + /* normalized case: b >= 2^(LIMB_BITS-1) */ + limb_t b_inv; + b_inv = udiv1norm_init(b); + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + q = udiv1norm(&r, t1, t0, b, b_inv); + tabr[i] = q; + } + } else { + limb_t b_inv; + b <<= shift; + b_inv = udiv1norm_init(b); + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + t1 = (t1 << shift) | (t0 >> (LIMB_BITS - shift)); + t0 <<= shift; + q = udiv1norm(&r, t1, t0, b, b_inv); + r >>= shift; + tabr[i] = q; + } + } + } else { + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + divdq(q, r, t1, t0, b); + tabr[i] = q; + } + } + return r; +} + +static __maybe_unused void mp_print_str_dec(const char *str, + const limb_t *tab, slimb_t n) +{ + slimb_t i; + printf("%s=", str); + for(i = n - 1; i >= 0; i--) { + if (i != n - 1) + printf("_"); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); + } + printf("\n"); +} + +static __maybe_unused void mp_print_str_h_dec(const char *str, + const limb_t *tab, slimb_t n, + limb_t high) +{ + slimb_t i; + printf("%s=", str); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, high); + for(i = n - 1; i >= 0; i--) { + printf("_"); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); + } + printf("\n"); +} + +//#define DEBUG_DIV_SLOW + +#define DIV_STATIC_ALLOC_LEN 16 + +/* return q = a / b and r = a % b. + + taba[na] must be allocated if tabb1[nb - 1] < B / 2. tabb1[nb - 1] + must be != zero. na must be >= nb. 's' can be NULL if tabb1[nb - 1] + >= B / 2. + + The remainder is is returned in taba and contains nb libms. tabq + contains na - nb + 1 limbs. No overlap is permitted. + + Running time of the standard method: (na - nb + 1) * nb + Return 0 if OK, -1 if memory alloc error +*/ +/* XXX: optimize */ +static int mp_div_dec(bf_context_t *s, limb_t *tabq, + limb_t *taba, mp_size_t na, + const limb_t *tabb1, mp_size_t nb) +{ + limb_t base = BF_DEC_BASE; + limb_t r, mult, t0, t1, a, c, q, v, *tabb; + mp_size_t i, j; + limb_t static_tabb[DIV_STATIC_ALLOC_LEN]; + +#ifdef DEBUG_DIV_SLOW + mp_print_str_dec("a", taba, na); + mp_print_str_dec("b", tabb1, nb); +#endif + + /* normalize tabb */ + r = tabb1[nb - 1]; + assert(r != 0); + i = na - nb; + if (r >= BF_DEC_BASE / 2) { + mult = 1; + tabb = (limb_t *)tabb1; + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[i + j] != tabb[j]) { + if (taba[i + j] < tabb[j]) + q = 0; + break; + } + } + tabq[i] = q; + if (q) { + mp_sub_dec(taba + i, taba + i, tabb, nb, 0); + } + i--; + } else { + mult = base / (r + 1); + if (likely(nb <= DIV_STATIC_ALLOC_LEN)) { + tabb = static_tabb; + } else { + tabb = bf_malloc(s, sizeof(limb_t) * nb); + if (!tabb) + return -1; + } + mp_mul1_dec(tabb, tabb1, nb, mult, 0); + taba[na] = mp_mul1_dec(taba, taba, na, mult, 0); + } + +#ifdef DEBUG_DIV_SLOW + printf("mult=" FMT_LIMB "\n", mult); + mp_print_str_dec("a_norm", taba, na + 1); + mp_print_str_dec("b_norm", tabb, nb); +#endif + + for(; i >= 0; i--) { + if (unlikely(taba[i + nb] >= tabb[nb - 1])) { + /* XXX: check if it is really possible */ + q = base - 1; + } else { + muldq(t1, t0, taba[i + nb], base); + adddq(t1, t0, 0, taba[i + nb - 1]); + divdq(q, r, t1, t0, tabb[nb - 1]); + } + // printf("i=%d q1=%ld\n", i, q); + + r = mp_sub_mul1_dec(taba + i, tabb, nb, q); + // mp_dump("r1", taba + i, nb, bd); + // printf("r2=%ld\n", r); + + v = taba[i + nb]; + a = v - r; + c = a > v; + if (c) + a += base; + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = mp_add_dec(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == base) { + break; + } + } + } + } + tabq[i] = q; + } + +#ifdef DEBUG_DIV_SLOW + mp_print_str_dec("q", tabq, na - nb + 1); + mp_print_str_dec("r", taba, nb); +#endif + + /* remove the normalization */ + if (mult != 1) { + mp_div1_dec(taba, taba, nb, mult, 0); + if (unlikely(tabb != static_tabb)) + bf_free(s, tabb); + } + return 0; +} + +/* divide by 10^shift */ +static limb_t mp_shr_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, + limb_t shift, limb_t high) +{ + mp_size_t i; + limb_t l, a, q, r; + + assert(shift >= 1 && shift < LIMB_DIGITS); + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + fast_shr_rem_dec(q, r, a, shift); + tab_r[i] = q + l * mp_pow_dec[LIMB_DIGITS - shift]; + l = r; + } + return l; +} + +/* multiply by 10^shift */ +static limb_t mp_shl_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, + limb_t shift, limb_t low) +{ + mp_size_t i; + limb_t l, a, q, r; + + assert(shift >= 1 && shift < LIMB_DIGITS); + l = low; + for(i = 0; i < n; i++) { + a = tab[i]; + fast_shr_rem_dec(q, r, a, LIMB_DIGITS - shift); + tab_r[i] = r * mp_pow_dec[shift] + l; + l = q; + } + return l; +} + +static limb_t mp_sqrtrem2_dec(limb_t *tabs, limb_t *taba) +{ + int k; + dlimb_t a, b, r; + limb_t taba1[2], s, r0, r1; + + /* convert to binary and normalize */ + a = (dlimb_t)taba[1] * BF_DEC_BASE + taba[0]; + k = clz(a >> LIMB_BITS) & ~1; + b = a << k; + taba1[0] = b; + taba1[1] = b >> LIMB_BITS; + mp_sqrtrem2(&s, taba1); + s >>= (k >> 1); + /* convert the remainder back to decimal */ + r = a - (dlimb_t)s * (dlimb_t)s; + divdq_base(r1, r0, r >> LIMB_BITS, r); + taba[0] = r0; + tabs[0] = s; + return r1; +} + +//#define DEBUG_SQRTREM_DEC + +/* tmp_buf must contain (n / 2 + 1 limbs) */ +static limb_t mp_sqrtrem_rec_dec(limb_t *tabs, limb_t *taba, limb_t n, + limb_t *tmp_buf) +{ + limb_t l, h, rh, ql, qh, c, i; + + if (n == 1) + return mp_sqrtrem2_dec(tabs, taba); +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_dec("a", taba, 2 * n); +#endif + l = n / 2; + h = n - l; + qh = mp_sqrtrem_rec_dec(tabs + l, taba + 2 * l, h, tmp_buf); +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_dec("s1", tabs + l, h); + mp_print_str_h_dec("r1", taba + 2 * l, h, qh); + mp_print_str_h_dec("r2", taba + l, n, qh); +#endif + + /* the remainder is in taba + 2 * l. Its high bit is in qh */ + if (qh) { + mp_sub_dec(taba + 2 * l, taba + 2 * l, tabs + l, h, 0); + } + /* instead of dividing by 2*s, divide by s (which is normalized) + and update q and r */ + mp_div_dec(NULL, tmp_buf, taba + l, n, tabs + l, h); + qh += tmp_buf[l]; + for(i = 0; i < l; i++) + tabs[i] = tmp_buf[i]; + ql = mp_div1_dec(tabs, tabs, l, 2, qh & 1); + qh = qh >> 1; /* 0 or 1 */ + if (ql) + rh = mp_add_dec(taba + l, taba + l, tabs + l, h, 0); + else + rh = 0; +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_h_dec("q", tabs, l, qh); + mp_print_str_h_dec("u", taba + l, h, rh); +#endif + + mp_add_ui_dec(tabs + l, qh, h); +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_dec("s2", tabs, n); +#endif + + /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */ + /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */ + if (qh) { + c = qh; + } else { + mp_mul_basecase_dec(taba + n, tabs, l, tabs, l); + c = mp_sub_dec(taba, taba, taba + n, 2 * l, 0); + } + rh -= mp_sub_ui_dec(taba + 2 * l, c, n - 2 * l); + if ((slimb_t)rh < 0) { + mp_sub_ui_dec(tabs, 1, n); + rh += mp_add_mul1_dec(taba, tabs, n, 2); + rh += mp_add_ui_dec(taba, 1, n); + } + return rh; +} + +/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= B/4. Return (s, + r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 * s. tabs has n + limbs. r is returned in the lower n limbs of taba. Its r[n] is the + returned value of the function. */ +int mp_sqrtrem_dec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n) +{ + limb_t tmp_buf1[8]; + limb_t *tmp_buf; + mp_size_t n2; + n2 = n / 2 + 1; + if (n2 <= countof(tmp_buf1)) { + tmp_buf = tmp_buf1; + } else { + tmp_buf = bf_malloc(s, sizeof(limb_t) * n2); + if (!tmp_buf) + return -1; + } + taba[n] = mp_sqrtrem_rec_dec(tabs, taba, n, tmp_buf); + if (tmp_buf != tmp_buf1) + bf_free(s, tmp_buf); + return 0; +} + +/* return the number of leading zero digits, from 0 to LIMB_DIGITS */ +static int clz_dec(limb_t a) +{ + if (a == 0) + return LIMB_DIGITS; + switch(LIMB_BITS - 1 - clz(a)) { + case 0: /* 1-1 */ + return LIMB_DIGITS - 1; + case 1: /* 2-3 */ + return LIMB_DIGITS - 1; + case 2: /* 4-7 */ + return LIMB_DIGITS - 1; + case 3: /* 8-15 */ + if (a < 10) + return LIMB_DIGITS - 1; + else + return LIMB_DIGITS - 2; + case 4: /* 16-31 */ + return LIMB_DIGITS - 2; + case 5: /* 32-63 */ + return LIMB_DIGITS - 2; + case 6: /* 64-127 */ + if (a < 100) + return LIMB_DIGITS - 2; + else + return LIMB_DIGITS - 3; + case 7: /* 128-255 */ + return LIMB_DIGITS - 3; + case 8: /* 256-511 */ + return LIMB_DIGITS - 3; + case 9: /* 512-1023 */ + if (a < 1000) + return LIMB_DIGITS - 3; + else + return LIMB_DIGITS - 4; + case 10: /* 1024-2047 */ + return LIMB_DIGITS - 4; + case 11: /* 2048-4095 */ + return LIMB_DIGITS - 4; + case 12: /* 4096-8191 */ + return LIMB_DIGITS - 4; + case 13: /* 8192-16383 */ + if (a < 10000) + return LIMB_DIGITS - 4; + else + return LIMB_DIGITS - 5; + case 14: /* 16384-32767 */ + return LIMB_DIGITS - 5; + case 15: /* 32768-65535 */ + return LIMB_DIGITS - 5; + case 16: /* 65536-131071 */ + if (a < 100000) + return LIMB_DIGITS - 5; + else + return LIMB_DIGITS - 6; + case 17: /* 131072-262143 */ + return LIMB_DIGITS - 6; + case 18: /* 262144-524287 */ + return LIMB_DIGITS - 6; + case 19: /* 524288-1048575 */ + if (a < 1000000) + return LIMB_DIGITS - 6; + else + return LIMB_DIGITS - 7; + case 20: /* 1048576-2097151 */ + return LIMB_DIGITS - 7; + case 21: /* 2097152-4194303 */ + return LIMB_DIGITS - 7; + case 22: /* 4194304-8388607 */ + return LIMB_DIGITS - 7; + case 23: /* 8388608-16777215 */ + if (a < 10000000) + return LIMB_DIGITS - 7; + else + return LIMB_DIGITS - 8; + case 24: /* 16777216-33554431 */ + return LIMB_DIGITS - 8; + case 25: /* 33554432-67108863 */ + return LIMB_DIGITS - 8; + case 26: /* 67108864-134217727 */ + if (a < 100000000) + return LIMB_DIGITS - 8; + else + return LIMB_DIGITS - 9; +#if LIMB_BITS == 64 + case 27: /* 134217728-268435455 */ + return LIMB_DIGITS - 9; + case 28: /* 268435456-536870911 */ + return LIMB_DIGITS - 9; + case 29: /* 536870912-1073741823 */ + if (a < 1000000000) + return LIMB_DIGITS - 9; + else + return LIMB_DIGITS - 10; + case 30: /* 1073741824-2147483647 */ + return LIMB_DIGITS - 10; + case 31: /* 2147483648-4294967295 */ + return LIMB_DIGITS - 10; + case 32: /* 4294967296-8589934591 */ + return LIMB_DIGITS - 10; + case 33: /* 8589934592-17179869183 */ + if (a < 10000000000) + return LIMB_DIGITS - 10; + else + return LIMB_DIGITS - 11; + case 34: /* 17179869184-34359738367 */ + return LIMB_DIGITS - 11; + case 35: /* 34359738368-68719476735 */ + return LIMB_DIGITS - 11; + case 36: /* 68719476736-137438953471 */ + if (a < 100000000000) + return LIMB_DIGITS - 11; + else + return LIMB_DIGITS - 12; + case 37: /* 137438953472-274877906943 */ + return LIMB_DIGITS - 12; + case 38: /* 274877906944-549755813887 */ + return LIMB_DIGITS - 12; + case 39: /* 549755813888-1099511627775 */ + if (a < 1000000000000) + return LIMB_DIGITS - 12; + else + return LIMB_DIGITS - 13; + case 40: /* 1099511627776-2199023255551 */ + return LIMB_DIGITS - 13; + case 41: /* 2199023255552-4398046511103 */ + return LIMB_DIGITS - 13; + case 42: /* 4398046511104-8796093022207 */ + return LIMB_DIGITS - 13; + case 43: /* 8796093022208-17592186044415 */ + if (a < 10000000000000) + return LIMB_DIGITS - 13; + else + return LIMB_DIGITS - 14; + case 44: /* 17592186044416-35184372088831 */ + return LIMB_DIGITS - 14; + case 45: /* 35184372088832-70368744177663 */ + return LIMB_DIGITS - 14; + case 46: /* 70368744177664-140737488355327 */ + if (a < 100000000000000) + return LIMB_DIGITS - 14; + else + return LIMB_DIGITS - 15; + case 47: /* 140737488355328-281474976710655 */ + return LIMB_DIGITS - 15; + case 48: /* 281474976710656-562949953421311 */ + return LIMB_DIGITS - 15; + case 49: /* 562949953421312-1125899906842623 */ + if (a < 1000000000000000) + return LIMB_DIGITS - 15; + else + return LIMB_DIGITS - 16; + case 50: /* 1125899906842624-2251799813685247 */ + return LIMB_DIGITS - 16; + case 51: /* 2251799813685248-4503599627370495 */ + return LIMB_DIGITS - 16; + case 52: /* 4503599627370496-9007199254740991 */ + return LIMB_DIGITS - 16; + case 53: /* 9007199254740992-18014398509481983 */ + if (a < 10000000000000000) + return LIMB_DIGITS - 16; + else + return LIMB_DIGITS - 17; + case 54: /* 18014398509481984-36028797018963967 */ + return LIMB_DIGITS - 17; + case 55: /* 36028797018963968-72057594037927935 */ + return LIMB_DIGITS - 17; + case 56: /* 72057594037927936-144115188075855871 */ + if (a < 100000000000000000) + return LIMB_DIGITS - 17; + else + return LIMB_DIGITS - 18; + case 57: /* 144115188075855872-288230376151711743 */ + return LIMB_DIGITS - 18; + case 58: /* 288230376151711744-576460752303423487 */ + return LIMB_DIGITS - 18; + case 59: /* 576460752303423488-1152921504606846975 */ + if (a < 1000000000000000000) + return LIMB_DIGITS - 18; + else + return LIMB_DIGITS - 19; +#endif + default: + return 0; + } +} + +/* for debugging */ +void bfdec_print_str(const char *str, const bfdec_t *a) +{ + slimb_t i; + printf("%s=", str); + + if (a->expn == BF_EXP_NAN) { + printf("NaN"); + } else { + if (a->sign) + putchar('-'); + if (a->expn == BF_EXP_ZERO) { + putchar('0'); + } else if (a->expn == BF_EXP_INF) { + printf("Inf"); + } else { + printf("0."); + for(i = a->len - 1; i >= 0; i--) + printf("%0*" PRIu_LIMB, LIMB_DIGITS, a->tab[i]); + printf("e%" PRId_LIMB, a->expn); + } + } + printf("\n"); +} + +/* return != 0 if one digit between 0 and bit_pos inclusive is not zero. */ +static inline limb_t scan_digit_nz(const bfdec_t *r, slimb_t bit_pos) +{ + slimb_t pos; + limb_t v, q; + int shift; + + if (bit_pos < 0) + return 0; + pos = (limb_t)bit_pos / LIMB_DIGITS; + shift = (limb_t)bit_pos % LIMB_DIGITS; + fast_shr_rem_dec(q, v, r->tab[pos], shift + 1); + (void)q; + if (v != 0) + return 1; + pos--; + while (pos >= 0) { + if (r->tab[pos] != 0) + return 1; + pos--; + } + return 0; +} + +static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos) +{ + slimb_t i; + int shift; + i = floor_div(pos, LIMB_DIGITS); + if (i < 0 || i >= len) + return 0; + shift = pos - i * LIMB_DIGITS; + return fast_shr_dec(tab[i], shift) % 10; +} + +/* return the addend for rounding. Note that prec can be <= 0 for bf_rint() */ +static int bfdec_get_rnd_add(int *pret, const bfdec_t *r, limb_t l, + slimb_t prec, int rnd_mode) +{ + int add_one, inexact; + limb_t digit1, digit0; + + // bfdec_print_str("get_rnd_add", r); + if (rnd_mode == BF_RNDF) { + digit0 = 1; /* faithful rounding does not honor the INEXACT flag */ + } else { + /* starting limb for bit 'prec + 1' */ + digit0 = scan_digit_nz(r, l * LIMB_DIGITS - 1 - bf_max(0, prec + 1)); + } + + /* get the digit at 'prec' */ + digit1 = get_digit(r->tab, l, l * LIMB_DIGITS - 1 - prec); + inexact = (digit1 | digit0) != 0; + + add_one = 0; + switch(rnd_mode) { + case BF_RNDZ: + break; + case BF_RNDN: + if (digit1 == 5) { + if (digit0) { + add_one = 1; + } else { + /* round to even */ + add_one = + get_digit(r->tab, l, l * LIMB_DIGITS - 1 - (prec - 1)) & 1; + } + } else if (digit1 > 5) { + add_one = 1; + } + break; + case BF_RNDD: + case BF_RNDU: + if (r->sign == (rnd_mode == BF_RNDD)) + add_one = inexact; + break; + case BF_RNDNA: + case BF_RNDF: + add_one = (digit1 >= 5); + break; + case BF_RNDA: + add_one = inexact; + break; + default: + abort(); + } + + if (inexact) + *pret |= BF_ST_INEXACT; + return add_one; +} + +/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is + assumed to have length 'l' (1 <= l <= r->len). prec1 can be + BF_PREC_INF. BF_FLAG_SUBNORMAL is not supported. Cannot fail with + BF_ST_MEM_ERROR. + */ +static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l) +{ + int shift, add_one, rnd_mode, ret; + slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; + + /* XXX: align to IEEE 754 2008 for decimal numbers ? */ + e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + e_min = -e_range + 3; + e_max = e_range; + + if (flags & BF_FLAG_RADPNT_PREC) { + /* 'prec' is the precision after the decimal point */ + if (prec1 != BF_PREC_INF) + prec = r->expn + prec1; + else + prec = prec1; + } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { + /* restrict the precision in case of potentially subnormal + result */ + assert(prec1 != BF_PREC_INF); + prec = prec1 - (e_min - r->expn); + } else { + prec = prec1; + } + + /* round to prec bits */ + rnd_mode = flags & BF_RND_MASK; + ret = 0; + add_one = bfdec_get_rnd_add(&ret, r, l, prec, rnd_mode); + + if (prec <= 0) { + if (add_one) { + bfdec_resize(r, 1); /* cannot fail because r is non zero */ + r->tab[0] = BF_DEC_BASE / 10; + r->expn += 1 - prec; + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + return ret; + } else { + goto underflow; + } + } else if (add_one) { + limb_t carry; + + /* add one starting at digit 'prec - 1' */ + bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); + pos = bit_pos / LIMB_DIGITS; + carry = mp_pow_dec[bit_pos % LIMB_DIGITS]; + carry = mp_add_ui_dec(r->tab + pos, carry, l - pos); + if (carry) { + /* shift right by one digit */ + mp_shr_dec(r->tab + pos, r->tab + pos, l - pos, 1, 1); + r->expn++; + } + } + + /* check underflow */ + if (unlikely(r->expn < e_min)) { + if (flags & BF_FLAG_SUBNORMAL) { + /* if inexact, also set the underflow flag */ + if (ret & BF_ST_INEXACT) + ret |= BF_ST_UNDERFLOW; + } else { + underflow: + bfdec_set_zero(r, r->sign); + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + return ret; + } + } + + /* check overflow */ + if (unlikely(r->expn > e_max)) { + bfdec_set_inf(r, r->sign); + ret |= BF_ST_OVERFLOW | BF_ST_INEXACT; + return ret; + } + + /* keep the bits starting at 'prec - 1' */ + bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); + i = floor_div(bit_pos, LIMB_DIGITS); + if (i >= 0) { + shift = smod(bit_pos, LIMB_DIGITS); + if (shift != 0) { + r->tab[i] = fast_shr_dec(r->tab[i], shift) * + mp_pow_dec[shift]; + } + } else { + i = 0; + } + /* remove trailing zeros */ + while (r->tab[i] == 0) + i++; + if (i > 0) { + l -= i; + memmove(r->tab, r->tab + i, l * sizeof(limb_t)); + } + bfdec_resize(r, l); /* cannot fail */ + return ret; +} + +/* Cannot fail with BF_ST_MEM_ERROR. */ +int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags) +{ + if (r->len == 0) + return 0; + return __bfdec_round(r, prec, flags, r->len); +} + +/* 'r' must be a finite number. Cannot fail with BF_ST_MEM_ERROR. */ +int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags) +{ + limb_t l, v; + int shift, ret; + + // bfdec_print_str("bf_renorm", r); + l = r->len; + while (l > 0 && r->tab[l - 1] == 0) + l--; + if (l == 0) { + /* zero */ + r->expn = BF_EXP_ZERO; + bfdec_resize(r, 0); /* cannot fail */ + ret = 0; + } else { + r->expn -= (r->len - l) * LIMB_DIGITS; + /* shift to have the MSB set to '1' */ + v = r->tab[l - 1]; + shift = clz_dec(v); + if (shift != 0) { + mp_shl_dec(r->tab, r->tab, l, shift, 0); + r->expn -= shift; + } + ret = __bfdec_round(r, prec1, flags, l); + } + // bf_print_str("r_final", r); + return ret; +} + +int bfdec_set_ui(bfdec_t *r, uint64_t v) +{ +#if LIMB_BITS == 32 + if (v >= BF_DEC_BASE * BF_DEC_BASE) { + if (bfdec_resize(r, 3)) + goto fail; + r->tab[0] = v % BF_DEC_BASE; + v /= BF_DEC_BASE; + r->tab[1] = v % BF_DEC_BASE; + r->tab[2] = v / BF_DEC_BASE; + r->expn = 3 * LIMB_DIGITS; + } else +#endif + if (v >= BF_DEC_BASE) { + if (bfdec_resize(r, 2)) + goto fail; + r->tab[0] = v % BF_DEC_BASE; + r->tab[1] = v / BF_DEC_BASE; + r->expn = 2 * LIMB_DIGITS; + } else { + if (bfdec_resize(r, 1)) + goto fail; + r->tab[0] = v; + r->expn = LIMB_DIGITS; + } + r->sign = 0; + return bfdec_normalize_and_round(r, BF_PREC_INF, 0); + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bfdec_set_si(bfdec_t *r, int64_t v) +{ + int ret; + if (v < 0) { + ret = bfdec_set_ui(r, -v); + r->sign = 1; + } else { + ret = bfdec_set_ui(r, v); + } + return ret; +} + +static int bfdec_add_internal(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, bf_flags_t flags, int b_neg) +{ + bf_context_t *s = r->ctx; + int is_sub, cmp_res, a_sign, b_sign, ret; + + a_sign = a->sign; + b_sign = b->sign ^ b_neg; + is_sub = a_sign ^ b_sign; + cmp_res = bfdec_cmpu(a, b); + if (cmp_res < 0) { + const bfdec_t *tmp; + tmp = a; + a = b; + b = tmp; + a_sign = b_sign; /* b_sign is never used later */ + } + /* abs(a) >= abs(b) */ + if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { + /* zero result */ + bfdec_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); + ret = 0; + } else if (a->len == 0 || b->len == 0) { + ret = 0; + if (a->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN) { + /* at least one operand is NaN */ + bfdec_set_nan(r); + ret = 0; + } else if (b->expn == BF_EXP_INF && is_sub) { + /* infinities with different signs */ + bfdec_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bfdec_set_inf(r, a_sign); + } + } else { + /* at least one zero and not subtract */ + if (bfdec_set(r, a)) + return BF_ST_MEM_ERROR; + r->sign = a_sign; + goto renorm; + } + } else { + slimb_t d, a_offset, b_offset, i, r_len; + limb_t carry; + limb_t *b1_tab; + int b_shift; + mp_size_t b1_len; + + d = a->expn - b->expn; + + /* XXX: not efficient in time and memory if the precision is + not infinite */ + r_len = bf_max(a->len, b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); + if (bfdec_resize(r, r_len)) + goto fail; + r->sign = a_sign; + r->expn = a->expn; + + a_offset = r_len - a->len; + for(i = 0; i < a_offset; i++) + r->tab[i] = 0; + for(i = 0; i < a->len; i++) + r->tab[a_offset + i] = a->tab[i]; + + b_shift = d % LIMB_DIGITS; + if (b_shift == 0) { + b1_len = b->len; + b1_tab = (limb_t *)b->tab; + } else { + b1_len = b->len + 1; + b1_tab = bf_malloc(s, sizeof(limb_t) * b1_len); + if (!b1_tab) + goto fail; + b1_tab[0] = mp_shr_dec(b1_tab + 1, b->tab, b->len, b_shift, 0) * + mp_pow_dec[LIMB_DIGITS - b_shift]; + } + b_offset = r_len - (b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); + + if (is_sub) { + carry = mp_sub_dec(r->tab + b_offset, r->tab + b_offset, + b1_tab, b1_len, 0); + if (carry != 0) { + carry = mp_sub_ui_dec(r->tab + b_offset + b1_len, carry, + r_len - (b_offset + b1_len)); + assert(carry == 0); + } + } else { + carry = mp_add_dec(r->tab + b_offset, r->tab + b_offset, + b1_tab, b1_len, 0); + if (carry != 0) { + carry = mp_add_ui_dec(r->tab + b_offset + b1_len, carry, + r_len - (b_offset + b1_len)); + } + if (carry != 0) { + if (bfdec_resize(r, r_len + 1)) { + if (b_shift != 0) + bf_free(s, b1_tab); + goto fail; + } + r->tab[r_len] = 1; + r->expn += LIMB_DIGITS; + } + } + if (b_shift != 0) + bf_free(s, b1_tab); + renorm: + ret = bfdec_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +static int __bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bfdec_add_internal(r, a, b, prec, flags, 0); +} + +static int __bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bfdec_add_internal(r, a, b, prec, flags, 1); +} + +int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, + (bf_op2_func_t *)__bfdec_add); +} + +int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, + (bf_op2_func_t *)__bfdec_sub); +} + +int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + int ret, r_sign; + + if (a->len < b->len) { + const bfdec_t *tmp = a; + a = b; + b = tmp; + } + r_sign = a->sign ^ b->sign; + /* here b->len <= a->len */ + if (b->len == 0) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + ret = 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { + if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || + (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { + bfdec_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bfdec_set_inf(r, r_sign); + ret = 0; + } + } else { + bfdec_set_zero(r, r_sign); + ret = 0; + } + } else { + bfdec_t tmp, *r1 = NULL; + limb_t a_len, b_len; + limb_t *a_tab, *b_tab; + + a_len = a->len; + b_len = b->len; + a_tab = a->tab; + b_tab = b->tab; + + if (r == a || r == b) { + bfdec_init(r->ctx, &tmp); + r1 = r; + r = &tmp; + } + if (bfdec_resize(r, a_len + b_len)) { + bfdec_set_nan(r); + ret = BF_ST_MEM_ERROR; + goto done; + } + mp_mul_basecase_dec(r->tab, a_tab, a_len, b_tab, b_len); + r->sign = r_sign; + r->expn = a->expn + b->expn; + ret = bfdec_normalize_and_round(r, prec, flags); + done: + if (r == &tmp) + bfdec_move(r1, &tmp); + } + return ret; +} + +int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bfdec_t b; + int ret; + bfdec_init(r->ctx, &b); + ret = bfdec_set_si(&b, b1); + ret |= bfdec_mul(r, a, &b, prec, flags); + bfdec_delete(&b); + return ret; +} + +int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bfdec_t b; + int ret; + + bfdec_init(r->ctx, &b); + ret = bfdec_set_si(&b, b1); + ret |= bfdec_add(r, a, &b, prec, flags); + bfdec_delete(&b); + return ret; +} + +static int __bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, + limb_t prec, bf_flags_t flags) +{ + int ret, r_sign; + limb_t n, nb, precl; + + r_sign = a->sign ^ b->sign; + if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else if (a->expn == BF_EXP_INF) { + bfdec_set_inf(r, r_sign); + return 0; + } else { + bfdec_set_zero(r, r_sign); + return 0; + } + } else if (a->expn == BF_EXP_ZERO) { + if (b->expn == BF_EXP_ZERO) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bfdec_set_zero(r, r_sign); + return 0; + } + } else if (b->expn == BF_EXP_ZERO) { + bfdec_set_inf(r, r_sign); + return BF_ST_DIVIDE_ZERO; + } + + nb = b->len; + if (prec == BF_PREC_INF) { + /* infinite precision: return BF_ST_INVALID_OP if not an exact + result */ + /* XXX: check */ + precl = nb + 1; + } else if (flags & BF_FLAG_RADPNT_PREC) { + /* number of digits after the decimal point */ + /* XXX: check (2 extra digits for rounding + 2 digits) */ + precl = (bf_max(a->expn - b->expn, 0) + 2 + + prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; + } else { + /* number of limbs of the quotient (2 extra digits for rounding) */ + precl = (prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; + } + n = bf_max(a->len, precl); + + { + limb_t *taba, na, i; + slimb_t d; + + na = n + nb; + taba = bf_malloc(r->ctx, (na + 1) * sizeof(limb_t)); + if (!taba) + goto fail; + d = na - a->len; + memset(taba, 0, d * sizeof(limb_t)); + memcpy(taba + d, a->tab, a->len * sizeof(limb_t)); + if (bfdec_resize(r, n + 1)) + goto fail1; + if (mp_div_dec(r->ctx, r->tab, taba, na, b->tab, nb)) { + fail1: + bf_free(r->ctx, taba); + goto fail; + } + /* see if non zero remainder */ + for(i = 0; i < nb; i++) { + if (taba[i] != 0) + break; + } + bf_free(r->ctx, taba); + if (i != nb) { + if (prec == BF_PREC_INF) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else { + r->tab[0] |= 1; + } + } + r->expn = a->expn - b->expn + LIMB_DIGITS; + r->sign = r_sign; + ret = bfdec_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, + (bf_op2_func_t *)__bfdec_div); +} + +/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the + integer defined as floor(a/b) and r = a - q * b. */ +static void bfdec_tdivremu(bf_context_t *s, bfdec_t *q, bfdec_t *r, + const bfdec_t *a, const bfdec_t *b) +{ + if (bfdec_cmpu(a, b) < 0) { + bfdec_set_ui(q, 0); + bfdec_set(r, a); + } else { + bfdec_div(q, a, b, 0, BF_RNDZ | BF_FLAG_RADPNT_PREC); + bfdec_mul(r, q, b, BF_PREC_INF, BF_RNDZ); + bfdec_sub(r, a, r, BF_PREC_INF, BF_RNDZ); + } +} + +/* division and remainder. + + rnd_mode is the rounding mode for the quotient. The additional + rounding mode BF_RND_EUCLIDIAN is supported. + + 'q' is an integer. 'r' is rounded with prec and flags (prec can be + BF_PREC_INF). +*/ +int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode) +{ + bf_context_t *s = q->ctx; + bfdec_t a1_s, *a1 = &a1_s; + bfdec_t b1_s, *b1 = &b1_s; + bfdec_t r1_s, *r1 = &r1_s; + int q_sign, res; + BOOL is_ceil, is_rndn; + + assert(q != a && q != b); + assert(r != a && r != b); + assert(q != r); + + if (a->len == 0 || b->len == 0) { + bfdec_set_zero(q, 0); + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bfdec_set(r, a); + return bfdec_round(r, prec, flags); + } + } + + q_sign = a->sign ^ b->sign; + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); + switch(rnd_mode) { + default: + case BF_RNDZ: + case BF_RNDN: + case BF_RNDNA: + is_ceil = FALSE; + break; + case BF_RNDD: + is_ceil = q_sign; + break; + case BF_RNDU: + is_ceil = q_sign ^ 1; + break; + case BF_RNDA: + is_ceil = TRUE; + break; + case BF_DIVREM_EUCLIDIAN: + is_ceil = a->sign; + break; + } + + a1->expn = a->expn; + a1->tab = a->tab; + a1->len = a->len; + a1->sign = 0; + + b1->expn = b->expn; + b1->tab = b->tab; + b1->len = b->len; + b1->sign = 0; + + // bfdec_print_str("a1", a1); + // bfdec_print_str("b1", b1); + /* XXX: could improve to avoid having a large 'q' */ + bfdec_tdivremu(s, q, r, a1, b1); + if (bfdec_is_nan(q) || bfdec_is_nan(r)) + goto fail; + // bfdec_print_str("q", q); + // bfdec_print_str("r", r); + + if (r->len != 0) { + if (is_rndn) { + bfdec_init(s, r1); + if (bfdec_set(r1, r)) + goto fail; + if (bfdec_mul_si(r1, r1, 2, BF_PREC_INF, BF_RNDZ)) { + bfdec_delete(r1); + goto fail; + } + res = bfdec_cmpu(r1, b); + bfdec_delete(r1); + if (res > 0 || + (res == 0 && + (rnd_mode == BF_RNDNA || + (get_digit(q->tab, q->len, q->len * LIMB_DIGITS - q->expn) & 1) != 0))) { + goto do_sub_r; + } + } else if (is_ceil) { + do_sub_r: + res = bfdec_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ); + res |= bfdec_sub(r, r, b1, BF_PREC_INF, BF_RNDZ); + if (res & BF_ST_MEM_ERROR) + goto fail; + } + } + + r->sign ^= a->sign; + q->sign = q_sign; + return bfdec_round(r, prec, flags); + fail: + bfdec_set_nan(q); + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode) +{ + bfdec_t q_s, *q = &q_s; + int ret; + + bfdec_init(r->ctx, q); + ret = bfdec_divrem(q, r, a, b, prec, flags, rnd_mode); + bfdec_delete(q); + return ret; +} + +/* convert to integer (infinite precision) */ +int bfdec_rint(bfdec_t *r, int rnd_mode) +{ + return bfdec_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); +} + +int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = a->ctx; + int ret, k; + limb_t *a1, v; + slimb_t n, n1, prec1; + limb_t res; + + assert(r != a); + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + } else if (a->expn == BF_EXP_INF && a->sign) { + goto invalid_op; + } else { + bfdec_set(r, a); + } + ret = 0; + } else if (a->sign || prec == BF_PREC_INF) { + invalid_op: + bfdec_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + if (flags & BF_FLAG_RADPNT_PREC) { + prec1 = bf_max(floor_div(a->expn + 1, 2) + prec, 1); + } else { + prec1 = prec; + } + /* convert the mantissa to an integer with at least 2 * + prec + 4 digits */ + n = (2 * (prec1 + 2) + 2 * LIMB_DIGITS - 1) / (2 * LIMB_DIGITS); + if (bfdec_resize(r, n)) + goto fail; + a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); + if (!a1) + goto fail; + n1 = bf_min(2 * n, a->len); + memset(a1, 0, (2 * n - n1) * sizeof(limb_t)); + memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t)); + if (a->expn & 1) { + res = mp_shr_dec(a1, a1, 2 * n, 1, 0); + } else { + res = 0; + } + /* normalize so that a1 >= B^(2*n)/4. Not need for n = 1 + because mp_sqrtrem2_dec already does it */ + k = 0; + if (n > 1) { + v = a1[2 * n - 1]; + while (v < BF_DEC_BASE / 4) { + k++; + v *= 4; + } + if (k != 0) + mp_mul1_dec(a1, a1, 2 * n, 1 << (2 * k), 0); + } + if (mp_sqrtrem_dec(s, r->tab, a1, n)) { + bf_free(s, a1); + goto fail; + } + if (k != 0) + mp_div1_dec(r->tab, r->tab, n, 1 << k, 0); + if (!res) { + res = mp_scan_nz(a1, n + 1); + } + bf_free(s, a1); + if (!res) { + res = mp_scan_nz(a->tab, a->len - n1); + } + if (res != 0) + r->tab[0] |= 1; + r->sign = 0; + r->expn = (a->expn + 1) >> 1; + ret = bfdec_round(r, prec, flags); + } + return ret; + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_OVERFLOW if there + is an overflow and 0 otherwise. No memory error is possible. */ +int bfdec_get_int32(int *pres, const bfdec_t *a) +{ + uint32_t v; + int ret; + if (a->expn >= BF_EXP_INF) { + ret = 0; + if (a->expn == BF_EXP_INF) { + v = (uint32_t)INT32_MAX + a->sign; + /* XXX: return overflow ? */ + } else { + v = INT32_MAX; + } + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->expn <= 9) { + v = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); + if (a->sign) + v = -v; + ret = 0; + } else if (a->expn == 10) { + uint64_t v1; + uint32_t v_max; +#if LIMB_BITS == 64 + v1 = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); +#else + v1 = (uint64_t)a->tab[a->len - 1] * 10 + + get_digit(a->tab, a->len, (a->len - 1) * LIMB_DIGITS - 1); +#endif + v_max = (uint32_t)INT32_MAX + a->sign; + if (v1 > v_max) { + v = v_max; + ret = BF_ST_OVERFLOW; + } else { + v = v1; + if (a->sign) + v = -v; + ret = 0; + } + } else { + v = (uint32_t)INT32_MAX + a->sign; + ret = BF_ST_OVERFLOW; + } + *pres = v; + return ret; +} + +/* power to an integer with infinite precision */ +int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b) +{ + int ret, n_bits, i; + + assert(r != a); + if (b == 0) + return bfdec_set_ui(r, 1); + ret = bfdec_set(r, a); + n_bits = LIMB_BITS - clz(b); + for(i = n_bits - 2; i >= 0; i--) { + ret |= bfdec_mul(r, r, r, BF_PREC_INF, BF_RNDZ); + if ((b >> i) & 1) + ret |= bfdec_mul(r, r, a, BF_PREC_INF, BF_RNDZ); + } + return ret; +} + +char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags) +{ + return bf_ftoa_internal(plen, (const bf_t *)a, 10, prec, flags, TRUE); +} + +int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, + limb_t prec, bf_flags_t flags) +{ + slimb_t dummy_exp; + return bf_atof_internal((bf_t *)r, &dummy_exp, str, pnext, 10, prec, + flags, TRUE); +} + +#endif /* USE_BF_DEC */ + +#ifdef USE_FFT_MUL +/***************************************************************/ +/* Integer multiplication with FFT */ + +/* or LIMB_BITS at bit position 'pos' in tab */ +static inline void put_bits(limb_t *tab, limb_t len, slimb_t pos, limb_t val) +{ + limb_t i; + int p; + + i = pos >> LIMB_LOG2_BITS; + p = pos & (LIMB_BITS - 1); + if (i < len) + tab[i] |= val << p; + if (p != 0) { + i++; + if (i < len) { + tab[i] |= val >> (LIMB_BITS - p); + } + } +} + +#if defined(__AVX2__) + +typedef double NTTLimb; + +/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */ +#define NTT_MOD_LOG2_MIN 50 +#define NTT_MOD_LOG2_MAX 51 +#define NB_MODS 5 +#define NTT_PROOT_2EXP 39 +static const int ntt_int_bits[NB_MODS] = { 254, 203, 152, 101, 50, }; + +static const limb_t ntt_mods[NB_MODS] = { 0x00073a8000000001, 0x0007858000000001, 0x0007a38000000001, 0x0007a68000000001, 0x0007fd8000000001, +}; + +static const limb_t ntt_proot[2][NB_MODS] = { + { 0x00056198d44332c8, 0x0002eb5d640aad39, 0x00047e31eaa35fd0, 0x0005271ac118a150, 0x00075e0ce8442bd5, }, + { 0x000461169761bcc5, 0x0002dac3cb2da688, 0x0004abc97751e3bf, 0x000656778fc8c485, 0x0000dc6469c269fa, }, +}; + +static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { + 0x00020e4da740da8e, 0x0004c3dc09c09c1d, 0x000063bd097b4271, 0x000799d8f18f18fd, + 0x0005384222222264, 0x000572b07c1f07fe, 0x00035cd08888889a, + 0x00066015555557e3, 0x000725960b60b623, + 0x0002fc1fa1d6ce12, +}; + +#else + +typedef limb_t NTTLimb; + +#if LIMB_BITS == 64 + +#define NTT_MOD_LOG2_MIN 61 +#define NTT_MOD_LOG2_MAX 62 +#define NB_MODS 5 +#define NTT_PROOT_2EXP 51 +static const int ntt_int_bits[NB_MODS] = { 307, 246, 185, 123, 61, }; + +static const limb_t ntt_mods[NB_MODS] = { 0x28d8000000000001, 0x2a88000000000001, 0x2ed8000000000001, 0x3508000000000001, 0x3aa8000000000001, +}; + +static const limb_t ntt_proot[2][NB_MODS] = { + { 0x1b8ea61034a2bea7, 0x21a9762de58206fb, 0x02ca782f0756a8ea, 0x278384537a3e50a1, 0x106e13fee74ce0ab, }, + { 0x233513af133e13b8, 0x1d13140d1c6f75f1, 0x12cde57f97e3eeda, 0x0d6149e23cbe654f, 0x36cd204f522a1379, }, +}; + +static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { + 0x08a9ed097b425eea, 0x18a44aaaaaaaaab3, 0x2493f57f57f57f5d, 0x126b8d0649a7f8d4, + 0x09d80ed7303b5ccc, 0x25b8bcf3cf3cf3d5, 0x2ce6ce63398ce638, + 0x0e31fad40a57eb59, 0x02a3529fd4a7f52f, + 0x3a5493e93e93e94a, +}; + +#elif LIMB_BITS == 32 + +/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */ +#define NTT_MOD_LOG2_MIN 29 +#define NTT_MOD_LOG2_MAX 30 +#define NB_MODS 5 +#define NTT_PROOT_2EXP 20 +static const int ntt_int_bits[NB_MODS] = { 148, 119, 89, 59, 29, }; + +static const limb_t ntt_mods[NB_MODS] = { 0x0000000032b00001, 0x0000000033700001, 0x0000000036d00001, 0x0000000037300001, 0x000000003e500001, +}; + +static const limb_t ntt_proot[2][NB_MODS] = { + { 0x0000000032525f31, 0x0000000005eb3b37, 0x00000000246eda9f, 0x0000000035f25901, 0x00000000022f5768, }, + { 0x00000000051eba1a, 0x00000000107be10e, 0x000000001cd574e0, 0x00000000053806e6, 0x000000002cd6bf98, }, +}; + +static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { + 0x000000000449559a, 0x000000001eba6ca9, 0x000000002ec18e46, 0x000000000860160b, + 0x000000000d321307, 0x000000000bf51120, 0x000000000f662938, + 0x000000000932ab3e, 0x000000002f40eef8, + 0x000000002e760905, +}; + +#endif /* LIMB_BITS */ + +#endif /* !AVX2 */ + +#if defined(__AVX2__) +#define NTT_TRIG_K_MAX 18 +#else +#define NTT_TRIG_K_MAX 19 +#endif + +typedef struct BFNTTState { + bf_context_t *ctx; + + /* used for mul_mod_fast() */ + limb_t ntt_mods_div[NB_MODS]; + + limb_t ntt_proot_pow[NB_MODS][2][NTT_PROOT_2EXP + 1]; + limb_t ntt_proot_pow_inv[NB_MODS][2][NTT_PROOT_2EXP + 1]; + NTTLimb *ntt_trig[NB_MODS][2][NTT_TRIG_K_MAX + 1]; + /* 1/2^n mod m */ + limb_t ntt_len_inv[NB_MODS][NTT_PROOT_2EXP + 1][2]; +#if defined(__AVX2__) + __m256d ntt_mods_cr_vec[NB_MODS * (NB_MODS - 1) / 2]; + __m256d ntt_mods_vec[NB_MODS]; + __m256d ntt_mods_inv_vec[NB_MODS]; +#else + limb_t ntt_mods_cr_inv[NB_MODS * (NB_MODS - 1) / 2]; +#endif +} BFNTTState; + +static NTTLimb *get_trig(BFNTTState *s, int k, int inverse, int m_idx); + +/* add modulo with up to (LIMB_BITS-1) bit modulo */ +static inline limb_t add_mod(limb_t a, limb_t b, limb_t m) +{ + limb_t r; + r = a + b; + if (r >= m) + r -= m; + return r; +} + +/* sub modulo with up to LIMB_BITS bit modulo */ +static inline limb_t sub_mod(limb_t a, limb_t b, limb_t m) +{ + limb_t r; + r = a - b; + if (r > a) + r += m; + return r; +} + +/* return (r0+r1*B) mod m + precondition: 0 <= r0+r1*B < 2^(64+NTT_MOD_LOG2_MIN) +*/ +static inline limb_t mod_fast(dlimb_t r, + limb_t m, limb_t m_inv) +{ + limb_t a1, q, t0, r1, r0; + + a1 = r >> NTT_MOD_LOG2_MIN; + + q = ((dlimb_t)a1 * m_inv) >> LIMB_BITS; + r = r - (dlimb_t)q * m - m * 2; + r1 = r >> LIMB_BITS; + t0 = (slimb_t)r1 >> 1; + r += m & t0; + r0 = r; + r1 = r >> LIMB_BITS; + r0 += m & r1; + return r0; +} + +/* faster version using precomputed modulo inverse. + precondition: 0 <= a * b < 2^(64+NTT_MOD_LOG2_MIN) */ +static inline limb_t mul_mod_fast(limb_t a, limb_t b, + limb_t m, limb_t m_inv) +{ + dlimb_t r; + r = (dlimb_t)a * (dlimb_t)b; + return mod_fast(r, m, m_inv); +} + +static inline limb_t init_mul_mod_fast(limb_t m) +{ + dlimb_t t; + assert(m < (limb_t)1 << NTT_MOD_LOG2_MAX); + assert(m >= (limb_t)1 << NTT_MOD_LOG2_MIN); + t = (dlimb_t)1 << (LIMB_BITS + NTT_MOD_LOG2_MIN); + return t / m; +} + +/* Faster version used when the multiplier is constant. 0 <= a < 2^64, + 0 <= b < m. */ +static inline limb_t mul_mod_fast2(limb_t a, limb_t b, + limb_t m, limb_t b_inv) +{ + limb_t r, q; + + q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS; + r = a * b - q * m; + if (r >= m) + r -= m; + return r; +} + +/* Faster version used when the multiplier is constant. 0 <= a < 2^64, + 0 <= b < m. Let r = a * b mod m. The return value is 'r' or 'r + + m'. */ +static inline limb_t mul_mod_fast3(limb_t a, limb_t b, + limb_t m, limb_t b_inv) +{ + limb_t r, q; + + q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS; + r = a * b - q * m; + return r; +} + +static inline limb_t init_mul_mod_fast2(limb_t b, limb_t m) +{ + return ((dlimb_t)b << LIMB_BITS) / m; +} + +#ifdef __AVX2__ + +static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m) +{ + slimb_t v; + v = a; + if (v < 0) + v += m; + if (v >= m) + v -= m; + return v; +} + +static inline NTTLimb int_to_ntt_limb(limb_t a, limb_t m) +{ + return (slimb_t)a; +} + +static inline NTTLimb int_to_ntt_limb2(limb_t a, limb_t m) +{ + if (a >= (m / 2)) + a -= m; + return (slimb_t)a; +} + +/* return r + m if r < 0 otherwise r. */ +static inline __m256d ntt_mod1(__m256d r, __m256d m) +{ + return _mm256_blendv_pd(r, r + m, r); +} + +/* input: abs(r) < 2 * m. Output: abs(r) < m */ +static inline __m256d ntt_mod(__m256d r, __m256d mf, __m256d m2f) +{ + return _mm256_blendv_pd(r, r + m2f, r) - mf; +} + +/* input: abs(a*b) < 2 * m^2, output: abs(r) < m */ +static inline __m256d ntt_mul_mod(__m256d a, __m256d b, __m256d mf, + __m256d m_inv) +{ + __m256d r, q, ab1, ab0, qm0, qm1; + ab1 = a * b; + q = _mm256_round_pd(ab1 * m_inv, 0); /* round to nearest */ + qm1 = q * mf; + qm0 = _mm256_fmsub_pd(q, mf, qm1); /* low part */ + ab0 = _mm256_fmsub_pd(a, b, ab1); /* low part */ + r = (ab1 - qm1) + (ab0 - qm0); + return r; +} + +static void *bf_aligned_malloc(bf_context_t *s, size_t size, size_t align) +{ + void *ptr; + void **ptr1; + ptr = bf_malloc(s, size + sizeof(void *) + align - 1); + if (!ptr) + return NULL; + ptr1 = (void **)(((uintptr_t)ptr + sizeof(void *) + align - 1) & + ~(align - 1)); + ptr1[-1] = ptr; + return ptr1; +} + +static void bf_aligned_free(bf_context_t *s, void *ptr) +{ + if (!ptr) + return; + bf_free(s, ((void **)ptr)[-1]); +} + +static void *ntt_malloc(BFNTTState *s, size_t size) +{ + return bf_aligned_malloc(s->ctx, size, 64); +} + +static void ntt_free(BFNTTState *s, void *ptr) +{ + bf_aligned_free(s->ctx, ptr); +} + +static no_inline int ntt_fft(BFNTTState *s, + NTTLimb *out_buf, NTTLimb *in_buf, + NTTLimb *tmp_buf, int fft_len_log2, + int inverse, int m_idx) +{ + limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j; + NTTLimb *tab_in, *tab_out, *tmp, *trig; + __m256d m_inv, mf, m2f, c, a0, a1, b0, b1; + limb_t m; + int l; + + m = ntt_mods[m_idx]; + + m_inv = _mm256_set1_pd(1.0 / (double)m); + mf = _mm256_set1_pd(m); + m2f = _mm256_set1_pd(m * 2); + + n = (limb_t)1 << fft_len_log2; + assert(n >= 8); + stride_in = n / 2; + + tab_in = in_buf; + tab_out = tmp_buf; + trig = get_trig(s, fft_len_log2, inverse, m_idx); + if (!trig) + return -1; + p = 0; + for(k = 0; k < stride_in; k += 4) { + a0 = _mm256_load_pd(&tab_in[k]); + a1 = _mm256_load_pd(&tab_in[k + stride_in]); + c = _mm256_load_pd(trig); + trig += 4; + b0 = ntt_mod(a0 + a1, mf, m2f); + b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); + a0 = _mm256_permute2f128_pd(b0, b1, 0x20); + a1 = _mm256_permute2f128_pd(b0, b1, 0x31); + a0 = _mm256_permute4x64_pd(a0, 0xd8); + a1 = _mm256_permute4x64_pd(a1, 0xd8); + _mm256_store_pd(&tab_out[p], a0); + _mm256_store_pd(&tab_out[p + 4], a1); + p += 2 * 4; + } + tmp = tab_in; + tab_in = tab_out; + tab_out = tmp; + + trig = get_trig(s, fft_len_log2 - 1, inverse, m_idx); + if (!trig) + return -1; + p = 0; + for(k = 0; k < stride_in; k += 4) { + a0 = _mm256_load_pd(&tab_in[k]); + a1 = _mm256_load_pd(&tab_in[k + stride_in]); + c = _mm256_setr_pd(trig[0], trig[0], trig[1], trig[1]); + trig += 2; + b0 = ntt_mod(a0 + a1, mf, m2f); + b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); + a0 = _mm256_permute2f128_pd(b0, b1, 0x20); + a1 = _mm256_permute2f128_pd(b0, b1, 0x31); + _mm256_store_pd(&tab_out[p], a0); + _mm256_store_pd(&tab_out[p + 4], a1); + p += 2 * 4; + } + tmp = tab_in; + tab_in = tab_out; + tab_out = tmp; + + nb_blocks = n / 4; + fft_per_block = 4; + + l = fft_len_log2 - 2; + while (nb_blocks != 2) { + nb_blocks >>= 1; + p = 0; + k = 0; + trig = get_trig(s, l, inverse, m_idx); + if (!trig) + return -1; + for(i = 0; i < nb_blocks; i++) { + c = _mm256_set1_pd(trig[0]); + trig++; + for(j = 0; j < fft_per_block; j += 4) { + a0 = _mm256_load_pd(&tab_in[k + j]); + a1 = _mm256_load_pd(&tab_in[k + j + stride_in]); + b0 = ntt_mod(a0 + a1, mf, m2f); + b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); + _mm256_store_pd(&tab_out[p + j], b0); + _mm256_store_pd(&tab_out[p + j + fft_per_block], b1); + } + k += fft_per_block; + p += 2 * fft_per_block; + } + fft_per_block <<= 1; + l--; + tmp = tab_in; + tab_in = tab_out; + tab_out = tmp; + } + + tab_out = out_buf; + for(k = 0; k < stride_in; k += 4) { + a0 = _mm256_load_pd(&tab_in[k]); + a1 = _mm256_load_pd(&tab_in[k + stride_in]); + b0 = ntt_mod(a0 + a1, mf, m2f); + b1 = ntt_mod(a0 - a1, mf, m2f); + _mm256_store_pd(&tab_out[k], b0); + _mm256_store_pd(&tab_out[k + stride_in], b1); + } + return 0; +} + +static void ntt_vec_mul(BFNTTState *s, + NTTLimb *tab1, NTTLimb *tab2, limb_t fft_len_log2, + int k_tot, int m_idx) +{ + limb_t i, c_inv, n, m; + __m256d m_inv, mf, a, b, c; + + m = ntt_mods[m_idx]; + c_inv = s->ntt_len_inv[m_idx][k_tot][0]; + m_inv = _mm256_set1_pd(1.0 / (double)m); + mf = _mm256_set1_pd(m); + c = _mm256_set1_pd(int_to_ntt_limb(c_inv, m)); + n = (limb_t)1 << fft_len_log2; + for(i = 0; i < n; i += 4) { + a = _mm256_load_pd(&tab1[i]); + b = _mm256_load_pd(&tab2[i]); + a = ntt_mul_mod(a, b, mf, m_inv); + a = ntt_mul_mod(a, c, mf, m_inv); + _mm256_store_pd(&tab1[i], a); + } +} + +static no_inline void mul_trig(NTTLimb *buf, + limb_t n, limb_t c1, limb_t m, limb_t m_inv1) +{ + limb_t i, c2, c3, c4; + __m256d c, c_mul, a0, mf, m_inv; + assert(n >= 2); + + mf = _mm256_set1_pd(m); + m_inv = _mm256_set1_pd(1.0 / (double)m); + + c2 = mul_mod_fast(c1, c1, m, m_inv1); + c3 = mul_mod_fast(c2, c1, m, m_inv1); + c4 = mul_mod_fast(c2, c2, m, m_inv1); + c = _mm256_setr_pd(1, int_to_ntt_limb(c1, m), + int_to_ntt_limb(c2, m), int_to_ntt_limb(c3, m)); + c_mul = _mm256_set1_pd(int_to_ntt_limb(c4, m)); + for(i = 0; i < n; i += 4) { + a0 = _mm256_load_pd(&buf[i]); + a0 = ntt_mul_mod(a0, c, mf, m_inv); + _mm256_store_pd(&buf[i], a0); + c = ntt_mul_mod(c, c_mul, mf, m_inv); + } +} + +#else + +static void *ntt_malloc(BFNTTState *s, size_t size) +{ + return bf_malloc(s->ctx, size); +} + +static void ntt_free(BFNTTState *s, void *ptr) +{ + bf_free(s->ctx, ptr); +} + +static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m) +{ + if (a >= m) + a -= m; + return a; +} + +static inline NTTLimb int_to_ntt_limb(slimb_t a, limb_t m) +{ + return a; +} + +static no_inline int ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf, + NTTLimb *tmp_buf, int fft_len_log2, + int inverse, int m_idx) +{ + limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j, m, m2; + NTTLimb *tab_in, *tab_out, *tmp, a0, a1, b0, b1, c, *trig, c_inv; + int l; + + m = ntt_mods[m_idx]; + m2 = 2 * m; + n = (limb_t)1 << fft_len_log2; + nb_blocks = n; + fft_per_block = 1; + stride_in = n / 2; + tab_in = in_buf; + tab_out = tmp_buf; + l = fft_len_log2; + while (nb_blocks != 2) { + nb_blocks >>= 1; + p = 0; + k = 0; + trig = get_trig(s, l, inverse, m_idx); + if (!trig) + return -1; + for(i = 0; i < nb_blocks; i++) { + c = trig[0]; + c_inv = trig[1]; + trig += 2; + for(j = 0; j < fft_per_block; j++) { + a0 = tab_in[k + j]; + a1 = tab_in[k + j + stride_in]; + b0 = add_mod(a0, a1, m2); + b1 = a0 - a1 + m2; + b1 = mul_mod_fast3(b1, c, m, c_inv); + tab_out[p + j] = b0; + tab_out[p + j + fft_per_block] = b1; + } + k += fft_per_block; + p += 2 * fft_per_block; + } + fft_per_block <<= 1; + l--; + tmp = tab_in; + tab_in = tab_out; + tab_out = tmp; + } + /* no twiddle in last step */ + tab_out = out_buf; + for(k = 0; k < stride_in; k++) { + a0 = tab_in[k]; + a1 = tab_in[k + stride_in]; + b0 = add_mod(a0, a1, m2); + b1 = sub_mod(a0, a1, m2); + tab_out[k] = b0; + tab_out[k + stride_in] = b1; + } + return 0; +} + +static void ntt_vec_mul(BFNTTState *s, + NTTLimb *tab1, NTTLimb *tab2, int fft_len_log2, + int k_tot, int m_idx) +{ + limb_t i, norm, norm_inv, a, n, m, m_inv; + + m = ntt_mods[m_idx]; + m_inv = s->ntt_mods_div[m_idx]; + norm = s->ntt_len_inv[m_idx][k_tot][0]; + norm_inv = s->ntt_len_inv[m_idx][k_tot][1]; + n = (limb_t)1 << fft_len_log2; + for(i = 0; i < n; i++) { + a = tab1[i]; + /* need to reduce the range so that the product is < + 2^(LIMB_BITS+NTT_MOD_LOG2_MIN) */ + if (a >= m) + a -= m; + a = mul_mod_fast(a, tab2[i], m, m_inv); + a = mul_mod_fast3(a, norm, m, norm_inv); + tab1[i] = a; + } +} + +static no_inline void mul_trig(NTTLimb *buf, + limb_t n, limb_t c_mul, limb_t m, limb_t m_inv) +{ + limb_t i, c0, c_mul_inv; + + c0 = 1; + c_mul_inv = init_mul_mod_fast2(c_mul, m); + for(i = 0; i < n; i++) { + buf[i] = mul_mod_fast(buf[i], c0, m, m_inv); + c0 = mul_mod_fast2(c0, c_mul, m, c_mul_inv); + } +} + +#endif /* !AVX2 */ + +static no_inline NTTLimb *get_trig(BFNTTState *s, + int k, int inverse, int m_idx) +{ + NTTLimb *tab; + limb_t i, n2, c, c_mul, m, c_mul_inv; + + if (k > NTT_TRIG_K_MAX) + return NULL; + + tab = s->ntt_trig[m_idx][inverse][k]; + if (tab) + return tab; + n2 = (limb_t)1 << (k - 1); + m = ntt_mods[m_idx]; +#ifdef __AVX2__ + tab = ntt_malloc(s, sizeof(NTTLimb) * n2); +#else + tab = ntt_malloc(s, sizeof(NTTLimb) * n2 * 2); +#endif + if (!tab) + return NULL; + c = 1; + c_mul = s->ntt_proot_pow[m_idx][inverse][k]; + c_mul_inv = s->ntt_proot_pow_inv[m_idx][inverse][k]; + for(i = 0; i < n2; i++) { +#ifdef __AVX2__ + tab[i] = int_to_ntt_limb2(c, m); +#else + tab[2 * i] = int_to_ntt_limb(c, m); + tab[2 * i + 1] = init_mul_mod_fast2(c, m); +#endif + c = mul_mod_fast2(c, c_mul, m, c_mul_inv); + } + s->ntt_trig[m_idx][inverse][k] = tab; + return tab; +} + +void fft_clear_cache(bf_context_t *s1) +{ + int m_idx, inverse, k; + BFNTTState *s = s1->ntt_state; + if (s) { + for(m_idx = 0; m_idx < NB_MODS; m_idx++) { + for(inverse = 0; inverse < 2; inverse++) { + for(k = 0; k < NTT_TRIG_K_MAX + 1; k++) { + if (s->ntt_trig[m_idx][inverse][k]) { + ntt_free(s, s->ntt_trig[m_idx][inverse][k]); + s->ntt_trig[m_idx][inverse][k] = NULL; + } + } + } + } +#if defined(__AVX2__) + bf_aligned_free(s1, s); +#else + bf_free(s1, s); +#endif + s1->ntt_state = NULL; + } +} + +#define STRIP_LEN 16 + +/* dst = buf1, src = buf2 */ +static int ntt_fft_partial(BFNTTState *s, NTTLimb *buf1, + int k1, int k2, limb_t n1, limb_t n2, int inverse, + limb_t m_idx) +{ + limb_t i, j, c_mul, c0, m, m_inv, strip_len, l; + NTTLimb *buf2, *buf3; + + buf2 = NULL; + buf3 = ntt_malloc(s, sizeof(NTTLimb) * n1); + if (!buf3) + goto fail; + if (k2 == 0) { + if (ntt_fft(s, buf1, buf1, buf3, k1, inverse, m_idx)) + goto fail; + } else { + strip_len = STRIP_LEN; + buf2 = ntt_malloc(s, sizeof(NTTLimb) * n1 * strip_len); + if (!buf2) + goto fail; + m = ntt_mods[m_idx]; + m_inv = s->ntt_mods_div[m_idx]; + c0 = s->ntt_proot_pow[m_idx][inverse][k1 + k2]; + c_mul = 1; + assert((n2 % strip_len) == 0); + for(j = 0; j < n2; j += strip_len) { + for(i = 0; i < n1; i++) { + for(l = 0; l < strip_len; l++) { + buf2[i + l * n1] = buf1[i * n2 + (j + l)]; + } + } + for(l = 0; l < strip_len; l++) { + if (inverse) + mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv); + if (ntt_fft(s, buf2 + l * n1, buf2 + l * n1, buf3, k1, inverse, m_idx)) + goto fail; + if (!inverse) + mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv); + c_mul = mul_mod_fast(c_mul, c0, m, m_inv); + } + + for(i = 0; i < n1; i++) { + for(l = 0; l < strip_len; l++) { + buf1[i * n2 + (j + l)] = buf2[i + l *n1]; + } + } + } + ntt_free(s, buf2); + } + ntt_free(s, buf3); + return 0; + fail: + ntt_free(s, buf2); + ntt_free(s, buf3); + return -1; +} + + +/* dst = buf1, src = buf2, tmp = buf3 */ +static int ntt_conv(BFNTTState *s, NTTLimb *buf1, NTTLimb *buf2, + int k, int k_tot, limb_t m_idx) +{ + limb_t n1, n2, i; + int k1, k2; + + if (k <= NTT_TRIG_K_MAX) { + k1 = k; + } else { + /* recursive split of the FFT */ + k1 = bf_min(k / 2, NTT_TRIG_K_MAX); + } + k2 = k - k1; + n1 = (limb_t)1 << k1; + n2 = (limb_t)1 << k2; + + if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 0, m_idx)) + return -1; + if (ntt_fft_partial(s, buf2, k1, k2, n1, n2, 0, m_idx)) + return -1; + if (k2 == 0) { + ntt_vec_mul(s, buf1, buf2, k, k_tot, m_idx); + } else { + for(i = 0; i < n1; i++) { + ntt_conv(s, buf1 + i * n2, buf2 + i * n2, k2, k_tot, m_idx); + } + } + if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 1, m_idx)) + return -1; + return 0; +} + + +static no_inline void limb_to_ntt(BFNTTState *s, + NTTLimb *tabr, limb_t fft_len, + const limb_t *taba, limb_t a_len, int dpl, + int first_m_idx, int nb_mods) +{ + slimb_t i, n; + dlimb_t a, b; + int j, shift; + limb_t base_mask1, a0, a1, a2, r, m, m_inv; + + memset(tabr, 0, sizeof(NTTLimb) * fft_len * nb_mods); + shift = dpl & (LIMB_BITS - 1); + if (shift == 0) + base_mask1 = -1; + else + base_mask1 = ((limb_t)1 << shift) - 1; + n = bf_min(fft_len, (a_len * LIMB_BITS + dpl - 1) / dpl); + for(i = 0; i < n; i++) { + a0 = get_bits(taba, a_len, i * dpl); + if (dpl <= LIMB_BITS) { + a0 &= base_mask1; + a = a0; + } else { + a1 = get_bits(taba, a_len, i * dpl + LIMB_BITS); + if (dpl <= (LIMB_BITS + NTT_MOD_LOG2_MIN)) { + a = a0 | ((dlimb_t)(a1 & base_mask1) << LIMB_BITS); + } else { + if (dpl > 2 * LIMB_BITS) { + a2 = get_bits(taba, a_len, i * dpl + LIMB_BITS * 2) & + base_mask1; + } else { + a1 &= base_mask1; + a2 = 0; + } + // printf("a=0x%016lx%016lx%016lx\n", a2, a1, a0); + a = (a0 >> (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | + ((dlimb_t)a1 << (NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)) | + ((dlimb_t)a2 << (LIMB_BITS + NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)); + a0 &= ((limb_t)1 << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) - 1; + } + } + for(j = 0; j < nb_mods; j++) { + m = ntt_mods[first_m_idx + j]; + m_inv = s->ntt_mods_div[first_m_idx + j]; + r = mod_fast(a, m, m_inv); + if (dpl > (LIMB_BITS + NTT_MOD_LOG2_MIN)) { + b = ((dlimb_t)r << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | a0; + r = mod_fast(b, m, m_inv); + } + tabr[i + j * fft_len] = int_to_ntt_limb(r, m); + } + } +} + +#if defined(__AVX2__) + +#define VEC_LEN 4 + +typedef union { + __m256d v; + double d[4]; +} VecUnion; + +static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len, + const NTTLimb *buf, int fft_len_log2, int dpl, + int nb_mods) +{ + const limb_t *mods = ntt_mods + NB_MODS - nb_mods; + const __m256d *mods_cr_vec, *mf, *m_inv; + VecUnion y[NB_MODS]; + limb_t u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r; + slimb_t i, len, pos; + int j, k, l, shift, n_limb1, p; + dlimb_t t; + + j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2; + mods_cr_vec = s->ntt_mods_cr_vec + j; + mf = s->ntt_mods_vec + NB_MODS - nb_mods; + m_inv = s->ntt_mods_inv_vec + NB_MODS - nb_mods; + + shift = dpl & (LIMB_BITS - 1); + if (shift == 0) + base_mask1 = -1; + else + base_mask1 = ((limb_t)1 << shift) - 1; + n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS; + for(j = 0; j < NB_MODS; j++) + carry[j] = 0; + for(j = 0; j < NB_MODS; j++) + u[j] = 0; /* avoid warnings */ + memset(tabr, 0, sizeof(limb_t) * r_len); + fft_len = (limb_t)1 << fft_len_log2; + len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl); + len = (len + VEC_LEN - 1) & ~(VEC_LEN - 1); + i = 0; + while (i < len) { + for(j = 0; j < nb_mods; j++) + y[j].v = *(__m256d *)&buf[i + fft_len * j]; + + /* Chinese remainder to get mixed radix representation */ + l = 0; + for(j = 0; j < nb_mods - 1; j++) { + y[j].v = ntt_mod1(y[j].v, mf[j]); + for(k = j + 1; k < nb_mods; k++) { + y[k].v = ntt_mul_mod(y[k].v - y[j].v, + mods_cr_vec[l], mf[k], m_inv[k]); + l++; + } + } + y[j].v = ntt_mod1(y[j].v, mf[j]); + + for(p = 0; p < VEC_LEN; p++) { + /* back to normal representation */ + u[0] = (int64_t)y[nb_mods - 1].d[p]; + l = 1; + for(j = nb_mods - 2; j >= 1; j--) { + r = (int64_t)y[j].d[p]; + for(k = 0; k < l; k++) { + t = (dlimb_t)u[k] * mods[j] + r; + r = t >> LIMB_BITS; + u[k] = t; + } + u[l] = r; + l++; + } + /* XXX: for nb_mods = 5, l should be 4 */ + + /* last step adds the carry */ + r = (int64_t)y[0].d[p]; + for(k = 0; k < l; k++) { + t = (dlimb_t)u[k] * mods[j] + r + carry[k]; + r = t >> LIMB_BITS; + u[k] = t; + } + u[l] = r + carry[l]; + + /* write the digits */ + pos = i * dpl; + for(j = 0; j < n_limb1; j++) { + put_bits(tabr, r_len, pos, u[j]); + pos += LIMB_BITS; + } + put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1); + /* shift by dpl digits and set the carry */ + if (shift == 0) { + for(j = n_limb1 + 1; j < nb_mods; j++) + carry[j - (n_limb1 + 1)] = u[j]; + } else { + for(j = n_limb1; j < nb_mods - 1; j++) { + carry[j - n_limb1] = (u[j] >> shift) | + (u[j + 1] << (LIMB_BITS - shift)); + } + carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift; + } + i++; + } + } +} +#else +static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len, + const NTTLimb *buf, int fft_len_log2, int dpl, + int nb_mods) +{ + const limb_t *mods = ntt_mods + NB_MODS - nb_mods; + const limb_t *mods_cr, *mods_cr_inv; + limb_t y[NB_MODS], u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r; + slimb_t i, len, pos; + int j, k, l, shift, n_limb1; + dlimb_t t; + + j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2; + mods_cr = ntt_mods_cr + j; + mods_cr_inv = s->ntt_mods_cr_inv + j; + + shift = dpl & (LIMB_BITS - 1); + if (shift == 0) + base_mask1 = -1; + else + base_mask1 = ((limb_t)1 << shift) - 1; + n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS; + for(j = 0; j < NB_MODS; j++) + carry[j] = 0; + for(j = 0; j < NB_MODS; j++) + u[j] = 0; /* avoid warnings */ + memset(tabr, 0, sizeof(limb_t) * r_len); + fft_len = (limb_t)1 << fft_len_log2; + len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl); + for(i = 0; i < len; i++) { + for(j = 0; j < nb_mods; j++) { + y[j] = ntt_limb_to_int(buf[i + fft_len * j], mods[j]); + } + + /* Chinese remainder to get mixed radix representation */ + l = 0; + for(j = 0; j < nb_mods - 1; j++) { + for(k = j + 1; k < nb_mods; k++) { + limb_t m; + m = mods[k]; + /* Note: there is no overflow in the sub_mod() because + the modulos are sorted by increasing order */ + y[k] = mul_mod_fast2(y[k] - y[j] + m, + mods_cr[l], m, mods_cr_inv[l]); + l++; + } + } + + /* back to normal representation */ + u[0] = y[nb_mods - 1]; + l = 1; + for(j = nb_mods - 2; j >= 1; j--) { + r = y[j]; + for(k = 0; k < l; k++) { + t = (dlimb_t)u[k] * mods[j] + r; + r = t >> LIMB_BITS; + u[k] = t; + } + u[l] = r; + l++; + } + + /* last step adds the carry */ + r = y[0]; + for(k = 0; k < l; k++) { + t = (dlimb_t)u[k] * mods[j] + r + carry[k]; + r = t >> LIMB_BITS; + u[k] = t; + } + u[l] = r + carry[l]; + + /* write the digits */ + pos = i * dpl; + for(j = 0; j < n_limb1; j++) { + put_bits(tabr, r_len, pos, u[j]); + pos += LIMB_BITS; + } + put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1); + /* shift by dpl digits and set the carry */ + if (shift == 0) { + for(j = n_limb1 + 1; j < nb_mods; j++) + carry[j - (n_limb1 + 1)] = u[j]; + } else { + for(j = n_limb1; j < nb_mods - 1; j++) { + carry[j - n_limb1] = (u[j] >> shift) | + (u[j + 1] << (LIMB_BITS - shift)); + } + carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift; + } + } +} +#endif + +static int ntt_static_init(bf_context_t *s1) +{ + BFNTTState *s; + int inverse, i, j, k, l; + limb_t c, c_inv, c_inv2, m, m_inv; + + if (s1->ntt_state) + return 0; +#if defined(__AVX2__) + s = bf_aligned_malloc(s1, sizeof(*s), 64); +#else + s = bf_malloc(s1, sizeof(*s)); +#endif + if (!s) + return -1; + memset(s, 0, sizeof(*s)); + s1->ntt_state = s; + s->ctx = s1; + + for(j = 0; j < NB_MODS; j++) { + m = ntt_mods[j]; + m_inv = init_mul_mod_fast(m); + s->ntt_mods_div[j] = m_inv; +#if defined(__AVX2__) + s->ntt_mods_vec[j] = _mm256_set1_pd(m); + s->ntt_mods_inv_vec[j] = _mm256_set1_pd(1.0 / (double)m); +#endif + c_inv2 = (m + 1) / 2; /* 1/2 */ + c_inv = 1; + for(i = 0; i <= NTT_PROOT_2EXP; i++) { + s->ntt_len_inv[j][i][0] = c_inv; + s->ntt_len_inv[j][i][1] = init_mul_mod_fast2(c_inv, m); + c_inv = mul_mod_fast(c_inv, c_inv2, m, m_inv); + } + + for(inverse = 0; inverse < 2; inverse++) { + c = ntt_proot[inverse][j]; + for(i = 0; i < NTT_PROOT_2EXP; i++) { + s->ntt_proot_pow[j][inverse][NTT_PROOT_2EXP - i] = c; + s->ntt_proot_pow_inv[j][inverse][NTT_PROOT_2EXP - i] = + init_mul_mod_fast2(c, m); + c = mul_mod_fast(c, c, m, m_inv); + } + } + } + + l = 0; + for(j = 0; j < NB_MODS - 1; j++) { + for(k = j + 1; k < NB_MODS; k++) { +#if defined(__AVX2__) + s->ntt_mods_cr_vec[l] = _mm256_set1_pd(int_to_ntt_limb2(ntt_mods_cr[l], + ntt_mods[k])); +#else + s->ntt_mods_cr_inv[l] = init_mul_mod_fast2(ntt_mods_cr[l], + ntt_mods[k]); +#endif + l++; + } + } + return 0; +} + +int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len) +{ + int dpl, fft_len_log2, n_bits, nb_mods, dpl_found, fft_len_log2_found; + int int_bits, nb_mods_found; + limb_t cost, min_cost; + + min_cost = -1; + dpl_found = 0; + nb_mods_found = 4; + fft_len_log2_found = 0; + for(nb_mods = 3; nb_mods <= NB_MODS; nb_mods++) { + int_bits = ntt_int_bits[NB_MODS - nb_mods]; + dpl = bf_min((int_bits - 4) / 2, + 2 * LIMB_BITS + 2 * NTT_MOD_LOG2_MIN - NTT_MOD_LOG2_MAX); + for(;;) { + fft_len_log2 = ceil_log2((len * LIMB_BITS + dpl - 1) / dpl); + if (fft_len_log2 > NTT_PROOT_2EXP) + goto next; + n_bits = fft_len_log2 + 2 * dpl; + if (n_bits <= int_bits) { + cost = ((limb_t)(fft_len_log2 + 1) << fft_len_log2) * nb_mods; + // printf("n=%d dpl=%d: cost=%" PRId64 "\n", nb_mods, dpl, (int64_t)cost); + if (cost < min_cost) { + min_cost = cost; + dpl_found = dpl; + nb_mods_found = nb_mods; + fft_len_log2_found = fft_len_log2; + } + break; + } + dpl--; + if (dpl == 0) + break; + } + next: ; + } + if (!dpl_found) + abort(); + /* limit dpl if possible to reduce fixed cost of limb/NTT conversion */ + if (dpl_found > (LIMB_BITS + NTT_MOD_LOG2_MIN) && + ((limb_t)(LIMB_BITS + NTT_MOD_LOG2_MIN) << fft_len_log2_found) >= + len * LIMB_BITS) { + dpl_found = LIMB_BITS + NTT_MOD_LOG2_MIN; + } + *pnb_mods = nb_mods_found; + *pdpl = dpl_found; + return fft_len_log2_found; +} + +/* return 0 if OK, -1 if memory error */ +static no_inline int fft_mul(bf_context_t *s1, + bf_t *res, limb_t *a_tab, limb_t a_len, + limb_t *b_tab, limb_t b_len, int mul_flags) +{ + BFNTTState *s; + int dpl, fft_len_log2, j, nb_mods, reduced_mem; + slimb_t len, fft_len; + NTTLimb *buf1, *buf2, *ptr; +#if defined(USE_MUL_CHECK) + limb_t ha, hb, hr, h_ref; +#endif + + if (ntt_static_init(s1)) + return -1; + s = s1->ntt_state; + + /* find the optimal number of digits per limb (dpl) */ + len = a_len + b_len; + fft_len_log2 = bf_get_fft_size(&dpl, &nb_mods, len); + fft_len = (uint64_t)1 << fft_len_log2; + // printf("len=%" PRId64 " fft_len_log2=%d dpl=%d\n", len, fft_len_log2, dpl); +#if defined(USE_MUL_CHECK) + ha = mp_mod1(a_tab, a_len, BF_CHKSUM_MOD, 0); + hb = mp_mod1(b_tab, b_len, BF_CHKSUM_MOD, 0); +#endif + if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == 0) { + if (!(mul_flags & FFT_MUL_R_NORESIZE)) + bf_resize(res, 0); + } else if (mul_flags & FFT_MUL_R_OVERLAP_B) { + limb_t *tmp_tab, tmp_len; + /* it is better to free 'b' first */ + tmp_tab = a_tab; + a_tab = b_tab; + b_tab = tmp_tab; + tmp_len = a_len; + a_len = b_len; + b_len = tmp_len; + } + buf1 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods); + if (!buf1) + return -1; + limb_to_ntt(s, buf1, fft_len, a_tab, a_len, dpl, + NB_MODS - nb_mods, nb_mods); + if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == + FFT_MUL_R_OVERLAP_A) { + if (!(mul_flags & FFT_MUL_R_NORESIZE)) + bf_resize(res, 0); + } + reduced_mem = (fft_len_log2 >= 14); + if (!reduced_mem) { + buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods); + if (!buf2) + goto fail; + limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl, + NB_MODS - nb_mods, nb_mods); + if (!(mul_flags & FFT_MUL_R_NORESIZE)) + bf_resize(res, 0); /* in case res == b */ + } else { + buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len); + if (!buf2) + goto fail; + } + for(j = 0; j < nb_mods; j++) { + if (reduced_mem) { + limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl, + NB_MODS - nb_mods + j, 1); + ptr = buf2; + } else { + ptr = buf2 + fft_len * j; + } + if (ntt_conv(s, buf1 + fft_len * j, ptr, + fft_len_log2, fft_len_log2, j + NB_MODS - nb_mods)) + goto fail; + } + if (!(mul_flags & FFT_MUL_R_NORESIZE)) + bf_resize(res, 0); /* in case res == b and reduced mem */ + ntt_free(s, buf2); + buf2 = NULL; + if (!(mul_flags & FFT_MUL_R_NORESIZE)) { + if (bf_resize(res, len)) + goto fail; + } + ntt_to_limb(s, res->tab, len, buf1, fft_len_log2, dpl, nb_mods); + ntt_free(s, buf1); +#if defined(USE_MUL_CHECK) + hr = mp_mod1(res->tab, len, BF_CHKSUM_MOD, 0); + h_ref = mul_mod(ha, hb, BF_CHKSUM_MOD); + if (hr != h_ref) { + printf("ntt_mul_error: len=%" PRId_LIMB " fft_len_log2=%d dpl=%d nb_mods=%d\n", + len, fft_len_log2, dpl, nb_mods); + // printf("ha=0x" FMT_LIMB" hb=0x" FMT_LIMB " hr=0x" FMT_LIMB " expected=0x" FMT_LIMB "\n", ha, hb, hr, h_ref); + exit(1); + } +#endif + return 0; + fail: + ntt_free(s, buf1); + ntt_free(s, buf2); + return -1; +} + +#else /* USE_FFT_MUL */ + +int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len) +{ + return 0; +} + +#endif /* !USE_FFT_MUL */ + +#undef malloc +#undef free +#undef realloc diff --git a/armorcore/sources/libs/quickjs/libbf.h b/armorcore/sources/libs/quickjs/libbf.h new file mode 100644 index 000000000..3586532e8 --- /dev/null +++ b/armorcore/sources/libs/quickjs/libbf.h @@ -0,0 +1,545 @@ +/* + * Tiny arbitrary precision floating point library + * + * Copyright (c) 2017-2021 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBBF_H +#define LIBBF_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if INTPTR_MAX >= INT64_MAX && !defined(_WIN32) && !defined(__TINYC__) +#define LIMB_LOG2_BITS 6 +#else +#define LIMB_LOG2_BITS 5 +#endif + +#define LIMB_BITS (1 << LIMB_LOG2_BITS) + +#if LIMB_BITS == 64 +#ifndef INT128_MAX +__extension__ typedef __int128 int128_t; +__extension__ typedef unsigned __int128 uint128_t; +#endif +typedef int64_t slimb_t; +typedef uint64_t limb_t; +typedef uint128_t dlimb_t; +#define BF_RAW_EXP_MIN INT64_MIN +#define BF_RAW_EXP_MAX INT64_MAX + +#define LIMB_DIGITS 19 +#define BF_DEC_BASE UINT64_C(10000000000000000000) + +#else + +typedef int32_t slimb_t; +typedef uint32_t limb_t; +typedef uint64_t dlimb_t; +#define BF_RAW_EXP_MIN INT32_MIN +#define BF_RAW_EXP_MAX INT32_MAX + +#define LIMB_DIGITS 9 +#define BF_DEC_BASE 1000000000U + +#endif + +/* in bits */ +/* minimum number of bits for the exponent */ +#define BF_EXP_BITS_MIN 3 +/* maximum number of bits for the exponent */ +#define BF_EXP_BITS_MAX (LIMB_BITS - 3) +/* extended range for exponent, used internally */ +#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1) +/* minimum possible precision */ +#define BF_PREC_MIN 2 +/* minimum possible precision */ +#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2) +/* some operations support infinite precision */ +#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */ + +#if LIMB_BITS == 64 +#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197)) +#else +#define BF_CHKSUM_MOD 975620677U +#endif + +#define BF_EXP_ZERO BF_RAW_EXP_MIN +#define BF_EXP_INF (BF_RAW_EXP_MAX - 1) +#define BF_EXP_NAN BF_RAW_EXP_MAX + +/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0, + +/-infinity is represented with expn = BF_EXP_INF and len = 0, + NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored) + */ +typedef struct { + struct bf_context_t *ctx; + int sign; + slimb_t expn; + limb_t len; + limb_t *tab; +} bf_t; + +typedef struct { + /* must be kept identical to bf_t */ + struct bf_context_t *ctx; + int sign; + slimb_t expn; + limb_t len; + limb_t *tab; +} bfdec_t; + +typedef enum { + BF_RNDN, /* round to nearest, ties to even */ + BF_RNDZ, /* round to zero */ + BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */ + BF_RNDU, /* round to +inf */ + BF_RNDNA, /* round to nearest, ties away from zero */ + BF_RNDA, /* round away from zero */ + BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU, + inexact flag is always set) */ +} bf_rnd_t; + +/* allow subnormal numbers. Only available if the number of exponent + bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */ +#define BF_FLAG_SUBNORMAL (1 << 3) +/* 'prec' is the precision after the radix point instead of the whole + mantissa. Can only be used with bf_round() and + bfdec_[add|sub|mul|div|sqrt|round](). */ +#define BF_FLAG_RADPNT_PREC (1 << 4) + +#define BF_RND_MASK 0x7 +#define BF_EXP_BITS_SHIFT 5 +#define BF_EXP_BITS_MASK 0x3f + +/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */ +#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT) + +/* contains the rounding mode and number of exponents bits */ +typedef uint32_t bf_flags_t; + +typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size); + +typedef struct { + bf_t val; + limb_t prec; +} BFConstCache; + +typedef struct bf_context_t { + void *realloc_opaque; + bf_realloc_func_t *realloc_func; + BFConstCache log2_cache; + BFConstCache pi_cache; + struct BFNTTState *ntt_state; +} bf_context_t; + +static inline int bf_get_exp_bits(bf_flags_t flags) +{ + int e; + e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK; + if (e == BF_EXP_BITS_MASK) + return BF_EXP_BITS_MAX + 1; + else + return BF_EXP_BITS_MAX - e; +} + +static inline bf_flags_t bf_set_exp_bits(int n) +{ + return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT; +} + +/* returned status */ +#define BF_ST_INVALID_OP (1 << 0) +#define BF_ST_DIVIDE_ZERO (1 << 1) +#define BF_ST_OVERFLOW (1 << 2) +#define BF_ST_UNDERFLOW (1 << 3) +#define BF_ST_INEXACT (1 << 4) +/* indicate that a memory allocation error occured. NaN is returned */ +#define BF_ST_MEM_ERROR (1 << 5) + +#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */ + +static inline slimb_t bf_max(slimb_t a, slimb_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline slimb_t bf_min(slimb_t a, slimb_t b) +{ + if (a < b) + return a; + else + return b; +} + +void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, + void *realloc_opaque); +void bf_context_end(bf_context_t *s); +/* free memory allocated for the bf cache data */ +void bf_clear_cache(bf_context_t *s); + +static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size) +{ + return s->realloc_func(s->realloc_opaque, ptr, size); +} + +/* 'size' must be != 0 */ +static inline void *bf_malloc(bf_context_t *s, size_t size) +{ + return bf_realloc(s, NULL, size); +} + +static inline void bf_free(bf_context_t *s, void *ptr) +{ + /* must test ptr otherwise equivalent to malloc(0) */ + if (ptr) + bf_realloc(s, ptr, 0); +} + +void bf_init(bf_context_t *s, bf_t *r); + +static inline void bf_delete(bf_t *r) +{ + bf_context_t *s = r->ctx; + /* we accept to delete a zeroed bf_t structure */ + if (s && r->tab) { + bf_realloc(s, r->tab, 0); + } +} + +static inline void bf_neg(bf_t *r) +{ + r->sign ^= 1; +} + +static inline int bf_is_finite(const bf_t *a) +{ + return (a->expn < BF_EXP_INF); +} + +static inline int bf_is_nan(const bf_t *a) +{ + return (a->expn == BF_EXP_NAN); +} + +static inline int bf_is_zero(const bf_t *a) +{ + return (a->expn == BF_EXP_ZERO); +} + +static inline void bf_memcpy(bf_t *r, const bf_t *a) +{ + *r = *a; +} + +int bf_set_ui(bf_t *r, uint64_t a); +int bf_set_si(bf_t *r, int64_t a); +void bf_set_nan(bf_t *r); +void bf_set_zero(bf_t *r, int is_neg); +void bf_set_inf(bf_t *r, int is_neg); +int bf_set(bf_t *r, const bf_t *a); +void bf_move(bf_t *r, bf_t *a); +int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode); +int bf_set_float64(bf_t *a, double d); + +int bf_cmpu(const bf_t *a, const bf_t *b); +int bf_cmp_full(const bf_t *a, const bf_t *b); +int bf_cmp(const bf_t *a, const bf_t *b); +static inline int bf_cmp_eq(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) == 0; +} + +static inline int bf_cmp_le(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) <= 0; +} + +static inline int bf_cmp_lt(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) < 0; +} + +int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags); +int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags); +int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags); +int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +#define BF_DIVREM_EUCLIDIAN BF_RNDF +int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode); +int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +/* round to integer with infinite precision */ +int bf_rint(bf_t *r, int rnd_mode); +int bf_round(bf_t *r, limb_t prec, bf_flags_t flags); +int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a); +int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +slimb_t bf_get_exp_min(const bf_t *a); +int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b); +int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b); +int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b); + +/* additional flags for bf_atof */ +/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */ +#define BF_ATOF_NO_HEX (1 << 16) +/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */ +#define BF_ATOF_BIN_OCT (1 << 17) +/* Do not parse NaN or Inf */ +#define BF_ATOF_NO_NAN_INF (1 << 18) +/* return the exponent separately */ +#define BF_ATOF_EXPONENT (1 << 19) + +int bf_atof(bf_t *a, const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags); +/* this version accepts prec = BF_PREC_INF and returns the radix + exponent */ +int bf_atof2(bf_t *r, slimb_t *pexponent, + const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags); +int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, + slimb_t expn, limb_t prec, bf_flags_t flags); + + +/* Conversion of floating point number to string. Return a null + terminated string or NULL if memory error. *plen contains its + length if plen != NULL. The exponent letter is "e" for base 10, + "p" for bases 2, 8, 16 with a binary exponent and "@" for the other + bases. */ + +#define BF_FTOA_FORMAT_MASK (3 << 16) + +/* fixed format: prec significant digits rounded with (flags & + BF_RND_MASK). Exponential notation is used if too many zeros are + needed.*/ +#define BF_FTOA_FORMAT_FIXED (0 << 16) +/* fractional format: prec digits after the decimal point rounded with + (flags & BF_RND_MASK) */ +#define BF_FTOA_FORMAT_FRAC (1 << 16) +/* free format: + + For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum + number of digits to represent 'a'. The precision and the rounding + mode are ignored. + + For the non binary radices with bf_ftoa(): use as many digits as + necessary so that bf_atof() return the same number when using + precision 'prec', rounding to nearest and the subnormal + configuration of 'flags'. The result is meaningful only if 'a' is + already rounded to 'prec' bits. If the subnormal flag is set, the + exponent in 'flags' must also be set to the desired exponent range. +*/ +#define BF_FTOA_FORMAT_FREE (2 << 16) +/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits + (takes more computation time). Identical to BF_FTOA_FORMAT_FREE for + binary radices with bf_ftoa() and for bfdec_ftoa(). */ +#define BF_FTOA_FORMAT_FREE_MIN (3 << 16) + +/* force exponential notation for fixed or free format */ +#define BF_FTOA_FORCE_EXP (1 << 20) +/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for + base 2 if non zero value */ +#define BF_FTOA_ADD_PREFIX (1 << 21) +/* return "Infinity" instead of "Inf" and add a "+" for positive + exponents */ +#define BF_FTOA_JS_QUIRKS (1 << 22) + +char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, + bf_flags_t flags); + +/* modulo 2^n instead of saturation. NaN and infinity return 0 */ +#define BF_GET_INT_MOD (1 << 0) +int bf_get_int32(int *pres, const bf_t *a, int flags); +int bf_get_int64(int64_t *pres, const bf_t *a, int flags); +int bf_get_uint64(uint64_t *pres, const bf_t *a); + +/* the following functions are exported for testing only. */ +void mp_print_str(const char *str, const limb_t *tab, limb_t n); +void bf_print_str(const char *str, const bf_t *a); +int bf_resize(bf_t *r, limb_t len); +int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len); +int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags); +int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k); +slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, + int is_ceil1); +int mp_mul(bf_context_t *s, limb_t *result, + const limb_t *op1, limb_t op1_size, + const limb_t *op2, limb_t op2_size); +limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, + limb_t n, limb_t carry); +limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n); +int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n); +int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n); +limb_t bf_isqrt(limb_t a); + +/* transcendental functions */ +int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags); +int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags); +int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */ +int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags); +int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, + limb_t prec, bf_flags_t flags); +int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); + +/* decimal floating point */ + +static inline void bfdec_init(bf_context_t *s, bfdec_t *r) +{ + bf_init(s, (bf_t *)r); +} +static inline void bfdec_delete(bfdec_t *r) +{ + bf_delete((bf_t *)r); +} + +static inline void bfdec_neg(bfdec_t *r) +{ + r->sign ^= 1; +} + +static inline int bfdec_is_finite(const bfdec_t *a) +{ + return (a->expn < BF_EXP_INF); +} + +static inline int bfdec_is_nan(const bfdec_t *a) +{ + return (a->expn == BF_EXP_NAN); +} + +static inline int bfdec_is_zero(const bfdec_t *a) +{ + return (a->expn == BF_EXP_ZERO); +} + +static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a) +{ + bf_memcpy((bf_t *)r, (const bf_t *)a); +} + +int bfdec_set_ui(bfdec_t *r, uint64_t a); +int bfdec_set_si(bfdec_t *r, int64_t a); + +static inline void bfdec_set_nan(bfdec_t *r) +{ + bf_set_nan((bf_t *)r); +} +static inline void bfdec_set_zero(bfdec_t *r, int is_neg) +{ + bf_set_zero((bf_t *)r, is_neg); +} +static inline void bfdec_set_inf(bfdec_t *r, int is_neg) +{ + bf_set_inf((bf_t *)r, is_neg); +} +static inline int bfdec_set(bfdec_t *r, const bfdec_t *a) +{ + return bf_set((bf_t *)r, (bf_t *)a); +} +static inline void bfdec_move(bfdec_t *r, bfdec_t *a) +{ + bf_move((bf_t *)r, (bf_t *)a); +} +static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmpu((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp_full((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) == 0; +} +static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) <= 0; +} +static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) < 0; +} + +int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode); +int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +int bfdec_rint(bfdec_t *r, int rnd_mode); +int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags); +int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags); +int bfdec_get_int32(int *pres, const bfdec_t *a); +int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b); + +char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags); +int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, + limb_t prec, bf_flags_t flags); + +/* the following functions are exported for testing only. */ +extern const limb_t mp_pow_dec[LIMB_DIGITS + 1]; +void bfdec_print_str(const char *str, const bfdec_t *a); +static inline int bfdec_resize(bfdec_t *r, limb_t len) +{ + return bf_resize((bf_t *)r, len); +} +int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags); + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIBBF_H */ diff --git a/armorcore/sources/libs/quickjs/libregexp-opcode.h b/armorcore/sources/libs/quickjs/libregexp-opcode.h new file mode 100644 index 000000000..7fdc887ae --- /dev/null +++ b/armorcore/sources/libs/quickjs/libregexp-opcode.h @@ -0,0 +1,59 @@ +/* + * Regular Expression Engine + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef DEF + +DEF(invalid, 1) /* never used */ +DEF(char8, 2) /* 7 bits in fact */ +DEF(char16, 3) +DEF(char32, 5) +DEF(dot, 1) +DEF(any, 1) /* same as dot but match any character including line terminator */ +DEF(line_start, 1) +DEF(line_end, 1) +DEF(goto, 5) +DEF(split_goto_first, 5) +DEF(split_next_first, 5) +DEF(match, 1) +DEF(save_start, 2) /* save start position */ +DEF(save_end, 2) /* save end position, must come after saved_start */ +DEF(save_reset, 3) /* reset save positions */ +DEF(loop, 5) /* decrement the top the stack and goto if != 0 */ +DEF(push_i32, 5) /* push integer on the stack */ +DEF(drop, 1) +DEF(word_boundary, 1) +DEF(not_word_boundary, 1) +DEF(back_reference, 2) +DEF(backward_back_reference, 2) /* must come after back_reference */ +DEF(range, 3) /* variable length */ +DEF(range32, 3) /* variable length */ +DEF(lookahead, 5) +DEF(negative_lookahead, 5) +DEF(push_char_pos, 1) /* push the character position on the stack */ +DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character + position */ +DEF(prev, 1) /* go to the previous char */ +DEF(simple_greedy_quant, 17) + +#endif /* DEF */ diff --git a/armorcore/sources/libs/quickjs/libregexp.c b/armorcore/sources/libs/quickjs/libregexp.c new file mode 100644 index 000000000..28af7a3b6 --- /dev/null +++ b/armorcore/sources/libs/quickjs/libregexp.c @@ -0,0 +1,2729 @@ +/* + * Regular Expression Engine + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "libregexp.h" + +/* + TODO: + + - Add full unicode canonicalize rules for character ranges (not + really useful but needed for exact "ignorecase" compatibility). + + - Add a lock step execution mode (=linear time execution guaranteed) + when the regular expression is "simple" i.e. no backreference nor + complicated lookahead. The opcodes are designed for this execution + model. +*/ + +#if defined(TEST) +#define DUMP_REOP +#endif + +typedef enum { +#define DEF(id, size) REOP_ ## id, +#include "libregexp-opcode.h" +#undef DEF + REOP_COUNT, +} REOPCodeEnum; + +#define CAPTURE_COUNT_MAX 255 +#define STACK_SIZE_MAX 255 + +/* unicode code points */ +#define CP_LS 0x2028 +#define CP_PS 0x2029 + +#define TMP_BUF_SIZE 128 + +// invariant: is_unicode ^ unicode_sets (or neither, but not both) +typedef struct { + DynBuf byte_code; + const uint8_t *buf_ptr; + const uint8_t *buf_end; + const uint8_t *buf_start; + int re_flags; + BOOL is_unicode; + BOOL unicode_sets; + BOOL ignore_case; + BOOL dotall; + int capture_count; + int total_capture_count; /* -1 = not computed yet */ + int has_named_captures; /* -1 = don't know, 0 = no, 1 = yes */ + void *opaque; + DynBuf group_names; + union { + char error_msg[TMP_BUF_SIZE]; + char tmp_buf[TMP_BUF_SIZE]; + } u; +} REParseState; + +typedef struct { +#ifdef DUMP_REOP + const char *name; +#endif + uint8_t size; +} REOpCode; + +static const REOpCode reopcode_info[REOP_COUNT] = { +#ifdef DUMP_REOP +#define DEF(id, size) { #id, size }, +#else +#define DEF(id, size) { size }, +#endif +#include "libregexp-opcode.h" +#undef DEF +}; + +#define RE_HEADER_FLAGS 0 +#define RE_HEADER_CAPTURE_COUNT 2 +#define RE_HEADER_STACK_SIZE 3 +#define RE_HEADER_BYTECODE_LEN 4 + +#define RE_HEADER_LEN 8 + +static inline int lre_is_digit(int c) { + return c >= '0' && c <= '9'; +} + +/* insert 'len' bytes at position 'pos'. Return < 0 if error. */ +static int dbuf_insert(DynBuf *s, int pos, int len) +{ + if (dbuf_realloc(s, s->size + len)) + return -1; + memmove(s->buf + pos + len, s->buf + pos, s->size - pos); + s->size += len; + return 0; +} + +/* canonicalize with the specific JS regexp rules */ +static uint32_t lre_canonicalize(uint32_t c, BOOL is_unicode) +{ + uint32_t res[LRE_CC_RES_LEN_MAX]; + int len; + if (is_unicode) { + if (likely(c < 128)) { + if (c >= 'A' && c <= 'Z') + c = c - 'A' + 'a'; + } else { + lre_case_conv(res, c, 2); + c = res[0]; + } + } else { + if (likely(c < 128)) { + if (c >= 'a' && c <= 'z') + c = c - 'a' + 'A'; + } else { + /* legacy regexp: to upper case if single char >= 128 */ + len = lre_case_conv(res, c, FALSE); + if (len == 1 && res[0] >= 128) + c = res[0]; + } + } + return c; +} + +static const uint16_t char_range_d[] = { + 1, + 0x0030, 0x0039 + 1, +}; + +/* code point ranges for Zs,Zl or Zp property */ +static const uint16_t char_range_s[] = { + 10, + 0x0009, 0x000D + 1, + 0x0020, 0x0020 + 1, + 0x00A0, 0x00A0 + 1, + 0x1680, 0x1680 + 1, + 0x2000, 0x200A + 1, + /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */ + /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */ + 0x2028, 0x2029 + 1, + 0x202F, 0x202F + 1, + 0x205F, 0x205F + 1, + 0x3000, 0x3000 + 1, + /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */ + 0xFEFF, 0xFEFF + 1, +}; + +BOOL lre_is_space(int c) +{ + int i, n, low, high; + n = (countof(char_range_s) - 1) / 2; + for(i = 0; i < n; i++) { + low = char_range_s[2 * i + 1]; + if (c < low) + return FALSE; + high = char_range_s[2 * i + 2]; + if (c < high) + return TRUE; + } + return FALSE; +} + +uint32_t const lre_id_start_table_ascii[4] = { + /* $ A-Z _ a-z */ + 0x00000000, 0x00000010, 0x87FFFFFE, 0x07FFFFFE +}; + +uint32_t const lre_id_continue_table_ascii[4] = { + /* $ 0-9 A-Z _ a-z */ + 0x00000000, 0x03FF0010, 0x87FFFFFE, 0x07FFFFFE +}; + + +static const uint16_t char_range_w[] = { + 4, + 0x0030, 0x0039 + 1, + 0x0041, 0x005A + 1, + 0x005F, 0x005F + 1, + 0x0061, 0x007A + 1, +}; + +#define CLASS_RANGE_BASE 0x40000000 + +typedef enum { + CHAR_RANGE_d, + CHAR_RANGE_D, + CHAR_RANGE_s, + CHAR_RANGE_S, + CHAR_RANGE_w, + CHAR_RANGE_W, +} CharRangeEnum; + +static const uint16_t *char_range_table[] = { + char_range_d, + char_range_s, + char_range_w, +}; + +static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c) +{ + BOOL invert; + const uint16_t *c_pt; + int len, i; + + invert = c & 1; + c_pt = char_range_table[c >> 1]; + len = *c_pt++; + cr_init(cr, s->opaque, lre_realloc); + for(i = 0; i < len * 2; i++) { + if (cr_add_point(cr, c_pt[i])) + goto fail; + } + if (invert) { + if (cr_invert(cr)) + goto fail; + } + return 0; + fail: + cr_free(cr); + return -1; +} + +static int cr_canonicalize(CharRange *cr) +{ + CharRange a; + uint32_t pt[2]; + int i, ret; + + cr_init(&a, cr->mem_opaque, lre_realloc); + pt[0] = 'a'; + pt[1] = 'z' + 1; + ret = cr_op(&a, cr->points, cr->len, pt, 2, CR_OP_INTER); + if (ret) + goto fail; + /* convert to upper case */ + /* XXX: the generic unicode case would be much more complicated + and not really useful */ + for(i = 0; i < a.len; i++) { + a.points[i] += 'A' - 'a'; + } + /* Note: for simplicity we keep the lower case ranges */ + ret = cr_union1(cr, a.points, a.len); + fail: + cr_free(&a); + return ret; +} + +#ifdef DUMP_REOP +static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, + int buf_len) +{ + int pos, len, opcode, bc_len, re_flags, i; + uint32_t val; + + assert(buf_len >= RE_HEADER_LEN); + + re_flags = lre_get_flags(buf); + bc_len = get_u32(buf + RE_HEADER_BYTECODE_LEN); + assert(bc_len + RE_HEADER_LEN <= buf_len); + printf("flags: 0x%x capture_count=%d stack_size=%d\n", + re_flags, buf[RE_HEADER_CAPTURE_COUNT], buf[RE_HEADER_STACK_SIZE]); + if (re_flags & LRE_FLAG_NAMED_GROUPS) { + const char *p; + p = (char *)buf + RE_HEADER_LEN + bc_len; + printf("named groups: "); + for(i = 1; i < buf[RE_HEADER_CAPTURE_COUNT]; i++) { + if (i != 1) + printf(","); + printf("<%s>", p); + p += strlen(p) + 1; + } + printf("\n"); + assert(p == (char *)(buf + buf_len)); + } + printf("bytecode_len=%d\n", bc_len); + + buf += RE_HEADER_LEN; + pos = 0; + while (pos < bc_len) { + printf("%5u: ", pos); + opcode = buf[pos]; + len = reopcode_info[opcode].size; + if (opcode >= REOP_COUNT) { + printf(" invalid opcode=0x%02x\n", opcode); + break; + } + if ((pos + len) > bc_len) { + printf(" buffer overflow (opcode=0x%02x)\n", opcode); + break; + } + printf("%s", reopcode_info[opcode].name); + switch(opcode) { + case REOP_char8: + val = get_u8(buf + pos + 1); + goto printchar; + case REOP_char16: + val = get_u16(buf + pos + 1); + goto printchar; + case REOP_char32: + val = get_u32(buf + pos + 1); + printchar: + if (val >= ' ' && val <= 126) + printf(" '%c'", val); + else + printf(" 0x%08x", val); + break; + case REOP_goto: + case REOP_split_goto_first: + case REOP_split_next_first: + case REOP_loop: + case REOP_lookahead: + case REOP_negative_lookahead: + case REOP_bne_char_pos: + val = get_u32(buf + pos + 1); + val += (pos + 5); + printf(" %u", val); + break; + case REOP_simple_greedy_quant: + printf(" %u %u %u %u", + get_u32(buf + pos + 1) + (pos + 17), + get_u32(buf + pos + 1 + 4), + get_u32(buf + pos + 1 + 8), + get_u32(buf + pos + 1 + 12)); + break; + case REOP_save_start: + case REOP_save_end: + case REOP_back_reference: + case REOP_backward_back_reference: + printf(" %u", buf[pos + 1]); + break; + case REOP_save_reset: + printf(" %u %u", buf[pos + 1], buf[pos + 2]); + break; + case REOP_push_i32: + val = get_u32(buf + pos + 1); + printf(" %d", val); + break; + case REOP_range: + { + int n, i; + n = get_u16(buf + pos + 1); + len += n * 4; + for(i = 0; i < n * 2; i++) { + val = get_u16(buf + pos + 3 + i * 2); + printf(" 0x%04x", val); + } + } + break; + case REOP_range32: + { + int n, i; + n = get_u16(buf + pos + 1); + len += n * 8; + for(i = 0; i < n * 2; i++) { + val = get_u32(buf + pos + 3 + i * 4); + printf(" 0x%08x", val); + } + } + break; + default: + break; + } + printf("\n"); + pos += len; + } +} +#endif + +static void re_emit_op(REParseState *s, int op) +{ + dbuf_putc(&s->byte_code, op); +} + +/* return the offset of the u32 value */ +static int re_emit_op_u32(REParseState *s, int op, uint32_t val) +{ + int pos; + dbuf_putc(&s->byte_code, op); + pos = s->byte_code.size; + dbuf_put_u32(&s->byte_code, val); + return pos; +} + +static int re_emit_goto(REParseState *s, int op, uint32_t val) +{ + int pos; + dbuf_putc(&s->byte_code, op); + pos = s->byte_code.size; + dbuf_put_u32(&s->byte_code, val - (pos + 4)); + return pos; +} + +static void re_emit_op_u8(REParseState *s, int op, uint32_t val) +{ + dbuf_putc(&s->byte_code, op); + dbuf_putc(&s->byte_code, val); +} + +static void re_emit_op_u16(REParseState *s, int op, uint32_t val) +{ + dbuf_putc(&s->byte_code, op); + dbuf_put_u16(&s->byte_code, val); +} + +static int __attribute__((format(printf, 2, 3))) re_parse_error(REParseState *s, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(s->u.error_msg, sizeof(s->u.error_msg), fmt, ap); + va_end(ap); + return -1; +} + +static int re_parse_out_of_memory(REParseState *s) +{ + return re_parse_error(s, "out of memory"); +} + +/* If allow_overflow is false, return -1 in case of + overflow. Otherwise return INT32_MAX. */ +static int parse_digits(const uint8_t **pp, BOOL allow_overflow) +{ + const uint8_t *p; + uint64_t v; + int c; + + p = *pp; + v = 0; + for(;;) { + c = *p; + if (c < '0' || c > '9') + break; + v = v * 10 + c - '0'; + if (v >= INT32_MAX) { + if (allow_overflow) + v = INT32_MAX; + else + return -1; + } + p++; + } + *pp = p; + return v; +} + +static int re_parse_expect(REParseState *s, const uint8_t **pp, int c) +{ + const uint8_t *p; + p = *pp; + if (*p != c) + return re_parse_error(s, "expecting '%c'", c); + p++; + *pp = p; + return 0; +} + +/* Parse an escape sequence, *pp points after the '\': + allow_utf16 value: + 0 : no UTF-16 escapes allowed + 1 : UTF-16 escapes allowed + 2 : UTF-16 escapes allowed and escapes of surrogate pairs are + converted to a unicode character (unicode regexp case). + + Return the unicode char and update *pp if recognized, + return -1 if malformed escape, + return -2 otherwise. */ +int lre_parse_escape(const uint8_t **pp, int allow_utf16) +{ + const uint8_t *p; + uint32_t c; + + p = *pp; + c = *p++; + switch(c) { + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = '\v'; + break; + case 'x': + case 'u': + { + int h, n, i; + uint32_t c1; + + if (*p == '{' && allow_utf16) { + p++; + c = 0; + for(;;) { + h = from_hex(*p++); + if (h < 0) + return -1; + c = (c << 4) | h; + if (c > 0x10FFFF) + return -1; + if (*p == '}') + break; + } + p++; + } else { + if (c == 'x') { + n = 2; + } else { + n = 4; + } + + c = 0; + for(i = 0; i < n; i++) { + h = from_hex(*p++); + if (h < 0) { + return -1; + } + c = (c << 4) | h; + } + if (is_hi_surrogate(c) && + allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') { + /* convert an escaped surrogate pair into a + unicode char */ + c1 = 0; + for(i = 0; i < 4; i++) { + h = from_hex(p[2 + i]); + if (h < 0) + break; + c1 = (c1 << 4) | h; + } + if (i == 4 && is_lo_surrogate(c1)) { + p += 6; + c = from_surrogate(c, c1); + } + } + } + } + break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c -= '0'; + if (allow_utf16 == 2) { + /* only accept \0 not followed by digit */ + if (c != 0 || lre_is_digit(*p)) + return -1; + } else { + /* parse a legacy octal sequence */ + uint32_t v; + v = *p - '0'; + if (v > 7) + break; + c = (c << 3) | v; + p++; + if (c >= 32) + break; + v = *p - '0'; + if (v > 7) + break; + c = (c << 3) | v; + p++; + } + break; + default: + return -2; + } + *pp = p; + return c; +} + +/* XXX: we use the same chars for name and value */ +static BOOL is_unicode_char(int c) +{ + return ((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || + (c >= 'a' && c <= 'z') || + (c == '_')); +} + +static int parse_unicode_property(REParseState *s, CharRange *cr, + const uint8_t **pp, BOOL is_inv) +{ + const uint8_t *p; + char name[64], value[64]; + char *q; + BOOL script_ext; + int ret; + + p = *pp; + if (*p != '{') + return re_parse_error(s, "expecting '{' after \\p"); + p++; + q = name; + while (is_unicode_char(*p)) { + if ((q - name) >= sizeof(name) - 1) + goto unknown_property_name; + *q++ = *p++; + } + *q = '\0'; + q = value; + if (*p == '=') { + p++; + while (is_unicode_char(*p)) { + if ((q - value) >= sizeof(value) - 1) + return re_parse_error(s, "unknown unicode property value"); + *q++ = *p++; + } + } + *q = '\0'; + if (*p != '}') + return re_parse_error(s, "expecting '}'"); + p++; + // printf("name=%s value=%s\n", name, value); + + if (!strcmp(name, "Script") || !strcmp(name, "sc")) { + script_ext = FALSE; + goto do_script; + } else if (!strcmp(name, "Script_Extensions") || !strcmp(name, "scx")) { + script_ext = TRUE; + do_script: + cr_init(cr, s->opaque, lre_realloc); + ret = unicode_script(cr, value, script_ext); + if (ret) { + cr_free(cr); + if (ret == -2) + return re_parse_error(s, "unknown unicode script"); + else + goto out_of_memory; + } + } else if (!strcmp(name, "General_Category") || !strcmp(name, "gc")) { + cr_init(cr, s->opaque, lre_realloc); + ret = unicode_general_category(cr, value); + if (ret) { + cr_free(cr); + if (ret == -2) + return re_parse_error(s, "unknown unicode general category"); + else + goto out_of_memory; + } + } else if (value[0] == '\0') { + cr_init(cr, s->opaque, lre_realloc); + ret = unicode_general_category(cr, name); + if (ret == -1) { + cr_free(cr); + goto out_of_memory; + } + if (ret < 0) { + ret = unicode_prop(cr, name); + if (ret) { + cr_free(cr); + if (ret == -2) + goto unknown_property_name; + else + goto out_of_memory; + } + } + } else { + unknown_property_name: + return re_parse_error(s, "unknown unicode property name"); + } + + if (is_inv) { + if (cr_invert(cr)) { + cr_free(cr); + return -1; + } + } + *pp = p; + return 0; + out_of_memory: + return re_parse_out_of_memory(s); +} + +/* return -1 if error otherwise the character or a class range + (CLASS_RANGE_BASE). In case of class range, 'cr' is + initialized. Otherwise, it is ignored. */ +static int get_class_atom(REParseState *s, CharRange *cr, + const uint8_t **pp, BOOL inclass) +{ + const uint8_t *p, *p_next; + uint32_t c; + int ret; + + p = *pp; + + c = *p; + switch(c) { + case '\\': + p++; + if (p >= s->buf_end) + goto unexpected_end; + c = *p++; + switch(c) { + case 'd': + c = CHAR_RANGE_d; + goto class_range; + case 'D': + c = CHAR_RANGE_D; + goto class_range; + case 's': + c = CHAR_RANGE_s; + goto class_range; + case 'S': + c = CHAR_RANGE_S; + goto class_range; + case 'w': + c = CHAR_RANGE_w; + goto class_range; + case 'W': + c = CHAR_RANGE_W; + class_range: + if (cr_init_char_range(s, cr, c)) + return -1; + c = CLASS_RANGE_BASE; + break; + case 'c': + c = *p; + if ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (((c >= '0' && c <= '9') || c == '_') && + inclass && !s->is_unicode)) { /* Annex B.1.4 */ + c &= 0x1f; + p++; + } else if (s->is_unicode) { + goto invalid_escape; + } else { + /* otherwise return '\' and 'c' */ + p--; + c = '\\'; + } + break; + case 'p': + case 'P': + if (s->is_unicode) { + if (parse_unicode_property(s, cr, &p, (c == 'P'))) + return -1; + c = CLASS_RANGE_BASE; + break; + } + /* fall thru */ + default: + p--; + ret = lre_parse_escape(&p, s->is_unicode * 2); + if (ret >= 0) { + c = ret; + } else { + if (ret == -2 && *p != '\0' && strchr("^$\\.*+?()[]{}|/", *p)) { + /* always valid to escape these characters */ + goto normal_char; + } else if (s->is_unicode) { + // special case: allowed inside [] but not outside + if (ret == -2 && *p == '-' && inclass) + goto normal_char; + invalid_escape: + return re_parse_error(s, "invalid escape sequence in regular expression"); + } else { + /* just ignore the '\' */ + goto normal_char; + } + } + break; + } + break; + case '\0': + if (p >= s->buf_end) { + unexpected_end: + return re_parse_error(s, "unexpected end"); + } + /* fall thru */ + default: + normal_char: + p++; + if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) + return re_parse_error(s, "invalid UTF-8 sequence"); + p = p_next; + if (c > 0xFFFF && !s->is_unicode) { + // TODO(chqrlie): should handle non BMP-1 code points in + // the calling function and no require the source string + // to be CESU-8 encoded if not s->is_unicode + return re_parse_error(s, "malformed unicode char"); + } + } + break; + } + *pp = p; + return c; +} + +static int re_emit_range(REParseState *s, const CharRange *cr) +{ + int len, i; + uint32_t high; + + len = (unsigned)cr->len / 2; + if (len >= 65535) + return re_parse_error(s, "too many ranges"); + if (len == 0) { + /* not sure it can really happen. Emit a match that is always + false */ + re_emit_op_u32(s, REOP_char32, -1); + } else { + high = cr->points[cr->len - 1]; + if (high == UINT32_MAX) + high = cr->points[cr->len - 2]; + if (high <= 0xffff) { + /* can use 16 bit ranges with the conversion that 0xffff = + infinity */ + re_emit_op_u16(s, REOP_range, len); + for(i = 0; i < cr->len; i += 2) { + dbuf_put_u16(&s->byte_code, cr->points[i]); + high = cr->points[i + 1] - 1; + if (high == UINT32_MAX - 1) + high = 0xffff; + dbuf_put_u16(&s->byte_code, high); + } + } else { + re_emit_op_u16(s, REOP_range32, len); + for(i = 0; i < cr->len; i += 2) { + dbuf_put_u32(&s->byte_code, cr->points[i]); + dbuf_put_u32(&s->byte_code, cr->points[i + 1] - 1); + } + } + } + return 0; +} + +// s->unicode turns patterns like []] into syntax errors +// s->unicode_sets turns more patterns into errors, like [a-] or [[] +static int re_parse_char_class(REParseState *s, const uint8_t **pp) +{ + const uint8_t *p; + uint32_t c1, c2; + CharRange cr_s, *cr = &cr_s; + CharRange cr1_s, *cr1 = &cr1_s; + BOOL invert; + + cr_init(cr, s->opaque, lre_realloc); + p = *pp; + p++; /* skip '[' */ + + if (s->unicode_sets) { + static const char verboten[] = + "()[{}/-|" "\0" + "&&!!##$$%%**++,,..::;;<<==>>??@@``~~" "\0" + "^^^_^^"; + const char *s = verboten; + int n = 1; + do { + if (!memcmp(s, p, n)) + if (p[n] == ']') + goto invalid_class_range; + s += n; + if (!*s) { + s++; + n++; + } + } while (n < 4); + } + + invert = FALSE; + if (*p == '^') { + p++; + invert = TRUE; + } + + for(;;) { + if (*p == ']') + break; + c1 = get_class_atom(s, cr1, &p, TRUE); + if ((int)c1 < 0) + goto fail; + if (*p == '-' && p[1] == ']' && s->unicode_sets) { + if (c1 >= CLASS_RANGE_BASE) + cr_free(cr1); + goto invalid_class_range; + } + if (*p == '-' && p[1] != ']') { + const uint8_t *p0 = p + 1; + if (c1 >= CLASS_RANGE_BASE) { + if (s->is_unicode) { + cr_free(cr1); + goto invalid_class_range; + } + /* Annex B: match '-' character */ + goto class_atom; + } + c2 = get_class_atom(s, cr1, &p0, TRUE); + if ((int)c2 < 0) + goto fail; + if (c2 >= CLASS_RANGE_BASE) { + cr_free(cr1); + if (s->is_unicode) { + goto invalid_class_range; + } + /* Annex B: match '-' character */ + goto class_atom; + } + p = p0; + if (c2 < c1) { + invalid_class_range: + re_parse_error(s, "invalid class range"); + goto fail; + } + if (cr_union_interval(cr, c1, c2)) + goto memory_error; + } else { + class_atom: + if (c1 >= CLASS_RANGE_BASE) { + int ret; + ret = cr_union1(cr, cr1->points, cr1->len); + cr_free(cr1); + if (ret) + goto memory_error; + } else { + if (cr_union_interval(cr, c1, c1)) + goto memory_error; + } + } + } + if (s->ignore_case) { + if (cr_canonicalize(cr)) + goto memory_error; + } + if (invert) { + if (cr_invert(cr)) + goto memory_error; + } + if (re_emit_range(s, cr)) + goto fail; + cr_free(cr); + p++; /* skip ']' */ + *pp = p; + return 0; + memory_error: + re_parse_out_of_memory(s); + fail: + cr_free(cr); + return -1; +} + +/* Return: + 1 if the opcodes in bc_buf[] always advance the character pointer. + 0 if the character pointer may not be advanced. + -1 if the code may depend on side effects of its previous execution (backreference) +*/ +static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len) +{ + int pos, opcode, ret, len, i; + uint32_t val, last; + BOOL has_back_reference; + uint8_t capture_bitmap[CAPTURE_COUNT_MAX]; + + ret = -2; /* not known yet */ + pos = 0; + has_back_reference = FALSE; + memset(capture_bitmap, 0, sizeof(capture_bitmap)); + + while (pos < bc_buf_len) { + opcode = bc_buf[pos]; + len = reopcode_info[opcode].size; + switch(opcode) { + case REOP_range: + val = get_u16(bc_buf + pos + 1); + len += val * 4; + goto simple_char; + case REOP_range32: + val = get_u16(bc_buf + pos + 1); + len += val * 8; + goto simple_char; + case REOP_char32: + case REOP_char16: + case REOP_char8: + case REOP_dot: + case REOP_any: + simple_char: + if (ret == -2) + ret = 1; + break; + case REOP_line_start: + case REOP_line_end: + case REOP_push_i32: + case REOP_push_char_pos: + case REOP_drop: + case REOP_word_boundary: + case REOP_not_word_boundary: + case REOP_prev: + /* no effect */ + break; + case REOP_save_start: + case REOP_save_end: + val = bc_buf[pos + 1]; + capture_bitmap[val] |= 1; + break; + case REOP_save_reset: + { + val = bc_buf[pos + 1]; + last = bc_buf[pos + 2]; + while (val < last) + capture_bitmap[val++] |= 1; + } + break; + case REOP_back_reference: + case REOP_backward_back_reference: + val = bc_buf[pos + 1]; + capture_bitmap[val] |= 2; + has_back_reference = TRUE; + break; + default: + /* safe behvior: we cannot predict the outcome */ + if (ret == -2) + ret = 0; + break; + } + pos += len; + } + if (has_back_reference) { + /* check if there is back reference which references a capture + made in the some code */ + for(i = 0; i < CAPTURE_COUNT_MAX; i++) { + if (capture_bitmap[i] == 3) + return -1; + } + } + if (ret == -2) + ret = 0; + return ret; +} + +/* return -1 if a simple quantifier cannot be used. Otherwise return + the number of characters in the atom. */ +static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len) +{ + int pos, opcode, len, count; + uint32_t val; + + count = 0; + pos = 0; + while (pos < bc_buf_len) { + opcode = bc_buf[pos]; + len = reopcode_info[opcode].size; + switch(opcode) { + case REOP_range: + val = get_u16(bc_buf + pos + 1); + len += val * 4; + goto simple_char; + case REOP_range32: + val = get_u16(bc_buf + pos + 1); + len += val * 8; + goto simple_char; + case REOP_char32: + case REOP_char16: + case REOP_char8: + case REOP_dot: + case REOP_any: + simple_char: + count++; + break; + case REOP_line_start: + case REOP_line_end: + case REOP_word_boundary: + case REOP_not_word_boundary: + break; + default: + return -1; + } + pos += len; + } + return count; +} + +/* '*pp' is the first char after '<' */ +static int re_parse_group_name(char *buf, int buf_size, const uint8_t **pp) +{ + const uint8_t *p, *p_next; + uint32_t c, d; + char *q; + + p = *pp; + q = buf; + for(;;) { + c = *p++; + if (c == '\\') { + if (*p != 'u') + return -1; + c = lre_parse_escape(&p, 2); // accept surrogate pairs + if ((int)c < 0) + return -1; + } else if (c == '>') { + break; + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) + return -1; + p = p_next; + if (is_hi_surrogate(c)) { + d = utf8_decode(p, &p_next); + if (is_lo_surrogate(d)) { + c = from_surrogate(c, d); + p = p_next; + } + } + } + if (q == buf) { + if (!lre_js_is_ident_first(c)) + return -1; + } else { + if (!lre_js_is_ident_next(c)) + return -1; + } + if ((q - buf + UTF8_CHAR_LEN_MAX + 1) > buf_size) + return -1; + if (c < 0x80) { + *q++ = c; + } else { + q += utf8_encode((uint8_t*)q, c); + } + } + if (q == buf) + return -1; + *q = '\0'; + *pp = p; + return 0; +} + +/* if capture_name = NULL: return the number of captures + 1. + Otherwise, return the capture index corresponding to capture_name + or -1 if none */ +static int re_parse_captures(REParseState *s, int *phas_named_captures, + const char *capture_name) +{ + const uint8_t *p; + int capture_index; + char name[TMP_BUF_SIZE]; + + capture_index = 1; + *phas_named_captures = 0; + for (p = s->buf_start; p < s->buf_end; p++) { + switch (*p) { + case '(': + if (p[1] == '?') { + if (p[2] == '<' && p[3] != '=' && p[3] != '!') { + *phas_named_captures = 1; + /* potential named capture */ + if (capture_name) { + p += 3; + if (re_parse_group_name(name, sizeof(name), &p) == 0) { + if (!strcmp(name, capture_name)) + return capture_index; + } + } + capture_index++; + if (capture_index >= CAPTURE_COUNT_MAX) + goto done; + } + } else { + capture_index++; + if (capture_index >= CAPTURE_COUNT_MAX) + goto done; + } + break; + case '\\': + p++; + break; + case '[': + for (p += 1 + (*p == ']'); p < s->buf_end && *p != ']'; p++) { + if (*p == '\\') + p++; + } + break; + } + } + done: + if (capture_name) + return -1; + else + return capture_index; +} + +static int re_count_captures(REParseState *s) +{ + if (s->total_capture_count < 0) { + s->total_capture_count = re_parse_captures(s, &s->has_named_captures, + NULL); + } + return s->total_capture_count; +} + +static BOOL re_has_named_captures(REParseState *s) +{ + if (s->has_named_captures < 0) + re_count_captures(s); + return s->has_named_captures; +} + +static int find_group_name(REParseState *s, const char *name) +{ + const char *p, *buf_end; + size_t len, name_len; + int capture_index; + + p = (char *)s->group_names.buf; + if (!p) return -1; + buf_end = (char *)s->group_names.buf + s->group_names.size; + name_len = strlen(name); + capture_index = 1; + while (p < buf_end) { + len = strlen(p); + if (len == name_len && memcmp(name, p, name_len) == 0) + return capture_index; + p += len + 1; + capture_index++; + } + return -1; +} + +static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir); + +static int re_parse_term(REParseState *s, BOOL is_backward_dir) +{ + const uint8_t *p; + int c, last_atom_start, quant_min, quant_max, last_capture_count; + BOOL greedy, add_zero_advance_check, is_neg, is_backward_lookahead; + CharRange cr_s, *cr = &cr_s; + + last_atom_start = -1; + last_capture_count = 0; + p = s->buf_ptr; + c = *p; + switch(c) { + case '^': + p++; + re_emit_op(s, REOP_line_start); + break; + case '$': + p++; + re_emit_op(s, REOP_line_end); + break; + case '.': + p++; + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + if (is_backward_dir) + re_emit_op(s, REOP_prev); + re_emit_op(s, s->dotall ? REOP_any : REOP_dot); + if (is_backward_dir) + re_emit_op(s, REOP_prev); + break; + case '{': + if (s->is_unicode) { + return re_parse_error(s, "syntax error"); + } else if (!lre_is_digit(p[1])) { + /* Annex B: we accept '{' not followed by digits as a + normal atom */ + goto parse_class_atom; + } else { + const uint8_t *p1 = p + 1; + /* Annex B: error if it is like a repetition count */ + parse_digits(&p1, TRUE); + if (*p1 == ',') { + p1++; + if (lre_is_digit(*p1)) { + parse_digits(&p1, TRUE); + } + } + if (*p1 != '}') { + goto parse_class_atom; + } + } + /* fall thru */ + case '*': + case '+': + case '?': + return re_parse_error(s, "nothing to repeat"); + case '(': + if (p[1] == '?') { + if (p[2] == ':') { + p += 3; + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + s->buf_ptr = p; + if (re_parse_disjunction(s, is_backward_dir)) + return -1; + p = s->buf_ptr; + if (re_parse_expect(s, &p, ')')) + return -1; + } else if ((p[2] == '=' || p[2] == '!')) { + is_neg = (p[2] == '!'); + is_backward_lookahead = FALSE; + p += 3; + goto lookahead; + } else if (p[2] == '<' && + (p[3] == '=' || p[3] == '!')) { + int pos; + is_neg = (p[3] == '!'); + is_backward_lookahead = TRUE; + p += 4; + /* lookahead */ + lookahead: + /* Annex B allows lookahead to be used as an atom for + the quantifiers */ + if (!s->is_unicode && !is_backward_lookahead) { + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + } + pos = re_emit_op_u32(s, REOP_lookahead + is_neg, 0); + s->buf_ptr = p; + if (re_parse_disjunction(s, is_backward_lookahead)) + return -1; + p = s->buf_ptr; + if (re_parse_expect(s, &p, ')')) + return -1; + re_emit_op(s, REOP_match); + /* jump after the 'match' after the lookahead is successful */ + if (dbuf_error(&s->byte_code)) + return -1; + put_u32(s->byte_code.buf + pos, s->byte_code.size - (pos + 4)); + } else if (p[2] == '<') { + p += 3; + if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), + &p)) { + return re_parse_error(s, "invalid group name"); + } + if (find_group_name(s, s->u.tmp_buf) > 0) { + return re_parse_error(s, "duplicate group name"); + } + /* group name with a trailing zero */ + dbuf_put(&s->group_names, (uint8_t *)s->u.tmp_buf, + strlen(s->u.tmp_buf) + 1); + s->has_named_captures = 1; + goto parse_capture; + } else { + return re_parse_error(s, "invalid group"); + } + } else { + int capture_index; + p++; + /* capture without group name */ + dbuf_putc(&s->group_names, 0); + parse_capture: + if (s->capture_count >= CAPTURE_COUNT_MAX) + return re_parse_error(s, "too many captures"); + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + capture_index = s->capture_count++; + re_emit_op_u8(s, REOP_save_start + is_backward_dir, + capture_index); + + s->buf_ptr = p; + if (re_parse_disjunction(s, is_backward_dir)) + return -1; + p = s->buf_ptr; + + re_emit_op_u8(s, REOP_save_start + 1 - is_backward_dir, + capture_index); + + if (re_parse_expect(s, &p, ')')) + return -1; + } + break; + case '\\': + switch(p[1]) { + case 'b': + case 'B': + re_emit_op(s, REOP_word_boundary + (p[1] != 'b')); + p += 2; + break; + case 'k': + { + const uint8_t *p1; + int dummy_res; + + p1 = p; + if (p1[2] != '<') { + /* annex B: we tolerate invalid group names in non + unicode mode if there is no named capture + definition */ + if (s->is_unicode || re_has_named_captures(s)) + return re_parse_error(s, "expecting group name"); + else + goto parse_class_atom; + } + p1 += 3; + if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), + &p1)) { + if (s->is_unicode || re_has_named_captures(s)) + return re_parse_error(s, "invalid group name"); + else + goto parse_class_atom; + } + c = find_group_name(s, s->u.tmp_buf); + if (c < 0) { + /* no capture name parsed before, try to look + after (inefficient, but hopefully not common */ + c = re_parse_captures(s, &dummy_res, s->u.tmp_buf); + if (c < 0) { + if (s->is_unicode || re_has_named_captures(s)) + return re_parse_error(s, "group name not defined"); + else + goto parse_class_atom; + } + } + p = p1; + } + goto emit_back_reference; + case '0': + p += 2; + c = 0; + if (s->is_unicode) { + if (lre_is_digit(*p)) { + return re_parse_error(s, "invalid decimal escape in regular expression"); + } + } else { + /* Annex B.1.4: accept legacy octal */ + if (*p >= '0' && *p <= '7') { + c = *p++ - '0'; + if (*p >= '0' && *p <= '7') { + c = (c << 3) + *p++ - '0'; + } + } + } + goto normal_char; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': + { + const uint8_t *q = ++p; + + c = parse_digits(&p, FALSE); + if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) { + if (!s->is_unicode) { + /* Annex B.1.4: accept legacy octal */ + p = q; + if (*p <= '7') { + c = 0; + if (*p <= '3') + c = *p++ - '0'; + if (*p >= '0' && *p <= '7') { + c = (c << 3) + *p++ - '0'; + if (*p >= '0' && *p <= '7') { + c = (c << 3) + *p++ - '0'; + } + } + } else { + c = *p++; + } + goto normal_char; + } + return re_parse_error(s, "back reference out of range in regular expression"); + } + emit_back_reference: + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + re_emit_op_u8(s, REOP_back_reference + is_backward_dir, c); + } + break; + default: + goto parse_class_atom; + } + break; + case '[': + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + if (is_backward_dir) + re_emit_op(s, REOP_prev); + if (re_parse_char_class(s, &p)) + return -1; + if (is_backward_dir) + re_emit_op(s, REOP_prev); + break; + case ']': + case '}': + if (s->is_unicode) + return re_parse_error(s, "syntax error"); + goto parse_class_atom; + default: + parse_class_atom: + c = get_class_atom(s, cr, &p, FALSE); + if ((int)c < 0) + return -1; + normal_char: + last_atom_start = s->byte_code.size; + last_capture_count = s->capture_count; + if (is_backward_dir) + re_emit_op(s, REOP_prev); + if (c >= CLASS_RANGE_BASE) { + int ret; + /* Note: canonicalization is not needed */ + ret = re_emit_range(s, cr); + cr_free(cr); + if (ret) + return -1; + } else { + if (s->ignore_case) + c = lre_canonicalize(c, s->is_unicode); + if (c <= 0x7f) + re_emit_op_u8(s, REOP_char8, c); + else if (c <= 0xffff) + re_emit_op_u16(s, REOP_char16, c); + else + re_emit_op_u32(s, REOP_char32, c); + } + if (is_backward_dir) + re_emit_op(s, REOP_prev); + break; + } + + /* quantifier */ + if (last_atom_start >= 0) { + c = *p; + switch(c) { + case '*': + p++; + quant_min = 0; + quant_max = INT32_MAX; + goto quantifier; + case '+': + p++; + quant_min = 1; + quant_max = INT32_MAX; + goto quantifier; + case '?': + p++; + quant_min = 0; + quant_max = 1; + goto quantifier; + case '{': + { + const uint8_t *p1 = p; + /* As an extension (see ES6 annex B), we accept '{' not + followed by digits as a normal atom */ + if (!lre_is_digit(p[1])) { + if (s->is_unicode) + goto invalid_quant_count; + break; + } + p++; + quant_min = parse_digits(&p, TRUE); + quant_max = quant_min; + if (*p == ',') { + p++; + if (lre_is_digit(*p)) { + quant_max = parse_digits(&p, TRUE); + if (quant_max < quant_min) { + invalid_quant_count: + return re_parse_error(s, "invalid repetition count"); + } + } else { + quant_max = INT32_MAX; /* infinity */ + } + } + if (*p != '}' && !s->is_unicode) { + /* Annex B: normal atom if invalid '{' syntax */ + p = p1; + break; + } + if (re_parse_expect(s, &p, '}')) + return -1; + } + quantifier: + greedy = TRUE; + if (*p == '?') { + p++; + greedy = FALSE; + } + if (last_atom_start < 0) { + return re_parse_error(s, "nothing to repeat"); + } + if (greedy) { + int len, pos; + + if (quant_max > 0) { + /* specific optimization for simple quantifiers */ + if (dbuf_error(&s->byte_code)) + goto out_of_memory; + len = re_is_simple_quantifier(s->byte_code.buf + last_atom_start, + s->byte_code.size - last_atom_start); + if (len > 0) { + re_emit_op(s, REOP_match); + + if (dbuf_insert(&s->byte_code, last_atom_start, 17)) + goto out_of_memory; + pos = last_atom_start; + s->byte_code.buf[pos++] = REOP_simple_greedy_quant; + put_u32(&s->byte_code.buf[pos], + s->byte_code.size - last_atom_start - 17); + pos += 4; + put_u32(&s->byte_code.buf[pos], quant_min); + pos += 4; + put_u32(&s->byte_code.buf[pos], quant_max); + pos += 4; + put_u32(&s->byte_code.buf[pos], len); + pos += 4; + goto done; + } + } + + if (dbuf_error(&s->byte_code)) + goto out_of_memory; + } + /* the spec tells that if there is no advance when + running the atom after the first quant_min times, + then there is no match. We remove this test when we + are sure the atom always advances the position. */ + add_zero_advance_check = (re_check_advance(s->byte_code.buf + last_atom_start, + s->byte_code.size - last_atom_start) == 0); + + { + int len, pos; + len = s->byte_code.size - last_atom_start; + if (quant_min == 0) { + /* need to reset the capture in case the atom is + not executed */ + if (last_capture_count != s->capture_count) { + if (dbuf_insert(&s->byte_code, last_atom_start, 3)) + goto out_of_memory; + s->byte_code.buf[last_atom_start++] = REOP_save_reset; + s->byte_code.buf[last_atom_start++] = last_capture_count; + s->byte_code.buf[last_atom_start++] = s->capture_count - 1; + } + if (quant_max == 0) { + s->byte_code.size = last_atom_start; + } else if (quant_max == 1) { + if (dbuf_insert(&s->byte_code, last_atom_start, 5)) + goto out_of_memory; + s->byte_code.buf[last_atom_start] = REOP_split_goto_first + + greedy; + put_u32(s->byte_code.buf + last_atom_start + 1, len); + } else if (quant_max == INT32_MAX) { + if (dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check)) + goto out_of_memory; + s->byte_code.buf[last_atom_start] = REOP_split_goto_first + + greedy; + put_u32(s->byte_code.buf + last_atom_start + 1, + len + 5 + add_zero_advance_check); + if (add_zero_advance_check) { + /* avoid infinite loop by stoping the + recursion if no advance was made in the + atom (only works if the atom has no + side effect) */ + s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos; + re_emit_goto(s, REOP_bne_char_pos, last_atom_start); + } else { + re_emit_goto(s, REOP_goto, last_atom_start); + } + } else { + if (dbuf_insert(&s->byte_code, last_atom_start, 10)) + goto out_of_memory; + pos = last_atom_start; + s->byte_code.buf[pos++] = REOP_push_i32; + put_u32(s->byte_code.buf + pos, quant_max); + pos += 4; + s->byte_code.buf[pos++] = REOP_split_goto_first + greedy; + put_u32(s->byte_code.buf + pos, len + 5); + re_emit_goto(s, REOP_loop, last_atom_start + 5); + re_emit_op(s, REOP_drop); + } + } else if (quant_min == 1 && quant_max == INT32_MAX && + !add_zero_advance_check) { + re_emit_goto(s, REOP_split_next_first - greedy, + last_atom_start); + } else { + if (quant_min == 1) { + /* nothing to add */ + } else { + if (dbuf_insert(&s->byte_code, last_atom_start, 5)) + goto out_of_memory; + s->byte_code.buf[last_atom_start] = REOP_push_i32; + put_u32(s->byte_code.buf + last_atom_start + 1, + quant_min); + last_atom_start += 5; + re_emit_goto(s, REOP_loop, last_atom_start); + re_emit_op(s, REOP_drop); + } + if (quant_max == INT32_MAX) { + pos = s->byte_code.size; + re_emit_op_u32(s, REOP_split_goto_first + greedy, + len + 5 + add_zero_advance_check); + if (add_zero_advance_check) + re_emit_op(s, REOP_push_char_pos); + /* copy the atom */ + dbuf_put_self(&s->byte_code, last_atom_start, len); + if (add_zero_advance_check) + re_emit_goto(s, REOP_bne_char_pos, pos); + else + re_emit_goto(s, REOP_goto, pos); + } else if (quant_max > quant_min) { + re_emit_op_u32(s, REOP_push_i32, quant_max - quant_min); + pos = s->byte_code.size; + re_emit_op_u32(s, REOP_split_goto_first + greedy, len + 5); + /* copy the atom */ + dbuf_put_self(&s->byte_code, last_atom_start, len); + + re_emit_goto(s, REOP_loop, pos); + re_emit_op(s, REOP_drop); + } + } + last_atom_start = -1; + } + break; + default: + break; + } + } + done: + s->buf_ptr = p; + return 0; + out_of_memory: + return re_parse_out_of_memory(s); +} + +static int re_parse_alternative(REParseState *s, BOOL is_backward_dir) +{ + const uint8_t *p; + int ret; + size_t start, term_start, end, term_size; + + start = s->byte_code.size; + for(;;) { + p = s->buf_ptr; + if (p >= s->buf_end) + break; + if (*p == '|' || *p == ')') + break; + term_start = s->byte_code.size; + ret = re_parse_term(s, is_backward_dir); + if (ret) + return ret; + if (is_backward_dir) { + /* reverse the order of the terms (XXX: inefficient, but + speed is not really critical here) */ + end = s->byte_code.size; + term_size = end - term_start; + if (dbuf_realloc(&s->byte_code, end + term_size)) + return -1; + memmove(s->byte_code.buf + start + term_size, + s->byte_code.buf + start, + end - start); + memcpy(s->byte_code.buf + start, s->byte_code.buf + end, + term_size); + } + } + return 0; +} + +static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir) +{ + int start, len, pos; + + if (lre_check_stack_overflow(s->opaque, 0)) + return re_parse_error(s, "stack overflow"); + + start = s->byte_code.size; + if (re_parse_alternative(s, is_backward_dir)) + return -1; + while (*s->buf_ptr == '|') { + s->buf_ptr++; + + len = s->byte_code.size - start; + + /* insert a split before the first alternative */ + if (dbuf_insert(&s->byte_code, start, 5)) { + return re_parse_out_of_memory(s); + } + s->byte_code.buf[start] = REOP_split_next_first; + put_u32(s->byte_code.buf + start + 1, len + 5); + + pos = re_emit_op_u32(s, REOP_goto, 0); + + if (re_parse_alternative(s, is_backward_dir)) + return -1; + + /* patch the goto */ + len = s->byte_code.size - (pos + 4); + put_u32(s->byte_code.buf + pos, len); + } + return 0; +} + +/* the control flow is recursive so the analysis can be linear */ +static int lre_compute_stack_size(const uint8_t *bc_buf, int bc_buf_len) +{ + int stack_size, stack_size_max, pos, opcode, len; + uint32_t val; + + stack_size = 0; + stack_size_max = 0; + bc_buf += RE_HEADER_LEN; + bc_buf_len -= RE_HEADER_LEN; + pos = 0; + while (pos < bc_buf_len) { + opcode = bc_buf[pos]; + len = reopcode_info[opcode].size; + assert(opcode < REOP_COUNT); + assert((pos + len) <= bc_buf_len); + switch(opcode) { + case REOP_push_i32: + case REOP_push_char_pos: + stack_size++; + if (stack_size > stack_size_max) { + if (stack_size > STACK_SIZE_MAX) + return -1; + stack_size_max = stack_size; + } + break; + case REOP_drop: + case REOP_bne_char_pos: + assert(stack_size > 0); + stack_size--; + break; + case REOP_range: + val = get_u16(bc_buf + pos + 1); + len += val * 4; + break; + case REOP_range32: + val = get_u16(bc_buf + pos + 1); + len += val * 8; + break; + } + pos += len; + } + return stack_size_max; +} + +/* 'buf' must be a zero terminated UTF-8 string of length buf_len. + Return NULL if error and allocate an error message in *perror_msg, + otherwise the compiled bytecode and its length in plen. +*/ +uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, + const char *buf, size_t buf_len, int re_flags, + void *opaque) +{ + REParseState s_s, *s = &s_s; + int stack_size; + BOOL is_sticky; + + memset(s, 0, sizeof(*s)); + s->opaque = opaque; + s->buf_ptr = (const uint8_t *)buf; + s->buf_end = s->buf_ptr + buf_len; + s->buf_start = s->buf_ptr; + s->re_flags = re_flags; + s->is_unicode = ((re_flags & LRE_FLAG_UNICODE) != 0); + is_sticky = ((re_flags & LRE_FLAG_STICKY) != 0); + s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0); + s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0); + s->unicode_sets = ((re_flags & LRE_FLAG_UNICODE_SETS) != 0); + s->capture_count = 1; + s->total_capture_count = -1; + s->has_named_captures = -1; + + dbuf_init2(&s->byte_code, opaque, lre_realloc); + dbuf_init2(&s->group_names, opaque, lre_realloc); + + dbuf_put_u16(&s->byte_code, re_flags); /* first element is the flags */ + dbuf_putc(&s->byte_code, 0); /* second element is the number of captures */ + dbuf_putc(&s->byte_code, 0); /* stack size */ + dbuf_put_u32(&s->byte_code, 0); /* bytecode length */ + + if (!is_sticky) { + /* iterate thru all positions (about the same as .*?( ... ) ) + . We do it without an explicit loop so that lock step + thread execution will be possible in an optimized + implementation */ + re_emit_op_u32(s, REOP_split_goto_first, 1 + 5); + re_emit_op(s, REOP_any); + re_emit_op_u32(s, REOP_goto, -(5 + 1 + 5)); + } + re_emit_op_u8(s, REOP_save_start, 0); + + if (re_parse_disjunction(s, FALSE)) { + error: + dbuf_free(&s->byte_code); + dbuf_free(&s->group_names); + pstrcpy(error_msg, error_msg_size, s->u.error_msg); + *plen = 0; + return NULL; + } + + re_emit_op_u8(s, REOP_save_end, 0); + + re_emit_op(s, REOP_match); + + if (*s->buf_ptr != '\0') { + re_parse_error(s, "extraneous characters at the end"); + goto error; + } + + if (dbuf_error(&s->byte_code)) { + re_parse_out_of_memory(s); + goto error; + } + + stack_size = lre_compute_stack_size(s->byte_code.buf, s->byte_code.size); + if (stack_size < 0) { + re_parse_error(s, "too many imbricated quantifiers"); + goto error; + } + + s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count; + s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size; + put_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN, + s->byte_code.size - RE_HEADER_LEN); + + /* add the named groups if needed */ + if (s->group_names.size > (s->capture_count - 1)) { + dbuf_put(&s->byte_code, s->group_names.buf, s->group_names.size); + put_u16(s->byte_code.buf + RE_HEADER_FLAGS, + LRE_FLAG_NAMED_GROUPS | lre_get_flags(s->byte_code.buf)); + } + dbuf_free(&s->group_names); + +#ifdef DUMP_REOP + lre_dump_bytecode(s->byte_code.buf, s->byte_code.size); +#endif + + error_msg[0] = '\0'; + *plen = s->byte_code.size; + return s->byte_code.buf; +} + +static BOOL is_line_terminator(uint32_t c) +{ + return (c == '\n' || c == '\r' || c == CP_LS || c == CP_PS); +} + +static BOOL is_word_char(uint32_t c) +{ + return ((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c == '_')); +} + +#define GET_CHAR(c, cptr, cbuf_end, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + c = *cptr++; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr; \ + const uint16_t *_end = (const uint16_t *)cbuf_end; \ + c = *_p++; \ + if (is_hi_surrogate(c)) \ + if (cbuf_type == 2) \ + if (_p < _end) \ + if (is_lo_surrogate(*_p)) \ + c = from_surrogate(c, *_p++); \ + cptr = (const void *)_p; \ + } \ + } while (0) + +#define PEEK_CHAR(c, cptr, cbuf_end, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + c = cptr[0]; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr; \ + const uint16_t *_end = (const uint16_t *)cbuf_end; \ + c = *_p++; \ + if (is_hi_surrogate(c)) \ + if (cbuf_type == 2) \ + if (_p < _end) \ + if (is_lo_surrogate(*_p)) \ + c = from_surrogate(c, *_p); \ + } \ + } while (0) + +#define PEEK_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + c = cptr[-1]; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr - 1; \ + const uint16_t *_start = (const uint16_t *)cbuf_start; \ + c = *_p; \ + if (is_lo_surrogate(c)) \ + if (cbuf_type == 2) \ + if (_p > _start) \ + if (is_hi_surrogate(_p[-1])) \ + c = from_surrogate(*--_p, c); \ + } \ + } while (0) + +#define GET_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + cptr--; \ + c = cptr[0]; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr - 1; \ + const uint16_t *_start = (const uint16_t *)cbuf_start; \ + c = *_p; \ + if (is_lo_surrogate(c)) \ + if (cbuf_type == 2) \ + if (_p > _start) \ + if (is_hi_surrogate(_p[-1])) \ + c = from_surrogate(*--_p, c); \ + cptr = (const void *)_p; \ + } \ + } while (0) + +#define PREV_CHAR(cptr, cbuf_start, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + cptr--; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr - 1; \ + const uint16_t *_start = (const uint16_t *)cbuf_start; \ + if (is_lo_surrogate(*_p)) \ + if (cbuf_type == 2) \ + if (_p > _start) \ + if (is_hi_surrogate(_p[-1])) \ + _p--; \ + cptr = (const void *)_p; \ + } \ + } while (0) + +typedef uintptr_t StackInt; + +typedef enum { + RE_EXEC_STATE_SPLIT, + RE_EXEC_STATE_LOOKAHEAD, + RE_EXEC_STATE_NEGATIVE_LOOKAHEAD, + RE_EXEC_STATE_GREEDY_QUANT, +} REExecStateEnum; + +typedef struct REExecState { + REExecStateEnum type : 8; + uint8_t stack_len; + size_t count; /* only used for RE_EXEC_STATE_GREEDY_QUANT */ + const uint8_t *cptr; + const uint8_t *pc; + void *buf[]; +} REExecState; + +typedef struct { + const uint8_t *cbuf; + const uint8_t *cbuf_end; + /* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16 */ + int cbuf_type; + int capture_count; + int stack_size_max; + BOOL multi_line; + BOOL ignore_case; + BOOL is_unicode; + void *opaque; /* used for stack overflow check */ + + size_t state_size; + uint8_t *state_stack; + size_t state_stack_size; + size_t state_stack_len; +} REExecContext; + +static int push_state(REExecContext *s, + uint8_t **capture, + StackInt *stack, size_t stack_len, + const uint8_t *pc, const uint8_t *cptr, + REExecStateEnum type, size_t count) +{ + REExecState *rs; + uint8_t *new_stack; + size_t new_size, i, n; + StackInt *stack_buf; + + if (unlikely((s->state_stack_len + 1) > s->state_stack_size)) { + /* reallocate the stack */ + new_size = s->state_stack_size * 3 / 2; + if (new_size < 8) + new_size = 8; + new_stack = lre_realloc(s->opaque, s->state_stack, new_size * s->state_size); + if (!new_stack) + return -1; + s->state_stack_size = new_size; + s->state_stack = new_stack; + } + rs = (REExecState *)(s->state_stack + s->state_stack_len * s->state_size); + s->state_stack_len++; + rs->type = type; + rs->count = count; + rs->stack_len = stack_len; + rs->cptr = cptr; + rs->pc = pc; + n = 2 * s->capture_count; + for(i = 0; i < n; i++) + rs->buf[i] = capture[i]; + stack_buf = (StackInt *)(rs->buf + n); + for(i = 0; i < stack_len; i++) + stack_buf[i] = stack[i]; + return 0; +} + +/* return 1 if match, 0 if not match or -1 if error. */ +static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, + StackInt *stack, int stack_len, + const uint8_t *pc, const uint8_t *cptr, + BOOL no_recurse) +{ + int opcode, ret; + int cbuf_type; + uint32_t val, c; + const uint8_t *cbuf_end; + + cbuf_type = s->cbuf_type; + cbuf_end = s->cbuf_end; + + for(;;) { + // printf("top=%p: pc=%d\n", th_list.top, (int)(pc - (bc_buf + RE_HEADER_LEN))); + opcode = *pc++; + switch(opcode) { + case REOP_match: + { + REExecState *rs; + if (no_recurse) + return (intptr_t)cptr; + ret = 1; + goto recurse; + no_match: + if (no_recurse) + return 0; + ret = 0; + recurse: + for(;;) { + if (s->state_stack_len == 0) + return ret; + rs = (REExecState *)(s->state_stack + + (s->state_stack_len - 1) * s->state_size); + if (rs->type == RE_EXEC_STATE_SPLIT) { + if (!ret) { + pop_state: + memcpy(capture, rs->buf, + sizeof(capture[0]) * 2 * s->capture_count); + pop_state1: + pc = rs->pc; + cptr = rs->cptr; + stack_len = rs->stack_len; + memcpy(stack, rs->buf + 2 * s->capture_count, + stack_len * sizeof(stack[0])); + s->state_stack_len--; + break; + } + } else if (rs->type == RE_EXEC_STATE_GREEDY_QUANT) { + if (!ret) { + uint32_t char_count, i; + memcpy(capture, rs->buf, + sizeof(capture[0]) * 2 * s->capture_count); + stack_len = rs->stack_len; + memcpy(stack, rs->buf + 2 * s->capture_count, + stack_len * sizeof(stack[0])); + pc = rs->pc; + cptr = rs->cptr; + /* go backward */ + char_count = get_u32(pc + 12); + for(i = 0; i < char_count; i++) { + PREV_CHAR(cptr, s->cbuf, cbuf_type); + } + pc = (pc + 16) + (int)get_u32(pc); + rs->cptr = cptr; + rs->count--; + if (rs->count == 0) { + s->state_stack_len--; + } + break; + } + } else { + ret = ((rs->type == RE_EXEC_STATE_LOOKAHEAD && ret) || + (rs->type == RE_EXEC_STATE_NEGATIVE_LOOKAHEAD && !ret)); + if (ret) { + /* keep the capture in case of positive lookahead */ + if (rs->type == RE_EXEC_STATE_LOOKAHEAD) + goto pop_state1; + else + goto pop_state; + } + } + s->state_stack_len--; + } + } + break; + case REOP_char32: + val = get_u32(pc); + pc += 4; + goto test_char; + case REOP_char16: + val = get_u16(pc); + pc += 2; + goto test_char; + case REOP_char8: + val = get_u8(pc); + pc += 1; + test_char: + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end, cbuf_type); + if (s->ignore_case) { + c = lre_canonicalize(c, s->is_unicode); + } + if (val != c) + goto no_match; + break; + case REOP_split_goto_first: + case REOP_split_next_first: + { + const uint8_t *pc1; + + val = get_u32(pc); + pc += 4; + if (opcode == REOP_split_next_first) { + pc1 = pc + (int)val; + } else { + pc1 = pc; + pc = pc + (int)val; + } + ret = push_state(s, capture, stack, stack_len, + pc1, cptr, RE_EXEC_STATE_SPLIT, 0); + if (ret < 0) + return -1; + break; + } + case REOP_lookahead: + case REOP_negative_lookahead: + val = get_u32(pc); + pc += 4; + ret = push_state(s, capture, stack, stack_len, + pc + (int)val, cptr, + RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead, + 0); + if (ret < 0) + return -1; + break; + + case REOP_goto: + val = get_u32(pc); + pc += 4 + (int)val; + break; + case REOP_line_start: + if (cptr == s->cbuf) + break; + if (!s->multi_line) + goto no_match; + PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); + if (!is_line_terminator(c)) + goto no_match; + break; + case REOP_line_end: + if (cptr == cbuf_end) + break; + if (!s->multi_line) + goto no_match; + PEEK_CHAR(c, cptr, cbuf_end, cbuf_type); + if (!is_line_terminator(c)) + goto no_match; + break; + case REOP_dot: + if (cptr == cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end, cbuf_type); + if (is_line_terminator(c)) + goto no_match; + break; + case REOP_any: + if (cptr == cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end, cbuf_type); + break; + case REOP_save_start: + case REOP_save_end: + val = *pc++; + assert(val < s->capture_count); + capture[2 * val + opcode - REOP_save_start] = (uint8_t *)cptr; + break; + case REOP_save_reset: + { + uint32_t val2; + val = pc[0]; + val2 = pc[1]; + pc += 2; + assert(val2 < s->capture_count); + while (val <= val2) { + capture[2 * val] = NULL; + capture[2 * val + 1] = NULL; + val++; + } + } + break; + case REOP_push_i32: + val = get_u32(pc); + pc += 4; + stack[stack_len++] = val; + break; + case REOP_drop: + stack_len--; + break; + case REOP_loop: + val = get_u32(pc); + pc += 4; + if (--stack[stack_len - 1] != 0) { + pc += (int)val; + } + break; + case REOP_push_char_pos: + stack[stack_len++] = (uintptr_t)cptr; + break; + case REOP_bne_char_pos: + val = get_u32(pc); + pc += 4; + if (stack[--stack_len] != (uintptr_t)cptr) + pc += (int)val; + break; + case REOP_word_boundary: + case REOP_not_word_boundary: + { + BOOL v1, v2; + /* char before */ + if (cptr == s->cbuf) { + v1 = FALSE; + } else { + PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); + v1 = is_word_char(c); + } + /* current char */ + if (cptr >= cbuf_end) { + v2 = FALSE; + } else { + PEEK_CHAR(c, cptr, cbuf_end, cbuf_type); + v2 = is_word_char(c); + } + if (v1 ^ v2 ^ (REOP_not_word_boundary - opcode)) + goto no_match; + } + break; + case REOP_back_reference: + case REOP_backward_back_reference: + { + const uint8_t *cptr1, *cptr1_end, *cptr1_start; + uint32_t c1, c2; + + val = *pc++; + if (val >= s->capture_count) + goto no_match; + cptr1_start = capture[2 * val]; + cptr1_end = capture[2 * val + 1]; + if (!cptr1_start || !cptr1_end) + break; + if (opcode == REOP_back_reference) { + cptr1 = cptr1_start; + while (cptr1 < cptr1_end) { + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c1, cptr1, cptr1_end, cbuf_type); + GET_CHAR(c2, cptr, cbuf_end, cbuf_type); + if (s->ignore_case) { + c1 = lre_canonicalize(c1, s->is_unicode); + c2 = lre_canonicalize(c2, s->is_unicode); + } + if (c1 != c2) + goto no_match; + } + } else { + cptr1 = cptr1_end; + while (cptr1 > cptr1_start) { + if (cptr == s->cbuf) + goto no_match; + GET_PREV_CHAR(c1, cptr1, cptr1_start, cbuf_type); + GET_PREV_CHAR(c2, cptr, s->cbuf, cbuf_type); + if (s->ignore_case) { + c1 = lre_canonicalize(c1, s->is_unicode); + c2 = lre_canonicalize(c2, s->is_unicode); + } + if (c1 != c2) + goto no_match; + } + } + } + break; + case REOP_range: + { + int n; + uint32_t low, high, idx_min, idx_max, idx; + + n = get_u16(pc); /* n must be >= 1 */ + pc += 2; + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end, cbuf_type); + if (s->ignore_case) { + c = lre_canonicalize(c, s->is_unicode); + } + idx_min = 0; + low = get_u16(pc + 0 * 4); + if (c < low) + goto no_match; + idx_max = n - 1; + high = get_u16(pc + idx_max * 4 + 2); + /* 0xffff in for last value means +infinity */ + if (unlikely(c >= 0xffff) && high == 0xffff) + goto range_match; + if (c > high) + goto no_match; + while (idx_min <= idx_max) { + idx = (idx_min + idx_max) / 2; + low = get_u16(pc + idx * 4); + high = get_u16(pc + idx * 4 + 2); + if (c < low) + idx_max = idx - 1; + else if (c > high) + idx_min = idx + 1; + else + goto range_match; + } + goto no_match; + range_match: + pc += 4 * n; + } + break; + case REOP_range32: + { + int n; + uint32_t low, high, idx_min, idx_max, idx; + + n = get_u16(pc); /* n must be >= 1 */ + pc += 2; + if (cptr >= cbuf_end) + goto no_match; + GET_CHAR(c, cptr, cbuf_end, cbuf_type); + if (s->ignore_case) { + c = lre_canonicalize(c, s->is_unicode); + } + idx_min = 0; + low = get_u32(pc + 0 * 8); + if (c < low) + goto no_match; + idx_max = n - 1; + high = get_u32(pc + idx_max * 8 + 4); + if (c > high) + goto no_match; + while (idx_min <= idx_max) { + idx = (idx_min + idx_max) / 2; + low = get_u32(pc + idx * 8); + high = get_u32(pc + idx * 8 + 4); + if (c < low) + idx_max = idx - 1; + else if (c > high) + idx_min = idx + 1; + else + goto range32_match; + } + goto no_match; + range32_match: + pc += 8 * n; + } + break; + case REOP_prev: + /* go to the previous char */ + if (cptr == s->cbuf) + goto no_match; + PREV_CHAR(cptr, s->cbuf, cbuf_type); + break; + case REOP_simple_greedy_quant: + { + uint32_t next_pos, quant_min, quant_max; + size_t q; + intptr_t res; + const uint8_t *pc1; + + next_pos = get_u32(pc); + quant_min = get_u32(pc + 4); + quant_max = get_u32(pc + 8); + pc += 16; + pc1 = pc; + pc += (int)next_pos; + + q = 0; + for(;;) { + res = lre_exec_backtrack(s, capture, stack, stack_len, + pc1, cptr, TRUE); + if (res == -1) + return res; + if (!res) + break; + cptr = (uint8_t *)res; + q++; + if (q >= quant_max && quant_max != INT32_MAX) + break; + } + if (q < quant_min) + goto no_match; + if (q > quant_min) { + /* will examine all matches down to quant_min */ + ret = push_state(s, capture, stack, stack_len, + pc1 - 16, cptr, + RE_EXEC_STATE_GREEDY_QUANT, + q - quant_min); + if (ret < 0) + return -1; + } + } + break; + default: + abort(); + } + } +} + +/* Return 1 if match, 0 if not match or -1 if error. cindex is the + starting position of the match and must be such as 0 <= cindex <= + clen. */ +int lre_exec(uint8_t **capture, + const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, + int cbuf_type, void *opaque) +{ + REExecContext s_s, *s = &s_s; + int re_flags, i, alloca_size, ret; + StackInt *stack_buf; + + re_flags = lre_get_flags(bc_buf); + s->multi_line = (re_flags & LRE_FLAG_MULTILINE) != 0; + s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0; + s->is_unicode = (re_flags & LRE_FLAG_UNICODE) != 0; + s->capture_count = bc_buf[RE_HEADER_CAPTURE_COUNT]; + s->stack_size_max = bc_buf[RE_HEADER_STACK_SIZE]; + s->cbuf = cbuf; + s->cbuf_end = cbuf + (clen << cbuf_type); + s->cbuf_type = cbuf_type; + if (s->cbuf_type == 1 && s->is_unicode) + s->cbuf_type = 2; + s->opaque = opaque; + + s->state_size = sizeof(REExecState) + + s->capture_count * sizeof(capture[0]) * 2 + + s->stack_size_max * sizeof(stack_buf[0]); + s->state_stack = NULL; + s->state_stack_len = 0; + s->state_stack_size = 0; + + for(i = 0; i < s->capture_count * 2; i++) + capture[i] = NULL; + alloca_size = s->stack_size_max * sizeof(stack_buf[0]); + stack_buf = alloca(alloca_size); + ret = lre_exec_backtrack(s, capture, stack_buf, 0, bc_buf + RE_HEADER_LEN, + cbuf + (cindex << cbuf_type), FALSE); + lre_realloc(s->opaque, s->state_stack, 0); + return ret; +} + +int lre_get_capture_count(const uint8_t *bc_buf) +{ + return bc_buf[RE_HEADER_CAPTURE_COUNT]; +} + +int lre_get_flags(const uint8_t *bc_buf) +{ + return get_u16(bc_buf + RE_HEADER_FLAGS); +} + +/* Return NULL if no group names. Otherwise, return a pointer to + 'capture_count - 1' zero terminated UTF-8 strings. */ +const char *lre_get_groupnames(const uint8_t *bc_buf) +{ + uint32_t re_bytecode_len; + if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0) + return NULL; + re_bytecode_len = get_u32(bc_buf + RE_HEADER_BYTECODE_LEN); + return (const char *)(bc_buf + RE_HEADER_LEN + re_bytecode_len); +} + +void lre_byte_swap(uint8_t *buf, size_t len, BOOL is_byte_swapped) +{ + uint8_t *p, *pe; + uint32_t n, r, nw; + + p = buf; + if (len < RE_HEADER_LEN) + abort(); + + // format is: + //
+ // + // + // + // etc. + inplace_bswap16(&p[RE_HEADER_FLAGS]); + + n = get_u32(&p[RE_HEADER_BYTECODE_LEN]); + inplace_bswap32(&p[RE_HEADER_BYTECODE_LEN]); + if (is_byte_swapped) + n = bswap32(n); + if (n > len - RE_HEADER_LEN) + abort(); + + p = &buf[RE_HEADER_LEN]; + pe = &p[n]; + + while (p < pe) { + n = reopcode_info[*p].size; + switch (n) { + case 1: + case 2: + break; + case 3: + switch (*p) { + case REOP_save_reset: // has two 8 bit arguments + break; + case REOP_range32: // variable length + nw = get_u16(&p[1]); // number of pairs of uint32_t + if (is_byte_swapped) + n = bswap16(n); + for (r = 3 + 8 * nw; n < r; n += 4) + inplace_bswap32(&p[n]); + goto doswap16; + case REOP_range: // variable length + nw = get_u16(&p[1]); // number of pairs of uint16_t + if (is_byte_swapped) + n = bswap16(n); + for (r = 3 + 4 * nw; n < r; n += 2) + inplace_bswap16(&p[n]); + goto doswap16; + default: + doswap16: + inplace_bswap16(&p[1]); + break; + } + break; + case 5: + inplace_bswap32(&p[1]); + break; + case 17: + assert(*p == REOP_simple_greedy_quant); + inplace_bswap32(&p[1]); + inplace_bswap32(&p[5]); + inplace_bswap32(&p[9]); + inplace_bswap32(&p[13]); + break; + default: + abort(); + } + p = &p[n]; + } +} + +#ifdef TEST + +BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size) +{ + return FALSE; +} + +void *lre_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +int main(int argc, char **argv) +{ + int len, flags, ret, i; + uint8_t *bc; + char error_msg[64]; + uint8_t *capture[CAPTURE_COUNT_MAX * 2]; + const char *input; + int input_len, capture_count; + + if (argc < 4) { + printf("usage: %s regexp flags input\n", argv[0]); + exit(1); + } + flags = atoi(argv[2]); + bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1], + strlen(argv[1]), flags, NULL); + if (!bc) { + fprintf(stderr, "error: %s\n", error_msg); + exit(1); + } + + input = argv[3]; + input_len = strlen(input); + + ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL); + printf("ret=%d\n", ret); + if (ret == 1) { + capture_count = lre_get_capture_count(bc); + for(i = 0; i < 2 * capture_count; i++) { + uint8_t *ptr; + ptr = capture[i]; + printf("%d: ", i); + if (!ptr) + printf(""); + else + printf("%u", (int)(ptr - (uint8_t *)input)); + printf("\n"); + } + } + return 0; +} +#endif diff --git a/armorcore/sources/libs/quickjs/libregexp.h b/armorcore/sources/libs/quickjs/libregexp.h new file mode 100644 index 000000000..c2e664a24 --- /dev/null +++ b/armorcore/sources/libs/quickjs/libregexp.h @@ -0,0 +1,95 @@ +/* + * Regular Expression Engine + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBREGEXP_H +#define LIBREGEXP_H + +#include + +#include "libunicode.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define LRE_BOOL int /* for documentation purposes */ + +#define LRE_FLAG_GLOBAL (1 << 0) +#define LRE_FLAG_IGNORECASE (1 << 1) +#define LRE_FLAG_MULTILINE (1 << 2) +#define LRE_FLAG_DOTALL (1 << 3) +#define LRE_FLAG_UNICODE (1 << 4) +#define LRE_FLAG_STICKY (1 << 5) +#define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */ +#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */ +#define LRE_FLAG_UNICODE_SETS (1 << 8) + +uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, + const char *buf, size_t buf_len, int re_flags, + void *opaque); +int lre_get_capture_count(const uint8_t *bc_buf); +int lre_get_flags(const uint8_t *bc_buf); +const char *lre_get_groupnames(const uint8_t *bc_buf); +int lre_exec(uint8_t **capture, + const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen, + int cbuf_type, void *opaque); + +int lre_parse_escape(const uint8_t **pp, int allow_utf16); +LRE_BOOL lre_is_space(int c); + +void lre_byte_swap(uint8_t *buf, size_t len, LRE_BOOL is_byte_swapped); + +/* must be provided by the user */ +LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); +void *lre_realloc(void *opaque, void *ptr, size_t size); + +/* JS identifier test */ +extern uint32_t const lre_id_start_table_ascii[4]; +extern uint32_t const lre_id_continue_table_ascii[4]; + +static inline int lre_js_is_ident_first(int c) +{ + if ((uint32_t)c < 128) { + return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { + return lre_is_id_start(c); + } +} + +static inline int lre_js_is_ident_next(int c) +{ + if ((uint32_t)c < 128) { + return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1; + } else { + /* ZWNJ and ZWJ are accepted in identifiers */ + return lre_is_id_continue(c) || c == 0x200C || c == 0x200D; + } +} + +#undef LRE_BOOL + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIBREGEXP_H */ diff --git a/armorcore/sources/libs/quickjs/libunicode-table.h b/armorcore/sources/libs/quickjs/libunicode-table.h new file mode 100644 index 000000000..a7b1e9d73 --- /dev/null +++ b/armorcore/sources/libs/quickjs/libunicode-table.h @@ -0,0 +1,4484 @@ +/* Compressed unicode tables */ +/* Automatically generated file - do not edit */ + +#include + +static const uint32_t case_conv_table1[370] = { + 0x00209a30, 0x00309a00, 0x005a8173, 0x00601730, + 0x006c0730, 0x006f81b3, 0x00701700, 0x007c0700, + 0x007f8100, 0x00803040, 0x009801c3, 0x00988190, + 0x00990640, 0x009c9040, 0x00a481b4, 0x00a52e40, + 0x00bc0130, 0x00bc8640, 0x00bf8170, 0x00c00100, + 0x00c08130, 0x00c10440, 0x00c30130, 0x00c38240, + 0x00c48230, 0x00c58240, 0x00c70130, 0x00c78130, + 0x00c80130, 0x00c88240, 0x00c98130, 0x00ca0130, + 0x00ca8100, 0x00cb0130, 0x00cb8130, 0x00cc0240, + 0x00cd0100, 0x00ce0130, 0x00ce8130, 0x00cf0100, + 0x00cf8130, 0x00d00640, 0x00d30130, 0x00d38240, + 0x00d48130, 0x00d60240, 0x00d70130, 0x00d78240, + 0x00d88230, 0x00d98440, 0x00db8130, 0x00dc0240, + 0x00de0240, 0x00df8100, 0x00e20350, 0x00e38350, + 0x00e50350, 0x00e69040, 0x00ee8100, 0x00ef1240, + 0x00f801b4, 0x00f88350, 0x00fa0240, 0x00fb0130, + 0x00fb8130, 0x00fc2840, 0x01100130, 0x01111240, + 0x011d0131, 0x011d8240, 0x011e8130, 0x011f0131, + 0x011f8201, 0x01208240, 0x01218130, 0x01220130, + 0x01228130, 0x01230a40, 0x01280101, 0x01288101, + 0x01290101, 0x01298100, 0x012a0100, 0x012b0200, + 0x012c8100, 0x012d8100, 0x012e0101, 0x01300100, + 0x01308101, 0x01318100, 0x01328101, 0x01330101, + 0x01340100, 0x01348100, 0x01350101, 0x01358101, + 0x01360101, 0x01378100, 0x01388101, 0x01390100, + 0x013a8100, 0x013e8101, 0x01400100, 0x01410101, + 0x01418100, 0x01438101, 0x01440100, 0x01448100, + 0x01450200, 0x01460100, 0x01490100, 0x014e8101, + 0x014f0101, 0x01a28173, 0x01b80440, 0x01bb0240, + 0x01bd8300, 0x01bf8130, 0x01c30130, 0x01c40330, + 0x01c60130, 0x01c70230, 0x01c801d0, 0x01c89130, + 0x01d18930, 0x01d60100, 0x01d68300, 0x01d801d3, + 0x01d89100, 0x01e10173, 0x01e18900, 0x01e60100, + 0x01e68200, 0x01e78130, 0x01e80173, 0x01e88173, + 0x01ea8173, 0x01eb0173, 0x01eb8100, 0x01ec1840, + 0x01f80173, 0x01f88173, 0x01f90100, 0x01f98100, + 0x01fa01a0, 0x01fa8173, 0x01fb8240, 0x01fc8130, + 0x01fd0240, 0x01fe8330, 0x02001030, 0x02082030, + 0x02182000, 0x02281000, 0x02302240, 0x02453640, + 0x02600130, 0x02608e40, 0x02678100, 0x02686040, + 0x0298a630, 0x02b0a600, 0x02c381b5, 0x08502631, + 0x08638131, 0x08668131, 0x08682b00, 0x087e8300, + 0x09d05011, 0x09f80610, 0x09fc0620, 0x0e400174, + 0x0e408174, 0x0e410174, 0x0e418174, 0x0e420174, + 0x0e428174, 0x0e430174, 0x0e438180, 0x0e440180, + 0x0e482b30, 0x0e5e8330, 0x0ebc8101, 0x0ebe8101, + 0x0ec70101, 0x0f007e40, 0x0f3f1840, 0x0f4b01b5, + 0x0f4b81b6, 0x0f4c01b6, 0x0f4c81b6, 0x0f4d01b7, + 0x0f4d8180, 0x0f4f0130, 0x0f506040, 0x0f800800, + 0x0f840830, 0x0f880600, 0x0f8c0630, 0x0f900800, + 0x0f940830, 0x0f980800, 0x0f9c0830, 0x0fa00600, + 0x0fa40630, 0x0fa801b0, 0x0fa88100, 0x0fa901d3, + 0x0fa98100, 0x0faa01d3, 0x0faa8100, 0x0fab01d3, + 0x0fab8100, 0x0fac8130, 0x0fad8130, 0x0fae8130, + 0x0faf8130, 0x0fb00800, 0x0fb40830, 0x0fb80200, + 0x0fb90400, 0x0fbb0200, 0x0fbc0201, 0x0fbd0201, + 0x0fbe0201, 0x0fc008b7, 0x0fc40867, 0x0fc808b8, + 0x0fcc0868, 0x0fd008b8, 0x0fd40868, 0x0fd80200, + 0x0fd901b9, 0x0fd981b1, 0x0fda01b9, 0x0fdb01b1, + 0x0fdb81d7, 0x0fdc0230, 0x0fdd0230, 0x0fde0161, + 0x0fdf0173, 0x0fe101b9, 0x0fe181b2, 0x0fe201ba, + 0x0fe301b2, 0x0fe381d8, 0x0fe40430, 0x0fe60162, + 0x0fe80200, 0x0fe901d0, 0x0fe981d0, 0x0feb01b0, + 0x0feb81d0, 0x0fec0230, 0x0fed0230, 0x0ff00201, + 0x0ff101d3, 0x0ff181d3, 0x0ff201ba, 0x0ff28101, + 0x0ff301b0, 0x0ff381d3, 0x0ff40230, 0x0ff50230, + 0x0ff60131, 0x0ff901ba, 0x0ff981b2, 0x0ffa01bb, + 0x0ffb01b2, 0x0ffb81d9, 0x0ffc0230, 0x0ffd0230, + 0x0ffe0162, 0x109301a0, 0x109501a0, 0x109581a0, + 0x10990131, 0x10a70101, 0x10b01031, 0x10b81001, + 0x10c18240, 0x125b1a31, 0x12681a01, 0x16003031, + 0x16183001, 0x16300240, 0x16310130, 0x16318130, + 0x16320130, 0x16328100, 0x16330100, 0x16338640, + 0x16368130, 0x16370130, 0x16378130, 0x16380130, + 0x16390240, 0x163a8240, 0x163f0230, 0x16406440, + 0x16758440, 0x16790240, 0x16802600, 0x16938100, + 0x16968100, 0x53202e40, 0x53401c40, 0x53910e40, + 0x53993e40, 0x53bc8440, 0x53be8130, 0x53bf0a40, + 0x53c58240, 0x53c68130, 0x53c80440, 0x53ca0101, + 0x53cb1440, 0x53d50130, 0x53d58130, 0x53d60130, + 0x53d68130, 0x53d70130, 0x53d80130, 0x53d88130, + 0x53d90130, 0x53d98131, 0x53da1040, 0x53e20131, + 0x53e28130, 0x53e30130, 0x53e38440, 0x53e80240, + 0x53eb0440, 0x53fa8240, 0x55a98101, 0x55b85020, + 0x7d8001b2, 0x7d8081b2, 0x7d8101b2, 0x7d8181da, + 0x7d8201da, 0x7d8281b3, 0x7d8301b3, 0x7d8981bb, + 0x7d8a01bb, 0x7d8a81bb, 0x7d8b01bc, 0x7d8b81bb, + 0x7f909a31, 0x7fa09a01, 0x82002831, 0x82142801, + 0x82582431, 0x826c2401, 0x82b80b31, 0x82be0f31, + 0x82c60731, 0x82ca0231, 0x82cb8b01, 0x82d18f01, + 0x82d98701, 0x82dd8201, 0x86403331, 0x86603301, + 0x8c502031, 0x8c602001, 0xb7202031, 0xb7302001, + 0xf4802231, 0xf4912201, +}; + +static const uint8_t case_conv_table2[370] = { + 0x01, 0x00, 0x9c, 0x06, 0x07, 0x4d, 0x03, 0x04, + 0x10, 0x00, 0x8f, 0x0b, 0x00, 0x00, 0x11, 0x00, + 0x08, 0x00, 0x53, 0x4a, 0x51, 0x00, 0x52, 0x00, + 0x53, 0x00, 0x3a, 0x54, 0x55, 0x00, 0x57, 0x59, + 0x3f, 0x5d, 0x5c, 0x00, 0x46, 0x61, 0x63, 0x42, + 0x64, 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, + 0x6c, 0x00, 0x6e, 0x00, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x1a, 0x00, 0x93, 0x00, 0x00, 0x20, + 0x35, 0x00, 0x27, 0x00, 0x21, 0x00, 0x24, 0x22, + 0x2a, 0x00, 0x13, 0x6b, 0x6d, 0x00, 0x26, 0x24, + 0x27, 0x14, 0x16, 0x18, 0x1b, 0x1c, 0x3e, 0x1e, + 0x3f, 0x1f, 0x39, 0x3d, 0x22, 0x21, 0x41, 0x1e, + 0x40, 0x25, 0x25, 0x26, 0x28, 0x20, 0x2a, 0x48, + 0x2c, 0x43, 0x2e, 0x4b, 0x30, 0x4c, 0x32, 0x44, + 0x42, 0x99, 0x00, 0x00, 0x95, 0x8f, 0x7d, 0x7e, + 0x83, 0x84, 0x12, 0x80, 0x82, 0x76, 0x77, 0x12, + 0x7b, 0xa3, 0x7c, 0x78, 0x79, 0x8a, 0x92, 0x98, + 0xa6, 0xa0, 0x85, 0x00, 0x9a, 0xa1, 0x93, 0x75, + 0x33, 0x95, 0x00, 0x8e, 0x00, 0x74, 0x99, 0x98, + 0x97, 0x96, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, + 0xa1, 0xa0, 0x15, 0x2e, 0x2f, 0x30, 0xb4, 0xb5, + 0x4f, 0xaa, 0xa9, 0x12, 0x14, 0x1e, 0x21, 0x22, + 0x22, 0x2a, 0x34, 0x35, 0xa6, 0xa7, 0x36, 0x1f, + 0x49, 0x00, 0x00, 0x97, 0x01, 0x5a, 0xda, 0x1d, + 0x36, 0x05, 0x00, 0xc4, 0xc3, 0xc6, 0xc5, 0xc8, + 0xc7, 0xca, 0xc9, 0xcc, 0xcb, 0xc4, 0xd5, 0x45, + 0xd6, 0x42, 0xd7, 0x46, 0xd8, 0xce, 0xd0, 0xd2, + 0xd4, 0xda, 0xd9, 0xee, 0xf6, 0xfe, 0x0e, 0x07, + 0x0f, 0x80, 0x9f, 0x00, 0x21, 0x80, 0xa3, 0xed, + 0x00, 0xc0, 0x40, 0xc6, 0x60, 0xe7, 0xdb, 0xe6, + 0x99, 0xc0, 0x00, 0x00, 0x06, 0x60, 0xdc, 0x29, + 0xfd, 0x15, 0x12, 0x06, 0x16, 0xf8, 0xdd, 0x06, + 0x15, 0x12, 0x84, 0x08, 0xc6, 0x16, 0xff, 0xdf, + 0x03, 0xc0, 0x40, 0x00, 0x46, 0x60, 0xde, 0xe0, + 0x6d, 0x37, 0x38, 0x39, 0x15, 0x14, 0x17, 0x16, + 0x00, 0x1a, 0x19, 0x1c, 0x1b, 0x00, 0x5f, 0xb7, + 0x65, 0x44, 0x47, 0x00, 0x4f, 0x62, 0x4e, 0x50, + 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xa3, 0xa4, + 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00, + 0x00, 0x5a, 0x00, 0x47, 0x00, 0x5b, 0x56, 0x58, + 0x60, 0x5e, 0x70, 0x69, 0x6f, 0x4e, 0x00, 0x3b, + 0x67, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x45, 0xa8, + 0x8a, 0x8b, 0x8c, 0xab, 0xac, 0x58, 0x58, 0xaf, + 0x94, 0xb0, 0x6f, 0xb2, 0x5d, 0x5c, 0x5f, 0x5e, + 0x61, 0x60, 0x66, 0x67, 0x68, 0x69, 0x62, 0x63, + 0x64, 0x65, 0x6b, 0x6a, 0x6d, 0x6c, 0x6f, 0x6e, + 0x71, 0x70, +}; + +static const uint16_t case_conv_ext[58] = { + 0x0399, 0x0308, 0x0301, 0x03a5, 0x0313, 0x0300, 0x0342, 0x0391, + 0x0397, 0x03a9, 0x0046, 0x0049, 0x004c, 0x0053, 0x0069, 0x0307, + 0x02bc, 0x004e, 0x004a, 0x030c, 0x0535, 0x0552, 0x0048, 0x0331, + 0x0054, 0x0057, 0x030a, 0x0059, 0x0041, 0x02be, 0x1f08, 0x1f80, + 0x1f28, 0x1f90, 0x1f68, 0x1fa0, 0x1fba, 0x0386, 0x1fb3, 0x1fca, + 0x0389, 0x1fc3, 0x03a1, 0x1ffa, 0x038f, 0x1ff3, 0x0544, 0x0546, + 0x053b, 0x054e, 0x053d, 0x03b8, 0x0462, 0xa64a, 0x1e60, 0x03c9, + 0x006b, 0x00e5, +}; + +static const uint8_t unicode_prop_Cased1_table[196] = { + 0x40, 0xa9, 0x80, 0x8e, 0x80, 0xfc, 0x80, 0xd3, + 0x80, 0x8c, 0x80, 0x8d, 0x81, 0x8d, 0x02, 0x80, + 0xe1, 0x80, 0x91, 0x85, 0x9a, 0x01, 0x00, 0x01, + 0x11, 0x00, 0x01, 0x04, 0x08, 0x01, 0x08, 0x30, + 0x08, 0x01, 0x15, 0x20, 0x00, 0x39, 0x99, 0x31, + 0x9d, 0x84, 0x40, 0x94, 0x80, 0xd6, 0x82, 0xa6, + 0x80, 0x41, 0x62, 0x80, 0xa6, 0x80, 0x4b, 0x72, + 0x80, 0x4c, 0x02, 0xf8, 0x02, 0x80, 0x8f, 0x80, + 0xb0, 0x40, 0xdb, 0x08, 0x80, 0x41, 0xd0, 0x80, + 0x8c, 0x80, 0x8f, 0x8c, 0xe4, 0x03, 0x01, 0x89, + 0x00, 0x14, 0x28, 0x10, 0x11, 0x02, 0x01, 0x18, + 0x0b, 0x24, 0x4b, 0x26, 0x01, 0x01, 0x86, 0xe5, + 0x80, 0x60, 0x79, 0xb6, 0x81, 0x40, 0x91, 0x81, + 0xbd, 0x88, 0x94, 0x05, 0x80, 0x98, 0x80, 0xa2, + 0x00, 0x80, 0x9b, 0x12, 0x82, 0x43, 0x34, 0xa2, + 0x06, 0x80, 0x8d, 0x60, 0x5c, 0x15, 0x01, 0x10, + 0xa9, 0x80, 0x88, 0x60, 0xcc, 0x44, 0xd4, 0x80, + 0xc6, 0x01, 0x08, 0x09, 0x0b, 0x80, 0x8b, 0x00, + 0x06, 0x80, 0xc0, 0x03, 0x0f, 0x06, 0x80, 0x9b, + 0x03, 0x04, 0x00, 0x16, 0x80, 0x41, 0x53, 0x81, + 0x98, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, + 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, + 0x9e, 0x80, 0x98, 0x07, 0x47, 0x33, 0x89, 0x80, + 0x93, 0x2d, 0x41, 0x04, 0xbd, 0x50, 0xc1, 0x99, + 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Cased1_index[21] = { + 0xb9, 0x02, 0xe0, 0xc0, 0x1d, 0x20, 0xe5, 0x2c, + 0x20, 0xb1, 0x07, 0x21, 0xc1, 0xd6, 0x21, 0x4a, + 0xf1, 0x01, 0x8a, 0xf1, 0x01, +}; + +static const uint8_t unicode_prop_Case_Ignorable_table[737] = { + 0xa6, 0x05, 0x80, 0x8a, 0x80, 0xa2, 0x00, 0x80, + 0xc6, 0x03, 0x00, 0x03, 0x01, 0x81, 0x41, 0xf6, + 0x40, 0xbf, 0x19, 0x18, 0x88, 0x08, 0x80, 0x40, + 0xfa, 0x86, 0x40, 0xce, 0x04, 0x80, 0xb0, 0xac, + 0x00, 0x01, 0x01, 0x00, 0xab, 0x80, 0x8a, 0x85, + 0x89, 0x8a, 0x00, 0xa2, 0x80, 0x89, 0x94, 0x8f, + 0x80, 0xe4, 0x38, 0x89, 0x03, 0xa0, 0x00, 0x80, + 0x9d, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0x18, 0x08, + 0x97, 0x97, 0xaa, 0x82, 0xab, 0x06, 0x0d, 0x87, + 0xa8, 0xb9, 0xb6, 0x00, 0x03, 0x3b, 0x02, 0x86, + 0x89, 0x81, 0x8c, 0x80, 0x8e, 0x80, 0xb9, 0x03, + 0x1f, 0x80, 0x93, 0x81, 0x99, 0x01, 0x81, 0xb8, + 0x03, 0x0b, 0x09, 0x12, 0x80, 0x9d, 0x0a, 0x80, + 0x8a, 0x81, 0xb8, 0x03, 0x20, 0x0b, 0x80, 0x93, + 0x81, 0x95, 0x28, 0x80, 0xb9, 0x01, 0x00, 0x1f, + 0x06, 0x81, 0x8a, 0x81, 0x9d, 0x80, 0xbc, 0x80, + 0x8b, 0x80, 0xb1, 0x02, 0x80, 0xb6, 0x00, 0x14, + 0x10, 0x1e, 0x81, 0x8a, 0x81, 0x9c, 0x80, 0xb9, + 0x01, 0x05, 0x04, 0x81, 0x93, 0x81, 0x9b, 0x81, + 0xb8, 0x0b, 0x1f, 0x80, 0x93, 0x81, 0x9c, 0x80, + 0xc7, 0x06, 0x10, 0x80, 0xd9, 0x01, 0x86, 0x8a, + 0x88, 0xe1, 0x01, 0x88, 0x88, 0x00, 0x86, 0xc8, + 0x81, 0x9a, 0x00, 0x00, 0x80, 0xb6, 0x8d, 0x04, + 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, 0x80, 0xe5, + 0x18, 0x28, 0x09, 0x81, 0x98, 0x0b, 0x82, 0x8f, + 0x83, 0x8c, 0x01, 0x0d, 0x80, 0x8e, 0x80, 0xdd, + 0x80, 0x42, 0x5f, 0x82, 0x43, 0xb1, 0x82, 0x9c, + 0x81, 0x9d, 0x81, 0x9d, 0x81, 0xbf, 0x08, 0x37, + 0x01, 0x8a, 0x10, 0x20, 0xac, 0x84, 0xb2, 0x80, + 0xc0, 0x81, 0xa1, 0x80, 0xf5, 0x13, 0x81, 0x88, + 0x05, 0x82, 0x40, 0xda, 0x09, 0x80, 0xb9, 0x00, + 0x30, 0x00, 0x01, 0x3d, 0x89, 0x08, 0xa6, 0x07, + 0x9e, 0xb0, 0x83, 0xaf, 0x00, 0x20, 0x04, 0x80, + 0xa7, 0x88, 0x8b, 0x81, 0x9f, 0x19, 0x08, 0x82, + 0xb7, 0x00, 0x0a, 0x00, 0x82, 0xb9, 0x39, 0x81, + 0xbf, 0x85, 0xd1, 0x10, 0x8c, 0x06, 0x18, 0x28, + 0x11, 0xb1, 0xbe, 0x8c, 0x80, 0xa1, 0xe4, 0x41, + 0xbc, 0x00, 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c, + 0x82, 0x8c, 0x81, 0x8b, 0x27, 0x81, 0x89, 0x01, + 0x01, 0x84, 0xb0, 0x20, 0x89, 0x00, 0x8c, 0x80, + 0x8f, 0x8c, 0xb2, 0xa0, 0x4b, 0x8a, 0x81, 0xf0, + 0x82, 0xfc, 0x80, 0x8e, 0x80, 0xdf, 0x9f, 0xae, + 0x80, 0x41, 0xd4, 0x80, 0xa3, 0x1a, 0x24, 0x80, + 0xdc, 0x85, 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80, + 0x44, 0xe1, 0x85, 0x41, 0x0d, 0x80, 0xe1, 0x18, + 0x89, 0x00, 0x9b, 0x83, 0xcf, 0x81, 0x8d, 0xa1, + 0xcd, 0x80, 0x96, 0x82, 0xe6, 0x12, 0x0f, 0x02, + 0x03, 0x80, 0x98, 0x0c, 0x80, 0x40, 0x96, 0x81, + 0x99, 0x91, 0x8c, 0x80, 0xa5, 0x87, 0x98, 0x8a, + 0xad, 0x82, 0xaf, 0x01, 0x19, 0x81, 0x90, 0x80, + 0x94, 0x81, 0xc1, 0x29, 0x09, 0x81, 0x8b, 0x07, + 0x80, 0xa2, 0x80, 0x8a, 0x80, 0xb2, 0x00, 0x11, + 0x0c, 0x08, 0x80, 0x9a, 0x80, 0x8d, 0x0c, 0x08, + 0x80, 0xe3, 0x84, 0x88, 0x82, 0xf8, 0x01, 0x03, + 0x80, 0x60, 0x4f, 0x2f, 0x80, 0x40, 0x92, 0x90, + 0x42, 0x3c, 0x8f, 0x10, 0x8b, 0x8f, 0xa1, 0x01, + 0x80, 0x40, 0xa8, 0x06, 0x05, 0x80, 0x8a, 0x80, + 0xa2, 0x00, 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2, + 0x80, 0x94, 0x82, 0x42, 0x00, 0x80, 0x40, 0xe1, + 0x80, 0x40, 0x94, 0x84, 0x44, 0x04, 0x28, 0xa9, + 0x80, 0x88, 0x42, 0x45, 0x10, 0x0c, 0x83, 0xa7, + 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x83, + 0x41, 0x82, 0x81, 0xcf, 0x82, 0xc5, 0x8a, 0xb0, + 0x83, 0xfa, 0x80, 0xb5, 0x8e, 0xa8, 0x01, 0x81, + 0x89, 0x82, 0xb0, 0x19, 0x09, 0x03, 0x80, 0x89, + 0x80, 0xb1, 0x82, 0xa3, 0x20, 0x87, 0xbd, 0x80, + 0x8b, 0x81, 0xb3, 0x88, 0x89, 0x19, 0x80, 0xde, + 0x11, 0x00, 0x0d, 0x01, 0x80, 0x40, 0x9c, 0x02, + 0x87, 0x94, 0x81, 0xb8, 0x0a, 0x80, 0xa4, 0x32, + 0x84, 0x40, 0xc2, 0x39, 0x10, 0x80, 0x96, 0x80, + 0xd3, 0x28, 0x03, 0x08, 0x81, 0x40, 0xed, 0x1d, + 0x08, 0x81, 0x9a, 0x81, 0xd4, 0x39, 0x00, 0x81, + 0xe9, 0x00, 0x01, 0x28, 0x80, 0xe4, 0x11, 0x18, + 0x84, 0x41, 0x02, 0x88, 0x01, 0x40, 0xff, 0x08, + 0x03, 0x80, 0x40, 0x8f, 0x19, 0x0b, 0x80, 0x9f, + 0x89, 0xa7, 0x29, 0x1f, 0x80, 0x88, 0x29, 0x82, + 0xad, 0x8c, 0x01, 0x41, 0x95, 0x30, 0x28, 0x80, + 0xd1, 0x95, 0x0e, 0x01, 0x01, 0xf9, 0x2a, 0x00, + 0x08, 0x30, 0x80, 0xc7, 0x0a, 0x00, 0x80, 0x41, + 0x5a, 0x81, 0x8a, 0x81, 0xb3, 0x24, 0x00, 0x80, + 0x54, 0xec, 0x90, 0x85, 0x8e, 0x60, 0x36, 0x99, + 0x84, 0xba, 0x86, 0x88, 0x83, 0x44, 0x0a, 0x80, + 0xbe, 0x90, 0xbf, 0x08, 0x81, 0x60, 0x40, 0x0a, + 0x18, 0x30, 0x81, 0x4c, 0x9d, 0x08, 0x83, 0x52, + 0x5b, 0xad, 0x81, 0x96, 0x42, 0x1f, 0x82, 0x88, + 0x8f, 0x0e, 0x9d, 0x83, 0x40, 0x93, 0x82, 0x47, + 0xba, 0xb6, 0x83, 0xb1, 0x38, 0x8d, 0x80, 0x95, + 0x20, 0x8e, 0x45, 0x4f, 0x30, 0x90, 0x0e, 0x01, + 0x04, 0x84, 0xbd, 0xa0, 0x80, 0x40, 0x9f, 0x8d, + 0x41, 0x6f, 0x80, 0xbc, 0x83, 0x41, 0xfa, 0x84, + 0x43, 0xdf, 0x86, 0xec, 0x87, 0x4a, 0xae, 0x84, + 0x6c, 0x0c, 0x00, 0x80, 0x9d, 0xdf, 0xff, 0x40, + 0xef, +}; + +static const uint8_t unicode_prop_Case_Ignorable_index[69] = { + 0xbe, 0x05, 0x00, 0xfe, 0x07, 0x00, 0x52, 0x0a, + 0xa0, 0xc1, 0x0b, 0x00, 0x82, 0x0d, 0x00, 0x3f, + 0x10, 0x80, 0xd4, 0x17, 0x40, 0xcf, 0x1a, 0x20, + 0xf5, 0x1c, 0x00, 0x80, 0x20, 0x00, 0x16, 0xa0, + 0x00, 0xc6, 0xa8, 0x00, 0xc2, 0xaa, 0x60, 0x56, + 0xfe, 0x20, 0xb1, 0x07, 0x01, 0x75, 0x10, 0x01, + 0xeb, 0x12, 0x21, 0x41, 0x16, 0x01, 0x5c, 0x1a, + 0x01, 0x43, 0x1f, 0x01, 0x2e, 0xcf, 0x41, 0x25, + 0xe0, 0x01, 0xf0, 0x01, 0x0e, +}; + +static const uint8_t unicode_prop_ID_Start_table[1100] = { + 0xc0, 0x99, 0x85, 0x99, 0xae, 0x80, 0x89, 0x03, + 0x04, 0x96, 0x80, 0x9e, 0x80, 0x41, 0xc9, 0x83, + 0x8b, 0x8d, 0x26, 0x00, 0x80, 0x40, 0x80, 0x20, + 0x09, 0x18, 0x05, 0x00, 0x10, 0x00, 0x93, 0x80, + 0xd2, 0x80, 0x40, 0x8a, 0x87, 0x40, 0xa5, 0x80, + 0xa5, 0x08, 0x85, 0xa8, 0xc6, 0x9a, 0x1b, 0xac, + 0xaa, 0xa2, 0x08, 0xe2, 0x00, 0x8e, 0x0e, 0x81, + 0x89, 0x11, 0x80, 0x8f, 0x00, 0x9d, 0x9c, 0xd8, + 0x8a, 0x80, 0x97, 0xa0, 0x88, 0x0b, 0x04, 0x95, + 0x18, 0x88, 0x02, 0x80, 0x96, 0x98, 0x86, 0x8a, + 0x84, 0x97, 0x05, 0x90, 0xa9, 0xb9, 0xb5, 0x10, + 0x91, 0x06, 0x89, 0x8e, 0x8f, 0x1f, 0x09, 0x81, + 0x95, 0x06, 0x00, 0x13, 0x10, 0x8f, 0x80, 0x8c, + 0x08, 0x82, 0x8d, 0x81, 0x89, 0x07, 0x2b, 0x09, + 0x95, 0x06, 0x01, 0x01, 0x01, 0x9e, 0x18, 0x80, + 0x92, 0x82, 0x8f, 0x88, 0x02, 0x80, 0x95, 0x06, + 0x01, 0x04, 0x10, 0x91, 0x80, 0x8e, 0x81, 0x96, + 0x80, 0x8a, 0x39, 0x09, 0x95, 0x06, 0x01, 0x04, + 0x10, 0x9d, 0x08, 0x82, 0x8e, 0x80, 0x90, 0x00, + 0x2a, 0x10, 0x1a, 0x08, 0x00, 0x0a, 0x0a, 0x12, + 0x8b, 0x95, 0x80, 0xb3, 0x38, 0x10, 0x96, 0x80, + 0x8f, 0x10, 0x99, 0x11, 0x01, 0x81, 0x9d, 0x03, + 0x38, 0x10, 0x96, 0x80, 0x89, 0x04, 0x10, 0x9e, + 0x08, 0x81, 0x8e, 0x81, 0x90, 0x88, 0x02, 0x80, + 0xa8, 0x08, 0x8f, 0x04, 0x17, 0x82, 0x97, 0x2c, + 0x91, 0x82, 0x97, 0x80, 0x88, 0x00, 0x0e, 0xb9, + 0xaf, 0x01, 0x8b, 0x86, 0xb9, 0x08, 0x00, 0x20, + 0x97, 0x00, 0x80, 0x89, 0x01, 0x88, 0x01, 0x20, + 0x80, 0x94, 0x83, 0x9f, 0x80, 0xbe, 0x38, 0xa3, + 0x9a, 0x84, 0xf2, 0xaa, 0x93, 0x80, 0x8f, 0x2b, + 0x1a, 0x02, 0x0e, 0x13, 0x8c, 0x8b, 0x80, 0x90, + 0xa5, 0x00, 0x20, 0x81, 0xaa, 0x80, 0x41, 0x4c, + 0x03, 0x0e, 0x00, 0x03, 0x81, 0xa8, 0x03, 0x81, + 0xa0, 0x03, 0x0e, 0x00, 0x03, 0x81, 0x8e, 0x80, + 0xb8, 0x03, 0x81, 0xc2, 0xa4, 0x8f, 0x8f, 0xd5, + 0x0d, 0x82, 0x42, 0x6b, 0x81, 0x90, 0x80, 0x99, + 0x84, 0xca, 0x82, 0x8a, 0x86, 0x91, 0x8c, 0x92, + 0x8d, 0x91, 0x8d, 0x8c, 0x02, 0x8e, 0xb3, 0xa2, + 0x03, 0x80, 0xc2, 0xd8, 0x86, 0xa8, 0x00, 0x84, + 0xc5, 0x89, 0x9e, 0xb0, 0x9d, 0x0c, 0x8a, 0xab, + 0x83, 0x99, 0xb5, 0x96, 0x88, 0xb4, 0xd1, 0x80, + 0xdc, 0xae, 0x90, 0x87, 0xb5, 0x9d, 0x8c, 0x81, + 0x89, 0xab, 0x99, 0xa3, 0xa8, 0x82, 0x89, 0xa3, + 0x81, 0x88, 0x86, 0xaa, 0x0a, 0xa8, 0x18, 0x28, + 0x0a, 0x04, 0x40, 0xbf, 0xbf, 0x41, 0x15, 0x0d, + 0x81, 0xa5, 0x0d, 0x0f, 0x00, 0x00, 0x00, 0x80, + 0x9e, 0x81, 0xb4, 0x06, 0x00, 0x12, 0x06, 0x13, + 0x0d, 0x83, 0x8c, 0x22, 0x06, 0xf3, 0x80, 0x8c, + 0x80, 0x8f, 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, + 0x0d, 0x28, 0x00, 0x00, 0x80, 0x8f, 0x0b, 0x24, + 0x18, 0x90, 0xa8, 0x4a, 0x76, 0x40, 0xe4, 0x2b, + 0x11, 0x8b, 0xa5, 0x00, 0x20, 0x81, 0xb7, 0x30, + 0x8f, 0x96, 0x88, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x86, 0x42, 0x25, 0x82, 0x98, 0x88, + 0x34, 0x0c, 0x83, 0xd5, 0x1c, 0x80, 0xd9, 0x03, + 0x84, 0xaa, 0x80, 0xdd, 0x90, 0x9f, 0xaf, 0x8f, + 0x41, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x56, 0x8c, + 0xc2, 0xad, 0x81, 0x41, 0x0c, 0x82, 0x8f, 0x89, + 0x81, 0x93, 0xae, 0x8f, 0x9e, 0x81, 0xcf, 0xa6, + 0x88, 0x81, 0xe6, 0x81, 0xbf, 0x21, 0x00, 0x04, + 0x97, 0x8f, 0x02, 0x03, 0x80, 0x96, 0x9c, 0xb3, + 0x8d, 0xb1, 0xbd, 0x2a, 0x00, 0x81, 0x8a, 0x9b, + 0x89, 0x96, 0x98, 0x9c, 0x86, 0xae, 0x9b, 0x80, + 0x8f, 0x20, 0x89, 0x89, 0x20, 0xa8, 0x96, 0x10, + 0x87, 0x93, 0x96, 0x10, 0x82, 0xb1, 0x00, 0x11, + 0x0c, 0x08, 0x00, 0x97, 0x11, 0x8a, 0x32, 0x8b, + 0x29, 0x29, 0x85, 0x88, 0x30, 0x30, 0xaa, 0x80, + 0x8d, 0x85, 0xf2, 0x9c, 0x60, 0x2b, 0xa3, 0x8b, + 0x96, 0x83, 0xb0, 0x60, 0x21, 0x03, 0x41, 0x6d, + 0x81, 0xe9, 0xa5, 0x86, 0x8b, 0x24, 0x00, 0x89, + 0x80, 0x8c, 0x04, 0x00, 0x01, 0x01, 0x80, 0xeb, + 0xa0, 0x41, 0x6a, 0x91, 0xbf, 0x81, 0xb5, 0xa7, + 0x8b, 0xf3, 0x20, 0x40, 0x86, 0xa3, 0x99, 0x85, + 0x99, 0x8a, 0xd8, 0x15, 0x0d, 0x0d, 0x0a, 0xa2, + 0x8b, 0x80, 0x99, 0x80, 0x92, 0x01, 0x80, 0x8e, + 0x81, 0x8d, 0xa1, 0xfa, 0xc4, 0xb4, 0x41, 0x0a, + 0x9c, 0x82, 0xb0, 0xae, 0x9f, 0x8c, 0x9d, 0x84, + 0xa5, 0x89, 0x9d, 0x81, 0xa3, 0x1f, 0x04, 0xa9, + 0x40, 0x9d, 0x91, 0xa3, 0x83, 0xa3, 0x83, 0xa7, + 0x87, 0xb3, 0x8b, 0x8a, 0x80, 0x8e, 0x06, 0x01, + 0x80, 0x8a, 0x80, 0x8e, 0x06, 0x01, 0xc2, 0x41, + 0x36, 0x88, 0x95, 0x89, 0x87, 0x97, 0x28, 0xa9, + 0x80, 0x88, 0xc4, 0x29, 0x00, 0xab, 0x01, 0x10, + 0x81, 0x96, 0x89, 0x96, 0x88, 0x9e, 0xc0, 0x92, + 0x01, 0x89, 0x95, 0x89, 0x99, 0xc5, 0xb7, 0x29, + 0xbf, 0x80, 0x8e, 0x18, 0x10, 0x9c, 0xa9, 0x9c, + 0x82, 0x9c, 0xa2, 0x38, 0x9b, 0x9a, 0xb5, 0x89, + 0x95, 0x89, 0x92, 0x8c, 0x91, 0xed, 0xc8, 0xb6, + 0xb2, 0x8c, 0xb2, 0x8c, 0xa3, 0x41, 0x5b, 0xa9, + 0x29, 0xcd, 0x9c, 0x89, 0x07, 0x95, 0xa9, 0x91, + 0xad, 0x94, 0x9a, 0x96, 0x8b, 0xb4, 0xb8, 0x09, + 0x80, 0x8c, 0xac, 0x9f, 0x98, 0x99, 0xa3, 0x9c, + 0x01, 0x07, 0xa2, 0x10, 0x8b, 0xaf, 0x8d, 0x83, + 0x94, 0x00, 0x80, 0xa2, 0x91, 0x80, 0x98, 0x92, + 0x81, 0xbe, 0x30, 0x00, 0x18, 0x8e, 0x80, 0x89, + 0x86, 0xae, 0xa5, 0x39, 0x09, 0x95, 0x06, 0x01, + 0x04, 0x10, 0x91, 0x80, 0x8b, 0x84, 0x40, 0x9d, + 0xb4, 0x91, 0x83, 0x93, 0x82, 0x9d, 0xaf, 0x93, + 0x08, 0x80, 0x40, 0xb7, 0xae, 0xa8, 0x83, 0xa3, + 0xaf, 0x93, 0x80, 0xba, 0xaa, 0x8c, 0x80, 0xc6, + 0x9a, 0xa4, 0x86, 0x40, 0xb8, 0xab, 0xf3, 0xbf, + 0x9e, 0x39, 0x01, 0x38, 0x08, 0x97, 0x8e, 0x00, + 0x80, 0xdd, 0x39, 0xa6, 0x8f, 0x00, 0x80, 0x9b, + 0x80, 0x89, 0xa7, 0x30, 0x94, 0x80, 0x8a, 0xad, + 0x92, 0x80, 0x91, 0xc8, 0x41, 0x06, 0x88, 0x80, + 0xa4, 0x90, 0x80, 0xb0, 0x9d, 0xef, 0x30, 0x08, + 0xa5, 0x94, 0x80, 0x98, 0x28, 0x08, 0x9f, 0x8d, + 0x80, 0x41, 0x46, 0x92, 0x8e, 0x00, 0x8c, 0x80, + 0xa1, 0xfb, 0x80, 0xce, 0x43, 0x99, 0xe5, 0xee, + 0x90, 0x40, 0xc3, 0x4a, 0x4b, 0xe0, 0x8e, 0x44, + 0x2f, 0x90, 0x85, 0x4f, 0xb8, 0x42, 0x46, 0x60, + 0x21, 0xb8, 0x42, 0x38, 0x86, 0x9e, 0x90, 0xce, + 0x90, 0x9d, 0x91, 0xaf, 0x8f, 0x83, 0x9e, 0x94, + 0x84, 0x92, 0x42, 0xaf, 0xbf, 0xff, 0xca, 0x20, + 0xc1, 0x8c, 0xbf, 0x08, 0x80, 0x9b, 0x57, 0xf7, + 0x87, 0x44, 0xd5, 0xa9, 0x88, 0x60, 0x22, 0xe6, + 0x18, 0x30, 0x08, 0x41, 0x22, 0x8e, 0x80, 0x9c, + 0x11, 0x80, 0x8d, 0x1f, 0x41, 0x8b, 0x49, 0x03, + 0xea, 0x84, 0x8c, 0x82, 0x88, 0x86, 0x89, 0x57, + 0x65, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, + 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f, + 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80, + 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x47, + 0x33, 0x9e, 0x2d, 0x41, 0x04, 0xbd, 0x40, 0x91, + 0xac, 0x89, 0x86, 0x8f, 0x80, 0x41, 0x40, 0x9d, + 0x91, 0xab, 0x41, 0xe3, 0x9b, 0x42, 0xf3, 0x30, + 0x18, 0x08, 0x8e, 0x80, 0x40, 0xc4, 0xba, 0xc3, + 0x30, 0x44, 0xb3, 0x18, 0x9a, 0x01, 0x00, 0x08, + 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, + 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, + 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, + 0x51, 0x43, 0x60, 0xa6, 0xdf, 0x9f, 0x50, 0x39, + 0x85, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, + 0x30, 0x4c, 0x1e, 0x42, 0x1d, 0x45, 0xe1, 0x53, + 0x4a, 0x84, 0x50, 0x5f, +}; + +static const uint8_t unicode_prop_ID_Start_index[105] = { + 0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xa9, 0x09, + 0x20, 0xb1, 0x0a, 0x00, 0xba, 0x0b, 0x20, 0x3b, + 0x0d, 0x20, 0xc7, 0x0e, 0x20, 0x49, 0x12, 0x00, + 0x9b, 0x16, 0x00, 0xac, 0x19, 0x00, 0xc0, 0x1d, + 0x80, 0x80, 0x20, 0x20, 0x70, 0x2d, 0x00, 0x00, + 0x32, 0x00, 0xda, 0xa7, 0x00, 0x4c, 0xaa, 0x20, + 0xc7, 0xd7, 0x20, 0xfc, 0xfd, 0x20, 0x9d, 0x02, + 0x21, 0x96, 0x05, 0x01, 0xf3, 0x08, 0x01, 0xb3, + 0x0c, 0x21, 0x73, 0x11, 0x61, 0x34, 0x13, 0x01, + 0x1b, 0x17, 0x21, 0x8a, 0x1a, 0x01, 0x34, 0x1f, + 0x21, 0xbf, 0x6a, 0x01, 0x23, 0xb1, 0xa1, 0xad, + 0xd4, 0x01, 0x6f, 0xd7, 0x01, 0xff, 0xe7, 0x61, + 0x5e, 0xee, 0x01, 0xe1, 0xeb, 0x22, 0xb0, 0x23, + 0x03, +}; + +static const uint8_t unicode_prop_ID_Continue1_table[660] = { + 0xaf, 0x89, 0xa4, 0x80, 0xd6, 0x80, 0x42, 0x47, + 0xef, 0x96, 0x80, 0x40, 0xfa, 0x84, 0x41, 0x08, + 0xac, 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, + 0x9e, 0x28, 0xe4, 0x31, 0x29, 0x08, 0x19, 0x89, + 0x96, 0x80, 0x9d, 0x9a, 0xda, 0x8a, 0x8e, 0x89, + 0xa0, 0x88, 0x88, 0x80, 0x97, 0x18, 0x88, 0x02, + 0x04, 0xaa, 0x82, 0xbb, 0x87, 0xa9, 0x97, 0x80, + 0xa0, 0xb5, 0x10, 0x91, 0x06, 0x89, 0x09, 0x89, + 0x90, 0x82, 0xb7, 0x00, 0x31, 0x09, 0x82, 0x88, + 0x80, 0x89, 0x09, 0x89, 0x8d, 0x01, 0x82, 0xb7, + 0x00, 0x23, 0x09, 0x12, 0x80, 0x93, 0x8b, 0x10, + 0x8a, 0x82, 0xb7, 0x00, 0x38, 0x10, 0x82, 0x93, + 0x09, 0x89, 0x89, 0x28, 0x82, 0xb7, 0x00, 0x31, + 0x09, 0x16, 0x82, 0x89, 0x09, 0x89, 0x91, 0x80, + 0xba, 0x22, 0x10, 0x83, 0x88, 0x80, 0x8d, 0x89, + 0x8f, 0x84, 0xb6, 0x00, 0x30, 0x10, 0x1e, 0x81, + 0x8a, 0x09, 0x89, 0x90, 0x82, 0xb7, 0x00, 0x30, + 0x10, 0x1e, 0x81, 0x8a, 0x09, 0x89, 0x10, 0x8b, + 0x83, 0xb6, 0x08, 0x30, 0x10, 0x83, 0x88, 0x80, + 0x89, 0x09, 0x89, 0x90, 0x82, 0xc5, 0x03, 0x28, + 0x00, 0x3d, 0x89, 0x09, 0xbc, 0x01, 0x86, 0x8b, + 0x38, 0x89, 0xd6, 0x01, 0x88, 0x8a, 0x30, 0x89, + 0xbd, 0x0d, 0x89, 0x8a, 0x00, 0x00, 0x03, 0x81, + 0xb0, 0x93, 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, + 0x80, 0xe3, 0x93, 0x80, 0x89, 0x8b, 0x1b, 0x10, + 0x11, 0x32, 0x83, 0x8c, 0x8b, 0x80, 0x8e, 0x42, + 0xbe, 0x82, 0x88, 0x88, 0x43, 0x9f, 0x83, 0x9b, + 0x82, 0x9c, 0x81, 0x9d, 0x81, 0xbf, 0x9f, 0x88, + 0x01, 0x89, 0xa0, 0x10, 0x8a, 0x40, 0x8e, 0x80, + 0xf5, 0x8b, 0x83, 0x8b, 0x89, 0x89, 0xff, 0x8a, + 0xbb, 0x84, 0xb8, 0x89, 0x80, 0x9c, 0x81, 0x8a, + 0x85, 0x89, 0x95, 0x8d, 0x80, 0x8f, 0xb0, 0x84, + 0xae, 0x90, 0x8a, 0x89, 0x90, 0x88, 0x8b, 0x82, + 0x9d, 0x8c, 0x81, 0x89, 0xab, 0x8d, 0xaf, 0x93, + 0x87, 0x89, 0x85, 0x89, 0xf5, 0x10, 0x94, 0x18, + 0x28, 0x0a, 0x40, 0xc5, 0xbf, 0x42, 0x3e, 0x81, + 0x92, 0x80, 0xfa, 0x8c, 0x18, 0x82, 0x8b, 0x4b, + 0xfd, 0x82, 0x40, 0x8c, 0x80, 0xdf, 0x9f, 0x42, + 0x29, 0x85, 0xe8, 0x81, 0x60, 0x75, 0x84, 0x89, + 0xc4, 0x03, 0x89, 0x9f, 0x81, 0xcf, 0x81, 0x41, + 0x0f, 0x02, 0x03, 0x80, 0x96, 0x23, 0x80, 0xd2, + 0x81, 0xb1, 0x91, 0x89, 0x89, 0x85, 0x91, 0x8c, + 0x8a, 0x9b, 0x87, 0x98, 0x8c, 0xab, 0x83, 0xae, + 0x8d, 0x8e, 0x89, 0x8a, 0x80, 0x89, 0x89, 0xae, + 0x8d, 0x8b, 0x07, 0x09, 0x89, 0xa0, 0x82, 0xb1, + 0x00, 0x11, 0x0c, 0x08, 0x80, 0xa8, 0x24, 0x81, + 0x40, 0xeb, 0x38, 0x09, 0x89, 0x60, 0x4f, 0x23, + 0x80, 0x42, 0xe0, 0x8f, 0x8f, 0x8f, 0x11, 0x97, + 0x82, 0x40, 0xbf, 0x89, 0xa4, 0x80, 0x42, 0xbc, + 0x80, 0x40, 0xe1, 0x80, 0x40, 0x94, 0x84, 0x41, + 0x24, 0x89, 0x45, 0x56, 0x10, 0x0c, 0x83, 0xa7, + 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x1f, + 0x89, 0x41, 0x70, 0x81, 0xcf, 0x82, 0xc5, 0x8a, + 0xb0, 0x83, 0xf9, 0x82, 0xb4, 0x8e, 0x9e, 0x8a, + 0x09, 0x89, 0x83, 0xac, 0x8a, 0x30, 0xac, 0x89, + 0x2a, 0xa3, 0x8d, 0x80, 0x89, 0x21, 0xab, 0x80, + 0x8b, 0x82, 0xaf, 0x8d, 0x3b, 0x80, 0x8b, 0xd1, + 0x8b, 0x28, 0x08, 0x40, 0x9c, 0x8b, 0x84, 0x89, + 0x2b, 0xb6, 0x08, 0x31, 0x09, 0x82, 0x88, 0x80, + 0x89, 0x09, 0x32, 0x84, 0x40, 0xbf, 0x91, 0x88, + 0x89, 0x18, 0xd0, 0x93, 0x8b, 0x89, 0x40, 0xd4, + 0x31, 0x88, 0x9a, 0x81, 0xd1, 0x90, 0x8e, 0x89, + 0xd0, 0x8c, 0x87, 0x89, 0xd2, 0x8e, 0x83, 0x89, + 0x40, 0xf1, 0x8e, 0x40, 0xa4, 0x89, 0xc5, 0x28, + 0x09, 0x18, 0x00, 0x81, 0x8b, 0x89, 0xf6, 0x31, + 0x32, 0x80, 0x9b, 0x89, 0xa7, 0x30, 0x1f, 0x80, + 0x88, 0x8a, 0xad, 0x8f, 0x41, 0x94, 0x38, 0x87, + 0x8f, 0x89, 0xb7, 0x95, 0x80, 0x8d, 0xf9, 0x2a, + 0x00, 0x08, 0x30, 0x07, 0x89, 0xaf, 0x20, 0x08, + 0x27, 0x89, 0x41, 0x48, 0x83, 0x88, 0x08, 0x80, + 0xaf, 0x32, 0x84, 0x8c, 0x89, 0x54, 0xe5, 0x05, + 0x8e, 0x60, 0x36, 0x09, 0x89, 0xd5, 0x89, 0xa5, + 0x84, 0xba, 0x86, 0x98, 0x89, 0x43, 0xf4, 0x00, + 0xb6, 0x33, 0xd0, 0x80, 0x8a, 0x81, 0x60, 0x4c, + 0xaa, 0x81, 0x52, 0x60, 0xad, 0x81, 0x96, 0x42, + 0x1d, 0x22, 0x2f, 0x39, 0x86, 0x9d, 0x83, 0x40, + 0x93, 0x82, 0x45, 0x88, 0xb1, 0x41, 0xff, 0xb6, + 0x83, 0xb1, 0x38, 0x8d, 0x80, 0x95, 0x20, 0x8e, + 0x45, 0x4f, 0x30, 0x90, 0x0e, 0x01, 0x04, 0xe3, + 0x80, 0x40, 0x9f, 0x86, 0x88, 0x89, 0x41, 0x63, + 0x80, 0xbc, 0x8d, 0x41, 0xf1, 0x8d, 0x43, 0xd5, + 0x86, 0xec, 0x34, 0x89, 0x52, 0x95, 0x89, 0x6c, + 0x05, 0x05, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_ID_Continue1_index[63] = { + 0xfa, 0x06, 0x00, 0x70, 0x09, 0x00, 0xf0, 0x0a, + 0x40, 0x57, 0x0c, 0x00, 0xf0, 0x0d, 0x60, 0xc7, + 0x0f, 0x20, 0xea, 0x17, 0x40, 0x05, 0x1b, 0x00, + 0x41, 0x20, 0x00, 0x0c, 0xa8, 0x80, 0x37, 0xaa, + 0x20, 0x50, 0xfe, 0x20, 0x3a, 0x0d, 0x21, 0x74, + 0x11, 0x01, 0x5a, 0x14, 0x21, 0x44, 0x19, 0x81, + 0x5a, 0x1d, 0xa1, 0xf5, 0x6a, 0x21, 0x45, 0xd2, + 0x41, 0xaf, 0xe2, 0x21, 0xf0, 0x01, 0x0e, +}; + +static const uint8_t unicode_cc_table[899] = { + 0xb2, 0xcf, 0xd4, 0x00, 0xe8, 0x03, 0xdc, 0x00, + 0xe8, 0x00, 0xd8, 0x04, 0xdc, 0x01, 0xca, 0x03, + 0xdc, 0x01, 0xca, 0x0a, 0xdc, 0x04, 0x01, 0x03, + 0xdc, 0xc7, 0x00, 0xf0, 0xc0, 0x02, 0xdc, 0xc2, + 0x01, 0xdc, 0x80, 0xc2, 0x03, 0xdc, 0xc0, 0x00, + 0xe8, 0x01, 0xdc, 0xc0, 0x41, 0xe9, 0x00, 0xea, + 0x41, 0xe9, 0x00, 0xea, 0x00, 0xe9, 0xcc, 0xb0, + 0xe2, 0xc4, 0xb0, 0xd8, 0x00, 0xdc, 0xc3, 0x00, + 0xdc, 0xc2, 0x00, 0xde, 0x00, 0xdc, 0xc5, 0x05, + 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xde, 0x00, + 0xe4, 0xc0, 0x49, 0x0a, 0x43, 0x13, 0x80, 0x00, + 0x17, 0x80, 0x41, 0x18, 0x80, 0xc0, 0x00, 0xdc, + 0x80, 0x00, 0x12, 0xb0, 0x17, 0xc7, 0x42, 0x1e, + 0xaf, 0x47, 0x1b, 0xc1, 0x01, 0xdc, 0xc4, 0x00, + 0xdc, 0xc1, 0x00, 0xdc, 0x8f, 0x00, 0x23, 0xb0, + 0x34, 0xc6, 0x81, 0xc3, 0x00, 0xdc, 0xc0, 0x81, + 0xc1, 0x80, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xa2, + 0x00, 0x24, 0x9d, 0xc0, 0x00, 0xdc, 0xc1, 0x00, + 0xdc, 0xc1, 0x02, 0xdc, 0xc0, 0x01, 0xdc, 0xc0, + 0x00, 0xdc, 0xc2, 0x00, 0xdc, 0xc0, 0x00, 0xdc, + 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, + 0x6f, 0xc6, 0x00, 0xdc, 0xc0, 0x88, 0x00, 0xdc, + 0x97, 0xc3, 0x80, 0xc8, 0x80, 0xc2, 0x80, 0xc4, + 0xaa, 0x02, 0xdc, 0xb0, 0x0b, 0xc0, 0x02, 0xdc, + 0xc3, 0xa9, 0xc4, 0x04, 0xdc, 0xcd, 0x80, 0x00, + 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xc2, + 0x02, 0xdc, 0x42, 0x1b, 0xc2, 0x00, 0xdc, 0xc1, + 0x01, 0xdc, 0xc4, 0xb0, 0x0b, 0x00, 0x07, 0x8f, + 0x00, 0x09, 0x82, 0xc0, 0x00, 0xdc, 0xc1, 0xb0, + 0x36, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xaf, 0xc0, + 0xb0, 0x0c, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, + 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3d, + 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x4e, 0x00, + 0x09, 0xb0, 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, + 0x86, 0x00, 0x54, 0x00, 0x5b, 0xb0, 0x34, 0x00, + 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3c, 0x01, 0x09, + 0x8f, 0x00, 0x09, 0xb0, 0x4b, 0x00, 0x09, 0xb0, + 0x3c, 0x01, 0x67, 0x00, 0x09, 0x8c, 0x03, 0x6b, + 0xb0, 0x3b, 0x01, 0x76, 0x00, 0x09, 0x8c, 0x03, + 0x7a, 0xb0, 0x1b, 0x01, 0xdc, 0x9a, 0x00, 0xdc, + 0x80, 0x00, 0xdc, 0x80, 0x00, 0xd8, 0xb0, 0x06, + 0x41, 0x81, 0x80, 0x00, 0x84, 0x84, 0x03, 0x82, + 0x81, 0x00, 0x82, 0x80, 0xc1, 0x00, 0x09, 0x80, + 0xc1, 0xb0, 0x0d, 0x00, 0xdc, 0xb0, 0x3f, 0x00, + 0x07, 0x80, 0x01, 0x09, 0xb0, 0x21, 0x00, 0xdc, + 0xb2, 0x9e, 0xc2, 0xb3, 0x83, 0x01, 0x09, 0x9d, + 0x00, 0x09, 0xb0, 0x6c, 0x00, 0x09, 0x89, 0xc0, + 0xb0, 0x9a, 0x00, 0xe4, 0xb0, 0x5e, 0x00, 0xde, + 0xc0, 0x00, 0xdc, 0xb0, 0xaa, 0xc0, 0x00, 0xdc, + 0xb0, 0x16, 0x00, 0x09, 0x93, 0xc7, 0x81, 0x00, + 0xdc, 0xaf, 0xc4, 0x05, 0xdc, 0xc1, 0x00, 0xdc, + 0x80, 0x01, 0xdc, 0xc1, 0x01, 0xdc, 0xc4, 0x00, + 0xdc, 0xc3, 0xb0, 0x34, 0x00, 0x07, 0x8e, 0x00, + 0x09, 0xa5, 0xc0, 0x00, 0xdc, 0xc6, 0xb0, 0x05, + 0x01, 0x09, 0xb0, 0x09, 0x00, 0x07, 0x8a, 0x01, + 0x09, 0xb0, 0x12, 0x00, 0x07, 0xb0, 0x67, 0xc2, + 0x41, 0x00, 0x04, 0xdc, 0xc1, 0x03, 0xdc, 0xc0, + 0x41, 0x00, 0x05, 0x01, 0x83, 0x00, 0xdc, 0x85, + 0xc0, 0x82, 0xc1, 0xb0, 0x95, 0xc1, 0x00, 0xdc, + 0xc6, 0x00, 0xdc, 0xc1, 0x00, 0xea, 0x00, 0xd6, + 0x00, 0xdc, 0x00, 0xca, 0xe4, 0x00, 0xe8, 0x01, + 0xe4, 0x00, 0xdc, 0x00, 0xda, 0xc0, 0x00, 0xe9, + 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xb2, 0x9f, 0xc1, + 0x01, 0x01, 0xc3, 0x02, 0x01, 0xc1, 0x83, 0xc0, + 0x82, 0x01, 0x01, 0xc0, 0x00, 0xdc, 0xc0, 0x01, + 0x01, 0x03, 0xdc, 0xc0, 0xb8, 0x03, 0xcd, 0xc2, + 0xb0, 0x5c, 0x00, 0x09, 0xb0, 0x2f, 0xdf, 0xb1, + 0xf9, 0x00, 0xda, 0x00, 0xe4, 0x00, 0xe8, 0x00, + 0xde, 0x01, 0xe0, 0xb0, 0x38, 0x01, 0x08, 0xb8, + 0x6d, 0xa3, 0xc0, 0x83, 0xc9, 0x9f, 0xc1, 0xb0, + 0x1f, 0xc1, 0xb0, 0xe3, 0x00, 0x09, 0xa4, 0x00, + 0x09, 0xb0, 0x66, 0x00, 0x09, 0x9a, 0xd1, 0xb0, + 0x08, 0x02, 0xdc, 0xa4, 0x00, 0x09, 0xb0, 0x2e, + 0x00, 0x07, 0x8b, 0x00, 0x09, 0xb0, 0xbe, 0xc0, + 0x80, 0xc1, 0x00, 0xdc, 0x81, 0xc1, 0x84, 0xc1, + 0x80, 0xc0, 0xb0, 0x03, 0x00, 0x09, 0xb0, 0xc5, + 0x00, 0x09, 0xb8, 0x46, 0xff, 0x00, 0x1a, 0xb2, + 0xd0, 0xc6, 0x06, 0xdc, 0xc1, 0xb3, 0x9c, 0x00, + 0xdc, 0xb0, 0xb1, 0x00, 0xdc, 0xb0, 0x64, 0xc4, + 0xb6, 0x61, 0x00, 0xdc, 0x80, 0xc0, 0xa7, 0xc0, + 0x00, 0x01, 0x00, 0xdc, 0x83, 0x00, 0x09, 0xb0, + 0x74, 0xc0, 0x00, 0xdc, 0xb2, 0x0c, 0xc3, 0xb1, + 0x52, 0xc1, 0xb0, 0x1f, 0x02, 0xdc, 0xb0, 0x15, + 0x01, 0xdc, 0xc2, 0x00, 0xdc, 0xc0, 0x03, 0xdc, + 0xb0, 0x00, 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, + 0xb0, 0x8f, 0x00, 0x09, 0xa8, 0x00, 0x09, 0x8d, + 0x00, 0x09, 0xb0, 0x08, 0x00, 0x09, 0x00, 0x07, + 0xb0, 0x14, 0xc2, 0xaf, 0x01, 0x09, 0xb0, 0x0d, + 0x00, 0x07, 0xb0, 0x1b, 0x00, 0x09, 0x88, 0x00, + 0x07, 0xb0, 0x39, 0x00, 0x09, 0x00, 0x07, 0xb0, + 0x81, 0x00, 0x07, 0x00, 0x09, 0xb0, 0x1f, 0x01, + 0x07, 0x8f, 0x00, 0x09, 0x97, 0xc6, 0x82, 0xc4, + 0xb0, 0x9c, 0x00, 0x09, 0x82, 0x00, 0x07, 0x96, + 0xc0, 0xb0, 0x32, 0x00, 0x09, 0x00, 0x07, 0xb0, + 0xca, 0x00, 0x09, 0x00, 0x07, 0xb0, 0x4d, 0x00, + 0x09, 0xb0, 0x45, 0x00, 0x09, 0x00, 0x07, 0xb0, + 0x42, 0x00, 0x09, 0xb0, 0xdc, 0x00, 0x09, 0x00, + 0x07, 0xb0, 0xd1, 0x01, 0x09, 0x83, 0x00, 0x07, + 0xb0, 0x6b, 0x00, 0x09, 0xb0, 0x22, 0x00, 0x09, + 0x91, 0x00, 0x09, 0xb0, 0x20, 0x00, 0x09, 0xb1, + 0x74, 0x00, 0x09, 0xb0, 0xd1, 0x00, 0x07, 0x80, + 0x01, 0x09, 0xb0, 0x20, 0x00, 0x09, 0xb1, 0x78, + 0x01, 0x09, 0xb8, 0x43, 0x7c, 0x04, 0x01, 0xb0, + 0x0a, 0xc6, 0xb4, 0x88, 0x01, 0x06, 0xb8, 0x44, + 0x7b, 0x00, 0x01, 0xb8, 0x0c, 0x95, 0x01, 0xd8, + 0x02, 0x01, 0x82, 0x00, 0xe2, 0x04, 0xd8, 0x87, + 0x07, 0xdc, 0x81, 0xc4, 0x01, 0xdc, 0x9d, 0xc3, + 0xb0, 0x63, 0xc2, 0xb8, 0x05, 0x8a, 0xc6, 0x80, + 0xd0, 0x81, 0xc6, 0x80, 0xc1, 0x80, 0xc4, 0xb0, + 0x33, 0xc0, 0xb0, 0x6f, 0xc6, 0xb1, 0x46, 0xc0, + 0xb0, 0x0c, 0xc3, 0xb1, 0xcb, 0x01, 0xe8, 0x00, + 0xdc, 0xc0, 0xb3, 0xaf, 0x06, 0xdc, 0xb0, 0x3c, + 0xc5, 0x00, 0x07, +}; + +static const uint8_t unicode_cc_index[87] = { + 0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05, + 0x00, 0xe7, 0x06, 0x00, 0x45, 0x07, 0x00, 0x9c, + 0x08, 0x00, 0x4d, 0x09, 0x00, 0x3c, 0x0b, 0x00, + 0x3d, 0x0d, 0x00, 0x36, 0x0f, 0x00, 0x38, 0x10, + 0x20, 0x3a, 0x19, 0x00, 0xcb, 0x1a, 0x20, 0xd3, + 0x1c, 0x00, 0xcf, 0x1d, 0x00, 0xe2, 0x20, 0x00, + 0x2e, 0x30, 0x20, 0x2b, 0xa9, 0x20, 0xed, 0xab, + 0x00, 0x39, 0x0a, 0x01, 0x51, 0x0f, 0x01, 0x73, + 0x11, 0x01, 0x75, 0x13, 0x01, 0x2b, 0x17, 0x21, + 0x3f, 0x1c, 0x21, 0x9e, 0xbc, 0x21, 0x08, 0xe0, + 0x01, 0x44, 0xe9, 0x01, 0x4b, 0xe9, 0x01, +}; + +static const uint32_t unicode_decomp_table1[699] = { + 0x00280081, 0x002a0097, 0x002a8081, 0x002bc097, + 0x002c8115, 0x002d0097, 0x002d4081, 0x002e0097, + 0x002e4115, 0x002f0199, 0x00302016, 0x00400842, + 0x00448a42, 0x004a0442, 0x004c0096, 0x004c8117, + 0x004d0242, 0x004e4342, 0x004fc12f, 0x0050c342, + 0x005240bf, 0x00530342, 0x00550942, 0x005a0842, + 0x005e0096, 0x005e4342, 0x005fc081, 0x00680142, + 0x006bc142, 0x00710185, 0x0071c317, 0x00734844, + 0x00778344, 0x00798342, 0x007b02be, 0x007c4197, + 0x007d0142, 0x007e0444, 0x00800e42, 0x00878142, + 0x00898744, 0x00ac0483, 0x00b60317, 0x00b80283, + 0x00d00214, 0x00d10096, 0x00dd0080, 0x00de8097, + 0x00df8080, 0x00e10097, 0x00e1413e, 0x00e1c080, + 0x00e204be, 0x00ea83ae, 0x00f282ae, 0x00f401ad, + 0x00f4c12e, 0x00f54103, 0x00fc0303, 0x00fe4081, + 0x0100023e, 0x0101c0be, 0x010301be, 0x010640be, + 0x010e40be, 0x0114023e, 0x0115c0be, 0x011701be, + 0x011d8144, 0x01304144, 0x01340244, 0x01358144, + 0x01368344, 0x01388344, 0x013a8644, 0x013e0144, + 0x0161c085, 0x018882ae, 0x019d422f, 0x01b00184, + 0x01b4c084, 0x024a4084, 0x024c4084, 0x024d0084, + 0x0256042e, 0x0272c12e, 0x02770120, 0x0277c084, + 0x028cc084, 0x028d8084, 0x029641ae, 0x02978084, + 0x02d20084, 0x02d2c12e, 0x02d70120, 0x02e50084, + 0x02f281ae, 0x03120084, 0x03300084, 0x0331c122, + 0x0332812e, 0x035281ae, 0x03768084, 0x037701ae, + 0x038cc085, 0x03acc085, 0x03b7012f, 0x03c30081, + 0x03d0c084, 0x03d34084, 0x03d48084, 0x03d5c084, + 0x03d70084, 0x03da4084, 0x03dcc084, 0x03dd412e, + 0x03ddc085, 0x03de0084, 0x03de4085, 0x03e04084, + 0x03e4c084, 0x03e74084, 0x03e88084, 0x03e9c084, + 0x03eb0084, 0x03ee4084, 0x04098084, 0x043f0081, + 0x06c18484, 0x06c48084, 0x06cec184, 0x06d00120, + 0x06d0c084, 0x074b0383, 0x074cc41f, 0x074f1783, + 0x075e0081, 0x0766d283, 0x07801d44, 0x078e8942, + 0x07931844, 0x079f0d42, 0x07a58216, 0x07a68085, + 0x07a6c0be, 0x07a80d44, 0x07aea044, 0x07c00122, + 0x07c08344, 0x07c20122, 0x07c28344, 0x07c40122, + 0x07c48244, 0x07c60122, 0x07c68244, 0x07c8113e, + 0x07d08244, 0x07d20122, 0x07d28244, 0x07d40122, + 0x07d48344, 0x07d64c3e, 0x07dc4080, 0x07dc80be, + 0x07dcc080, 0x07dd00be, 0x07dd4080, 0x07dd80be, + 0x07ddc080, 0x07de00be, 0x07de4080, 0x07de80be, + 0x07dec080, 0x07df00be, 0x07df4080, 0x07e00820, + 0x07e40820, 0x07e80820, 0x07ec05be, 0x07eec080, + 0x07ef00be, 0x07ef4097, 0x07ef8080, 0x07efc117, + 0x07f0443e, 0x07f24080, 0x07f280be, 0x07f2c080, + 0x07f303be, 0x07f4c080, 0x07f582ae, 0x07f6c080, + 0x07f7433e, 0x07f8c080, 0x07f903ae, 0x07fac080, + 0x07fb013e, 0x07fb8102, 0x07fc83be, 0x07fe4080, + 0x07fe80be, 0x07fec080, 0x07ff00be, 0x07ff4080, + 0x07ff8097, 0x0800011e, 0x08008495, 0x08044081, + 0x0805c097, 0x08090081, 0x08094097, 0x08098099, + 0x080bc081, 0x080cc085, 0x080d00b1, 0x080d8085, + 0x080dc0b1, 0x080f0197, 0x0811c197, 0x0815c0b3, + 0x0817c081, 0x081c0595, 0x081ec081, 0x081f0215, + 0x0820051f, 0x08228583, 0x08254415, 0x082a0097, + 0x08400119, 0x08408081, 0x0840c0bf, 0x08414119, + 0x0841c081, 0x084240bf, 0x0842852d, 0x08454081, + 0x08458097, 0x08464295, 0x08480097, 0x08484099, + 0x08488097, 0x08490081, 0x08498080, 0x084a0081, + 0x084a8102, 0x084b0495, 0x084d421f, 0x084e4081, + 0x084ec099, 0x084f0283, 0x08514295, 0x08540119, + 0x0854809b, 0x0854c619, 0x0857c097, 0x08580081, + 0x08584097, 0x08588099, 0x0858c097, 0x08590081, + 0x08594097, 0x08598099, 0x0859c09b, 0x085a0097, + 0x085a4081, 0x085a8097, 0x085ac099, 0x085b0295, + 0x085c4097, 0x085c8099, 0x085cc097, 0x085d0081, + 0x085d4097, 0x085d8099, 0x085dc09b, 0x085e0097, + 0x085e4081, 0x085e8097, 0x085ec099, 0x085f0215, + 0x08624099, 0x0866813e, 0x086b80be, 0x087341be, + 0x088100be, 0x088240be, 0x088300be, 0x088901be, + 0x088b0085, 0x088b40b1, 0x088bc085, 0x088c00b1, + 0x089040be, 0x089100be, 0x0891c1be, 0x089801be, + 0x089b42be, 0x089d0144, 0x089e0144, 0x08a00144, + 0x08a10144, 0x08a20144, 0x08ab023e, 0x08b80244, + 0x08ba8220, 0x08ca411e, 0x0918049f, 0x091a4523, + 0x091cc097, 0x091d04a5, 0x091f452b, 0x0921c09b, + 0x092204a1, 0x09244525, 0x0926c099, 0x09270d25, + 0x092d8d1f, 0x09340d1f, 0x093a8081, 0x0a8300b3, + 0x0a9d0099, 0x0a9d4097, 0x0a9d8099, 0x0ab700be, + 0x0b1f0115, 0x0b5bc081, 0x0ba7c081, 0x0bbcc081, + 0x0bc004ad, 0x0bc244ad, 0x0bc484ad, 0x0bc6f383, + 0x0be0852d, 0x0be31d03, 0x0bf1882d, 0x0c000081, + 0x0c0d8283, 0x0c130b84, 0x0c194284, 0x0c1c0122, + 0x0c1cc122, 0x0c1d8122, 0x0c1e4122, 0x0c1f0122, + 0x0c250084, 0x0c26c123, 0x0c278084, 0x0c27c085, + 0x0c2b0b84, 0x0c314284, 0x0c340122, 0x0c34c122, + 0x0c358122, 0x0c364122, 0x0c370122, 0x0c3d0084, + 0x0c3dc220, 0x0c3f8084, 0x0c3fc085, 0x0c4c4a2d, + 0x0c51451f, 0x0c53ca9f, 0x0c5915ad, 0x0c648703, + 0x0c800741, 0x0c838089, 0x0c83c129, 0x0c8441a9, + 0x0c850089, 0x0c854129, 0x0c85c2a9, 0x0c870089, + 0x0c87408f, 0x0c87808d, 0x0c881241, 0x0c910203, + 0x0c940099, 0x0c9444a3, 0x0c968323, 0x0c98072d, + 0x0c9b84af, 0x0c9dc2a1, 0x0c9f00b5, 0x0c9f40b3, + 0x0c9f8085, 0x0ca01883, 0x0cac4223, 0x0cad4523, + 0x0cafc097, 0x0cb004a1, 0x0cb241a5, 0x0cb30097, + 0x0cb34099, 0x0cb38097, 0x0cb3c099, 0x0cb417ad, + 0x0cbfc085, 0x0cc001b3, 0x0cc0c0b1, 0x0cc100b3, + 0x0cc14131, 0x0cc1c0b5, 0x0cc200b3, 0x0cc241b1, + 0x0cc30133, 0x0cc38131, 0x0cc40085, 0x0cc440b1, + 0x0cc48133, 0x0cc50085, 0x0cc540b5, 0x0cc580b7, + 0x0cc5c0b5, 0x0cc600b1, 0x0cc64135, 0x0cc6c0b3, + 0x0cc701b1, 0x0cc7c0b3, 0x0cc800b5, 0x0cc840b3, + 0x0cc881b1, 0x0cc9422f, 0x0cca4131, 0x0ccac0b5, + 0x0ccb00b1, 0x0ccb40b3, 0x0ccb80b5, 0x0ccbc0b1, + 0x0ccc012f, 0x0ccc80b5, 0x0cccc0b3, 0x0ccd00b5, + 0x0ccd40b1, 0x0ccd80b5, 0x0ccdc085, 0x0cce02b1, + 0x0ccf40b3, 0x0ccf80b1, 0x0ccfc085, 0x0cd001b1, + 0x0cd0c0b3, 0x0cd101b1, 0x0cd1c0b5, 0x0cd200b3, + 0x0cd24085, 0x0cd280b5, 0x0cd2c085, 0x0cd30133, + 0x0cd381b1, 0x0cd440b3, 0x0cd48085, 0x0cd4c0b1, + 0x0cd500b3, 0x0cd54085, 0x0cd580b5, 0x0cd5c0b1, + 0x0cd60521, 0x0cd88525, 0x0cdb02a5, 0x0cdc4099, + 0x0cdc8117, 0x0cdd0099, 0x0cdd4197, 0x0cde0127, + 0x0cde8285, 0x0cdfc089, 0x0ce0043f, 0x0ce20099, + 0x0ce2409b, 0x0ce283bf, 0x0ce44219, 0x0ce54205, + 0x0ce6433f, 0x0ce7c131, 0x0ce84085, 0x0ce881b1, + 0x0ce94085, 0x0ce98107, 0x0cea0089, 0x0cea4097, + 0x0cea8219, 0x0ceb809d, 0x0cebc08d, 0x0cec083f, + 0x0cf00105, 0x0cf0809b, 0x0cf0c197, 0x0cf1809b, + 0x0cf1c099, 0x0cf20517, 0x0cf48099, 0x0cf4c117, + 0x0cf54119, 0x0cf5c097, 0x0cf6009b, 0x0cf64099, + 0x0cf68217, 0x0cf78119, 0x0cf804a1, 0x0cfa4525, + 0x0cfcc525, 0x0cff4125, 0x0cffc099, 0x29a70103, + 0x29dc0081, 0x29fc8195, 0x29fe0103, 0x2ad70203, + 0x2ada4081, 0x3e401482, 0x3e4a7f82, 0x3e6a3f82, + 0x3e8aa102, 0x3e9b0110, 0x3e9c2f82, 0x3eb3c590, + 0x3ec00197, 0x3ec0c119, 0x3ec1413f, 0x3ec4c2af, + 0x3ec74184, 0x3ec804ad, 0x3eca4081, 0x3eca8304, + 0x3ecc03a0, 0x3ece02a0, 0x3ecf8084, 0x3ed00120, + 0x3ed0c120, 0x3ed184ae, 0x3ed3c085, 0x3ed4312d, + 0x3ef4cbad, 0x3efa892f, 0x3eff022d, 0x3f002f2f, + 0x3f1782a5, 0x3f18c0b1, 0x3f1907af, 0x3f1cffaf, + 0x3f3c81a5, 0x3f3d64af, 0x3f542031, 0x3f649b31, + 0x3f7c0131, 0x3f7c83b3, 0x3f7e40b1, 0x3f7e80bd, + 0x3f7ec0bb, 0x3f7f00b3, 0x3f840503, 0x3f8c01ad, + 0x3f8cc315, 0x3f8e462d, 0x3f91cc03, 0x3f97c695, + 0x3f9c01af, 0x3f9d0085, 0x3f9d852f, 0x3fa03aad, + 0x3fbd442f, 0x3fc06f1f, 0x3fd7c11f, 0x3fd85fad, + 0x3fe80081, 0x3fe84f1f, 0x3ff0831f, 0x3ff2831f, + 0x3ff4831f, 0x3ff6819f, 0x3ff80783, 0x41e04d83, + 0x41e70f91, 0x44268192, 0x442ac092, 0x444b8112, + 0x44d2c112, 0x452ec212, 0x456e8112, 0x464e0092, + 0x74578392, 0x746ec312, 0x75000d1f, 0x75068d1f, + 0x750d0d1f, 0x7513839f, 0x7515891f, 0x751a0d1f, + 0x75208d1f, 0x75271015, 0x752f439f, 0x7531459f, + 0x75340d1f, 0x753a8d1f, 0x75410395, 0x7543441f, + 0x7545839f, 0x75478d1f, 0x754e0795, 0x7552839f, + 0x75548d1f, 0x755b0d1f, 0x75618d1f, 0x75680d1f, + 0x756e8d1f, 0x75750d1f, 0x757b8d1f, 0x75820d1f, + 0x75888d1f, 0x758f0d1f, 0x75958d1f, 0x759c0d1f, + 0x75a28d1f, 0x75a90103, 0x75aa089f, 0x75ae4081, + 0x75ae839f, 0x75b04081, 0x75b08c9f, 0x75b6c081, + 0x75b7032d, 0x75b8889f, 0x75bcc081, 0x75bd039f, + 0x75bec081, 0x75bf0c9f, 0x75c54081, 0x75c5832d, + 0x75c7089f, 0x75cb4081, 0x75cb839f, 0x75cd4081, + 0x75cd8c9f, 0x75d3c081, 0x75d4032d, 0x75d5889f, + 0x75d9c081, 0x75da039f, 0x75dbc081, 0x75dc0c9f, + 0x75e24081, 0x75e2832d, 0x75e4089f, 0x75e84081, + 0x75e8839f, 0x75ea4081, 0x75ea8c9f, 0x75f0c081, + 0x75f1042d, 0x75f3851f, 0x75f6051f, 0x75f8851f, + 0x75fb051f, 0x75fd851f, 0x780c049f, 0x780e419f, + 0x780f059f, 0x7811c203, 0x7812d0ad, 0x781b0103, + 0x7b80022d, 0x7b814dad, 0x7b884203, 0x7b89c081, + 0x7b8a452d, 0x7b8d0403, 0x7b908081, 0x7b91dc03, + 0x7ba0052d, 0x7ba2c8ad, 0x7ba84483, 0x7baac8ad, + 0x7c400097, 0x7c404521, 0x7c440d25, 0x7c4a8087, + 0x7c4ac115, 0x7c4b4117, 0x7c4c0d1f, 0x7c528217, + 0x7c538099, 0x7c53c097, 0x7c5a8197, 0x7c640097, + 0x7c80012f, 0x7c808081, 0x7c841603, 0x7c9004c1, + 0x7c940103, 0x7efc051f, 0xbe0001ac, 0xbe00d110, + 0xbe0947ac, 0xbe0d3910, 0xbe29872c, 0xbe2d022c, + 0xbe2e3790, 0xbe49ff90, 0xbe69bc10, +}; + +static const uint16_t unicode_decomp_table2[699] = { + 0x0020, 0x0000, 0x0061, 0x0002, 0x0004, 0x0006, 0x03bc, 0x0008, + 0x000a, 0x000c, 0x0015, 0x0095, 0x00a5, 0x00b9, 0x00c1, 0x00c3, + 0x00c7, 0x00cb, 0x00d1, 0x00d7, 0x00dd, 0x00e0, 0x00e6, 0x00f8, + 0x0108, 0x010a, 0x0073, 0x0110, 0x0112, 0x0114, 0x0120, 0x012c, + 0x0144, 0x014d, 0x0153, 0x0162, 0x0168, 0x016a, 0x0176, 0x0192, + 0x0194, 0x01a9, 0x01bb, 0x01c7, 0x01d1, 0x01d5, 0x02b9, 0x01d7, + 0x003b, 0x01d9, 0x01db, 0x00b7, 0x01e1, 0x01fc, 0x020c, 0x0218, + 0x021d, 0x0223, 0x0227, 0x03a3, 0x0233, 0x023f, 0x0242, 0x024b, + 0x024e, 0x0251, 0x025d, 0x0260, 0x0269, 0x026c, 0x026f, 0x0275, + 0x0278, 0x0281, 0x028a, 0x029c, 0x029f, 0x02a3, 0x02af, 0x02b9, + 0x02c5, 0x02c9, 0x02cd, 0x02d1, 0x02d5, 0x02e7, 0x02ed, 0x02f1, + 0x02f5, 0x02f9, 0x02fd, 0x0305, 0x0309, 0x030d, 0x0313, 0x0317, + 0x031b, 0x0323, 0x0327, 0x032b, 0x032f, 0x0335, 0x033d, 0x0341, + 0x0349, 0x034d, 0x0351, 0x0f0b, 0x0357, 0x035b, 0x035f, 0x0363, + 0x0367, 0x036b, 0x036f, 0x0373, 0x0379, 0x037d, 0x0381, 0x0385, + 0x0389, 0x038d, 0x0391, 0x0395, 0x0399, 0x039d, 0x03a1, 0x10dc, + 0x03a5, 0x03c9, 0x03cd, 0x03d9, 0x03dd, 0x03e1, 0x03ef, 0x03f1, + 0x043d, 0x044f, 0x0499, 0x04f0, 0x0502, 0x054a, 0x0564, 0x056c, + 0x0570, 0x0573, 0x059a, 0x05fa, 0x05fe, 0x0607, 0x060b, 0x0614, + 0x0618, 0x061e, 0x0622, 0x0628, 0x068e, 0x0694, 0x0698, 0x069e, + 0x06a2, 0x06ab, 0x03ac, 0x06f3, 0x03ad, 0x06f6, 0x03ae, 0x06f9, + 0x03af, 0x06fc, 0x03cc, 0x06ff, 0x03cd, 0x0702, 0x03ce, 0x0705, + 0x0709, 0x070d, 0x0711, 0x0386, 0x0732, 0x0735, 0x03b9, 0x0737, + 0x073b, 0x0388, 0x0753, 0x0389, 0x0756, 0x0390, 0x076b, 0x038a, + 0x0777, 0x03b0, 0x0789, 0x038e, 0x0799, 0x079f, 0x07a3, 0x038c, + 0x07b8, 0x038f, 0x07bb, 0x00b4, 0x07be, 0x07c0, 0x07c2, 0x2010, + 0x07cb, 0x002e, 0x07cd, 0x07cf, 0x0020, 0x07d2, 0x07d6, 0x07db, + 0x07df, 0x07e4, 0x07ea, 0x07f0, 0x0020, 0x07f6, 0x2212, 0x0801, + 0x0805, 0x0807, 0x081d, 0x0825, 0x0827, 0x0043, 0x082d, 0x0830, + 0x0190, 0x0836, 0x0839, 0x004e, 0x0845, 0x0847, 0x084c, 0x084e, + 0x0851, 0x005a, 0x03a9, 0x005a, 0x0853, 0x0857, 0x0860, 0x0069, + 0x0862, 0x0865, 0x086f, 0x0874, 0x087a, 0x087e, 0x08a2, 0x0049, + 0x08a4, 0x08a6, 0x08a9, 0x0056, 0x08ab, 0x08ad, 0x08b0, 0x08b4, + 0x0058, 0x08b6, 0x08b8, 0x08bb, 0x08c0, 0x08c2, 0x08c5, 0x0076, + 0x08c7, 0x08c9, 0x08cc, 0x08d0, 0x0078, 0x08d2, 0x08d4, 0x08d7, + 0x08db, 0x08de, 0x08e4, 0x08e7, 0x08f0, 0x08f3, 0x08f6, 0x08f9, + 0x0902, 0x0906, 0x090b, 0x090f, 0x0914, 0x0917, 0x091a, 0x0923, + 0x092c, 0x093b, 0x093e, 0x0941, 0x0944, 0x0947, 0x094a, 0x0956, + 0x095c, 0x0960, 0x0962, 0x0964, 0x0968, 0x096a, 0x0970, 0x0978, + 0x097c, 0x0980, 0x0986, 0x0989, 0x098f, 0x0991, 0x0030, 0x0993, + 0x0999, 0x099c, 0x099e, 0x09a1, 0x09a4, 0x2d61, 0x6bcd, 0x9f9f, + 0x09a6, 0x09b1, 0x09bc, 0x09c7, 0x0a95, 0x0aa1, 0x0b15, 0x0020, + 0x0b27, 0x0b31, 0x0b8d, 0x0ba1, 0x0ba5, 0x0ba9, 0x0bad, 0x0bb1, + 0x0bb5, 0x0bb9, 0x0bbd, 0x0bc1, 0x0bc5, 0x0c21, 0x0c35, 0x0c39, + 0x0c3d, 0x0c41, 0x0c45, 0x0c49, 0x0c4d, 0x0c51, 0x0c55, 0x0c59, + 0x0c6f, 0x0c71, 0x0c73, 0x0ca0, 0x0cbc, 0x0cdc, 0x0ce4, 0x0cec, + 0x0cf4, 0x0cfc, 0x0d04, 0x0d0c, 0x0d14, 0x0d22, 0x0d2e, 0x0d7a, + 0x0d82, 0x0d85, 0x0d89, 0x0d8d, 0x0d9d, 0x0db1, 0x0db5, 0x0dbc, + 0x0dc2, 0x0dc6, 0x0e28, 0x0e2c, 0x0e30, 0x0e32, 0x0e36, 0x0e3c, + 0x0e3e, 0x0e41, 0x0e43, 0x0e46, 0x0e77, 0x0e7b, 0x0e89, 0x0e8e, + 0x0e94, 0x0e9c, 0x0ea3, 0x0ea9, 0x0eb4, 0x0ebe, 0x0ec6, 0x0eca, + 0x0ecf, 0x0ed9, 0x0edd, 0x0ee4, 0x0eec, 0x0ef3, 0x0ef8, 0x0f04, + 0x0f0a, 0x0f15, 0x0f1b, 0x0f22, 0x0f28, 0x0f33, 0x0f3d, 0x0f45, + 0x0f4c, 0x0f51, 0x0f57, 0x0f5e, 0x0f63, 0x0f69, 0x0f70, 0x0f76, + 0x0f7d, 0x0f82, 0x0f89, 0x0f8d, 0x0f9e, 0x0fa4, 0x0fa9, 0x0fad, + 0x0fb8, 0x0fbe, 0x0fc9, 0x0fd0, 0x0fd6, 0x0fda, 0x0fe1, 0x0fe5, + 0x0fef, 0x0ffa, 0x1000, 0x1004, 0x1009, 0x100f, 0x1013, 0x101a, + 0x101f, 0x1023, 0x1029, 0x102f, 0x1032, 0x1036, 0x1039, 0x103f, + 0x1045, 0x1059, 0x1061, 0x1079, 0x107c, 0x1080, 0x1095, 0x10a1, + 0x10b1, 0x10c3, 0x10cb, 0x10cf, 0x10da, 0x10de, 0x10ea, 0x10f2, + 0x10f4, 0x1100, 0x1105, 0x1111, 0x1141, 0x1149, 0x114d, 0x1153, + 0x1157, 0x115a, 0x116e, 0x1171, 0x1175, 0x117b, 0x117d, 0x1181, + 0x1184, 0x118c, 0x1192, 0x1196, 0x119c, 0x11a2, 0x11a8, 0x11ab, + 0xa76f, 0x11af, 0x11b2, 0x11b6, 0x028d, 0x11be, 0x1210, 0x130e, + 0x140c, 0x1490, 0x1495, 0x1553, 0x156c, 0x1572, 0x1578, 0x157e, + 0x158a, 0x1596, 0x002b, 0x15a1, 0x15b9, 0x15bd, 0x15c1, 0x15c5, + 0x15c9, 0x15cd, 0x15e1, 0x15e5, 0x1649, 0x1662, 0x1688, 0x168e, + 0x174c, 0x1752, 0x1757, 0x1777, 0x1877, 0x187d, 0x1911, 0x19d3, + 0x1a77, 0x1a7f, 0x1a9d, 0x1aa2, 0x1ab6, 0x1ac0, 0x1ac6, 0x1ada, + 0x1adf, 0x1ae5, 0x1af3, 0x1b23, 0x1b30, 0x1b38, 0x1b3c, 0x1b52, + 0x1bc9, 0x1bdb, 0x1bdd, 0x1bdf, 0x3164, 0x1c20, 0x1c22, 0x1c24, + 0x1c26, 0x1c28, 0x1c2a, 0x1c48, 0x1c7e, 0x1cc4, 0x1cd2, 0x1cd7, + 0x1ce0, 0x1ce9, 0x1cfb, 0x1d04, 0x1d09, 0x1d29, 0x1d44, 0x1d46, + 0x1d48, 0x1d4a, 0x1d4c, 0x1d4e, 0x1d50, 0x1d52, 0x1d72, 0x1d74, + 0x1d76, 0x1d78, 0x1d7a, 0x1d81, 0x1d83, 0x1d85, 0x1d87, 0x1d96, + 0x1d98, 0x1d9a, 0x1d9c, 0x1d9e, 0x1da0, 0x1da2, 0x1da4, 0x1da6, + 0x1da8, 0x1daa, 0x1dac, 0x1dae, 0x1db0, 0x1db2, 0x1db6, 0x03f4, + 0x1db8, 0x2207, 0x1dba, 0x2202, 0x1dbc, 0x1dc4, 0x03f4, 0x1dc6, + 0x2207, 0x1dc8, 0x2202, 0x1dca, 0x1dd2, 0x03f4, 0x1dd4, 0x2207, + 0x1dd6, 0x2202, 0x1dd8, 0x1de0, 0x03f4, 0x1de2, 0x2207, 0x1de4, + 0x2202, 0x1de6, 0x1dee, 0x03f4, 0x1df0, 0x2207, 0x1df2, 0x2202, + 0x1df4, 0x1dfe, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, + 0x1e0c, 0x1e0e, 0x1e16, 0x1e39, 0x1e3d, 0x1e43, 0x1e60, 0x062d, + 0x1e68, 0x1e74, 0x062c, 0x1e84, 0x1ef4, 0x1f00, 0x1f13, 0x1f25, + 0x1f38, 0x1f3a, 0x1f3e, 0x1f44, 0x1f4a, 0x1f4c, 0x1f50, 0x1f52, + 0x1f5a, 0x1f5d, 0x1f5f, 0x1f65, 0x1f67, 0x30b5, 0x1f6d, 0x1fc5, + 0x1fdb, 0x1fdf, 0x1fe1, 0x1fe6, 0x2033, 0x2044, 0x2145, 0x2155, + 0x215b, 0x2255, 0x2373, +}; + +static const uint8_t unicode_decomp_data[9345] = { + 0x20, 0x88, 0x20, 0x84, 0x32, 0x33, 0x20, 0x81, + 0x20, 0xa7, 0x31, 0x6f, 0x31, 0xd0, 0x34, 0x31, + 0xd0, 0x32, 0x33, 0xd0, 0x34, 0x41, 0x80, 0x41, + 0x81, 0x41, 0x82, 0x41, 0x83, 0x41, 0x88, 0x41, + 0x8a, 0x00, 0x00, 0x43, 0xa7, 0x45, 0x80, 0x45, + 0x81, 0x45, 0x82, 0x45, 0x88, 0x49, 0x80, 0x49, + 0x81, 0x49, 0x82, 0x49, 0x88, 0x00, 0x00, 0x4e, + 0x83, 0x4f, 0x80, 0x4f, 0x81, 0x4f, 0x82, 0x4f, + 0x83, 0x4f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x55, + 0x80, 0x55, 0x81, 0x55, 0x82, 0x55, 0x88, 0x59, + 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x80, 0x61, + 0x81, 0x61, 0x82, 0x61, 0x83, 0x61, 0x88, 0x61, + 0x8a, 0x00, 0x00, 0x63, 0xa7, 0x65, 0x80, 0x65, + 0x81, 0x65, 0x82, 0x65, 0x88, 0x69, 0x80, 0x69, + 0x81, 0x69, 0x82, 0x69, 0x88, 0x00, 0x00, 0x6e, + 0x83, 0x6f, 0x80, 0x6f, 0x81, 0x6f, 0x82, 0x6f, + 0x83, 0x6f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x75, + 0x80, 0x75, 0x81, 0x75, 0x82, 0x75, 0x88, 0x79, + 0x81, 0x00, 0x00, 0x79, 0x88, 0x41, 0x84, 0x41, + 0x86, 0x41, 0xa8, 0x43, 0x81, 0x43, 0x82, 0x43, + 0x87, 0x43, 0x8c, 0x44, 0x8c, 0x45, 0x84, 0x45, + 0x86, 0x45, 0x87, 0x45, 0xa8, 0x45, 0x8c, 0x47, + 0x82, 0x47, 0x86, 0x47, 0x87, 0x47, 0xa7, 0x48, + 0x82, 0x49, 0x83, 0x49, 0x84, 0x49, 0x86, 0x49, + 0xa8, 0x49, 0x87, 0x49, 0x4a, 0x69, 0x6a, 0x4a, + 0x82, 0x4b, 0xa7, 0x4c, 0x81, 0x4c, 0xa7, 0x4c, + 0x8c, 0x4c, 0x00, 0x00, 0x6b, 0x20, 0x6b, 0x4e, + 0x81, 0x4e, 0xa7, 0x4e, 0x8c, 0xbc, 0x02, 0x6e, + 0x4f, 0x84, 0x4f, 0x86, 0x4f, 0x8b, 0x52, 0x81, + 0x52, 0xa7, 0x52, 0x8c, 0x53, 0x81, 0x53, 0x82, + 0x53, 0xa7, 0x53, 0x8c, 0x54, 0xa7, 0x54, 0x8c, + 0x55, 0x83, 0x55, 0x84, 0x55, 0x86, 0x55, 0x8a, + 0x55, 0x8b, 0x55, 0xa8, 0x57, 0x82, 0x59, 0x82, + 0x59, 0x88, 0x5a, 0x81, 0x5a, 0x87, 0x5a, 0x8c, + 0x4f, 0x9b, 0x55, 0x9b, 0x44, 0x00, 0x7d, 0x01, + 0x44, 0x00, 0x7e, 0x01, 0x64, 0x00, 0x7e, 0x01, + 0x4c, 0x4a, 0x4c, 0x6a, 0x6c, 0x6a, 0x4e, 0x4a, + 0x4e, 0x6a, 0x6e, 0x6a, 0x41, 0x00, 0x8c, 0x49, + 0x00, 0x8c, 0x4f, 0x00, 0x8c, 0x55, 0x00, 0x8c, + 0xdc, 0x00, 0x84, 0xdc, 0x00, 0x81, 0xdc, 0x00, + 0x8c, 0xdc, 0x00, 0x80, 0xc4, 0x00, 0x84, 0x26, + 0x02, 0x84, 0xc6, 0x00, 0x84, 0x47, 0x8c, 0x4b, + 0x8c, 0x4f, 0xa8, 0xea, 0x01, 0x84, 0xeb, 0x01, + 0x84, 0xb7, 0x01, 0x8c, 0x92, 0x02, 0x8c, 0x6a, + 0x00, 0x8c, 0x44, 0x5a, 0x44, 0x7a, 0x64, 0x7a, + 0x47, 0x81, 0x4e, 0x00, 0x80, 0xc5, 0x00, 0x81, + 0xc6, 0x00, 0x81, 0xd8, 0x00, 0x81, 0x41, 0x8f, + 0x41, 0x91, 0x45, 0x8f, 0x45, 0x91, 0x49, 0x8f, + 0x49, 0x91, 0x4f, 0x8f, 0x4f, 0x91, 0x52, 0x8f, + 0x52, 0x91, 0x55, 0x8f, 0x55, 0x91, 0x53, 0xa6, + 0x54, 0xa6, 0x48, 0x8c, 0x41, 0x00, 0x87, 0x45, + 0x00, 0xa7, 0xd6, 0x00, 0x84, 0xd5, 0x00, 0x84, + 0x4f, 0x00, 0x87, 0x2e, 0x02, 0x84, 0x59, 0x00, + 0x84, 0x68, 0x00, 0x66, 0x02, 0x6a, 0x00, 0x72, + 0x00, 0x79, 0x02, 0x7b, 0x02, 0x81, 0x02, 0x77, + 0x00, 0x79, 0x00, 0x20, 0x86, 0x20, 0x87, 0x20, + 0x8a, 0x20, 0xa8, 0x20, 0x83, 0x20, 0x8b, 0x63, + 0x02, 0x6c, 0x00, 0x73, 0x00, 0x78, 0x00, 0x95, + 0x02, 0x80, 0x81, 0x00, 0x93, 0x88, 0x81, 0x20, + 0xc5, 0x20, 0x81, 0xa8, 0x00, 0x81, 0x91, 0x03, + 0x81, 0x95, 0x03, 0x81, 0x97, 0x03, 0x81, 0x99, + 0x03, 0x81, 0x00, 0x00, 0x00, 0x9f, 0x03, 0x81, + 0x00, 0x00, 0x00, 0xa5, 0x03, 0x81, 0xa9, 0x03, + 0x81, 0xca, 0x03, 0x81, 0x01, 0x03, 0x98, 0x07, + 0xa4, 0x07, 0xb0, 0x00, 0xb4, 0x00, 0xb6, 0x00, + 0xb8, 0x00, 0xca, 0x00, 0x01, 0x03, 0xb8, 0x07, + 0xc4, 0x07, 0xbe, 0x00, 0xc4, 0x00, 0xc8, 0x00, + 0xa5, 0x03, 0x0d, 0x13, 0x00, 0x01, 0x03, 0xd1, + 0x00, 0xd1, 0x07, 0xc6, 0x03, 0xc0, 0x03, 0xba, + 0x03, 0xc1, 0x03, 0xc2, 0x03, 0x00, 0x00, 0x98, + 0x03, 0xb5, 0x03, 0x15, 0x04, 0x80, 0x15, 0x04, + 0x88, 0x00, 0x00, 0x00, 0x13, 0x04, 0x81, 0x06, + 0x04, 0x88, 0x1a, 0x04, 0x81, 0x18, 0x04, 0x80, + 0x23, 0x04, 0x86, 0x18, 0x04, 0x86, 0x38, 0x04, + 0x86, 0x35, 0x04, 0x80, 0x35, 0x04, 0x88, 0x00, + 0x00, 0x00, 0x33, 0x04, 0x81, 0x56, 0x04, 0x88, + 0x3a, 0x04, 0x81, 0x38, 0x04, 0x80, 0x43, 0x04, + 0x86, 0x74, 0x04, 0x8f, 0x16, 0x04, 0x86, 0x10, + 0x04, 0x86, 0x10, 0x04, 0x88, 0x15, 0x04, 0x86, + 0xd8, 0x04, 0x88, 0x16, 0x04, 0x88, 0x17, 0x04, + 0x88, 0x18, 0x04, 0x84, 0x18, 0x04, 0x88, 0x1e, + 0x04, 0x88, 0xe8, 0x04, 0x88, 0x2d, 0x04, 0x88, + 0x23, 0x04, 0x84, 0x23, 0x04, 0x88, 0x23, 0x04, + 0x8b, 0x27, 0x04, 0x88, 0x2b, 0x04, 0x88, 0x65, + 0x05, 0x82, 0x05, 0x27, 0x06, 0x00, 0x2c, 0x00, + 0x2d, 0x21, 0x2d, 0x00, 0x2e, 0x23, 0x2d, 0x27, + 0x06, 0x00, 0x4d, 0x21, 0x4d, 0xa0, 0x4d, 0x23, + 0x4d, 0xd5, 0x06, 0x54, 0x06, 0x00, 0x00, 0x00, + 0x00, 0xc1, 0x06, 0x54, 0x06, 0xd2, 0x06, 0x54, + 0x06, 0x28, 0x09, 0x3c, 0x09, 0x30, 0x09, 0x3c, + 0x09, 0x33, 0x09, 0x3c, 0x09, 0x15, 0x09, 0x00, + 0x27, 0x01, 0x27, 0x02, 0x27, 0x07, 0x27, 0x0c, + 0x27, 0x0d, 0x27, 0x16, 0x27, 0x1a, 0x27, 0xbe, + 0x09, 0x09, 0x00, 0x09, 0x19, 0xa1, 0x09, 0xbc, + 0x09, 0xaf, 0x09, 0xbc, 0x09, 0x32, 0x0a, 0x3c, + 0x0a, 0x38, 0x0a, 0x3c, 0x0a, 0x16, 0x0a, 0x00, + 0x26, 0x01, 0x26, 0x06, 0x26, 0x2b, 0x0a, 0x3c, + 0x0a, 0x47, 0x0b, 0x56, 0x0b, 0x3e, 0x0b, 0x09, + 0x00, 0x09, 0x19, 0x21, 0x0b, 0x3c, 0x0b, 0x92, + 0x0b, 0xd7, 0x0b, 0xbe, 0x0b, 0x08, 0x00, 0x09, + 0x00, 0x08, 0x19, 0x46, 0x0c, 0x56, 0x0c, 0xbf, + 0x0c, 0xd5, 0x0c, 0xc6, 0x0c, 0xd5, 0x0c, 0xc2, + 0x0c, 0x04, 0x00, 0x08, 0x13, 0x3e, 0x0d, 0x08, + 0x00, 0x09, 0x00, 0x08, 0x19, 0xd9, 0x0d, 0xca, + 0x0d, 0xca, 0x0d, 0x0f, 0x05, 0x12, 0x00, 0x0f, + 0x15, 0x4d, 0x0e, 0x32, 0x0e, 0xcd, 0x0e, 0xb2, + 0x0e, 0x99, 0x0e, 0x12, 0x00, 0x12, 0x08, 0x42, + 0x0f, 0xb7, 0x0f, 0x4c, 0x0f, 0xb7, 0x0f, 0x51, + 0x0f, 0xb7, 0x0f, 0x56, 0x0f, 0xb7, 0x0f, 0x5b, + 0x0f, 0xb7, 0x0f, 0x40, 0x0f, 0xb5, 0x0f, 0x71, + 0x0f, 0x72, 0x0f, 0x71, 0x0f, 0x00, 0x03, 0x41, + 0x0f, 0xb2, 0x0f, 0x81, 0x0f, 0xb3, 0x0f, 0x80, + 0x0f, 0xb3, 0x0f, 0x81, 0x0f, 0x71, 0x0f, 0x80, + 0x0f, 0x92, 0x0f, 0xb7, 0x0f, 0x9c, 0x0f, 0xb7, + 0x0f, 0xa1, 0x0f, 0xb7, 0x0f, 0xa6, 0x0f, 0xb7, + 0x0f, 0xab, 0x0f, 0xb7, 0x0f, 0x90, 0x0f, 0xb5, + 0x0f, 0x25, 0x10, 0x2e, 0x10, 0x05, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x09, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x1b, 0x35, + 0x1b, 0x11, 0x1b, 0x35, 0x1b, 0x3a, 0x1b, 0x35, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x1b, 0x35, + 0x1b, 0x3e, 0x1b, 0x35, 0x1b, 0x42, 0x1b, 0x35, + 0x1b, 0x41, 0x00, 0xc6, 0x00, 0x42, 0x00, 0x00, + 0x00, 0x44, 0x00, 0x45, 0x00, 0x8e, 0x01, 0x47, + 0x00, 0x4f, 0x00, 0x22, 0x02, 0x50, 0x00, 0x52, + 0x00, 0x54, 0x00, 0x55, 0x00, 0x57, 0x00, 0x61, + 0x00, 0x50, 0x02, 0x51, 0x02, 0x02, 0x1d, 0x62, + 0x00, 0x64, 0x00, 0x65, 0x00, 0x59, 0x02, 0x5b, + 0x02, 0x5c, 0x02, 0x67, 0x00, 0x00, 0x00, 0x6b, + 0x00, 0x6d, 0x00, 0x4b, 0x01, 0x6f, 0x00, 0x54, + 0x02, 0x16, 0x1d, 0x17, 0x1d, 0x70, 0x00, 0x74, + 0x00, 0x75, 0x00, 0x1d, 0x1d, 0x6f, 0x02, 0x76, + 0x00, 0x25, 0x1d, 0xb2, 0x03, 0xb3, 0x03, 0xb4, + 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x69, 0x00, 0x72, + 0x00, 0x75, 0x00, 0x76, 0x00, 0xb2, 0x03, 0xb3, + 0x03, 0xc1, 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x52, + 0x02, 0x63, 0x00, 0x55, 0x02, 0xf0, 0x00, 0x5c, + 0x02, 0x66, 0x00, 0x5f, 0x02, 0x61, 0x02, 0x65, + 0x02, 0x68, 0x02, 0x69, 0x02, 0x6a, 0x02, 0x7b, + 0x1d, 0x9d, 0x02, 0x6d, 0x02, 0x85, 0x1d, 0x9f, + 0x02, 0x71, 0x02, 0x70, 0x02, 0x72, 0x02, 0x73, + 0x02, 0x74, 0x02, 0x75, 0x02, 0x78, 0x02, 0x82, + 0x02, 0x83, 0x02, 0xab, 0x01, 0x89, 0x02, 0x8a, + 0x02, 0x1c, 0x1d, 0x8b, 0x02, 0x8c, 0x02, 0x7a, + 0x00, 0x90, 0x02, 0x91, 0x02, 0x92, 0x02, 0xb8, + 0x03, 0x41, 0x00, 0xa5, 0x42, 0x00, 0x87, 0x42, + 0x00, 0xa3, 0x42, 0x00, 0xb1, 0xc7, 0x00, 0x81, + 0x44, 0x00, 0x87, 0x44, 0x00, 0xa3, 0x44, 0x00, + 0xb1, 0x44, 0x00, 0xa7, 0x44, 0x00, 0xad, 0x12, + 0x01, 0x80, 0x12, 0x01, 0x81, 0x45, 0x00, 0xad, + 0x45, 0x00, 0xb0, 0x28, 0x02, 0x86, 0x46, 0x00, + 0x87, 0x47, 0x00, 0x84, 0x48, 0x00, 0x87, 0x48, + 0x00, 0xa3, 0x48, 0x00, 0x88, 0x48, 0x00, 0xa7, + 0x48, 0x00, 0xae, 0x49, 0x00, 0xb0, 0xcf, 0x00, + 0x81, 0x4b, 0x00, 0x81, 0x4b, 0x00, 0xa3, 0x4b, + 0x00, 0xb1, 0x4c, 0x00, 0xa3, 0x36, 0x1e, 0x84, + 0x4c, 0xb1, 0x4c, 0xad, 0x4d, 0x81, 0x4d, 0x87, + 0x4d, 0xa3, 0x4e, 0x87, 0x4e, 0xa3, 0x4e, 0xb1, + 0x4e, 0xad, 0xd5, 0x00, 0x81, 0xd5, 0x00, 0x88, + 0x4c, 0x01, 0x80, 0x4c, 0x01, 0x81, 0x50, 0x00, + 0x81, 0x50, 0x00, 0x87, 0x52, 0x00, 0x87, 0x52, + 0x00, 0xa3, 0x5a, 0x1e, 0x84, 0x52, 0x00, 0xb1, + 0x53, 0x00, 0x87, 0x53, 0x00, 0xa3, 0x5a, 0x01, + 0x87, 0x60, 0x01, 0x87, 0x62, 0x1e, 0x87, 0x54, + 0x00, 0x87, 0x54, 0x00, 0xa3, 0x54, 0x00, 0xb1, + 0x54, 0x00, 0xad, 0x55, 0x00, 0xa4, 0x55, 0x00, + 0xb0, 0x55, 0x00, 0xad, 0x68, 0x01, 0x81, 0x6a, + 0x01, 0x88, 0x56, 0x83, 0x56, 0xa3, 0x57, 0x80, + 0x57, 0x81, 0x57, 0x88, 0x57, 0x87, 0x57, 0xa3, + 0x58, 0x87, 0x58, 0x88, 0x59, 0x87, 0x5a, 0x82, + 0x5a, 0xa3, 0x5a, 0xb1, 0x68, 0xb1, 0x74, 0x88, + 0x77, 0x8a, 0x79, 0x8a, 0x61, 0x00, 0xbe, 0x02, + 0x7f, 0x01, 0x87, 0x41, 0x00, 0xa3, 0x41, 0x00, + 0x89, 0xc2, 0x00, 0x81, 0xc2, 0x00, 0x80, 0xc2, + 0x00, 0x89, 0xc2, 0x00, 0x83, 0xa0, 0x1e, 0x82, + 0x02, 0x01, 0x81, 0x02, 0x01, 0x80, 0x02, 0x01, + 0x89, 0x02, 0x01, 0x83, 0xa0, 0x1e, 0x86, 0x45, + 0x00, 0xa3, 0x45, 0x00, 0x89, 0x45, 0x00, 0x83, + 0xca, 0x00, 0x81, 0xca, 0x00, 0x80, 0xca, 0x00, + 0x89, 0xca, 0x00, 0x83, 0xb8, 0x1e, 0x82, 0x49, + 0x00, 0x89, 0x49, 0x00, 0xa3, 0x4f, 0x00, 0xa3, + 0x4f, 0x00, 0x89, 0xd4, 0x00, 0x81, 0xd4, 0x00, + 0x80, 0xd4, 0x00, 0x89, 0xd4, 0x00, 0x83, 0xcc, + 0x1e, 0x82, 0xa0, 0x01, 0x81, 0xa0, 0x01, 0x80, + 0xa0, 0x01, 0x89, 0xa0, 0x01, 0x83, 0xa0, 0x01, + 0xa3, 0x55, 0x00, 0xa3, 0x55, 0x00, 0x89, 0xaf, + 0x01, 0x81, 0xaf, 0x01, 0x80, 0xaf, 0x01, 0x89, + 0xaf, 0x01, 0x83, 0xaf, 0x01, 0xa3, 0x59, 0x00, + 0x80, 0x59, 0x00, 0xa3, 0x59, 0x00, 0x89, 0x59, + 0x00, 0x83, 0xb1, 0x03, 0x13, 0x03, 0x00, 0x1f, + 0x80, 0x00, 0x1f, 0x81, 0x00, 0x1f, 0xc2, 0x91, + 0x03, 0x13, 0x03, 0x08, 0x1f, 0x80, 0x08, 0x1f, + 0x81, 0x08, 0x1f, 0xc2, 0xb5, 0x03, 0x13, 0x03, + 0x10, 0x1f, 0x80, 0x10, 0x1f, 0x81, 0x95, 0x03, + 0x13, 0x03, 0x18, 0x1f, 0x80, 0x18, 0x1f, 0x81, + 0xb7, 0x03, 0x93, 0xb7, 0x03, 0x94, 0x20, 0x1f, + 0x80, 0x21, 0x1f, 0x80, 0x20, 0x1f, 0x81, 0x21, + 0x1f, 0x81, 0x20, 0x1f, 0xc2, 0x21, 0x1f, 0xc2, + 0x97, 0x03, 0x93, 0x97, 0x03, 0x94, 0x28, 0x1f, + 0x80, 0x29, 0x1f, 0x80, 0x28, 0x1f, 0x81, 0x29, + 0x1f, 0x81, 0x28, 0x1f, 0xc2, 0x29, 0x1f, 0xc2, + 0xb9, 0x03, 0x93, 0xb9, 0x03, 0x94, 0x30, 0x1f, + 0x80, 0x31, 0x1f, 0x80, 0x30, 0x1f, 0x81, 0x31, + 0x1f, 0x81, 0x30, 0x1f, 0xc2, 0x31, 0x1f, 0xc2, + 0x99, 0x03, 0x93, 0x99, 0x03, 0x94, 0x38, 0x1f, + 0x80, 0x39, 0x1f, 0x80, 0x38, 0x1f, 0x81, 0x39, + 0x1f, 0x81, 0x38, 0x1f, 0xc2, 0x39, 0x1f, 0xc2, + 0xbf, 0x03, 0x93, 0xbf, 0x03, 0x94, 0x40, 0x1f, + 0x80, 0x40, 0x1f, 0x81, 0x9f, 0x03, 0x13, 0x03, + 0x48, 0x1f, 0x80, 0x48, 0x1f, 0x81, 0xc5, 0x03, + 0x13, 0x03, 0x50, 0x1f, 0x80, 0x50, 0x1f, 0x81, + 0x50, 0x1f, 0xc2, 0xa5, 0x03, 0x94, 0x00, 0x00, + 0x00, 0x59, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x59, + 0x1f, 0x81, 0x00, 0x00, 0x00, 0x59, 0x1f, 0xc2, + 0xc9, 0x03, 0x93, 0xc9, 0x03, 0x94, 0x60, 0x1f, + 0x80, 0x61, 0x1f, 0x80, 0x60, 0x1f, 0x81, 0x61, + 0x1f, 0x81, 0x60, 0x1f, 0xc2, 0x61, 0x1f, 0xc2, + 0xa9, 0x03, 0x93, 0xa9, 0x03, 0x94, 0x68, 0x1f, + 0x80, 0x69, 0x1f, 0x80, 0x68, 0x1f, 0x81, 0x69, + 0x1f, 0x81, 0x68, 0x1f, 0xc2, 0x69, 0x1f, 0xc2, + 0xb1, 0x03, 0x80, 0xb5, 0x03, 0x80, 0xb7, 0x03, + 0x80, 0xb9, 0x03, 0x80, 0xbf, 0x03, 0x80, 0xc5, + 0x03, 0x80, 0xc9, 0x03, 0x80, 0x00, 0x1f, 0x45, + 0x03, 0x20, 0x1f, 0x45, 0x03, 0x60, 0x1f, 0x45, + 0x03, 0xb1, 0x03, 0x86, 0xb1, 0x03, 0x84, 0x70, + 0x1f, 0xc5, 0xb1, 0x03, 0xc5, 0xac, 0x03, 0xc5, + 0x00, 0x00, 0x00, 0xb1, 0x03, 0xc2, 0xb6, 0x1f, + 0xc5, 0x91, 0x03, 0x86, 0x91, 0x03, 0x84, 0x91, + 0x03, 0x80, 0x91, 0x03, 0xc5, 0x20, 0x93, 0x20, + 0x93, 0x20, 0xc2, 0xa8, 0x00, 0xc2, 0x74, 0x1f, + 0xc5, 0xb7, 0x03, 0xc5, 0xae, 0x03, 0xc5, 0x00, + 0x00, 0x00, 0xb7, 0x03, 0xc2, 0xc6, 0x1f, 0xc5, + 0x95, 0x03, 0x80, 0x97, 0x03, 0x80, 0x97, 0x03, + 0xc5, 0xbf, 0x1f, 0x80, 0xbf, 0x1f, 0x81, 0xbf, + 0x1f, 0xc2, 0xb9, 0x03, 0x86, 0xb9, 0x03, 0x84, + 0xca, 0x03, 0x80, 0x00, 0x03, 0xb9, 0x42, 0xca, + 0x42, 0x99, 0x06, 0x99, 0x04, 0x99, 0x00, 0xfe, + 0x1f, 0x80, 0xfe, 0x1f, 0x81, 0xfe, 0x1f, 0xc2, + 0xc5, 0x03, 0x86, 0xc5, 0x03, 0x84, 0xcb, 0x03, + 0x80, 0x00, 0x03, 0xc1, 0x13, 0xc1, 0x14, 0xc5, + 0x42, 0xcb, 0x42, 0xa5, 0x06, 0xa5, 0x04, 0xa5, + 0x00, 0xa1, 0x03, 0x94, 0xa8, 0x00, 0x80, 0x85, + 0x03, 0x60, 0x00, 0x7c, 0x1f, 0xc5, 0xc9, 0x03, + 0xc5, 0xce, 0x03, 0xc5, 0x00, 0x00, 0x00, 0xc9, + 0x03, 0xc2, 0xf6, 0x1f, 0xc5, 0x9f, 0x03, 0x80, + 0xa9, 0x03, 0x80, 0xa9, 0x03, 0xc5, 0x20, 0x94, + 0x02, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xb3, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x32, 0x20, 0x32, 0x20, 0x32, 0x20, + 0x00, 0x00, 0x00, 0x35, 0x20, 0x35, 0x20, 0x35, + 0x20, 0x00, 0x00, 0x00, 0x21, 0x21, 0x00, 0x00, + 0x20, 0x85, 0x3f, 0x3f, 0x3f, 0x21, 0x21, 0x3f, + 0x32, 0x20, 0x00, 0x00, 0x00, 0x00, 0x30, 0x69, + 0x00, 0x00, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x2b, 0x3d, 0x28, 0x29, 0x6e, 0x30, 0x00, 0x2b, + 0x00, 0x12, 0x22, 0x3d, 0x00, 0x28, 0x00, 0x29, + 0x00, 0x00, 0x00, 0x61, 0x00, 0x65, 0x00, 0x6f, + 0x00, 0x78, 0x00, 0x59, 0x02, 0x68, 0x6b, 0x6c, + 0x6d, 0x6e, 0x70, 0x73, 0x74, 0x52, 0x73, 0x61, + 0x2f, 0x63, 0x61, 0x2f, 0x73, 0xb0, 0x00, 0x43, + 0x63, 0x2f, 0x6f, 0x63, 0x2f, 0x75, 0xb0, 0x00, + 0x46, 0x48, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20, + 0xdf, 0x01, 0x01, 0x04, 0x24, 0x4e, 0x6f, 0x50, + 0x51, 0x52, 0x52, 0x52, 0x53, 0x4d, 0x54, 0x45, + 0x4c, 0x54, 0x4d, 0x4b, 0x00, 0xc5, 0x00, 0x42, + 0x43, 0x00, 0x65, 0x45, 0x46, 0x00, 0x4d, 0x6f, + 0xd0, 0x05, 0x46, 0x41, 0x58, 0xc0, 0x03, 0xb3, + 0x03, 0x93, 0x03, 0xa0, 0x03, 0x11, 0x22, 0x44, + 0x64, 0x65, 0x69, 0x6a, 0x31, 0xd0, 0x37, 0x31, + 0xd0, 0x39, 0x31, 0xd0, 0x31, 0x30, 0x31, 0xd0, + 0x33, 0x32, 0xd0, 0x33, 0x31, 0xd0, 0x35, 0x32, + 0xd0, 0x35, 0x33, 0xd0, 0x35, 0x34, 0xd0, 0x35, + 0x31, 0xd0, 0x36, 0x35, 0xd0, 0x36, 0x31, 0xd0, + 0x38, 0x33, 0xd0, 0x38, 0x35, 0xd0, 0x38, 0x37, + 0xd0, 0x38, 0x31, 0xd0, 0x49, 0x49, 0x49, 0x49, + 0x49, 0x49, 0x56, 0x56, 0x49, 0x56, 0x49, 0x49, + 0x56, 0x49, 0x49, 0x49, 0x49, 0x58, 0x58, 0x49, + 0x58, 0x49, 0x49, 0x4c, 0x43, 0x44, 0x4d, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x76, 0x76, + 0x69, 0x76, 0x69, 0x69, 0x76, 0x69, 0x69, 0x69, + 0x69, 0x78, 0x78, 0x69, 0x78, 0x69, 0x69, 0x6c, + 0x63, 0x64, 0x6d, 0x30, 0xd0, 0x33, 0x90, 0x21, + 0xb8, 0x92, 0x21, 0xb8, 0x94, 0x21, 0xb8, 0xd0, + 0x21, 0xb8, 0xd4, 0x21, 0xb8, 0xd2, 0x21, 0xb8, + 0x03, 0x22, 0xb8, 0x08, 0x22, 0xb8, 0x0b, 0x22, + 0xb8, 0x23, 0x22, 0xb8, 0x00, 0x00, 0x00, 0x25, + 0x22, 0xb8, 0x2b, 0x22, 0x2b, 0x22, 0x2b, 0x22, + 0x00, 0x00, 0x00, 0x2e, 0x22, 0x2e, 0x22, 0x2e, + 0x22, 0x00, 0x00, 0x00, 0x3c, 0x22, 0xb8, 0x43, + 0x22, 0xb8, 0x45, 0x22, 0xb8, 0x00, 0x00, 0x00, + 0x48, 0x22, 0xb8, 0x3d, 0x00, 0xb8, 0x00, 0x00, + 0x00, 0x61, 0x22, 0xb8, 0x4d, 0x22, 0xb8, 0x3c, + 0x00, 0xb8, 0x3e, 0x00, 0xb8, 0x64, 0x22, 0xb8, + 0x65, 0x22, 0xb8, 0x72, 0x22, 0xb8, 0x76, 0x22, + 0xb8, 0x7a, 0x22, 0xb8, 0x82, 0x22, 0xb8, 0x86, + 0x22, 0xb8, 0xa2, 0x22, 0xb8, 0xa8, 0x22, 0xb8, + 0xa9, 0x22, 0xb8, 0xab, 0x22, 0xb8, 0x7c, 0x22, + 0xb8, 0x91, 0x22, 0xb8, 0xb2, 0x22, 0x38, 0x03, + 0x08, 0x30, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00, + 0x32, 0x30, 0x28, 0x00, 0x31, 0x00, 0x29, 0x00, + 0x28, 0x00, 0x31, 0x00, 0x30, 0x00, 0x29, 0x00, + 0x28, 0x32, 0x30, 0x29, 0x31, 0x00, 0x2e, 0x00, + 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x30, + 0x2e, 0x28, 0x00, 0x61, 0x00, 0x29, 0x00, 0x41, + 0x00, 0x61, 0x00, 0x2b, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x3a, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0xdd, 0x2a, 0xb8, 0x6a, 0x56, 0x00, 0x4e, + 0x00, 0x28, 0x36, 0x3f, 0x59, 0x85, 0x8c, 0xa0, + 0xba, 0x3f, 0x51, 0x00, 0x26, 0x2c, 0x43, 0x57, + 0x6c, 0xa1, 0xb6, 0xc1, 0x9b, 0x52, 0x00, 0x5e, + 0x7a, 0x7f, 0x9d, 0xa6, 0xc1, 0xce, 0xe7, 0xb6, + 0x53, 0xc8, 0x53, 0xe3, 0x53, 0xd7, 0x56, 0x1f, + 0x57, 0xeb, 0x58, 0x02, 0x59, 0x0a, 0x59, 0x15, + 0x59, 0x27, 0x59, 0x73, 0x59, 0x50, 0x5b, 0x80, + 0x5b, 0xf8, 0x5b, 0x0f, 0x5c, 0x22, 0x5c, 0x38, + 0x5c, 0x6e, 0x5c, 0x71, 0x5c, 0xdb, 0x5d, 0xe5, + 0x5d, 0xf1, 0x5d, 0xfe, 0x5d, 0x72, 0x5e, 0x7a, + 0x5e, 0x7f, 0x5e, 0xf4, 0x5e, 0xfe, 0x5e, 0x0b, + 0x5f, 0x13, 0x5f, 0x50, 0x5f, 0x61, 0x5f, 0x73, + 0x5f, 0xc3, 0x5f, 0x08, 0x62, 0x36, 0x62, 0x4b, + 0x62, 0x2f, 0x65, 0x34, 0x65, 0x87, 0x65, 0x97, + 0x65, 0xa4, 0x65, 0xb9, 0x65, 0xe0, 0x65, 0xe5, + 0x65, 0xf0, 0x66, 0x08, 0x67, 0x28, 0x67, 0x20, + 0x6b, 0x62, 0x6b, 0x79, 0x6b, 0xb3, 0x6b, 0xcb, + 0x6b, 0xd4, 0x6b, 0xdb, 0x6b, 0x0f, 0x6c, 0x14, + 0x6c, 0x34, 0x6c, 0x6b, 0x70, 0x2a, 0x72, 0x36, + 0x72, 0x3b, 0x72, 0x3f, 0x72, 0x47, 0x72, 0x59, + 0x72, 0x5b, 0x72, 0xac, 0x72, 0x84, 0x73, 0x89, + 0x73, 0xdc, 0x74, 0xe6, 0x74, 0x18, 0x75, 0x1f, + 0x75, 0x28, 0x75, 0x30, 0x75, 0x8b, 0x75, 0x92, + 0x75, 0x76, 0x76, 0x7d, 0x76, 0xae, 0x76, 0xbf, + 0x76, 0xee, 0x76, 0xdb, 0x77, 0xe2, 0x77, 0xf3, + 0x77, 0x3a, 0x79, 0xb8, 0x79, 0xbe, 0x79, 0x74, + 0x7a, 0xcb, 0x7a, 0xf9, 0x7a, 0x73, 0x7c, 0xf8, + 0x7c, 0x36, 0x7f, 0x51, 0x7f, 0x8a, 0x7f, 0xbd, + 0x7f, 0x01, 0x80, 0x0c, 0x80, 0x12, 0x80, 0x33, + 0x80, 0x7f, 0x80, 0x89, 0x80, 0xe3, 0x81, 0x00, + 0x07, 0x10, 0x19, 0x29, 0x38, 0x3c, 0x8b, 0x8f, + 0x95, 0x4d, 0x86, 0x6b, 0x86, 0x40, 0x88, 0x4c, + 0x88, 0x63, 0x88, 0x7e, 0x89, 0x8b, 0x89, 0xd2, + 0x89, 0x00, 0x8a, 0x37, 0x8c, 0x46, 0x8c, 0x55, + 0x8c, 0x78, 0x8c, 0x9d, 0x8c, 0x64, 0x8d, 0x70, + 0x8d, 0xb3, 0x8d, 0xab, 0x8e, 0xca, 0x8e, 0x9b, + 0x8f, 0xb0, 0x8f, 0xb5, 0x8f, 0x91, 0x90, 0x49, + 0x91, 0xc6, 0x91, 0xcc, 0x91, 0xd1, 0x91, 0x77, + 0x95, 0x80, 0x95, 0x1c, 0x96, 0xb6, 0x96, 0xb9, + 0x96, 0xe8, 0x96, 0x51, 0x97, 0x5e, 0x97, 0x62, + 0x97, 0x69, 0x97, 0xcb, 0x97, 0xed, 0x97, 0xf3, + 0x97, 0x01, 0x98, 0xa8, 0x98, 0xdb, 0x98, 0xdf, + 0x98, 0x96, 0x99, 0x99, 0x99, 0xac, 0x99, 0xa8, + 0x9a, 0xd8, 0x9a, 0xdf, 0x9a, 0x25, 0x9b, 0x2f, + 0x9b, 0x32, 0x9b, 0x3c, 0x9b, 0x5a, 0x9b, 0xe5, + 0x9c, 0x75, 0x9e, 0x7f, 0x9e, 0xa5, 0x9e, 0x00, + 0x16, 0x1e, 0x28, 0x2c, 0x54, 0x58, 0x69, 0x6e, + 0x7b, 0x96, 0xa5, 0xad, 0xe8, 0xf7, 0xfb, 0x12, + 0x30, 0x00, 0x00, 0x41, 0x53, 0x44, 0x53, 0x45, + 0x53, 0x4b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x4d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x4f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x51, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x53, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x55, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x57, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x59, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x61, 0x30, 0x99, 0x30, 0x64, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x66, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x68, 0x30, 0x99, + 0x30, 0x6f, 0x30, 0x99, 0x30, 0x72, 0x30, 0x99, + 0x30, 0x75, 0x30, 0x99, 0x30, 0x78, 0x30, 0x99, + 0x30, 0x7b, 0x30, 0x99, 0x30, 0x46, 0x30, 0x99, + 0x30, 0x20, 0x00, 0x99, 0x30, 0x9d, 0x30, 0x99, + 0x30, 0x88, 0x30, 0x8a, 0x30, 0xab, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xad, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x30, 0x99, + 0x30, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x30, 0x99, + 0x30, 0xc4, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xc8, 0x30, 0x99, 0x30, 0xcf, 0x30, 0x99, + 0x30, 0xd2, 0x30, 0x99, 0x30, 0xd5, 0x30, 0x99, + 0x30, 0xd8, 0x30, 0x99, 0x30, 0xdb, 0x30, 0x99, + 0x30, 0xa6, 0x30, 0x99, 0x30, 0xef, 0x30, 0x99, + 0x30, 0xfd, 0x30, 0x99, 0x30, 0xb3, 0x30, 0xc8, + 0x30, 0x00, 0x11, 0x00, 0x01, 0xaa, 0x02, 0xac, + 0xad, 0x03, 0x04, 0x05, 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0x1a, 0x06, 0x07, 0x08, 0x21, 0x09, + 0x11, 0x61, 0x11, 0x14, 0x11, 0x4c, 0x00, 0x01, + 0xb3, 0xb4, 0xb8, 0xba, 0xbf, 0xc3, 0xc5, 0x08, + 0xc9, 0xcb, 0x09, 0x0a, 0x0c, 0x0e, 0x0f, 0x13, + 0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x22, + 0x2c, 0x33, 0x38, 0xdd, 0xde, 0x43, 0x44, 0x45, + 0x70, 0x71, 0x74, 0x7d, 0x7e, 0x80, 0x8a, 0x8d, + 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, + 0x0a, 0x4e, 0x2d, 0x4e, 0x0b, 0x4e, 0x32, 0x75, + 0x59, 0x4e, 0x19, 0x4e, 0x01, 0x4e, 0x29, 0x59, + 0x30, 0x57, 0xba, 0x4e, 0x28, 0x00, 0x29, 0x00, + 0x00, 0x11, 0x02, 0x11, 0x03, 0x11, 0x05, 0x11, + 0x06, 0x11, 0x07, 0x11, 0x09, 0x11, 0x0b, 0x11, + 0x0c, 0x11, 0x0e, 0x11, 0x0f, 0x11, 0x10, 0x11, + 0x11, 0x11, 0x12, 0x11, 0x28, 0x00, 0x00, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x02, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x05, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x09, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0e, 0x11, + 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0c, 0x11, + 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, + 0x69, 0x11, 0x0c, 0x11, 0x65, 0x11, 0xab, 0x11, + 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, 0x69, 0x11, + 0x12, 0x11, 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, + 0x29, 0x00, 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, + 0xdb, 0x56, 0x94, 0x4e, 0x6d, 0x51, 0x03, 0x4e, + 0x6b, 0x51, 0x5d, 0x4e, 0x41, 0x53, 0x08, 0x67, + 0x6b, 0x70, 0x34, 0x6c, 0x28, 0x67, 0xd1, 0x91, + 0x1f, 0x57, 0xe5, 0x65, 0x2a, 0x68, 0x09, 0x67, + 0x3e, 0x79, 0x0d, 0x54, 0x79, 0x72, 0xa1, 0x8c, + 0x5d, 0x79, 0xb4, 0x52, 0xe3, 0x4e, 0x7c, 0x54, + 0x66, 0x5b, 0xe3, 0x76, 0x01, 0x4f, 0xc7, 0x8c, + 0x54, 0x53, 0x6d, 0x79, 0x11, 0x4f, 0xea, 0x81, + 0xf3, 0x81, 0x4f, 0x55, 0x7c, 0x5e, 0x87, 0x65, + 0x8f, 0x7b, 0x50, 0x54, 0x45, 0x32, 0x00, 0x31, + 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x11, 0x00, + 0x02, 0x03, 0x05, 0x06, 0x07, 0x09, 0x0b, 0x0c, + 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x00, 0x11, 0x00, + 0x61, 0x02, 0x61, 0x03, 0x61, 0x05, 0x61, 0x06, + 0x61, 0x07, 0x61, 0x09, 0x61, 0x0b, 0x61, 0x0c, + 0x61, 0x0e, 0x11, 0x61, 0x11, 0x00, 0x11, 0x0e, + 0x61, 0xb7, 0x00, 0x69, 0x0b, 0x11, 0x01, 0x63, + 0x00, 0x69, 0x0b, 0x11, 0x6e, 0x11, 0x00, 0x4e, + 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, 0x94, 0x4e, + 0x6d, 0x51, 0x03, 0x4e, 0x6b, 0x51, 0x5d, 0x4e, + 0x41, 0x53, 0x08, 0x67, 0x6b, 0x70, 0x34, 0x6c, + 0x28, 0x67, 0xd1, 0x91, 0x1f, 0x57, 0xe5, 0x65, + 0x2a, 0x68, 0x09, 0x67, 0x3e, 0x79, 0x0d, 0x54, + 0x79, 0x72, 0xa1, 0x8c, 0x5d, 0x79, 0xb4, 0x52, + 0xd8, 0x79, 0x37, 0x75, 0x73, 0x59, 0x69, 0x90, + 0x2a, 0x51, 0x70, 0x53, 0xe8, 0x6c, 0x05, 0x98, + 0x11, 0x4f, 0x99, 0x51, 0x63, 0x6b, 0x0a, 0x4e, + 0x2d, 0x4e, 0x0b, 0x4e, 0xe6, 0x5d, 0xf3, 0x53, + 0x3b, 0x53, 0x97, 0x5b, 0x66, 0x5b, 0xe3, 0x76, + 0x01, 0x4f, 0xc7, 0x8c, 0x54, 0x53, 0x1c, 0x59, + 0x33, 0x00, 0x36, 0x00, 0x34, 0x00, 0x30, 0x00, + 0x35, 0x30, 0x31, 0x00, 0x08, 0x67, 0x31, 0x00, + 0x30, 0x00, 0x08, 0x67, 0x48, 0x67, 0x65, 0x72, + 0x67, 0x65, 0x56, 0x4c, 0x54, 0x44, 0xa2, 0x30, + 0x00, 0x02, 0x04, 0x06, 0x08, 0x09, 0x0b, 0x0d, + 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, + 0x1f, 0x22, 0x24, 0x26, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x30, 0x33, 0x36, 0x39, 0x3c, 0x3d, + 0x3e, 0x3f, 0x40, 0x42, 0x44, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0xe4, + 0x4e, 0x8c, 0x54, 0xa1, 0x30, 0x01, 0x30, 0x5b, + 0x27, 0x01, 0x4a, 0x34, 0x00, 0x01, 0x52, 0x39, + 0x01, 0xa2, 0x30, 0x00, 0x5a, 0x49, 0xa4, 0x30, + 0x00, 0x27, 0x4f, 0x0c, 0xa4, 0x30, 0x00, 0x4f, + 0x1d, 0x02, 0x05, 0x4f, 0xa8, 0x30, 0x00, 0x11, + 0x07, 0x54, 0x21, 0xa8, 0x30, 0x00, 0x54, 0x03, + 0x54, 0xa4, 0x30, 0x06, 0x4f, 0x15, 0x06, 0x58, + 0x3c, 0x07, 0x00, 0x46, 0xab, 0x30, 0x00, 0x3e, + 0x18, 0x1d, 0x00, 0x42, 0x3f, 0x51, 0xac, 0x30, + 0x00, 0x41, 0x47, 0x00, 0x47, 0x32, 0xae, 0x30, + 0xac, 0x30, 0xae, 0x30, 0x00, 0x1d, 0x4e, 0xad, + 0x30, 0x00, 0x38, 0x3d, 0x4f, 0x01, 0x3e, 0x13, + 0x4f, 0xad, 0x30, 0xed, 0x30, 0xad, 0x30, 0x00, + 0x40, 0x03, 0x3c, 0x33, 0xad, 0x30, 0x00, 0x40, + 0x34, 0x4f, 0x1b, 0x3e, 0xad, 0x30, 0x00, 0x40, + 0x42, 0x16, 0x1b, 0xb0, 0x30, 0x00, 0x39, 0x30, + 0xa4, 0x30, 0x0c, 0x45, 0x3c, 0x24, 0x4f, 0x0b, + 0x47, 0x18, 0x00, 0x49, 0xaf, 0x30, 0x00, 0x3e, + 0x4d, 0x1e, 0xb1, 0x30, 0x00, 0x4b, 0x08, 0x02, + 0x3a, 0x19, 0x02, 0x4b, 0x2c, 0xa4, 0x30, 0x11, + 0x00, 0x0b, 0x47, 0xb5, 0x30, 0x00, 0x3e, 0x0c, + 0x47, 0x2b, 0xb0, 0x30, 0x07, 0x3a, 0x43, 0x00, + 0xb9, 0x30, 0x02, 0x3a, 0x08, 0x02, 0x3a, 0x0f, + 0x07, 0x43, 0x00, 0xb7, 0x30, 0x10, 0x00, 0x12, + 0x34, 0x11, 0x3c, 0x13, 0x17, 0xa4, 0x30, 0x2a, + 0x1f, 0x24, 0x2b, 0x00, 0x20, 0xbb, 0x30, 0x16, + 0x41, 0x00, 0x38, 0x0d, 0xc4, 0x30, 0x0d, 0x38, + 0x00, 0xd0, 0x30, 0x00, 0x2c, 0x1c, 0x1b, 0xa2, + 0x30, 0x32, 0x00, 0x17, 0x26, 0x49, 0xaf, 0x30, + 0x25, 0x00, 0x3c, 0xb3, 0x30, 0x21, 0x00, 0x20, + 0x38, 0xa1, 0x30, 0x34, 0x00, 0x48, 0x22, 0x28, + 0xa3, 0x30, 0x32, 0x00, 0x59, 0x25, 0xa7, 0x30, + 0x2f, 0x1c, 0x10, 0x00, 0x44, 0xd5, 0x30, 0x00, + 0x14, 0x1e, 0xaf, 0x30, 0x29, 0x00, 0x10, 0x4d, + 0x3c, 0xda, 0x30, 0xbd, 0x30, 0xb8, 0x30, 0x22, + 0x13, 0x1a, 0x20, 0x33, 0x0c, 0x22, 0x3b, 0x01, + 0x22, 0x44, 0x00, 0x21, 0x44, 0x07, 0xa4, 0x30, + 0x39, 0x00, 0x4f, 0x24, 0xc8, 0x30, 0x14, 0x23, + 0x00, 0xdb, 0x30, 0xf3, 0x30, 0xc9, 0x30, 0x14, + 0x2a, 0x00, 0x12, 0x33, 0x22, 0x12, 0x33, 0x2a, + 0xa4, 0x30, 0x3a, 0x00, 0x0b, 0x49, 0xa4, 0x30, + 0x3a, 0x00, 0x47, 0x3a, 0x1f, 0x2b, 0x3a, 0x47, + 0x0b, 0xb7, 0x30, 0x27, 0x3c, 0x00, 0x30, 0x3c, + 0xaf, 0x30, 0x30, 0x00, 0x3e, 0x44, 0xdf, 0x30, + 0xea, 0x30, 0xd0, 0x30, 0x0f, 0x1a, 0x00, 0x2c, + 0x1b, 0xe1, 0x30, 0xac, 0x30, 0xac, 0x30, 0x35, + 0x00, 0x1c, 0x47, 0x35, 0x50, 0x1c, 0x3f, 0xa2, + 0x30, 0x42, 0x5a, 0x27, 0x42, 0x5a, 0x49, 0x44, + 0x00, 0x51, 0xc3, 0x30, 0x27, 0x00, 0x05, 0x28, + 0xea, 0x30, 0xe9, 0x30, 0xd4, 0x30, 0x17, 0x00, + 0x28, 0xd6, 0x30, 0x15, 0x26, 0x00, 0x15, 0xec, + 0x30, 0xe0, 0x30, 0xb2, 0x30, 0x3a, 0x41, 0x16, + 0x00, 0x41, 0xc3, 0x30, 0x2c, 0x00, 0x05, 0x30, + 0x00, 0xb9, 0x70, 0x31, 0x00, 0x30, 0x00, 0xb9, + 0x70, 0x32, 0x00, 0x30, 0x00, 0xb9, 0x70, 0x68, + 0x50, 0x61, 0x64, 0x61, 0x41, 0x55, 0x62, 0x61, + 0x72, 0x6f, 0x56, 0x70, 0x63, 0x64, 0x6d, 0x64, + 0x00, 0x6d, 0x00, 0xb2, 0x00, 0x49, 0x00, 0x55, + 0x00, 0x73, 0x5e, 0x10, 0x62, 0x2d, 0x66, 0x8c, + 0x54, 0x27, 0x59, 0x63, 0x6b, 0x0e, 0x66, 0xbb, + 0x6c, 0x2a, 0x68, 0x0f, 0x5f, 0x1a, 0x4f, 0x3e, + 0x79, 0x70, 0x00, 0x41, 0x6e, 0x00, 0x41, 0xbc, + 0x03, 0x41, 0x6d, 0x00, 0x41, 0x6b, 0x00, 0x41, + 0x4b, 0x00, 0x42, 0x4d, 0x00, 0x42, 0x47, 0x00, + 0x42, 0x63, 0x61, 0x6c, 0x6b, 0x63, 0x61, 0x6c, + 0x70, 0x00, 0x46, 0x6e, 0x00, 0x46, 0xbc, 0x03, + 0x46, 0xbc, 0x03, 0x67, 0x6d, 0x00, 0x67, 0x6b, + 0x00, 0x67, 0x48, 0x00, 0x7a, 0x6b, 0x48, 0x7a, + 0x4d, 0x48, 0x7a, 0x47, 0x48, 0x7a, 0x54, 0x48, + 0x7a, 0xbc, 0x03, 0x13, 0x21, 0x6d, 0x00, 0x13, + 0x21, 0x64, 0x00, 0x13, 0x21, 0x6b, 0x00, 0x13, + 0x21, 0x66, 0x00, 0x6d, 0x6e, 0x00, 0x6d, 0xbc, + 0x03, 0x6d, 0x6d, 0x00, 0x6d, 0x63, 0x00, 0x6d, + 0x6b, 0x00, 0x6d, 0x63, 0x00, 0x0a, 0x0a, 0x4f, + 0x00, 0x0a, 0x4f, 0x6d, 0x00, 0xb2, 0x00, 0x63, + 0x00, 0x08, 0x0a, 0x4f, 0x0a, 0x0a, 0x50, 0x00, + 0x0a, 0x50, 0x6d, 0x00, 0xb3, 0x00, 0x6b, 0x00, + 0x6d, 0x00, 0xb3, 0x00, 0x6d, 0x00, 0x15, 0x22, + 0x73, 0x00, 0x6d, 0x00, 0x15, 0x22, 0x73, 0x00, + 0xb2, 0x00, 0x50, 0x61, 0x6b, 0x50, 0x61, 0x4d, + 0x50, 0x61, 0x47, 0x50, 0x61, 0x72, 0x61, 0x64, + 0x72, 0x61, 0x64, 0xd1, 0x73, 0x72, 0x00, 0x61, + 0x00, 0x64, 0x00, 0x15, 0x22, 0x73, 0x00, 0xb2, + 0x00, 0x70, 0x00, 0x73, 0x6e, 0x00, 0x73, 0xbc, + 0x03, 0x73, 0x6d, 0x00, 0x73, 0x70, 0x00, 0x56, + 0x6e, 0x00, 0x56, 0xbc, 0x03, 0x56, 0x6d, 0x00, + 0x56, 0x6b, 0x00, 0x56, 0x4d, 0x00, 0x56, 0x70, + 0x00, 0x57, 0x6e, 0x00, 0x57, 0xbc, 0x03, 0x57, + 0x6d, 0x00, 0x57, 0x6b, 0x00, 0x57, 0x4d, 0x00, + 0x57, 0x6b, 0x00, 0xa9, 0x03, 0x4d, 0x00, 0xa9, + 0x03, 0x61, 0x2e, 0x6d, 0x2e, 0x42, 0x71, 0x63, + 0x63, 0x63, 0x64, 0x43, 0xd1, 0x6b, 0x67, 0x43, + 0x6f, 0x2e, 0x64, 0x42, 0x47, 0x79, 0x68, 0x61, + 0x48, 0x50, 0x69, 0x6e, 0x4b, 0x4b, 0x4b, 0x4d, + 0x6b, 0x74, 0x6c, 0x6d, 0x6c, 0x6e, 0x6c, 0x6f, + 0x67, 0x6c, 0x78, 0x6d, 0x62, 0x6d, 0x69, 0x6c, + 0x6d, 0x6f, 0x6c, 0x50, 0x48, 0x70, 0x2e, 0x6d, + 0x2e, 0x50, 0x50, 0x4d, 0x50, 0x52, 0x73, 0x72, + 0x53, 0x76, 0x57, 0x62, 0x56, 0xd1, 0x6d, 0x41, + 0xd1, 0x6d, 0x31, 0x00, 0xe5, 0x65, 0x31, 0x00, + 0x30, 0x00, 0xe5, 0x65, 0x32, 0x00, 0x30, 0x00, + 0xe5, 0x65, 0x33, 0x00, 0x30, 0x00, 0xe5, 0x65, + 0x67, 0x61, 0x6c, 0x4a, 0x04, 0x4c, 0x04, 0x43, + 0x46, 0x51, 0x26, 0x01, 0x53, 0x01, 0x27, 0xa7, + 0x37, 0xab, 0x6b, 0x02, 0x52, 0xab, 0x48, 0x8c, + 0xf4, 0x66, 0xca, 0x8e, 0xc8, 0x8c, 0xd1, 0x6e, + 0x32, 0x4e, 0xe5, 0x53, 0x9c, 0x9f, 0x9c, 0x9f, + 0x51, 0x59, 0xd1, 0x91, 0x87, 0x55, 0x48, 0x59, + 0xf6, 0x61, 0x69, 0x76, 0x85, 0x7f, 0x3f, 0x86, + 0xba, 0x87, 0xf8, 0x88, 0x8f, 0x90, 0x02, 0x6a, + 0x1b, 0x6d, 0xd9, 0x70, 0xde, 0x73, 0x3d, 0x84, + 0x6a, 0x91, 0xf1, 0x99, 0x82, 0x4e, 0x75, 0x53, + 0x04, 0x6b, 0x1b, 0x72, 0x2d, 0x86, 0x1e, 0x9e, + 0x50, 0x5d, 0xeb, 0x6f, 0xcd, 0x85, 0x64, 0x89, + 0xc9, 0x62, 0xd8, 0x81, 0x1f, 0x88, 0xca, 0x5e, + 0x17, 0x67, 0x6a, 0x6d, 0xfc, 0x72, 0xce, 0x90, + 0x86, 0x4f, 0xb7, 0x51, 0xde, 0x52, 0xc4, 0x64, + 0xd3, 0x6a, 0x10, 0x72, 0xe7, 0x76, 0x01, 0x80, + 0x06, 0x86, 0x5c, 0x86, 0xef, 0x8d, 0x32, 0x97, + 0x6f, 0x9b, 0xfa, 0x9d, 0x8c, 0x78, 0x7f, 0x79, + 0xa0, 0x7d, 0xc9, 0x83, 0x04, 0x93, 0x7f, 0x9e, + 0xd6, 0x8a, 0xdf, 0x58, 0x04, 0x5f, 0x60, 0x7c, + 0x7e, 0x80, 0x62, 0x72, 0xca, 0x78, 0xc2, 0x8c, + 0xf7, 0x96, 0xd8, 0x58, 0x62, 0x5c, 0x13, 0x6a, + 0xda, 0x6d, 0x0f, 0x6f, 0x2f, 0x7d, 0x37, 0x7e, + 0x4b, 0x96, 0xd2, 0x52, 0x8b, 0x80, 0xdc, 0x51, + 0xcc, 0x51, 0x1c, 0x7a, 0xbe, 0x7d, 0xf1, 0x83, + 0x75, 0x96, 0x80, 0x8b, 0xcf, 0x62, 0x02, 0x6a, + 0xfe, 0x8a, 0x39, 0x4e, 0xe7, 0x5b, 0x12, 0x60, + 0x87, 0x73, 0x70, 0x75, 0x17, 0x53, 0xfb, 0x78, + 0xbf, 0x4f, 0xa9, 0x5f, 0x0d, 0x4e, 0xcc, 0x6c, + 0x78, 0x65, 0x22, 0x7d, 0xc3, 0x53, 0x5e, 0x58, + 0x01, 0x77, 0x49, 0x84, 0xaa, 0x8a, 0xba, 0x6b, + 0xb0, 0x8f, 0x88, 0x6c, 0xfe, 0x62, 0xe5, 0x82, + 0xa0, 0x63, 0x65, 0x75, 0xae, 0x4e, 0x69, 0x51, + 0xc9, 0x51, 0x81, 0x68, 0xe7, 0x7c, 0x6f, 0x82, + 0xd2, 0x8a, 0xcf, 0x91, 0xf5, 0x52, 0x42, 0x54, + 0x73, 0x59, 0xec, 0x5e, 0xc5, 0x65, 0xfe, 0x6f, + 0x2a, 0x79, 0xad, 0x95, 0x6a, 0x9a, 0x97, 0x9e, + 0xce, 0x9e, 0x9b, 0x52, 0xc6, 0x66, 0x77, 0x6b, + 0x62, 0x8f, 0x74, 0x5e, 0x90, 0x61, 0x00, 0x62, + 0x9a, 0x64, 0x23, 0x6f, 0x49, 0x71, 0x89, 0x74, + 0xca, 0x79, 0xf4, 0x7d, 0x6f, 0x80, 0x26, 0x8f, + 0xee, 0x84, 0x23, 0x90, 0x4a, 0x93, 0x17, 0x52, + 0xa3, 0x52, 0xbd, 0x54, 0xc8, 0x70, 0xc2, 0x88, + 0xaa, 0x8a, 0xc9, 0x5e, 0xf5, 0x5f, 0x7b, 0x63, + 0xae, 0x6b, 0x3e, 0x7c, 0x75, 0x73, 0xe4, 0x4e, + 0xf9, 0x56, 0xe7, 0x5b, 0xba, 0x5d, 0x1c, 0x60, + 0xb2, 0x73, 0x69, 0x74, 0x9a, 0x7f, 0x46, 0x80, + 0x34, 0x92, 0xf6, 0x96, 0x48, 0x97, 0x18, 0x98, + 0x8b, 0x4f, 0xae, 0x79, 0xb4, 0x91, 0xb8, 0x96, + 0xe1, 0x60, 0x86, 0x4e, 0xda, 0x50, 0xee, 0x5b, + 0x3f, 0x5c, 0x99, 0x65, 0x02, 0x6a, 0xce, 0x71, + 0x42, 0x76, 0xfc, 0x84, 0x7c, 0x90, 0x8d, 0x9f, + 0x88, 0x66, 0x2e, 0x96, 0x89, 0x52, 0x7b, 0x67, + 0xf3, 0x67, 0x41, 0x6d, 0x9c, 0x6e, 0x09, 0x74, + 0x59, 0x75, 0x6b, 0x78, 0x10, 0x7d, 0x5e, 0x98, + 0x6d, 0x51, 0x2e, 0x62, 0x78, 0x96, 0x2b, 0x50, + 0x19, 0x5d, 0xea, 0x6d, 0x2a, 0x8f, 0x8b, 0x5f, + 0x44, 0x61, 0x17, 0x68, 0x87, 0x73, 0x86, 0x96, + 0x29, 0x52, 0x0f, 0x54, 0x65, 0x5c, 0x13, 0x66, + 0x4e, 0x67, 0xa8, 0x68, 0xe5, 0x6c, 0x06, 0x74, + 0xe2, 0x75, 0x79, 0x7f, 0xcf, 0x88, 0xe1, 0x88, + 0xcc, 0x91, 0xe2, 0x96, 0x3f, 0x53, 0xba, 0x6e, + 0x1d, 0x54, 0xd0, 0x71, 0x98, 0x74, 0xfa, 0x85, + 0xa3, 0x96, 0x57, 0x9c, 0x9f, 0x9e, 0x97, 0x67, + 0xcb, 0x6d, 0xe8, 0x81, 0xcb, 0x7a, 0x20, 0x7b, + 0x92, 0x7c, 0xc0, 0x72, 0x99, 0x70, 0x58, 0x8b, + 0xc0, 0x4e, 0x36, 0x83, 0x3a, 0x52, 0x07, 0x52, + 0xa6, 0x5e, 0xd3, 0x62, 0xd6, 0x7c, 0x85, 0x5b, + 0x1e, 0x6d, 0xb4, 0x66, 0x3b, 0x8f, 0x4c, 0x88, + 0x4d, 0x96, 0x8b, 0x89, 0xd3, 0x5e, 0x40, 0x51, + 0xc0, 0x55, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x58, + 0x00, 0x00, 0x74, 0x66, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x51, 0x2a, 0x73, 0xca, 0x76, 0x3c, 0x79, + 0x5e, 0x79, 0x65, 0x79, 0x8f, 0x79, 0x56, 0x97, + 0xbe, 0x7c, 0xbd, 0x7f, 0x00, 0x00, 0x12, 0x86, + 0x00, 0x00, 0xf8, 0x8a, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x90, 0xfd, 0x90, 0xef, 0x98, 0xfc, 0x98, + 0x28, 0x99, 0xb4, 0x9d, 0xde, 0x90, 0xb7, 0x96, + 0xae, 0x4f, 0xe7, 0x50, 0x4d, 0x51, 0xc9, 0x52, + 0xe4, 0x52, 0x51, 0x53, 0x9d, 0x55, 0x06, 0x56, + 0x68, 0x56, 0x40, 0x58, 0xa8, 0x58, 0x64, 0x5c, + 0x6e, 0x5c, 0x94, 0x60, 0x68, 0x61, 0x8e, 0x61, + 0xf2, 0x61, 0x4f, 0x65, 0xe2, 0x65, 0x91, 0x66, + 0x85, 0x68, 0x77, 0x6d, 0x1a, 0x6e, 0x22, 0x6f, + 0x6e, 0x71, 0x2b, 0x72, 0x22, 0x74, 0x91, 0x78, + 0x3e, 0x79, 0x49, 0x79, 0x48, 0x79, 0x50, 0x79, + 0x56, 0x79, 0x5d, 0x79, 0x8d, 0x79, 0x8e, 0x79, + 0x40, 0x7a, 0x81, 0x7a, 0xc0, 0x7b, 0xf4, 0x7d, + 0x09, 0x7e, 0x41, 0x7e, 0x72, 0x7f, 0x05, 0x80, + 0xed, 0x81, 0x79, 0x82, 0x79, 0x82, 0x57, 0x84, + 0x10, 0x89, 0x96, 0x89, 0x01, 0x8b, 0x39, 0x8b, + 0xd3, 0x8c, 0x08, 0x8d, 0xb6, 0x8f, 0x38, 0x90, + 0xe3, 0x96, 0xff, 0x97, 0x3b, 0x98, 0x75, 0x60, + 0xee, 0x42, 0x18, 0x82, 0x02, 0x26, 0x4e, 0xb5, + 0x51, 0x68, 0x51, 0x80, 0x4f, 0x45, 0x51, 0x80, + 0x51, 0xc7, 0x52, 0xfa, 0x52, 0x9d, 0x55, 0x55, + 0x55, 0x99, 0x55, 0xe2, 0x55, 0x5a, 0x58, 0xb3, + 0x58, 0x44, 0x59, 0x54, 0x59, 0x62, 0x5a, 0x28, + 0x5b, 0xd2, 0x5e, 0xd9, 0x5e, 0x69, 0x5f, 0xad, + 0x5f, 0xd8, 0x60, 0x4e, 0x61, 0x08, 0x61, 0x8e, + 0x61, 0x60, 0x61, 0xf2, 0x61, 0x34, 0x62, 0xc4, + 0x63, 0x1c, 0x64, 0x52, 0x64, 0x56, 0x65, 0x74, + 0x66, 0x17, 0x67, 0x1b, 0x67, 0x56, 0x67, 0x79, + 0x6b, 0xba, 0x6b, 0x41, 0x6d, 0xdb, 0x6e, 0xcb, + 0x6e, 0x22, 0x6f, 0x1e, 0x70, 0x6e, 0x71, 0xa7, + 0x77, 0x35, 0x72, 0xaf, 0x72, 0x2a, 0x73, 0x71, + 0x74, 0x06, 0x75, 0x3b, 0x75, 0x1d, 0x76, 0x1f, + 0x76, 0xca, 0x76, 0xdb, 0x76, 0xf4, 0x76, 0x4a, + 0x77, 0x40, 0x77, 0xcc, 0x78, 0xb1, 0x7a, 0xc0, + 0x7b, 0x7b, 0x7c, 0x5b, 0x7d, 0xf4, 0x7d, 0x3e, + 0x7f, 0x05, 0x80, 0x52, 0x83, 0xef, 0x83, 0x79, + 0x87, 0x41, 0x89, 0x86, 0x89, 0x96, 0x89, 0xbf, + 0x8a, 0xf8, 0x8a, 0xcb, 0x8a, 0x01, 0x8b, 0xfe, + 0x8a, 0xed, 0x8a, 0x39, 0x8b, 0x8a, 0x8b, 0x08, + 0x8d, 0x38, 0x8f, 0x72, 0x90, 0x99, 0x91, 0x76, + 0x92, 0x7c, 0x96, 0xe3, 0x96, 0x56, 0x97, 0xdb, + 0x97, 0xff, 0x97, 0x0b, 0x98, 0x3b, 0x98, 0x12, + 0x9b, 0x9c, 0x9f, 0x4a, 0x28, 0x44, 0x28, 0xd5, + 0x33, 0x9d, 0x3b, 0x18, 0x40, 0x39, 0x40, 0x49, + 0x52, 0xd0, 0x5c, 0xd3, 0x7e, 0x43, 0x9f, 0x8e, + 0x9f, 0x2a, 0xa0, 0x02, 0x66, 0x66, 0x66, 0x69, + 0x66, 0x6c, 0x66, 0x66, 0x69, 0x66, 0x66, 0x6c, + 0x7f, 0x01, 0x74, 0x73, 0x00, 0x74, 0x65, 0x05, + 0x0f, 0x11, 0x0f, 0x00, 0x0f, 0x06, 0x19, 0x11, + 0x0f, 0x08, 0xd9, 0x05, 0xb4, 0x05, 0x00, 0x00, + 0x00, 0x00, 0xf2, 0x05, 0xb7, 0x05, 0xd0, 0x05, + 0x12, 0x00, 0x03, 0x04, 0x0b, 0x0c, 0x0d, 0x18, + 0x1a, 0xe9, 0x05, 0xc1, 0x05, 0xe9, 0x05, 0xc2, + 0x05, 0x49, 0xfb, 0xc1, 0x05, 0x49, 0xfb, 0xc2, + 0x05, 0xd0, 0x05, 0xb7, 0x05, 0xd0, 0x05, 0xb8, + 0x05, 0xd0, 0x05, 0xbc, 0x05, 0xd8, 0x05, 0xbc, + 0x05, 0xde, 0x05, 0xbc, 0x05, 0xe0, 0x05, 0xbc, + 0x05, 0xe3, 0x05, 0xbc, 0x05, 0xb9, 0x05, 0x2d, + 0x03, 0x2e, 0x03, 0x2f, 0x03, 0x30, 0x03, 0x31, + 0x03, 0x1c, 0x00, 0x18, 0x06, 0x22, 0x06, 0x2b, + 0x06, 0xd0, 0x05, 0xdc, 0x05, 0x71, 0x06, 0x00, + 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0f, 0x0f, 0x0f, 0x0f, 0x09, 0x09, 0x09, + 0x09, 0x0e, 0x0e, 0x0e, 0x0e, 0x08, 0x08, 0x08, + 0x08, 0x33, 0x33, 0x33, 0x33, 0x35, 0x35, 0x35, + 0x35, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, + 0x12, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, + 0x16, 0x1c, 0x1c, 0x1b, 0x1b, 0x1d, 0x1d, 0x17, + 0x17, 0x27, 0x27, 0x20, 0x20, 0x38, 0x38, 0x38, + 0x38, 0x3e, 0x3e, 0x3e, 0x3e, 0x42, 0x42, 0x42, + 0x42, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, 0x4a, + 0x4a, 0x4a, 0x4a, 0x4f, 0x4f, 0x50, 0x50, 0x50, + 0x50, 0x4d, 0x4d, 0x4d, 0x4d, 0x61, 0x61, 0x62, + 0x62, 0x49, 0x06, 0x64, 0x64, 0x64, 0x64, 0x7e, + 0x7e, 0x7d, 0x7d, 0x7f, 0x7f, 0x2e, 0x82, 0x82, + 0x7c, 0x7c, 0x80, 0x80, 0x87, 0x87, 0x87, 0x87, + 0x00, 0x00, 0x26, 0x06, 0x00, 0x01, 0x00, 0x01, + 0x00, 0xaf, 0x00, 0xaf, 0x00, 0x22, 0x00, 0x22, + 0x00, 0xa1, 0x00, 0xa1, 0x00, 0xa0, 0x00, 0xa0, + 0x00, 0xa2, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0xaa, + 0x00, 0xaa, 0x00, 0x23, 0x00, 0x23, 0x00, 0x23, + 0xcc, 0x06, 0x00, 0x00, 0x00, 0x00, 0x26, 0x06, + 0x00, 0x06, 0x00, 0x07, 0x00, 0x1f, 0x00, 0x23, + 0x00, 0x24, 0x02, 0x06, 0x02, 0x07, 0x02, 0x08, + 0x02, 0x1f, 0x02, 0x23, 0x02, 0x24, 0x04, 0x06, + 0x04, 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x23, + 0x04, 0x24, 0x05, 0x06, 0x05, 0x1f, 0x05, 0x23, + 0x05, 0x24, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06, + 0x07, 0x1f, 0x08, 0x06, 0x08, 0x07, 0x08, 0x1f, + 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x08, 0x0d, 0x1f, + 0x0f, 0x07, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07, + 0x10, 0x08, 0x10, 0x1f, 0x11, 0x07, 0x11, 0x1f, + 0x12, 0x1f, 0x13, 0x06, 0x13, 0x1f, 0x14, 0x06, + 0x14, 0x1f, 0x1b, 0x06, 0x1b, 0x07, 0x1b, 0x08, + 0x1b, 0x1f, 0x1b, 0x23, 0x1b, 0x24, 0x1c, 0x07, + 0x1c, 0x1f, 0x1c, 0x23, 0x1c, 0x24, 0x1d, 0x01, + 0x1d, 0x06, 0x1d, 0x07, 0x1d, 0x08, 0x1d, 0x1e, + 0x1d, 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x06, + 0x1e, 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x23, + 0x1e, 0x24, 0x1f, 0x06, 0x1f, 0x07, 0x1f, 0x08, + 0x1f, 0x1f, 0x1f, 0x23, 0x1f, 0x24, 0x20, 0x06, + 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, 0x20, 0x23, + 0x20, 0x24, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x23, + 0x21, 0x24, 0x24, 0x06, 0x24, 0x07, 0x24, 0x08, + 0x24, 0x1f, 0x24, 0x23, 0x24, 0x24, 0x0a, 0x4a, + 0x0b, 0x4a, 0x23, 0x4a, 0x20, 0x00, 0x4c, 0x06, + 0x51, 0x06, 0x51, 0x06, 0xff, 0x00, 0x1f, 0x26, + 0x06, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x1f, 0x00, + 0x20, 0x00, 0x23, 0x00, 0x24, 0x02, 0x0b, 0x02, + 0x0c, 0x02, 0x1f, 0x02, 0x20, 0x02, 0x23, 0x02, + 0x24, 0x04, 0x0b, 0x04, 0x0c, 0x04, 0x1f, 0x26, + 0x06, 0x04, 0x20, 0x04, 0x23, 0x04, 0x24, 0x05, + 0x0b, 0x05, 0x0c, 0x05, 0x1f, 0x05, 0x20, 0x05, + 0x23, 0x05, 0x24, 0x1b, 0x23, 0x1b, 0x24, 0x1c, + 0x23, 0x1c, 0x24, 0x1d, 0x01, 0x1d, 0x1e, 0x1d, + 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x1f, 0x1e, + 0x23, 0x1e, 0x24, 0x1f, 0x01, 0x1f, 0x1f, 0x20, + 0x0b, 0x20, 0x0c, 0x20, 0x1f, 0x20, 0x20, 0x20, + 0x23, 0x20, 0x24, 0x23, 0x4a, 0x24, 0x0b, 0x24, + 0x0c, 0x24, 0x1f, 0x24, 0x20, 0x24, 0x23, 0x24, + 0x24, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, + 0x1f, 0x00, 0x21, 0x02, 0x06, 0x02, 0x07, 0x02, + 0x08, 0x02, 0x1f, 0x02, 0x21, 0x04, 0x06, 0x04, + 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x21, 0x05, + 0x1f, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06, 0x07, + 0x1f, 0x08, 0x06, 0x08, 0x1f, 0x0d, 0x06, 0x0d, + 0x07, 0x0d, 0x08, 0x0d, 0x1f, 0x0f, 0x07, 0x0f, + 0x08, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07, 0x10, + 0x08, 0x10, 0x1f, 0x11, 0x07, 0x12, 0x1f, 0x13, + 0x06, 0x13, 0x1f, 0x14, 0x06, 0x14, 0x1f, 0x1b, + 0x06, 0x1b, 0x07, 0x1b, 0x08, 0x1b, 0x1f, 0x1c, + 0x07, 0x1c, 0x1f, 0x1d, 0x06, 0x1d, 0x07, 0x1d, + 0x08, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x06, 0x1e, + 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x21, 0x1f, + 0x06, 0x1f, 0x07, 0x1f, 0x08, 0x1f, 0x1f, 0x20, + 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, 0x20, + 0x21, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x4a, 0x24, + 0x06, 0x24, 0x07, 0x24, 0x08, 0x24, 0x1f, 0x24, + 0x21, 0x00, 0x1f, 0x00, 0x21, 0x02, 0x1f, 0x02, + 0x21, 0x04, 0x1f, 0x04, 0x21, 0x05, 0x1f, 0x05, + 0x21, 0x0d, 0x1f, 0x0d, 0x21, 0x0e, 0x1f, 0x0e, + 0x21, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x1f, 0x20, + 0x1f, 0x20, 0x21, 0x24, 0x1f, 0x24, 0x21, 0x40, + 0x06, 0x4e, 0x06, 0x51, 0x06, 0x27, 0x06, 0x10, + 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, 0x13, + 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, 0x0d, + 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, 0x05, + 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, 0x0e, + 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, 0x0d, + 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0d, + 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x10, + 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, 0x13, + 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, 0x0d, + 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, 0x05, + 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, 0x0e, + 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, 0x0d, + 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0d, + 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x0d, + 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0c, + 0x20, 0x0d, 0x20, 0x10, 0x1e, 0x0c, 0x05, 0x0c, + 0x06, 0x0c, 0x07, 0x0d, 0x05, 0x0d, 0x06, 0x0d, + 0x07, 0x10, 0x1e, 0x11, 0x1e, 0x00, 0x24, 0x00, + 0x24, 0x2a, 0x06, 0x00, 0x02, 0x1b, 0x00, 0x03, + 0x02, 0x00, 0x03, 0x02, 0x00, 0x03, 0x1b, 0x00, + 0x04, 0x1b, 0x00, 0x1b, 0x02, 0x00, 0x1b, 0x03, + 0x00, 0x1b, 0x04, 0x02, 0x1b, 0x03, 0x02, 0x1b, + 0x03, 0x03, 0x1b, 0x20, 0x03, 0x1b, 0x1f, 0x09, + 0x03, 0x02, 0x09, 0x02, 0x03, 0x09, 0x02, 0x1f, + 0x09, 0x1b, 0x03, 0x09, 0x1b, 0x03, 0x09, 0x1b, + 0x02, 0x09, 0x1b, 0x1b, 0x09, 0x1b, 0x1b, 0x0b, + 0x03, 0x03, 0x0b, 0x03, 0x03, 0x0b, 0x1b, 0x1b, + 0x0a, 0x03, 0x1b, 0x0a, 0x03, 0x1b, 0x0a, 0x02, + 0x20, 0x0a, 0x1b, 0x04, 0x0a, 0x1b, 0x04, 0x0a, + 0x1b, 0x1b, 0x0a, 0x1b, 0x1b, 0x0c, 0x03, 0x1f, + 0x0c, 0x04, 0x1b, 0x0c, 0x04, 0x1b, 0x0d, 0x1b, + 0x03, 0x0d, 0x1b, 0x03, 0x0d, 0x1b, 0x1b, 0x0d, + 0x1b, 0x20, 0x0f, 0x02, 0x1b, 0x0f, 0x1b, 0x1b, + 0x0f, 0x1b, 0x1b, 0x0f, 0x1b, 0x1f, 0x10, 0x1b, + 0x1b, 0x10, 0x1b, 0x20, 0x10, 0x1b, 0x1f, 0x17, + 0x04, 0x1b, 0x17, 0x04, 0x1b, 0x18, 0x1b, 0x03, + 0x18, 0x1b, 0x1b, 0x1a, 0x03, 0x1b, 0x1a, 0x03, + 0x20, 0x1a, 0x03, 0x1f, 0x1a, 0x02, 0x02, 0x1a, + 0x02, 0x02, 0x1a, 0x04, 0x1b, 0x1a, 0x04, 0x1b, + 0x1a, 0x1b, 0x03, 0x1a, 0x1b, 0x03, 0x1b, 0x03, + 0x02, 0x1b, 0x03, 0x1b, 0x1b, 0x03, 0x20, 0x1b, + 0x02, 0x03, 0x1b, 0x02, 0x1b, 0x1b, 0x04, 0x02, + 0x1b, 0x04, 0x1b, 0x28, 0x06, 0x1d, 0x04, 0x06, + 0x1f, 0x1d, 0x04, 0x1f, 0x1d, 0x1d, 0x1e, 0x05, + 0x1d, 0x1e, 0x05, 0x21, 0x1e, 0x04, 0x1d, 0x1e, + 0x04, 0x1d, 0x1e, 0x04, 0x21, 0x1e, 0x1d, 0x22, + 0x1e, 0x1d, 0x21, 0x22, 0x1d, 0x1d, 0x22, 0x1d, + 0x1d, 0x00, 0x06, 0x22, 0x02, 0x04, 0x22, 0x02, + 0x04, 0x21, 0x02, 0x06, 0x22, 0x02, 0x06, 0x21, + 0x02, 0x1d, 0x22, 0x02, 0x1d, 0x21, 0x04, 0x1d, + 0x22, 0x04, 0x05, 0x21, 0x04, 0x1d, 0x21, 0x0b, + 0x06, 0x21, 0x0d, 0x05, 0x22, 0x0c, 0x05, 0x22, + 0x0e, 0x05, 0x22, 0x1c, 0x04, 0x22, 0x1c, 0x1d, + 0x22, 0x22, 0x05, 0x22, 0x22, 0x04, 0x22, 0x22, + 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1a, 0x1d, 0x22, + 0x1e, 0x05, 0x22, 0x1a, 0x1d, 0x05, 0x1c, 0x05, + 0x1d, 0x11, 0x1d, 0x22, 0x1b, 0x1d, 0x22, 0x1e, + 0x04, 0x05, 0x1d, 0x06, 0x22, 0x1c, 0x04, 0x1d, + 0x1b, 0x1d, 0x1d, 0x1c, 0x04, 0x1d, 0x1e, 0x04, + 0x05, 0x04, 0x05, 0x22, 0x05, 0x04, 0x22, 0x1d, + 0x04, 0x22, 0x19, 0x1d, 0x22, 0x00, 0x05, 0x22, + 0x1b, 0x1d, 0x1d, 0x11, 0x04, 0x1d, 0x0d, 0x1d, + 0x1d, 0x0b, 0x06, 0x22, 0x1e, 0x04, 0x22, 0x35, + 0x06, 0x00, 0x0f, 0x9d, 0x0d, 0x0f, 0x9d, 0x27, + 0x06, 0x00, 0x1d, 0x1d, 0x20, 0x00, 0x1c, 0x01, + 0x0a, 0x1e, 0x06, 0x1e, 0x08, 0x0e, 0x1d, 0x12, + 0x1e, 0x0a, 0x0c, 0x21, 0x1d, 0x12, 0x1d, 0x23, + 0x20, 0x21, 0x0c, 0x1d, 0x1e, 0x35, 0x06, 0x00, + 0x0f, 0x14, 0x27, 0x06, 0x0e, 0x1d, 0x22, 0xff, + 0x00, 0x1d, 0x1d, 0x20, 0xff, 0x12, 0x1d, 0x23, + 0x20, 0xff, 0x21, 0x0c, 0x1d, 0x1e, 0x27, 0x06, + 0x05, 0x1d, 0xff, 0x05, 0x1d, 0x00, 0x1d, 0x20, + 0x27, 0x06, 0x0a, 0xa5, 0x00, 0x1d, 0x2c, 0x00, + 0x01, 0x30, 0x02, 0x30, 0x3a, 0x00, 0x3b, 0x00, + 0x21, 0x00, 0x3f, 0x00, 0x16, 0x30, 0x17, 0x30, + 0x26, 0x20, 0x13, 0x20, 0x12, 0x01, 0x00, 0x5f, + 0x5f, 0x28, 0x29, 0x7b, 0x7d, 0x08, 0x30, 0x0c, + 0x0d, 0x08, 0x09, 0x02, 0x03, 0x00, 0x01, 0x04, + 0x05, 0x06, 0x07, 0x5b, 0x00, 0x5d, 0x00, 0x3e, + 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x5f, + 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x2c, 0x00, 0x01, + 0x30, 0x2e, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x3a, + 0x00, 0x3f, 0x00, 0x21, 0x00, 0x14, 0x20, 0x28, + 0x00, 0x29, 0x00, 0x7b, 0x00, 0x7d, 0x00, 0x14, + 0x30, 0x15, 0x30, 0x23, 0x26, 0x2a, 0x2b, 0x2d, + 0x3c, 0x3e, 0x3d, 0x00, 0x5c, 0x24, 0x25, 0x40, + 0x40, 0x06, 0xff, 0x0b, 0x00, 0x0b, 0xff, 0x0c, + 0x20, 0x00, 0x4d, 0x06, 0x40, 0x06, 0xff, 0x0e, + 0x00, 0x0e, 0xff, 0x0f, 0x00, 0x0f, 0xff, 0x10, + 0x00, 0x10, 0xff, 0x11, 0x00, 0x11, 0xff, 0x12, + 0x00, 0x12, 0x21, 0x06, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x05, + 0x05, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, + 0x08, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f, + 0x0f, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x12, + 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, + 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, + 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, + 0x18, 0x19, 0x19, 0x19, 0x19, 0x20, 0x20, 0x20, + 0x20, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, + 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, + 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, + 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x29, + 0x29, 0x22, 0x06, 0x22, 0x00, 0x22, 0x00, 0x22, + 0x01, 0x22, 0x01, 0x22, 0x03, 0x22, 0x03, 0x22, + 0x05, 0x22, 0x05, 0x21, 0x00, 0x85, 0x29, 0x01, + 0x30, 0x01, 0x0b, 0x0c, 0x00, 0xfa, 0xf1, 0xa0, + 0xa2, 0xa4, 0xa6, 0xa8, 0xe2, 0xe4, 0xe6, 0xc2, + 0xfb, 0xa1, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa, 0xac, + 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, + 0xbe, 0xc0, 0xc3, 0xc5, 0xc7, 0xc9, 0xca, 0xcb, + 0xcc, 0xcd, 0xce, 0xd1, 0xd4, 0xd7, 0xda, 0xdd, + 0xde, 0xdf, 0xe0, 0xe1, 0xe3, 0xe5, 0xe7, 0xe8, + 0xe9, 0xea, 0xeb, 0xec, 0xee, 0xf2, 0x98, 0x99, + 0x31, 0x31, 0x4f, 0x31, 0x55, 0x31, 0x5b, 0x31, + 0x61, 0x31, 0xa2, 0x00, 0xa3, 0x00, 0xac, 0x00, + 0xaf, 0x00, 0xa6, 0x00, 0xa5, 0x00, 0xa9, 0x20, + 0x00, 0x00, 0x02, 0x25, 0x90, 0x21, 0x91, 0x21, + 0x92, 0x21, 0x93, 0x21, 0xa0, 0x25, 0xcb, 0x25, + 0xd0, 0x02, 0xd1, 0x02, 0xe6, 0x00, 0x99, 0x02, + 0x53, 0x02, 0x00, 0x00, 0xa3, 0x02, 0x66, 0xab, + 0xa5, 0x02, 0xa4, 0x02, 0x56, 0x02, 0x57, 0x02, + 0x91, 0x1d, 0x58, 0x02, 0x5e, 0x02, 0xa9, 0x02, + 0x64, 0x02, 0x62, 0x02, 0x60, 0x02, 0x9b, 0x02, + 0x27, 0x01, 0x9c, 0x02, 0x67, 0x02, 0x84, 0x02, + 0xaa, 0x02, 0xab, 0x02, 0x6c, 0x02, 0x04, 0xdf, + 0x8e, 0xa7, 0x6e, 0x02, 0x05, 0xdf, 0x8e, 0x02, + 0x06, 0xdf, 0xf8, 0x00, 0x76, 0x02, 0x77, 0x02, + 0x71, 0x00, 0x7a, 0x02, 0x08, 0xdf, 0x7d, 0x02, + 0x7e, 0x02, 0x80, 0x02, 0xa8, 0x02, 0xa6, 0x02, + 0x67, 0xab, 0xa7, 0x02, 0x88, 0x02, 0x71, 0x2c, + 0x00, 0x00, 0x8f, 0x02, 0xa1, 0x02, 0xa2, 0x02, + 0x98, 0x02, 0xc0, 0x01, 0xc1, 0x01, 0xc2, 0x01, + 0x0a, 0xdf, 0x1e, 0xdf, 0x41, 0x04, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x99, 0x10, 0xba, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x9b, 0x10, 0xba, 0x10, + 0x05, 0x05, 0xa5, 0x10, 0xba, 0x10, 0x05, 0x31, + 0x11, 0x27, 0x11, 0x32, 0x11, 0x27, 0x11, 0x55, + 0x47, 0x13, 0x3e, 0x13, 0x47, 0x13, 0x57, 0x13, + 0x55, 0xb9, 0x14, 0xba, 0x14, 0xb9, 0x14, 0xb0, + 0x14, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x14, 0xbd, + 0x14, 0x55, 0x50, 0xb8, 0x15, 0xaf, 0x15, 0xb9, + 0x15, 0xaf, 0x15, 0x55, 0x35, 0x19, 0x30, 0x19, + 0x05, 0x57, 0xd1, 0x65, 0xd1, 0x58, 0xd1, 0x65, + 0xd1, 0x5f, 0xd1, 0x6e, 0xd1, 0x5f, 0xd1, 0x6f, + 0xd1, 0x5f, 0xd1, 0x70, 0xd1, 0x5f, 0xd1, 0x71, + 0xd1, 0x5f, 0xd1, 0x72, 0xd1, 0x55, 0x55, 0x55, + 0x05, 0xb9, 0xd1, 0x65, 0xd1, 0xba, 0xd1, 0x65, + 0xd1, 0xbb, 0xd1, 0x6e, 0xd1, 0xbc, 0xd1, 0x6e, + 0xd1, 0xbb, 0xd1, 0x6f, 0xd1, 0xbc, 0xd1, 0x6f, + 0xd1, 0x55, 0x55, 0x55, 0x41, 0x00, 0x61, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x69, 0x00, 0x41, 0x00, + 0x61, 0x00, 0x41, 0x00, 0x43, 0x44, 0x00, 0x00, + 0x47, 0x00, 0x00, 0x4a, 0x4b, 0x00, 0x00, 0x4e, + 0x4f, 0x50, 0x51, 0x00, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, + 0x00, 0x66, 0x68, 0x00, 0x70, 0x00, 0x41, 0x00, + 0x61, 0x00, 0x41, 0x42, 0x00, 0x44, 0x45, 0x46, + 0x47, 0x4a, 0x00, 0x53, 0x00, 0x61, 0x00, 0x41, + 0x42, 0x00, 0x44, 0x45, 0x46, 0x47, 0x00, 0x49, + 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x4f, 0x53, 0x00, + 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, + 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, + 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00, + 0x61, 0x00, 0x31, 0x01, 0x37, 0x02, 0x91, 0x03, + 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, + 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, + 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, + 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, + 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, + 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, + 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, + 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, + 0x1f, 0x04, 0x20, 0x05, 0x0b, 0x0c, 0x30, 0x00, + 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, + 0x30, 0x04, 0x3a, 0x04, 0x3e, 0x04, 0x4b, 0x04, + 0x4d, 0x04, 0x4e, 0x04, 0x89, 0xa6, 0x30, 0x04, + 0xa9, 0x26, 0x28, 0xb9, 0x7f, 0x9f, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0a, + 0x0b, 0x0e, 0x0f, 0x11, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x1a, 0x1b, 0x61, 0x26, 0x25, 0x2f, + 0x7b, 0x51, 0xa6, 0xb1, 0x04, 0x27, 0x06, 0x00, + 0x01, 0x05, 0x08, 0x2a, 0x06, 0x1e, 0x08, 0x03, + 0x0d, 0x20, 0x19, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, + 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, + 0x06, 0x0c, 0x0e, 0x10, 0x44, 0x90, 0x77, 0x45, + 0x28, 0x06, 0x2c, 0x06, 0x00, 0x00, 0x47, 0x06, + 0x33, 0x06, 0x17, 0x10, 0x11, 0x12, 0x13, 0x00, + 0x06, 0x0e, 0x02, 0x0f, 0x34, 0x06, 0x2a, 0x06, + 0x2b, 0x06, 0x2e, 0x06, 0x00, 0x00, 0x36, 0x06, + 0x00, 0x00, 0x3a, 0x06, 0x2d, 0x06, 0x00, 0x00, + 0x4a, 0x06, 0x00, 0x00, 0x44, 0x06, 0x00, 0x00, + 0x46, 0x06, 0x33, 0x06, 0x39, 0x06, 0x00, 0x00, + 0x35, 0x06, 0x42, 0x06, 0x00, 0x00, 0x34, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x2e, 0x06, 0x00, 0x00, + 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, 0x00, 0x00, + 0xba, 0x06, 0x00, 0x00, 0x6f, 0x06, 0x00, 0x00, + 0x28, 0x06, 0x2c, 0x06, 0x00, 0x00, 0x47, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x2d, 0x06, 0x37, 0x06, + 0x4a, 0x06, 0x43, 0x06, 0x00, 0x00, 0x45, 0x06, + 0x46, 0x06, 0x33, 0x06, 0x39, 0x06, 0x41, 0x06, + 0x35, 0x06, 0x42, 0x06, 0x00, 0x00, 0x34, 0x06, + 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, 0x00, 0x00, + 0x36, 0x06, 0x38, 0x06, 0x3a, 0x06, 0x6e, 0x06, + 0x00, 0x00, 0xa1, 0x06, 0x27, 0x06, 0x00, 0x01, + 0x05, 0x08, 0x20, 0x21, 0x0b, 0x06, 0x10, 0x23, + 0x2a, 0x06, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, 0x17, + 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, 0x06, + 0x0c, 0x0e, 0x10, 0x28, 0x06, 0x2c, 0x06, 0x2f, + 0x06, 0x00, 0x00, 0x48, 0x06, 0x32, 0x06, 0x2d, + 0x06, 0x37, 0x06, 0x4a, 0x06, 0x2a, 0x06, 0x1a, + 0x1b, 0x1c, 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, + 0x0a, 0x00, 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, + 0x30, 0x2e, 0x30, 0x00, 0x2c, 0x00, 0x28, 0x00, + 0x41, 0x00, 0x29, 0x00, 0x14, 0x30, 0x53, 0x00, + 0x15, 0x30, 0x43, 0x52, 0x43, 0x44, 0x57, 0x5a, + 0x41, 0x00, 0x48, 0x56, 0x4d, 0x56, 0x53, 0x44, + 0x53, 0x53, 0x50, 0x50, 0x56, 0x57, 0x43, 0x4d, + 0x43, 0x4d, 0x44, 0x4d, 0x52, 0x44, 0x4a, 0x4b, + 0x30, 0x30, 0x00, 0x68, 0x68, 0x4b, 0x62, 0x57, + 0x5b, 0xcc, 0x53, 0xc7, 0x30, 0x8c, 0x4e, 0x1a, + 0x59, 0xe3, 0x89, 0x29, 0x59, 0xa4, 0x4e, 0x20, + 0x66, 0x21, 0x71, 0x99, 0x65, 0x4d, 0x52, 0x8c, + 0x5f, 0x8d, 0x51, 0xb0, 0x65, 0x1d, 0x52, 0x42, + 0x7d, 0x1f, 0x75, 0xa9, 0x8c, 0xf0, 0x58, 0x39, + 0x54, 0x14, 0x6f, 0x95, 0x62, 0x55, 0x63, 0x00, + 0x4e, 0x09, 0x4e, 0x4a, 0x90, 0xe6, 0x5d, 0x2d, + 0x4e, 0xf3, 0x53, 0x07, 0x63, 0x70, 0x8d, 0x53, + 0x62, 0x81, 0x79, 0x7a, 0x7a, 0x08, 0x54, 0x80, + 0x6e, 0x09, 0x67, 0x08, 0x67, 0x33, 0x75, 0x72, + 0x52, 0xb6, 0x55, 0x4d, 0x91, 0x14, 0x30, 0x15, + 0x30, 0x2c, 0x67, 0x09, 0x4e, 0x8c, 0x4e, 0x89, + 0x5b, 0xb9, 0x70, 0x53, 0x62, 0xd7, 0x76, 0xdd, + 0x52, 0x57, 0x65, 0x97, 0x5f, 0xef, 0x53, 0x30, + 0x00, 0x38, 0x4e, 0x05, 0x00, 0x09, 0x22, 0x01, + 0x60, 0x4f, 0xae, 0x4f, 0xbb, 0x4f, 0x02, 0x50, + 0x7a, 0x50, 0x99, 0x50, 0xe7, 0x50, 0xcf, 0x50, + 0x9e, 0x34, 0x3a, 0x06, 0x4d, 0x51, 0x54, 0x51, + 0x64, 0x51, 0x77, 0x51, 0x1c, 0x05, 0xb9, 0x34, + 0x67, 0x51, 0x8d, 0x51, 0x4b, 0x05, 0x97, 0x51, + 0xa4, 0x51, 0xcc, 0x4e, 0xac, 0x51, 0xb5, 0x51, + 0xdf, 0x91, 0xf5, 0x51, 0x03, 0x52, 0xdf, 0x34, + 0x3b, 0x52, 0x46, 0x52, 0x72, 0x52, 0x77, 0x52, + 0x15, 0x35, 0x02, 0x00, 0x20, 0x80, 0x80, 0x00, + 0x08, 0x00, 0x00, 0xc7, 0x52, 0x00, 0x02, 0x1d, + 0x33, 0x3e, 0x3f, 0x50, 0x82, 0x8a, 0x93, 0xac, + 0xb6, 0xb8, 0xb8, 0xb8, 0x2c, 0x0a, 0x70, 0x70, + 0xca, 0x53, 0xdf, 0x53, 0x63, 0x0b, 0xeb, 0x53, + 0xf1, 0x53, 0x06, 0x54, 0x9e, 0x54, 0x38, 0x54, + 0x48, 0x54, 0x68, 0x54, 0xa2, 0x54, 0xf6, 0x54, + 0x10, 0x55, 0x53, 0x55, 0x63, 0x55, 0x84, 0x55, + 0x84, 0x55, 0x99, 0x55, 0xab, 0x55, 0xb3, 0x55, + 0xc2, 0x55, 0x16, 0x57, 0x06, 0x56, 0x17, 0x57, + 0x51, 0x56, 0x74, 0x56, 0x07, 0x52, 0xee, 0x58, + 0xce, 0x57, 0xf4, 0x57, 0x0d, 0x58, 0x8b, 0x57, + 0x32, 0x58, 0x31, 0x58, 0xac, 0x58, 0xe4, 0x14, + 0xf2, 0x58, 0xf7, 0x58, 0x06, 0x59, 0x1a, 0x59, + 0x22, 0x59, 0x62, 0x59, 0xa8, 0x16, 0xea, 0x16, + 0xec, 0x59, 0x1b, 0x5a, 0x27, 0x5a, 0xd8, 0x59, + 0x66, 0x5a, 0xee, 0x36, 0xfc, 0x36, 0x08, 0x5b, + 0x3e, 0x5b, 0x3e, 0x5b, 0xc8, 0x19, 0xc3, 0x5b, + 0xd8, 0x5b, 0xe7, 0x5b, 0xf3, 0x5b, 0x18, 0x1b, + 0xff, 0x5b, 0x06, 0x5c, 0x53, 0x5f, 0x22, 0x5c, + 0x81, 0x37, 0x60, 0x5c, 0x6e, 0x5c, 0xc0, 0x5c, + 0x8d, 0x5c, 0xe4, 0x1d, 0x43, 0x5d, 0xe6, 0x1d, + 0x6e, 0x5d, 0x6b, 0x5d, 0x7c, 0x5d, 0xe1, 0x5d, + 0xe2, 0x5d, 0x2f, 0x38, 0xfd, 0x5d, 0x28, 0x5e, + 0x3d, 0x5e, 0x69, 0x5e, 0x62, 0x38, 0x83, 0x21, + 0x7c, 0x38, 0xb0, 0x5e, 0xb3, 0x5e, 0xb6, 0x5e, + 0xca, 0x5e, 0x92, 0xa3, 0xfe, 0x5e, 0x31, 0x23, + 0x31, 0x23, 0x01, 0x82, 0x22, 0x5f, 0x22, 0x5f, + 0xc7, 0x38, 0xb8, 0x32, 0xda, 0x61, 0x62, 0x5f, + 0x6b, 0x5f, 0xe3, 0x38, 0x9a, 0x5f, 0xcd, 0x5f, + 0xd7, 0x5f, 0xf9, 0x5f, 0x81, 0x60, 0x3a, 0x39, + 0x1c, 0x39, 0x94, 0x60, 0xd4, 0x26, 0xc7, 0x60, + 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x0a, 0x00, 0x00, 0x02, 0x08, + 0x00, 0x80, 0x08, 0x00, 0x00, 0x08, 0x80, 0x28, + 0x80, 0x02, 0x00, 0x00, 0x02, 0x48, 0x61, 0x00, + 0x04, 0x06, 0x04, 0x32, 0x46, 0x6a, 0x5c, 0x67, + 0x96, 0xaa, 0xae, 0xc8, 0xd3, 0x5d, 0x62, 0x00, + 0x54, 0x77, 0xf3, 0x0c, 0x2b, 0x3d, 0x63, 0xfc, + 0x62, 0x68, 0x63, 0x83, 0x63, 0xe4, 0x63, 0xf1, + 0x2b, 0x22, 0x64, 0xc5, 0x63, 0xa9, 0x63, 0x2e, + 0x3a, 0x69, 0x64, 0x7e, 0x64, 0x9d, 0x64, 0x77, + 0x64, 0x6c, 0x3a, 0x4f, 0x65, 0x6c, 0x65, 0x0a, + 0x30, 0xe3, 0x65, 0xf8, 0x66, 0x49, 0x66, 0x19, + 0x3b, 0x91, 0x66, 0x08, 0x3b, 0xe4, 0x3a, 0x92, + 0x51, 0x95, 0x51, 0x00, 0x67, 0x9c, 0x66, 0xad, + 0x80, 0xd9, 0x43, 0x17, 0x67, 0x1b, 0x67, 0x21, + 0x67, 0x5e, 0x67, 0x53, 0x67, 0xc3, 0x33, 0x49, + 0x3b, 0xfa, 0x67, 0x85, 0x67, 0x52, 0x68, 0x85, + 0x68, 0x6d, 0x34, 0x8e, 0x68, 0x1f, 0x68, 0x14, + 0x69, 0x9d, 0x3b, 0x42, 0x69, 0xa3, 0x69, 0xea, + 0x69, 0xa8, 0x6a, 0xa3, 0x36, 0xdb, 0x6a, 0x18, + 0x3c, 0x21, 0x6b, 0xa7, 0x38, 0x54, 0x6b, 0x4e, + 0x3c, 0x72, 0x6b, 0x9f, 0x6b, 0xba, 0x6b, 0xbb, + 0x6b, 0x8d, 0x3a, 0x0b, 0x1d, 0xfa, 0x3a, 0x4e, + 0x6c, 0xbc, 0x3c, 0xbf, 0x6c, 0xcd, 0x6c, 0x67, + 0x6c, 0x16, 0x6d, 0x3e, 0x6d, 0x77, 0x6d, 0x41, + 0x6d, 0x69, 0x6d, 0x78, 0x6d, 0x85, 0x6d, 0x1e, + 0x3d, 0x34, 0x6d, 0x2f, 0x6e, 0x6e, 0x6e, 0x33, + 0x3d, 0xcb, 0x6e, 0xc7, 0x6e, 0xd1, 0x3e, 0xf9, + 0x6d, 0x6e, 0x6f, 0x5e, 0x3f, 0x8e, 0x3f, 0xc6, + 0x6f, 0x39, 0x70, 0x1e, 0x70, 0x1b, 0x70, 0x96, + 0x3d, 0x4a, 0x70, 0x7d, 0x70, 0x77, 0x70, 0xad, + 0x70, 0x25, 0x05, 0x45, 0x71, 0x63, 0x42, 0x9c, + 0x71, 0xab, 0x43, 0x28, 0x72, 0x35, 0x72, 0x50, + 0x72, 0x08, 0x46, 0x80, 0x72, 0x95, 0x72, 0x35, + 0x47, 0x02, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x80, 0x00, 0x00, 0x02, 0x02, + 0x80, 0x8a, 0x00, 0x00, 0x20, 0x00, 0x08, 0x0a, + 0x00, 0x80, 0x88, 0x80, 0x20, 0x14, 0x48, 0x7a, + 0x73, 0x8b, 0x73, 0xac, 0x3e, 0xa5, 0x73, 0xb8, + 0x3e, 0xb8, 0x3e, 0x47, 0x74, 0x5c, 0x74, 0x71, + 0x74, 0x85, 0x74, 0xca, 0x74, 0x1b, 0x3f, 0x24, + 0x75, 0x36, 0x4c, 0x3e, 0x75, 0x92, 0x4c, 0x70, + 0x75, 0x9f, 0x21, 0x10, 0x76, 0xa1, 0x4f, 0xb8, + 0x4f, 0x44, 0x50, 0xfc, 0x3f, 0x08, 0x40, 0xf4, + 0x76, 0xf3, 0x50, 0xf2, 0x50, 0x19, 0x51, 0x33, + 0x51, 0x1e, 0x77, 0x1f, 0x77, 0x1f, 0x77, 0x4a, + 0x77, 0x39, 0x40, 0x8b, 0x77, 0x46, 0x40, 0x96, + 0x40, 0x1d, 0x54, 0x4e, 0x78, 0x8c, 0x78, 0xcc, + 0x78, 0xe3, 0x40, 0x26, 0x56, 0x56, 0x79, 0x9a, + 0x56, 0xc5, 0x56, 0x8f, 0x79, 0xeb, 0x79, 0x2f, + 0x41, 0x40, 0x7a, 0x4a, 0x7a, 0x4f, 0x7a, 0x7c, + 0x59, 0xa7, 0x5a, 0xa7, 0x5a, 0xee, 0x7a, 0x02, + 0x42, 0xab, 0x5b, 0xc6, 0x7b, 0xc9, 0x7b, 0x27, + 0x42, 0x80, 0x5c, 0xd2, 0x7c, 0xa0, 0x42, 0xe8, + 0x7c, 0xe3, 0x7c, 0x00, 0x7d, 0x86, 0x5f, 0x63, + 0x7d, 0x01, 0x43, 0xc7, 0x7d, 0x02, 0x7e, 0x45, + 0x7e, 0x34, 0x43, 0x28, 0x62, 0x47, 0x62, 0x59, + 0x43, 0xd9, 0x62, 0x7a, 0x7f, 0x3e, 0x63, 0x95, + 0x7f, 0xfa, 0x7f, 0x05, 0x80, 0xda, 0x64, 0x23, + 0x65, 0x60, 0x80, 0xa8, 0x65, 0x70, 0x80, 0x5f, + 0x33, 0xd5, 0x43, 0xb2, 0x80, 0x03, 0x81, 0x0b, + 0x44, 0x3e, 0x81, 0xb5, 0x5a, 0xa7, 0x67, 0xb5, + 0x67, 0x93, 0x33, 0x9c, 0x33, 0x01, 0x82, 0x04, + 0x82, 0x9e, 0x8f, 0x6b, 0x44, 0x91, 0x82, 0x8b, + 0x82, 0x9d, 0x82, 0xb3, 0x52, 0xb1, 0x82, 0xb3, + 0x82, 0xbd, 0x82, 0xe6, 0x82, 0x3c, 0x6b, 0xe5, + 0x82, 0x1d, 0x83, 0x63, 0x83, 0xad, 0x83, 0x23, + 0x83, 0xbd, 0x83, 0xe7, 0x83, 0x57, 0x84, 0x53, + 0x83, 0xca, 0x83, 0xcc, 0x83, 0xdc, 0x83, 0x36, + 0x6c, 0x6b, 0x6d, 0x02, 0x00, 0x00, 0x20, 0x22, + 0x2a, 0xa0, 0x0a, 0x00, 0x20, 0x80, 0x28, 0x00, + 0xa8, 0x20, 0x20, 0x00, 0x02, 0x80, 0x22, 0x02, + 0x8a, 0x08, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x28, 0xd5, 0x6c, 0x2b, 0x45, 0xf1, + 0x84, 0xf3, 0x84, 0x16, 0x85, 0xca, 0x73, 0x64, + 0x85, 0x2c, 0x6f, 0x5d, 0x45, 0x61, 0x45, 0xb1, + 0x6f, 0xd2, 0x70, 0x6b, 0x45, 0x50, 0x86, 0x5c, + 0x86, 0x67, 0x86, 0x69, 0x86, 0xa9, 0x86, 0x88, + 0x86, 0x0e, 0x87, 0xe2, 0x86, 0x79, 0x87, 0x28, + 0x87, 0x6b, 0x87, 0x86, 0x87, 0xd7, 0x45, 0xe1, + 0x87, 0x01, 0x88, 0xf9, 0x45, 0x60, 0x88, 0x63, + 0x88, 0x67, 0x76, 0xd7, 0x88, 0xde, 0x88, 0x35, + 0x46, 0xfa, 0x88, 0xbb, 0x34, 0xae, 0x78, 0x66, + 0x79, 0xbe, 0x46, 0xc7, 0x46, 0xa0, 0x8a, 0xed, + 0x8a, 0x8a, 0x8b, 0x55, 0x8c, 0xa8, 0x7c, 0xab, + 0x8c, 0xc1, 0x8c, 0x1b, 0x8d, 0x77, 0x8d, 0x2f, + 0x7f, 0x04, 0x08, 0xcb, 0x8d, 0xbc, 0x8d, 0xf0, + 0x8d, 0xde, 0x08, 0xd4, 0x8e, 0x38, 0x8f, 0xd2, + 0x85, 0xed, 0x85, 0x94, 0x90, 0xf1, 0x90, 0x11, + 0x91, 0x2e, 0x87, 0x1b, 0x91, 0x38, 0x92, 0xd7, + 0x92, 0xd8, 0x92, 0x7c, 0x92, 0xf9, 0x93, 0x15, + 0x94, 0xfa, 0x8b, 0x8b, 0x95, 0x95, 0x49, 0xb7, + 0x95, 0x77, 0x8d, 0xe6, 0x49, 0xc3, 0x96, 0xb2, + 0x5d, 0x23, 0x97, 0x45, 0x91, 0x1a, 0x92, 0x6e, + 0x4a, 0x76, 0x4a, 0xe0, 0x97, 0x0a, 0x94, 0xb2, + 0x4a, 0x96, 0x94, 0x0b, 0x98, 0x0b, 0x98, 0x29, + 0x98, 0xb6, 0x95, 0xe2, 0x98, 0x33, 0x4b, 0x29, + 0x99, 0xa7, 0x99, 0xc2, 0x99, 0xfe, 0x99, 0xce, + 0x4b, 0x30, 0x9b, 0x12, 0x9b, 0x40, 0x9c, 0xfd, + 0x9c, 0xce, 0x4c, 0xed, 0x4c, 0x67, 0x9d, 0xce, + 0xa0, 0xf8, 0x4c, 0x05, 0xa1, 0x0e, 0xa2, 0x91, + 0xa2, 0xbb, 0x9e, 0x56, 0x4d, 0xf9, 0x9e, 0xfe, + 0x9e, 0x05, 0x9f, 0x0f, 0x9f, 0x16, 0x9f, 0x3b, + 0x9f, 0x00, 0xa6, 0x02, 0x88, 0xa0, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x28, 0x00, 0x08, 0xa0, + 0x80, 0xa0, 0x80, 0x00, 0x80, 0x80, 0x00, 0x0a, + 0x88, 0x80, 0x00, 0x80, 0x00, 0x20, 0x2a, 0x00, + 0x80, +}; + +static const uint16_t unicode_comp_table[945] = { + 0x4a01, 0x49c0, 0x4a02, 0x0280, 0x0281, 0x0282, 0x0283, 0x02c0, + 0x02c2, 0x0a00, 0x0284, 0x2442, 0x0285, 0x07c0, 0x0980, 0x0982, + 0x2440, 0x2280, 0x02c4, 0x2282, 0x2284, 0x2286, 0x02c6, 0x02c8, + 0x02ca, 0x02cc, 0x0287, 0x228a, 0x02ce, 0x228c, 0x2290, 0x2292, + 0x228e, 0x0288, 0x0289, 0x028a, 0x2482, 0x0300, 0x0302, 0x0304, + 0x028b, 0x2480, 0x0308, 0x0984, 0x0986, 0x2458, 0x0a02, 0x0306, + 0x2298, 0x229a, 0x229e, 0x0900, 0x030a, 0x22a0, 0x030c, 0x030e, + 0x0840, 0x0310, 0x0312, 0x22a2, 0x22a6, 0x09c0, 0x22a4, 0x22a8, + 0x22aa, 0x028c, 0x028d, 0x028e, 0x0340, 0x0342, 0x0344, 0x0380, + 0x028f, 0x248e, 0x07c2, 0x0988, 0x098a, 0x2490, 0x0346, 0x22ac, + 0x0400, 0x22b0, 0x0842, 0x22b2, 0x0402, 0x22b4, 0x0440, 0x0444, + 0x22b6, 0x0442, 0x22c2, 0x22c0, 0x22c4, 0x22c6, 0x22c8, 0x0940, + 0x04c0, 0x0291, 0x22ca, 0x04c4, 0x22cc, 0x04c2, 0x22d0, 0x22ce, + 0x0292, 0x0293, 0x0294, 0x0295, 0x0540, 0x0542, 0x0a08, 0x0296, + 0x2494, 0x0544, 0x07c4, 0x098c, 0x098e, 0x06c0, 0x2492, 0x0844, + 0x2308, 0x230a, 0x0580, 0x230c, 0x0584, 0x0990, 0x0992, 0x230e, + 0x0582, 0x2312, 0x0586, 0x0588, 0x2314, 0x058c, 0x2316, 0x0998, + 0x058a, 0x231e, 0x0590, 0x2320, 0x099a, 0x058e, 0x2324, 0x2322, + 0x0299, 0x029a, 0x029b, 0x05c0, 0x05c2, 0x05c4, 0x029c, 0x24ac, + 0x05c6, 0x05c8, 0x07c6, 0x0994, 0x0996, 0x0700, 0x24aa, 0x2326, + 0x05ca, 0x232a, 0x2328, 0x2340, 0x2342, 0x2344, 0x2346, 0x05cc, + 0x234a, 0x2348, 0x234c, 0x234e, 0x2350, 0x24b8, 0x029d, 0x05ce, + 0x24be, 0x0a0c, 0x2352, 0x0600, 0x24bc, 0x24ba, 0x0640, 0x2354, + 0x0642, 0x0644, 0x2356, 0x2358, 0x02a0, 0x02a1, 0x02a2, 0x02a3, + 0x02c1, 0x02c3, 0x0a01, 0x02a4, 0x2443, 0x02a5, 0x07c1, 0x0981, + 0x0983, 0x2441, 0x2281, 0x02c5, 0x2283, 0x2285, 0x2287, 0x02c7, + 0x02c9, 0x02cb, 0x02cd, 0x02a7, 0x228b, 0x02cf, 0x228d, 0x2291, + 0x2293, 0x228f, 0x02a8, 0x02a9, 0x02aa, 0x2483, 0x0301, 0x0303, + 0x0305, 0x02ab, 0x2481, 0x0309, 0x0985, 0x0987, 0x2459, 0x0a03, + 0x0307, 0x2299, 0x229b, 0x229f, 0x0901, 0x030b, 0x22a1, 0x030d, + 0x030f, 0x0841, 0x0311, 0x0313, 0x22a3, 0x22a7, 0x09c1, 0x22a5, + 0x22a9, 0x22ab, 0x2380, 0x02ac, 0x02ad, 0x02ae, 0x0341, 0x0343, + 0x0345, 0x02af, 0x248f, 0x07c3, 0x0989, 0x098b, 0x2491, 0x0347, + 0x22ad, 0x0401, 0x0884, 0x22b1, 0x0843, 0x22b3, 0x0403, 0x22b5, + 0x0441, 0x0445, 0x22b7, 0x0443, 0x22c3, 0x22c1, 0x22c5, 0x22c7, + 0x22c9, 0x0941, 0x04c1, 0x02b1, 0x22cb, 0x04c5, 0x22cd, 0x04c3, + 0x22d1, 0x22cf, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x0541, 0x0543, + 0x0a09, 0x02b6, 0x2495, 0x0545, 0x07c5, 0x098d, 0x098f, 0x06c1, + 0x2493, 0x0845, 0x2309, 0x230b, 0x0581, 0x230d, 0x0585, 0x0991, + 0x0993, 0x230f, 0x0583, 0x2313, 0x0587, 0x0589, 0x2315, 0x058d, + 0x2317, 0x0999, 0x058b, 0x231f, 0x2381, 0x0591, 0x2321, 0x099b, + 0x058f, 0x2325, 0x2323, 0x02b9, 0x02ba, 0x02bb, 0x05c1, 0x05c3, + 0x05c5, 0x02bc, 0x24ad, 0x05c7, 0x05c9, 0x07c7, 0x0995, 0x0997, + 0x0701, 0x24ab, 0x2327, 0x05cb, 0x232b, 0x2329, 0x2341, 0x2343, + 0x2345, 0x2347, 0x05cd, 0x234b, 0x2349, 0x2382, 0x234d, 0x234f, + 0x2351, 0x24b9, 0x02bd, 0x05cf, 0x24bf, 0x0a0d, 0x2353, 0x02bf, + 0x24bd, 0x2383, 0x24bb, 0x0641, 0x2355, 0x0643, 0x0645, 0x2357, + 0x2359, 0x3101, 0x0c80, 0x2e00, 0x2446, 0x2444, 0x244a, 0x2448, + 0x0800, 0x0942, 0x0944, 0x0804, 0x2288, 0x2486, 0x2484, 0x248a, + 0x2488, 0x22ae, 0x2498, 0x2496, 0x249c, 0x249a, 0x2300, 0x0a06, + 0x2302, 0x0a04, 0x0946, 0x07ce, 0x07ca, 0x07c8, 0x07cc, 0x2447, + 0x2445, 0x244b, 0x2449, 0x0801, 0x0943, 0x0945, 0x0805, 0x2289, + 0x2487, 0x2485, 0x248b, 0x2489, 0x22af, 0x2499, 0x2497, 0x249d, + 0x249b, 0x2301, 0x0a07, 0x2303, 0x0a05, 0x0947, 0x07cf, 0x07cb, + 0x07c9, 0x07cd, 0x2450, 0x244e, 0x2454, 0x2452, 0x2451, 0x244f, + 0x2455, 0x2453, 0x2294, 0x2296, 0x2295, 0x2297, 0x2304, 0x2306, + 0x2305, 0x2307, 0x2318, 0x2319, 0x231a, 0x231b, 0x232c, 0x232d, + 0x232e, 0x232f, 0x2400, 0x24a2, 0x24a0, 0x24a6, 0x24a4, 0x24a8, + 0x24a3, 0x24a1, 0x24a7, 0x24a5, 0x24a9, 0x24b0, 0x24ae, 0x24b4, + 0x24b2, 0x24b6, 0x24b1, 0x24af, 0x24b5, 0x24b3, 0x24b7, 0x0882, + 0x0880, 0x0881, 0x0802, 0x0803, 0x229c, 0x229d, 0x0a0a, 0x0a0b, + 0x0883, 0x0b40, 0x2c8a, 0x0c81, 0x2c89, 0x2c88, 0x2540, 0x2541, + 0x2d00, 0x2e07, 0x0d00, 0x2640, 0x2641, 0x2e80, 0x0d01, 0x26c8, + 0x26c9, 0x2f00, 0x2f84, 0x0d02, 0x2f83, 0x2f82, 0x0d40, 0x26d8, + 0x26d9, 0x3186, 0x0d04, 0x2740, 0x2741, 0x3100, 0x3086, 0x0d06, + 0x3085, 0x3084, 0x0d41, 0x2840, 0x3200, 0x0d07, 0x284f, 0x2850, + 0x3280, 0x2c84, 0x2e03, 0x2857, 0x0d42, 0x2c81, 0x2c80, 0x24c0, + 0x24c1, 0x2c86, 0x2c83, 0x28c0, 0x0d43, 0x25c0, 0x25c1, 0x2940, + 0x0d44, 0x26c0, 0x26c1, 0x2e05, 0x2e02, 0x29c0, 0x0d45, 0x2f05, + 0x2f04, 0x0d80, 0x26d0, 0x26d1, 0x2f80, 0x2a40, 0x0d82, 0x26e0, + 0x26e1, 0x3080, 0x3081, 0x2ac0, 0x0d83, 0x3004, 0x3003, 0x0d81, + 0x27c0, 0x27c1, 0x3082, 0x2b40, 0x0d84, 0x2847, 0x2848, 0x3184, + 0x3181, 0x2f06, 0x0d08, 0x2f81, 0x3005, 0x0d46, 0x3083, 0x3182, + 0x0e00, 0x0e01, 0x0f40, 0x1180, 0x1182, 0x0f03, 0x0f00, 0x11c0, + 0x0f01, 0x1140, 0x1202, 0x1204, 0x0f81, 0x1240, 0x0fc0, 0x1242, + 0x0f80, 0x1244, 0x1284, 0x0f82, 0x1286, 0x1288, 0x128a, 0x12c0, + 0x1282, 0x1181, 0x1183, 0x1043, 0x1040, 0x11c1, 0x1041, 0x1141, + 0x1203, 0x1205, 0x10c1, 0x1241, 0x1000, 0x1243, 0x10c0, 0x1245, + 0x1285, 0x10c2, 0x1287, 0x1289, 0x128b, 0x12c1, 0x1283, 0x1080, + 0x1100, 0x1101, 0x1200, 0x1201, 0x1280, 0x1281, 0x1340, 0x1341, + 0x1343, 0x1342, 0x1344, 0x13c2, 0x1400, 0x13c0, 0x1440, 0x1480, + 0x14c0, 0x1540, 0x1541, 0x1740, 0x1700, 0x1741, 0x17c0, 0x1800, + 0x1802, 0x1801, 0x1840, 0x1880, 0x1900, 0x18c0, 0x18c1, 0x1901, + 0x1940, 0x1942, 0x1941, 0x1980, 0x19c0, 0x19c2, 0x19c1, 0x1c80, + 0x1cc0, 0x1dc0, 0x1f80, 0x2000, 0x2002, 0x2004, 0x2006, 0x2008, + 0x2040, 0x2080, 0x2082, 0x20c0, 0x20c1, 0x2100, 0x22b8, 0x22b9, + 0x2310, 0x2311, 0x231c, 0x231d, 0x244c, 0x2456, 0x244d, 0x2457, + 0x248c, 0x248d, 0x249e, 0x249f, 0x2500, 0x2502, 0x2504, 0x2bc0, + 0x2501, 0x2503, 0x2505, 0x2bc1, 0x2bc2, 0x2bc3, 0x2bc4, 0x2bc5, + 0x2bc6, 0x2bc7, 0x2580, 0x2582, 0x2584, 0x2bc8, 0x2581, 0x2583, + 0x2585, 0x2bc9, 0x2bca, 0x2bcb, 0x2bcc, 0x2bcd, 0x2bce, 0x2bcf, + 0x2600, 0x2602, 0x2601, 0x2603, 0x2680, 0x2682, 0x2681, 0x2683, + 0x26c2, 0x26c4, 0x26c6, 0x2c00, 0x26c3, 0x26c5, 0x26c7, 0x2c01, + 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x26ca, 0x26cc, + 0x26ce, 0x2c08, 0x26cb, 0x26cd, 0x26cf, 0x2c09, 0x2c0a, 0x2c0b, + 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x26d2, 0x26d4, 0x26d6, 0x26d3, + 0x26d5, 0x26d7, 0x26da, 0x26dc, 0x26de, 0x26db, 0x26dd, 0x26df, + 0x2700, 0x2702, 0x2701, 0x2703, 0x2780, 0x2782, 0x2781, 0x2783, + 0x2800, 0x2802, 0x2804, 0x2801, 0x2803, 0x2805, 0x2842, 0x2844, + 0x2846, 0x2849, 0x284b, 0x284d, 0x2c40, 0x284a, 0x284c, 0x284e, + 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2851, + 0x2853, 0x2855, 0x2c48, 0x2852, 0x2854, 0x2856, 0x2c49, 0x2c4a, + 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c82, 0x2e01, 0x3180, + 0x2c87, 0x2f01, 0x2f02, 0x2f03, 0x2e06, 0x3185, 0x3000, 0x3001, + 0x3002, 0x4640, 0x4641, 0x4680, 0x46c0, 0x46c2, 0x46c1, 0x4700, + 0x4740, 0x4780, 0x47c0, 0x47c2, 0x4900, 0x4940, 0x4980, 0x4982, + 0x4a00, 0x49c2, 0x4a03, 0x4a04, 0x4a40, 0x4a41, 0x4a80, 0x4a81, + 0x4ac0, 0x4ac1, 0x4bc0, 0x4bc1, 0x4b00, 0x4b01, 0x4b40, 0x4b41, + 0x4bc2, 0x4bc3, 0x4b80, 0x4b81, 0x4b82, 0x4b83, 0x4c00, 0x4c01, + 0x4c02, 0x4c03, 0x5600, 0x5440, 0x5442, 0x5444, 0x5446, 0x5448, + 0x544a, 0x544c, 0x544e, 0x5450, 0x5452, 0x5454, 0x5456, 0x5480, + 0x5482, 0x5484, 0x54c0, 0x54c1, 0x5500, 0x5501, 0x5540, 0x5541, + 0x5580, 0x5581, 0x55c0, 0x55c1, 0x5680, 0x58c0, 0x5700, 0x5702, + 0x5704, 0x5706, 0x5708, 0x570a, 0x570c, 0x570e, 0x5710, 0x5712, + 0x5714, 0x5716, 0x5740, 0x5742, 0x5744, 0x5780, 0x5781, 0x57c0, + 0x57c1, 0x5800, 0x5801, 0x5840, 0x5841, 0x5880, 0x5881, 0x5900, + 0x5901, 0x5902, 0x5903, 0x5940, 0x8f40, 0x8f42, 0x8f80, 0x8fc0, + 0x8fc1, 0x9000, 0x9001, 0x9041, 0x9040, 0x9043, 0x9080, 0x9081, + 0x90c0, +}; + +typedef enum { + UNICODE_GC_Cn, + UNICODE_GC_Lu, + UNICODE_GC_Ll, + UNICODE_GC_Lt, + UNICODE_GC_Lm, + UNICODE_GC_Lo, + UNICODE_GC_Mn, + UNICODE_GC_Mc, + UNICODE_GC_Me, + UNICODE_GC_Nd, + UNICODE_GC_Nl, + UNICODE_GC_No, + UNICODE_GC_Sm, + UNICODE_GC_Sc, + UNICODE_GC_Sk, + UNICODE_GC_So, + UNICODE_GC_Pc, + UNICODE_GC_Pd, + UNICODE_GC_Ps, + UNICODE_GC_Pe, + UNICODE_GC_Pi, + UNICODE_GC_Pf, + UNICODE_GC_Po, + UNICODE_GC_Zs, + UNICODE_GC_Zl, + UNICODE_GC_Zp, + UNICODE_GC_Cc, + UNICODE_GC_Cf, + UNICODE_GC_Cs, + UNICODE_GC_Co, + UNICODE_GC_LC, + UNICODE_GC_L, + UNICODE_GC_M, + UNICODE_GC_N, + UNICODE_GC_S, + UNICODE_GC_P, + UNICODE_GC_Z, + UNICODE_GC_C, + UNICODE_GC_COUNT, +} UnicodeGCEnum; + +static const char unicode_gc_name_table[] = + "Cn,Unassigned" "\0" + "Lu,Uppercase_Letter" "\0" + "Ll,Lowercase_Letter" "\0" + "Lt,Titlecase_Letter" "\0" + "Lm,Modifier_Letter" "\0" + "Lo,Other_Letter" "\0" + "Mn,Nonspacing_Mark" "\0" + "Mc,Spacing_Mark" "\0" + "Me,Enclosing_Mark" "\0" + "Nd,Decimal_Number,digit" "\0" + "Nl,Letter_Number" "\0" + "No,Other_Number" "\0" + "Sm,Math_Symbol" "\0" + "Sc,Currency_Symbol" "\0" + "Sk,Modifier_Symbol" "\0" + "So,Other_Symbol" "\0" + "Pc,Connector_Punctuation" "\0" + "Pd,Dash_Punctuation" "\0" + "Ps,Open_Punctuation" "\0" + "Pe,Close_Punctuation" "\0" + "Pi,Initial_Punctuation" "\0" + "Pf,Final_Punctuation" "\0" + "Po,Other_Punctuation" "\0" + "Zs,Space_Separator" "\0" + "Zl,Line_Separator" "\0" + "Zp,Paragraph_Separator" "\0" + "Cc,Control,cntrl" "\0" + "Cf,Format" "\0" + "Cs,Surrogate" "\0" + "Co,Private_Use" "\0" + "LC,Cased_Letter" "\0" + "L,Letter" "\0" + "M,Mark,Combining_Mark" "\0" + "N,Number" "\0" + "S,Symbol" "\0" + "P,Punctuation,punct" "\0" + "Z,Separator" "\0" + "C,Other" "\0" +; + +static const uint8_t unicode_gc_table[3948] = { + 0xfa, 0x18, 0x17, 0x56, 0x0d, 0x56, 0x12, 0x13, + 0x16, 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, 0x36, + 0x4c, 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e, + 0x10, 0x0e, 0xe2, 0x12, 0x12, 0x0c, 0x13, 0x0c, + 0xfa, 0x19, 0x17, 0x16, 0x6d, 0x0f, 0x16, 0x0e, + 0x0f, 0x05, 0x14, 0x0c, 0x1b, 0x0f, 0x0e, 0x0f, + 0x0c, 0x2b, 0x0e, 0x02, 0x36, 0x0e, 0x0b, 0x05, + 0x15, 0x4b, 0x16, 0xe1, 0x0f, 0x0c, 0xc1, 0xe2, + 0x10, 0x0c, 0xe2, 0x00, 0xff, 0x30, 0x02, 0xff, + 0x08, 0x02, 0xff, 0x27, 0xbf, 0x22, 0x21, 0x02, + 0x5f, 0x5f, 0x21, 0x22, 0x61, 0x02, 0x21, 0x02, + 0x41, 0x42, 0x21, 0x02, 0x21, 0x02, 0x9f, 0x7f, + 0x02, 0x5f, 0x5f, 0x21, 0x02, 0x5f, 0x3f, 0x02, + 0x05, 0x3f, 0x22, 0x65, 0x01, 0x03, 0x02, 0x01, + 0x03, 0x02, 0x01, 0x03, 0x02, 0xff, 0x08, 0x02, + 0xff, 0x0a, 0x02, 0x01, 0x03, 0x02, 0x5f, 0x21, + 0x02, 0xff, 0x32, 0xa2, 0x21, 0x02, 0x21, 0x22, + 0x5f, 0x41, 0x02, 0xff, 0x00, 0xe2, 0x3c, 0x05, + 0xe2, 0x13, 0xe4, 0x0a, 0x6e, 0xe4, 0x04, 0xee, + 0x06, 0x84, 0xce, 0x04, 0x0e, 0x04, 0xee, 0x09, + 0xe6, 0x68, 0x7f, 0x04, 0x0e, 0x3f, 0x20, 0x04, + 0x42, 0x16, 0x01, 0x60, 0x2e, 0x01, 0x16, 0x41, + 0x00, 0x01, 0x00, 0x21, 0x02, 0xe1, 0x09, 0x00, + 0xe1, 0x01, 0xe2, 0x1b, 0x3f, 0x02, 0x41, 0x42, + 0xff, 0x10, 0x62, 0x3f, 0x0c, 0x5f, 0x3f, 0x02, + 0xe1, 0x2b, 0xe2, 0x28, 0xff, 0x1a, 0x0f, 0x86, + 0x28, 0xff, 0x2f, 0xff, 0x06, 0x02, 0xff, 0x58, + 0x00, 0xe1, 0x1e, 0x20, 0x04, 0xb6, 0xe2, 0x21, + 0x16, 0x11, 0x20, 0x2f, 0x0d, 0x00, 0xe6, 0x25, + 0x11, 0x06, 0x16, 0x26, 0x16, 0x26, 0x16, 0x06, + 0xe0, 0x00, 0xe5, 0x13, 0x60, 0x65, 0x36, 0xe0, + 0x03, 0xbb, 0x4c, 0x36, 0x0d, 0x36, 0x2f, 0xe6, + 0x03, 0x16, 0x1b, 0x56, 0xe5, 0x18, 0x04, 0xe5, + 0x02, 0xe6, 0x0d, 0xe9, 0x02, 0x76, 0x25, 0x06, + 0xe5, 0x5b, 0x16, 0x05, 0xc6, 0x1b, 0x0f, 0xa6, + 0x24, 0x26, 0x0f, 0x66, 0x25, 0xe9, 0x02, 0x45, + 0x2f, 0x05, 0xf6, 0x06, 0x00, 0x1b, 0x05, 0x06, + 0xe5, 0x16, 0xe6, 0x13, 0x20, 0xe5, 0x51, 0xe6, + 0x03, 0x05, 0xe0, 0x06, 0xe9, 0x02, 0xe5, 0x19, + 0xe6, 0x01, 0x24, 0x0f, 0x56, 0x04, 0x20, 0x06, + 0x2d, 0xe5, 0x0e, 0x66, 0x04, 0xe6, 0x01, 0x04, + 0x46, 0x04, 0x86, 0x20, 0xf6, 0x07, 0x00, 0xe5, + 0x11, 0x46, 0x20, 0x16, 0x00, 0xe5, 0x03, 0x80, + 0xe5, 0x10, 0x0e, 0xa5, 0x00, 0x3b, 0xa0, 0xe6, + 0x00, 0xe5, 0x21, 0x04, 0xe6, 0x10, 0x1b, 0xe6, + 0x18, 0x07, 0xe5, 0x2e, 0x06, 0x07, 0x06, 0x05, + 0x47, 0xe6, 0x00, 0x67, 0x06, 0x27, 0x05, 0xc6, + 0xe5, 0x02, 0x26, 0x36, 0xe9, 0x02, 0x16, 0x04, + 0xe5, 0x07, 0x06, 0x27, 0x00, 0xe5, 0x00, 0x20, + 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x05, + 0x40, 0x65, 0x20, 0x06, 0x05, 0x47, 0x66, 0x20, + 0x27, 0x20, 0x27, 0x06, 0x05, 0xe0, 0x00, 0x07, + 0x60, 0x25, 0x00, 0x45, 0x26, 0x20, 0xe9, 0x02, + 0x25, 0x2d, 0xab, 0x0f, 0x0d, 0x05, 0x16, 0x06, + 0x20, 0x26, 0x07, 0x00, 0xa5, 0x60, 0x25, 0x20, + 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, 0x25, + 0x00, 0x25, 0x20, 0x06, 0x00, 0x47, 0x26, 0x60, + 0x26, 0x20, 0x46, 0x40, 0x06, 0xc0, 0x65, 0x00, + 0x05, 0xc0, 0xe9, 0x02, 0x26, 0x45, 0x06, 0x16, + 0xe0, 0x02, 0x26, 0x07, 0x00, 0xe5, 0x01, 0x00, + 0x45, 0x00, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, + 0x00, 0x85, 0x20, 0x06, 0x05, 0x47, 0x86, 0x00, + 0x26, 0x07, 0x00, 0x27, 0x06, 0x20, 0x05, 0xe0, + 0x07, 0x25, 0x26, 0x20, 0xe9, 0x02, 0x16, 0x0d, + 0xc0, 0x05, 0xa6, 0x00, 0x06, 0x27, 0x00, 0xe5, + 0x00, 0x20, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, + 0x00, 0x25, 0x00, 0x85, 0x20, 0x06, 0x05, 0x07, + 0x06, 0x07, 0x66, 0x20, 0x27, 0x20, 0x27, 0x06, + 0xc0, 0x26, 0x07, 0x60, 0x25, 0x00, 0x45, 0x26, + 0x20, 0xe9, 0x02, 0x0f, 0x05, 0xab, 0xe0, 0x02, + 0x06, 0x05, 0x00, 0xa5, 0x40, 0x45, 0x00, 0x65, + 0x40, 0x25, 0x00, 0x05, 0x00, 0x25, 0x40, 0x25, + 0x40, 0x45, 0x40, 0xe5, 0x04, 0x60, 0x27, 0x06, + 0x27, 0x40, 0x47, 0x00, 0x47, 0x06, 0x20, 0x05, + 0xa0, 0x07, 0xe0, 0x06, 0xe9, 0x02, 0x4b, 0xaf, + 0x0d, 0x0f, 0x80, 0x06, 0x47, 0x06, 0xe5, 0x00, + 0x00, 0x45, 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x08, + 0x20, 0x06, 0x05, 0x46, 0x67, 0x00, 0x46, 0x00, + 0x66, 0xc0, 0x26, 0x00, 0x45, 0x20, 0x05, 0x20, + 0x25, 0x26, 0x20, 0xe9, 0x02, 0xc0, 0x16, 0xcb, + 0x0f, 0x05, 0x06, 0x27, 0x16, 0xe5, 0x00, 0x00, + 0x45, 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x02, 0x00, + 0x85, 0x20, 0x06, 0x05, 0x07, 0x06, 0x87, 0x00, + 0x06, 0x27, 0x00, 0x27, 0x26, 0xc0, 0x27, 0xa0, + 0x25, 0x00, 0x25, 0x26, 0x20, 0xe9, 0x02, 0x00, + 0x25, 0x07, 0xe0, 0x04, 0x26, 0x27, 0xe5, 0x01, + 0x00, 0x45, 0x00, 0xe5, 0x21, 0x26, 0x05, 0x47, + 0x66, 0x00, 0x47, 0x00, 0x47, 0x06, 0x05, 0x0f, + 0x60, 0x45, 0x07, 0xcb, 0x45, 0x26, 0x20, 0xe9, + 0x02, 0xeb, 0x01, 0x0f, 0xa5, 0x00, 0x06, 0x27, + 0x00, 0xe5, 0x0a, 0x40, 0xe5, 0x10, 0x00, 0xe5, + 0x01, 0x00, 0x05, 0x20, 0xc5, 0x40, 0x06, 0x60, + 0x47, 0x46, 0x00, 0x06, 0x00, 0xe7, 0x00, 0xa0, + 0xe9, 0x02, 0x20, 0x27, 0x16, 0xe0, 0x04, 0xe5, + 0x28, 0x06, 0x25, 0xc6, 0x60, 0x0d, 0xa5, 0x04, + 0xe6, 0x00, 0x16, 0xe9, 0x02, 0x36, 0xe0, 0x1d, + 0x25, 0x00, 0x05, 0x00, 0x85, 0x00, 0xe5, 0x10, + 0x00, 0x05, 0x00, 0xe5, 0x02, 0x06, 0x25, 0xe6, + 0x01, 0x05, 0x20, 0x85, 0x00, 0x04, 0x00, 0xc6, + 0x00, 0xe9, 0x02, 0x20, 0x65, 0xe0, 0x18, 0x05, + 0x4f, 0xf6, 0x07, 0x0f, 0x16, 0x4f, 0x26, 0xaf, + 0xe9, 0x02, 0xeb, 0x02, 0x0f, 0x06, 0x0f, 0x06, + 0x0f, 0x06, 0x12, 0x13, 0x12, 0x13, 0x27, 0xe5, + 0x00, 0x00, 0xe5, 0x1c, 0x60, 0xe6, 0x06, 0x07, + 0x86, 0x16, 0x26, 0x85, 0xe6, 0x03, 0x00, 0xe6, + 0x1c, 0x00, 0xef, 0x00, 0x06, 0xaf, 0x00, 0x2f, + 0x96, 0x6f, 0x36, 0xe0, 0x1d, 0xe5, 0x23, 0x27, + 0x66, 0x07, 0xa6, 0x07, 0x26, 0x27, 0x26, 0x05, + 0xe9, 0x02, 0xb6, 0xa5, 0x27, 0x26, 0x65, 0x46, + 0x05, 0x47, 0x25, 0xc7, 0x45, 0x66, 0xe5, 0x05, + 0x06, 0x27, 0x26, 0xa7, 0x06, 0x05, 0x07, 0xe9, + 0x02, 0x47, 0x06, 0x2f, 0xe1, 0x1e, 0x00, 0x01, + 0x80, 0x01, 0x20, 0xe2, 0x23, 0x16, 0x04, 0x42, + 0xe5, 0x80, 0xc1, 0x00, 0x65, 0x20, 0xc5, 0x00, + 0x05, 0x00, 0x65, 0x20, 0xe5, 0x21, 0x00, 0x65, + 0x20, 0xe5, 0x19, 0x00, 0x65, 0x20, 0xc5, 0x00, + 0x05, 0x00, 0x65, 0x20, 0xe5, 0x07, 0x00, 0xe5, + 0x31, 0x00, 0x65, 0x20, 0xe5, 0x3b, 0x20, 0x46, + 0xf6, 0x01, 0xeb, 0x0c, 0x40, 0xe5, 0x08, 0xef, + 0x02, 0xa0, 0xe1, 0x4e, 0x20, 0xa2, 0x20, 0x11, + 0xe5, 0x81, 0xe4, 0x0f, 0x16, 0xe5, 0x09, 0x17, + 0xe5, 0x12, 0x12, 0x13, 0x40, 0xe5, 0x43, 0x56, + 0x4a, 0xe5, 0x00, 0xc0, 0xe5, 0x0a, 0x46, 0x07, + 0xe0, 0x01, 0xe5, 0x0b, 0x26, 0x07, 0x36, 0xe0, + 0x01, 0xe5, 0x0a, 0x26, 0xe0, 0x04, 0xe5, 0x05, + 0x00, 0x45, 0x00, 0x26, 0xe0, 0x04, 0xe5, 0x2c, + 0x26, 0x07, 0xc6, 0xe7, 0x00, 0x06, 0x27, 0xe6, + 0x03, 0x56, 0x04, 0x56, 0x0d, 0x05, 0x06, 0x20, + 0xe9, 0x02, 0xa0, 0xeb, 0x02, 0xa0, 0xb6, 0x11, + 0x76, 0x46, 0x1b, 0x06, 0xe9, 0x02, 0xa0, 0xe5, + 0x1b, 0x04, 0xe5, 0x2d, 0xc0, 0x85, 0x26, 0xe5, + 0x1a, 0x06, 0x05, 0x80, 0xe5, 0x3e, 0xe0, 0x02, + 0xe5, 0x17, 0x00, 0x46, 0x67, 0x26, 0x47, 0x60, + 0x27, 0x06, 0xa7, 0x46, 0x60, 0x0f, 0x40, 0x36, + 0xe9, 0x02, 0xe5, 0x16, 0x20, 0x85, 0xe0, 0x03, + 0xe5, 0x24, 0x60, 0xe5, 0x12, 0xa0, 0xe9, 0x02, + 0x0b, 0x40, 0xef, 0x1a, 0xe5, 0x0f, 0x26, 0x27, + 0x06, 0x20, 0x36, 0xe5, 0x2d, 0x07, 0x06, 0x07, + 0xc6, 0x00, 0x06, 0x07, 0x06, 0x27, 0xe6, 0x00, + 0xa7, 0xe6, 0x02, 0x20, 0x06, 0xe9, 0x02, 0xa0, + 0xe9, 0x02, 0xa0, 0xd6, 0x04, 0xb6, 0x20, 0xe6, + 0x06, 0x08, 0xe6, 0x08, 0xe0, 0x29, 0x66, 0x07, + 0xe5, 0x27, 0x06, 0x07, 0x86, 0x07, 0x06, 0x87, + 0x06, 0x27, 0xe5, 0x00, 0x40, 0xe9, 0x02, 0xd6, + 0xef, 0x02, 0xe6, 0x01, 0xef, 0x01, 0x36, 0x00, + 0x26, 0x07, 0xe5, 0x16, 0x07, 0x66, 0x27, 0x26, + 0x07, 0x46, 0x25, 0xe9, 0x02, 0xe5, 0x24, 0x06, + 0x07, 0x26, 0x47, 0x06, 0x07, 0x46, 0x27, 0xe0, + 0x00, 0x76, 0xe5, 0x1c, 0xe7, 0x00, 0xe6, 0x00, + 0x27, 0x26, 0x40, 0x96, 0xe9, 0x02, 0x40, 0x45, + 0xe9, 0x02, 0xe5, 0x16, 0xa4, 0x36, 0xe2, 0x01, + 0xc0, 0xe1, 0x23, 0x20, 0x41, 0xf6, 0x00, 0xe0, + 0x00, 0x46, 0x16, 0xe6, 0x05, 0x07, 0xc6, 0x65, + 0x06, 0xa5, 0x06, 0x25, 0x07, 0x26, 0x05, 0x80, + 0xe2, 0x24, 0xe4, 0x37, 0xe2, 0x05, 0x04, 0xe2, + 0x1a, 0xe4, 0x1d, 0xe6, 0x38, 0xff, 0x80, 0x0e, + 0xe2, 0x00, 0xff, 0x5a, 0xe2, 0x00, 0xe1, 0x00, + 0xa2, 0x20, 0xa1, 0x20, 0xe2, 0x00, 0xe1, 0x00, + 0xe2, 0x00, 0xe1, 0x00, 0xa2, 0x20, 0xa1, 0x20, + 0xe2, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x3f, 0xc2, 0xe1, 0x00, 0xe2, 0x06, 0x20, + 0xe2, 0x00, 0xe3, 0x00, 0xe2, 0x00, 0xe3, 0x00, + 0xe2, 0x00, 0xe3, 0x00, 0x82, 0x00, 0x22, 0x61, + 0x03, 0x0e, 0x02, 0x4e, 0x42, 0x00, 0x22, 0x61, + 0x03, 0x4e, 0x62, 0x20, 0x22, 0x61, 0x00, 0x4e, + 0xe2, 0x00, 0x81, 0x4e, 0x20, 0x42, 0x00, 0x22, + 0x61, 0x03, 0x2e, 0x00, 0xf7, 0x03, 0x9b, 0xb1, + 0x36, 0x14, 0x15, 0x12, 0x34, 0x15, 0x12, 0x14, + 0xf6, 0x00, 0x18, 0x19, 0x9b, 0x17, 0xf6, 0x01, + 0x14, 0x15, 0x76, 0x30, 0x56, 0x0c, 0x12, 0x13, + 0xf6, 0x03, 0x0c, 0x16, 0x10, 0xf6, 0x02, 0x17, + 0x9b, 0x00, 0xfb, 0x02, 0x0b, 0x04, 0x20, 0xab, + 0x4c, 0x12, 0x13, 0x04, 0xeb, 0x02, 0x4c, 0x12, + 0x13, 0x00, 0xe4, 0x05, 0x40, 0xed, 0x19, 0xe0, + 0x07, 0xe6, 0x05, 0x68, 0x06, 0x48, 0xe6, 0x04, + 0xe0, 0x07, 0x2f, 0x01, 0x6f, 0x01, 0x2f, 0x02, + 0x41, 0x22, 0x41, 0x02, 0x0f, 0x01, 0x2f, 0x0c, + 0x81, 0xaf, 0x01, 0x0f, 0x01, 0x0f, 0x01, 0x0f, + 0x61, 0x0f, 0x02, 0x61, 0x02, 0x65, 0x02, 0x2f, + 0x22, 0x21, 0x8c, 0x3f, 0x42, 0x0f, 0x0c, 0x2f, + 0x02, 0x0f, 0xeb, 0x08, 0xea, 0x1b, 0x3f, 0x6a, + 0x0b, 0x2f, 0x60, 0x8c, 0x8f, 0x2c, 0x6f, 0x0c, + 0x2f, 0x0c, 0x2f, 0x0c, 0xcf, 0x0c, 0xef, 0x17, + 0x2c, 0x2f, 0x0c, 0x0f, 0x0c, 0xef, 0x17, 0xec, + 0x80, 0x84, 0xef, 0x00, 0x12, 0x13, 0x12, 0x13, + 0xef, 0x0c, 0x2c, 0xcf, 0x12, 0x13, 0xef, 0x49, + 0x0c, 0xef, 0x16, 0xec, 0x11, 0xef, 0x20, 0xac, + 0xef, 0x3d, 0xe0, 0x11, 0xef, 0x03, 0xe0, 0x0d, + 0xeb, 0x34, 0xef, 0x46, 0xeb, 0x0e, 0xef, 0x80, + 0x2f, 0x0c, 0xef, 0x01, 0x0c, 0xef, 0x2e, 0xec, + 0x00, 0xef, 0x67, 0x0c, 0xef, 0x80, 0x70, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0xeb, 0x16, 0xef, + 0x24, 0x8c, 0x12, 0x13, 0xec, 0x17, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0xec, 0x08, 0xef, 0x80, 0x78, 0xec, 0x7b, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0xec, 0x37, 0x12, + 0x13, 0x12, 0x13, 0xec, 0x18, 0x12, 0x13, 0xec, + 0x80, 0x7a, 0xef, 0x28, 0xec, 0x0d, 0x2f, 0xac, + 0xef, 0x1f, 0x20, 0xef, 0x18, 0x00, 0xef, 0x61, + 0xe1, 0x28, 0xe2, 0x28, 0x5f, 0x21, 0x22, 0xdf, + 0x41, 0x02, 0x3f, 0x02, 0x3f, 0x82, 0x24, 0x41, + 0x02, 0xff, 0x5a, 0x02, 0xaf, 0x7f, 0x46, 0x3f, + 0x80, 0x76, 0x0b, 0x36, 0xe2, 0x1e, 0x00, 0x02, + 0x80, 0x02, 0x20, 0xe5, 0x30, 0xc0, 0x04, 0x16, + 0xe0, 0x06, 0x06, 0xe5, 0x0f, 0xe0, 0x01, 0xc5, + 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, + 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xe6, + 0x18, 0x36, 0x14, 0x15, 0x14, 0x15, 0x56, 0x14, + 0x15, 0x16, 0x14, 0x15, 0xf6, 0x01, 0x11, 0x36, + 0x11, 0x16, 0x14, 0x15, 0x36, 0x14, 0x15, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x96, + 0x04, 0xf6, 0x02, 0x31, 0x76, 0x11, 0x16, 0x12, + 0xf6, 0x05, 0x2f, 0x56, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x11, 0xe0, 0x1a, 0xef, + 0x12, 0x00, 0xef, 0x51, 0xe0, 0x04, 0xef, 0x80, + 0x4e, 0xe0, 0x12, 0xef, 0x04, 0x60, 0x17, 0x56, + 0x0f, 0x04, 0x05, 0x0a, 0x12, 0x13, 0x12, 0x13, + 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x2f, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x11, + 0x12, 0x33, 0x0f, 0xea, 0x01, 0x66, 0x27, 0x11, + 0x84, 0x2f, 0x4a, 0x04, 0x05, 0x16, 0x2f, 0x00, + 0xe5, 0x4e, 0x20, 0x26, 0x2e, 0x24, 0x05, 0x11, + 0xe5, 0x52, 0x16, 0x44, 0x05, 0x80, 0xe5, 0x23, + 0x00, 0xe5, 0x56, 0x00, 0x2f, 0x6b, 0xef, 0x02, + 0xe5, 0x18, 0xef, 0x1c, 0xe0, 0x04, 0xe5, 0x08, + 0xef, 0x17, 0x00, 0xeb, 0x02, 0xef, 0x16, 0xeb, + 0x00, 0x0f, 0xeb, 0x07, 0xef, 0x18, 0xeb, 0x02, + 0xef, 0x1f, 0xeb, 0x07, 0xef, 0x80, 0xb8, 0xe5, + 0x99, 0x38, 0xef, 0x38, 0xe5, 0xc0, 0x11, 0x8d, + 0x04, 0xe5, 0x83, 0xef, 0x40, 0xef, 0x2f, 0xe0, + 0x01, 0xe5, 0x20, 0xa4, 0x36, 0xe5, 0x80, 0x84, + 0x04, 0x56, 0xe5, 0x08, 0xe9, 0x02, 0x25, 0xe0, + 0x0c, 0xff, 0x26, 0x05, 0x06, 0x48, 0x16, 0xe6, + 0x02, 0x16, 0x04, 0xff, 0x14, 0x24, 0x26, 0xe5, + 0x3e, 0xea, 0x02, 0x26, 0xb6, 0xe0, 0x00, 0xee, + 0x0f, 0xe4, 0x01, 0x2e, 0xff, 0x06, 0x22, 0xff, + 0x36, 0x04, 0xe2, 0x00, 0x9f, 0xff, 0x02, 0x04, + 0x2e, 0x7f, 0x05, 0x7f, 0x22, 0xff, 0x0d, 0x61, + 0x02, 0x81, 0x02, 0xff, 0x07, 0x41, 0x02, 0x3f, + 0x80, 0x3f, 0x00, 0x02, 0x00, 0x02, 0x7f, 0xe0, + 0x10, 0x44, 0x3f, 0x05, 0x24, 0x02, 0xc5, 0x06, + 0x45, 0x06, 0x65, 0x06, 0xe5, 0x0f, 0x27, 0x26, + 0x07, 0x6f, 0x06, 0x40, 0xab, 0x2f, 0x0d, 0x0f, + 0xa0, 0xe5, 0x2c, 0x76, 0xe0, 0x00, 0x27, 0xe5, + 0x2a, 0xe7, 0x08, 0x26, 0xe0, 0x00, 0x36, 0xe9, + 0x02, 0xa0, 0xe6, 0x0a, 0xa5, 0x56, 0x05, 0x16, + 0x25, 0x06, 0xe9, 0x02, 0xe5, 0x14, 0xe6, 0x00, + 0x36, 0xe5, 0x0f, 0xe6, 0x03, 0x27, 0xe0, 0x03, + 0x16, 0xe5, 0x15, 0x40, 0x46, 0x07, 0xe5, 0x27, + 0x06, 0x27, 0x66, 0x27, 0x26, 0x47, 0xf6, 0x05, + 0x00, 0x04, 0xe9, 0x02, 0x60, 0x36, 0x85, 0x06, + 0x04, 0xe5, 0x01, 0xe9, 0x02, 0x85, 0x00, 0xe5, + 0x21, 0xa6, 0x27, 0x26, 0x27, 0x26, 0xe0, 0x01, + 0x45, 0x06, 0xe5, 0x00, 0x06, 0x07, 0x20, 0xe9, + 0x02, 0x20, 0x76, 0xe5, 0x08, 0x04, 0xa5, 0x4f, + 0x05, 0x07, 0x06, 0x07, 0xe5, 0x2a, 0x06, 0x05, + 0x46, 0x25, 0x26, 0x85, 0x26, 0x05, 0x06, 0x05, + 0xe0, 0x10, 0x25, 0x04, 0x36, 0xe5, 0x03, 0x07, + 0x26, 0x27, 0x36, 0x05, 0x24, 0x07, 0x06, 0xe0, + 0x02, 0xa5, 0x20, 0xa5, 0x20, 0xa5, 0xe0, 0x01, + 0xc5, 0x00, 0xc5, 0x00, 0xe2, 0x23, 0x0e, 0x64, + 0xe2, 0x01, 0x04, 0x2e, 0x60, 0xe2, 0x48, 0xe5, + 0x1b, 0x27, 0x06, 0x27, 0x06, 0x27, 0x16, 0x07, + 0x06, 0x20, 0xe9, 0x02, 0xa0, 0xe5, 0xab, 0x1c, + 0xe0, 0x04, 0xe5, 0x0f, 0x60, 0xe5, 0x29, 0x60, + 0xfc, 0x87, 0x78, 0xfd, 0x98, 0x78, 0xe5, 0x80, + 0xe6, 0x20, 0xe5, 0x62, 0xe0, 0x1e, 0xc2, 0xe0, + 0x04, 0x82, 0x80, 0x05, 0x06, 0xe5, 0x02, 0x0c, + 0xe5, 0x05, 0x00, 0x85, 0x00, 0x05, 0x00, 0x25, + 0x00, 0x25, 0x00, 0xe5, 0x64, 0xee, 0x09, 0xe0, + 0x08, 0xe5, 0x80, 0xe3, 0x13, 0x12, 0xef, 0x08, + 0xe5, 0x38, 0x20, 0xe5, 0x2e, 0xc0, 0x0f, 0xe0, + 0x18, 0xe5, 0x04, 0x0d, 0x4f, 0xe6, 0x08, 0xd6, + 0x12, 0x13, 0x16, 0xa0, 0xe6, 0x08, 0x16, 0x31, + 0x30, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, + 0x13, 0x36, 0x12, 0x13, 0x76, 0x50, 0x56, 0x00, + 0x76, 0x11, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, + 0x56, 0x0c, 0x11, 0x4c, 0x00, 0x16, 0x0d, 0x36, + 0x60, 0x85, 0x00, 0xe5, 0x7f, 0x20, 0x1b, 0x00, + 0x56, 0x0d, 0x56, 0x12, 0x13, 0x16, 0x0c, 0x16, + 0x11, 0x36, 0xe9, 0x02, 0x36, 0x4c, 0x36, 0xe1, + 0x12, 0x12, 0x16, 0x13, 0x0e, 0x10, 0x0e, 0xe2, + 0x12, 0x12, 0x0c, 0x13, 0x0c, 0x12, 0x13, 0x16, + 0x12, 0x13, 0x36, 0xe5, 0x02, 0x04, 0xe5, 0x25, + 0x24, 0xe5, 0x17, 0x40, 0xa5, 0x20, 0xa5, 0x20, + 0xa5, 0x20, 0x45, 0x40, 0x2d, 0x0c, 0x0e, 0x0f, + 0x2d, 0x00, 0x0f, 0x6c, 0x2f, 0xe0, 0x02, 0x5b, + 0x2f, 0x20, 0xe5, 0x04, 0x00, 0xe5, 0x12, 0x00, + 0xe5, 0x0b, 0x00, 0x25, 0x00, 0xe5, 0x07, 0x20, + 0xe5, 0x06, 0xe0, 0x1a, 0xe5, 0x73, 0x80, 0x56, + 0x60, 0xeb, 0x25, 0x40, 0xef, 0x01, 0xea, 0x2d, + 0x6b, 0xef, 0x09, 0x2b, 0x4f, 0x00, 0xef, 0x05, + 0x40, 0x0f, 0xe0, 0x27, 0xef, 0x25, 0x06, 0xe0, + 0x7a, 0xe5, 0x15, 0x40, 0xe5, 0x29, 0xe0, 0x07, + 0x06, 0xeb, 0x13, 0x60, 0xe5, 0x18, 0x6b, 0xe0, + 0x01, 0xe5, 0x0c, 0x0a, 0xe5, 0x00, 0x0a, 0x80, + 0xe5, 0x1e, 0x86, 0x80, 0xe5, 0x16, 0x00, 0x16, + 0xe5, 0x1c, 0x60, 0xe5, 0x00, 0x16, 0x8a, 0xe0, + 0x22, 0xe1, 0x20, 0xe2, 0x20, 0xe5, 0x46, 0x20, + 0xe9, 0x02, 0xa0, 0xe1, 0x1c, 0x60, 0xe2, 0x1c, + 0x60, 0xe5, 0x20, 0xe0, 0x00, 0xe5, 0x2c, 0xe0, + 0x03, 0x16, 0xe1, 0x03, 0x00, 0xe1, 0x07, 0x00, + 0xc1, 0x00, 0x21, 0x00, 0xe2, 0x03, 0x00, 0xe2, + 0x07, 0x00, 0xc2, 0x00, 0x22, 0xe0, 0x3b, 0xe5, + 0x80, 0xaf, 0xe0, 0x01, 0xe5, 0x0e, 0xe0, 0x02, + 0xe5, 0x00, 0xe0, 0x10, 0xa4, 0x00, 0xe4, 0x22, + 0x00, 0xe4, 0x01, 0xe0, 0x3d, 0xa5, 0x20, 0x05, + 0x00, 0xe5, 0x24, 0x00, 0x25, 0x40, 0x05, 0x20, + 0xe5, 0x0f, 0x00, 0x16, 0xeb, 0x00, 0xe5, 0x0f, + 0x2f, 0xcb, 0xe5, 0x17, 0xe0, 0x00, 0xeb, 0x01, + 0xe0, 0x28, 0xe5, 0x0b, 0x00, 0x25, 0x80, 0x8b, + 0xe5, 0x0e, 0xab, 0x40, 0x16, 0xe5, 0x12, 0x80, + 0x16, 0xe0, 0x38, 0xe5, 0x30, 0x60, 0x2b, 0x25, + 0xeb, 0x08, 0x20, 0xeb, 0x26, 0x05, 0x46, 0x00, + 0x26, 0x80, 0x66, 0x65, 0x00, 0x45, 0x00, 0xe5, + 0x15, 0x20, 0x46, 0x60, 0x06, 0xeb, 0x01, 0xc0, + 0xf6, 0x01, 0xc0, 0xe5, 0x15, 0x2b, 0x16, 0xe5, + 0x15, 0x4b, 0xe0, 0x18, 0xe5, 0x00, 0x0f, 0xe5, + 0x14, 0x26, 0x60, 0x8b, 0xd6, 0xe0, 0x01, 0xe5, + 0x2e, 0x40, 0xd6, 0xe5, 0x0e, 0x20, 0xeb, 0x00, + 0xe5, 0x0b, 0x80, 0xeb, 0x00, 0xe5, 0x0a, 0xc0, + 0x76, 0xe0, 0x04, 0xcb, 0xe0, 0x48, 0xe5, 0x41, + 0xe0, 0x2f, 0xe1, 0x2b, 0xe0, 0x05, 0xe2, 0x2b, + 0xc0, 0xab, 0xe5, 0x1c, 0x66, 0xe0, 0x00, 0xe9, + 0x02, 0xe0, 0x80, 0x9e, 0xeb, 0x17, 0x00, 0xe5, + 0x22, 0x00, 0x26, 0x11, 0x20, 0x25, 0xe0, 0x43, + 0x46, 0xe5, 0x15, 0xeb, 0x02, 0x05, 0xe0, 0x00, + 0xe5, 0x0e, 0xe6, 0x03, 0x6b, 0x96, 0xe0, 0x0e, + 0xe5, 0x0a, 0x66, 0x76, 0xe0, 0x1e, 0xe5, 0x0d, + 0xcb, 0xe0, 0x0c, 0xe5, 0x0f, 0xe0, 0x01, 0x07, + 0x06, 0x07, 0xe5, 0x2d, 0xe6, 0x07, 0xd6, 0x60, + 0xeb, 0x0c, 0xe9, 0x02, 0x06, 0x25, 0x26, 0x05, + 0xe0, 0x01, 0x46, 0x07, 0xe5, 0x25, 0x47, 0x66, + 0x27, 0x26, 0x36, 0x1b, 0x76, 0x06, 0xe0, 0x02, + 0x1b, 0x20, 0xe5, 0x11, 0xc0, 0xe9, 0x02, 0xa0, + 0x46, 0xe5, 0x1c, 0x86, 0x07, 0xe6, 0x00, 0x00, + 0xe9, 0x02, 0x76, 0x05, 0x27, 0x05, 0xe0, 0x00, + 0xe5, 0x1b, 0x06, 0x36, 0x05, 0xe0, 0x01, 0x26, + 0x07, 0xe5, 0x28, 0x47, 0xe6, 0x01, 0x27, 0x65, + 0x76, 0x66, 0x16, 0x07, 0x06, 0xe9, 0x02, 0x05, + 0x16, 0x05, 0x56, 0x00, 0xeb, 0x0c, 0xe0, 0x03, + 0xe5, 0x0a, 0x00, 0xe5, 0x11, 0x47, 0x46, 0x27, + 0x06, 0x07, 0x26, 0xb6, 0x06, 0x25, 0x06, 0xe0, + 0x36, 0xc5, 0x00, 0x05, 0x00, 0x65, 0x00, 0xe5, + 0x07, 0x00, 0xe5, 0x02, 0x16, 0xa0, 0xe5, 0x27, + 0x06, 0x47, 0xe6, 0x00, 0x80, 0xe9, 0x02, 0xa0, + 0x26, 0x27, 0x00, 0xe5, 0x00, 0x20, 0x25, 0x20, + 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, 0x85, + 0x00, 0x26, 0x05, 0x27, 0x06, 0x67, 0x20, 0x27, + 0x20, 0x47, 0x20, 0x05, 0xa0, 0x07, 0x80, 0x85, + 0x27, 0x20, 0xc6, 0x40, 0x86, 0xe0, 0x80, 0x03, + 0xe5, 0x2d, 0x47, 0xe6, 0x00, 0x27, 0x46, 0x07, + 0x06, 0x65, 0x96, 0xe9, 0x02, 0x36, 0x00, 0x16, + 0x06, 0x45, 0xe0, 0x16, 0xe5, 0x28, 0x47, 0xa6, + 0x07, 0x06, 0x67, 0x26, 0x07, 0x26, 0x25, 0x16, + 0x05, 0xe0, 0x00, 0xe9, 0x02, 0xe0, 0x80, 0x1e, + 0xe5, 0x27, 0x47, 0x66, 0x20, 0x67, 0x26, 0x07, + 0x26, 0xf6, 0x0f, 0x65, 0x26, 0xe0, 0x1a, 0xe5, + 0x28, 0x47, 0xe6, 0x00, 0x27, 0x06, 0x07, 0x26, + 0x56, 0x05, 0xe0, 0x03, 0xe9, 0x02, 0xa0, 0xf6, + 0x05, 0xe0, 0x0b, 0xe5, 0x23, 0x06, 0x07, 0x06, + 0x27, 0xa6, 0x07, 0x06, 0x05, 0x16, 0xa0, 0xe9, + 0x02, 0xe0, 0x2e, 0xe5, 0x13, 0x20, 0x46, 0x27, + 0x66, 0x07, 0x86, 0x60, 0xe9, 0x02, 0x2b, 0x56, + 0x0f, 0xc5, 0xe0, 0x80, 0x31, 0xe5, 0x24, 0x47, + 0xe6, 0x01, 0x07, 0x26, 0x16, 0xe0, 0x5c, 0xe1, + 0x18, 0xe2, 0x18, 0xe9, 0x02, 0xeb, 0x01, 0xe0, + 0x04, 0xe5, 0x00, 0x20, 0x05, 0x20, 0xe5, 0x00, + 0x00, 0x25, 0x00, 0xe5, 0x10, 0xa7, 0x00, 0x27, + 0x20, 0x26, 0x07, 0x06, 0x05, 0x07, 0x05, 0x07, + 0x06, 0x56, 0xe0, 0x01, 0xe9, 0x02, 0xe0, 0x3e, + 0xe5, 0x00, 0x20, 0xe5, 0x1f, 0x47, 0x66, 0x20, + 0x26, 0x67, 0x06, 0x05, 0x16, 0x05, 0x07, 0xe0, + 0x13, 0x05, 0xe6, 0x02, 0xe5, 0x20, 0xa6, 0x07, + 0x05, 0x66, 0xf6, 0x00, 0x06, 0xe0, 0x00, 0x05, + 0xa6, 0x27, 0x46, 0xe5, 0x26, 0xe6, 0x05, 0x07, + 0x26, 0x56, 0x05, 0x96, 0xe0, 0x05, 0xe5, 0x41, + 0xc0, 0xf6, 0x02, 0xe0, 0x80, 0x6e, 0xe5, 0x01, + 0x00, 0xe5, 0x1d, 0x07, 0xc6, 0x00, 0xa6, 0x07, + 0x06, 0x05, 0x96, 0xe0, 0x02, 0xe9, 0x02, 0xeb, + 0x0b, 0x40, 0x36, 0xe5, 0x16, 0x20, 0xe6, 0x0e, + 0x00, 0x07, 0xc6, 0x07, 0x26, 0x07, 0x26, 0xe0, + 0x41, 0xc5, 0x00, 0x25, 0x00, 0xe5, 0x1e, 0xa6, + 0x40, 0x06, 0x00, 0x26, 0x00, 0xc6, 0x05, 0x06, + 0xe0, 0x00, 0xe9, 0x02, 0xa0, 0xa5, 0x00, 0x25, + 0x00, 0xe5, 0x18, 0x87, 0x00, 0x26, 0x00, 0x27, + 0x06, 0x07, 0x06, 0x05, 0xc0, 0xe9, 0x02, 0xe0, + 0x80, 0xae, 0xe5, 0x0b, 0x26, 0x27, 0x36, 0xc0, + 0x26, 0x05, 0x07, 0xe5, 0x05, 0x00, 0xe5, 0x1a, + 0x27, 0x86, 0x40, 0x27, 0x06, 0x07, 0x06, 0xf6, + 0x05, 0xe9, 0x02, 0xe0, 0x4e, 0x05, 0xe0, 0x07, + 0xeb, 0x0d, 0xef, 0x00, 0x6d, 0xef, 0x09, 0xe0, + 0x05, 0x16, 0xe5, 0x83, 0x12, 0xe0, 0x5e, 0xea, + 0x67, 0x00, 0x96, 0xe0, 0x03, 0xe5, 0x80, 0x3c, + 0xe0, 0x89, 0xc4, 0xe5, 0x59, 0x36, 0xe0, 0x05, + 0xe5, 0x83, 0xa8, 0xfb, 0x08, 0x06, 0xa5, 0xe6, + 0x07, 0xe0, 0x8f, 0x22, 0xe5, 0x81, 0xbf, 0xe0, + 0xa1, 0x31, 0xe5, 0x81, 0xb1, 0xc0, 0xe5, 0x17, + 0x00, 0xe9, 0x02, 0x60, 0x36, 0xe5, 0x47, 0x00, + 0xe9, 0x02, 0xa0, 0xe5, 0x16, 0x20, 0x86, 0x16, + 0xe0, 0x02, 0xe5, 0x28, 0xc6, 0x96, 0x6f, 0x64, + 0x16, 0x0f, 0xe0, 0x02, 0xe9, 0x02, 0x00, 0xcb, + 0x00, 0xe5, 0x0d, 0x80, 0xe5, 0x0b, 0xe0, 0x82, + 0x28, 0xe1, 0x18, 0xe2, 0x18, 0xeb, 0x0f, 0x76, + 0xe0, 0x5d, 0xe5, 0x43, 0x60, 0x06, 0x05, 0xe7, + 0x2f, 0xc0, 0x66, 0xe4, 0x05, 0xe0, 0x38, 0x24, + 0x16, 0x04, 0x06, 0xe0, 0x03, 0x27, 0xe0, 0x06, + 0xe5, 0x97, 0x70, 0xe0, 0x00, 0xe5, 0x84, 0x4e, + 0xe0, 0x22, 0xe5, 0x01, 0xe0, 0xa2, 0x5f, 0x64, + 0x00, 0xc4, 0x00, 0x24, 0x00, 0xe5, 0x80, 0x9b, + 0xe0, 0x07, 0x05, 0xe0, 0x15, 0x45, 0x20, 0x05, + 0xe0, 0x06, 0x65, 0xe0, 0x00, 0xe5, 0x81, 0x04, + 0xe0, 0x88, 0x7c, 0xe5, 0x63, 0x80, 0xe5, 0x05, + 0x40, 0xe5, 0x01, 0xc0, 0xe5, 0x02, 0x20, 0x0f, + 0x26, 0x16, 0x7b, 0xe0, 0x91, 0xd4, 0xe6, 0x26, + 0x20, 0xe6, 0x0f, 0xe0, 0x01, 0xef, 0x6c, 0xe0, + 0x34, 0xef, 0x80, 0x6e, 0xe0, 0x02, 0xef, 0x1f, + 0x20, 0xef, 0x34, 0x27, 0x46, 0x4f, 0xa7, 0xfb, + 0x00, 0xe6, 0x00, 0x2f, 0xc6, 0xef, 0x16, 0x66, + 0xef, 0x35, 0xe0, 0x0d, 0xef, 0x3a, 0x46, 0x0f, + 0xe0, 0x72, 0xeb, 0x0c, 0xe0, 0x04, 0xeb, 0x0c, + 0xe0, 0x04, 0xef, 0x4f, 0xe0, 0x01, 0xeb, 0x11, + 0xe0, 0x7f, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12, + 0xc2, 0x00, 0xe2, 0x0a, 0xe1, 0x12, 0xe2, 0x12, + 0x01, 0x00, 0x21, 0x20, 0x01, 0x20, 0x21, 0x20, + 0x61, 0x00, 0xe1, 0x00, 0x62, 0x00, 0x02, 0x00, + 0xc2, 0x00, 0xe2, 0x03, 0xe1, 0x12, 0xe2, 0x12, + 0x21, 0x00, 0x61, 0x20, 0xe1, 0x00, 0x00, 0xc1, + 0x00, 0xe2, 0x12, 0x21, 0x00, 0x61, 0x00, 0x81, + 0x00, 0x01, 0x40, 0xc1, 0x00, 0xe2, 0x12, 0xe1, + 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, + 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, + 0x12, 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x14, 0x20, + 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0xe1, + 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0xe1, 0x11, + 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0xe1, 0x11, 0x0c, + 0xe2, 0x11, 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, + 0x11, 0x0c, 0xa2, 0x3f, 0x20, 0xe9, 0x2a, 0xef, + 0x81, 0x78, 0xe6, 0x2f, 0x6f, 0xe6, 0x2a, 0xef, + 0x00, 0x06, 0xef, 0x06, 0x06, 0x2f, 0x96, 0xe0, + 0x07, 0x86, 0x00, 0xe6, 0x07, 0xe0, 0x83, 0xc8, + 0xe2, 0x02, 0x05, 0xe2, 0x0c, 0xa0, 0xa2, 0xe0, + 0x80, 0x4d, 0xc6, 0x00, 0xe6, 0x09, 0x20, 0xc6, + 0x00, 0x26, 0x00, 0x86, 0x80, 0xe4, 0x36, 0xe0, + 0x19, 0x06, 0xe0, 0x68, 0xe5, 0x25, 0x40, 0xc6, + 0xc4, 0x20, 0xe9, 0x02, 0x60, 0x05, 0x0f, 0xe0, + 0x80, 0xb8, 0xe5, 0x16, 0x06, 0xe0, 0x09, 0xe5, + 0x24, 0x66, 0xe9, 0x02, 0x80, 0x0d, 0xe0, 0x81, + 0x48, 0xe5, 0x13, 0x04, 0x66, 0xe9, 0x02, 0xe0, + 0x82, 0x5e, 0xc5, 0x00, 0x65, 0x00, 0x25, 0x00, + 0xe5, 0x07, 0x00, 0xe5, 0x80, 0x3d, 0x20, 0xeb, + 0x01, 0xc6, 0xe0, 0x21, 0xe1, 0x1a, 0xe2, 0x1a, + 0xc6, 0x04, 0x60, 0xe9, 0x02, 0x60, 0x36, 0xe0, + 0x82, 0x89, 0xeb, 0x33, 0x0f, 0x4b, 0x0d, 0x6b, + 0xe0, 0x44, 0xeb, 0x25, 0x0f, 0xeb, 0x07, 0xe0, + 0x80, 0x3a, 0x65, 0x00, 0xe5, 0x13, 0x00, 0x25, + 0x00, 0x05, 0x20, 0x05, 0x00, 0xe5, 0x02, 0x00, + 0x65, 0x00, 0x05, 0x00, 0x05, 0xa0, 0x05, 0x60, + 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x45, 0x00, + 0x25, 0x00, 0x05, 0x20, 0x05, 0x00, 0x05, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x25, 0x00, + 0x05, 0x20, 0x65, 0x00, 0xc5, 0x00, 0x65, 0x00, + 0x65, 0x00, 0x05, 0x00, 0xe5, 0x02, 0x00, 0xe5, + 0x09, 0x80, 0x45, 0x00, 0x85, 0x00, 0xe5, 0x09, + 0xe0, 0x2c, 0x2c, 0xe0, 0x80, 0x86, 0xef, 0x24, + 0x60, 0xef, 0x5c, 0xe0, 0x04, 0xef, 0x07, 0x20, + 0xef, 0x07, 0x00, 0xef, 0x07, 0x00, 0xef, 0x1d, + 0xe0, 0x02, 0xeb, 0x05, 0xef, 0x80, 0x19, 0xe0, + 0x30, 0xef, 0x15, 0xe0, 0x05, 0xef, 0x24, 0x60, + 0xef, 0x01, 0xc0, 0x2f, 0xe0, 0x06, 0xaf, 0xe0, + 0x80, 0x12, 0xef, 0x80, 0x73, 0x8e, 0xef, 0x82, + 0x50, 0x60, 0xef, 0x09, 0x40, 0xef, 0x05, 0x40, + 0xef, 0x6f, 0x60, 0xef, 0x57, 0xa0, 0xef, 0x04, + 0x60, 0x0f, 0xe0, 0x07, 0xef, 0x04, 0x60, 0xef, + 0x30, 0xe0, 0x00, 0xef, 0x02, 0xa0, 0xef, 0x20, + 0xe0, 0x00, 0xef, 0x16, 0x20, 0x2f, 0xe0, 0x46, + 0xef, 0x80, 0xcc, 0xe0, 0x04, 0xef, 0x06, 0x20, + 0xef, 0x05, 0x40, 0xef, 0x01, 0xc0, 0xef, 0x26, + 0x00, 0xcf, 0xe0, 0x00, 0xef, 0x06, 0x60, 0xef, + 0x01, 0xc0, 0xef, 0x01, 0xc0, 0xef, 0x80, 0x0b, + 0x00, 0xef, 0x2f, 0xe0, 0x1d, 0xe9, 0x02, 0xe0, + 0x83, 0x7e, 0xe5, 0xc0, 0x66, 0x58, 0xe0, 0x18, + 0xe5, 0x8f, 0xb2, 0xa0, 0xe5, 0x80, 0x56, 0x20, + 0xe5, 0x95, 0xfa, 0xe0, 0x06, 0xe5, 0x9c, 0xa9, + 0xe0, 0x8b, 0x97, 0xe5, 0x81, 0x96, 0xe0, 0x85, + 0x5a, 0xe5, 0x92, 0xc3, 0x80, 0xe5, 0x8f, 0xd8, + 0xe0, 0xca, 0x9b, 0xc9, 0x1b, 0xe0, 0x16, 0xfb, + 0x58, 0xe0, 0x78, 0xe6, 0x80, 0x68, 0xe0, 0xc0, + 0xbd, 0x88, 0xfd, 0xc0, 0xbf, 0x76, 0x20, 0xfd, + 0xc0, 0xbf, 0x76, 0x20, +}; + +typedef enum { + UNICODE_SCRIPT_Unknown, + UNICODE_SCRIPT_Adlam, + UNICODE_SCRIPT_Ahom, + UNICODE_SCRIPT_Anatolian_Hieroglyphs, + UNICODE_SCRIPT_Arabic, + UNICODE_SCRIPT_Armenian, + UNICODE_SCRIPT_Avestan, + UNICODE_SCRIPT_Balinese, + UNICODE_SCRIPT_Bamum, + UNICODE_SCRIPT_Bassa_Vah, + UNICODE_SCRIPT_Batak, + UNICODE_SCRIPT_Bengali, + UNICODE_SCRIPT_Bhaiksuki, + UNICODE_SCRIPT_Bopomofo, + UNICODE_SCRIPT_Brahmi, + UNICODE_SCRIPT_Braille, + UNICODE_SCRIPT_Buginese, + UNICODE_SCRIPT_Buhid, + UNICODE_SCRIPT_Canadian_Aboriginal, + UNICODE_SCRIPT_Carian, + UNICODE_SCRIPT_Caucasian_Albanian, + UNICODE_SCRIPT_Chakma, + UNICODE_SCRIPT_Cham, + UNICODE_SCRIPT_Cherokee, + UNICODE_SCRIPT_Chorasmian, + UNICODE_SCRIPT_Common, + UNICODE_SCRIPT_Coptic, + UNICODE_SCRIPT_Cuneiform, + UNICODE_SCRIPT_Cypriot, + UNICODE_SCRIPT_Cyrillic, + UNICODE_SCRIPT_Cypro_Minoan, + UNICODE_SCRIPT_Deseret, + UNICODE_SCRIPT_Devanagari, + UNICODE_SCRIPT_Dives_Akuru, + UNICODE_SCRIPT_Dogra, + UNICODE_SCRIPT_Duployan, + UNICODE_SCRIPT_Egyptian_Hieroglyphs, + UNICODE_SCRIPT_Elbasan, + UNICODE_SCRIPT_Elymaic, + UNICODE_SCRIPT_Ethiopic, + UNICODE_SCRIPT_Georgian, + UNICODE_SCRIPT_Glagolitic, + UNICODE_SCRIPT_Gothic, + UNICODE_SCRIPT_Grantha, + UNICODE_SCRIPT_Greek, + UNICODE_SCRIPT_Gujarati, + UNICODE_SCRIPT_Gunjala_Gondi, + UNICODE_SCRIPT_Gurmukhi, + UNICODE_SCRIPT_Han, + UNICODE_SCRIPT_Hangul, + UNICODE_SCRIPT_Hanifi_Rohingya, + UNICODE_SCRIPT_Hanunoo, + UNICODE_SCRIPT_Hatran, + UNICODE_SCRIPT_Hebrew, + UNICODE_SCRIPT_Hiragana, + UNICODE_SCRIPT_Imperial_Aramaic, + UNICODE_SCRIPT_Inherited, + UNICODE_SCRIPT_Inscriptional_Pahlavi, + UNICODE_SCRIPT_Inscriptional_Parthian, + UNICODE_SCRIPT_Javanese, + UNICODE_SCRIPT_Kaithi, + UNICODE_SCRIPT_Kannada, + UNICODE_SCRIPT_Katakana, + UNICODE_SCRIPT_Kawi, + UNICODE_SCRIPT_Kayah_Li, + UNICODE_SCRIPT_Kharoshthi, + UNICODE_SCRIPT_Khmer, + UNICODE_SCRIPT_Khojki, + UNICODE_SCRIPT_Khitan_Small_Script, + UNICODE_SCRIPT_Khudawadi, + UNICODE_SCRIPT_Lao, + UNICODE_SCRIPT_Latin, + UNICODE_SCRIPT_Lepcha, + UNICODE_SCRIPT_Limbu, + UNICODE_SCRIPT_Linear_A, + UNICODE_SCRIPT_Linear_B, + UNICODE_SCRIPT_Lisu, + UNICODE_SCRIPT_Lycian, + UNICODE_SCRIPT_Lydian, + UNICODE_SCRIPT_Makasar, + UNICODE_SCRIPT_Mahajani, + UNICODE_SCRIPT_Malayalam, + UNICODE_SCRIPT_Mandaic, + UNICODE_SCRIPT_Manichaean, + UNICODE_SCRIPT_Marchen, + UNICODE_SCRIPT_Masaram_Gondi, + UNICODE_SCRIPT_Medefaidrin, + UNICODE_SCRIPT_Meetei_Mayek, + UNICODE_SCRIPT_Mende_Kikakui, + UNICODE_SCRIPT_Meroitic_Cursive, + UNICODE_SCRIPT_Meroitic_Hieroglyphs, + UNICODE_SCRIPT_Miao, + UNICODE_SCRIPT_Modi, + UNICODE_SCRIPT_Mongolian, + UNICODE_SCRIPT_Mro, + UNICODE_SCRIPT_Multani, + UNICODE_SCRIPT_Myanmar, + UNICODE_SCRIPT_Nabataean, + UNICODE_SCRIPT_Nag_Mundari, + UNICODE_SCRIPT_Nandinagari, + UNICODE_SCRIPT_New_Tai_Lue, + UNICODE_SCRIPT_Newa, + UNICODE_SCRIPT_Nko, + UNICODE_SCRIPT_Nushu, + UNICODE_SCRIPT_Nyiakeng_Puachue_Hmong, + UNICODE_SCRIPT_Ogham, + UNICODE_SCRIPT_Ol_Chiki, + UNICODE_SCRIPT_Old_Hungarian, + UNICODE_SCRIPT_Old_Italic, + UNICODE_SCRIPT_Old_North_Arabian, + UNICODE_SCRIPT_Old_Permic, + UNICODE_SCRIPT_Old_Persian, + UNICODE_SCRIPT_Old_Sogdian, + UNICODE_SCRIPT_Old_South_Arabian, + UNICODE_SCRIPT_Old_Turkic, + UNICODE_SCRIPT_Old_Uyghur, + UNICODE_SCRIPT_Oriya, + UNICODE_SCRIPT_Osage, + UNICODE_SCRIPT_Osmanya, + UNICODE_SCRIPT_Pahawh_Hmong, + UNICODE_SCRIPT_Palmyrene, + UNICODE_SCRIPT_Pau_Cin_Hau, + UNICODE_SCRIPT_Phags_Pa, + UNICODE_SCRIPT_Phoenician, + UNICODE_SCRIPT_Psalter_Pahlavi, + UNICODE_SCRIPT_Rejang, + UNICODE_SCRIPT_Runic, + UNICODE_SCRIPT_Samaritan, + UNICODE_SCRIPT_Saurashtra, + UNICODE_SCRIPT_Sharada, + UNICODE_SCRIPT_Shavian, + UNICODE_SCRIPT_Siddham, + UNICODE_SCRIPT_SignWriting, + UNICODE_SCRIPT_Sinhala, + UNICODE_SCRIPT_Sogdian, + UNICODE_SCRIPT_Sora_Sompeng, + UNICODE_SCRIPT_Soyombo, + UNICODE_SCRIPT_Sundanese, + UNICODE_SCRIPT_Syloti_Nagri, + UNICODE_SCRIPT_Syriac, + UNICODE_SCRIPT_Tagalog, + UNICODE_SCRIPT_Tagbanwa, + UNICODE_SCRIPT_Tai_Le, + UNICODE_SCRIPT_Tai_Tham, + UNICODE_SCRIPT_Tai_Viet, + UNICODE_SCRIPT_Takri, + UNICODE_SCRIPT_Tamil, + UNICODE_SCRIPT_Tangut, + UNICODE_SCRIPT_Telugu, + UNICODE_SCRIPT_Thaana, + UNICODE_SCRIPT_Thai, + UNICODE_SCRIPT_Tibetan, + UNICODE_SCRIPT_Tifinagh, + UNICODE_SCRIPT_Tirhuta, + UNICODE_SCRIPT_Tangsa, + UNICODE_SCRIPT_Toto, + UNICODE_SCRIPT_Ugaritic, + UNICODE_SCRIPT_Vai, + UNICODE_SCRIPT_Vithkuqi, + UNICODE_SCRIPT_Wancho, + UNICODE_SCRIPT_Warang_Citi, + UNICODE_SCRIPT_Yezidi, + UNICODE_SCRIPT_Yi, + UNICODE_SCRIPT_Zanabazar_Square, + UNICODE_SCRIPT_COUNT, +} UnicodeScriptEnum; + +static const char unicode_script_name_table[] = + "Adlam,Adlm" "\0" + "Ahom,Ahom" "\0" + "Anatolian_Hieroglyphs,Hluw" "\0" + "Arabic,Arab" "\0" + "Armenian,Armn" "\0" + "Avestan,Avst" "\0" + "Balinese,Bali" "\0" + "Bamum,Bamu" "\0" + "Bassa_Vah,Bass" "\0" + "Batak,Batk" "\0" + "Bengali,Beng" "\0" + "Bhaiksuki,Bhks" "\0" + "Bopomofo,Bopo" "\0" + "Brahmi,Brah" "\0" + "Braille,Brai" "\0" + "Buginese,Bugi" "\0" + "Buhid,Buhd" "\0" + "Canadian_Aboriginal,Cans" "\0" + "Carian,Cari" "\0" + "Caucasian_Albanian,Aghb" "\0" + "Chakma,Cakm" "\0" + "Cham,Cham" "\0" + "Cherokee,Cher" "\0" + "Chorasmian,Chrs" "\0" + "Common,Zyyy" "\0" + "Coptic,Copt,Qaac" "\0" + "Cuneiform,Xsux" "\0" + "Cypriot,Cprt" "\0" + "Cyrillic,Cyrl" "\0" + "Cypro_Minoan,Cpmn" "\0" + "Deseret,Dsrt" "\0" + "Devanagari,Deva" "\0" + "Dives_Akuru,Diak" "\0" + "Dogra,Dogr" "\0" + "Duployan,Dupl" "\0" + "Egyptian_Hieroglyphs,Egyp" "\0" + "Elbasan,Elba" "\0" + "Elymaic,Elym" "\0" + "Ethiopic,Ethi" "\0" + "Georgian,Geor" "\0" + "Glagolitic,Glag" "\0" + "Gothic,Goth" "\0" + "Grantha,Gran" "\0" + "Greek,Grek" "\0" + "Gujarati,Gujr" "\0" + "Gunjala_Gondi,Gong" "\0" + "Gurmukhi,Guru" "\0" + "Han,Hani" "\0" + "Hangul,Hang" "\0" + "Hanifi_Rohingya,Rohg" "\0" + "Hanunoo,Hano" "\0" + "Hatran,Hatr" "\0" + "Hebrew,Hebr" "\0" + "Hiragana,Hira" "\0" + "Imperial_Aramaic,Armi" "\0" + "Inherited,Zinh,Qaai" "\0" + "Inscriptional_Pahlavi,Phli" "\0" + "Inscriptional_Parthian,Prti" "\0" + "Javanese,Java" "\0" + "Kaithi,Kthi" "\0" + "Kannada,Knda" "\0" + "Katakana,Kana" "\0" + "Kawi,Kawi" "\0" + "Kayah_Li,Kali" "\0" + "Kharoshthi,Khar" "\0" + "Khmer,Khmr" "\0" + "Khojki,Khoj" "\0" + "Khitan_Small_Script,Kits" "\0" + "Khudawadi,Sind" "\0" + "Lao,Laoo" "\0" + "Latin,Latn" "\0" + "Lepcha,Lepc" "\0" + "Limbu,Limb" "\0" + "Linear_A,Lina" "\0" + "Linear_B,Linb" "\0" + "Lisu,Lisu" "\0" + "Lycian,Lyci" "\0" + "Lydian,Lydi" "\0" + "Makasar,Maka" "\0" + "Mahajani,Mahj" "\0" + "Malayalam,Mlym" "\0" + "Mandaic,Mand" "\0" + "Manichaean,Mani" "\0" + "Marchen,Marc" "\0" + "Masaram_Gondi,Gonm" "\0" + "Medefaidrin,Medf" "\0" + "Meetei_Mayek,Mtei" "\0" + "Mende_Kikakui,Mend" "\0" + "Meroitic_Cursive,Merc" "\0" + "Meroitic_Hieroglyphs,Mero" "\0" + "Miao,Plrd" "\0" + "Modi,Modi" "\0" + "Mongolian,Mong" "\0" + "Mro,Mroo" "\0" + "Multani,Mult" "\0" + "Myanmar,Mymr" "\0" + "Nabataean,Nbat" "\0" + "Nag_Mundari,Nagm" "\0" + "Nandinagari,Nand" "\0" + "New_Tai_Lue,Talu" "\0" + "Newa,Newa" "\0" + "Nko,Nkoo" "\0" + "Nushu,Nshu" "\0" + "Nyiakeng_Puachue_Hmong,Hmnp" "\0" + "Ogham,Ogam" "\0" + "Ol_Chiki,Olck" "\0" + "Old_Hungarian,Hung" "\0" + "Old_Italic,Ital" "\0" + "Old_North_Arabian,Narb" "\0" + "Old_Permic,Perm" "\0" + "Old_Persian,Xpeo" "\0" + "Old_Sogdian,Sogo" "\0" + "Old_South_Arabian,Sarb" "\0" + "Old_Turkic,Orkh" "\0" + "Old_Uyghur,Ougr" "\0" + "Oriya,Orya" "\0" + "Osage,Osge" "\0" + "Osmanya,Osma" "\0" + "Pahawh_Hmong,Hmng" "\0" + "Palmyrene,Palm" "\0" + "Pau_Cin_Hau,Pauc" "\0" + "Phags_Pa,Phag" "\0" + "Phoenician,Phnx" "\0" + "Psalter_Pahlavi,Phlp" "\0" + "Rejang,Rjng" "\0" + "Runic,Runr" "\0" + "Samaritan,Samr" "\0" + "Saurashtra,Saur" "\0" + "Sharada,Shrd" "\0" + "Shavian,Shaw" "\0" + "Siddham,Sidd" "\0" + "SignWriting,Sgnw" "\0" + "Sinhala,Sinh" "\0" + "Sogdian,Sogd" "\0" + "Sora_Sompeng,Sora" "\0" + "Soyombo,Soyo" "\0" + "Sundanese,Sund" "\0" + "Syloti_Nagri,Sylo" "\0" + "Syriac,Syrc" "\0" + "Tagalog,Tglg" "\0" + "Tagbanwa,Tagb" "\0" + "Tai_Le,Tale" "\0" + "Tai_Tham,Lana" "\0" + "Tai_Viet,Tavt" "\0" + "Takri,Takr" "\0" + "Tamil,Taml" "\0" + "Tangut,Tang" "\0" + "Telugu,Telu" "\0" + "Thaana,Thaa" "\0" + "Thai,Thai" "\0" + "Tibetan,Tibt" "\0" + "Tifinagh,Tfng" "\0" + "Tirhuta,Tirh" "\0" + "Tangsa,Tnsa" "\0" + "Toto,Toto" "\0" + "Ugaritic,Ugar" "\0" + "Vai,Vaii" "\0" + "Vithkuqi,Vith" "\0" + "Wancho,Wcho" "\0" + "Warang_Citi,Wara" "\0" + "Yezidi,Yezi" "\0" + "Yi,Yiii" "\0" + "Zanabazar_Square,Zanb" "\0" +; + +static const uint8_t unicode_script_table[2720] = { + 0xc0, 0x19, 0x99, 0x47, 0x85, 0x19, 0x99, 0x47, + 0xae, 0x19, 0x80, 0x47, 0x8e, 0x19, 0x80, 0x47, + 0x84, 0x19, 0x96, 0x47, 0x80, 0x19, 0x9e, 0x47, + 0x80, 0x19, 0xe1, 0x60, 0x47, 0xa6, 0x19, 0x84, + 0x47, 0x84, 0x19, 0x81, 0x0d, 0x93, 0x19, 0xe0, + 0x0f, 0x38, 0x83, 0x2c, 0x80, 0x19, 0x82, 0x2c, + 0x01, 0x83, 0x2c, 0x80, 0x19, 0x80, 0x2c, 0x03, + 0x80, 0x2c, 0x80, 0x19, 0x80, 0x2c, 0x80, 0x19, + 0x82, 0x2c, 0x00, 0x80, 0x2c, 0x00, 0x93, 0x2c, + 0x00, 0xbe, 0x2c, 0x8d, 0x1a, 0x8f, 0x2c, 0xe0, + 0x24, 0x1d, 0x81, 0x38, 0xe0, 0x48, 0x1d, 0x00, + 0xa5, 0x05, 0x01, 0xb1, 0x05, 0x01, 0x82, 0x05, + 0x00, 0xb6, 0x35, 0x07, 0x9a, 0x35, 0x03, 0x85, + 0x35, 0x0a, 0x84, 0x04, 0x80, 0x19, 0x85, 0x04, + 0x80, 0x19, 0x8d, 0x04, 0x80, 0x19, 0x82, 0x04, + 0x80, 0x19, 0x9f, 0x04, 0x80, 0x19, 0x89, 0x04, + 0x8a, 0x38, 0x99, 0x04, 0x80, 0x38, 0xe0, 0x0b, + 0x04, 0x80, 0x19, 0xa1, 0x04, 0x8d, 0x8b, 0x00, + 0xbb, 0x8b, 0x01, 0x82, 0x8b, 0xaf, 0x04, 0xb1, + 0x95, 0x0d, 0xba, 0x66, 0x01, 0x82, 0x66, 0xad, + 0x7f, 0x01, 0x8e, 0x7f, 0x00, 0x9b, 0x52, 0x01, + 0x80, 0x52, 0x00, 0x8a, 0x8b, 0x04, 0x9e, 0x04, + 0x00, 0x81, 0x04, 0x05, 0xc9, 0x04, 0x80, 0x19, + 0x9c, 0x04, 0xd0, 0x20, 0x83, 0x38, 0x8e, 0x20, + 0x81, 0x19, 0x99, 0x20, 0x83, 0x0b, 0x00, 0x87, + 0x0b, 0x01, 0x81, 0x0b, 0x01, 0x95, 0x0b, 0x00, + 0x86, 0x0b, 0x00, 0x80, 0x0b, 0x02, 0x83, 0x0b, + 0x01, 0x88, 0x0b, 0x01, 0x81, 0x0b, 0x01, 0x83, + 0x0b, 0x07, 0x80, 0x0b, 0x03, 0x81, 0x0b, 0x00, + 0x84, 0x0b, 0x01, 0x98, 0x0b, 0x01, 0x82, 0x2f, + 0x00, 0x85, 0x2f, 0x03, 0x81, 0x2f, 0x01, 0x95, + 0x2f, 0x00, 0x86, 0x2f, 0x00, 0x81, 0x2f, 0x00, + 0x81, 0x2f, 0x00, 0x81, 0x2f, 0x01, 0x80, 0x2f, + 0x00, 0x84, 0x2f, 0x03, 0x81, 0x2f, 0x01, 0x82, + 0x2f, 0x02, 0x80, 0x2f, 0x06, 0x83, 0x2f, 0x00, + 0x80, 0x2f, 0x06, 0x90, 0x2f, 0x09, 0x82, 0x2d, + 0x00, 0x88, 0x2d, 0x00, 0x82, 0x2d, 0x00, 0x95, + 0x2d, 0x00, 0x86, 0x2d, 0x00, 0x81, 0x2d, 0x00, + 0x84, 0x2d, 0x01, 0x89, 0x2d, 0x00, 0x82, 0x2d, + 0x00, 0x82, 0x2d, 0x01, 0x80, 0x2d, 0x0e, 0x83, + 0x2d, 0x01, 0x8b, 0x2d, 0x06, 0x86, 0x2d, 0x00, + 0x82, 0x74, 0x00, 0x87, 0x74, 0x01, 0x81, 0x74, + 0x01, 0x95, 0x74, 0x00, 0x86, 0x74, 0x00, 0x81, + 0x74, 0x00, 0x84, 0x74, 0x01, 0x88, 0x74, 0x01, + 0x81, 0x74, 0x01, 0x82, 0x74, 0x06, 0x82, 0x74, + 0x03, 0x81, 0x74, 0x00, 0x84, 0x74, 0x01, 0x91, + 0x74, 0x09, 0x81, 0x92, 0x00, 0x85, 0x92, 0x02, + 0x82, 0x92, 0x00, 0x83, 0x92, 0x02, 0x81, 0x92, + 0x00, 0x80, 0x92, 0x00, 0x81, 0x92, 0x02, 0x81, + 0x92, 0x02, 0x82, 0x92, 0x02, 0x8b, 0x92, 0x03, + 0x84, 0x92, 0x02, 0x82, 0x92, 0x00, 0x83, 0x92, + 0x01, 0x80, 0x92, 0x05, 0x80, 0x92, 0x0d, 0x94, + 0x92, 0x04, 0x8c, 0x94, 0x00, 0x82, 0x94, 0x00, + 0x96, 0x94, 0x00, 0x8f, 0x94, 0x01, 0x88, 0x94, + 0x00, 0x82, 0x94, 0x00, 0x83, 0x94, 0x06, 0x81, + 0x94, 0x00, 0x82, 0x94, 0x01, 0x80, 0x94, 0x01, + 0x83, 0x94, 0x01, 0x89, 0x94, 0x06, 0x88, 0x94, + 0x8c, 0x3d, 0x00, 0x82, 0x3d, 0x00, 0x96, 0x3d, + 0x00, 0x89, 0x3d, 0x00, 0x84, 0x3d, 0x01, 0x88, + 0x3d, 0x00, 0x82, 0x3d, 0x00, 0x83, 0x3d, 0x06, + 0x81, 0x3d, 0x05, 0x81, 0x3d, 0x00, 0x83, 0x3d, + 0x01, 0x89, 0x3d, 0x00, 0x82, 0x3d, 0x0b, 0x8c, + 0x51, 0x00, 0x82, 0x51, 0x00, 0xb2, 0x51, 0x00, + 0x82, 0x51, 0x00, 0x85, 0x51, 0x03, 0x8f, 0x51, + 0x01, 0x99, 0x51, 0x00, 0x82, 0x85, 0x00, 0x91, + 0x85, 0x02, 0x97, 0x85, 0x00, 0x88, 0x85, 0x00, + 0x80, 0x85, 0x01, 0x86, 0x85, 0x02, 0x80, 0x85, + 0x03, 0x85, 0x85, 0x00, 0x80, 0x85, 0x00, 0x87, + 0x85, 0x05, 0x89, 0x85, 0x01, 0x82, 0x85, 0x0b, + 0xb9, 0x96, 0x03, 0x80, 0x19, 0x9b, 0x96, 0x24, + 0x81, 0x46, 0x00, 0x80, 0x46, 0x00, 0x84, 0x46, + 0x00, 0x97, 0x46, 0x00, 0x80, 0x46, 0x00, 0x96, + 0x46, 0x01, 0x84, 0x46, 0x00, 0x80, 0x46, 0x00, + 0x86, 0x46, 0x00, 0x89, 0x46, 0x01, 0x83, 0x46, + 0x1f, 0xc7, 0x97, 0x00, 0xa3, 0x97, 0x03, 0xa6, + 0x97, 0x00, 0xa3, 0x97, 0x00, 0x8e, 0x97, 0x00, + 0x86, 0x97, 0x83, 0x19, 0x81, 0x97, 0x24, 0xe0, + 0x3f, 0x60, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04, + 0x80, 0x28, 0x01, 0xaa, 0x28, 0x80, 0x19, 0x83, + 0x28, 0xe0, 0x9f, 0x31, 0xc8, 0x27, 0x00, 0x83, + 0x27, 0x01, 0x86, 0x27, 0x00, 0x80, 0x27, 0x00, + 0x83, 0x27, 0x01, 0xa8, 0x27, 0x00, 0x83, 0x27, + 0x01, 0xa0, 0x27, 0x00, 0x83, 0x27, 0x01, 0x86, + 0x27, 0x00, 0x80, 0x27, 0x00, 0x83, 0x27, 0x01, + 0x8e, 0x27, 0x00, 0xb8, 0x27, 0x00, 0x83, 0x27, + 0x01, 0xc2, 0x27, 0x01, 0x9f, 0x27, 0x02, 0x99, + 0x27, 0x05, 0xd5, 0x17, 0x01, 0x85, 0x17, 0x01, + 0xe2, 0x1f, 0x12, 0x9c, 0x69, 0x02, 0xca, 0x7e, + 0x82, 0x19, 0x8a, 0x7e, 0x06, 0x95, 0x8c, 0x08, + 0x80, 0x8c, 0x94, 0x33, 0x81, 0x19, 0x08, 0x93, + 0x11, 0x0b, 0x8c, 0x8d, 0x00, 0x82, 0x8d, 0x00, + 0x81, 0x8d, 0x0b, 0xdd, 0x42, 0x01, 0x89, 0x42, + 0x05, 0x89, 0x42, 0x05, 0x81, 0x5d, 0x81, 0x19, + 0x80, 0x5d, 0x80, 0x19, 0x93, 0x5d, 0x05, 0xd8, + 0x5d, 0x06, 0xaa, 0x5d, 0x04, 0xc5, 0x12, 0x09, + 0x9e, 0x49, 0x00, 0x8b, 0x49, 0x03, 0x8b, 0x49, + 0x03, 0x80, 0x49, 0x02, 0x8b, 0x49, 0x9d, 0x8e, + 0x01, 0x84, 0x8e, 0x0a, 0xab, 0x64, 0x03, 0x99, + 0x64, 0x05, 0x8a, 0x64, 0x02, 0x81, 0x64, 0x9f, + 0x42, 0x9b, 0x10, 0x01, 0x81, 0x10, 0xbe, 0x8f, + 0x00, 0x9c, 0x8f, 0x01, 0x8a, 0x8f, 0x05, 0x89, + 0x8f, 0x05, 0x8d, 0x8f, 0x01, 0x9e, 0x38, 0x30, + 0xcc, 0x07, 0x02, 0xae, 0x07, 0x00, 0xbf, 0x89, + 0xb3, 0x0a, 0x07, 0x83, 0x0a, 0xb7, 0x48, 0x02, + 0x8e, 0x48, 0x02, 0x82, 0x48, 0xaf, 0x6a, 0x88, + 0x1d, 0x06, 0xaa, 0x28, 0x01, 0x82, 0x28, 0x87, + 0x89, 0x07, 0x82, 0x38, 0x80, 0x19, 0x8c, 0x38, + 0x80, 0x19, 0x86, 0x38, 0x83, 0x19, 0x80, 0x38, + 0x85, 0x19, 0x80, 0x38, 0x82, 0x19, 0x81, 0x38, + 0x80, 0x19, 0x04, 0xa5, 0x47, 0x84, 0x2c, 0x80, + 0x1d, 0xb0, 0x47, 0x84, 0x2c, 0x83, 0x47, 0x84, + 0x2c, 0x8c, 0x47, 0x80, 0x1d, 0xc5, 0x47, 0x80, + 0x2c, 0xbf, 0x38, 0xe0, 0x9f, 0x47, 0x95, 0x2c, + 0x01, 0x85, 0x2c, 0x01, 0xa5, 0x2c, 0x01, 0x85, + 0x2c, 0x01, 0x87, 0x2c, 0x00, 0x80, 0x2c, 0x00, + 0x80, 0x2c, 0x00, 0x80, 0x2c, 0x00, 0x9e, 0x2c, + 0x01, 0xb4, 0x2c, 0x00, 0x8e, 0x2c, 0x00, 0x8d, + 0x2c, 0x01, 0x85, 0x2c, 0x00, 0x92, 0x2c, 0x01, + 0x82, 0x2c, 0x00, 0x88, 0x2c, 0x00, 0x8b, 0x19, + 0x81, 0x38, 0xd6, 0x19, 0x00, 0x8a, 0x19, 0x80, + 0x47, 0x01, 0x8a, 0x19, 0x80, 0x47, 0x8e, 0x19, + 0x00, 0x8c, 0x47, 0x02, 0xa0, 0x19, 0x0e, 0xa0, + 0x38, 0x0e, 0xa5, 0x19, 0x80, 0x2c, 0x82, 0x19, + 0x81, 0x47, 0x85, 0x19, 0x80, 0x47, 0x9a, 0x19, + 0x80, 0x47, 0x90, 0x19, 0xa8, 0x47, 0x82, 0x19, + 0x03, 0xe2, 0x36, 0x19, 0x18, 0x8a, 0x19, 0x14, + 0xe3, 0x3f, 0x19, 0xe0, 0x9f, 0x0f, 0xe2, 0x13, + 0x19, 0x01, 0x9f, 0x19, 0x00, 0xe0, 0x08, 0x19, + 0xdf, 0x29, 0x9f, 0x47, 0xe0, 0x13, 0x1a, 0x04, + 0x86, 0x1a, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04, + 0x80, 0x28, 0x01, 0xb7, 0x98, 0x06, 0x81, 0x98, + 0x0d, 0x80, 0x98, 0x96, 0x27, 0x08, 0x86, 0x27, + 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, + 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, + 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x9f, 0x1d, + 0xdd, 0x19, 0x21, 0x99, 0x30, 0x00, 0xd8, 0x30, + 0x0b, 0xe0, 0x75, 0x30, 0x19, 0x8b, 0x19, 0x03, + 0x84, 0x19, 0x80, 0x30, 0x80, 0x19, 0x80, 0x30, + 0x98, 0x19, 0x88, 0x30, 0x83, 0x38, 0x81, 0x31, + 0x87, 0x19, 0x83, 0x30, 0x83, 0x19, 0x00, 0xd5, + 0x36, 0x01, 0x81, 0x38, 0x81, 0x19, 0x82, 0x36, + 0x80, 0x19, 0xd9, 0x3e, 0x81, 0x19, 0x82, 0x3e, + 0x04, 0xaa, 0x0d, 0x00, 0xdd, 0x31, 0x00, 0x8f, + 0x19, 0x9f, 0x0d, 0xa3, 0x19, 0x0b, 0x8f, 0x3e, + 0x9e, 0x31, 0x00, 0xbf, 0x19, 0x9e, 0x31, 0xd0, + 0x19, 0xae, 0x3e, 0x80, 0x19, 0xd7, 0x3e, 0xe0, + 0x47, 0x19, 0xf0, 0x09, 0x5f, 0x30, 0xbf, 0x19, + 0xf0, 0x41, 0x9f, 0x30, 0xe4, 0x2c, 0xa2, 0x02, + 0xb6, 0xa2, 0x08, 0xaf, 0x4c, 0xe0, 0xcb, 0x9d, + 0x13, 0xdf, 0x1d, 0xd7, 0x08, 0x07, 0xa1, 0x19, + 0xe0, 0x05, 0x47, 0x82, 0x19, 0xbf, 0x47, 0x04, + 0x81, 0x47, 0x00, 0x80, 0x47, 0x00, 0x84, 0x47, + 0x17, 0x8d, 0x47, 0xac, 0x8a, 0x02, 0x89, 0x19, + 0x05, 0xb7, 0x7a, 0x07, 0xc5, 0x80, 0x07, 0x8b, + 0x80, 0x05, 0x9f, 0x20, 0xad, 0x40, 0x80, 0x19, + 0x80, 0x40, 0xa3, 0x7d, 0x0a, 0x80, 0x7d, 0x9c, + 0x31, 0x02, 0xcd, 0x3b, 0x00, 0x80, 0x19, 0x89, + 0x3b, 0x03, 0x81, 0x3b, 0x9e, 0x60, 0x00, 0xb6, + 0x16, 0x08, 0x8d, 0x16, 0x01, 0x89, 0x16, 0x01, + 0x83, 0x16, 0x9f, 0x60, 0xc2, 0x90, 0x17, 0x84, + 0x90, 0x96, 0x57, 0x09, 0x85, 0x27, 0x01, 0x85, + 0x27, 0x01, 0x85, 0x27, 0x08, 0x86, 0x27, 0x00, + 0x86, 0x27, 0x00, 0xaa, 0x47, 0x80, 0x19, 0x88, + 0x47, 0x80, 0x2c, 0x83, 0x47, 0x81, 0x19, 0x03, + 0xcf, 0x17, 0xad, 0x57, 0x01, 0x89, 0x57, 0x05, + 0xf0, 0x1b, 0x43, 0x31, 0x0b, 0x96, 0x31, 0x03, + 0xb0, 0x31, 0x70, 0x10, 0xa3, 0xe1, 0x0d, 0x30, + 0x01, 0xe0, 0x09, 0x30, 0x25, 0x86, 0x47, 0x0b, + 0x84, 0x05, 0x04, 0x99, 0x35, 0x00, 0x84, 0x35, + 0x00, 0x80, 0x35, 0x00, 0x81, 0x35, 0x00, 0x81, + 0x35, 0x00, 0x89, 0x35, 0xe0, 0x12, 0x04, 0x0f, + 0xe1, 0x0a, 0x04, 0x81, 0x19, 0xcf, 0x04, 0x01, + 0xb5, 0x04, 0x06, 0x80, 0x04, 0x1f, 0x8f, 0x04, + 0x8f, 0x38, 0x89, 0x19, 0x05, 0x8d, 0x38, 0x81, + 0x1d, 0xa2, 0x19, 0x00, 0x92, 0x19, 0x00, 0x83, + 0x19, 0x03, 0x84, 0x04, 0x00, 0xe0, 0x26, 0x04, + 0x01, 0x80, 0x19, 0x00, 0x9f, 0x19, 0x99, 0x47, + 0x85, 0x19, 0x99, 0x47, 0x8a, 0x19, 0x89, 0x3e, + 0x80, 0x19, 0xac, 0x3e, 0x81, 0x19, 0x9e, 0x31, + 0x02, 0x85, 0x31, 0x01, 0x85, 0x31, 0x01, 0x85, + 0x31, 0x01, 0x82, 0x31, 0x02, 0x86, 0x19, 0x00, + 0x86, 0x19, 0x09, 0x84, 0x19, 0x01, 0x8b, 0x4b, + 0x00, 0x99, 0x4b, 0x00, 0x92, 0x4b, 0x00, 0x81, + 0x4b, 0x00, 0x8e, 0x4b, 0x01, 0x8d, 0x4b, 0x21, + 0xe0, 0x1a, 0x4b, 0x04, 0x82, 0x19, 0x03, 0xac, + 0x19, 0x02, 0x88, 0x19, 0xce, 0x2c, 0x00, 0x8c, + 0x19, 0x02, 0x80, 0x2c, 0x2e, 0xac, 0x19, 0x80, + 0x38, 0x60, 0x21, 0x9c, 0x4d, 0x02, 0xb0, 0x13, + 0x0e, 0x80, 0x38, 0x9a, 0x19, 0x03, 0xa3, 0x6c, + 0x08, 0x82, 0x6c, 0x9a, 0x2a, 0x04, 0xaa, 0x6e, + 0x04, 0x9d, 0x9c, 0x00, 0x80, 0x9c, 0xa3, 0x6f, + 0x03, 0x8d, 0x6f, 0x29, 0xcf, 0x1f, 0xaf, 0x82, + 0x9d, 0x76, 0x01, 0x89, 0x76, 0x05, 0xa3, 0x75, + 0x03, 0xa3, 0x75, 0x03, 0xa7, 0x25, 0x07, 0xb3, + 0x14, 0x0a, 0x80, 0x14, 0x8a, 0x9e, 0x00, 0x8e, + 0x9e, 0x00, 0x86, 0x9e, 0x00, 0x81, 0x9e, 0x00, + 0x8a, 0x9e, 0x00, 0x8e, 0x9e, 0x00, 0x86, 0x9e, + 0x00, 0x81, 0x9e, 0x42, 0xe0, 0xd6, 0x4a, 0x08, + 0x95, 0x4a, 0x09, 0x87, 0x4a, 0x17, 0x85, 0x47, + 0x00, 0xa9, 0x47, 0x00, 0x88, 0x47, 0x44, 0x85, + 0x1c, 0x01, 0x80, 0x1c, 0x00, 0xab, 0x1c, 0x00, + 0x81, 0x1c, 0x02, 0x80, 0x1c, 0x01, 0x80, 0x1c, + 0x95, 0x37, 0x00, 0x88, 0x37, 0x9f, 0x78, 0x9e, + 0x61, 0x07, 0x88, 0x61, 0x2f, 0x92, 0x34, 0x00, + 0x81, 0x34, 0x04, 0x84, 0x34, 0x9b, 0x7b, 0x02, + 0x80, 0x7b, 0x99, 0x4e, 0x04, 0x80, 0x4e, 0x3f, + 0x9f, 0x5a, 0x97, 0x59, 0x03, 0x93, 0x59, 0x01, + 0xad, 0x59, 0x83, 0x41, 0x00, 0x81, 0x41, 0x04, + 0x87, 0x41, 0x00, 0x82, 0x41, 0x00, 0x9c, 0x41, + 0x01, 0x82, 0x41, 0x03, 0x89, 0x41, 0x06, 0x88, + 0x41, 0x06, 0x9f, 0x71, 0x9f, 0x6d, 0x1f, 0xa6, + 0x53, 0x03, 0x8b, 0x53, 0x08, 0xb5, 0x06, 0x02, + 0x86, 0x06, 0x95, 0x3a, 0x01, 0x87, 0x3a, 0x92, + 0x39, 0x04, 0x87, 0x39, 0x91, 0x7c, 0x06, 0x83, + 0x7c, 0x0b, 0x86, 0x7c, 0x4f, 0xc8, 0x72, 0x36, + 0xb2, 0x6b, 0x0c, 0xb2, 0x6b, 0x06, 0x85, 0x6b, + 0xa7, 0x32, 0x07, 0x89, 0x32, 0x60, 0xc5, 0x9e, + 0x04, 0x00, 0xa9, 0xa1, 0x00, 0x82, 0xa1, 0x01, + 0x81, 0xa1, 0x4a, 0x82, 0x04, 0xa7, 0x70, 0x07, + 0xa9, 0x86, 0x15, 0x99, 0x73, 0x25, 0x9b, 0x18, + 0x13, 0x96, 0x26, 0x08, 0xcd, 0x0e, 0x03, 0xa3, + 0x0e, 0x08, 0x80, 0x0e, 0xc2, 0x3c, 0x09, 0x80, + 0x3c, 0x01, 0x98, 0x87, 0x06, 0x89, 0x87, 0x05, + 0xb4, 0x15, 0x00, 0x91, 0x15, 0x07, 0xa6, 0x50, + 0x08, 0xdf, 0x81, 0x00, 0x93, 0x85, 0x0a, 0x91, + 0x43, 0x00, 0xae, 0x43, 0x3d, 0x86, 0x5f, 0x00, + 0x80, 0x5f, 0x00, 0x83, 0x5f, 0x00, 0x8e, 0x5f, + 0x00, 0x8a, 0x5f, 0x05, 0xba, 0x45, 0x04, 0x89, + 0x45, 0x05, 0x83, 0x2b, 0x00, 0x87, 0x2b, 0x01, + 0x81, 0x2b, 0x01, 0x95, 0x2b, 0x00, 0x86, 0x2b, + 0x00, 0x81, 0x2b, 0x00, 0x84, 0x2b, 0x00, 0x80, + 0x38, 0x88, 0x2b, 0x01, 0x81, 0x2b, 0x01, 0x82, + 0x2b, 0x01, 0x80, 0x2b, 0x05, 0x80, 0x2b, 0x04, + 0x86, 0x2b, 0x01, 0x86, 0x2b, 0x02, 0x84, 0x2b, + 0x60, 0x2a, 0xdb, 0x65, 0x00, 0x84, 0x65, 0x1d, + 0xc7, 0x99, 0x07, 0x89, 0x99, 0x60, 0x45, 0xb5, + 0x83, 0x01, 0xa5, 0x83, 0x21, 0xc4, 0x5c, 0x0a, + 0x89, 0x5c, 0x05, 0x8c, 0x5d, 0x12, 0xb9, 0x91, + 0x05, 0x89, 0x91, 0x35, 0x9a, 0x02, 0x01, 0x8e, + 0x02, 0x03, 0x96, 0x02, 0x60, 0x58, 0xbb, 0x22, + 0x60, 0x03, 0xd2, 0xa0, 0x0b, 0x80, 0xa0, 0x86, + 0x21, 0x01, 0x80, 0x21, 0x01, 0x87, 0x21, 0x00, + 0x81, 0x21, 0x00, 0x9d, 0x21, 0x00, 0x81, 0x21, + 0x01, 0x8b, 0x21, 0x08, 0x89, 0x21, 0x45, 0x87, + 0x63, 0x01, 0xad, 0x63, 0x01, 0x8a, 0x63, 0x1a, + 0xc7, 0xa3, 0x07, 0xd2, 0x88, 0x0c, 0x8f, 0x12, + 0xb8, 0x79, 0x06, 0x89, 0x20, 0x60, 0x95, 0x88, + 0x0c, 0x00, 0xac, 0x0c, 0x00, 0x8d, 0x0c, 0x09, + 0x9c, 0x0c, 0x02, 0x9f, 0x54, 0x01, 0x95, 0x54, + 0x00, 0x8d, 0x54, 0x48, 0x86, 0x55, 0x00, 0x81, + 0x55, 0x00, 0xab, 0x55, 0x02, 0x80, 0x55, 0x00, + 0x81, 0x55, 0x00, 0x88, 0x55, 0x07, 0x89, 0x55, + 0x05, 0x85, 0x2e, 0x00, 0x81, 0x2e, 0x00, 0xa4, + 0x2e, 0x00, 0x81, 0x2e, 0x00, 0x85, 0x2e, 0x06, + 0x89, 0x2e, 0x60, 0xd5, 0x98, 0x4f, 0x06, 0x90, + 0x3f, 0x00, 0xa8, 0x3f, 0x02, 0x9b, 0x3f, 0x55, + 0x80, 0x4c, 0x0e, 0xb1, 0x92, 0x0c, 0x80, 0x92, + 0xe3, 0x39, 0x1b, 0x60, 0x05, 0xe0, 0x0e, 0x1b, + 0x00, 0x84, 0x1b, 0x0a, 0xe0, 0x63, 0x1b, 0x69, + 0xeb, 0xe0, 0x02, 0x1e, 0x0c, 0xe3, 0xf5, 0x24, + 0x6f, 0x49, 0xe1, 0xe6, 0x03, 0x70, 0x11, 0x58, + 0xe1, 0xd8, 0x08, 0x06, 0x9e, 0x5e, 0x00, 0x89, + 0x5e, 0x03, 0x81, 0x5e, 0xce, 0x9a, 0x00, 0x89, + 0x9a, 0x05, 0x9d, 0x09, 0x01, 0x85, 0x09, 0x09, + 0xc5, 0x77, 0x09, 0x89, 0x77, 0x00, 0x86, 0x77, + 0x00, 0x94, 0x77, 0x04, 0x92, 0x77, 0x62, 0x4f, + 0xda, 0x56, 0x60, 0x04, 0xca, 0x5b, 0x03, 0xb8, + 0x5b, 0x06, 0x90, 0x5b, 0x3f, 0x80, 0x93, 0x80, + 0x67, 0x81, 0x30, 0x80, 0x44, 0x0a, 0x81, 0x30, + 0x0d, 0xf0, 0x07, 0x97, 0x93, 0x07, 0xe2, 0x9f, + 0x93, 0xe1, 0x75, 0x44, 0x29, 0x88, 0x93, 0x70, + 0x12, 0x86, 0x83, 0x3e, 0x00, 0x86, 0x3e, 0x00, + 0x81, 0x3e, 0x00, 0x80, 0x3e, 0xe0, 0xbe, 0x36, + 0x82, 0x3e, 0x0e, 0x80, 0x36, 0x1c, 0x82, 0x36, + 0x01, 0x80, 0x3e, 0x0d, 0x83, 0x3e, 0x07, 0xe1, + 0x2b, 0x67, 0x68, 0xa3, 0xe0, 0x0a, 0x23, 0x04, + 0x8c, 0x23, 0x02, 0x88, 0x23, 0x06, 0x89, 0x23, + 0x01, 0x83, 0x23, 0x83, 0x19, 0x70, 0x01, 0xfb, + 0xad, 0x38, 0x01, 0x96, 0x38, 0x08, 0xe0, 0x13, + 0x19, 0x3b, 0xe0, 0x95, 0x19, 0x09, 0xa6, 0x19, + 0x01, 0xbd, 0x19, 0x82, 0x38, 0x90, 0x19, 0x87, + 0x38, 0x81, 0x19, 0x86, 0x38, 0x9d, 0x19, 0x83, + 0x38, 0xbc, 0x19, 0x14, 0xc5, 0x2c, 0x60, 0x19, + 0x93, 0x19, 0x0b, 0x93, 0x19, 0x0b, 0xd6, 0x19, + 0x08, 0x98, 0x19, 0x60, 0x26, 0xd4, 0x19, 0x00, + 0xc6, 0x19, 0x00, 0x81, 0x19, 0x01, 0x80, 0x19, + 0x01, 0x81, 0x19, 0x01, 0x83, 0x19, 0x00, 0x8b, + 0x19, 0x00, 0x80, 0x19, 0x00, 0x86, 0x19, 0x00, + 0xc0, 0x19, 0x00, 0x83, 0x19, 0x01, 0x87, 0x19, + 0x00, 0x86, 0x19, 0x00, 0x9b, 0x19, 0x00, 0x83, + 0x19, 0x00, 0x84, 0x19, 0x00, 0x80, 0x19, 0x02, + 0x86, 0x19, 0x00, 0xe0, 0xf3, 0x19, 0x01, 0xe0, + 0xc3, 0x19, 0x01, 0xb1, 0x19, 0xe2, 0x2b, 0x84, + 0x0e, 0x84, 0x84, 0x00, 0x8e, 0x84, 0x63, 0xef, + 0x9e, 0x47, 0x05, 0x85, 0x47, 0x60, 0x74, 0x86, + 0x29, 0x00, 0x90, 0x29, 0x01, 0x86, 0x29, 0x00, + 0x81, 0x29, 0x00, 0x84, 0x29, 0x04, 0xbd, 0x1d, + 0x20, 0x80, 0x1d, 0x60, 0x0f, 0xac, 0x68, 0x02, + 0x8d, 0x68, 0x01, 0x89, 0x68, 0x03, 0x81, 0x68, + 0x60, 0xdf, 0x9e, 0x9b, 0x10, 0xb9, 0x9f, 0x04, + 0x80, 0x9f, 0x61, 0x6f, 0xa9, 0x62, 0x62, 0x85, + 0x86, 0x27, 0x00, 0x83, 0x27, 0x00, 0x81, 0x27, + 0x00, 0x8e, 0x27, 0x00, 0xe0, 0x64, 0x58, 0x01, + 0x8f, 0x58, 0x28, 0xcb, 0x01, 0x03, 0x89, 0x01, + 0x03, 0x81, 0x01, 0x62, 0xb0, 0xc3, 0x19, 0x4b, + 0xbc, 0x19, 0x60, 0x61, 0x83, 0x04, 0x00, 0x9a, + 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, 0x01, + 0x80, 0x04, 0x00, 0x89, 0x04, 0x00, 0x83, 0x04, + 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x05, 0x80, + 0x04, 0x03, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, + 0x80, 0x04, 0x00, 0x82, 0x04, 0x00, 0x81, 0x04, + 0x00, 0x80, 0x04, 0x01, 0x80, 0x04, 0x00, 0x80, + 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00, + 0x80, 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, + 0x01, 0x83, 0x04, 0x00, 0x86, 0x04, 0x00, 0x83, + 0x04, 0x00, 0x83, 0x04, 0x00, 0x80, 0x04, 0x00, + 0x89, 0x04, 0x00, 0x90, 0x04, 0x04, 0x82, 0x04, + 0x00, 0x84, 0x04, 0x00, 0x90, 0x04, 0x33, 0x81, + 0x04, 0x60, 0xad, 0xab, 0x19, 0x03, 0xe0, 0x03, + 0x19, 0x0b, 0x8e, 0x19, 0x01, 0x8e, 0x19, 0x00, + 0x8e, 0x19, 0x00, 0xa4, 0x19, 0x09, 0xe0, 0x4d, + 0x19, 0x37, 0x99, 0x19, 0x80, 0x36, 0x81, 0x19, + 0x0c, 0xab, 0x19, 0x03, 0x88, 0x19, 0x06, 0x81, + 0x19, 0x0d, 0x85, 0x19, 0x60, 0x39, 0xe3, 0x77, + 0x19, 0x03, 0x90, 0x19, 0x02, 0x8c, 0x19, 0x02, + 0xe0, 0x16, 0x19, 0x03, 0xde, 0x19, 0x05, 0x8b, + 0x19, 0x03, 0x80, 0x19, 0x0e, 0x8b, 0x19, 0x03, + 0xb7, 0x19, 0x07, 0x89, 0x19, 0x05, 0xa7, 0x19, + 0x07, 0x9d, 0x19, 0x01, 0x81, 0x19, 0x4d, 0xe0, + 0xf3, 0x19, 0x0b, 0x8d, 0x19, 0x01, 0x8c, 0x19, + 0x02, 0x88, 0x19, 0x06, 0xad, 0x19, 0x00, 0x86, + 0x19, 0x07, 0x8d, 0x19, 0x03, 0x88, 0x19, 0x06, + 0x88, 0x19, 0x06, 0xe0, 0x32, 0x19, 0x00, 0xb6, + 0x19, 0x24, 0x89, 0x19, 0x63, 0xa5, 0xf0, 0x96, + 0x7f, 0x30, 0x1f, 0xef, 0xd9, 0x30, 0x05, 0xe0, + 0x7d, 0x30, 0x01, 0xf0, 0x06, 0x21, 0x30, 0x0d, + 0xf0, 0x0c, 0xd0, 0x30, 0x6b, 0xbe, 0xe1, 0xbd, + 0x30, 0x65, 0x81, 0xf0, 0x02, 0xea, 0x30, 0x04, + 0xef, 0xff, 0x30, 0x7a, 0xcb, 0xf0, 0x80, 0x19, + 0x1d, 0xdf, 0x19, 0x60, 0x1f, 0xe0, 0x8f, 0x38, +}; + +static const uint8_t unicode_script_ext_table[828] = { + 0x82, 0xc1, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x00, + 0x00, 0x01, 0x2c, 0x1c, 0x00, 0x0c, 0x01, 0x47, + 0x80, 0x92, 0x00, 0x00, 0x02, 0x1d, 0x6e, 0x00, + 0x02, 0x1d, 0x29, 0x01, 0x02, 0x1d, 0x47, 0x00, + 0x02, 0x1d, 0x29, 0x81, 0x03, 0x00, 0x00, 0x06, + 0x04, 0x66, 0x32, 0x8b, 0x95, 0xa1, 0x0d, 0x00, + 0x00, 0x06, 0x04, 0x66, 0x32, 0x8b, 0x95, 0xa1, + 0x00, 0x03, 0x04, 0x8b, 0x95, 0x01, 0x00, 0x00, + 0x07, 0x01, 0x04, 0x66, 0x32, 0x8b, 0x95, 0xa1, + 0x1f, 0x00, 0x00, 0x09, 0x01, 0x04, 0x52, 0x53, + 0x73, 0x7c, 0x32, 0x86, 0x8b, 0x09, 0x00, 0x0a, + 0x02, 0x04, 0x8b, 0x09, 0x00, 0x09, 0x03, 0x04, + 0x95, 0xa1, 0x05, 0x00, 0x00, 0x02, 0x04, 0x8b, + 0x62, 0x00, 0x00, 0x02, 0x04, 0x32, 0x81, 0xfb, + 0x00, 0x00, 0x0d, 0x0b, 0x20, 0x2b, 0x2d, 0x2f, + 0x3d, 0x47, 0x51, 0x74, 0x81, 0x92, 0x94, 0x99, + 0x00, 0x0c, 0x0b, 0x20, 0x2b, 0x2d, 0x2f, 0x3d, + 0x47, 0x51, 0x74, 0x92, 0x94, 0x99, 0x10, 0x00, + 0x00, 0x14, 0x0b, 0x20, 0x22, 0x2e, 0x55, 0x2b, + 0x2d, 0x2f, 0x3d, 0x50, 0x51, 0x63, 0x74, 0x45, + 0x85, 0x8a, 0x91, 0x92, 0x94, 0x99, 0x00, 0x15, + 0x0b, 0x20, 0x22, 0x2e, 0x55, 0x2b, 0x2d, 0x2f, + 0x3d, 0x49, 0x50, 0x51, 0x63, 0x74, 0x45, 0x85, + 0x8a, 0x91, 0x92, 0x94, 0x99, 0x09, 0x04, 0x20, + 0x22, 0x3c, 0x50, 0x75, 0x00, 0x09, 0x03, 0x0b, + 0x15, 0x8a, 0x75, 0x00, 0x09, 0x02, 0x2f, 0x5f, + 0x75, 0x00, 0x09, 0x02, 0x2d, 0x43, 0x80, 0x75, + 0x00, 0x0d, 0x02, 0x2b, 0x92, 0x80, 0x71, 0x00, + 0x09, 0x02, 0x3d, 0x63, 0x82, 0xcf, 0x00, 0x09, + 0x03, 0x15, 0x60, 0x8e, 0x80, 0x30, 0x00, 0x00, + 0x02, 0x28, 0x47, 0x85, 0xb8, 0x00, 0x01, 0x04, + 0x11, 0x33, 0x8d, 0x8c, 0x80, 0x4a, 0x00, 0x01, + 0x02, 0x5d, 0x7a, 0x00, 0x00, 0x00, 0x02, 0x5d, + 0x7a, 0x84, 0x49, 0x00, 0x00, 0x04, 0x0b, 0x20, + 0x2b, 0x3d, 0x00, 0x01, 0x20, 0x00, 0x04, 0x0b, + 0x20, 0x2b, 0x3d, 0x00, 0x02, 0x20, 0x2b, 0x00, + 0x01, 0x20, 0x01, 0x02, 0x0b, 0x20, 0x00, 0x02, + 0x20, 0x81, 0x00, 0x02, 0x0b, 0x20, 0x00, 0x02, + 0x20, 0x81, 0x00, 0x06, 0x20, 0x3d, 0x51, 0x74, + 0x92, 0x94, 0x00, 0x01, 0x20, 0x01, 0x02, 0x20, + 0x81, 0x01, 0x01, 0x20, 0x00, 0x02, 0x20, 0x81, + 0x00, 0x02, 0x0b, 0x20, 0x06, 0x01, 0x20, 0x00, + 0x02, 0x20, 0x63, 0x00, 0x02, 0x0b, 0x20, 0x01, + 0x01, 0x20, 0x00, 0x02, 0x0b, 0x20, 0x03, 0x01, + 0x20, 0x00, 0x08, 0x0b, 0x20, 0x2b, 0x3d, 0x63, + 0x74, 0x94, 0x99, 0x00, 0x02, 0x20, 0x2b, 0x00, + 0x03, 0x20, 0x2b, 0x3d, 0x01, 0x02, 0x0b, 0x20, + 0x00, 0x01, 0x0b, 0x01, 0x02, 0x20, 0x2b, 0x00, + 0x01, 0x63, 0x80, 0x44, 0x00, 0x01, 0x01, 0x2c, + 0x35, 0x00, 0x00, 0x02, 0x1d, 0x8b, 0x00, 0x00, + 0x00, 0x01, 0x8b, 0x81, 0xb3, 0x00, 0x00, 0x02, + 0x47, 0x5d, 0x80, 0x3f, 0x00, 0x00, 0x03, 0x20, + 0x2b, 0x47, 0x8c, 0xd1, 0x00, 0x00, 0x02, 0x1d, + 0x29, 0x81, 0x3c, 0x00, 0x01, 0x06, 0x0d, 0x31, + 0x30, 0x36, 0x3e, 0xa2, 0x00, 0x05, 0x0d, 0x31, + 0x30, 0x36, 0x3e, 0x01, 0x00, 0x00, 0x01, 0x30, + 0x00, 0x00, 0x09, 0x06, 0x0d, 0x31, 0x30, 0x36, + 0x3e, 0xa2, 0x00, 0x00, 0x00, 0x05, 0x0d, 0x31, + 0x30, 0x36, 0x3e, 0x07, 0x06, 0x0d, 0x31, 0x30, + 0x36, 0x3e, 0xa2, 0x03, 0x05, 0x0d, 0x31, 0x30, + 0x36, 0x3e, 0x09, 0x00, 0x03, 0x02, 0x0d, 0x30, + 0x01, 0x00, 0x00, 0x05, 0x0d, 0x31, 0x30, 0x36, + 0x3e, 0x04, 0x02, 0x36, 0x3e, 0x00, 0x00, 0x00, + 0x05, 0x0d, 0x31, 0x30, 0x36, 0x3e, 0x03, 0x00, + 0x01, 0x03, 0x30, 0x36, 0x3e, 0x01, 0x01, 0x30, + 0x58, 0x00, 0x03, 0x02, 0x36, 0x3e, 0x02, 0x00, + 0x00, 0x02, 0x36, 0x3e, 0x59, 0x00, 0x00, 0x06, + 0x0d, 0x31, 0x30, 0x36, 0x3e, 0xa2, 0x00, 0x02, + 0x36, 0x3e, 0x80, 0x12, 0x00, 0x0f, 0x01, 0x30, + 0x1f, 0x00, 0x23, 0x01, 0x30, 0x3b, 0x00, 0x27, + 0x01, 0x30, 0x37, 0x00, 0x30, 0x01, 0x30, 0x0e, + 0x00, 0x0b, 0x01, 0x30, 0x32, 0x00, 0x00, 0x01, + 0x30, 0x57, 0x00, 0x18, 0x01, 0x30, 0x09, 0x00, + 0x04, 0x01, 0x30, 0x5f, 0x00, 0x1e, 0x01, 0x30, + 0xc0, 0x31, 0xef, 0x00, 0x00, 0x02, 0x1d, 0x29, + 0x80, 0x0f, 0x00, 0x07, 0x02, 0x30, 0x47, 0x80, + 0xa7, 0x00, 0x02, 0x0e, 0x20, 0x22, 0x2d, 0x2f, + 0x43, 0x3d, 0x3c, 0x50, 0x51, 0x5c, 0x63, 0x45, + 0x91, 0x99, 0x02, 0x0d, 0x20, 0x22, 0x2d, 0x2f, + 0x43, 0x3d, 0x3c, 0x50, 0x5c, 0x63, 0x45, 0x91, + 0x99, 0x03, 0x0b, 0x20, 0x22, 0x2d, 0x2f, 0x43, + 0x3c, 0x50, 0x5c, 0x45, 0x91, 0x99, 0x80, 0x36, + 0x00, 0x00, 0x02, 0x0b, 0x20, 0x00, 0x00, 0x00, + 0x02, 0x20, 0x92, 0x39, 0x00, 0x00, 0x03, 0x40, + 0x47, 0x60, 0x80, 0x1f, 0x00, 0x00, 0x02, 0x10, + 0x3b, 0xc0, 0x12, 0xed, 0x00, 0x01, 0x02, 0x04, + 0x66, 0x80, 0x31, 0x00, 0x00, 0x02, 0x04, 0x95, + 0x09, 0x00, 0x00, 0x02, 0x04, 0x95, 0x46, 0x00, + 0x01, 0x05, 0x0d, 0x31, 0x30, 0x36, 0x3e, 0x80, + 0x99, 0x00, 0x04, 0x06, 0x0d, 0x31, 0x30, 0x36, + 0x3e, 0xa2, 0x09, 0x00, 0x00, 0x02, 0x36, 0x3e, + 0x2c, 0x00, 0x01, 0x02, 0x36, 0x3e, 0x80, 0xdf, + 0x00, 0x01, 0x03, 0x1e, 0x1c, 0x4b, 0x00, 0x02, + 0x1c, 0x4b, 0x03, 0x00, 0x2c, 0x03, 0x1c, 0x4a, + 0x4b, 0x02, 0x00, 0x08, 0x02, 0x1c, 0x4b, 0x81, + 0x1f, 0x00, 0x1b, 0x02, 0x04, 0x1a, 0x87, 0x75, + 0x00, 0x00, 0x02, 0x53, 0x73, 0x87, 0x8d, 0x00, + 0x00, 0x02, 0x2b, 0x92, 0x00, 0x00, 0x00, 0x02, + 0x2b, 0x92, 0x36, 0x00, 0x01, 0x02, 0x2b, 0x92, + 0x8c, 0x12, 0x00, 0x01, 0x02, 0x2b, 0x92, 0x00, + 0x00, 0x00, 0x02, 0x2b, 0x92, 0xc0, 0x5c, 0x4b, + 0x00, 0x03, 0x01, 0x23, 0x96, 0x3b, 0x00, 0x11, + 0x01, 0x30, 0x9e, 0x5d, 0x00, 0x01, 0x01, 0x30, + 0xce, 0xcd, 0x2d, 0x00, +}; + +static const uint8_t unicode_prop_Hyphen_table[28] = { + 0xac, 0x80, 0xfe, 0x80, 0x44, 0xdb, 0x80, 0x52, + 0x7a, 0x80, 0x48, 0x08, 0x81, 0x4e, 0x04, 0x80, + 0x42, 0xe2, 0x80, 0x60, 0xcd, 0x66, 0x80, 0x40, + 0xa8, 0x80, 0xd6, 0x80, +}; + +static const uint8_t unicode_prop_Other_Math_table[200] = { + 0xdd, 0x80, 0x43, 0x70, 0x11, 0x80, 0x99, 0x09, + 0x81, 0x5c, 0x1f, 0x80, 0x9a, 0x82, 0x8a, 0x80, + 0x9f, 0x83, 0x97, 0x81, 0x8d, 0x81, 0xc0, 0x8c, + 0x18, 0x11, 0x1c, 0x91, 0x03, 0x01, 0x89, 0x00, + 0x14, 0x28, 0x11, 0x09, 0x02, 0x05, 0x13, 0x24, + 0xca, 0x21, 0x18, 0x08, 0x08, 0x00, 0x21, 0x0b, + 0x0b, 0x91, 0x09, 0x00, 0x06, 0x00, 0x29, 0x41, + 0x21, 0x83, 0x40, 0xa7, 0x08, 0x80, 0x97, 0x80, + 0x90, 0x80, 0x41, 0xbc, 0x81, 0x8b, 0x88, 0x24, + 0x21, 0x09, 0x14, 0x8d, 0x00, 0x01, 0x85, 0x97, + 0x81, 0xb8, 0x00, 0x80, 0x9c, 0x83, 0x88, 0x81, + 0x41, 0x55, 0x81, 0x9e, 0x89, 0x41, 0x92, 0x95, + 0xbe, 0x83, 0x9f, 0x81, 0x60, 0xd4, 0x62, 0x00, + 0x03, 0x80, 0x40, 0xd2, 0x00, 0x80, 0x60, 0xd4, + 0xc0, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b, + 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f, + 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80, + 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e, + 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x81, + 0xb1, 0x55, 0xff, 0x18, 0x9a, 0x01, 0x00, 0x08, + 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, + 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, + 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, +}; + +static const uint8_t unicode_prop_Other_Alphabetic_table[428] = { + 0x43, 0x44, 0x80, 0x42, 0x69, 0x8d, 0x00, 0x01, + 0x01, 0x00, 0xc7, 0x8a, 0xaf, 0x8c, 0x06, 0x8f, + 0x80, 0xe4, 0x33, 0x19, 0x0b, 0x80, 0xa2, 0x80, + 0x9d, 0x8f, 0xe5, 0x8a, 0xe4, 0x0a, 0x88, 0x02, + 0x03, 0x40, 0xa6, 0x8b, 0x16, 0x85, 0x93, 0xb5, + 0x09, 0x8e, 0x01, 0x22, 0x89, 0x81, 0x9c, 0x82, + 0xb9, 0x31, 0x09, 0x81, 0x89, 0x80, 0x89, 0x81, + 0x9c, 0x82, 0xb9, 0x23, 0x09, 0x0b, 0x80, 0x9d, + 0x0a, 0x80, 0x8a, 0x82, 0xb9, 0x38, 0x10, 0x81, + 0x94, 0x81, 0x95, 0x13, 0x82, 0xb9, 0x31, 0x09, + 0x81, 0x88, 0x81, 0x89, 0x81, 0x9d, 0x80, 0xba, + 0x22, 0x10, 0x82, 0x89, 0x80, 0xa7, 0x84, 0xb8, + 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9c, 0x82, + 0xb9, 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x8e, + 0x80, 0x8b, 0x83, 0xb9, 0x30, 0x10, 0x82, 0x89, + 0x80, 0x89, 0x81, 0x9c, 0x82, 0xca, 0x28, 0x00, + 0x87, 0x91, 0x81, 0xbc, 0x01, 0x86, 0x91, 0x80, + 0xe2, 0x01, 0x28, 0x81, 0x8f, 0x80, 0x40, 0xa2, + 0x92, 0x88, 0x8a, 0x80, 0xa3, 0xed, 0x8b, 0x00, + 0x0b, 0x96, 0x1b, 0x10, 0x11, 0x32, 0x83, 0x8c, + 0x8b, 0x00, 0x89, 0x83, 0x46, 0x73, 0x81, 0x9d, + 0x81, 0x9d, 0x81, 0x9d, 0x81, 0xc1, 0x92, 0x40, + 0xbb, 0x81, 0xa1, 0x80, 0xf5, 0x8b, 0x83, 0x88, + 0x40, 0xdd, 0x84, 0xb8, 0x89, 0x81, 0x93, 0xc9, + 0x81, 0x8a, 0x82, 0xb0, 0x84, 0xaf, 0x8e, 0xbb, + 0x82, 0x9d, 0x88, 0x09, 0xb8, 0x8a, 0xb1, 0x92, + 0x41, 0xaf, 0x8d, 0x46, 0xc0, 0xb3, 0x48, 0xf5, + 0x9f, 0x60, 0x78, 0x73, 0x87, 0xa1, 0x81, 0x41, + 0x61, 0x07, 0x80, 0x96, 0x84, 0xd7, 0x81, 0xb1, + 0x8f, 0x00, 0xb8, 0x80, 0xa5, 0x84, 0x9b, 0x8b, + 0xac, 0x83, 0xaf, 0x8b, 0xa4, 0x80, 0xc2, 0x8d, + 0x8b, 0x07, 0x81, 0xac, 0x82, 0xb1, 0x00, 0x11, + 0x0c, 0x80, 0xab, 0x24, 0x80, 0x40, 0xec, 0x87, + 0x60, 0x4f, 0x32, 0x80, 0x48, 0x56, 0x84, 0x46, + 0x85, 0x10, 0x0c, 0x83, 0x43, 0x13, 0x83, 0x41, + 0x82, 0x81, 0x41, 0x52, 0x82, 0xb4, 0x8d, 0xac, + 0x81, 0x8a, 0x82, 0xac, 0x88, 0x88, 0x80, 0xbc, + 0x82, 0xa3, 0x8b, 0x91, 0x81, 0xb8, 0x82, 0xaf, + 0x8c, 0x8d, 0x81, 0xdb, 0x88, 0x08, 0x28, 0x08, + 0x40, 0x9c, 0x89, 0x96, 0x83, 0xb9, 0x31, 0x09, + 0x81, 0x89, 0x80, 0x89, 0x81, 0x40, 0xd0, 0x8c, + 0x02, 0xe9, 0x91, 0x40, 0xec, 0x31, 0x86, 0x9c, + 0x81, 0xd1, 0x8e, 0x00, 0xe9, 0x8a, 0xe6, 0x8d, + 0x41, 0x00, 0x8c, 0x40, 0xf6, 0x28, 0x09, 0x0a, + 0x00, 0x80, 0x40, 0x8d, 0x31, 0x2b, 0x80, 0x9b, + 0x89, 0xa9, 0x20, 0x83, 0x91, 0x8a, 0xad, 0x8d, + 0x41, 0x96, 0x38, 0x86, 0xd2, 0x95, 0x80, 0x8d, + 0xf9, 0x2a, 0x00, 0x08, 0x10, 0x02, 0x80, 0xc1, + 0x20, 0x08, 0x83, 0x41, 0x5b, 0x83, 0x88, 0x08, + 0x80, 0xaf, 0x32, 0x82, 0x60, 0x50, 0x0d, 0x00, + 0xb6, 0x33, 0xdc, 0x81, 0x60, 0x4c, 0xab, 0x80, + 0x60, 0x23, 0x60, 0x30, 0x90, 0x0e, 0x01, 0x04, + 0xe3, 0x80, 0x48, 0xb6, 0x80, 0x47, 0xe7, 0x99, + 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Other_Lowercase_table[69] = { + 0x40, 0xa9, 0x80, 0x8e, 0x80, 0x41, 0xf4, 0x88, + 0x31, 0x9d, 0x84, 0xdf, 0x80, 0xb3, 0x80, 0x4d, + 0x80, 0x80, 0x4c, 0x2e, 0xbe, 0x8c, 0x80, 0xa1, + 0xa4, 0x42, 0xb0, 0x80, 0x8c, 0x80, 0x8f, 0x8c, + 0x40, 0xd2, 0x8f, 0x43, 0x4f, 0x99, 0x47, 0x91, + 0x81, 0x60, 0x7a, 0x1d, 0x81, 0x40, 0xd1, 0x80, + 0x40, 0x80, 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, + 0x80, 0x60, 0x5c, 0x15, 0x01, 0x10, 0xa9, 0x80, + 0x88, 0x60, 0xd8, 0x74, 0xbd, +}; + +static const uint8_t unicode_prop_Other_Uppercase_table[15] = { + 0x60, 0x21, 0x5f, 0x8f, 0x43, 0x45, 0x99, 0x61, + 0xcc, 0x5f, 0x99, 0x85, 0x99, 0x85, 0x99, +}; + +static const uint8_t unicode_prop_Other_Grapheme_Extend_table[65] = { + 0x49, 0xbd, 0x80, 0x97, 0x80, 0x41, 0x65, 0x80, + 0x97, 0x80, 0xe5, 0x80, 0x97, 0x80, 0x40, 0xe9, + 0x80, 0x91, 0x81, 0xe6, 0x80, 0x97, 0x80, 0xf6, + 0x80, 0x8e, 0x80, 0x4d, 0x54, 0x80, 0x44, 0xd5, + 0x80, 0x50, 0x20, 0x81, 0x60, 0xcf, 0x6d, 0x81, + 0x53, 0x9d, 0x80, 0x97, 0x80, 0x41, 0x57, 0x80, + 0x8b, 0x80, 0x40, 0xf0, 0x80, 0x43, 0x7f, 0x80, + 0x60, 0xb8, 0x33, 0x07, 0x84, 0x6c, 0x2e, 0xac, + 0xdf, +}; + +static const uint8_t unicode_prop_Other_Default_Ignorable_Code_Point_table[32] = { + 0x43, 0x4e, 0x80, 0x4e, 0x0e, 0x81, 0x46, 0x52, + 0x81, 0x48, 0xae, 0x80, 0x50, 0xfd, 0x80, 0x60, + 0xce, 0x3a, 0x80, 0xce, 0x88, 0x6d, 0x00, 0x06, + 0x00, 0x9d, 0xdf, 0xff, 0x40, 0xef, 0x4e, 0x0f, +}; + +static const uint8_t unicode_prop_Other_ID_Start_table[11] = { + 0x58, 0x84, 0x81, 0x48, 0x90, 0x80, 0x94, 0x80, + 0x4f, 0x6b, 0x81, +}; + +static const uint8_t unicode_prop_Other_ID_Continue_table[12] = { + 0x40, 0xb6, 0x80, 0x42, 0xce, 0x80, 0x4f, 0xe0, + 0x88, 0x46, 0x67, 0x80, +}; + +static const uint8_t unicode_prop_Prepended_Concatenation_Mark_table[19] = { + 0x45, 0xff, 0x85, 0x40, 0xd6, 0x80, 0xb0, 0x80, + 0x41, 0x7f, 0x81, 0xcf, 0x80, 0x61, 0x07, 0xd9, + 0x80, 0x8e, 0x80, +}; + +static const uint8_t unicode_prop_XID_Start1_table[31] = { + 0x43, 0x79, 0x80, 0x4a, 0xb7, 0x80, 0xfe, 0x80, + 0x60, 0x21, 0xe6, 0x81, 0x60, 0xcb, 0xc0, 0x85, + 0x41, 0x95, 0x81, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x41, 0x1e, 0x81, +}; + +static const uint8_t unicode_prop_XID_Continue1_table[23] = { + 0x43, 0x79, 0x80, 0x60, 0x2d, 0x1f, 0x81, 0x60, + 0xcb, 0xc0, 0x85, 0x41, 0x95, 0x81, 0xf3, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_Titlecased1_table[22] = { + 0x41, 0xc3, 0x08, 0x08, 0x81, 0xa4, 0x81, 0x4e, + 0xdc, 0xaa, 0x0a, 0x4e, 0x87, 0x3f, 0x3f, 0x87, + 0x8b, 0x80, 0x8e, 0x80, 0xae, 0x80, +}; + +static const uint8_t unicode_prop_Changes_When_Casefolded1_table[33] = { + 0x40, 0xde, 0x80, 0xcf, 0x80, 0x97, 0x80, 0x44, + 0x3c, 0x80, 0x59, 0x11, 0x80, 0x40, 0xe4, 0x3f, + 0x3f, 0x87, 0x89, 0x11, 0x05, 0x02, 0x11, 0x80, + 0xa9, 0x11, 0x80, 0x60, 0xdb, 0x07, 0x86, 0x8b, + 0x84, +}; + +static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[451] = { + 0x40, 0x9f, 0x06, 0x00, 0x01, 0x00, 0x01, 0x12, + 0x10, 0x82, 0x9f, 0x80, 0xcf, 0x01, 0x80, 0x8b, + 0x07, 0x80, 0xfb, 0x01, 0x01, 0x80, 0xa5, 0x80, + 0x40, 0xbb, 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08, + 0x81, 0x89, 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08, + 0x80, 0xc9, 0x82, 0x9c, 0x80, 0x41, 0x93, 0x80, + 0x40, 0x93, 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87, + 0xfb, 0x08, 0x80, 0xd2, 0x01, 0x80, 0xa1, 0x11, + 0x80, 0x40, 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe, + 0x80, 0xa7, 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88, + 0x03, 0x03, 0x03, 0x80, 0x8b, 0x80, 0x88, 0x00, + 0x26, 0x80, 0x90, 0x80, 0x88, 0x03, 0x03, 0x03, + 0x80, 0x8b, 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81, + 0x46, 0x52, 0x81, 0xd4, 0x84, 0x45, 0x1b, 0x10, + 0x8a, 0x80, 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1, + 0xa4, 0x40, 0xd9, 0x80, 0x40, 0xd5, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x3f, 0x87, + 0x89, 0x11, 0x04, 0x00, 0x29, 0x04, 0x12, 0x80, + 0x88, 0x12, 0x80, 0x88, 0x11, 0x11, 0x04, 0x08, + 0x8f, 0x00, 0x20, 0x8b, 0x12, 0x2a, 0x08, 0x0b, + 0x00, 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a, + 0x80, 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a, + 0x01, 0x0c, 0x0a, 0x00, 0x10, 0x11, 0x02, 0x06, + 0x05, 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80, + 0x40, 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41, + 0x34, 0xd5, 0x99, 0x9a, 0x45, 0x20, 0x80, 0xe6, + 0x82, 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0, + 0x80, 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40, + 0xd5, 0xa9, 0x80, 0xb4, 0x00, 0x82, 0xdf, 0x09, + 0x80, 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf, + 0x9e, 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f, + 0x60, 0x72, 0x9b, 0x81, 0x40, 0xd1, 0x80, 0x40, + 0x80, 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, 0x80, + 0x60, 0x4d, 0x95, 0x41, 0x0d, 0x08, 0x00, 0x81, + 0x89, 0x00, 0x00, 0x09, 0x82, 0xc3, 0x81, 0xe9, + 0xa5, 0x86, 0x8b, 0x24, 0x00, 0x97, 0x04, 0x00, + 0x01, 0x01, 0x80, 0xeb, 0xa0, 0x41, 0x6a, 0x91, + 0xbf, 0x81, 0xb5, 0xa7, 0x8c, 0x82, 0x99, 0x95, + 0x94, 0x81, 0x8b, 0x80, 0x92, 0x03, 0x1a, 0x00, + 0x80, 0x40, 0x86, 0x08, 0x80, 0x9f, 0x99, 0x40, + 0x83, 0x15, 0x0d, 0x0d, 0x0a, 0x16, 0x06, 0x80, + 0x88, 0x47, 0x87, 0x20, 0xa9, 0x80, 0x88, 0x60, + 0xb4, 0xe4, 0x83, 0x54, 0xb9, 0x86, 0x8d, 0x87, + 0xbf, 0x85, 0x42, 0x3e, 0xd4, 0x80, 0xc6, 0x01, + 0x08, 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, + 0xc0, 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, + 0x00, 0x16, 0x80, 0x41, 0x53, 0x81, 0x41, 0x23, + 0x81, 0xb1, 0x48, 0x2f, 0xbd, 0x4d, 0x91, 0x18, + 0x9a, 0x01, 0x00, 0x08, 0x80, 0x89, 0x03, 0x00, + 0x00, 0x28, 0x18, 0x00, 0x00, 0x02, 0x01, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b, + 0x06, 0x03, 0x03, 0x00, 0x80, 0x89, 0x80, 0x90, + 0x22, 0x04, 0x80, 0x90, 0x42, 0x43, 0x8a, 0x84, + 0x9e, 0x80, 0x9f, 0x99, 0x82, 0xa2, 0x80, 0xee, + 0x82, 0x8c, 0xab, 0x83, 0x88, 0x31, 0x49, 0x9d, + 0x89, 0x60, 0xfc, 0x05, 0x42, 0x1d, 0x6b, 0x05, + 0xe1, 0x4f, 0xff, +}; + +static const uint8_t unicode_prop_ASCII_Hex_Digit_table[5] = { + 0xaf, 0x89, 0x35, 0x99, 0x85, +}; + +static const uint8_t unicode_prop_Bidi_Control_table[10] = { + 0x46, 0x1b, 0x80, 0x59, 0xf0, 0x81, 0x99, 0x84, + 0xb6, 0x83, +}; + +static const uint8_t unicode_prop_Dash_table[55] = { + 0xac, 0x80, 0x45, 0x5b, 0x80, 0xb2, 0x80, 0x4e, + 0x40, 0x80, 0x44, 0x04, 0x80, 0x48, 0x08, 0x85, + 0xbc, 0x80, 0xa6, 0x80, 0x8e, 0x80, 0x41, 0x85, + 0x80, 0x4c, 0x03, 0x01, 0x80, 0x9e, 0x0b, 0x80, + 0x9b, 0x80, 0x41, 0xbd, 0x80, 0x92, 0x80, 0xee, + 0x80, 0x60, 0xcd, 0x8f, 0x81, 0xa4, 0x80, 0x89, + 0x80, 0x40, 0xa8, 0x80, 0x4f, 0x9e, 0x80, +}; + +static const uint8_t unicode_prop_Deprecated_table[23] = { + 0x41, 0x48, 0x80, 0x45, 0x28, 0x80, 0x49, 0x02, + 0x00, 0x80, 0x48, 0x28, 0x81, 0x48, 0xc4, 0x85, + 0x42, 0xb8, 0x81, 0x6d, 0xdc, 0xd5, 0x80, +}; + +static const uint8_t unicode_prop_Diacritic_table[399] = { + 0xdd, 0x00, 0x80, 0xc6, 0x05, 0x03, 0x01, 0x81, + 0x41, 0xf6, 0x40, 0x9e, 0x07, 0x25, 0x90, 0x0b, + 0x80, 0x88, 0x81, 0x40, 0xfc, 0x84, 0x40, 0xd0, + 0x80, 0xb6, 0x90, 0x80, 0x9a, 0x00, 0x01, 0x00, + 0x40, 0x85, 0x3b, 0x81, 0x40, 0x85, 0x0b, 0x0a, + 0x82, 0xc2, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0xa1, + 0x81, 0xfd, 0x87, 0xa8, 0x89, 0x8f, 0x9b, 0xbc, + 0x80, 0x8f, 0x02, 0x83, 0x9b, 0x80, 0xc9, 0x80, + 0x8f, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed, 0x80, + 0x8f, 0x80, 0xae, 0x82, 0xbb, 0x80, 0x8f, 0x06, + 0x80, 0xf6, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed, + 0x80, 0x8f, 0x80, 0xec, 0x81, 0x8f, 0x80, 0xfb, + 0x80, 0xfb, 0x28, 0x80, 0xea, 0x80, 0x8c, 0x84, + 0xca, 0x81, 0x9a, 0x00, 0x00, 0x03, 0x81, 0xc1, + 0x10, 0x81, 0xbd, 0x80, 0xef, 0x00, 0x81, 0xa7, + 0x0b, 0x84, 0x98, 0x30, 0x80, 0x89, 0x81, 0x42, + 0xc0, 0x82, 0x43, 0xb3, 0x81, 0x40, 0xb2, 0x8a, + 0x88, 0x80, 0x41, 0x5a, 0x82, 0x41, 0x38, 0x39, + 0x80, 0xaf, 0x8e, 0x81, 0x8a, 0xe7, 0x80, 0x8e, + 0x80, 0xa5, 0x88, 0xb5, 0x81, 0x40, 0x89, 0x81, + 0xbf, 0x85, 0xd1, 0x98, 0x18, 0x28, 0x0a, 0xb1, + 0xbe, 0xd8, 0x8b, 0xa4, 0x8a, 0x41, 0xbc, 0x00, + 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c, 0x82, 0x8c, + 0x81, 0x4c, 0xef, 0x82, 0x41, 0x3c, 0x80, 0x41, + 0xf9, 0x85, 0xe8, 0x83, 0xde, 0x80, 0x60, 0x75, + 0x71, 0x80, 0x8b, 0x08, 0x80, 0x9b, 0x81, 0xd1, + 0x81, 0x8d, 0xa1, 0xe5, 0x82, 0xec, 0x81, 0x40, + 0xc9, 0x80, 0x9a, 0x91, 0xb8, 0x83, 0xa3, 0x80, + 0xde, 0x80, 0x8b, 0x80, 0xa3, 0x80, 0x40, 0x94, + 0x82, 0xc0, 0x83, 0xb2, 0x80, 0xe3, 0x84, 0x88, + 0x82, 0xff, 0x81, 0x60, 0x4f, 0x2f, 0x80, 0x43, + 0x00, 0x8f, 0x41, 0x0d, 0x00, 0x80, 0xae, 0x80, + 0xac, 0x81, 0xc2, 0x80, 0x42, 0xfb, 0x80, 0x44, + 0x9e, 0x28, 0xa9, 0x80, 0x88, 0x43, 0x29, 0x81, + 0x42, 0x3a, 0x85, 0x41, 0xd4, 0x82, 0xc5, 0x8a, + 0xb0, 0x83, 0x40, 0xbf, 0x80, 0xa8, 0x80, 0xc7, + 0x81, 0xf7, 0x81, 0xbd, 0x80, 0xcb, 0x80, 0x88, + 0x82, 0xe7, 0x81, 0x40, 0xb1, 0x81, 0xd0, 0x80, + 0x8f, 0x80, 0x97, 0x32, 0x84, 0x40, 0xcc, 0x02, + 0x80, 0xfa, 0x81, 0x40, 0xfa, 0x81, 0xfd, 0x80, + 0xf5, 0x81, 0xf2, 0x80, 0x41, 0x0c, 0x81, 0x41, + 0x01, 0x0b, 0x80, 0x40, 0x9b, 0x80, 0xd2, 0x80, + 0x91, 0x80, 0xd0, 0x80, 0x41, 0xa4, 0x80, 0x41, + 0x01, 0x00, 0x81, 0xd0, 0x80, 0x56, 0xae, 0x8e, + 0x60, 0x36, 0x99, 0x84, 0xba, 0x86, 0x44, 0x57, + 0x90, 0xcf, 0x81, 0x60, 0x3f, 0xfd, 0x18, 0x30, + 0x81, 0x5f, 0x00, 0xad, 0x81, 0x96, 0x42, 0x1f, + 0x12, 0x2f, 0x39, 0x86, 0x9d, 0x83, 0x4e, 0x81, + 0xbd, 0x40, 0xc1, 0x86, 0x41, 0x76, 0x80, 0xbc, + 0x83, 0x45, 0xdf, 0x86, 0xec, 0x10, 0x82, +}; + +static const uint8_t unicode_prop_Extender_table[92] = { + 0x40, 0xb6, 0x80, 0x42, 0x17, 0x81, 0x43, 0x6d, + 0x80, 0x41, 0xb8, 0x80, 0x43, 0x59, 0x80, 0x42, + 0xef, 0x80, 0xfe, 0x80, 0x49, 0x42, 0x80, 0xb7, + 0x80, 0x42, 0x62, 0x80, 0x41, 0x8d, 0x80, 0xc3, + 0x80, 0x53, 0x88, 0x80, 0xaa, 0x84, 0xe6, 0x81, + 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80, 0x45, 0xf5, + 0x80, 0x43, 0xc1, 0x80, 0x95, 0x80, 0x40, 0x88, + 0x80, 0xeb, 0x80, 0x94, 0x81, 0x60, 0x54, 0x7a, + 0x80, 0x48, 0x0f, 0x81, 0x4b, 0xd9, 0x80, 0x42, + 0x67, 0x82, 0x44, 0xce, 0x80, 0x60, 0x50, 0xa8, + 0x81, 0x44, 0x9b, 0x08, 0x80, 0x60, 0x71, 0x57, + 0x81, 0x48, 0x05, 0x82, +}; + +static const uint8_t unicode_prop_Hex_Digit_table[12] = { + 0xaf, 0x89, 0x35, 0x99, 0x85, 0x60, 0xfe, 0xa8, + 0x89, 0x35, 0x99, 0x85, +}; + +static const uint8_t unicode_prop_IDS_Binary_Operator_table[5] = { + 0x60, 0x2f, 0xef, 0x09, 0x87, +}; + +static const uint8_t unicode_prop_IDS_Trinary_Operator_table[4] = { + 0x60, 0x2f, 0xf1, 0x81, +}; + +static const uint8_t unicode_prop_Ideographic_table[69] = { + 0x60, 0x30, 0x05, 0x81, 0x98, 0x88, 0x8d, 0x82, + 0x43, 0xc4, 0x59, 0xbf, 0xbf, 0x60, 0x51, 0xff, + 0x60, 0x58, 0xff, 0x41, 0x6d, 0x81, 0xe9, 0x60, + 0x75, 0x09, 0x80, 0x9a, 0x57, 0xf7, 0x87, 0x44, + 0xd5, 0xa9, 0x88, 0x60, 0x24, 0x66, 0x41, 0x8b, + 0x60, 0x4d, 0x03, 0x60, 0xa6, 0xdf, 0x9f, 0x50, + 0x39, 0x85, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, + 0x5d, 0x30, 0x4c, 0x1e, 0x42, 0x1d, 0x45, 0xe1, + 0x53, 0x4a, 0x84, 0x50, 0x5f, +}; + +static const uint8_t unicode_prop_Join_Control_table[4] = { + 0x60, 0x20, 0x0b, 0x81, +}; + +static const uint8_t unicode_prop_Logical_Order_Exception_table[15] = { + 0x4e, 0x3f, 0x84, 0xfa, 0x84, 0x4a, 0xef, 0x11, + 0x80, 0x60, 0x90, 0xf9, 0x09, 0x00, 0x81, +}; + +static const uint8_t unicode_prop_Noncharacter_Code_Point_table[71] = { + 0x60, 0xfd, 0xcf, 0x9f, 0x42, 0x0d, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60, + 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, +}; + +static const uint8_t unicode_prop_Pattern_Syntax_table[58] = { + 0xa0, 0x8e, 0x89, 0x86, 0x99, 0x18, 0x80, 0x99, + 0x83, 0xa1, 0x30, 0x00, 0x08, 0x00, 0x0b, 0x03, + 0x02, 0x80, 0x96, 0x80, 0x9e, 0x80, 0x5f, 0x17, + 0x97, 0x87, 0x8e, 0x81, 0x92, 0x80, 0x89, 0x41, + 0x30, 0x42, 0xcf, 0x40, 0x9f, 0x42, 0x75, 0x9d, + 0x44, 0x6b, 0x41, 0xff, 0xff, 0x41, 0x80, 0x13, + 0x98, 0x8e, 0x80, 0x60, 0xcd, 0x0c, 0x81, 0x41, + 0x04, 0x81, +}; + +static const uint8_t unicode_prop_Pattern_White_Space_table[11] = { + 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x5f, 0x87, + 0x81, 0x97, 0x81, +}; + +static const uint8_t unicode_prop_Quotation_Mark_table[31] = { + 0xa1, 0x03, 0x80, 0x40, 0x82, 0x80, 0x8e, 0x80, + 0x5f, 0x5b, 0x87, 0x98, 0x81, 0x4e, 0x06, 0x80, + 0x41, 0xc8, 0x83, 0x8c, 0x82, 0x60, 0xce, 0x20, + 0x83, 0x40, 0xbc, 0x03, 0x80, 0xd9, 0x81, +}; + +static const uint8_t unicode_prop_Radical_table[9] = { + 0x60, 0x2e, 0x7f, 0x99, 0x80, 0xd8, 0x8b, 0x40, + 0xd5, +}; + +static const uint8_t unicode_prop_Regional_Indicator_table[4] = { + 0x61, 0xf1, 0xe5, 0x99, +}; + +static const uint8_t unicode_prop_Sentence_Terminal_table[196] = { + 0xa0, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0x45, 0x48, + 0x80, 0x40, 0x92, 0x82, 0x40, 0xb3, 0x80, 0xaa, + 0x82, 0x40, 0xf5, 0x80, 0xbc, 0x00, 0x02, 0x81, + 0x41, 0x24, 0x81, 0x46, 0xe3, 0x81, 0x43, 0x15, + 0x03, 0x81, 0x43, 0x04, 0x80, 0x40, 0xc5, 0x81, + 0x40, 0xcb, 0x04, 0x80, 0x41, 0x39, 0x81, 0x41, + 0x61, 0x83, 0x40, 0xad, 0x09, 0x81, 0x9c, 0x81, + 0x40, 0xbb, 0x81, 0xc0, 0x81, 0x43, 0xbb, 0x81, + 0x88, 0x82, 0x4d, 0xe3, 0x80, 0x8c, 0x80, 0x95, + 0x81, 0x41, 0xac, 0x80, 0x60, 0x74, 0xfb, 0x80, + 0x41, 0x0d, 0x81, 0x40, 0xe2, 0x02, 0x80, 0x41, + 0x7d, 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, 0x97, + 0x81, 0x40, 0x92, 0x82, 0x40, 0x8f, 0x81, 0x40, + 0xf8, 0x80, 0x60, 0x52, 0x65, 0x02, 0x81, 0x40, + 0xa8, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0xc0, 0x80, + 0x4a, 0xf3, 0x81, 0x44, 0xfc, 0x84, 0xab, 0x83, + 0x40, 0xbc, 0x81, 0xf4, 0x83, 0xfe, 0x82, 0x40, + 0x80, 0x0d, 0x80, 0x8f, 0x81, 0xd7, 0x08, 0x81, + 0xeb, 0x80, 0x41, 0xa0, 0x81, 0x41, 0x74, 0x0c, + 0x8e, 0xe8, 0x81, 0x40, 0xf8, 0x82, 0x42, 0x04, + 0x00, 0x80, 0x40, 0xfa, 0x81, 0xd6, 0x81, 0x41, + 0xa3, 0x81, 0x42, 0xb3, 0x81, 0xc9, 0x81, 0x60, + 0x4b, 0x28, 0x81, 0x40, 0x84, 0x80, 0xc0, 0x81, + 0x8a, 0x80, 0x43, 0x52, 0x80, 0x60, 0x4e, 0x05, + 0x80, 0x5d, 0xe7, 0x80, +}; + +static const uint8_t unicode_prop_Soft_Dotted_table[79] = { + 0xe8, 0x81, 0x40, 0xc3, 0x80, 0x41, 0x18, 0x80, + 0x9d, 0x80, 0xb3, 0x80, 0x93, 0x80, 0x41, 0x3f, + 0x80, 0xe1, 0x00, 0x80, 0x59, 0x08, 0x80, 0xb2, + 0x80, 0x8c, 0x02, 0x80, 0x40, 0x83, 0x80, 0x40, + 0x9c, 0x80, 0x41, 0xa4, 0x80, 0x40, 0xd5, 0x81, + 0x4b, 0x31, 0x80, 0x61, 0xa7, 0xa4, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, + 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0x48, + 0x85, 0x80, 0x41, 0x30, 0x81, 0x99, 0x80, +}; + +static const uint8_t unicode_prop_Terminal_Punctuation_table[248] = { + 0xa0, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80, + 0x43, 0x3d, 0x07, 0x80, 0x42, 0x00, 0x80, 0xb8, + 0x80, 0xc7, 0x80, 0x8d, 0x00, 0x82, 0x40, 0xb3, + 0x80, 0xaa, 0x8a, 0x00, 0x40, 0xea, 0x81, 0xb5, + 0x8e, 0x9e, 0x80, 0x41, 0x04, 0x81, 0x44, 0xf3, + 0x81, 0x40, 0xab, 0x03, 0x85, 0x41, 0x36, 0x81, + 0x43, 0x14, 0x87, 0x43, 0x04, 0x80, 0xfb, 0x82, + 0xc6, 0x81, 0x40, 0x9c, 0x12, 0x80, 0xa6, 0x19, + 0x81, 0x41, 0x39, 0x81, 0x41, 0x61, 0x83, 0x40, + 0xad, 0x08, 0x82, 0x9c, 0x81, 0x40, 0xbb, 0x84, + 0xbd, 0x81, 0x43, 0xbb, 0x81, 0x88, 0x82, 0x4d, + 0xe3, 0x80, 0x8c, 0x03, 0x80, 0x89, 0x00, 0x0a, + 0x81, 0x41, 0xab, 0x81, 0x60, 0x74, 0xfa, 0x81, + 0x41, 0x0c, 0x82, 0x40, 0xe2, 0x84, 0x41, 0x7d, + 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, 0x96, 0x82, + 0x40, 0x92, 0x82, 0xfe, 0x80, 0x8f, 0x81, 0x40, + 0xf8, 0x80, 0x60, 0x52, 0x63, 0x10, 0x83, 0x40, + 0xa8, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80, + 0xc0, 0x01, 0x80, 0x44, 0x39, 0x80, 0xaf, 0x80, + 0x44, 0x85, 0x80, 0x40, 0xc6, 0x80, 0x41, 0x35, + 0x81, 0x40, 0x97, 0x85, 0xc3, 0x85, 0xd8, 0x83, + 0x43, 0xb7, 0x84, 0xab, 0x83, 0x40, 0xbc, 0x86, + 0xef, 0x83, 0xfe, 0x82, 0x40, 0x80, 0x0d, 0x80, + 0x8f, 0x81, 0xd7, 0x84, 0xeb, 0x80, 0x41, 0xa0, + 0x82, 0x8b, 0x81, 0x41, 0x65, 0x1a, 0x8e, 0xe8, + 0x81, 0x40, 0xf8, 0x82, 0x42, 0x04, 0x00, 0x80, + 0x40, 0xfa, 0x81, 0xd6, 0x0b, 0x81, 0x41, 0x9d, + 0x82, 0xac, 0x80, 0x42, 0x84, 0x81, 0xc9, 0x81, + 0x45, 0x2a, 0x84, 0x60, 0x45, 0xf8, 0x81, 0x40, + 0x84, 0x80, 0xc0, 0x82, 0x89, 0x80, 0x43, 0x51, + 0x81, 0x60, 0x4e, 0x05, 0x80, 0x5d, 0xe6, 0x83, +}; + +static const uint8_t unicode_prop_Unified_Ideograph_table[45] = { + 0x60, 0x33, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x51, + 0xff, 0x60, 0x5a, 0x0d, 0x08, 0x00, 0x81, 0x89, + 0x00, 0x00, 0x09, 0x82, 0x61, 0x05, 0xd5, 0x60, + 0xa6, 0xdf, 0x9f, 0x50, 0x39, 0x85, 0x40, 0xdd, + 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x54, 0x1e, + 0x53, 0x4a, 0x84, 0x50, 0x5f, +}; + +static const uint8_t unicode_prop_Variation_Selector_table[13] = { + 0x58, 0x0a, 0x10, 0x80, 0x60, 0xe5, 0xef, 0x8f, + 0x6d, 0x02, 0xef, 0x40, 0xef, +}; + +static const uint8_t unicode_prop_White_Space_table[22] = { + 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x99, 0x80, + 0x55, 0xde, 0x80, 0x49, 0x7e, 0x8a, 0x9c, 0x0c, + 0x80, 0xae, 0x80, 0x4f, 0x9f, 0x80, +}; + +static const uint8_t unicode_prop_Bidi_Mirrored_table[173] = { + 0xa7, 0x81, 0x91, 0x00, 0x80, 0x9b, 0x00, 0x80, + 0x9c, 0x00, 0x80, 0xac, 0x80, 0x8e, 0x80, 0x4e, + 0x7d, 0x83, 0x47, 0x5c, 0x81, 0x49, 0x9b, 0x81, + 0x89, 0x81, 0xb5, 0x81, 0x8d, 0x81, 0x40, 0xb0, + 0x80, 0x40, 0xbf, 0x1a, 0x2a, 0x02, 0x0a, 0x18, + 0x18, 0x00, 0x03, 0x88, 0x20, 0x80, 0x91, 0x23, + 0x88, 0x08, 0x00, 0x39, 0x9e, 0x0b, 0x20, 0x88, + 0x09, 0x92, 0x21, 0x88, 0x21, 0x0b, 0x97, 0x81, + 0x8f, 0x3b, 0x93, 0x0e, 0x81, 0x44, 0x3c, 0x8d, + 0xc9, 0x01, 0x18, 0x08, 0x14, 0x1c, 0x12, 0x8d, + 0x41, 0x92, 0x95, 0x0d, 0x80, 0x8d, 0x38, 0x35, + 0x10, 0x1c, 0x01, 0x0c, 0x18, 0x02, 0x09, 0x89, + 0x29, 0x81, 0x8b, 0x92, 0x03, 0x08, 0x00, 0x08, + 0x03, 0x21, 0x2a, 0x97, 0x81, 0x8a, 0x0b, 0x18, + 0x09, 0x0b, 0xaa, 0x0f, 0x80, 0xa7, 0x20, 0x00, + 0x14, 0x22, 0x18, 0x14, 0x00, 0x40, 0xff, 0x80, + 0x42, 0x02, 0x1a, 0x08, 0x81, 0x8d, 0x09, 0x89, + 0xaa, 0x87, 0x41, 0xaa, 0x89, 0x0f, 0x60, 0xce, + 0x3c, 0x2c, 0x81, 0x40, 0xa1, 0x81, 0x91, 0x00, + 0x80, 0x9b, 0x00, 0x80, 0x9c, 0x00, 0x00, 0x08, + 0x81, 0x60, 0xd7, 0x76, 0x80, 0xb8, 0x80, 0xb8, + 0x80, 0xb8, 0x80, 0xb8, 0x80, +}; + +static const uint8_t unicode_prop_Emoji_table[239] = { + 0xa2, 0x05, 0x04, 0x89, 0xee, 0x03, 0x80, 0x5f, + 0x8c, 0x80, 0x8b, 0x80, 0x40, 0xd7, 0x80, 0x95, + 0x80, 0xd9, 0x85, 0x8e, 0x81, 0x41, 0x6e, 0x81, + 0x8b, 0x80, 0x40, 0xa5, 0x80, 0x98, 0x8a, 0x1a, + 0x40, 0xc6, 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, + 0x88, 0x80, 0xb9, 0x18, 0x84, 0x88, 0x01, 0x01, + 0x09, 0x03, 0x01, 0x00, 0x09, 0x02, 0x02, 0x0f, + 0x14, 0x00, 0x04, 0x8b, 0x8a, 0x09, 0x00, 0x08, + 0x80, 0x91, 0x01, 0x81, 0x91, 0x28, 0x00, 0x0a, + 0x0c, 0x01, 0x0b, 0x81, 0x8a, 0x0c, 0x09, 0x04, + 0x08, 0x00, 0x81, 0x93, 0x0c, 0x28, 0x19, 0x03, + 0x01, 0x01, 0x28, 0x01, 0x00, 0x00, 0x05, 0x02, + 0x05, 0x80, 0x89, 0x81, 0x8e, 0x01, 0x03, 0x00, + 0x03, 0x10, 0x80, 0x8a, 0x81, 0xaf, 0x82, 0x88, + 0x80, 0x8d, 0x80, 0x8d, 0x80, 0x41, 0x73, 0x81, + 0x41, 0xce, 0x82, 0x92, 0x81, 0xb2, 0x03, 0x80, + 0x44, 0xd9, 0x80, 0x8b, 0x80, 0x42, 0x58, 0x00, + 0x80, 0x61, 0xbd, 0x69, 0x80, 0x40, 0xc9, 0x80, + 0x40, 0x9f, 0x81, 0x8b, 0x81, 0x8d, 0x01, 0x89, + 0xca, 0x99, 0x01, 0x96, 0x80, 0x93, 0x01, 0x88, + 0x94, 0x81, 0x40, 0xad, 0xa1, 0x81, 0xef, 0x09, + 0x02, 0x81, 0xd2, 0x0a, 0x80, 0x41, 0x06, 0x80, + 0xbe, 0x8a, 0x28, 0x97, 0x31, 0x0f, 0x8b, 0x01, + 0x19, 0x03, 0x81, 0x8c, 0x09, 0x07, 0x81, 0x88, + 0x04, 0x82, 0x8b, 0x17, 0x11, 0x00, 0x03, 0x05, + 0x02, 0x05, 0xd5, 0xaf, 0xc5, 0x27, 0x0a, 0x83, + 0x89, 0x10, 0x01, 0x10, 0x81, 0x89, 0x40, 0xe2, + 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, 0x89, 0x80, + 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x88, 0x86, 0xad, + 0x06, 0x87, 0x8d, 0x83, 0x88, 0x86, 0x88, +}; + +static const uint8_t unicode_prop_Emoji_Component_table[28] = { + 0xa2, 0x05, 0x04, 0x89, 0x5f, 0xd2, 0x80, 0x40, + 0xd4, 0x80, 0x60, 0xdd, 0x2a, 0x80, 0x60, 0xf3, + 0xd5, 0x99, 0x41, 0xfa, 0x84, 0x45, 0xaf, 0x83, + 0x6c, 0x06, 0x6b, 0xdf, +}; + +static const uint8_t unicode_prop_Emoji_Modifier_table[4] = { + 0x61, 0xf3, 0xfa, 0x84, +}; + +static const uint8_t unicode_prop_Emoji_Modifier_Base_table[71] = { + 0x60, 0x26, 0x1c, 0x80, 0x40, 0xda, 0x80, 0x8f, + 0x83, 0x61, 0xcc, 0x76, 0x80, 0xbb, 0x11, 0x01, + 0x82, 0xf4, 0x09, 0x8a, 0x94, 0x92, 0x10, 0x1a, + 0x02, 0x30, 0x00, 0x97, 0x80, 0x40, 0xc8, 0x0b, + 0x80, 0x94, 0x03, 0x81, 0x40, 0xad, 0x12, 0x84, + 0xd2, 0x80, 0x8f, 0x82, 0x88, 0x80, 0x8a, 0x80, + 0x42, 0x3e, 0x01, 0x07, 0x3d, 0x80, 0x88, 0x89, + 0x0a, 0xb7, 0x80, 0xbc, 0x08, 0x08, 0x80, 0x90, + 0x10, 0x8c, 0x40, 0xe4, 0x82, 0xa9, 0x88, +}; + +static const uint8_t unicode_prop_Emoji_Presentation_table[145] = { + 0x60, 0x23, 0x19, 0x81, 0x40, 0xcc, 0x1a, 0x01, + 0x80, 0x42, 0x08, 0x81, 0x94, 0x81, 0xb1, 0x8b, + 0xaa, 0x80, 0x92, 0x80, 0x8c, 0x07, 0x81, 0x90, + 0x0c, 0x0f, 0x04, 0x80, 0x94, 0x06, 0x08, 0x03, + 0x01, 0x06, 0x03, 0x81, 0x9b, 0x80, 0xa2, 0x00, + 0x03, 0x10, 0x80, 0xbc, 0x82, 0x97, 0x80, 0x8d, + 0x80, 0x43, 0x5a, 0x81, 0xb2, 0x03, 0x80, 0x61, + 0xc4, 0xad, 0x80, 0x40, 0xc9, 0x80, 0x40, 0xbd, + 0x01, 0x89, 0xca, 0x99, 0x00, 0x97, 0x80, 0x93, + 0x01, 0x20, 0x82, 0x94, 0x81, 0x40, 0xad, 0xa0, + 0x8b, 0x88, 0x80, 0xc5, 0x80, 0x95, 0x8b, 0xaa, + 0x1c, 0x8b, 0x90, 0x10, 0x82, 0xc6, 0x00, 0x80, + 0x40, 0xba, 0x81, 0xbe, 0x8c, 0x18, 0x97, 0x91, + 0x80, 0x99, 0x81, 0x8c, 0x80, 0xd5, 0xd4, 0xaf, + 0xc5, 0x28, 0x12, 0x0a, 0x1b, 0x8a, 0x0e, 0x88, + 0x40, 0xe2, 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, + 0x89, 0x80, 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x88, + 0x86, 0xad, 0x06, 0x87, 0x8d, 0x83, 0x88, 0x86, + 0x88, +}; + +static const uint8_t unicode_prop_Extended_Pictographic_table[156] = { + 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b, + 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85, + 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0xde, + 0x80, 0xc5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6, + 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80, + 0xb9, 0x18, 0x28, 0x8b, 0x80, 0xf1, 0x89, 0xf5, + 0x81, 0x8a, 0x00, 0x00, 0x28, 0x10, 0x28, 0x89, + 0x81, 0x8e, 0x01, 0x03, 0x00, 0x03, 0x10, 0x80, + 0x8a, 0x84, 0xac, 0x82, 0x88, 0x80, 0x8d, 0x80, + 0x8d, 0x80, 0x41, 0x73, 0x81, 0x41, 0xce, 0x82, + 0x92, 0x81, 0xb2, 0x03, 0x80, 0x44, 0xd9, 0x80, + 0x8b, 0x80, 0x42, 0x58, 0x00, 0x80, 0x61, 0xbd, + 0x65, 0x40, 0xff, 0x8c, 0x82, 0x9e, 0x80, 0xbb, + 0x85, 0x8b, 0x81, 0x8d, 0x01, 0x89, 0x91, 0xb8, + 0x9a, 0x8e, 0x89, 0x80, 0x93, 0x01, 0x88, 0x03, + 0x88, 0x41, 0xb1, 0x84, 0x41, 0x3d, 0x87, 0x41, + 0x09, 0xaf, 0xff, 0xf3, 0x8b, 0xd4, 0xaa, 0x8b, + 0x83, 0xb7, 0x87, 0x89, 0x85, 0xa7, 0x87, 0x9d, + 0xd1, 0x8b, 0xae, 0x80, 0x89, 0x80, 0x41, 0xb8, + 0x40, 0xff, 0x43, 0xfd, +}; + +static const uint8_t unicode_prop_Default_Ignorable_Code_Point_table[51] = { + 0x40, 0xac, 0x80, 0x42, 0xa0, 0x80, 0x42, 0xcb, + 0x80, 0x4b, 0x41, 0x81, 0x46, 0x52, 0x81, 0xd4, + 0x84, 0x47, 0xfa, 0x84, 0x99, 0x84, 0xb0, 0x8f, + 0x50, 0xf3, 0x80, 0x60, 0xcc, 0x9a, 0x8f, 0x40, + 0xee, 0x80, 0x40, 0x9f, 0x80, 0xce, 0x88, 0x60, + 0xbc, 0xa6, 0x83, 0x54, 0xce, 0x87, 0x6c, 0x2e, + 0x84, 0x4f, 0xff, +}; + +typedef enum { + UNICODE_PROP_Hyphen, + UNICODE_PROP_Other_Math, + UNICODE_PROP_Other_Alphabetic, + UNICODE_PROP_Other_Lowercase, + UNICODE_PROP_Other_Uppercase, + UNICODE_PROP_Other_Grapheme_Extend, + UNICODE_PROP_Other_Default_Ignorable_Code_Point, + UNICODE_PROP_Other_ID_Start, + UNICODE_PROP_Other_ID_Continue, + UNICODE_PROP_Prepended_Concatenation_Mark, + UNICODE_PROP_ID_Continue1, + UNICODE_PROP_XID_Start1, + UNICODE_PROP_XID_Continue1, + UNICODE_PROP_Changes_When_Titlecased1, + UNICODE_PROP_Changes_When_Casefolded1, + UNICODE_PROP_Changes_When_NFKC_Casefolded1, + UNICODE_PROP_ASCII_Hex_Digit, + UNICODE_PROP_Bidi_Control, + UNICODE_PROP_Dash, + UNICODE_PROP_Deprecated, + UNICODE_PROP_Diacritic, + UNICODE_PROP_Extender, + UNICODE_PROP_Hex_Digit, + UNICODE_PROP_IDS_Binary_Operator, + UNICODE_PROP_IDS_Trinary_Operator, + UNICODE_PROP_Ideographic, + UNICODE_PROP_Join_Control, + UNICODE_PROP_Logical_Order_Exception, + UNICODE_PROP_Noncharacter_Code_Point, + UNICODE_PROP_Pattern_Syntax, + UNICODE_PROP_Pattern_White_Space, + UNICODE_PROP_Quotation_Mark, + UNICODE_PROP_Radical, + UNICODE_PROP_Regional_Indicator, + UNICODE_PROP_Sentence_Terminal, + UNICODE_PROP_Soft_Dotted, + UNICODE_PROP_Terminal_Punctuation, + UNICODE_PROP_Unified_Ideograph, + UNICODE_PROP_Variation_Selector, + UNICODE_PROP_White_Space, + UNICODE_PROP_Bidi_Mirrored, + UNICODE_PROP_Emoji, + UNICODE_PROP_Emoji_Component, + UNICODE_PROP_Emoji_Modifier, + UNICODE_PROP_Emoji_Modifier_Base, + UNICODE_PROP_Emoji_Presentation, + UNICODE_PROP_Extended_Pictographic, + UNICODE_PROP_Default_Ignorable_Code_Point, + UNICODE_PROP_ID_Start, + UNICODE_PROP_Case_Ignorable, + UNICODE_PROP_ASCII, + UNICODE_PROP_Alphabetic, + UNICODE_PROP_Any, + UNICODE_PROP_Assigned, + UNICODE_PROP_Cased, + UNICODE_PROP_Changes_When_Casefolded, + UNICODE_PROP_Changes_When_Casemapped, + UNICODE_PROP_Changes_When_Lowercased, + UNICODE_PROP_Changes_When_NFKC_Casefolded, + UNICODE_PROP_Changes_When_Titlecased, + UNICODE_PROP_Changes_When_Uppercased, + UNICODE_PROP_Grapheme_Base, + UNICODE_PROP_Grapheme_Extend, + UNICODE_PROP_ID_Continue, + UNICODE_PROP_Lowercase, + UNICODE_PROP_Math, + UNICODE_PROP_Uppercase, + UNICODE_PROP_XID_Continue, + UNICODE_PROP_XID_Start, + UNICODE_PROP_Cased1, + UNICODE_PROP_COUNT, +} UnicodePropertyEnum; + +static const char unicode_prop_name_table[] = + "ASCII_Hex_Digit,AHex" "\0" + "Bidi_Control,Bidi_C" "\0" + "Dash" "\0" + "Deprecated,Dep" "\0" + "Diacritic,Dia" "\0" + "Extender,Ext" "\0" + "Hex_Digit,Hex" "\0" + "IDS_Binary_Operator,IDSB" "\0" + "IDS_Trinary_Operator,IDST" "\0" + "Ideographic,Ideo" "\0" + "Join_Control,Join_C" "\0" + "Logical_Order_Exception,LOE" "\0" + "Noncharacter_Code_Point,NChar" "\0" + "Pattern_Syntax,Pat_Syn" "\0" + "Pattern_White_Space,Pat_WS" "\0" + "Quotation_Mark,QMark" "\0" + "Radical" "\0" + "Regional_Indicator,RI" "\0" + "Sentence_Terminal,STerm" "\0" + "Soft_Dotted,SD" "\0" + "Terminal_Punctuation,Term" "\0" + "Unified_Ideograph,UIdeo" "\0" + "Variation_Selector,VS" "\0" + "White_Space,space" "\0" + "Bidi_Mirrored,Bidi_M" "\0" + "Emoji" "\0" + "Emoji_Component,EComp" "\0" + "Emoji_Modifier,EMod" "\0" + "Emoji_Modifier_Base,EBase" "\0" + "Emoji_Presentation,EPres" "\0" + "Extended_Pictographic,ExtPict" "\0" + "Default_Ignorable_Code_Point,DI" "\0" + "ID_Start,IDS" "\0" + "Case_Ignorable,CI" "\0" + "ASCII" "\0" + "Alphabetic,Alpha" "\0" + "Any" "\0" + "Assigned" "\0" + "Cased" "\0" + "Changes_When_Casefolded,CWCF" "\0" + "Changes_When_Casemapped,CWCM" "\0" + "Changes_When_Lowercased,CWL" "\0" + "Changes_When_NFKC_Casefolded,CWKCF" "\0" + "Changes_When_Titlecased,CWT" "\0" + "Changes_When_Uppercased,CWU" "\0" + "Grapheme_Base,Gr_Base" "\0" + "Grapheme_Extend,Gr_Ext" "\0" + "ID_Continue,IDC" "\0" + "Lowercase,Lower" "\0" + "Math" "\0" + "Uppercase,Upper" "\0" + "XID_Continue,XIDC" "\0" + "XID_Start,XIDS" "\0" +; + +static const uint8_t * const unicode_prop_table[] = { + unicode_prop_Hyphen_table, + unicode_prop_Other_Math_table, + unicode_prop_Other_Alphabetic_table, + unicode_prop_Other_Lowercase_table, + unicode_prop_Other_Uppercase_table, + unicode_prop_Other_Grapheme_Extend_table, + unicode_prop_Other_Default_Ignorable_Code_Point_table, + unicode_prop_Other_ID_Start_table, + unicode_prop_Other_ID_Continue_table, + unicode_prop_Prepended_Concatenation_Mark_table, + unicode_prop_ID_Continue1_table, + unicode_prop_XID_Start1_table, + unicode_prop_XID_Continue1_table, + unicode_prop_Changes_When_Titlecased1_table, + unicode_prop_Changes_When_Casefolded1_table, + unicode_prop_Changes_When_NFKC_Casefolded1_table, + unicode_prop_ASCII_Hex_Digit_table, + unicode_prop_Bidi_Control_table, + unicode_prop_Dash_table, + unicode_prop_Deprecated_table, + unicode_prop_Diacritic_table, + unicode_prop_Extender_table, + unicode_prop_Hex_Digit_table, + unicode_prop_IDS_Binary_Operator_table, + unicode_prop_IDS_Trinary_Operator_table, + unicode_prop_Ideographic_table, + unicode_prop_Join_Control_table, + unicode_prop_Logical_Order_Exception_table, + unicode_prop_Noncharacter_Code_Point_table, + unicode_prop_Pattern_Syntax_table, + unicode_prop_Pattern_White_Space_table, + unicode_prop_Quotation_Mark_table, + unicode_prop_Radical_table, + unicode_prop_Regional_Indicator_table, + unicode_prop_Sentence_Terminal_table, + unicode_prop_Soft_Dotted_table, + unicode_prop_Terminal_Punctuation_table, + unicode_prop_Unified_Ideograph_table, + unicode_prop_Variation_Selector_table, + unicode_prop_White_Space_table, + unicode_prop_Bidi_Mirrored_table, + unicode_prop_Emoji_table, + unicode_prop_Emoji_Component_table, + unicode_prop_Emoji_Modifier_table, + unicode_prop_Emoji_Modifier_Base_table, + unicode_prop_Emoji_Presentation_table, + unicode_prop_Extended_Pictographic_table, + unicode_prop_Default_Ignorable_Code_Point_table, + unicode_prop_ID_Start_table, + unicode_prop_Case_Ignorable_table, +}; + +static const uint16_t unicode_prop_len_table[] = { + countof(unicode_prop_Hyphen_table), + countof(unicode_prop_Other_Math_table), + countof(unicode_prop_Other_Alphabetic_table), + countof(unicode_prop_Other_Lowercase_table), + countof(unicode_prop_Other_Uppercase_table), + countof(unicode_prop_Other_Grapheme_Extend_table), + countof(unicode_prop_Other_Default_Ignorable_Code_Point_table), + countof(unicode_prop_Other_ID_Start_table), + countof(unicode_prop_Other_ID_Continue_table), + countof(unicode_prop_Prepended_Concatenation_Mark_table), + countof(unicode_prop_ID_Continue1_table), + countof(unicode_prop_XID_Start1_table), + countof(unicode_prop_XID_Continue1_table), + countof(unicode_prop_Changes_When_Titlecased1_table), + countof(unicode_prop_Changes_When_Casefolded1_table), + countof(unicode_prop_Changes_When_NFKC_Casefolded1_table), + countof(unicode_prop_ASCII_Hex_Digit_table), + countof(unicode_prop_Bidi_Control_table), + countof(unicode_prop_Dash_table), + countof(unicode_prop_Deprecated_table), + countof(unicode_prop_Diacritic_table), + countof(unicode_prop_Extender_table), + countof(unicode_prop_Hex_Digit_table), + countof(unicode_prop_IDS_Binary_Operator_table), + countof(unicode_prop_IDS_Trinary_Operator_table), + countof(unicode_prop_Ideographic_table), + countof(unicode_prop_Join_Control_table), + countof(unicode_prop_Logical_Order_Exception_table), + countof(unicode_prop_Noncharacter_Code_Point_table), + countof(unicode_prop_Pattern_Syntax_table), + countof(unicode_prop_Pattern_White_Space_table), + countof(unicode_prop_Quotation_Mark_table), + countof(unicode_prop_Radical_table), + countof(unicode_prop_Regional_Indicator_table), + countof(unicode_prop_Sentence_Terminal_table), + countof(unicode_prop_Soft_Dotted_table), + countof(unicode_prop_Terminal_Punctuation_table), + countof(unicode_prop_Unified_Ideograph_table), + countof(unicode_prop_Variation_Selector_table), + countof(unicode_prop_White_Space_table), + countof(unicode_prop_Bidi_Mirrored_table), + countof(unicode_prop_Emoji_table), + countof(unicode_prop_Emoji_Component_table), + countof(unicode_prop_Emoji_Modifier_table), + countof(unicode_prop_Emoji_Modifier_Base_table), + countof(unicode_prop_Emoji_Presentation_table), + countof(unicode_prop_Extended_Pictographic_table), + countof(unicode_prop_Default_Ignorable_Code_Point_table), + countof(unicode_prop_ID_Start_table), + countof(unicode_prop_Case_Ignorable_table), +}; diff --git a/armorcore/sources/libs/quickjs/libunicode.c b/armorcore/sources/libs/quickjs/libunicode.c new file mode 100644 index 000000000..f0b514691 --- /dev/null +++ b/armorcore/sources/libs/quickjs/libunicode.c @@ -0,0 +1,1504 @@ +/* + * Unicode utilities + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include + +#include "cutils.h" +#include "libunicode.h" +#include "libunicode-table.h" + +enum { + RUN_TYPE_U, + RUN_TYPE_L, + RUN_TYPE_UF, + RUN_TYPE_LF, + RUN_TYPE_UL, + RUN_TYPE_LSU, + RUN_TYPE_U2L_399_EXT2, + RUN_TYPE_UF_D20, + RUN_TYPE_UF_D1_EXT, + RUN_TYPE_U_EXT, + RUN_TYPE_LF_EXT, + RUN_TYPE_U_EXT2, + RUN_TYPE_L_EXT2, + RUN_TYPE_U_EXT3, +}; + +/* conv_type: + 0 = to upper + 1 = to lower + 2 = case folding (= to lower with modifications) +*/ +int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) +{ + if (c < 128) { + if (conv_type) { + if (c >= 'A' && c <= 'Z') { + c = c - 'A' + 'a'; + } + } else { + if (c >= 'a' && c <= 'z') { + c = c - 'a' + 'A'; + } + } + } else { + uint32_t v, code, data, type, len, a, is_lower; + int idx, idx_min, idx_max; + + is_lower = (conv_type != 0); + idx_min = 0; + idx_max = countof(case_conv_table1) - 1; + while (idx_min <= idx_max) { + idx = (unsigned)(idx_max + idx_min) / 2; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + type = (v >> (32 - 17 - 7 - 4)) & 0xf; + data = ((v & 0xf) << 8) | case_conv_table2[idx]; + switch(type) { + case RUN_TYPE_U: + case RUN_TYPE_L: + case RUN_TYPE_UF: + case RUN_TYPE_LF: + if (conv_type == (type & 1) || + (type >= RUN_TYPE_UF && conv_type == 2)) { + c = c - code + (case_conv_table1[data] >> (32 - 17)); + } + break; + case RUN_TYPE_UL: + a = c - code; + if ((a & 1) != (1 - is_lower)) + break; + c = (a ^ 1) + code; + break; + case RUN_TYPE_LSU: + a = c - code; + if (a == 1) { + c += 2 * is_lower - 1; + } else if (a == (1 - is_lower) * 2) { + c += (2 * is_lower - 1) * 2; + } + break; + case RUN_TYPE_U2L_399_EXT2: + if (!is_lower) { + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = 0x399; + return 2; + } else { + c = c - code + case_conv_ext[data & 0x3f]; + } + break; + case RUN_TYPE_UF_D20: + if (conv_type == 1) + break; + c = data + (conv_type == 2) * 0x20; + break; + case RUN_TYPE_UF_D1_EXT: + if (conv_type == 1) + break; + c = case_conv_ext[data] + (conv_type == 2); + break; + case RUN_TYPE_U_EXT: + case RUN_TYPE_LF_EXT: + if (is_lower != (type - RUN_TYPE_U_EXT)) + break; + c = case_conv_ext[data]; + break; + case RUN_TYPE_U_EXT2: + case RUN_TYPE_L_EXT2: + if (conv_type != (type - RUN_TYPE_U_EXT2)) + break; + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = case_conv_ext[data & 0x3f]; + return 2; + default: + case RUN_TYPE_U_EXT3: + if (conv_type != 0) + break; + res[0] = case_conv_ext[data >> 8]; + res[1] = case_conv_ext[(data >> 4) & 0xf]; + res[2] = case_conv_ext[data & 0xf]; + return 3; + } + break; + } + } + } + res[0] = c; + return 1; +} + +static uint32_t get_le24(const uint8_t *ptr) +{ + return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16); +} + +#define UNICODE_INDEX_BLOCK_LEN 32 + +/* return -1 if not in table, otherwise the offset in the block */ +static int get_index_pos(uint32_t *pcode, uint32_t c, + const uint8_t *index_table, int index_table_len) +{ + uint32_t code, v; + int idx_min, idx_max, idx; + + idx_min = 0; + v = get_le24(index_table); + code = v & ((1 << 21) - 1); + if (c < code) { + *pcode = 0; + return 0; + } + idx_max = index_table_len - 1; + code = get_le24(index_table + idx_max * 3); + if (c >= code) + return -1; + /* invariant: tab[idx_min] <= c < tab2[idx_max] */ + while ((idx_max - idx_min) > 1) { + idx = (idx_max + idx_min) / 2; + v = get_le24(index_table + idx * 3); + code = v & ((1 << 21) - 1); + if (c < code) { + idx_max = idx; + } else { + idx_min = idx; + } + } + v = get_le24(index_table + idx_min * 3); + *pcode = v & ((1 << 21) - 1); + return (idx_min + 1) * UNICODE_INDEX_BLOCK_LEN + (v >> 21); +} + +static BOOL lre_is_in_table(uint32_t c, const uint8_t *table, + const uint8_t *index_table, int index_table_len) +{ + uint32_t code, b, bit; + int pos; + const uint8_t *p; + + pos = get_index_pos(&code, c, index_table, index_table_len); + if (pos < 0) + return FALSE; /* outside the table */ + p = table + pos; + bit = 0; + for(;;) { + b = *p++; + if (b < 64) { + code += (b >> 3) + 1; + if (c < code) + return bit; + bit ^= 1; + code += (b & 7) + 1; + } else if (b >= 0x80) { + code += b - 0x80 + 1; + } else if (b < 0x60) { + code += (((b - 0x40) << 8) | p[0]) + 1; + p++; + } else { + code += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; + p += 2; + } + if (c < code) + return bit; + bit ^= 1; + } +} + +BOOL lre_is_cased(uint32_t c) +{ + uint32_t v, code, len; + int idx, idx_min, idx_max; + + idx_min = 0; + idx_max = countof(case_conv_table1) - 1; + while (idx_min <= idx_max) { + idx = (unsigned)(idx_max + idx_min) / 2; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + return TRUE; + } + } + return lre_is_in_table(c, unicode_prop_Cased1_table, + unicode_prop_Cased1_index, + sizeof(unicode_prop_Cased1_index) / 3); +} + +BOOL lre_is_case_ignorable(uint32_t c) +{ + return lre_is_in_table(c, unicode_prop_Case_Ignorable_table, + unicode_prop_Case_Ignorable_index, + sizeof(unicode_prop_Case_Ignorable_index) / 3); +} + +/* character range */ + +static __maybe_unused void cr_dump(CharRange *cr) +{ + int i; + for(i = 0; i < cr->len; i++) + printf("%d: 0x%04x\n", i, cr->points[i]); +} + +static void *cr_default_realloc(void *opaque, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +void cr_init(CharRange *cr, void *mem_opaque, DynBufReallocFunc *realloc_func) +{ + cr->len = cr->size = 0; + cr->points = NULL; + cr->mem_opaque = mem_opaque; + cr->realloc_func = realloc_func ? realloc_func : cr_default_realloc; +} + +void cr_free(CharRange *cr) +{ + cr->realloc_func(cr->mem_opaque, cr->points, 0); +} + +int cr_realloc(CharRange *cr, int size) +{ + int new_size; + uint32_t *new_buf; + + if (size > cr->size) { + new_size = max_int(size, cr->size * 3 / 2); + new_buf = cr->realloc_func(cr->mem_opaque, cr->points, + new_size * sizeof(cr->points[0])); + if (!new_buf) + return -1; + cr->points = new_buf; + cr->size = new_size; + } + return 0; +} + +int cr_copy(CharRange *cr, const CharRange *cr1) +{ + if (cr_realloc(cr, cr1->len)) + return -1; + memcpy(cr->points, cr1->points, sizeof(cr->points[0]) * cr1->len); + cr->len = cr1->len; + return 0; +} + +/* merge consecutive intervals and remove empty intervals */ +static void cr_compress(CharRange *cr) +{ + int i, j, k, len; + uint32_t *pt; + + pt = cr->points; + len = cr->len; + i = 0; + j = 0; + k = 0; + while ((i + 1) < len) { + if (pt[i] == pt[i + 1]) { + /* empty interval */ + i += 2; + } else { + j = i; + while ((j + 3) < len && pt[j + 1] == pt[j + 2]) + j += 2; + /* just copy */ + pt[k] = pt[i]; + pt[k + 1] = pt[j + 1]; + k += 2; + i = j + 2; + } + } + cr->len = k; +} + +/* union or intersection */ +int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, + const uint32_t *b_pt, int b_len, int op) +{ + int a_idx, b_idx, is_in; + uint32_t v; + + a_idx = 0; + b_idx = 0; + for(;;) { + /* get one more point from a or b in increasing order */ + if (a_idx < a_len && b_idx < b_len) { + if (a_pt[a_idx] < b_pt[b_idx]) { + goto a_add; + } else if (a_pt[a_idx] == b_pt[b_idx]) { + v = a_pt[a_idx]; + a_idx++; + b_idx++; + } else { + goto b_add; + } + } else if (a_idx < a_len) { + a_add: + v = a_pt[a_idx++]; + } else if (b_idx < b_len) { + b_add: + v = b_pt[b_idx++]; + } else { + break; + } + /* add the point if the in/out status changes */ + switch(op) { + case CR_OP_UNION: + is_in = (a_idx & 1) | (b_idx & 1); + break; + case CR_OP_INTER: + is_in = (a_idx & 1) & (b_idx & 1); + break; + case CR_OP_XOR: + is_in = (a_idx & 1) ^ (b_idx & 1); + break; + default: + abort(); + } + if (is_in != (cr->len & 1)) { + if (cr_add_point(cr, v)) + return -1; + } + } + cr_compress(cr); + return 0; +} + +int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len) +{ + CharRange a = *cr; + int ret; + cr->len = 0; + cr->size = 0; + cr->points = NULL; + ret = cr_op(cr, a.points, a.len, b_pt, b_len, CR_OP_UNION); + cr_free(&a); + return ret; +} + +int cr_invert(CharRange *cr) +{ + int len; + len = cr->len; + if (cr_realloc(cr, len + 2)) + return -1; + memmove(cr->points + 1, cr->points, len * sizeof(cr->points[0])); + cr->points[0] = 0; + cr->points[len + 1] = UINT32_MAX; + cr->len = len + 2; + cr_compress(cr); + return 0; +} + +BOOL lre_is_id_start(uint32_t c) +{ + return lre_is_in_table(c, unicode_prop_ID_Start_table, + unicode_prop_ID_Start_index, + sizeof(unicode_prop_ID_Start_index) / 3); +} + +BOOL lre_is_id_continue(uint32_t c) +{ + return lre_is_id_start(c) || + lre_is_in_table(c, unicode_prop_ID_Continue1_table, + unicode_prop_ID_Continue1_index, + sizeof(unicode_prop_ID_Continue1_index) / 3); +} + +#define UNICODE_DECOMP_LEN_MAX 18 + +typedef enum { + DECOMP_TYPE_C1, /* 16 bit char */ + DECOMP_TYPE_L1, /* 16 bit char table */ + DECOMP_TYPE_L2, + DECOMP_TYPE_L3, + DECOMP_TYPE_L4, + DECOMP_TYPE_L5, /* XXX: not used */ + DECOMP_TYPE_L6, /* XXX: could remove */ + DECOMP_TYPE_L7, /* XXX: could remove */ + DECOMP_TYPE_LL1, /* 18 bit char table */ + DECOMP_TYPE_LL2, + DECOMP_TYPE_S1, /* 8 bit char table */ + DECOMP_TYPE_S2, + DECOMP_TYPE_S3, + DECOMP_TYPE_S4, + DECOMP_TYPE_S5, + DECOMP_TYPE_I1, /* increment 16 bit char value */ + DECOMP_TYPE_I2_0, + DECOMP_TYPE_I2_1, + DECOMP_TYPE_I3_1, + DECOMP_TYPE_I3_2, + DECOMP_TYPE_I4_1, + DECOMP_TYPE_I4_2, + DECOMP_TYPE_B1, /* 16 bit base + 8 bit offset */ + DECOMP_TYPE_B2, + DECOMP_TYPE_B3, + DECOMP_TYPE_B4, + DECOMP_TYPE_B5, + DECOMP_TYPE_B6, + DECOMP_TYPE_B7, + DECOMP_TYPE_B8, + DECOMP_TYPE_B18, + DECOMP_TYPE_LS2, + DECOMP_TYPE_PAT3, + DECOMP_TYPE_S2_UL, + DECOMP_TYPE_LS2_UL, +} DecompTypeEnum; + +static uint32_t unicode_get_short_code(uint32_t c) +{ + static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 }; + + if (c < 0x80) + return c; + else if (c < 0x80 + 0x50) + return c - 0x80 + 0x300; + else + return unicode_short_table[c - 0x80 - 0x50]; +} + +static uint32_t unicode_get_lower_simple(uint32_t c) +{ + if (c < 0x100 || (c >= 0x410 && c <= 0x42f)) + c += 0x20; + else + c++; + return c; +} + +static uint16_t unicode_get16(const uint8_t *p) +{ + return p[0] | (p[1] << 8); +} + +static int unicode_decomp_entry(uint32_t *res, uint32_t c, + int idx, uint32_t code, uint32_t len, + uint32_t type) +{ + uint32_t c1; + int l, i, p; + const uint8_t *d; + + if (type == DECOMP_TYPE_C1) { + res[0] = unicode_decomp_table2[idx]; + return 1; + } else { + d = unicode_decomp_data + unicode_decomp_table2[idx]; + switch(type) { + case DECOMP_TYPE_L1: + case DECOMP_TYPE_L2: + case DECOMP_TYPE_L3: + case DECOMP_TYPE_L4: + case DECOMP_TYPE_L5: + case DECOMP_TYPE_L6: + case DECOMP_TYPE_L7: + l = type - DECOMP_TYPE_L1 + 1; + d += (c - code) * l * 2; + for(i = 0; i < l; i++) { + if ((res[i] = unicode_get16(d + 2 * i)) == 0) + return 0; + } + return l; + case DECOMP_TYPE_LL1: + case DECOMP_TYPE_LL2: + { + uint32_t k, p; + l = type - DECOMP_TYPE_LL1 + 1; + k = (c - code) * l; + p = len * l * 2; + for(i = 0; i < l; i++) { + c1 = unicode_get16(d + 2 * k) | + (((d[p + (k / 4)] >> ((k % 4) * 2)) & 3) << 16); + if (!c1) + return 0; + res[i] = c1; + k++; + } + } + return l; + case DECOMP_TYPE_S1: + case DECOMP_TYPE_S2: + case DECOMP_TYPE_S3: + case DECOMP_TYPE_S4: + case DECOMP_TYPE_S5: + l = type - DECOMP_TYPE_S1 + 1; + d += (c - code) * l; + for(i = 0; i < l; i++) { + if ((res[i] = unicode_get_short_code(d[i])) == 0) + return 0; + } + return l; + case DECOMP_TYPE_I1: + l = 1; + p = 0; + goto decomp_type_i; + case DECOMP_TYPE_I2_0: + case DECOMP_TYPE_I2_1: + case DECOMP_TYPE_I3_1: + case DECOMP_TYPE_I3_2: + case DECOMP_TYPE_I4_1: + case DECOMP_TYPE_I4_2: + l = 2 + ((type - DECOMP_TYPE_I2_0) >> 1); + p = ((type - DECOMP_TYPE_I2_0) & 1) + (l > 2); + decomp_type_i: + for(i = 0; i < l; i++) { + c1 = unicode_get16(d + 2 * i); + if (i == p) + c1 += c - code; + res[i] = c1; + } + return l; + case DECOMP_TYPE_B18: + l = 18; + goto decomp_type_b; + case DECOMP_TYPE_B1: + case DECOMP_TYPE_B2: + case DECOMP_TYPE_B3: + case DECOMP_TYPE_B4: + case DECOMP_TYPE_B5: + case DECOMP_TYPE_B6: + case DECOMP_TYPE_B7: + case DECOMP_TYPE_B8: + l = type - DECOMP_TYPE_B1 + 1; + decomp_type_b: + { + uint32_t c_min; + c_min = unicode_get16(d); + d += 2 + (c - code) * l; + for(i = 0; i < l; i++) { + c1 = d[i]; + if (c1 == 0xff) + c1 = 0x20; + else + c1 += c_min; + res[i] = c1; + } + } + return l; + case DECOMP_TYPE_LS2: + d += (c - code) * 3; + if (!(res[0] = unicode_get16(d))) + return 0; + res[1] = unicode_get_short_code(d[2]); + return 2; + case DECOMP_TYPE_PAT3: + res[0] = unicode_get16(d); + res[2] = unicode_get16(d + 2); + d += 4 + (c - code) * 2; + res[1] = unicode_get16(d); + return 3; + case DECOMP_TYPE_S2_UL: + case DECOMP_TYPE_LS2_UL: + c1 = c - code; + if (type == DECOMP_TYPE_S2_UL) { + d += c1 & ~1; + c = unicode_get_short_code(*d); + d++; + } else { + d += (c1 >> 1) * 3; + c = unicode_get16(d); + d += 2; + } + if (c1 & 1) + c = unicode_get_lower_simple(c); + res[0] = c; + res[1] = unicode_get_short_code(*d); + return 2; + } + } + return 0; +} + + +/* return the length of the decomposition (length <= + UNICODE_DECOMP_LEN_MAX) or 0 if no decomposition */ +static int unicode_decomp_char(uint32_t *res, uint32_t c, BOOL is_compat1) +{ + uint32_t v, type, is_compat, code, len; + int idx_min, idx_max, idx; + + idx_min = 0; + idx_max = countof(unicode_decomp_table1) - 1; + while (idx_min <= idx_max) { + idx = (idx_max + idx_min) / 2; + v = unicode_decomp_table1[idx]; + code = v >> (32 - 18); + len = (v >> (32 - 18 - 7)) & 0x7f; + // printf("idx=%d code=%05x len=%d\n", idx, code, len); + if (c < code) { + idx_max = idx - 1; + } else if (c >= code + len) { + idx_min = idx + 1; + } else { + is_compat = v & 1; + if (is_compat1 < is_compat) + break; + type = (v >> (32 - 18 - 7 - 6)) & 0x3f; + return unicode_decomp_entry(res, c, idx, code, len, type); + } + } + return 0; +} + +/* return 0 if no pair found */ +static int unicode_compose_pair(uint32_t c0, uint32_t c1) +{ + uint32_t code, len, type, v, idx1, d_idx, d_offset, ch; + int idx_min, idx_max, idx, d; + uint32_t pair[2]; + + idx_min = 0; + idx_max = countof(unicode_comp_table) - 1; + while (idx_min <= idx_max) { + idx = (idx_max + idx_min) / 2; + idx1 = unicode_comp_table[idx]; + + /* idx1 represent an entry of the decomposition table */ + d_idx = idx1 >> 6; + d_offset = idx1 & 0x3f; + v = unicode_decomp_table1[d_idx]; + code = v >> (32 - 18); + len = (v >> (32 - 18 - 7)) & 0x7f; + type = (v >> (32 - 18 - 7 - 6)) & 0x3f; + ch = code + d_offset; + unicode_decomp_entry(pair, ch, d_idx, code, len, type); + d = c0 - pair[0]; + if (d == 0) + d = c1 - pair[1]; + if (d < 0) { + idx_max = idx - 1; + } else if (d > 0) { + idx_min = idx + 1; + } else { + return ch; + } + } + return 0; +} + +/* return the combining class of character c (between 0 and 255) */ +static int unicode_get_cc(uint32_t c) +{ + uint32_t code, n, type, cc, c1, b; + int pos; + const uint8_t *p; + + pos = get_index_pos(&code, c, + unicode_cc_index, sizeof(unicode_cc_index) / 3); + if (pos < 0) + return 0; + p = unicode_cc_table + pos; + for(;;) { + b = *p++; + type = b >> 6; + n = b & 0x3f; + if (n < 48) { + } else if (n < 56) { + n = (n - 48) << 8; + n |= *p++; + n += 48; + } else { + n = (n - 56) << 8; + n |= *p++ << 8; + n |= *p++; + n += 48 + (1 << 11); + } + if (type <= 1) + p++; + c1 = code + n + 1; + if (c < c1) { + switch(type) { + case 0: + cc = p[-1]; + break; + case 1: + cc = p[-1] + c - code; + break; + case 2: + cc = 0; + break; + default: + case 3: + cc = 230; + break; + } + return cc; + } + code = c1; + } +} + +static void sort_cc(int *buf, int len) +{ + int i, j, k, cc, cc1, start, ch1; + + for(i = 0; i < len; i++) { + cc = unicode_get_cc(buf[i]); + if (cc != 0) { + start = i; + j = i + 1; + while (j < len) { + ch1 = buf[j]; + cc1 = unicode_get_cc(ch1); + if (cc1 == 0) + break; + k = j - 1; + while (k >= start) { + if (unicode_get_cc(buf[k]) <= cc1) + break; + buf[k + 1] = buf[k]; + k--; + } + buf[k + 1] = ch1; + j++; + } + i = j; + } + } +} + +static void to_nfd_rec(DynBuf *dbuf, + const int *src, int src_len, int is_compat) +{ + uint32_t c, v; + int i, l; + uint32_t res[UNICODE_DECOMP_LEN_MAX]; + + for(i = 0; i < src_len; i++) { + c = src[i]; + if (c >= 0xac00 && c < 0xd7a4) { + /* Hangul decomposition */ + c -= 0xac00; + dbuf_put_u32(dbuf, 0x1100 + c / 588); + dbuf_put_u32(dbuf, 0x1161 + (c % 588) / 28); + v = c % 28; + if (v != 0) + dbuf_put_u32(dbuf, 0x11a7 + v); + } else { + l = unicode_decomp_char(res, c, is_compat); + if (l) { + to_nfd_rec(dbuf, (int *)res, l, is_compat); + } else { + dbuf_put_u32(dbuf, c); + } + } + } +} + +/* return 0 if not found */ +static int compose_pair(uint32_t c0, uint32_t c1) +{ + /* Hangul composition */ + if (c0 >= 0x1100 && c0 < 0x1100 + 19 && + c1 >= 0x1161 && c1 < 0x1161 + 21) { + return 0xac00 + (c0 - 0x1100) * 588 + (c1 - 0x1161) * 28; + } else if (c0 >= 0xac00 && c0 < 0xac00 + 11172 && + (c0 - 0xac00) % 28 == 0 && + c1 >= 0x11a7 && c1 < 0x11a7 + 28) { + return c0 + c1 - 0x11a7; + } else { + return unicode_compose_pair(c0, c1); + } +} + +int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, + UnicodeNormalizationEnum n_type, + void *opaque, DynBufReallocFunc *realloc_func) +{ + int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len; + BOOL is_compat; + DynBuf dbuf_s, *dbuf = &dbuf_s; + + is_compat = n_type >> 1; + + dbuf_init2(dbuf, opaque, realloc_func); + if (dbuf_realloc(dbuf, sizeof(int) * src_len)) + goto fail; + + /* common case: latin1 is unaffected by NFC */ + if (n_type == UNICODE_NFC) { + for(i = 0; i < src_len; i++) { + if (src[i] >= 0x100) + goto not_latin1; + } + buf = (int *)dbuf->buf; + memcpy(buf, src, src_len * sizeof(int)); + *pdst = (uint32_t *)buf; + return src_len; + not_latin1: ; + } + + to_nfd_rec(dbuf, (const int *)src, src_len, is_compat); + if (dbuf_error(dbuf)) { + fail: + *pdst = NULL; + return -1; + } + buf = (int *)dbuf->buf; + buf_len = dbuf->size / sizeof(int); + + sort_cc(buf, buf_len); + + if (buf_len <= 1 || (n_type & 1) != 0) { + /* NFD / NFKD */ + *pdst = (uint32_t *)buf; + return buf_len; + } + + i = 1; + out_len = 1; + while (i < buf_len) { + /* find the starter character and test if it is blocked from + the character at 'i' */ + last_cc = unicode_get_cc(buf[i]); + starter_pos = out_len - 1; + while (starter_pos >= 0) { + cc = unicode_get_cc(buf[starter_pos]); + if (cc == 0) + break; + if (cc >= last_cc) + goto next; + last_cc = 256; + starter_pos--; + } + if (starter_pos >= 0 && + (p = compose_pair(buf[starter_pos], buf[i])) != 0) { + buf[starter_pos] = p; + i++; + } else { + next: + buf[out_len++] = buf[i++]; + } + } + *pdst = (uint32_t *)buf; + return out_len; +} + +/* char ranges for various unicode properties */ + +static int unicode_find_name(const char *name_table, const char *name) +{ + const char *p, *r; + int pos; + size_t name_len, len; + + p = name_table; + pos = 0; + name_len = strlen(name); + while (*p) { + for(;;) { + r = strchr(p, ','); + if (!r) + len = strlen(p); + else + len = r - p; + if (len == name_len && !memcmp(p, name, name_len)) + return pos; + p += len + 1; + if (!r) + break; + } + pos++; + } + return -1; +} + +/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 + if not found */ +int unicode_script(CharRange *cr, + const char *script_name, BOOL is_ext) +{ + int script_idx; + const uint8_t *p, *p_end; + uint32_t c, c1, b, n, v, v_len, i, type; + CharRange cr1_s = { 0 }, *cr1 = NULL; + CharRange cr2_s = { 0 }, *cr2 = &cr2_s; + BOOL is_common; + + script_idx = unicode_find_name(unicode_script_name_table, script_name); + if (script_idx < 0) + return -2; + /* Note: we remove the "Unknown" Script */ + script_idx += UNICODE_SCRIPT_Unknown + 1; + + is_common = (script_idx == UNICODE_SCRIPT_Common || + script_idx == UNICODE_SCRIPT_Inherited); + if (is_ext) { + cr1 = &cr1_s; + cr_init(cr1, cr->mem_opaque, cr->realloc_func); + cr_init(cr2, cr->mem_opaque, cr->realloc_func); + } else { + cr1 = cr; + } + + p = unicode_script_table; + p_end = unicode_script_table + countof(unicode_script_table); + c = 0; + while (p < p_end) { + b = *p++; + type = b >> 7; + n = b & 0x7f; + if (n < 96) { + } else if (n < 112) { + n = (n - 96) << 8; + n |= *p++; + n += 96; + } else { + n = (n - 112) << 16; + n |= *p++ << 8; + n |= *p++; + n += 96 + (1 << 12); + } + if (type == 0) + v = 0; + else + v = *p++; + c1 = c + n + 1; + if (v == script_idx) { + if (cr_add_interval(cr1, c, c1)) + goto fail; + } + c = c1; + } + + if (is_ext) { + /* add the script extensions */ + p = unicode_script_ext_table; + p_end = unicode_script_ext_table + countof(unicode_script_ext_table); + c = 0; + while (p < p_end) { + b = *p++; + if (b < 128) { + n = b; + } else if (b < 128 + 64) { + n = (b - 128) << 8; + n |= *p++; + n += 128; + } else { + n = (b - 128 - 64) << 16; + n |= *p++ << 8; + n |= *p++; + n += 128 + (1 << 14); + } + c1 = c + n + 1; + v_len = *p++; + if (is_common) { + if (v_len != 0) { + if (cr_add_interval(cr2, c, c1)) + goto fail; + } + } else { + for(i = 0; i < v_len; i++) { + if (p[i] == script_idx) { + if (cr_add_interval(cr2, c, c1)) + goto fail; + break; + } + } + } + p += v_len; + c = c1; + } + if (is_common) { + /* remove all the characters with script extensions */ + if (cr_invert(cr2)) + goto fail; + if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, + CR_OP_INTER)) + goto fail; + } else { + if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len, + CR_OP_UNION)) + goto fail; + } + cr_free(cr1); + cr_free(cr2); + } + return 0; + fail: + if (is_ext) { + cr_free(cr1); + cr_free(cr2); + } + goto fail; +} + +#define M(id) (1U << UNICODE_GC_ ## id) + +static int unicode_general_category1(CharRange *cr, uint32_t gc_mask) +{ + const uint8_t *p, *p_end; + uint32_t c, c0, b, n, v; + + p = unicode_gc_table; + p_end = unicode_gc_table + countof(unicode_gc_table); + c = 0; + while (p < p_end) { + b = *p++; + n = b >> 5; + v = b & 0x1f; + if (n == 7) { + n = *p++; + if (n < 128) { + n += 7; + } else if (n < 128 + 64) { + n = (n - 128) << 8; + n |= *p++; + n += 7 + 128; + } else { + n = (n - 128 - 64) << 16; + n |= *p++ << 8; + n |= *p++; + n += 7 + 128 + (1 << 14); + } + } + c0 = c; + c += n + 1; + if (v == 31) { + /* run of Lu / Ll */ + b = gc_mask & (M(Lu) | M(Ll)); + if (b != 0) { + if (b == (M(Lu) | M(Ll))) { + goto add_range; + } else { + c0 += ((gc_mask & M(Ll)) != 0); + for(; c0 < c; c0 += 2) { + if (cr_add_interval(cr, c0, c0 + 1)) + return -1; + } + } + } + } else if ((gc_mask >> v) & 1) { + add_range: + if (cr_add_interval(cr, c0, c)) + return -1; + } + } + return 0; +} + +static int unicode_prop1(CharRange *cr, int prop_idx) +{ + const uint8_t *p, *p_end; + uint32_t c, c0, b, bit; + + p = unicode_prop_table[prop_idx]; + p_end = p + unicode_prop_len_table[prop_idx]; + c = 0; + bit = 0; + while (p < p_end) { + c0 = c; + b = *p++; + if (b < 64) { + c += (b >> 3) + 1; + if (bit) { + if (cr_add_interval(cr, c0, c)) + return -1; + } + bit ^= 1; + c0 = c; + c += (b & 7) + 1; + } else if (b >= 0x80) { + c += b - 0x80 + 1; + } else if (b < 0x60) { + c += (((b - 0x40) << 8) | p[0]) + 1; + p++; + } else { + c += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1; + p += 2; + } + if (bit) { + if (cr_add_interval(cr, c0, c)) + return -1; + } + bit ^= 1; + } + return 0; +} + +#define CASE_U (1 << 0) +#define CASE_L (1 << 1) +#define CASE_F (1 << 2) + +/* use the case conversion table to generate range of characters. + CASE_U: set char if modified by uppercasing, + CASE_L: set char if modified by lowercasing, + CASE_F: set char if modified by case folding, + */ +static int unicode_case1(CharRange *cr, int case_mask) +{ +#define MR(x) (1 << RUN_TYPE_ ## x) + const uint32_t tab_run_mask[3] = { + MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) | + MR(UF_D1_EXT) | MR(U_EXT) | MR(U_EXT2) | MR(U_EXT3), + + MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(L_EXT2), + + MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT), + }; +#undef MR + uint32_t mask, v, code, type, len, i, idx; + + if (case_mask == 0) + return 0; + mask = 0; + for(i = 0; i < 3; i++) { + if ((case_mask >> i) & 1) + mask |= tab_run_mask[i]; + } + for(idx = 0; idx < countof(case_conv_table1); idx++) { + v = case_conv_table1[idx]; + type = (v >> (32 - 17 - 7 - 4)) & 0xf; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + if ((mask >> type) & 1) { + // printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1); + switch(type) { + case RUN_TYPE_UL: + if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) + goto def_case; + code += ((case_mask & CASE_U) != 0); + for(i = 0; i < len; i += 2) { + if (cr_add_interval(cr, code + i, code + i + 1)) + return -1; + } + break; + case RUN_TYPE_LSU: + if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F))) + goto def_case; + if (!(case_mask & CASE_U)) { + if (cr_add_interval(cr, code, code + 1)) + return -1; + } + if (cr_add_interval(cr, code + 1, code + 2)) + return -1; + if (case_mask & CASE_U) { + if (cr_add_interval(cr, code + 2, code + 3)) + return -1; + } + break; + default: + def_case: + if (cr_add_interval(cr, code, code + len)) + return -1; + break; + } + } + } + return 0; +} + +typedef enum { + POP_GC, + POP_PROP, + POP_CASE, + POP_UNION, + POP_INTER, + POP_XOR, + POP_INVERT, + POP_END, +} PropOPEnum; + +#define POP_STACK_LEN_MAX 4 + +static int unicode_prop_ops(CharRange *cr, ...) +{ + va_list ap; + CharRange stack[POP_STACK_LEN_MAX]; + int stack_len, op, ret, i; + uint32_t a; + + va_start(ap, cr); + stack_len = 0; + for(;;) { + op = va_arg(ap, int); + switch(op) { + case POP_GC: + assert(stack_len < POP_STACK_LEN_MAX); + a = va_arg(ap, int); + cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); + if (unicode_general_category1(&stack[stack_len - 1], a)) + goto fail; + break; + case POP_PROP: + assert(stack_len < POP_STACK_LEN_MAX); + a = va_arg(ap, int); + cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); + if (unicode_prop1(&stack[stack_len - 1], a)) + goto fail; + break; + case POP_CASE: + assert(stack_len < POP_STACK_LEN_MAX); + a = va_arg(ap, int); + cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func); + if (unicode_case1(&stack[stack_len - 1], a)) + goto fail; + break; + case POP_UNION: + case POP_INTER: + case POP_XOR: + { + CharRange *cr1, *cr2, *cr3; + assert(stack_len >= 2); + assert(stack_len < POP_STACK_LEN_MAX); + cr1 = &stack[stack_len - 2]; + cr2 = &stack[stack_len - 1]; + cr3 = &stack[stack_len++]; + cr_init(cr3, cr->mem_opaque, cr->realloc_func); + if (cr_op(cr3, cr1->points, cr1->len, + cr2->points, cr2->len, op - POP_UNION + CR_OP_UNION)) + goto fail; + cr_free(cr1); + cr_free(cr2); + *cr1 = *cr3; + stack_len -= 2; + } + break; + case POP_INVERT: + assert(stack_len >= 1); + if (cr_invert(&stack[stack_len - 1])) + goto fail; + break; + case POP_END: + goto done; + default: + abort(); + } + } + done: + assert(stack_len == 1); + ret = cr_copy(cr, &stack[0]); + cr_free(&stack[0]); + return ret; + fail: + for(i = 0; i < stack_len; i++) + cr_free(&stack[i]); + return -1; +} + +static const uint32_t unicode_gc_mask_table[] = { + M(Lu) | M(Ll) | M(Lt), /* LC */ + M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo), /* L */ + M(Mn) | M(Mc) | M(Me), /* M */ + M(Nd) | M(Nl) | M(No), /* N */ + M(Sm) | M(Sc) | M(Sk) | M(So), /* S */ + M(Pc) | M(Pd) | M(Ps) | M(Pe) | M(Pi) | M(Pf) | M(Po), /* P */ + M(Zs) | M(Zl) | M(Zp), /* Z */ + M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn), /* C */ +}; + +/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 + if not found */ +int unicode_general_category(CharRange *cr, const char *gc_name) +{ + int gc_idx; + uint32_t gc_mask; + + gc_idx = unicode_find_name(unicode_gc_name_table, gc_name); + if (gc_idx < 0) + return -2; + if (gc_idx <= UNICODE_GC_Co) { + gc_mask = (uint64_t)1 << gc_idx; + } else { + gc_mask = unicode_gc_mask_table[gc_idx - UNICODE_GC_LC]; + } + return unicode_general_category1(cr, gc_mask); +} + + +/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2 + if not found */ +int unicode_prop(CharRange *cr, const char *prop_name) +{ + int prop_idx, ret; + + prop_idx = unicode_find_name(unicode_prop_name_table, prop_name); + if (prop_idx < 0) + return -2; + prop_idx += UNICODE_PROP_ASCII_Hex_Digit; + + ret = 0; + switch(prop_idx) { + case UNICODE_PROP_ASCII: + if (cr_add_interval(cr, 0x00, 0x7f + 1)) + return -1; + break; + case UNICODE_PROP_Any: + if (cr_add_interval(cr, 0x00000, 0x10ffff + 1)) + return -1; + break; + case UNICODE_PROP_Assigned: + ret = unicode_prop_ops(cr, + POP_GC, M(Cn), + POP_INVERT, + POP_END); + break; + case UNICODE_PROP_Math: + ret = unicode_prop_ops(cr, + POP_GC, M(Sm), + POP_PROP, UNICODE_PROP_Other_Math, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Lowercase: + ret = unicode_prop_ops(cr, + POP_GC, M(Ll), + POP_PROP, UNICODE_PROP_Other_Lowercase, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Uppercase: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu), + POP_PROP, UNICODE_PROP_Other_Uppercase, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Cased: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt), + POP_PROP, UNICODE_PROP_Other_Uppercase, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_Lowercase, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Alphabetic: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), + POP_PROP, UNICODE_PROP_Other_Uppercase, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_Lowercase, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_Alphabetic, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_Grapheme_Base: + ret = unicode_prop_ops(cr, + POP_GC, M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn) | M(Zl) | M(Zp) | M(Me) | M(Mn), + POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, + POP_UNION, + POP_INVERT, + POP_END); + break; + case UNICODE_PROP_Grapheme_Extend: + ret = unicode_prop_ops(cr, + POP_GC, M(Me) | M(Mn), + POP_PROP, UNICODE_PROP_Other_Grapheme_Extend, + POP_UNION, + POP_END); + break; + case UNICODE_PROP_XID_Start: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl), + POP_PROP, UNICODE_PROP_Other_ID_Start, + POP_UNION, + POP_PROP, UNICODE_PROP_Pattern_Syntax, + POP_PROP, UNICODE_PROP_Pattern_White_Space, + POP_UNION, + POP_PROP, UNICODE_PROP_XID_Start1, + POP_UNION, + POP_INVERT, + POP_INTER, + POP_END); + break; + case UNICODE_PROP_XID_Continue: + ret = unicode_prop_ops(cr, + POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) | + M(Mn) | M(Mc) | M(Nd) | M(Pc), + POP_PROP, UNICODE_PROP_Other_ID_Start, + POP_UNION, + POP_PROP, UNICODE_PROP_Other_ID_Continue, + POP_UNION, + POP_PROP, UNICODE_PROP_Pattern_Syntax, + POP_PROP, UNICODE_PROP_Pattern_White_Space, + POP_UNION, + POP_PROP, UNICODE_PROP_XID_Continue1, + POP_UNION, + POP_INVERT, + POP_INTER, + POP_END); + break; + case UNICODE_PROP_Changes_When_Uppercased: + ret = unicode_case1(cr, CASE_U); + break; + case UNICODE_PROP_Changes_When_Lowercased: + ret = unicode_case1(cr, CASE_L); + break; + case UNICODE_PROP_Changes_When_Casemapped: + ret = unicode_case1(cr, CASE_U | CASE_L | CASE_F); + break; + case UNICODE_PROP_Changes_When_Titlecased: + ret = unicode_prop_ops(cr, + POP_CASE, CASE_U, + POP_PROP, UNICODE_PROP_Changes_When_Titlecased1, + POP_XOR, + POP_END); + break; + case UNICODE_PROP_Changes_When_Casefolded: + ret = unicode_prop_ops(cr, + POP_CASE, CASE_F, + POP_PROP, UNICODE_PROP_Changes_When_Casefolded1, + POP_XOR, + POP_END); + break; + case UNICODE_PROP_Changes_When_NFKC_Casefolded: + ret = unicode_prop_ops(cr, + POP_CASE, CASE_F, + POP_PROP, UNICODE_PROP_Changes_When_NFKC_Casefolded1, + POP_XOR, + POP_END); + break; + /* we use the existing tables */ + case UNICODE_PROP_ID_Continue: + ret = unicode_prop_ops(cr, + POP_PROP, UNICODE_PROP_ID_Start, + POP_PROP, UNICODE_PROP_ID_Continue1, + POP_XOR, + POP_END); + break; + default: + if (prop_idx >= countof(unicode_prop_table)) + return -2; + ret = unicode_prop1(cr, prop_idx); + break; + } + return ret; +} diff --git a/armorcore/sources/libs/quickjs/libunicode.h b/armorcore/sources/libs/quickjs/libunicode.h new file mode 100644 index 000000000..337ffdc5a --- /dev/null +++ b/armorcore/sources/libs/quickjs/libunicode.h @@ -0,0 +1,126 @@ +/* + * Unicode utilities + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBUNICODE_H +#define LIBUNICODE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define LRE_BOOL int /* for documentation purposes */ + +#define LRE_CC_RES_LEN_MAX 3 + +typedef enum { + UNICODE_NFC, + UNICODE_NFD, + UNICODE_NFKC, + UNICODE_NFKD, +} UnicodeNormalizationEnum; + +int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); +LRE_BOOL lre_is_cased(uint32_t c); +LRE_BOOL lre_is_case_ignorable(uint32_t c); + +/* char ranges */ + +typedef struct { + int len; /* in points, always even */ + int size; + uint32_t *points; /* points sorted by increasing value */ + void *mem_opaque; + void *(*realloc_func)(void *opaque, void *ptr, size_t size); +} CharRange; + +typedef enum { + CR_OP_UNION, + CR_OP_INTER, + CR_OP_XOR, +} CharRangeOpEnum; + +void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); +void cr_free(CharRange *cr); +int cr_realloc(CharRange *cr, int size); +int cr_copy(CharRange *cr, const CharRange *cr1); + +static inline int cr_add_point(CharRange *cr, uint32_t v) +{ + if (cr->len >= cr->size) { + if (cr_realloc(cr, cr->len + 1)) + return -1; + } + cr->points[cr->len++] = v; + return 0; +} + +static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2) +{ + if ((cr->len + 2) > cr->size) { + if (cr_realloc(cr, cr->len + 2)) + return -1; + } + cr->points[cr->len++] = c1; + cr->points[cr->len++] = c2; + return 0; +} + +int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len); + +static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2) +{ + uint32_t b_pt[2]; + b_pt[0] = c1; + b_pt[1] = c2 + 1; + return cr_union1(cr, b_pt, 2); +} + +int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, + const uint32_t *b_pt, int b_len, int op); + +int cr_invert(CharRange *cr); + +LRE_BOOL lre_is_id_start(uint32_t c); +LRE_BOOL lre_is_id_continue(uint32_t c); + +int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, + UnicodeNormalizationEnum n_type, + void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size)); + +/* Unicode character range functions */ + +int unicode_script(CharRange *cr, + const char *script_name, LRE_BOOL is_ext); +int unicode_general_category(CharRange *cr, const char *gc_name); +int unicode_prop(CharRange *cr, const char *prop_name); + +#undef LRE_BOOL + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIBUNICODE_H */ diff --git a/armorcore/sources/libs/quickjs/list.h b/armorcore/sources/libs/quickjs/list.h new file mode 100644 index 000000000..b8dd71681 --- /dev/null +++ b/armorcore/sources/libs/quickjs/list.h @@ -0,0 +1,107 @@ +/* + * Linux klist like system + * + * Copyright (c) 2016-2017 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIST_H +#define LIST_H + +#ifndef NULL +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct list_head { + struct list_head *prev; + struct list_head *next; +}; + +#define LIST_HEAD_INIT(el) { &(el), &(el) } + +/* return the pointer of type 'type *' containing 'el' as field 'member' */ +#define list_entry(el, type, member) container_of(el, type, member) + +static inline void init_list_head(struct list_head *head) +{ + head->prev = head; + head->next = head; +} + +/* insert 'el' between 'prev' and 'next' */ +static inline void __list_add(struct list_head *el, + struct list_head *prev, struct list_head *next) +{ + prev->next = el; + el->prev = prev; + el->next = next; + next->prev = el; +} + +/* add 'el' at the head of the list 'head' (= after element head) */ +static inline void list_add(struct list_head *el, struct list_head *head) +{ + __list_add(el, head, head->next); +} + +/* add 'el' at the end of the list 'head' (= before element head) */ +static inline void list_add_tail(struct list_head *el, struct list_head *head) +{ + __list_add(el, head->prev, head); +} + +static inline void list_del(struct list_head *el) +{ + struct list_head *prev, *next; + prev = el->prev; + next = el->next; + prev->next = next; + next->prev = prev; + el->prev = NULL; /* fail safe */ + el->next = NULL; /* fail safe */ +} + +static inline int list_empty(struct list_head *el) +{ + return el->next == el; +} + +#define list_for_each(el, head) \ + for(el = (head)->next; el != (head); el = el->next) + +#define list_for_each_safe(el, el1, head) \ + for(el = (head)->next, el1 = el->next; el != (head); \ + el = el1, el1 = el->next) + +#define list_for_each_prev(el, head) \ + for(el = (head)->prev; el != (head); el = el->prev) + +#define list_for_each_prev_safe(el, el1, head) \ + for(el = (head)->prev, el1 = el->prev; el != (head); \ + el = el1, el1 = el->prev) + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIST_H */ diff --git a/armorcore/sources/libs/quickjs/quickjs-atom.h b/armorcore/sources/libs/quickjs/quickjs-atom.h new file mode 100644 index 000000000..01d1910cd --- /dev/null +++ b/armorcore/sources/libs/quickjs/quickjs-atom.h @@ -0,0 +1,255 @@ +/* + * QuickJS atom definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef DEF + +/* Note: first atoms are considered as keywords in the parser */ +DEF(null, "null") /* must be first */ +DEF(false, "false") +DEF(true, "true") +DEF(if, "if") +DEF(else, "else") +DEF(return, "return") +DEF(var, "var") +DEF(this, "this") +DEF(delete, "delete") +DEF(void, "void") +DEF(typeof, "typeof") +DEF(new, "new") +DEF(in, "in") +DEF(instanceof, "instanceof") +DEF(do, "do") +DEF(while, "while") +DEF(for, "for") +DEF(break, "break") +DEF(continue, "continue") +DEF(switch, "switch") +DEF(case, "case") +DEF(default, "default") +DEF(throw, "throw") +DEF(try, "try") +DEF(catch, "catch") +DEF(finally, "finally") +DEF(function, "function") +DEF(debugger, "debugger") +DEF(with, "with") +/* FutureReservedWord */ +DEF(class, "class") +DEF(const, "const") +DEF(enum, "enum") +DEF(export, "export") +DEF(extends, "extends") +DEF(import, "import") +DEF(super, "super") +/* FutureReservedWords when parsing strict mode code */ +DEF(implements, "implements") +DEF(interface, "interface") +DEF(let, "let") +DEF(package, "package") +DEF(private, "private") +DEF(protected, "protected") +DEF(public, "public") +DEF(static, "static") +DEF(yield, "yield") +DEF(await, "await") + +/* empty string */ +DEF(empty_string, "") +/* identifiers */ +DEF(length, "length") +DEF(message, "message") +DEF(cause, "cause") +DEF(errors, "errors") +DEF(stack, "stack") +DEF(name, "name") +DEF(toString, "toString") +DEF(toLocaleString, "toLocaleString") +DEF(valueOf, "valueOf") +DEF(eval, "eval") +DEF(prototype, "prototype") +DEF(constructor, "constructor") +DEF(configurable, "configurable") +DEF(writable, "writable") +DEF(enumerable, "enumerable") +DEF(value, "value") +DEF(get, "get") +DEF(set, "set") +DEF(of, "of") +DEF(__proto__, "__proto__") +DEF(undefined, "undefined") +DEF(number, "number") +DEF(boolean, "boolean") +DEF(string, "string") +DEF(object, "object") +DEF(symbol, "symbol") +DEF(integer, "integer") +DEF(unknown, "unknown") +DEF(arguments, "arguments") +DEF(callee, "callee") +DEF(caller, "caller") +DEF(_eval_, "") +DEF(_ret_, "") +DEF(_var_, "") +DEF(_arg_var_, "") +DEF(_with_, "") +DEF(lastIndex, "lastIndex") +DEF(target, "target") +DEF(index, "index") +DEF(input, "input") +DEF(defineProperties, "defineProperties") +DEF(apply, "apply") +DEF(join, "join") +DEF(concat, "concat") +DEF(split, "split") +DEF(construct, "construct") +DEF(getPrototypeOf, "getPrototypeOf") +DEF(setPrototypeOf, "setPrototypeOf") +DEF(isExtensible, "isExtensible") +DEF(preventExtensions, "preventExtensions") +DEF(has, "has") +DEF(deleteProperty, "deleteProperty") +DEF(defineProperty, "defineProperty") +DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor") +DEF(ownKeys, "ownKeys") +DEF(add, "add") +DEF(done, "done") +DEF(next, "next") +DEF(values, "values") +DEF(source, "source") +DEF(flags, "flags") +DEF(global, "global") +DEF(unicode, "unicode") +DEF(raw, "raw") +DEF(new_target, "new.target") +DEF(this_active_func, "this.active_func") +DEF(home_object, "") +DEF(computed_field, "") +DEF(static_computed_field, "") /* must come after computed_fields */ +DEF(class_fields_init, "") +DEF(brand, "") +DEF(hash_constructor, "#constructor") +DEF(as, "as") +DEF(from, "from") +DEF(meta, "meta") +DEF(_default_, "*default*") +DEF(_star_, "*") +DEF(Module, "Module") +DEF(then, "then") +DEF(resolve, "resolve") +DEF(reject, "reject") +DEF(promise, "promise") +DEF(proxy, "proxy") +DEF(revoke, "revoke") +DEF(async, "async") +DEF(exec, "exec") +DEF(groups, "groups") +DEF(indices, "indices") +DEF(status, "status") +DEF(reason, "reason") +DEF(globalThis, "globalThis") +DEF(bigint, "bigint") +DEF(not_equal, "not-equal") +DEF(timed_out, "timed-out") +DEF(ok, "ok") +DEF(toJSON, "toJSON") +/* class names */ +DEF(Object, "Object") +DEF(Array, "Array") +DEF(Error, "Error") +DEF(Number, "Number") +DEF(String, "String") +DEF(Boolean, "Boolean") +DEF(Symbol, "Symbol") +DEF(Arguments, "Arguments") +DEF(Math, "Math") +DEF(JSON, "JSON") +DEF(Date, "Date") +DEF(Function, "Function") +DEF(GeneratorFunction, "GeneratorFunction") +DEF(ForInIterator, "ForInIterator") +DEF(RegExp, "RegExp") +DEF(ArrayBuffer, "ArrayBuffer") +DEF(SharedArrayBuffer, "SharedArrayBuffer") +/* must keep same order as class IDs for typed arrays */ +DEF(Uint8ClampedArray, "Uint8ClampedArray") +DEF(Int8Array, "Int8Array") +DEF(Uint8Array, "Uint8Array") +DEF(Int16Array, "Int16Array") +DEF(Uint16Array, "Uint16Array") +DEF(Int32Array, "Int32Array") +DEF(Uint32Array, "Uint32Array") +DEF(BigInt64Array, "BigInt64Array") +DEF(BigUint64Array, "BigUint64Array") +DEF(Float32Array, "Float32Array") +DEF(Float64Array, "Float64Array") +DEF(DataView, "DataView") +DEF(BigInt, "BigInt") +DEF(WeakRef, "WeakRef") +DEF(FinalizationRegistry, "FinalizationRegistry") +DEF(Map, "Map") +DEF(Set, "Set") /* Map + 1 */ +DEF(WeakMap, "WeakMap") /* Map + 2 */ +DEF(WeakSet, "WeakSet") /* Map + 3 */ +DEF(Map_Iterator, "Map Iterator") +DEF(Set_Iterator, "Set Iterator") +DEF(Array_Iterator, "Array Iterator") +DEF(String_Iterator, "String Iterator") +DEF(RegExp_String_Iterator, "RegExp String Iterator") +DEF(Generator, "Generator") +DEF(Proxy, "Proxy") +DEF(Promise, "Promise") +DEF(PromiseResolveFunction, "PromiseResolveFunction") +DEF(PromiseRejectFunction, "PromiseRejectFunction") +DEF(AsyncFunction, "AsyncFunction") +DEF(AsyncFunctionResolve, "AsyncFunctionResolve") +DEF(AsyncFunctionReject, "AsyncFunctionReject") +DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction") +DEF(AsyncGenerator, "AsyncGenerator") +DEF(EvalError, "EvalError") +DEF(RangeError, "RangeError") +DEF(ReferenceError, "ReferenceError") +DEF(SyntaxError, "SyntaxError") +DEF(TypeError, "TypeError") +DEF(URIError, "URIError") +DEF(InternalError, "InternalError") +DEF(CallSite, "CallSite") +/* private symbols */ +DEF(Private_brand, "") +/* symbols */ +DEF(Symbol_toPrimitive, "Symbol.toPrimitive") +DEF(Symbol_iterator, "Symbol.iterator") +DEF(Symbol_match, "Symbol.match") +DEF(Symbol_matchAll, "Symbol.matchAll") +DEF(Symbol_replace, "Symbol.replace") +DEF(Symbol_search, "Symbol.search") +DEF(Symbol_split, "Symbol.split") +DEF(Symbol_toStringTag, "Symbol.toStringTag") +DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable") +DEF(Symbol_hasInstance, "Symbol.hasInstance") +DEF(Symbol_species, "Symbol.species") +DEF(Symbol_unscopables, "Symbol.unscopables") +DEF(Symbol_asyncIterator, "Symbol.asyncIterator") + +#endif /* DEF */ diff --git a/armorcore/sources/libs/quickjs/quickjs-c-atomics.h b/armorcore/sources/libs/quickjs/quickjs-c-atomics.h new file mode 100644 index 000000000..8fc6b7203 --- /dev/null +++ b/armorcore/sources/libs/quickjs/quickjs-c-atomics.h @@ -0,0 +1,54 @@ +/* + * QuickJS C atomics definitions + * + * Copyright (c) 2023 Marcin Kolny + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if (defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) + // Use GCC builtins for version < 4.9 +# if((__GNUC__ << 16) + __GNUC_MINOR__ < ((4) << 16) + 9) +# define GCC_BUILTIN_ATOMICS +# endif +#endif + +#ifdef GCC_BUILTIN_ATOMICS +#define atomic_fetch_add(obj, arg) \ + __atomic_fetch_add(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_compare_exchange_strong(obj, expected, desired) \ + __atomic_compare_exchange_n(obj, expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#define atomic_exchange(obj, desired) \ + __atomic_exchange_n (obj, desired, __ATOMIC_SEQ_CST) +#define atomic_load(obj) \ + __atomic_load_n(obj, __ATOMIC_SEQ_CST) +#define atomic_store(obj, desired) \ + __atomic_store_n(obj, desired, __ATOMIC_SEQ_CST) +#define atomic_fetch_or(obj, arg) \ + __atomic_fetch_or(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_fetch_xor(obj, arg) \ + __atomic_fetch_xor(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_fetch_and(obj, arg) \ + __atomic_fetch_and(obj, arg, __ATOMIC_SEQ_CST) +#define atomic_fetch_sub(obj, arg) \ + __atomic_fetch_sub(obj, arg, __ATOMIC_SEQ_CST) +#define _Atomic +#else +#include +#endif diff --git a/armorcore/sources/libs/quickjs/quickjs-libc.c b/armorcore/sources/libs/quickjs/quickjs-libc.c new file mode 100644 index 000000000..9fba83a8e --- /dev/null +++ b/armorcore/sources/libs/quickjs/quickjs-libc.c @@ -0,0 +1,4091 @@ +/* + * QuickJS C library + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#include +#include +#include +#include +#if defined(_MSC_VER) +#include "dirent_compat.h" +#else +#include +#endif +#if defined(_WIN32) +#include +#include +#include +#include +#include +#include +#include +#define popen _popen +#define pclose _pclose +#define rmdir _rmdir +#define getcwd _getcwd +#define chdir _chdir +#else +#include +#if !defined(__wasi__) +#include +#include +#include +#include +#endif + +#if defined(__APPLE__) +typedef sig_t sighandler_t; +#include +#define environ (*_NSGetEnviron()) +#endif + +#if defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) +typedef sig_t sighandler_t; +extern char **environ; +#endif + +#endif /* _WIN32 */ + +#if !defined(_WIN32) && !defined(__wasi__) +/* enable the os.Worker API. IT relies on POSIX threads */ +//#define USE_WORKER +#endif + +#ifdef USE_WORKER +#include +#include "quickjs-c-atomics.h" +#endif + +#include "cutils.h" +#include "list.h" +#include "quickjs-libc.h" + +/* TODO: + - add socket calls +*/ + +typedef struct { + struct list_head link; + int fd; + JSValue rw_func[2]; +} JSOSRWHandler; + +typedef struct { + struct list_head link; + int sig_num; + JSValue func; +} JSOSSignalHandler; + +typedef struct { + struct list_head link; + uint8_t has_object:1; + uint8_t repeats:1; + int64_t timeout; + int64_t delay; + JSValue func; +} JSOSTimer; + +typedef struct { + struct list_head link; + uint8_t *data; + size_t data_len; + /* list of SharedArrayBuffers, necessary to free the message */ + uint8_t **sab_tab; + size_t sab_tab_len; +} JSWorkerMessage; + +typedef struct { + int ref_count; +#ifdef USE_WORKER + pthread_mutex_t mutex; +#endif + struct list_head msg_queue; /* list of JSWorkerMessage.link */ + int read_fd; + int write_fd; +} JSWorkerMessagePipe; + +typedef struct { + struct list_head link; + JSWorkerMessagePipe *recv_pipe; + JSValue on_message_func; +} JSWorkerMessageHandler; + +typedef struct JSThreadState { + struct list_head os_rw_handlers; /* list of JSOSRWHandler.link */ + struct list_head os_signal_handlers; /* list JSOSSignalHandler.link */ + struct list_head os_timers; /* list of JSOSTimer.link */ + struct list_head port_list; /* list of JSWorkerMessageHandler.link */ + int eval_script_recurse; /* only used in the main thread */ + /* not used in the main thread */ + JSWorkerMessagePipe *recv_pipe, *send_pipe; +} JSThreadState; + +static uint64_t os_pending_signals; +static int (*os_poll_func)(JSContext *ctx); + +static void js_std_dbuf_init(JSContext *ctx, DynBuf *s) +{ + dbuf_init2(s, JS_GetRuntime(ctx), (DynBufReallocFunc *)js_realloc_rt); +} + +static BOOL my_isdigit(int c) +{ + return (c >= '0' && c <= '9'); +} + +static JSValue js_printf_internal(JSContext *ctx, + int argc, JSValue *argv, FILE *fp) +{ + char fmtbuf[32]; + uint8_t cbuf[UTF8_CHAR_LEN_MAX+1]; + JSValue res; + DynBuf dbuf; + const char *fmt_str = NULL; + const uint8_t *fmt, *fmt_end; + const uint8_t *p; + char *q; + int i, c, len, mod; + size_t fmt_len; + int32_t int32_arg; + int64_t int64_arg; + double double_arg; + const char *string_arg; + int (*dbuf_printf_fun)(DynBuf *s, const char *fmt, ...) = dbuf_printf; + + js_std_dbuf_init(ctx, &dbuf); + + if (argc > 0) { + fmt_str = JS_ToCStringLen(ctx, &fmt_len, argv[0]); + if (!fmt_str) + goto fail; + + i = 1; + fmt = (const uint8_t *)fmt_str; + fmt_end = fmt + fmt_len; + while (fmt < fmt_end) { + for (p = fmt; fmt < fmt_end && *fmt != '%'; fmt++) + continue; + dbuf_put(&dbuf, p, fmt - p); + if (fmt >= fmt_end) + break; + q = fmtbuf; + *q++ = *fmt++; /* copy '%' */ + + /* flags */ + for(;;) { + c = *fmt; + if (c == '0' || c == '#' || c == '+' || c == '-' || c == ' ' || + c == '\'') { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = c; + fmt++; + } else { + break; + } + } + /* width */ + if (*fmt == '*') { + if (i >= argc) + goto missing; + if (JS_ToInt32(ctx, &int32_arg, argv[i++])) + goto fail; + q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg); + fmt++; + } else { + while (my_isdigit(*fmt)) { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = *fmt++; + } + } + if (*fmt == '.') { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = *fmt++; + if (*fmt == '*') { + if (i >= argc) + goto missing; + if (JS_ToInt32(ctx, &int32_arg, argv[i++])) + goto fail; + q += snprintf(q, fmtbuf + sizeof(fmtbuf) - q, "%d", int32_arg); + fmt++; + } else { + while (my_isdigit(*fmt)) { + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = *fmt++; + } + } + } + + /* we only support the "l" modifier for 64 bit numbers */ + mod = ' '; + if (*fmt == 'l') { + mod = *fmt++; + } + + /* type */ + c = *fmt++; + if (q >= fmtbuf + sizeof(fmtbuf) - 1) + goto invalid; + *q++ = c; + *q = '\0'; + + switch (c) { + case 'c': + if (i >= argc) + goto missing; + if (JS_IsString(argv[i])) { + // TODO(chqrlie) need an API to wrap charCodeAt and codePointAt */ + string_arg = JS_ToCString(ctx, argv[i++]); + if (!string_arg) + goto fail; + int32_arg = utf8_decode((const uint8_t *)string_arg, &p); + JS_FreeCString(ctx, string_arg); + } else { + if (JS_ToInt32(ctx, &int32_arg, argv[i++])) + goto fail; + } + // XXX: throw an exception? + if ((unsigned)int32_arg > 0x10FFFF) + int32_arg = 0xFFFD; + /* ignore conversion flags, width and precision */ + len = utf8_encode(cbuf, int32_arg); + dbuf_put(&dbuf, cbuf, len); + break; + + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + if (i >= argc) + goto missing; + if (JS_ToInt64Ext(ctx, &int64_arg, argv[i++])) + goto fail; + if (mod == 'l') { + /* 64 bit number */ +#if defined(_WIN32) + if (q >= fmtbuf + sizeof(fmtbuf) - 3) + goto invalid; + q[2] = q[-1]; + q[-1] = 'I'; + q[0] = '6'; + q[1] = '4'; + q[3] = '\0'; + dbuf_printf_fun(&dbuf, fmtbuf, (int64_t)int64_arg); +#else + if (q >= fmtbuf + sizeof(fmtbuf) - 2) + goto invalid; + q[1] = q[-1]; + q[-1] = q[0] = 'l'; + q[2] = '\0'; + dbuf_printf_fun(&dbuf, fmtbuf, (long long)int64_arg); +#endif + } else { + dbuf_printf_fun(&dbuf, fmtbuf, (int)int64_arg); + } + break; + + case 's': + if (i >= argc) + goto missing; + /* XXX: handle strings containing null characters */ + string_arg = JS_ToCString(ctx, argv[i++]); + if (!string_arg) + goto fail; + dbuf_printf_fun(&dbuf, fmtbuf, string_arg); + JS_FreeCString(ctx, string_arg); + break; + + case 'e': + case 'f': + case 'g': + case 'a': + case 'E': + case 'F': + case 'G': + case 'A': + if (i >= argc) + goto missing; + if (JS_ToFloat64(ctx, &double_arg, argv[i++])) + goto fail; + dbuf_printf_fun(&dbuf, fmtbuf, double_arg); + break; + + case '%': + dbuf_putc(&dbuf, '%'); + break; + + default: + /* XXX: should support an extension mechanism */ + invalid: + JS_ThrowTypeError(ctx, "invalid conversion specifier in format string"); + goto fail; + missing: + JS_ThrowReferenceError(ctx, "missing argument for conversion specifier"); + goto fail; + } + } + JS_FreeCString(ctx, fmt_str); + } + if (dbuf.error) { + res = JS_ThrowOutOfMemory(ctx); + } else { + if (fp) { + len = fwrite(dbuf.buf, 1, dbuf.size, fp); + res = JS_NewInt32(ctx, len); + } else { + res = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size); + } + } + dbuf_free(&dbuf); + return res; + +fail: + JS_FreeCString(ctx, fmt_str); + dbuf_free(&dbuf); + return JS_EXCEPTION; +} + +uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename) +{ + FILE *f; + uint8_t *buf; + size_t buf_len; + long lret; + + f = fopen(filename, "rb"); + if (!f) + return NULL; + if (fseek(f, 0, SEEK_END) < 0) + goto fail; + lret = ftell(f); + if (lret < 0) + goto fail; + /* XXX: on Linux, ftell() return LONG_MAX for directories */ + if (lret == LONG_MAX) { + errno = EISDIR; + goto fail; + } + buf_len = lret; + if (fseek(f, 0, SEEK_SET) < 0) + goto fail; + if (ctx) + buf = js_malloc(ctx, buf_len + 1); + else + buf = malloc(buf_len + 1); + if (!buf) + goto fail; + if (fread(buf, 1, buf_len, f) != buf_len) { + errno = EIO; + if (ctx) + js_free(ctx, buf); + else + free(buf); + fail: + fclose(f); + return NULL; + } + buf[buf_len] = '\0'; + fclose(f); + *pbuf_len = buf_len; + return buf; +} + +/* load and evaluate a file */ +static JSValue js_loadScript(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + uint8_t *buf; + const char *filename; + JSValue ret; + size_t buf_len; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + buf = js_load_file(ctx, &buf_len, filename); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load '%s'", filename); + JS_FreeCString(ctx, filename); + return JS_EXCEPTION; + } + ret = JS_Eval(ctx, (char *)buf, buf_len, filename, + JS_EVAL_TYPE_GLOBAL); + js_free(ctx, buf); + JS_FreeCString(ctx, filename); + return ret; +} + +/* load a file as a UTF-8 encoded string */ +static JSValue js_std_loadFile(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + uint8_t *buf; + const char *filename; + JSValue ret; + size_t buf_len; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + buf = js_load_file(ctx, &buf_len, filename); + JS_FreeCString(ctx, filename); + if (!buf) + return JS_NULL; + ret = JS_NewStringLen(ctx, (char *)buf, buf_len); + js_free(ctx, buf); + return ret; +} + +typedef JSModuleDef *(JSInitModuleFunc)(JSContext *ctx, + const char *module_name); + + +#if defined(_WIN32) || defined(__wasi__) +static JSModuleDef *js_module_loader_so(JSContext *ctx, + const char *module_name) +{ + JS_ThrowReferenceError(ctx, "shared library modules are not supported yet"); + return NULL; +} +#else +static JSModuleDef *js_module_loader_so(JSContext *ctx, + const char *module_name) +{ + JSModuleDef *m; + void *hd; + JSInitModuleFunc *init; + char *filename; + + if (!strchr(module_name, '/')) { + /* must add a '/' so that the DLL is not searched in the + system library paths */ + filename = js_malloc(ctx, strlen(module_name) + 2 + 1); + if (!filename) + return NULL; + strcpy(filename, "./"); + strcpy(filename + 2, module_name); + } else { + filename = (char *)module_name; + } + + /* C module */ + hd = dlopen(filename, RTLD_NOW | RTLD_LOCAL); + if (filename != module_name) + js_free(ctx, filename); + if (!hd) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s' as shared library: %s", + module_name, dlerror()); + goto fail; + } + + *(void **) (&init) = dlsym(hd, "js_init_module"); + if (!init) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s': js_init_module not found", + module_name); + goto fail; + } + + m = init(ctx, module_name); + if (!m) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s': initialization error", + module_name); + fail: + if (hd) + dlclose(hd); + return NULL; + } + return m; +} +#endif /* !_WIN32 */ + +int js_module_set_import_meta(JSContext *ctx, JSValue func_val, + JS_BOOL use_realpath, JS_BOOL is_main) +{ + JSModuleDef *m; + char buf[PATH_MAX + 16]; + JSValue meta_obj; + JSAtom module_name_atom; + const char *module_name; + + assert(JS_VALUE_GET_TAG(func_val) == JS_TAG_MODULE); + m = JS_VALUE_GET_PTR(func_val); + + module_name_atom = JS_GetModuleName(ctx, m); + module_name = JS_AtomToCString(ctx, module_name_atom); + JS_FreeAtom(ctx, module_name_atom); + if (!module_name) + return -1; + if (!strchr(module_name, ':')) { + strcpy(buf, "file://"); +#if !defined(_WIN32) && !defined(__wasi__) + /* realpath() cannot be used with modules compiled with qjsc + because the corresponding module source code is not + necessarily present */ + if (use_realpath) { + char *res = realpath(module_name, buf + strlen(buf)); + if (!res) { + JS_ThrowTypeError(ctx, "realpath failure"); + JS_FreeCString(ctx, module_name); + return -1; + } + } else +#endif + { + pstrcat(buf, sizeof(buf), module_name); + } + } else { + pstrcpy(buf, sizeof(buf), module_name); + } + JS_FreeCString(ctx, module_name); + + meta_obj = JS_GetImportMeta(ctx, m); + if (JS_IsException(meta_obj)) + return -1; + JS_DefinePropertyValueStr(ctx, meta_obj, "url", + JS_NewString(ctx, buf), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, meta_obj, "main", + JS_NewBool(ctx, is_main), + JS_PROP_C_W_E); + JS_FreeValue(ctx, meta_obj); + return 0; +} + +JSModuleDef *js_module_loader(JSContext *ctx, + const char *module_name, void *opaque) +{ + JSModuleDef *m; + + if (has_suffix(module_name, ".so")) { + m = js_module_loader_so(ctx, module_name); + } else { + size_t buf_len; + uint8_t *buf; + JSValue func_val; + + buf = js_load_file(ctx, &buf_len, module_name); + if (!buf) { + JS_ThrowReferenceError(ctx, "could not load module filename '%s'", + module_name); + return NULL; + } + + /* compile the module */ + func_val = JS_Eval(ctx, (char *)buf, buf_len, module_name, + JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); + js_free(ctx, buf); + if (JS_IsException(func_val)) + return NULL; + /* XXX: could propagate the exception */ + js_module_set_import_meta(ctx, func_val, TRUE, FALSE); + /* the module is already referenced, so we must free it */ + m = JS_VALUE_GET_PTR(func_val); + JS_FreeValue(ctx, func_val); + } + return m; +} + +static JSValue js_std_exit(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int status; + if (JS_ToInt32(ctx, &status, argv[0])) + status = -1; + exit(status); + return JS_UNDEFINED; +} + +static JSValue js_std_getenv(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *name, *str; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + str = getenv(name); + JS_FreeCString(ctx, name); + if (!str) + return JS_UNDEFINED; + else + return JS_NewString(ctx, str); +} + +#if defined(_WIN32) +static void setenv(const char *name, const char *value, int overwrite) +{ + char *str; + size_t name_len, value_len; + name_len = strlen(name); + value_len = strlen(value); + str = malloc(name_len + 1 + value_len + 1); + memcpy(str, name, name_len); + str[name_len] = '='; + memcpy(str + name_len + 1, value, value_len); + str[name_len + 1 + value_len] = '\0'; + _putenv(str); + free(str); +} + +static void unsetenv(const char *name) +{ + setenv(name, "", TRUE); +} +#endif /* _WIN32 */ + +static JSValue js_std_setenv(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *name, *value; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + value = JS_ToCString(ctx, argv[1]); + if (!value) { + JS_FreeCString(ctx, name); + return JS_EXCEPTION; + } + setenv(name, value, TRUE); + JS_FreeCString(ctx, name); + JS_FreeCString(ctx, value); + return JS_UNDEFINED; +} + +static JSValue js_std_unsetenv(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *name; + name = JS_ToCString(ctx, argv[0]); + if (!name) + return JS_EXCEPTION; + unsetenv(name); + JS_FreeCString(ctx, name); + return JS_UNDEFINED; +} + +/* return an object containing the list of the available environment + variables. */ +static JSValue js_std_getenviron(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + char **envp; + const char *name, *p, *value; + JSValue obj; + uint32_t idx; + size_t name_len; + JSAtom atom; + int ret; + + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + envp = environ; + for(idx = 0; envp[idx] != NULL; idx++) { + name = envp[idx]; + p = strchr(name, '='); + name_len = p - name; + if (!p) + continue; + value = p + 1; + atom = JS_NewAtomLen(ctx, name, name_len); + if (atom == JS_ATOM_NULL) + goto fail; + ret = JS_DefinePropertyValue(ctx, obj, atom, JS_NewString(ctx, value), + JS_PROP_C_W_E); + JS_FreeAtom(ctx, atom); + if (ret < 0) + goto fail; + } + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_std_gc(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JS_RunGC(JS_GetRuntime(ctx)); + return JS_UNDEFINED; +} + +static int interrupt_handler(JSRuntime *rt, void *opaque) +{ + return (os_pending_signals >> SIGINT) & 1; +} + +static int get_bool_option(JSContext *ctx, BOOL *pbool, + JSValue obj, + const char *option) +{ + JSValue val; + val = JS_GetPropertyStr(ctx, obj, option); + if (JS_IsException(val)) + return -1; + if (!JS_IsUndefined(val)) { + *pbool = JS_ToBool(ctx, val); + } + JS_FreeValue(ctx, val); + return 0; +} + +static JSValue js_evalScript(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + const char *str; + size_t len; + JSValue ret; + JSValue options_obj; + BOOL backtrace_barrier = FALSE; + BOOL is_async = FALSE; + int flags; + + if (argc >= 2) { + options_obj = argv[1]; + if (get_bool_option(ctx, &backtrace_barrier, options_obj, + "backtrace_barrier")) + return JS_EXCEPTION; + if (get_bool_option(ctx, &is_async, options_obj, + "async")) + return JS_EXCEPTION; + } + + str = JS_ToCStringLen(ctx, &len, argv[0]); + if (!str) + return JS_EXCEPTION; + if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) { + /* install the interrupt handler */ + JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); + } + flags = JS_EVAL_TYPE_GLOBAL; + if (backtrace_barrier) + flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER; + if (is_async) + flags |= JS_EVAL_FLAG_ASYNC; + ret = JS_Eval(ctx, str, len, "", flags); + JS_FreeCString(ctx, str); + if (!ts->recv_pipe && --ts->eval_script_recurse == 0) { + /* remove the interrupt handler */ + JS_SetInterruptHandler(JS_GetRuntime(ctx), NULL, NULL); + os_pending_signals &= ~((uint64_t)1 << SIGINT); + /* convert the uncatchable "interrupted" error into a normal error + so that it can be caught by the REPL */ + if (JS_IsException(ret)) + JS_ResetUncatchableError(ctx); + } + return ret; +} + +static JSClassID js_std_file_class_id; + +typedef struct { + FILE *f; + BOOL close_in_finalizer; + BOOL is_popen; +} JSSTDFile; + +static void js_std_file_finalizer(JSRuntime *rt, JSValue val) +{ + JSSTDFile *s = JS_GetOpaque(val, js_std_file_class_id); + if (s) { + if (s->f && s->close_in_finalizer) { +#if !defined(__wasi__) + if (s->is_popen) + pclose(s->f); + else +#endif + fclose(s->f); + } + js_free_rt(rt, s); + } +} + +static ssize_t js_get_errno(ssize_t ret) +{ + if (ret == -1) + ret = -errno; + return ret; +} + +static JSValue js_std_strerror(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int err; + if (JS_ToInt32(ctx, &err, argv[0])) + return JS_EXCEPTION; + return JS_NewString(ctx, strerror(err)); +} + +static JSValue js_new_std_file(JSContext *ctx, FILE *f, + BOOL close_in_finalizer, + BOOL is_popen) +{ + JSSTDFile *s; + JSValue obj; + obj = JS_NewObjectClass(ctx, js_std_file_class_id); + if (JS_IsException(obj)) + return obj; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + s->close_in_finalizer = close_in_finalizer; + s->is_popen = is_popen; + s->f = f; + JS_SetOpaque(obj, s); + return obj; +} + +static void js_set_error_object(JSContext *ctx, JSValue obj, int err) +{ + if (!JS_IsUndefined(obj)) { + JS_SetPropertyStr(ctx, obj, "errno", JS_NewInt32(ctx, err)); + } +} + +static JSValue js_std_open(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *filename, *mode = NULL; + FILE *f; + int err; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rwa+b")] != '\0') { + JS_ThrowTypeError(ctx, "invalid file mode"); + goto fail; + } + + f = fopen(filename, mode); + if (!f) + err = errno; + else + err = 0; + if (argc >= 3) + js_set_error_object(ctx, argv[2], err); + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + if (!f) + return JS_NULL; + return js_new_std_file(ctx, f, TRUE, FALSE); + fail: + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} + +#if !defined(__wasi__) +static JSValue js_std_popen(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *filename, *mode = NULL; + FILE *f; + int err; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rw")] != '\0') { + JS_ThrowTypeError(ctx, "invalid file mode"); + goto fail; + } + + f = popen(filename, mode); + if (!f) + err = errno; + else + err = 0; + if (argc >= 3) + js_set_error_object(ctx, argv[2], err); + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + if (!f) + return JS_NULL; + return js_new_std_file(ctx, f, TRUE, TRUE); + fail: + JS_FreeCString(ctx, filename); + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} +#endif // !defined(__wasi__) + +static JSValue js_std_fdopen(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *mode; + FILE *f; + int fd, err; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + mode = JS_ToCString(ctx, argv[1]); + if (!mode) + goto fail; + if (mode[strspn(mode, "rwa+")] != '\0') { + JS_ThrowTypeError(ctx, "invalid file mode"); + goto fail; + } + + f = fdopen(fd, mode); + if (!f) + err = errno; + else + err = 0; + if (argc >= 3) + js_set_error_object(ctx, argv[2], err); + JS_FreeCString(ctx, mode); + if (!f) + return JS_NULL; + return js_new_std_file(ctx, f, TRUE, FALSE); + fail: + JS_FreeCString(ctx, mode); + return JS_EXCEPTION; +} + +#if !defined(__wasi__) +static JSValue js_std_tmpfile(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f; + f = tmpfile(); + if (argc >= 1) + js_set_error_object(ctx, argv[0], f ? 0 : errno); + if (!f) + return JS_NULL; + return js_new_std_file(ctx, f, TRUE, FALSE); +} +#endif + +static JSValue js_std_sprintf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return js_printf_internal(ctx, argc, argv, NULL); +} + +static JSValue js_std_printf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return js_printf_internal(ctx, argc, argv, stdout); +} + +static FILE *js_std_file_get(JSContext *ctx, JSValue obj) +{ + JSSTDFile *s = JS_GetOpaque2(ctx, obj, js_std_file_class_id); + if (!s) + return NULL; + if (!s->f) { + JS_ThrowTypeError(ctx, "invalid file handle"); + return NULL; + } + return s->f; +} + +static JSValue js_std_file_puts(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + FILE *f; + int i; + const char *str; + size_t len; + + if (magic == 0) { + f = stdout; + } else { + f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + } + + for(i = 0; i < argc; i++) { + str = JS_ToCStringLen(ctx, &len, argv[i]); + if (!str) + return JS_EXCEPTION; + fwrite(str, 1, len, f); + JS_FreeCString(ctx, str); + } + return JS_UNDEFINED; +} + +static JSValue js_std_file_close(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSSTDFile *s = JS_GetOpaque2(ctx, this_val, js_std_file_class_id); + int err; + if (!s) + return JS_EXCEPTION; + if (!s->f) + return JS_ThrowTypeError(ctx, "invalid file handle"); +#if !defined(__wasi__) + if (s->is_popen) + err = js_get_errno(pclose(s->f)); + else +#endif + err = js_get_errno(fclose(s->f)); + s->f = NULL; + return JS_NewInt32(ctx, err); +} + +static JSValue js_std_file_printf(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return js_printf_internal(ctx, argc, argv, f); +} + +static JSValue js_std_file_flush(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + fflush(f); + return JS_UNDEFINED; +} + +static JSValue js_std_file_tell(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int is_bigint) +{ + FILE *f = js_std_file_get(ctx, this_val); + int64_t pos; + if (!f) + return JS_EXCEPTION; +#if defined(__linux__) + pos = ftello(f); +#else + pos = ftell(f); +#endif + if (is_bigint) + return JS_NewBigInt64(ctx, pos); + else + return JS_NewInt64(ctx, pos); +} + +static JSValue js_std_file_seek(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int64_t pos; + int whence, ret; + if (!f) + return JS_EXCEPTION; + if (JS_ToInt64Ext(ctx, &pos, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &whence, argv[1])) + return JS_EXCEPTION; +#if defined(__linux__) + ret = fseeko(f, pos, whence); +#else + ret = fseek(f, pos, whence); +#endif + if (ret < 0) + ret = -errno; + return JS_NewInt32(ctx, ret); +} + +static JSValue js_std_file_eof(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewBool(ctx, feof(f)); +} + +static JSValue js_std_file_error(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewBool(ctx, ferror(f)); +} + +static JSValue js_std_file_clearerr(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + clearerr(f); + return JS_UNDEFINED; +} + +static JSValue js_std_file_fileno(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewInt32(ctx, fileno(f)); +} + +static JSValue js_std_file_read_write(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + FILE *f = js_std_file_get(ctx, this_val); + uint64_t pos, len; + size_t size, ret; + uint8_t *buf; + + if (!f) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &pos, argv[1])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &len, argv[2])) + return JS_EXCEPTION; + buf = JS_GetArrayBuffer(ctx, &size, argv[0]); + if (!buf) + return JS_EXCEPTION; + if (pos + len > size) + return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); + if (magic) + ret = fwrite(buf + pos, 1, len, f); + else + ret = fread(buf + pos, 1, len, f); + return JS_NewInt64(ctx, ret); +} + +/* XXX: could use less memory and go faster */ +static JSValue js_std_file_getline(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + DynBuf dbuf; + JSValue obj; + + if (!f) + return JS_EXCEPTION; + + js_std_dbuf_init(ctx, &dbuf); + for(;;) { + c = fgetc(f); + if (c == EOF) { + if (dbuf.size == 0) { + /* EOF */ + dbuf_free(&dbuf); + return JS_NULL; + } else { + break; + } + } + if (c == '\n') + break; + if (dbuf_putc(&dbuf, c)) { + dbuf_free(&dbuf); + return JS_ThrowOutOfMemory(ctx); + } + } + obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); + dbuf_free(&dbuf); + return obj; +} + +/* XXX: could use less memory and go faster */ +static JSValue js_std_file_readAsString(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + DynBuf dbuf; + JSValue obj; + uint64_t max_size64; + size_t max_size; + JSValue max_size_val; + + if (!f) + return JS_EXCEPTION; + + if (argc >= 1) + max_size_val = argv[0]; + else + max_size_val = JS_UNDEFINED; + max_size = (size_t)-1; + if (!JS_IsUndefined(max_size_val)) { + if (JS_ToIndex(ctx, &max_size64, max_size_val)) + return JS_EXCEPTION; + if (max_size64 < max_size) + max_size = max_size64; + } + + js_std_dbuf_init(ctx, &dbuf); + while (max_size != 0) { + c = fgetc(f); + if (c == EOF) + break; + if (dbuf_putc(&dbuf, c)) { + dbuf_free(&dbuf); + return JS_EXCEPTION; + } + max_size--; + } + obj = JS_NewStringLen(ctx, (const char *)dbuf.buf, dbuf.size); + dbuf_free(&dbuf); + return obj; +} + +static JSValue js_std_file_getByte(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + if (!f) + return JS_EXCEPTION; + return JS_NewInt32(ctx, fgetc(f)); +} + +static JSValue js_std_file_putByte(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + FILE *f = js_std_file_get(ctx, this_val); + int c; + if (!f) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &c, argv[0])) + return JS_EXCEPTION; + c = fputc(c, f); + return JS_NewInt32(ctx, c); +} + +/* urlGet */ +#if !defined(__wasi__) + +#define URL_GET_PROGRAM "curl -s -i --" +#define URL_GET_BUF_SIZE 4096 + +static int http_get_header_line(FILE *f, char *buf, size_t buf_size, + DynBuf *dbuf) +{ + int c; + char *p; + + p = buf; + for(;;) { + c = fgetc(f); + if (c < 0) + return -1; + if ((p - buf) < buf_size - 1) + *p++ = c; + if (dbuf) + dbuf_putc(dbuf, c); + if (c == '\n') + break; + } + *p = '\0'; + return 0; +} + +static int http_get_status(const char *buf) +{ + const char *p = buf; + while (*p != ' ' && *p != '\0') + p++; + if (*p != ' ') + return 0; + while (*p == ' ') + p++; + return atoi(p); +} + +static JSValue js_std_urlGet(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *url; + DynBuf cmd_buf; + DynBuf data_buf_s, *data_buf = &data_buf_s; + DynBuf header_buf_s, *header_buf = &header_buf_s; + char *buf; + size_t i, len; + int status; + JSValue response = JS_UNDEFINED, ret_obj; + JSValue options_obj; + FILE *f; + BOOL binary_flag, full_flag; + + url = JS_ToCString(ctx, argv[0]); + if (!url) + return JS_EXCEPTION; + + binary_flag = FALSE; + full_flag = FALSE; + + if (argc >= 2) { + options_obj = argv[1]; + + if (get_bool_option(ctx, &binary_flag, options_obj, "binary")) + goto fail_obj; + + if (get_bool_option(ctx, &full_flag, options_obj, "full")) { + fail_obj: + JS_FreeCString(ctx, url); + return JS_EXCEPTION; + } + } + + js_std_dbuf_init(ctx, &cmd_buf); + dbuf_printf(&cmd_buf, "%s '", URL_GET_PROGRAM); + for(i = 0; url[i] != '\0'; i++) { + unsigned char c = url[i]; + switch (c) { + case '\'': + /* shell single quoted string does not support \' */ + dbuf_putstr(&cmd_buf, "'\\''"); + break; + case '[': case ']': case '{': case '}': case '\\': + /* prevent interpretation by curl as range or set specification */ + dbuf_putc(&cmd_buf, '\\'); + /* FALLTHROUGH */ + default: + dbuf_putc(&cmd_buf, c); + break; + } + } + JS_FreeCString(ctx, url); + dbuf_putstr(&cmd_buf, "'"); + dbuf_putc(&cmd_buf, '\0'); + if (dbuf_error(&cmd_buf)) { + dbuf_free(&cmd_buf); + return JS_EXCEPTION; + } + // printf("%s\n", (char *)cmd_buf.buf); + f = popen((char *)cmd_buf.buf, "r"); + dbuf_free(&cmd_buf); + if (!f) { + return JS_ThrowTypeError(ctx, "could not start curl"); + } + + js_std_dbuf_init(ctx, data_buf); + js_std_dbuf_init(ctx, header_buf); + + buf = js_malloc(ctx, URL_GET_BUF_SIZE); + if (!buf) + goto fail; + + /* get the HTTP status */ + if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, NULL) < 0) { + status = 0; + goto bad_header; + } + status = http_get_status(buf); + if (!full_flag && !(status >= 200 && status <= 299)) { + goto bad_header; + } + + /* wait until there is an empty line */ + for(;;) { + if (http_get_header_line(f, buf, URL_GET_BUF_SIZE, header_buf) < 0) { + bad_header: + response = JS_NULL; + goto done; + } + if (!strcmp(buf, "\r\n")) + break; + } + if (dbuf_error(header_buf)) + goto fail; + header_buf->size -= 2; /* remove the trailing CRLF */ + + /* download the data */ + for(;;) { + len = fread(buf, 1, URL_GET_BUF_SIZE, f); + if (len == 0) + break; + dbuf_put(data_buf, (uint8_t *)buf, len); + } + if (dbuf_error(data_buf)) + goto fail; + if (binary_flag) { + response = JS_NewArrayBufferCopy(ctx, + data_buf->buf, data_buf->size); + } else { + response = JS_NewStringLen(ctx, (char *)data_buf->buf, data_buf->size); + } + if (JS_IsException(response)) + goto fail; + done: + js_free(ctx, buf); + buf = NULL; + pclose(f); + f = NULL; + dbuf_free(data_buf); + data_buf = NULL; + + if (full_flag) { + ret_obj = JS_NewObject(ctx); + if (JS_IsException(ret_obj)) + goto fail; + JS_DefinePropertyValueStr(ctx, ret_obj, "response", + response, + JS_PROP_C_W_E); + if (!JS_IsNull(response)) { + JS_DefinePropertyValueStr(ctx, ret_obj, "responseHeaders", + JS_NewStringLen(ctx, (char *)header_buf->buf, + header_buf->size), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, ret_obj, "status", + JS_NewInt32(ctx, status), + JS_PROP_C_W_E); + } + } else { + ret_obj = response; + } + dbuf_free(header_buf); + return ret_obj; + fail: + if (f) + pclose(f); + js_free(ctx, buf); + if (data_buf) + dbuf_free(data_buf); + if (header_buf) + dbuf_free(header_buf); + JS_FreeValue(ctx, response); + return JS_EXCEPTION; +} +#endif // !defined(__wasi__) + +static JSClassDef js_std_file_class = { + "FILE", + .finalizer = js_std_file_finalizer, +}; + +static const JSCFunctionListEntry js_std_error_props[] = { + /* various errno values */ +#define DEF(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) + DEF(EINVAL), + DEF(EIO), + DEF(EACCES), + DEF(EEXIST), + DEF(ENOSPC), + DEF(ENOSYS), + DEF(EBUSY), + DEF(ENOENT), + DEF(EPERM), + DEF(EPIPE), + DEF(EBADF), +#undef DEF +}; + +static const JSCFunctionListEntry js_std_funcs[] = { + JS_CFUNC_DEF("exit", 1, js_std_exit ), + JS_CFUNC_DEF("gc", 0, js_std_gc ), + JS_CFUNC_DEF("evalScript", 1, js_evalScript ), + JS_CFUNC_DEF("loadScript", 1, js_loadScript ), + JS_CFUNC_DEF("getenv", 1, js_std_getenv ), + JS_CFUNC_DEF("setenv", 1, js_std_setenv ), + JS_CFUNC_DEF("unsetenv", 1, js_std_unsetenv ), + JS_CFUNC_DEF("getenviron", 1, js_std_getenviron ), +#if !defined(__wasi__) + JS_CFUNC_DEF("urlGet", 1, js_std_urlGet ), +#endif + JS_CFUNC_DEF("loadFile", 1, js_std_loadFile ), + JS_CFUNC_DEF("strerror", 1, js_std_strerror ), + + /* FILE I/O */ + JS_CFUNC_DEF("open", 2, js_std_open ), +#if !defined(__wasi__) + JS_CFUNC_DEF("popen", 2, js_std_popen ), + JS_CFUNC_DEF("tmpfile", 0, js_std_tmpfile ), +#endif + JS_CFUNC_DEF("fdopen", 2, js_std_fdopen ), + JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 0 ), + JS_CFUNC_DEF("printf", 1, js_std_printf ), + JS_CFUNC_DEF("sprintf", 1, js_std_sprintf ), + JS_PROP_INT32_DEF("SEEK_SET", SEEK_SET, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("SEEK_CUR", SEEK_CUR, JS_PROP_CONFIGURABLE ), + JS_PROP_INT32_DEF("SEEK_END", SEEK_END, JS_PROP_CONFIGURABLE ), + JS_OBJECT_DEF("Error", js_std_error_props, countof(js_std_error_props), JS_PROP_CONFIGURABLE), +}; + +static const JSCFunctionListEntry js_std_file_proto_funcs[] = { + JS_CFUNC_DEF("close", 0, js_std_file_close ), + JS_CFUNC_MAGIC_DEF("puts", 1, js_std_file_puts, 1 ), + JS_CFUNC_DEF("printf", 1, js_std_file_printf ), + JS_CFUNC_DEF("flush", 0, js_std_file_flush ), + JS_CFUNC_MAGIC_DEF("tell", 0, js_std_file_tell, 0 ), + JS_CFUNC_MAGIC_DEF("tello", 0, js_std_file_tell, 1 ), + JS_CFUNC_DEF("seek", 2, js_std_file_seek ), + JS_CFUNC_DEF("eof", 0, js_std_file_eof ), + JS_CFUNC_DEF("fileno", 0, js_std_file_fileno ), + JS_CFUNC_DEF("error", 0, js_std_file_error ), + JS_CFUNC_DEF("clearerr", 0, js_std_file_clearerr ), + JS_CFUNC_MAGIC_DEF("read", 3, js_std_file_read_write, 0 ), + JS_CFUNC_MAGIC_DEF("write", 3, js_std_file_read_write, 1 ), + JS_CFUNC_DEF("getline", 0, js_std_file_getline ), + JS_CFUNC_DEF("readAsString", 0, js_std_file_readAsString ), + JS_CFUNC_DEF("getByte", 0, js_std_file_getByte ), + JS_CFUNC_DEF("putByte", 1, js_std_file_putByte ), + /* setvbuf, ... */ +}; + +static int js_std_init(JSContext *ctx, JSModuleDef *m) +{ + JSValue proto; + JSRuntime *rt = JS_GetRuntime(ctx); + + /* FILE class */ + /* the class ID is created once */ + JS_NewClassID(rt, &js_std_file_class_id); + /* the class is created once per runtime */ + JS_NewClass(rt, js_std_file_class_id, &js_std_file_class); + proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_std_file_proto_funcs, + countof(js_std_file_proto_funcs)); + JS_SetClassProto(ctx, js_std_file_class_id, proto); + + JS_SetModuleExportList(ctx, m, js_std_funcs, + countof(js_std_funcs)); + JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, FALSE, FALSE)); + JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, FALSE, FALSE)); + JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, FALSE, FALSE)); + return 0; +} + +JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_std_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_std_funcs, countof(js_std_funcs)); + JS_AddModuleExport(ctx, m, "in"); + JS_AddModuleExport(ctx, m, "out"); + JS_AddModuleExport(ctx, m, "err"); + return m; +} + +/**********************************************************/ +/* 'os' object */ + +static JSValue js_os_open(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *filename; + int flags, mode, ret; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &flags, argv[1])) + goto fail; + if (argc >= 3 && !JS_IsUndefined(argv[2])) { + if (JS_ToInt32(ctx, &mode, argv[2])) { + fail: + JS_FreeCString(ctx, filename); + return JS_EXCEPTION; + } + } else { + mode = 0666; + } +#if defined(_WIN32) + /* force binary mode by default */ + if (!(flags & O_TEXT)) + flags |= O_BINARY; +#endif + ret = js_get_errno(open(filename, flags, mode)); + JS_FreeCString(ctx, filename); + return JS_NewInt32(ctx, ret); +} + +static JSValue js_os_close(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd, ret; + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + ret = js_get_errno(close(fd)); + return JS_NewInt32(ctx, ret); +} + +static JSValue js_os_seek(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd, whence; + int64_t pos, ret; + BOOL is_bigint; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + is_bigint = JS_IsBigInt(ctx, argv[1]); + if (JS_ToInt64Ext(ctx, &pos, argv[1])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &whence, argv[2])) + return JS_EXCEPTION; + ret = lseek(fd, pos, whence); + if (ret == -1) + ret = -errno; + if (is_bigint) + return JS_NewBigInt64(ctx, ret); + else + return JS_NewInt64(ctx, ret); +} + +static JSValue js_os_read_write(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + int fd; + uint64_t pos, len; + size_t size; + ssize_t ret; + uint8_t *buf; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &pos, argv[2])) + return JS_EXCEPTION; + if (JS_ToIndex(ctx, &len, argv[3])) + return JS_EXCEPTION; + buf = JS_GetArrayBuffer(ctx, &size, argv[1]); + if (!buf) + return JS_EXCEPTION; + if (pos + len > size) + return JS_ThrowRangeError(ctx, "read/write array buffer overflow"); + if (magic) + ret = js_get_errno(write(fd, buf + pos, len)); + else + ret = js_get_errno(read(fd, buf + pos, len)); + return JS_NewInt64(ctx, ret); +} + +static JSValue js_os_isatty(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd; + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + return JS_NewBool(ctx, (isatty(fd) != 0)); +} + +#if defined(_WIN32) +static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd; + HANDLE handle; + CONSOLE_SCREEN_BUFFER_INFO info; + JSValue obj; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + handle = (HANDLE)_get_osfhandle(fd); + + if (!GetConsoleScreenBufferInfo(handle, &info)) + return JS_NULL; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, info.dwSize.X), JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, info.dwSize.Y), JS_PROP_C_W_E); + return obj; +} + +/* Windows 10 built-in VT100 emulation */ +#define __ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#define __ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 + +static JSValue js_os_ttySetRaw(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd; + HANDLE handle; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + handle = (HANDLE)_get_osfhandle(fd); + SetConsoleMode(handle, ENABLE_WINDOW_INPUT | __ENABLE_VIRTUAL_TERMINAL_INPUT); + _setmode(fd, _O_BINARY); + if (fd == 0) { + handle = (HANDLE)_get_osfhandle(1); /* corresponding output */ + SetConsoleMode(handle, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | __ENABLE_VIRTUAL_TERMINAL_PROCESSING); + } + return JS_UNDEFINED; +} +#elif !defined(__wasi__) +static JSValue js_os_ttyGetWinSize(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd; + struct winsize ws; + JSValue obj; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (ioctl(fd, TIOCGWINSZ, &ws) == 0 && + ws.ws_col >= 4 && ws.ws_row >= 4) { + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ws.ws_col), JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, ws.ws_row), JS_PROP_C_W_E); + return obj; + } else { + return JS_NULL; + } +} + +static struct termios oldtty; + +static void term_exit(void) +{ + tcsetattr(0, TCSANOW, &oldtty); +} + +/* XXX: should add a way to go back to normal mode */ +static JSValue js_os_ttySetRaw(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + struct termios tty; + int fd; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + + memset(&tty, 0, sizeof(tty)); + tcgetattr(fd, &tty); + oldtty = tty; + + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + |INLCR|IGNCR|ICRNL|IXON); + tty.c_oflag |= OPOST; + tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); + tty.c_cflag &= ~(CSIZE|PARENB); + tty.c_cflag |= CS8; + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + + tcsetattr(fd, TCSANOW, &tty); + + atexit(term_exit); + return JS_UNDEFINED; +} + +#endif /* !_WIN32 && !__wasi__ */ + +static JSValue js_os_remove(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *filename; + int ret; + + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + return JS_EXCEPTION; +#if defined(_WIN32) + { + struct stat st; + if (stat(filename, &st) == 0 && S_ISDIR(st.st_mode)) { + ret = rmdir(filename); + } else { + ret = unlink(filename); + } + } +#else + ret = remove(filename); +#endif + ret = js_get_errno(ret); + JS_FreeCString(ctx, filename); + return JS_NewInt32(ctx, ret); +} + +static JSValue js_os_rename(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *oldpath, *newpath; + int ret; + + oldpath = JS_ToCString(ctx, argv[0]); + if (!oldpath) + return JS_EXCEPTION; + newpath = JS_ToCString(ctx, argv[1]); + if (!newpath) { + JS_FreeCString(ctx, oldpath); + return JS_EXCEPTION; + } + ret = js_get_errno(rename(oldpath, newpath)); + JS_FreeCString(ctx, oldpath); + JS_FreeCString(ctx, newpath); + return JS_NewInt32(ctx, ret); +} + +static BOOL is_main_thread(JSRuntime *rt) +{ + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + return !ts->recv_pipe; +} + +static JSOSRWHandler *find_rh(JSThreadState *ts, int fd) +{ + JSOSRWHandler *rh; + struct list_head *el; + + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == fd) + return rh; + } + return NULL; +} + +static void free_rw_handler(JSRuntime *rt, JSOSRWHandler *rh) +{ + int i; + list_del(&rh->link); + for(i = 0; i < 2; i++) { + JS_FreeValueRT(rt, rh->rw_func[i]); + } + js_free_rt(rt, rh); +} + +static JSValue js_os_setReadHandler(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + JSOSRWHandler *rh; + int fd; + JSValue func; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + func = argv[1]; + if (JS_IsNull(func)) { + rh = find_rh(ts, fd); + if (rh) { + JS_FreeValue(ctx, rh->rw_func[magic]); + rh->rw_func[magic] = JS_NULL; + if (JS_IsNull(rh->rw_func[0]) && + JS_IsNull(rh->rw_func[1])) { + /* remove the entry */ + free_rw_handler(JS_GetRuntime(ctx), rh); + } + } + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + rh = find_rh(ts, fd); + if (!rh) { + rh = js_mallocz(ctx, sizeof(*rh)); + if (!rh) + return JS_EXCEPTION; + rh->fd = fd; + rh->rw_func[0] = JS_NULL; + rh->rw_func[1] = JS_NULL; + list_add_tail(&rh->link, &ts->os_rw_handlers); + } + JS_FreeValue(ctx, rh->rw_func[magic]); + rh->rw_func[magic] = JS_DupValue(ctx, func); + } + return JS_UNDEFINED; +} + +static JSOSSignalHandler *find_sh(JSThreadState *ts, int sig_num) +{ + JSOSSignalHandler *sh; + struct list_head *el; + list_for_each(el, &ts->os_signal_handlers) { + sh = list_entry(el, JSOSSignalHandler, link); + if (sh->sig_num == sig_num) + return sh; + } + return NULL; +} + +static void free_sh(JSRuntime *rt, JSOSSignalHandler *sh) +{ + list_del(&sh->link); + JS_FreeValueRT(rt, sh->func); + js_free_rt(rt, sh); +} + +static void os_signal_handler(int sig_num) +{ + os_pending_signals |= ((uint64_t)1 << sig_num); +} + +#if defined(_WIN32) +typedef void (*sighandler_t)(int sig_num); +#endif + +static JSValue js_os_signal(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + JSOSSignalHandler *sh; + uint32_t sig_num; + JSValue func; + sighandler_t handler; + + if (!is_main_thread(rt)) + return JS_ThrowTypeError(ctx, "signal handler can only be set in the main thread"); + + if (JS_ToUint32(ctx, &sig_num, argv[0])) + return JS_EXCEPTION; + if (sig_num >= 64) + return JS_ThrowRangeError(ctx, "invalid signal number"); + func = argv[1]; + /* func = null: SIG_DFL, func = undefined, SIG_IGN */ + if (JS_IsNull(func) || JS_IsUndefined(func)) { + sh = find_sh(ts, sig_num); + if (sh) { + free_sh(JS_GetRuntime(ctx), sh); + } + if (JS_IsNull(func)) + handler = SIG_DFL; + else + handler = SIG_IGN; + signal(sig_num, handler); + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + sh = find_sh(ts, sig_num); + if (!sh) { + sh = js_mallocz(ctx, sizeof(*sh)); + if (!sh) + return JS_EXCEPTION; + sh->sig_num = sig_num; + list_add_tail(&sh->link, &ts->os_signal_handlers); + } + JS_FreeValue(ctx, sh->func); + sh->func = JS_DupValue(ctx, func); + signal(sig_num, os_signal_handler); + } + return JS_UNDEFINED; +} + +#if !defined(_WIN32) && !defined(__wasi__) +static JSValue js_os_cputime(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + struct rusage ru; + int64_t cputime; + + cputime = 0; + if (!getrusage(RUSAGE_SELF, &ru)) + cputime = (int64_t)ru.ru_utime.tv_sec * 1000000 + ru.ru_utime.tv_usec; + + return JS_NewInt64(ctx, cputime); +} +#endif + +static JSValue js_os_now(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_NewInt64(ctx, js__hrtime_ns() / 1000); +} + +static uint64_t js__hrtime_ms(void) +{ + return js__hrtime_ns() / (1000 * 1000); +} + +static void unlink_timer(JSRuntime *rt, JSOSTimer *th) +{ + if (th->link.prev) { + list_del(&th->link); + th->link.prev = th->link.next = NULL; + } +} + +static void free_timer(JSRuntime *rt, JSOSTimer *th) +{ + JS_FreeValueRT(rt, th->func); + js_free_rt(rt, th); +} + +static JSClassID js_os_timer_class_id; + +static void js_os_timer_finalizer(JSRuntime *rt, JSValue val) +{ + JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id); + if (th) { + th->has_object = FALSE; + if (!th->link.prev) + free_timer(rt, th); + } +} + +static void js_os_timer_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSOSTimer *th = JS_GetOpaque(val, js_os_timer_class_id); + if (th) { + JS_MarkValue(rt, th->func, mark_func); + } +} + +// TODO(bnoordhuis) accept string as first arg and eval at timer expiry +// TODO(bnoordhuis) retain argv[2..] as args for callback if argc > 2 +static JSValue js_os_setTimeout(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + int64_t delay; + JSValue func; + JSOSTimer *th; + JSValue obj; + + func = argv[0]; + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + if (JS_ToInt64(ctx, &delay, argv[1])) + return JS_EXCEPTION; + if (delay < 1) + delay = 1; + obj = JS_NewObjectClass(ctx, js_os_timer_class_id); + if (JS_IsException(obj)) + return obj; + th = js_mallocz(ctx, sizeof(*th)); + if (!th) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + th->has_object = TRUE; + th->repeats = (magic > 0); + th->timeout = js__hrtime_ms() + delay; + th->delay = delay; + th->func = JS_DupValue(ctx, func); + list_add_tail(&th->link, &ts->os_timers); + JS_SetOpaque(obj, th); + return obj; +} + +static JSValue js_os_clearTimeout(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSOSTimer *th = JS_GetOpaque2(ctx, argv[0], js_os_timer_class_id); + if (!th) + return JS_EXCEPTION; + unlink_timer(JS_GetRuntime(ctx), th); + JS_FreeValue(ctx, th->func); + th->func = JS_UNDEFINED; + return JS_UNDEFINED; +} + +static JSClassDef js_os_timer_class = { + "OSTimer", + .finalizer = js_os_timer_finalizer, + .gc_mark = js_os_timer_mark, +}; + +/* return a promise */ +static JSValue js_os_sleepAsync(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + int64_t delay; + JSOSTimer *th; + JSValue promise, resolving_funcs[2]; + + if (JS_ToInt64(ctx, &delay, argv[0])) + return JS_EXCEPTION; + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + + th = js_mallocz(ctx, sizeof(*th)); + if (!th) { + JS_FreeValue(ctx, promise); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return JS_EXCEPTION; + } + th->has_object = FALSE; + th->timeout = js__hrtime_ms() + delay; + th->func = JS_DupValue(ctx, resolving_funcs[0]); + list_add_tail(&th->link, &ts->os_timers); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; +} + +static void call_handler(JSContext *ctx, JSValue func) +{ + JSValue ret, func1; + /* 'func' might be destroyed when calling itself (if it frees the + handler), so must take extra care */ + func1 = JS_DupValue(ctx, func); + ret = JS_Call(ctx, func1, JS_UNDEFINED, 0, NULL); + JS_FreeValue(ctx, func1); + if (JS_IsException(ret)) + js_std_dump_error(ctx); + JS_FreeValue(ctx, ret); +} + +static int js_os_run_timers(JSRuntime *rt, JSContext *ctx, JSThreadState *ts) +{ + JSValue func; + JSOSTimer *th; + int min_delay; + int64_t cur_time, delay; + struct list_head *el; + + if (list_empty(&ts->os_timers)) + return -1; + + cur_time = js__hrtime_ms(); + min_delay = 10000; + + list_for_each(el, &ts->os_timers) { + th = list_entry(el, JSOSTimer, link); + delay = th->timeout - cur_time; + if (delay > 0) { + min_delay = min_int(min_delay, delay); + } else { + func = JS_DupValueRT(rt, th->func); + unlink_timer(rt, th); + if (th->repeats) { + th->timeout = cur_time + th->delay; + list_add_tail(&th->link, &ts->os_timers); + } else if (!th->has_object) { + free_timer(rt, th); + } + call_handler(ctx, func); + JS_FreeValueRT(rt, func); + return 0; + } + } + + return min_delay; +} + +#if defined(_WIN32) + +static int js_os_poll(JSContext *ctx) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + int min_delay, console_fd; + JSOSRWHandler *rh; + struct list_head *el; + + /* XXX: handle signals if useful */ + + min_delay = js_os_run_timers(rt, ctx, ts); + if (min_delay == 0) + return 0; // expired timer + if (min_delay < 0) + if (list_empty(&ts->os_rw_handlers)) + return -1; /* no more events */ + + console_fd = -1; + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == 0 && !JS_IsNull(rh->rw_func[0])) { + console_fd = rh->fd; + break; + } + } + + if (console_fd >= 0) { + DWORD ti, ret; + HANDLE handle; + if (min_delay == -1) + ti = INFINITE; + else + ti = min_delay; + handle = (HANDLE)_get_osfhandle(console_fd); + ret = WaitForSingleObject(handle, ti); + if (ret == WAIT_OBJECT_0) { + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (rh->fd == console_fd && !JS_IsNull(rh->rw_func[0])) { + call_handler(ctx, rh->rw_func[0]); + /* must stop because the list may have been modified */ + break; + } + } + } + } else { + Sleep(min_delay); + } + return 0; +} +#else + +#ifdef USE_WORKER + +static void js_free_message(JSWorkerMessage *msg); + +/* return 1 if a message was handled, 0 if no message */ +static int handle_posted_message(JSRuntime *rt, JSContext *ctx, + JSWorkerMessageHandler *port) +{ + JSWorkerMessagePipe *ps = port->recv_pipe; + int ret; + struct list_head *el; + JSWorkerMessage *msg; + JSValue obj, data_obj, func, retval; + + pthread_mutex_lock(&ps->mutex); + if (!list_empty(&ps->msg_queue)) { + el = ps->msg_queue.next; + msg = list_entry(el, JSWorkerMessage, link); + + /* remove the message from the queue */ + list_del(&msg->link); + + if (list_empty(&ps->msg_queue)) { + uint8_t buf[16]; + int ret; + for(;;) { + ret = read(ps->read_fd, buf, sizeof(buf)); + if (ret >= 0) + break; + if (errno != EAGAIN && errno != EINTR) + break; + } + } + + pthread_mutex_unlock(&ps->mutex); + + data_obj = JS_ReadObject(ctx, msg->data, msg->data_len, + JS_READ_OBJ_SAB | JS_READ_OBJ_REFERENCE); + + js_free_message(msg); + + if (JS_IsException(data_obj)) + goto fail; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, data_obj); + goto fail; + } + JS_DefinePropertyValueStr(ctx, obj, "data", data_obj, JS_PROP_C_W_E); + + /* 'func' might be destroyed when calling itself (if it frees the + handler), so must take extra care */ + func = JS_DupValue(ctx, port->on_message_func); + retval = JS_Call(ctx, func, JS_UNDEFINED, 1, &obj); + JS_FreeValue(ctx, obj); + JS_FreeValue(ctx, func); + if (JS_IsException(retval)) { + fail: + js_std_dump_error(ctx); + } else { + JS_FreeValue(ctx, retval); + } + ret = 1; + } else { + pthread_mutex_unlock(&ps->mutex); + ret = 0; + } + return ret; +} +#else +static int handle_posted_message(JSRuntime *rt, JSContext *ctx, + JSWorkerMessageHandler *port) +{ + return 0; +} +#endif + +static int js_os_poll(JSContext *ctx) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + int ret, fd_max, min_delay; + fd_set rfds, wfds; + JSOSRWHandler *rh; + struct list_head *el; + struct timeval tv, *tvp; + + /* only check signals in the main thread */ + if (!ts->recv_pipe && + unlikely(os_pending_signals != 0)) { + JSOSSignalHandler *sh; + uint64_t mask; + + list_for_each(el, &ts->os_signal_handlers) { + sh = list_entry(el, JSOSSignalHandler, link); + mask = (uint64_t)1 << sh->sig_num; + if (os_pending_signals & mask) { + os_pending_signals &= ~mask; + call_handler(ctx, sh->func); + return 0; + } + } + } + + min_delay = js_os_run_timers(rt, ctx, ts); + if (min_delay == 0) + return 0; // expired timer + if (min_delay < 0) + if (list_empty(&ts->os_rw_handlers) && list_empty(&ts->port_list)) + return -1; /* no more events */ + + tvp = NULL; + if (min_delay >= 0) { + tv.tv_sec = min_delay / 1000; + tv.tv_usec = (min_delay % 1000) * 1000; + tvp = &tv; + } + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + fd_max = -1; + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + fd_max = max_int(fd_max, rh->fd); + if (!JS_IsNull(rh->rw_func[0])) + FD_SET(rh->fd, &rfds); + if (!JS_IsNull(rh->rw_func[1])) + FD_SET(rh->fd, &wfds); + } + + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (!JS_IsNull(port->on_message_func)) { + JSWorkerMessagePipe *ps = port->recv_pipe; + fd_max = max_int(fd_max, ps->read_fd); + FD_SET(ps->read_fd, &rfds); + } + } + + ret = select(fd_max + 1, &rfds, &wfds, NULL, tvp); + if (ret > 0) { + list_for_each(el, &ts->os_rw_handlers) { + rh = list_entry(el, JSOSRWHandler, link); + if (!JS_IsNull(rh->rw_func[0]) && + FD_ISSET(rh->fd, &rfds)) { + call_handler(ctx, rh->rw_func[0]); + /* must stop because the list may have been modified */ + goto done; + } + if (!JS_IsNull(rh->rw_func[1]) && + FD_ISSET(rh->fd, &wfds)) { + call_handler(ctx, rh->rw_func[1]); + /* must stop because the list may have been modified */ + goto done; + } + } + + list_for_each(el, &ts->port_list) { + JSWorkerMessageHandler *port = list_entry(el, JSWorkerMessageHandler, link); + if (!JS_IsNull(port->on_message_func)) { + JSWorkerMessagePipe *ps = port->recv_pipe; + if (FD_ISSET(ps->read_fd, &rfds)) { + if (handle_posted_message(rt, ctx, port)) + goto done; + } + } + } + } + done: + return 0; +} +#endif /* !_WIN32 */ + +static JSValue make_obj_error(JSContext *ctx, + JSValue obj, + int err) +{ + JSValue arr; + if (JS_IsException(obj)) + return obj; + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + return JS_EXCEPTION; + JS_DefinePropertyValueUint32(ctx, arr, 0, obj, + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, arr, 1, JS_NewInt32(ctx, err), + JS_PROP_C_W_E); + return arr; +} + +static JSValue make_string_error(JSContext *ctx, + const char *buf, + int err) +{ + return make_obj_error(ctx, JS_NewString(ctx, buf), err); +} + +/* return [cwd, errorcode] */ +static JSValue js_os_getcwd(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + char buf[PATH_MAX]; + int err; + + if (!getcwd(buf, sizeof(buf))) { + buf[0] = '\0'; + err = errno; + } else { + err = 0; + } + return make_string_error(ctx, buf, err); +} + +static JSValue js_os_chdir(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *target; + int err; + + target = JS_ToCString(ctx, argv[0]); + if (!target) + return JS_EXCEPTION; + err = js_get_errno(chdir(target)); + JS_FreeCString(ctx, target); + return JS_NewInt32(ctx, err); +} + +static JSValue js_os_mkdir(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int mode, ret; + const char *path; + + if (argc >= 2) { + if (JS_ToInt32(ctx, &mode, argv[1])) + return JS_EXCEPTION; + } else { + mode = 0777; + } + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; +#if defined(_WIN32) + (void)mode; + ret = js_get_errno(mkdir(path)); +#else + ret = js_get_errno(mkdir(path, mode)); +#endif + JS_FreeCString(ctx, path); + return JS_NewInt32(ctx, ret); +} + +/* return [array, errorcode] */ +static JSValue js_os_readdir(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *path; + DIR *f; + struct dirent *d; + JSValue obj; + int err; + uint32_t len; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) { + JS_FreeCString(ctx, path); + return JS_EXCEPTION; + } + f = opendir(path); + if (!f) + err = errno; + else + err = 0; + JS_FreeCString(ctx, path); + if (!f) + goto done; + len = 0; + for(;;) { + errno = 0; + d = readdir(f); + if (!d) { + err = errno; + break; + } + JS_DefinePropertyValueUint32(ctx, obj, len++, + JS_NewString(ctx, d->d_name), + JS_PROP_C_W_E); + } + closedir(f); + done: + return make_obj_error(ctx, obj, err); +} + +#if !defined(_WIN32) +static int64_t timespec_to_ms(const struct timespec *tv) +{ + return (int64_t)tv->tv_sec * 1000 + (tv->tv_nsec / 1000000); +} +#endif + +/* return [obj, errcode] */ +static JSValue js_os_stat(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int is_lstat) +{ + const char *path; + int err, res; + struct stat st; + JSValue obj; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; +#if defined(_WIN32) + res = stat(path, &st); +#else + if (is_lstat) + res = lstat(path, &st); + else + res = stat(path, &st); +#endif + err = (res < 0) ? errno : 0; + JS_FreeCString(ctx, path); + if (res < 0) { + obj = JS_NULL; + } else { + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + JS_DefinePropertyValueStr(ctx, obj, "dev", + JS_NewInt64(ctx, st.st_dev), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ino", + JS_NewInt64(ctx, st.st_ino), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mode", + JS_NewInt32(ctx, st.st_mode), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "nlink", + JS_NewInt64(ctx, st.st_nlink), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "uid", + JS_NewInt64(ctx, st.st_uid), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "gid", + JS_NewInt64(ctx, st.st_gid), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "rdev", + JS_NewInt64(ctx, st.st_rdev), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "size", + JS_NewInt64(ctx, st.st_size), + JS_PROP_C_W_E); +#if !defined(_WIN32) + JS_DefinePropertyValueStr(ctx, obj, "blocks", + JS_NewInt64(ctx, st.st_blocks), + JS_PROP_C_W_E); +#endif +#if defined(_WIN32) + JS_DefinePropertyValueStr(ctx, obj, "atime", + JS_NewInt64(ctx, (int64_t)st.st_atime * 1000), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, (int64_t)st.st_mtime * 1000), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, (int64_t)st.st_ctime * 1000), + JS_PROP_C_W_E); +#elif defined(__APPLE__) + JS_DefinePropertyValueStr(ctx, obj, "atime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_atimespec)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_mtimespec)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_ctimespec)), + JS_PROP_C_W_E); +#else + JS_DefinePropertyValueStr(ctx, obj, "atime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_atim)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "mtime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_mtim)), + JS_PROP_C_W_E); + JS_DefinePropertyValueStr(ctx, obj, "ctime", + JS_NewInt64(ctx, timespec_to_ms(&st.st_ctim)), + JS_PROP_C_W_E); +#endif + } + return make_obj_error(ctx, obj, err); +} + +#if !defined(_WIN32) +static void ms_to_timeval(struct timeval *tv, uint64_t v) +{ + tv->tv_sec = v / 1000; + tv->tv_usec = (v % 1000) * 1000; +} +#endif + +static JSValue js_os_utimes(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *path; + int64_t atime, mtime; + int ret; + + if (JS_ToInt64(ctx, &atime, argv[1])) + return JS_EXCEPTION; + if (JS_ToInt64(ctx, &mtime, argv[2])) + return JS_EXCEPTION; + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; +#if defined(_WIN32) + { + struct _utimbuf times; + times.actime = atime / 1000; + times.modtime = mtime / 1000; + ret = js_get_errno(_utime(path, ×)); + } +#else + { + struct timeval times[2]; + ms_to_timeval(×[0], atime); + ms_to_timeval(×[1], mtime); + ret = js_get_errno(utimes(path, times)); + } +#endif + JS_FreeCString(ctx, path); + return JS_NewInt32(ctx, ret); +} + +/* sleep(delay_ms) */ +static JSValue js_os_sleep(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int64_t delay; + int ret; + + if (JS_ToInt64(ctx, &delay, argv[0])) + return JS_EXCEPTION; + if (delay < 0) + delay = 0; +#if defined(_WIN32) + { + if (delay > INT32_MAX) + delay = INT32_MAX; + Sleep(delay); + ret = 0; + } +#else + { + struct timespec ts; + + ts.tv_sec = delay / 1000; + ts.tv_nsec = (delay % 1000) * 1000000; + ret = js_get_errno(nanosleep(&ts, NULL)); + } +#endif + return JS_NewInt32(ctx, ret); +} + +#if defined(_WIN32) +static char *realpath(const char *path, char *buf) +{ + if (!_fullpath(buf, path, PATH_MAX)) { + errno = ENOENT; + return NULL; + } else { + return buf; + } +} +#endif + +#if !defined(__wasi__) +/* return [path, errorcode] */ +static JSValue js_os_realpath(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *path; + char buf[PATH_MAX], *res; + int err; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + res = realpath(path, buf); + JS_FreeCString(ctx, path); + if (!res) { + buf[0] = '\0'; + err = errno; + } else { + err = 0; + } + return make_string_error(ctx, buf, err); +} +#endif + +#if !defined(_WIN32) && !defined(__wasi__) +static JSValue js_os_symlink(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *target, *linkpath; + int err; + + target = JS_ToCString(ctx, argv[0]); + if (!target) + return JS_EXCEPTION; + linkpath = JS_ToCString(ctx, argv[1]); + if (!linkpath) { + JS_FreeCString(ctx, target); + return JS_EXCEPTION; + } + err = js_get_errno(symlink(target, linkpath)); + JS_FreeCString(ctx, target); + JS_FreeCString(ctx, linkpath); + return JS_NewInt32(ctx, err); +} + +/* return [path, errorcode] */ +static JSValue js_os_readlink(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + const char *path; + char buf[PATH_MAX]; + int err; + ssize_t res; + + path = JS_ToCString(ctx, argv[0]); + if (!path) + return JS_EXCEPTION; + res = readlink(path, buf, sizeof(buf) - 1); + if (res < 0) { + buf[0] = '\0'; + err = errno; + } else { + buf[res] = '\0'; + err = 0; + } + JS_FreeCString(ctx, path); + return make_string_error(ctx, buf, err); +} + +static char **build_envp(JSContext *ctx, JSValue obj) +{ + uint32_t len, i; + JSPropertyEnum *tab; + char **envp, *pair; + const char *key, *str; + JSValue val; + size_t key_len, str_len; + + if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) + return NULL; + envp = js_mallocz(ctx, sizeof(envp[0]) * ((size_t)len + 1)); + if (!envp) + goto fail; + for(i = 0; i < len; i++) { + val = JS_GetProperty(ctx, obj, tab[i].atom); + if (JS_IsException(val)) + goto fail; + str = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!str) + goto fail; + key = JS_AtomToCString(ctx, tab[i].atom); + if (!key) { + JS_FreeCString(ctx, str); + goto fail; + } + key_len = strlen(key); + str_len = strlen(str); + pair = js_malloc(ctx, key_len + str_len + 2); + if (!pair) { + JS_FreeCString(ctx, key); + JS_FreeCString(ctx, str); + goto fail; + } + memcpy(pair, key, key_len); + pair[key_len] = '='; + memcpy(pair + key_len + 1, str, str_len); + pair[key_len + 1 + str_len] = '\0'; + envp[i] = pair; + JS_FreeCString(ctx, key); + JS_FreeCString(ctx, str); + } + done: + for(i = 0; i < len; i++) + JS_FreeAtom(ctx, tab[i].atom); + js_free(ctx, tab); + return envp; + fail: + if (envp) { + for(i = 0; i < len; i++) + js_free(ctx, envp[i]); + js_free(ctx, envp); + envp = NULL; + } + goto done; +} + +/* execvpe is not available on non GNU systems */ +static int my_execvpe(const char *filename, char **argv, char **envp) +{ + char *path, *p, *p_next, *p1; + char buf[PATH_MAX]; + size_t filename_len, path_len; + BOOL eacces_error; + + filename_len = strlen(filename); + if (filename_len == 0) { + errno = ENOENT; + return -1; + } + if (strchr(filename, '/')) + return execve(filename, argv, envp); + + path = getenv("PATH"); + if (!path) + path = (char *)"/bin:/usr/bin"; + eacces_error = FALSE; + p = path; + for(p = path; p != NULL; p = p_next) { + p1 = strchr(p, ':'); + if (!p1) { + p_next = NULL; + path_len = strlen(p); + } else { + p_next = p1 + 1; + path_len = p1 - p; + } + /* path too long */ + if ((path_len + 1 + filename_len + 1) > PATH_MAX) + continue; + memcpy(buf, p, path_len); + buf[path_len] = '/'; + memcpy(buf + path_len + 1, filename, filename_len); + buf[path_len + 1 + filename_len] = '\0'; + + execve(buf, argv, envp); + + switch(errno) { + case EACCES: + eacces_error = TRUE; + break; + case ENOENT: + case ENOTDIR: + break; + default: + return -1; + } + } + if (eacces_error) + errno = EACCES; + return -1; +} + +/* exec(args[, options]) -> exitcode */ +static JSValue js_os_exec(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSValue options, args = argv[0]; + JSValue val, ret_val; + const char **exec_argv, *file = NULL, *str, *cwd = NULL; + char **envp = environ; + uint32_t exec_argc, i; + int ret, pid, status; + BOOL block_flag = TRUE, use_path = TRUE; + static const char *std_name[3] = { "stdin", "stdout", "stderr" }; + int std_fds[3]; + uint32_t uid = -1, gid = -1; + + val = JS_GetPropertyStr(ctx, args, "length"); + if (JS_IsException(val)) + return JS_EXCEPTION; + ret = JS_ToUint32(ctx, &exec_argc, val); + JS_FreeValue(ctx, val); + if (ret) + return JS_EXCEPTION; + /* arbitrary limit to avoid overflow */ + if (exec_argc < 1 || exec_argc > 65535) { + return JS_ThrowTypeError(ctx, "invalid number of arguments"); + } + exec_argv = js_mallocz(ctx, sizeof(exec_argv[0]) * (exec_argc + 1)); + if (!exec_argv) + return JS_EXCEPTION; + for(i = 0; i < exec_argc; i++) { + val = JS_GetPropertyUint32(ctx, args, i); + if (JS_IsException(val)) + goto exception; + str = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!str) + goto exception; + exec_argv[i] = str; + } + exec_argv[exec_argc] = NULL; + + for(i = 0; i < 3; i++) + std_fds[i] = i; + + /* get the options, if any */ + if (argc >= 2) { + options = argv[1]; + + if (get_bool_option(ctx, &block_flag, options, "block")) + goto exception; + if (get_bool_option(ctx, &use_path, options, "usePath")) + goto exception; + + val = JS_GetPropertyStr(ctx, options, "file"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + file = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!file) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "cwd"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + cwd = JS_ToCString(ctx, val); + JS_FreeValue(ctx, val); + if (!cwd) + goto exception; + } + + /* stdin/stdout/stderr handles */ + for(i = 0; i < 3; i++) { + val = JS_GetPropertyStr(ctx, options, std_name[i]); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + int fd; + ret = JS_ToInt32(ctx, &fd, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + std_fds[i] = fd; + } + } + + val = JS_GetPropertyStr(ctx, options, "env"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + envp = build_envp(ctx, val); + JS_FreeValue(ctx, val); + if (!envp) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "uid"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + ret = JS_ToUint32(ctx, &uid, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + } + + val = JS_GetPropertyStr(ctx, options, "gid"); + if (JS_IsException(val)) + goto exception; + if (!JS_IsUndefined(val)) { + ret = JS_ToUint32(ctx, &gid, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + } + } + + pid = fork(); + if (pid < 0) { + JS_ThrowTypeError(ctx, "fork error"); + goto exception; + } + if (pid == 0) { + /* child */ + int fd_max = sysconf(_SC_OPEN_MAX); + + /* remap the stdin/stdout/stderr handles if necessary */ + for(i = 0; i < 3; i++) { + if (std_fds[i] != i) { + if (dup2(std_fds[i], i) < 0) + _exit(127); + } + } + + for(i = 3; i < fd_max; i++) + close(i); + if (cwd) { + if (chdir(cwd) < 0) + _exit(127); + } + if (uid != -1) { + if (setuid(uid) < 0) + _exit(127); + } + if (gid != -1) { + if (setgid(gid) < 0) + _exit(127); + } + + if (!file) + file = exec_argv[0]; + if (use_path) + ret = my_execvpe(file, (char **)exec_argv, envp); + else + ret = execve(file, (char **)exec_argv, envp); + _exit(127); + } + /* parent */ + if (block_flag) { + for(;;) { + ret = waitpid(pid, &status, 0); + if (ret == pid) { + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + break; + } else if (WIFSIGNALED(status)) { + ret = -WTERMSIG(status); + break; + } + } + } + } else { + ret = pid; + } + ret_val = JS_NewInt32(ctx, ret); + done: + JS_FreeCString(ctx, file); + JS_FreeCString(ctx, cwd); + for(i = 0; i < exec_argc; i++) + JS_FreeCString(ctx, exec_argv[i]); + js_free(ctx, exec_argv); + if (envp != environ) { + char **p; + p = envp; + while (*p != NULL) { + js_free(ctx, *p); + p++; + } + js_free(ctx, envp); + } + return ret_val; + exception: + ret_val = JS_EXCEPTION; + goto done; +} + +/* getpid() -> pid */ +static JSValue js_os_getpid(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_NewInt32(ctx, getpid()); +} + +/* waitpid(pid, block) -> [pid, status] */ +static JSValue js_os_waitpid(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int pid, status, options, ret; + JSValue obj; + + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &options, argv[1])) + return JS_EXCEPTION; + + ret = waitpid(pid, &status, options); + if (ret < 0) { + ret = -errno; + status = 0; + } + + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, ret), + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, status), + JS_PROP_C_W_E); + return obj; +} + +/* pipe() -> [read_fd, write_fd] or null if error */ +static JSValue js_os_pipe(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int pipe_fds[2], ret; + JSValue obj; + + ret = pipe(pipe_fds); + if (ret < 0) + return JS_NULL; + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]), + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]), + JS_PROP_C_W_E); + return obj; +} + +/* kill(pid, sig) */ +static JSValue js_os_kill(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int pid, sig, ret; + + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &sig, argv[1])) + return JS_EXCEPTION; + ret = js_get_errno(kill(pid, sig)); + return JS_NewInt32(ctx, ret); +} + +/* dup(fd) */ +static JSValue js_os_dup(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd, ret; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + ret = js_get_errno(dup(fd)); + return JS_NewInt32(ctx, ret); +} + +/* dup2(fd) */ +static JSValue js_os_dup2(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int fd, fd2, ret; + + if (JS_ToInt32(ctx, &fd, argv[0])) + return JS_EXCEPTION; + if (JS_ToInt32(ctx, &fd2, argv[1])) + return JS_EXCEPTION; + ret = js_get_errno(dup2(fd, fd2)); + return JS_NewInt32(ctx, ret); +} + +#endif /* !_WIN32 */ + +#ifdef USE_WORKER + +/* Worker */ + +typedef struct { + JSWorkerMessagePipe *recv_pipe; + JSWorkerMessagePipe *send_pipe; + JSWorkerMessageHandler *msg_handler; +} JSWorkerData; + +typedef struct { + char *filename; /* module filename */ + char *basename; /* module base name */ + JSWorkerMessagePipe *recv_pipe, *send_pipe; +} WorkerFuncArgs; + +typedef struct { + int ref_count; + uint64_t buf[]; +} JSSABHeader; + +static JSClassID js_worker_class_id; +static JSContext *(*js_worker_new_context_func)(JSRuntime *rt); + +static int atomic_add_int(int *ptr, int v) +{ + return atomic_fetch_add((_Atomic uint32_t*)ptr, v) + v; +} + +/* shared array buffer allocator */ +static void *js_sab_alloc(void *opaque, size_t size) +{ + JSSABHeader *sab; + sab = malloc(sizeof(JSSABHeader) + size); + if (!sab) + return NULL; + sab->ref_count = 1; + return sab->buf; +} + +static void js_sab_free(void *opaque, void *ptr) +{ + JSSABHeader *sab; + int ref_count; + sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader)); + ref_count = atomic_add_int(&sab->ref_count, -1); + assert(ref_count >= 0); + if (ref_count == 0) { + free(sab); + } +} + +static void js_sab_dup(void *opaque, void *ptr) +{ + JSSABHeader *sab; + sab = (JSSABHeader *)((uint8_t *)ptr - sizeof(JSSABHeader)); + atomic_add_int(&sab->ref_count, 1); +} + +static JSWorkerMessagePipe *js_new_message_pipe(void) +{ + JSWorkerMessagePipe *ps; + int pipe_fds[2]; + + if (pipe(pipe_fds) < 0) + return NULL; + + ps = malloc(sizeof(*ps)); + if (!ps) { + close(pipe_fds[0]); + close(pipe_fds[1]); + return NULL; + } + ps->ref_count = 1; + init_list_head(&ps->msg_queue); + pthread_mutex_init(&ps->mutex, NULL); + ps->read_fd = pipe_fds[0]; + ps->write_fd = pipe_fds[1]; + return ps; +} + +static JSWorkerMessagePipe *js_dup_message_pipe(JSWorkerMessagePipe *ps) +{ + atomic_add_int(&ps->ref_count, 1); + return ps; +} + +static void js_free_message(JSWorkerMessage *msg) +{ + size_t i; + /* free the SAB */ + for(i = 0; i < msg->sab_tab_len; i++) { + js_sab_free(NULL, msg->sab_tab[i]); + } + free(msg->sab_tab); + free(msg->data); + free(msg); +} + +static void js_free_message_pipe(JSWorkerMessagePipe *ps) +{ + struct list_head *el, *el1; + JSWorkerMessage *msg; + int ref_count; + + if (!ps) + return; + + ref_count = atomic_add_int(&ps->ref_count, -1); + assert(ref_count >= 0); + if (ref_count == 0) { + list_for_each_safe(el, el1, &ps->msg_queue) { + msg = list_entry(el, JSWorkerMessage, link); + js_free_message(msg); + } + pthread_mutex_destroy(&ps->mutex); + close(ps->read_fd); + close(ps->write_fd); + free(ps); + } +} + +static void js_free_port(JSRuntime *rt, JSWorkerMessageHandler *port) +{ + if (port) { + js_free_message_pipe(port->recv_pipe); + JS_FreeValueRT(rt, port->on_message_func); + list_del(&port->link); + js_free_rt(rt, port); + } +} + +static void js_worker_finalizer(JSRuntime *rt, JSValue val) +{ + JSWorkerData *worker = JS_GetOpaque(val, js_worker_class_id); + if (worker) { + js_free_message_pipe(worker->recv_pipe); + js_free_message_pipe(worker->send_pipe); + js_free_port(rt, worker->msg_handler); + js_free_rt(rt, worker); + } +} + +static JSClassDef js_worker_class = { + "Worker", + .finalizer = js_worker_finalizer, +}; + +static void *worker_func(void *opaque) +{ + WorkerFuncArgs *args = opaque; + JSRuntime *rt; + JSThreadState *ts; + JSContext *ctx; + JSValue val; + + rt = JS_NewRuntime(); + if (rt == NULL) { + fprintf(stderr, "JS_NewRuntime failure"); + exit(1); + } + js_std_init_handlers(rt); + + JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL); + + /* set the pipe to communicate with the parent */ + ts = JS_GetRuntimeOpaque(rt); + ts->recv_pipe = args->recv_pipe; + ts->send_pipe = args->send_pipe; + + /* function pointer to avoid linking the whole JS_NewContext() if + not needed */ + ctx = js_worker_new_context_func(rt); + if (ctx == NULL) { + fprintf(stderr, "JS_NewContext failure"); + } + + JS_SetCanBlock(rt, TRUE); + + js_std_add_helpers(ctx, -1, NULL); + + val = JS_LoadModule(ctx, args->basename, args->filename); + free(args->filename); + free(args->basename); + free(args); + val = js_std_await(ctx, val); + if (JS_IsException(val)) + js_std_dump_error(ctx); + JS_FreeValue(ctx, val); + + js_std_loop(ctx); + + JS_FreeContext(ctx); + js_std_free_handlers(rt); + JS_FreeRuntime(rt); + return NULL; +} + +static JSValue js_worker_ctor_internal(JSContext *ctx, JSValue new_target, + JSWorkerMessagePipe *recv_pipe, + JSWorkerMessagePipe *send_pipe) +{ + JSValue obj = JS_UNDEFINED, proto; + JSWorkerData *s; + + /* create the object */ + if (JS_IsUndefined(new_target)) { + proto = JS_GetClassProto(ctx, js_worker_class_id); + } else { + proto = JS_GetPropertyStr(ctx, new_target, "prototype"); + if (JS_IsException(proto)) + goto fail; + } + obj = JS_NewObjectProtoClass(ctx, proto, js_worker_class_id); + JS_FreeValue(ctx, proto); + if (JS_IsException(obj)) + goto fail; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + goto fail; + s->recv_pipe = js_dup_message_pipe(recv_pipe); + s->send_pipe = js_dup_message_pipe(send_pipe); + + JS_SetOpaque(obj, s); + return obj; + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_worker_ctor(JSContext *ctx, JSValue new_target, + int argc, JSValue *argv) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + WorkerFuncArgs *args = NULL; + pthread_t tid; + pthread_attr_t attr; + JSValue obj = JS_UNDEFINED; + int ret; + const char *filename = NULL, *basename; + JSAtom basename_atom; + + /* XXX: in order to avoid problems with resource liberation, we + don't support creating workers inside workers */ + if (!is_main_thread(rt)) + return JS_ThrowTypeError(ctx, "cannot create a worker inside a worker"); + + /* base name, assuming the calling function is a normal JS + function */ + basename_atom = JS_GetScriptOrModuleName(ctx, 1); + if (basename_atom == JS_ATOM_NULL) { + return JS_ThrowTypeError(ctx, "could not determine calling script or module name"); + } + basename = JS_AtomToCString(ctx, basename_atom); + JS_FreeAtom(ctx, basename_atom); + if (!basename) + goto fail; + + /* module name */ + filename = JS_ToCString(ctx, argv[0]); + if (!filename) + goto fail; + + args = malloc(sizeof(*args)); + if (!args) + goto oom_fail; + memset(args, 0, sizeof(*args)); + args->filename = strdup(filename); + args->basename = strdup(basename); + + /* ports */ + args->recv_pipe = js_new_message_pipe(); + if (!args->recv_pipe) + goto oom_fail; + args->send_pipe = js_new_message_pipe(); + if (!args->send_pipe) + goto oom_fail; + + obj = js_worker_ctor_internal(ctx, new_target, + args->send_pipe, args->recv_pipe); + if (JS_IsException(obj)) + goto fail; + + pthread_attr_init(&attr); + /* no join at the end */ + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + // musl libc gives threads 80 kb stacks, much smaller than + // JS_DEFAULT_STACK_SIZE (256 kb) + pthread_attr_setstacksize(&attr, 2 << 20); // 2 MB, glibc default + ret = pthread_create(&tid, &attr, worker_func, args); + pthread_attr_destroy(&attr); + if (ret != 0) { + JS_ThrowTypeError(ctx, "could not create worker"); + goto fail; + } + JS_FreeCString(ctx, basename); + JS_FreeCString(ctx, filename); + return obj; + oom_fail: + JS_ThrowOutOfMemory(ctx); + fail: + JS_FreeCString(ctx, basename); + JS_FreeCString(ctx, filename); + if (args) { + free(args->filename); + free(args->basename); + js_free_message_pipe(args->recv_pipe); + js_free_message_pipe(args->send_pipe); + free(args); + } + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_worker_postMessage(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id); + JSWorkerMessagePipe *ps; + size_t data_len, i; + uint8_t *data; + JSWorkerMessage *msg; + JSSABTab sab_tab; + + if (!worker) + return JS_EXCEPTION; + + data = JS_WriteObject2(ctx, &data_len, argv[0], + JS_WRITE_OBJ_SAB | JS_WRITE_OBJ_REFERENCE, + &sab_tab); + if (!data) + return JS_EXCEPTION; + + msg = malloc(sizeof(*msg)); + if (!msg) + goto fail; + msg->data = NULL; + msg->sab_tab = NULL; + + /* must reallocate because the allocator may be different */ + msg->data = malloc(data_len); + if (!msg->data) + goto fail; + memcpy(msg->data, data, data_len); + msg->data_len = data_len; + + if (sab_tab.len > 0) { + msg->sab_tab = malloc(sizeof(msg->sab_tab[0]) * sab_tab.len); + if (!msg->sab_tab) + goto fail; + memcpy(msg->sab_tab, sab_tab.tab, sizeof(msg->sab_tab[0]) * sab_tab.len); + } + msg->sab_tab_len = sab_tab.len; + + js_free(ctx, data); + js_free(ctx, sab_tab.tab); + + /* increment the SAB reference counts */ + for(i = 0; i < msg->sab_tab_len; i++) { + js_sab_dup(NULL, msg->sab_tab[i]); + } + + ps = worker->send_pipe; + pthread_mutex_lock(&ps->mutex); + /* indicate that data is present */ + if (list_empty(&ps->msg_queue)) { + uint8_t ch = '\0'; + int ret; + for(;;) { + ret = write(ps->write_fd, &ch, 1); + if (ret == 1) + break; + if (ret < 0 && (errno != EAGAIN || errno != EINTR)) + break; + } + } + list_add_tail(&msg->link, &ps->msg_queue); + pthread_mutex_unlock(&ps->mutex); + return JS_UNDEFINED; + fail: + if (msg) { + free(msg->data); + free(msg->sab_tab); + free(msg); + } + js_free(ctx, data); + js_free(ctx, sab_tab.tab); + return JS_EXCEPTION; + +} + +static JSValue js_worker_set_onmessage(JSContext *ctx, JSValue this_val, + JSValue func) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id); + JSWorkerMessageHandler *port; + + if (!worker) + return JS_EXCEPTION; + + port = worker->msg_handler; + if (JS_IsNull(func)) { + if (port) { + js_free_port(rt, port); + worker->msg_handler = NULL; + } + } else { + if (!JS_IsFunction(ctx, func)) + return JS_ThrowTypeError(ctx, "not a function"); + if (!port) { + port = js_mallocz(ctx, sizeof(*port)); + if (!port) + return JS_EXCEPTION; + port->recv_pipe = js_dup_message_pipe(worker->recv_pipe); + port->on_message_func = JS_NULL; + list_add_tail(&port->link, &ts->port_list); + worker->msg_handler = port; + } + JS_FreeValue(ctx, port->on_message_func); + port->on_message_func = JS_DupValue(ctx, func); + } + return JS_UNDEFINED; +} + +static JSValue js_worker_get_onmessage(JSContext *ctx, JSValue this_val) +{ + JSWorkerData *worker = JS_GetOpaque2(ctx, this_val, js_worker_class_id); + JSWorkerMessageHandler *port; + if (!worker) + return JS_EXCEPTION; + port = worker->msg_handler; + if (port) { + return JS_DupValue(ctx, port->on_message_func); + } else { + return JS_NULL; + } +} + +static const JSCFunctionListEntry js_worker_proto_funcs[] = { + JS_CFUNC_DEF("postMessage", 1, js_worker_postMessage ), + JS_CGETSET_DEF("onmessage", js_worker_get_onmessage, js_worker_set_onmessage ), +}; + +#endif /* USE_WORKER */ + +void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)) +{ +#ifdef USE_WORKER + js_worker_new_context_func = func; +#endif +} + +#if defined(_WIN32) +#define OS_PLATFORM "win32" +#elif defined(__APPLE__) +#define OS_PLATFORM "darwin" +#elif defined(EMSCRIPTEN) +#define OS_PLATFORM "js" +#elif defined(__CYGWIN__) +#define OS_PLATFORM "cygwin" +#elif defined(__linux__) +#define OS_PLATFORM "linux" +#elif defined(__OpenBSD__) +#define OS_PLATFORM "openbsd" +#elif defined(__NetBSD__) +#define OS_PLATFORM "netbsd" +#elif defined(__FreeBSD__) +#define OS_PLATFORM "freebsd" +#elif defined(__wasi__) +#define OS_PLATFORM "wasi" +#else +#define OS_PLATFORM "unknown" +#endif + +#define OS_FLAG(x) JS_PROP_INT32_DEF(#x, x, JS_PROP_CONFIGURABLE ) + +static const JSCFunctionListEntry js_os_funcs[] = { + JS_CFUNC_DEF("open", 2, js_os_open ), + OS_FLAG(O_RDONLY), + OS_FLAG(O_WRONLY), + OS_FLAG(O_RDWR), + OS_FLAG(O_APPEND), + OS_FLAG(O_CREAT), + OS_FLAG(O_EXCL), + OS_FLAG(O_TRUNC), +#if defined(_WIN32) + OS_FLAG(O_BINARY), + OS_FLAG(O_TEXT), +#endif + JS_CFUNC_DEF("close", 1, js_os_close ), + JS_CFUNC_DEF("seek", 3, js_os_seek ), + JS_CFUNC_MAGIC_DEF("read", 4, js_os_read_write, 0 ), + JS_CFUNC_MAGIC_DEF("write", 4, js_os_read_write, 1 ), + JS_CFUNC_DEF("isatty", 1, js_os_isatty ), +#if !defined(__wasi__) + JS_CFUNC_DEF("ttyGetWinSize", 1, js_os_ttyGetWinSize ), + JS_CFUNC_DEF("ttySetRaw", 1, js_os_ttySetRaw ), +#endif + JS_CFUNC_DEF("remove", 1, js_os_remove ), + JS_CFUNC_DEF("rename", 2, js_os_rename ), + JS_CFUNC_MAGIC_DEF("setReadHandler", 2, js_os_setReadHandler, 0 ), + JS_CFUNC_MAGIC_DEF("setWriteHandler", 2, js_os_setReadHandler, 1 ), + JS_CFUNC_DEF("signal", 2, js_os_signal ), + OS_FLAG(SIGINT), + OS_FLAG(SIGABRT), + OS_FLAG(SIGFPE), + OS_FLAG(SIGILL), + OS_FLAG(SIGSEGV), + OS_FLAG(SIGTERM), +#if !defined(_WIN32) && !defined(__wasi__) + OS_FLAG(SIGQUIT), + OS_FLAG(SIGPIPE), + OS_FLAG(SIGALRM), + OS_FLAG(SIGUSR1), + OS_FLAG(SIGUSR2), + OS_FLAG(SIGCHLD), + OS_FLAG(SIGCONT), + OS_FLAG(SIGSTOP), + OS_FLAG(SIGTSTP), + OS_FLAG(SIGTTIN), + OS_FLAG(SIGTTOU), + JS_CFUNC_DEF("cputime", 0, js_os_cputime ), +#endif + JS_CFUNC_DEF("now", 0, js_os_now ), + JS_CFUNC_MAGIC_DEF("setTimeout", 2, js_os_setTimeout, 0 ), + JS_CFUNC_MAGIC_DEF("setInterval", 2, js_os_setTimeout, 1 ), + // per spec: both functions can cancel timeouts and intervals + JS_CFUNC_DEF("clearTimeout", 1, js_os_clearTimeout ), + JS_CFUNC_DEF("clearInterval", 1, js_os_clearTimeout ), + JS_CFUNC_DEF("sleepAsync", 1, js_os_sleepAsync ), + JS_PROP_STRING_DEF("platform", OS_PLATFORM, 0 ), + JS_CFUNC_DEF("getcwd", 0, js_os_getcwd ), + JS_CFUNC_DEF("chdir", 0, js_os_chdir ), + JS_CFUNC_DEF("mkdir", 1, js_os_mkdir ), + JS_CFUNC_DEF("readdir", 1, js_os_readdir ), + /* st_mode constants */ + OS_FLAG(S_IFMT), + OS_FLAG(S_IFIFO), + OS_FLAG(S_IFCHR), + OS_FLAG(S_IFDIR), + OS_FLAG(S_IFBLK), + OS_FLAG(S_IFREG), +#if !defined(_WIN32) + OS_FLAG(S_IFSOCK), + OS_FLAG(S_IFLNK), + OS_FLAG(S_ISGID), + OS_FLAG(S_ISUID), +#endif + JS_CFUNC_MAGIC_DEF("stat", 1, js_os_stat, 0 ), + JS_CFUNC_DEF("utimes", 3, js_os_utimes ), + JS_CFUNC_DEF("sleep", 1, js_os_sleep ), +#if !defined(__wasi__) + JS_CFUNC_DEF("realpath", 1, js_os_realpath ), +#endif +#if !defined(_WIN32) && !defined(__wasi__) + JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), + JS_CFUNC_DEF("symlink", 2, js_os_symlink ), + JS_CFUNC_DEF("readlink", 1, js_os_readlink ), + JS_CFUNC_DEF("exec", 1, js_os_exec ), + JS_CFUNC_DEF("getpid", 0, js_os_getpid ), + JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ), + OS_FLAG(WNOHANG), + JS_CFUNC_DEF("pipe", 0, js_os_pipe ), + JS_CFUNC_DEF("kill", 2, js_os_kill ), + JS_CFUNC_DEF("dup", 1, js_os_dup ), + JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), +#endif +}; + +static int js_os_init(JSContext *ctx, JSModuleDef *m) +{ + JSRuntime *rt = JS_GetRuntime(ctx); + os_poll_func = js_os_poll; + + /* OSTimer class */ + JS_NewClassID(rt, &js_os_timer_class_id); + JS_NewClass(rt, js_os_timer_class_id, &js_os_timer_class); + +#ifdef USE_WORKER + { + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + JSValue proto, obj; + /* Worker class */ + JS_NewClassID(rt, &js_worker_class_id); + JS_NewClass(rt, js_worker_class_id, &js_worker_class); + proto = JS_NewObject(ctx); + JS_SetPropertyFunctionList(ctx, proto, js_worker_proto_funcs, countof(js_worker_proto_funcs)); + + obj = JS_NewCFunction2(ctx, js_worker_ctor, "Worker", 1, + JS_CFUNC_constructor, 0); + JS_SetConstructor(ctx, obj, proto); + + JS_SetClassProto(ctx, js_worker_class_id, proto); + + /* set 'Worker.parent' if necessary */ + if (ts->recv_pipe && ts->send_pipe) { + JS_DefinePropertyValueStr(ctx, obj, "parent", + js_worker_ctor_internal(ctx, JS_UNDEFINED, ts->recv_pipe, ts->send_pipe), + JS_PROP_C_W_E); + } + + JS_SetModuleExport(ctx, m, "Worker", obj); + } +#endif /* USE_WORKER */ + + return JS_SetModuleExportList(ctx, m, js_os_funcs, + countof(js_os_funcs)); +} + +JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name) +{ + JSModuleDef *m; + m = JS_NewCModule(ctx, module_name, js_os_init); + if (!m) + return NULL; + JS_AddModuleExportList(ctx, m, js_os_funcs, countof(js_os_funcs)); +#ifdef USE_WORKER + JS_AddModuleExport(ctx, m, "Worker"); +#endif + return m; +} + +/**********************************************************/ + +static JSValue js_print(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + int i; + const char *str; + size_t len; + + for(i = 0; i < argc; i++) { + if (i != 0) + putchar(' '); + str = JS_ToCStringLen(ctx, &len, argv[i]); + if (!str) + return JS_EXCEPTION; + fwrite(str, 1, len, stdout); + JS_FreeCString(ctx, str); + } + putchar('\n'); + fflush(stdout); + return JS_UNDEFINED; +} + +void js_std_add_helpers(JSContext *ctx, int argc, char **argv) +{ + JSValue global_obj, console, args; + int i; + + /* XXX: should these global definitions be enumerable? */ + global_obj = JS_GetGlobalObject(ctx); + + console = JS_NewObject(ctx); + JS_SetPropertyStr(ctx, console, "log", + JS_NewCFunction(ctx, js_print, "log", 1)); + JS_SetPropertyStr(ctx, global_obj, "console", console); + + /* same methods as the mozilla JS shell */ + if (argc >= 0) { + args = JS_NewArray(ctx); + for(i = 0; i < argc; i++) { + JS_SetPropertyUint32(ctx, args, i, JS_NewString(ctx, argv[i])); + } + JS_SetPropertyStr(ctx, global_obj, "scriptArgs", args); + } + + JS_SetPropertyStr(ctx, global_obj, "print", + JS_NewCFunction(ctx, js_print, "print", 1)); + JS_SetPropertyStr(ctx, global_obj, "__loadScript", + JS_NewCFunction(ctx, js_loadScript, "__loadScript", 1)); + + JS_FreeValue(ctx, global_obj); +} + +void js_std_init_handlers(JSRuntime *rt) +{ + JSThreadState *ts; + + ts = malloc(sizeof(*ts)); + if (!ts) { + fprintf(stderr, "Could not allocate memory for the worker"); + exit(1); + } + memset(ts, 0, sizeof(*ts)); + init_list_head(&ts->os_rw_handlers); + init_list_head(&ts->os_signal_handlers); + init_list_head(&ts->os_timers); + init_list_head(&ts->port_list); + + JS_SetRuntimeOpaque(rt, ts); + +#ifdef USE_WORKER + /* set the SharedArrayBuffer memory handlers */ + { + JSSharedArrayBufferFunctions sf; + memset(&sf, 0, sizeof(sf)); + sf.sab_alloc = js_sab_alloc; + sf.sab_free = js_sab_free; + sf.sab_dup = js_sab_dup; + JS_SetSharedArrayBufferFunctions(rt, &sf); + } +#endif +} + +void js_std_free_handlers(JSRuntime *rt) +{ + JSThreadState *ts = JS_GetRuntimeOpaque(rt); + struct list_head *el, *el1; + + list_for_each_safe(el, el1, &ts->os_rw_handlers) { + JSOSRWHandler *rh = list_entry(el, JSOSRWHandler, link); + free_rw_handler(rt, rh); + } + + list_for_each_safe(el, el1, &ts->os_signal_handlers) { + JSOSSignalHandler *sh = list_entry(el, JSOSSignalHandler, link); + free_sh(rt, sh); + } + + list_for_each_safe(el, el1, &ts->os_timers) { + JSOSTimer *th = list_entry(el, JSOSTimer, link); + unlink_timer(rt, th); + if (!th->has_object) + free_timer(rt, th); + } + +#ifdef USE_WORKER + /* XXX: free port_list ? */ + js_free_message_pipe(ts->recv_pipe); + js_free_message_pipe(ts->send_pipe); +#endif + + free(ts); + JS_SetRuntimeOpaque(rt, NULL); /* fail safe */ +} + +static void js_dump_obj(JSContext *ctx, FILE *f, JSValue val) +{ + const char *str; + + str = JS_ToCString(ctx, val); + if (str) { + fprintf(f, "%s\n", str); + JS_FreeCString(ctx, str); + } else { + fprintf(f, "[exception]\n"); + } +} + +static void js_std_dump_error1(JSContext *ctx, JSValue exception_val) +{ + JSValue val; + BOOL is_error; + + is_error = JS_IsError(ctx, exception_val); + js_dump_obj(ctx, stderr, exception_val); + if (is_error) { + val = JS_GetPropertyStr(ctx, exception_val, "stack"); + if (!JS_IsUndefined(val)) { + js_dump_obj(ctx, stderr, val); + } + JS_FreeValue(ctx, val); + } +} + +void js_std_dump_error(JSContext *ctx) +{ + JSValue exception_val; + + exception_val = JS_GetException(ctx); + js_std_dump_error1(ctx, exception_val); + JS_FreeValue(ctx, exception_val); +} + +void js_std_promise_rejection_tracker(JSContext *ctx, JSValue promise, + JSValue reason, + BOOL is_handled, void *opaque) +{ + if (!is_handled) { + fprintf(stderr, "Possibly unhandled promise rejection: "); + js_std_dump_error1(ctx, reason); + } +} + +/* main loop which calls the user JS callbacks */ +void js_std_loop(JSContext *ctx) +{ + JSContext *ctx1; + int err; + + for(;;) { + /* execute the pending jobs */ + for(;;) { + err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (err <= 0) { + if (err < 0) { + js_std_dump_error(ctx1); + } + break; + } + } + + if (!os_poll_func || os_poll_func(ctx)) + break; + } +} + +/* Wait for a promise and execute pending jobs while waiting for + it. Return the promise result or JS_EXCEPTION in case of promise + rejection. */ +JSValue js_std_await(JSContext *ctx, JSValue obj) +{ + JSValue ret; + int state; + + for(;;) { + state = JS_PromiseState(ctx, obj); + if (state == JS_PROMISE_FULFILLED) { + ret = JS_PromiseResult(ctx, obj); + JS_FreeValue(ctx, obj); + break; + } else if (state == JS_PROMISE_REJECTED) { + ret = JS_Throw(ctx, JS_PromiseResult(ctx, obj)); + JS_FreeValue(ctx, obj); + break; + } else if (state == JS_PROMISE_PENDING) { + JSContext *ctx1; + int err; + err = JS_ExecutePendingJob(JS_GetRuntime(ctx), &ctx1); + if (err < 0) { + js_std_dump_error(ctx1); + } + if (os_poll_func) + os_poll_func(ctx); + } else { + /* not a promise */ + ret = obj; + break; + } + } + return ret; +} + +void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int load_only) +{ + JSValue obj, val; + obj = JS_ReadObject(ctx, buf, buf_len, JS_READ_OBJ_BYTECODE); + if (JS_IsException(obj)) + goto exception; + if (load_only) { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { + js_module_set_import_meta(ctx, obj, FALSE, FALSE); + } + } else { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) { + if (JS_ResolveModule(ctx, obj) < 0) { + JS_FreeValue(ctx, obj); + goto exception; + } + js_module_set_import_meta(ctx, obj, FALSE, TRUE); + val = JS_EvalFunction(ctx, obj); + val = js_std_await(ctx, val); + } else { + val = JS_EvalFunction(ctx, obj); + } + if (JS_IsException(val)) { + exception: + js_std_dump_error(ctx); + exit(1); + } + JS_FreeValue(ctx, val); + } +} diff --git a/armorcore/sources/libs/quickjs/quickjs-libc.h b/armorcore/sources/libs/quickjs/quickjs-libc.h new file mode 100644 index 000000000..ad850a32d --- /dev/null +++ b/armorcore/sources/libs/quickjs/quickjs-libc.h @@ -0,0 +1,60 @@ +/* + * QuickJS C library + * + * Copyright (c) 2017-2018 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef QUICKJS_LIBC_H +#define QUICKJS_LIBC_H + +#include +#include + +#include "quickjs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +JSModuleDef *js_init_module_std(JSContext *ctx, const char *module_name); +JSModuleDef *js_init_module_os(JSContext *ctx, const char *module_name); +void js_std_add_helpers(JSContext *ctx, int argc, char **argv); +void js_std_loop(JSContext *ctx); +JSValue js_std_await(JSContext *ctx, JSValue obj); +void js_std_init_handlers(JSRuntime *rt); +void js_std_free_handlers(JSRuntime *rt); +void js_std_dump_error(JSContext *ctx); +uint8_t *js_load_file(JSContext *ctx, size_t *pbuf_len, const char *filename); +int js_module_set_import_meta(JSContext *ctx, JSValue func_val, + JS_BOOL use_realpath, JS_BOOL is_main); +JSModuleDef *js_module_loader(JSContext *ctx, + const char *module_name, void *opaque); +void js_std_eval_binary(JSContext *ctx, const uint8_t *buf, size_t buf_len, + int flags); +void js_std_promise_rejection_tracker(JSContext *ctx, JSValue promise, + JSValue reason, + JS_BOOL is_handled, void *opaque); +void js_std_set_worker_new_context_func(JSContext *(*func)(JSRuntime *rt)); + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* QUICKJS_LIBC_H */ diff --git a/armorcore/sources/libs/quickjs/quickjs-opcode.h b/armorcore/sources/libs/quickjs/quickjs-opcode.h new file mode 100644 index 000000000..0681e926c --- /dev/null +++ b/armorcore/sources/libs/quickjs/quickjs-opcode.h @@ -0,0 +1,371 @@ +/* + * QuickJS opcode definitions + * + * Copyright (c) 2017-2018 Fabrice Bellard + * Copyright (c) 2017-2018 Charlie Gordon + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifdef FMT +FMT(none) +FMT(none_int) +FMT(none_loc) +FMT(none_arg) +FMT(none_var_ref) +FMT(u8) +FMT(i8) +FMT(loc8) +FMT(const8) +FMT(label8) +FMT(u16) +FMT(i16) +FMT(label16) +FMT(npop) +FMT(npopx) +FMT(npop_u16) +FMT(loc) +FMT(arg) +FMT(var_ref) +FMT(u32) +FMT(u32x2) +FMT(i32) +FMT(const) +FMT(label) +FMT(atom) +FMT(atom_u8) +FMT(atom_u16) +FMT(atom_label_u8) +FMT(atom_label_u16) +FMT(label_u16) +#undef FMT +#endif /* FMT */ + +#ifdef DEF + +#ifndef def +#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f) +#endif + +DEF(invalid, 1, 0, 0, none) /* never emitted */ + +/* push values */ +DEF( push_i32, 5, 0, 1, i32) +DEF( push_const, 5, 0, 1, const) +DEF( fclosure, 5, 0, 1, const) /* must follow push_const */ +DEF(push_atom_value, 5, 0, 1, atom) +DEF( private_symbol, 5, 0, 1, atom) +DEF( undefined, 1, 0, 1, none) +DEF( null, 1, 0, 1, none) +DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */ +DEF( push_false, 1, 0, 1, none) +DEF( push_true, 1, 0, 1, none) +DEF( object, 1, 0, 1, none) +DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */ +DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */ + +DEF( drop, 1, 1, 0, none) /* a -> */ +DEF( nip, 1, 2, 1, none) /* a b -> b */ +DEF( nip1, 1, 3, 2, none) /* a b c -> b c */ +DEF( dup, 1, 1, 2, none) /* a -> a a */ +DEF( dup1, 1, 2, 3, none) /* a b -> a a b */ +DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */ +DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */ +DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */ +DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */ +DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */ +DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */ +DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */ +DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */ +DEF( swap, 1, 2, 2, none) /* a b -> b a */ +DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */ +DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */ +DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */ +DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */ +DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */ + +DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */ +DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */ +DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */ +DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */ +DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */ +DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */ +DEF( apply, 3, 3, 1, u16) +DEF( return, 1, 1, 0, none) +DEF( return_undef, 1, 0, 0, none) +DEF(check_ctor_return, 1, 1, 2, none) +DEF( check_ctor, 1, 0, 0, none) +DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */ +DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */ +DEF( return_async, 1, 1, 0, none) +DEF( throw, 1, 1, 0, none) +DEF( throw_error, 6, 0, 0, atom_u8) +DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */ +DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */ +DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a + bytecode string */ +DEF( get_super, 1, 1, 1, none) +DEF( import, 1, 1, 1, none) /* dynamic module import */ + +DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */ +DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */ +DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */ +DEF( put_var, 5, 1, 0, atom) /* must come after get_var */ +DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */ +DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */ + +DEF( get_ref_value, 1, 2, 3, none) +DEF( put_ref_value, 1, 3, 0, none) + +DEF( define_var, 6, 0, 0, atom_u8) +DEF(check_define_var, 6, 0, 0, atom_u8) +DEF( define_func, 6, 1, 0, atom_u8) + +// order matters, see IC counterparts +DEF( get_field, 5, 1, 1, atom) +DEF( get_field2, 5, 1, 2, atom) +DEF( put_field, 5, 2, 0, atom) + +DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */ +DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */ +DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */ +DEF( get_array_el, 1, 2, 1, none) +DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */ +DEF( put_array_el, 1, 3, 0, none) +DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */ +DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */ +DEF( define_field, 5, 2, 1, atom) +DEF( set_name, 5, 1, 1, atom) +DEF(set_name_computed, 1, 2, 2, none) +DEF( set_proto, 1, 2, 1, none) +DEF(set_home_object, 1, 2, 2, none) +DEF(define_array_el, 1, 3, 2, none) +DEF( append, 1, 3, 2, none) /* append enumerated object, update length */ +DEF(copy_data_properties, 2, 3, 3, u8) +DEF( define_method, 6, 2, 1, atom_u8) +DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */ +DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */ +DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */ + +DEF( get_loc, 3, 0, 1, loc) +DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */ +DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */ +DEF( get_arg, 3, 0, 1, arg) +DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */ +DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */ +DEF( get_var_ref, 3, 0, 1, var_ref) +DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */ +DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */ +DEF(set_loc_uninitialized, 3, 0, 0, loc) +DEF( get_loc_check, 3, 0, 1, loc) +DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */ +DEF( put_loc_check_init, 3, 1, 0, loc) +DEF(get_var_ref_check, 3, 0, 1, var_ref) +DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */ +DEF(put_var_ref_check_init, 3, 1, 0, var_ref) +DEF( close_loc, 3, 0, 0, loc) +DEF( if_false, 5, 1, 0, label) +DEF( if_true, 5, 1, 0, label) /* must come after if_false */ +DEF( goto, 5, 0, 0, label) /* must come after if_true */ +DEF( catch, 5, 0, 1, label) +DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */ +DEF( ret, 1, 1, 0, none) /* used to return from the finally block */ +DEF( nip_catch, 1, 2, 1, none) /* catch ... a -> a */ + +DEF( to_object, 1, 1, 1, none) +//DEF( to_string, 1, 1, 1, none) +DEF( to_propkey, 1, 1, 1, none) +DEF( to_propkey2, 1, 2, 2, none) + +DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */ +DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8) + +DEF( make_loc_ref, 7, 0, 2, atom_u16) +DEF( make_arg_ref, 7, 0, 2, atom_u16) +DEF(make_var_ref_ref, 7, 0, 2, atom_u16) +DEF( make_var_ref, 5, 0, 2, atom) + +DEF( for_in_start, 1, 1, 1, none) +DEF( for_of_start, 1, 1, 3, none) +DEF(for_await_of_start, 1, 1, 3, none) +DEF( for_in_next, 1, 1, 3, none) +DEF( for_of_next, 2, 3, 5, u8) +DEF(iterator_check_object, 1, 1, 1, none) +DEF(iterator_get_value_done, 1, 1, 2, none) +DEF( iterator_close, 1, 3, 0, none) +DEF( iterator_next, 1, 4, 4, none) +DEF( iterator_call, 2, 4, 5, u8) +DEF( initial_yield, 1, 0, 0, none) +DEF( yield, 1, 1, 2, none) +DEF( yield_star, 1, 1, 2, none) +DEF(async_yield_star, 1, 1, 2, none) +DEF( await, 1, 1, 1, none) + +/* arithmetic/logic operations */ +DEF( neg, 1, 1, 1, none) +DEF( plus, 1, 1, 1, none) +DEF( dec, 1, 1, 1, none) +DEF( inc, 1, 1, 1, none) +DEF( post_dec, 1, 1, 2, none) +DEF( post_inc, 1, 1, 2, none) +DEF( dec_loc, 2, 0, 0, loc8) +DEF( inc_loc, 2, 0, 0, loc8) +DEF( add_loc, 2, 1, 0, loc8) +DEF( not, 1, 1, 1, none) +DEF( lnot, 1, 1, 1, none) +DEF( typeof, 1, 1, 1, none) +DEF( delete, 1, 2, 1, none) +DEF( delete_var, 5, 0, 1, atom) + +/* warning: order matters (see js_parse_assign_expr) */ +DEF( mul, 1, 2, 1, none) +DEF( div, 1, 2, 1, none) +DEF( mod, 1, 2, 1, none) +DEF( add, 1, 2, 1, none) +DEF( sub, 1, 2, 1, none) +DEF( shl, 1, 2, 1, none) +DEF( sar, 1, 2, 1, none) +DEF( shr, 1, 2, 1, none) +DEF( and, 1, 2, 1, none) +DEF( xor, 1, 2, 1, none) +DEF( or, 1, 2, 1, none) +DEF( pow, 1, 2, 1, none) + +DEF( lt, 1, 2, 1, none) +DEF( lte, 1, 2, 1, none) +DEF( gt, 1, 2, 1, none) +DEF( gte, 1, 2, 1, none) +DEF( instanceof, 1, 2, 1, none) +DEF( in, 1, 2, 1, none) +DEF( eq, 1, 2, 1, none) +DEF( neq, 1, 2, 1, none) +DEF( strict_eq, 1, 2, 1, none) +DEF( strict_neq, 1, 2, 1, none) +DEF(is_undefined_or_null, 1, 1, 1, none) +/* must be the last non short and non temporary opcode */ +DEF( nop, 1, 0, 0, none) + +/* temporary opcodes: never emitted in the final bytecode */ + +def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ +def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */ + +def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */ + +def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */ +def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */ +def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */ +def(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ + +def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */ + +def( source_loc, 9, 0, 0, u32x2) /* emitted in phase 1, removed in phase 3 */ + +DEF( push_minus1, 1, 0, 1, none_int) +DEF( push_0, 1, 0, 1, none_int) +DEF( push_1, 1, 0, 1, none_int) +DEF( push_2, 1, 0, 1, none_int) +DEF( push_3, 1, 0, 1, none_int) +DEF( push_4, 1, 0, 1, none_int) +DEF( push_5, 1, 0, 1, none_int) +DEF( push_6, 1, 0, 1, none_int) +DEF( push_7, 1, 0, 1, none_int) +DEF( push_i8, 2, 0, 1, i8) +DEF( push_i16, 3, 0, 1, i16) +DEF( push_const8, 2, 0, 1, const8) +DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */ +DEF(push_empty_string, 1, 0, 1, none) + +DEF( get_loc8, 2, 0, 1, loc8) +DEF( put_loc8, 2, 1, 0, loc8) +DEF( set_loc8, 2, 1, 1, loc8) + +DEF( get_loc0_loc1, 1, 0, 2, none_loc) +DEF( get_loc0, 1, 0, 1, none_loc) +DEF( get_loc1, 1, 0, 1, none_loc) +DEF( get_loc2, 1, 0, 1, none_loc) +DEF( get_loc3, 1, 0, 1, none_loc) +DEF( put_loc0, 1, 1, 0, none_loc) +DEF( put_loc1, 1, 1, 0, none_loc) +DEF( put_loc2, 1, 1, 0, none_loc) +DEF( put_loc3, 1, 1, 0, none_loc) +DEF( set_loc0, 1, 1, 1, none_loc) +DEF( set_loc1, 1, 1, 1, none_loc) +DEF( set_loc2, 1, 1, 1, none_loc) +DEF( set_loc3, 1, 1, 1, none_loc) +DEF( get_arg0, 1, 0, 1, none_arg) +DEF( get_arg1, 1, 0, 1, none_arg) +DEF( get_arg2, 1, 0, 1, none_arg) +DEF( get_arg3, 1, 0, 1, none_arg) +DEF( put_arg0, 1, 1, 0, none_arg) +DEF( put_arg1, 1, 1, 0, none_arg) +DEF( put_arg2, 1, 1, 0, none_arg) +DEF( put_arg3, 1, 1, 0, none_arg) +DEF( set_arg0, 1, 1, 1, none_arg) +DEF( set_arg1, 1, 1, 1, none_arg) +DEF( set_arg2, 1, 1, 1, none_arg) +DEF( set_arg3, 1, 1, 1, none_arg) +DEF( get_var_ref0, 1, 0, 1, none_var_ref) +DEF( get_var_ref1, 1, 0, 1, none_var_ref) +DEF( get_var_ref2, 1, 0, 1, none_var_ref) +DEF( get_var_ref3, 1, 0, 1, none_var_ref) +DEF( put_var_ref0, 1, 1, 0, none_var_ref) +DEF( put_var_ref1, 1, 1, 0, none_var_ref) +DEF( put_var_ref2, 1, 1, 0, none_var_ref) +DEF( put_var_ref3, 1, 1, 0, none_var_ref) +DEF( set_var_ref0, 1, 1, 1, none_var_ref) +DEF( set_var_ref1, 1, 1, 1, none_var_ref) +DEF( set_var_ref2, 1, 1, 1, none_var_ref) +DEF( set_var_ref3, 1, 1, 1, none_var_ref) + +DEF( get_length, 1, 1, 1, none) + +DEF( if_false8, 2, 1, 0, label8) +DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */ +DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */ +DEF( goto16, 3, 0, 0, label16) + +DEF( call0, 1, 1, 1, npopx) +DEF( call1, 1, 1, 1, npopx) +DEF( call2, 1, 1, 1, npopx) +DEF( call3, 1, 1, 1, npopx) + +DEF( is_undefined, 1, 1, 1, none) +DEF( is_null, 1, 1, 1, none) +DEF(typeof_is_undefined, 1, 1, 1, none) +DEF( typeof_is_function, 1, 1, 1, none) + +// order matters, see non-IC counterparts +DEF( get_field_ic, 5, 1, 1, none) +DEF( get_field2_ic, 5, 1, 2, none) +DEF( put_field_ic, 5, 2, 0, none) + +#undef DEF +#undef def +#endif /* DEF */ diff --git a/armorcore/sources/libs/quickjs/quickjs.c b/armorcore/sources/libs/quickjs/quickjs.c new file mode 100644 index 000000000..527fea87c --- /dev/null +++ b/armorcore/sources/libs/quickjs/quickjs.c @@ -0,0 +1,53217 @@ +/* + * QuickJS Javascript Engine + * + * Copyright (c) 2017-2021 Fabrice Bellard + * Copyright (c) 2017-2021 Charlie Gordon + * Copyright (c) 2023 Ben Noordhuis + * Copyright (c) 2023 Saúl Ibarra Corretgé + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#if !defined(_MSC_VER) +#include +#if defined(_WIN32) +#include +#endif +#endif +#include +#include +#include + +#include "cutils.h" +#include "list.h" +#include "quickjs.h" +#include "libregexp.h" +#include "libbf.h" + +#if defined(EMSCRIPTEN) || defined(_MSC_VER) +#define DIRECT_DISPATCH 0 +#else +#define DIRECT_DISPATCH 1 +#endif + +#if defined(__APPLE__) +#define MALLOC_OVERHEAD 0 +#else +#define MALLOC_OVERHEAD 8 +#endif + +#if defined(__NEWLIB__) +#define NO_TM_GMTOFF +#endif + +#if !defined(EMSCRIPTEN) && !defined(__wasi__) +#include "quickjs-c-atomics.h" +#define CONFIG_ATOMICS +#endif + +// Debug trace system: +// uncomment one or more DUMP_XXX definition to produce debug output. +// define the DUMP_XXX symbol as empty or 0 for unconditional output +// otherwhise the debug output will be produced to the dump stream (currently +// stdout) if qjs is invoked with -d with the corresponding bit set. + +//#define DUMP_BYTECODE_FINAL 0x01 /* dump pass 3 final byte code */ +//#define DUMP_BYTECODE_PASS2 0x02 /* dump pass 2 code */ +//#define DUMP_BYTECODE_PASS1 0x04 /* dump pass 1 code */ +//#define DUMP_BYTECODE_HEX 0x10 /* dump bytecode in hex */ +//#define DUMP_BYTECODE_PC2LINE 0x20 /* dump line number table */ +//#define DUMP_BYTECODE_STACK 0x40 /* dump compute_stack_size */ +//#define DUMP_BYTECODE_STEP 0x80 /* dump executed bytecode */ +//#define DUMP_READ_OBJECT 0x100 /* dump the marshalled objects at load time */ +//#define DUMP_FREE 0x200 /* dump every object free */ +//#define DUMP_GC 0x400 /* dump the occurrence of the automatic GC */ +//#define DUMP_GC_FREE 0x800 /* dump objects freed by the GC */ +//#define DUMP_MODULE_RESOLVE 0x1000 /* dump module resolution steps */ +//#define DUMP_PROMISE 0x2000 /* dump promise steps */ +//#define DUMP_LEAKS 0x4000 /* dump leaked objects and strings in JS_FreeRuntime */ +//#define DUMP_ATOM_LEAKS 0x8000 /* dump leaked atoms in JS_FreeRuntime */ +//#define DUMP_MEM 0x10000 /* dump memory usage in JS_FreeRuntime */ +//#define DUMP_OBJECTS 0x20000 /* dump objects in JS_FreeRuntime */ +//#define DUMP_ATOMS 0x40000 /* dump atoms in JS_FreeRuntime */ +//#define DUMP_SHAPES 0x80000 /* dump shapes in JS_FreeRuntime */ + +//#define FORCE_GC_AT_MALLOC /* test the GC by forcing it before each object allocation */ + +#define check_dump_flag(rt, flag) ((rt->dump_flags & (flag +0)) == (flag +0)) + +#define STRINGIFY_(x) #x +#define STRINGIFY(x) STRINGIFY_(x) + +#define QJS_VERSION_STRING \ + STRINGIFY(QJS_VERSION_MAJOR) "." STRINGIFY(QJS_VERSION_MINOR) "." STRINGIFY(QJS_VERSION_PATCH) QJS_VERSION_SUFFIX + +const char* JS_GetVersion(void) { + return QJS_VERSION_STRING; +} + +#undef STRINFIGY_ +#undef STRINGIFY + +enum { + /* classid tag */ /* union usage | properties */ + JS_CLASS_OBJECT = 1, /* must be first */ + JS_CLASS_ARRAY, /* u.array | length */ + JS_CLASS_ERROR, + JS_CLASS_NUMBER, /* u.object_data */ + JS_CLASS_STRING, /* u.object_data */ + JS_CLASS_BOOLEAN, /* u.object_data */ + JS_CLASS_SYMBOL, /* u.object_data */ + JS_CLASS_ARGUMENTS, /* u.array | length */ + JS_CLASS_MAPPED_ARGUMENTS, /* | length */ + JS_CLASS_DATE, /* u.object_data */ + JS_CLASS_MODULE_NS, + JS_CLASS_C_FUNCTION, /* u.cfunc */ + JS_CLASS_BYTECODE_FUNCTION, /* u.func */ + JS_CLASS_BOUND_FUNCTION, /* u.bound_function */ + JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */ + JS_CLASS_GENERATOR_FUNCTION, /* u.func */ + JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */ + JS_CLASS_REGEXP, /* u.regexp */ + JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */ + JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */ + JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */ + JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */ + JS_CLASS_DATAVIEW, /* u.typed_array */ + JS_CLASS_BIG_INT, /* u.object_data */ + JS_CLASS_MAP, /* u.map_state */ + JS_CLASS_SET, /* u.map_state */ + JS_CLASS_WEAKMAP, /* u.map_state */ + JS_CLASS_WEAKSET, /* u.map_state */ + JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ + JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */ + JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */ + JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */ + JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */ + JS_CLASS_GENERATOR, /* u.generator_data */ + JS_CLASS_PROXY, /* u.proxy_data */ + JS_CLASS_PROMISE, /* u.promise_data */ + JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */ + JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */ + JS_CLASS_ASYNC_FUNCTION, /* u.func */ + JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */ + JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */ + JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */ + JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */ + JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ + JS_CLASS_WEAK_REF, + JS_CLASS_FINALIZATION_REGISTRY, + JS_CLASS_CALL_SITE, + + JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ +}; + +/* number of typed array types */ +#define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1) +static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT]; +#define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY]) + +typedef enum JSErrorEnum { + JS_EVAL_ERROR, + JS_RANGE_ERROR, + JS_REFERENCE_ERROR, + JS_SYNTAX_ERROR, + JS_TYPE_ERROR, + JS_URI_ERROR, + JS_INTERNAL_ERROR, + JS_AGGREGATE_ERROR, + + JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ + JS_PLAIN_ERROR = JS_NATIVE_ERROR_COUNT +} JSErrorEnum; + +#define JS_MAX_LOCAL_VARS 65535 +#define JS_STACK_SIZE_MAX 65534 +#define JS_STRING_LEN_MAX ((1 << 30) - 1) + +#define __exception __attribute__((warn_unused_result)) + +typedef struct JSShape JSShape; +typedef struct JSString JSString; +typedef struct JSString JSAtomStruct; + +#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v)) + +typedef enum { + JS_GC_PHASE_NONE, + JS_GC_PHASE_DECREF, + JS_GC_PHASE_REMOVE_CYCLES, +} JSGCPhaseEnum; + +struct JSRuntime { + JSMallocFunctions mf; + JSMallocState malloc_state; + const char *rt_info; + + int atom_hash_size; /* power of two */ + int atom_count; + int atom_size; + int atom_count_resize; /* resize hash table at this count */ + uint32_t *atom_hash; + JSAtomStruct **atom_array; + int atom_free_index; /* 0 = none */ + + JSClassID js_class_id_alloc; /* counter for user defined classes */ + int class_count; /* size of class_array */ + JSClass *class_array; + + struct list_head context_list; /* list of JSContext.link */ + /* list of JSGCObjectHeader.link. List of allocated GC objects (used + by the garbage collector) */ + struct list_head gc_obj_list; + /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */ + struct list_head gc_zero_ref_count_list; + struct list_head tmp_obj_list; /* used during GC */ + JSGCPhaseEnum gc_phase : 8; + size_t malloc_gc_threshold; +#ifdef DUMP_LEAKS + struct list_head string_list; /* list of JSString.link */ +#endif + /* stack limitation */ + uintptr_t stack_size; /* in bytes, 0 if no limit */ + uintptr_t stack_top; + uintptr_t stack_limit; /* lower stack limit */ + + JSValue current_exception; + /* true if inside an out of memory error, to avoid recursing */ + BOOL in_out_of_memory : 8; + /* and likewise if inside Error.prepareStackTrace() */ + BOOL in_prepare_stack_trace : 8; + + struct JSStackFrame *current_stack_frame; + + JSInterruptHandler *interrupt_handler; + void *interrupt_opaque; + + JSHostPromiseRejectionTracker *host_promise_rejection_tracker; + void *host_promise_rejection_tracker_opaque; + + struct list_head job_list; /* list of JSJobEntry.link */ + + JSModuleNormalizeFunc *module_normalize_func; + JSModuleLoaderFunc *module_loader_func; + void *module_loader_opaque; + /* timestamp for internal use in module evaluation */ + int64_t module_async_evaluation_next_timestamp; + + /* used to allocate, free and clone SharedArrayBuffers */ + JSSharedArrayBufferFunctions sab_funcs; + + BOOL can_block : 8; /* TRUE if Atomics.wait can block */ + uint32_t dump_flags : 24; + + /* Shape hash table */ + int shape_hash_bits; + int shape_hash_size; + int shape_hash_count; /* number of hashed shapes */ + JSShape **shape_hash; + bf_context_t bf_ctx; + void *user_opaque; +}; + +struct JSClass { + uint32_t class_id; /* 0 means free entry */ + JSAtom class_name; + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; + JSClassCall *call; + /* pointers for exotic behavior, can be NULL if none are present */ + const JSClassExoticMethods *exotic; +}; + +#define JS_MODE_STRICT (1 << 0) + +typedef struct JSStackFrame { + struct JSStackFrame *prev_frame; /* NULL if first stack frame */ + JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */ + JSValue *arg_buf; /* arguments */ + JSValue *var_buf; /* variables */ + struct list_head var_ref_list; /* list of JSVarRef.link */ + uint8_t *cur_pc; /* only used in bytecode functions : PC of the + instruction after the call */ + int arg_count; + int js_mode; + /* only used in generators. Current stack pointer value. NULL if + the function is running. */ + JSValue *cur_sp; +} JSStackFrame; + +typedef enum { + JS_GC_OBJ_TYPE_JS_OBJECT, + JS_GC_OBJ_TYPE_FUNCTION_BYTECODE, + JS_GC_OBJ_TYPE_SHAPE, + JS_GC_OBJ_TYPE_VAR_REF, + JS_GC_OBJ_TYPE_ASYNC_FUNCTION, + JS_GC_OBJ_TYPE_JS_CONTEXT, +} JSGCObjectTypeEnum; + +/* header for GC objects. GC objects are C data structures with a + reference count that can reference other GC objects. JS Objects are + a particular type of GC object. */ +struct JSGCObjectHeader { + int ref_count; /* must come first, 32-bit */ + JSGCObjectTypeEnum gc_obj_type : 4; + uint8_t mark : 4; /* used by the GC */ + uint8_t dummy1; /* not used by the GC */ + uint16_t dummy2; /* not used by the GC */ + struct list_head link; +}; + +typedef struct JSVarRef { + union { + JSGCObjectHeader header; /* must come first */ + struct { + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ + + /* 0 : the JSVarRef is on the stack. header.link is an element + of JSStackFrame.var_ref_list. + 1 : the JSVarRef is detached. header.link has the normal meanning + */ + uint8_t is_detached : 1; + uint8_t is_arg : 1; + uint16_t var_idx; /* index of the corresponding function variable on + the stack */ + }; + }; + JSValue *pvalue; /* pointer to the value, either on the stack or + to 'value' */ + JSValue value; /* used when the variable is no longer on the stack */ +} JSVarRef; + +/* the same structure is used for big integers. + Big integers are never infinite or NaNs */ +typedef struct JSBigInt { + JSRefCountHeader header; /* must come first, 32-bit */ + bf_t num; +} JSBigInt; + +typedef enum { + JS_AUTOINIT_ID_PROTOTYPE, + JS_AUTOINIT_ID_MODULE_NS, + JS_AUTOINIT_ID_PROP, +} JSAutoInitIDEnum; + +/* must be large enough to have a negligible runtime cost and small + enough to call the interrupt callback often. */ +#define JS_INTERRUPT_COUNTER_INIT 10000 + +struct JSContext { + JSGCObjectHeader header; /* must come first */ + JSRuntime *rt; + struct list_head link; + + uint16_t binary_object_count; + int binary_object_size; + + JSShape *array_shape; /* initial shape for Array objects */ + + JSValue *class_proto; + JSValue function_proto; + JSValue function_ctor; + JSValue array_ctor; + JSValue regexp_ctor; + JSValue promise_ctor; + JSValue native_error_proto[JS_NATIVE_ERROR_COUNT]; + JSValue error_ctor; + JSValue error_prepare_stack; + int error_stack_trace_limit; + JSValue iterator_proto; + JSValue async_iterator_proto; + JSValue array_proto_values; + JSValue throw_type_error; + JSValue eval_obj; + + JSValue global_obj; /* global object */ + JSValue global_var_obj; /* contains the global let/const definitions */ + + double time_origin; + + uint64_t random_state; + bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */ + /* when the counter reaches zero, JSRutime.interrupt_handler is called */ + int interrupt_counter; + + struct list_head loaded_modules; /* list of JSModuleDef.link */ + + /* if NULL, RegExp compilation is not supported */ + JSValue (*compile_regexp)(JSContext *ctx, JSValue pattern, + JSValue flags); + /* if NULL, eval is not supported */ + JSValue (*eval_internal)(JSContext *ctx, JSValue this_obj, + const char *input, size_t input_len, + const char *filename, int flags, int scope_idx); + void *user_opaque; +}; + +typedef union JSFloat64Union { + double d; + uint64_t u64; + uint32_t u32[2]; +} JSFloat64Union; + +typedef enum { + JS_WEAK_REF_KIND_MAP, + JS_WEAK_REF_KIND_WEAK_REF, + JS_WEAK_REF_KIND_FINALIZATION_REGISTRY_ENTRY, +} JSWeakRefKindEnum; + +typedef struct JSWeakRefRecord { + JSWeakRefKindEnum kind; + struct JSWeakRefRecord *next_weak_ref; + union { + struct JSMapRecord *map_record; + struct JSWeakRefData *weak_ref_data; + struct JSFinRecEntry *fin_rec_entry; + } u; +} JSWeakRefRecord; + +enum { + JS_ATOM_TYPE_STRING = 1, + JS_ATOM_TYPE_GLOBAL_SYMBOL, + JS_ATOM_TYPE_SYMBOL, + JS_ATOM_TYPE_PRIVATE, +}; + +enum { + JS_ATOM_HASH_SYMBOL, + JS_ATOM_HASH_PRIVATE, +}; + +typedef enum { + JS_ATOM_KIND_STRING, + JS_ATOM_KIND_SYMBOL, + JS_ATOM_KIND_PRIVATE, +} JSAtomKindEnum; + +#define JS_ATOM_HASH_MASK ((1 << 30) - 1) + +struct JSString { + JSRefCountHeader header; /* must come first, 32-bit */ + uint32_t len : 31; + uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */ + /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3, + for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3 + XXX: could change encoding to have one more bit in hash */ + uint32_t hash : 30; + uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ + uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ + JSWeakRefRecord *first_weak_ref; +#ifdef DUMP_LEAKS + struct list_head link; /* string list */ +#endif + union { + __extension__ uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */ + __extension__ uint16_t str16[0]; + } u; +}; + +typedef struct JSClosureVar { + uint8_t is_local : 1; + uint8_t is_arg : 1; + uint8_t is_const : 1; + uint8_t is_lexical : 1; + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* 8 bits available */ + uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the + parent function. otherwise: index to a closure + variable of the parent function */ + JSAtom var_name; +} JSClosureVar; + +#define ARG_SCOPE_INDEX 1 +#define ARG_SCOPE_END (-2) + +typedef struct JSVarScope { + int parent; /* index into fd->scopes of the enclosing scope */ + int first; /* index into fd->vars of the last variable in this scope */ +} JSVarScope; + +typedef enum { + /* XXX: add more variable kinds here instead of using bit fields */ + JS_VAR_NORMAL, + JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */ + JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator + function declaration */ + JS_VAR_CATCH, + JS_VAR_FUNCTION_NAME, /* function expression name */ + JS_VAR_PRIVATE_FIELD, + JS_VAR_PRIVATE_METHOD, + JS_VAR_PRIVATE_GETTER, + JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */ + JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */ +} JSVarKindEnum; + +/* XXX: could use a different structure in bytecode functions to save + memory */ +typedef struct JSVarDef { + JSAtom var_name; + /* index into fd->scopes of this variable lexical scope */ + int scope_level; + /* during compilation: + - if scope_level = 0: scope in which the variable is defined + - if scope_level != 0: index into fd->vars of the next + variable in the same or enclosing lexical scope + in a bytecode function: + index into fd->vars of the next + variable in the same or enclosing lexical scope + */ + int scope_next; + uint8_t is_const : 1; + uint8_t is_lexical : 1; + uint8_t is_captured : 1; + uint8_t is_static_private : 1; /* only used during private class field parsing */ + uint8_t var_kind : 4; /* see JSVarKindEnum */ + /* only used during compilation: function pool index for lexical + variables with var_kind = + JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of + the definition of the 'var' variables (they have scope_level = + 0) */ + int func_pool_idx : 24; /* only used during compilation : index in + the constant pool for hoisted function + definition */ +} JSVarDef; + +/* for the encoding of the pc2line table */ +#define PC2LINE_BASE (-1) +#define PC2LINE_RANGE 5 +#define PC2LINE_OP_FIRST 1 +#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE) + +typedef enum JSFunctionKindEnum { + JS_FUNC_NORMAL = 0, + JS_FUNC_GENERATOR = (1 << 0), + JS_FUNC_ASYNC = (1 << 1), + JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC), +} JSFunctionKindEnum; + +#define IC_CACHE_ITEM_CAPACITY 4 + +typedef struct JSInlineCacheRingSlot { + /* SoA for space optimization: 56 bytes */ + JSShape* shape[IC_CACHE_ITEM_CAPACITY]; + uint32_t prop_offset[IC_CACHE_ITEM_CAPACITY]; + JSAtom atom; + uint8_t index; +} JSInlineCacheRingSlot; + +typedef struct JSInlineCacheHashSlot { + JSAtom atom; + uint32_t index; + struct JSInlineCacheHashSlot *next; +} JSInlineCacheHashSlot; + +typedef struct JSInlineCache { + uint32_t count; + uint32_t capacity; + uint32_t hash_bits; + JSInlineCacheHashSlot **hash; + JSInlineCacheRingSlot *cache; + uint32_t updated_offset; + BOOL updated; +} JSInlineCache; + +static JSInlineCache *init_ic(JSContext *ctx); +static int rebuild_ic(JSContext *ctx, JSInlineCache *ic); +static int resize_ic_hash(JSContext *ctx, JSInlineCache *ic); +static int free_ic(JSRuntime *rt, JSInlineCache *ic); +static uint32_t add_ic_slot(JSContext *ctx, JSInlineCache *ic, JSAtom atom, JSObject *object, + uint32_t prop_offset); + +static int32_t get_ic_prop_offset(JSInlineCache *ic, uint32_t cache_offset, + JSShape *shape) +{ + uint32_t i; + JSInlineCacheRingSlot *cr; + JSShape *shape_slot; + assert(cache_offset < ic->capacity); + cr = ic->cache + cache_offset; + i = cr->index; + for (;;) { + shape_slot = *(cr->shape + i); + if (likely(shape_slot == shape)) { + cr->index = i; + return cr->prop_offset[i]; + } + + i = (i + 1) % countof(cr->shape); + if (unlikely(i == cr->index)) { + break; + } + } + + return -1; +} + +static force_inline JSAtom get_ic_atom(JSInlineCache *ic, uint32_t cache_offset) +{ + assert(cache_offset < ic->capacity); + return ic->cache[cache_offset].atom; +} + +typedef struct JSFunctionBytecode { + JSGCObjectHeader header; /* must come first */ + uint8_t js_mode; + uint8_t has_prototype : 1; /* true if a prototype field is necessary */ + uint8_t has_simple_parameter_list : 1; + uint8_t is_derived_class_constructor : 1; + /* true if home_object needs to be initialized */ + uint8_t need_home_object : 1; + uint8_t func_kind : 2; + uint8_t new_target_allowed : 1; + uint8_t super_call_allowed : 1; + uint8_t super_allowed : 1; + uint8_t arguments_allowed : 1; + uint8_t backtrace_barrier : 1; /* stop backtrace on this function */ + /* XXX: 5 bits available */ + uint8_t *byte_code_buf; /* (self pointer) */ + int byte_code_len; + JSAtom func_name; + JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */ + JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */ + uint16_t arg_count; + uint16_t var_count; + uint16_t defined_arg_count; /* for length function property */ + uint16_t stack_size; /* maximum stack size */ + JSContext *realm; /* function realm */ + JSValue *cpool; /* constant pool (self pointer) */ + int cpool_count; + int closure_var_count; + JSInlineCache *ic; + JSAtom filename; + int line_num; + int col_num; + int source_len; + int pc2line_len; + uint8_t *pc2line_buf; + char *source; +} JSFunctionBytecode; + +typedef struct JSBoundFunction { + JSValue func_obj; + JSValue this_val; + int argc; + JSValue argv[]; +} JSBoundFunction; + +typedef enum JSIteratorKindEnum { + JS_ITERATOR_KIND_KEY, + JS_ITERATOR_KIND_VALUE, + JS_ITERATOR_KIND_KEY_AND_VALUE, +} JSIteratorKindEnum; + +typedef struct JSForInIterator { + JSValue obj; + BOOL is_array; + uint32_t array_length; + uint32_t idx; +} JSForInIterator; + +typedef struct JSRegExp { + JSString *pattern; + JSString *bytecode; /* also contains the flags */ +} JSRegExp; + +typedef struct JSProxyData { + JSValue target; + JSValue handler; + uint8_t is_func; + uint8_t is_revoked; +} JSProxyData; + +typedef struct JSArrayBuffer { + int byte_length; /* 0 if detached */ + uint8_t detached; + uint8_t shared; /* if shared, the array buffer cannot be detached */ + uint8_t *data; /* NULL if detached */ + struct list_head array_list; + void *opaque; + JSFreeArrayBufferDataFunc *free_func; +} JSArrayBuffer; + +typedef struct JSTypedArray { + struct list_head link; /* link to arraybuffer */ + JSObject *obj; /* back pointer to the TypedArray/DataView object */ + JSObject *buffer; /* based array buffer */ + uint32_t offset; /* offset in the array buffer */ + uint32_t length; /* length in the array buffer */ +} JSTypedArray; + +typedef struct JSAsyncFunctionState { + JSValue this_val; /* 'this' generator argument */ + int argc; /* number of function arguments */ + BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */ + JSStackFrame frame; +} JSAsyncFunctionState; + +/* XXX: could use an object instead to avoid the + JS_TAG_ASYNC_FUNCTION tag for the GC */ +typedef struct JSAsyncFunctionData { + JSGCObjectHeader header; /* must come first */ + JSValue resolving_funcs[2]; + BOOL is_active; /* true if the async function state is valid */ + JSAsyncFunctionState func_state; +} JSAsyncFunctionData; + +typedef struct JSReqModuleEntry { + JSAtom module_name; + JSModuleDef *module; /* used using resolution */ +} JSReqModuleEntry; + +typedef enum JSExportTypeEnum { + JS_EXPORT_TYPE_LOCAL, + JS_EXPORT_TYPE_INDIRECT, +} JSExportTypeEnum; + +typedef struct JSExportEntry { + union { + struct { + int var_idx; /* closure variable index */ + JSVarRef *var_ref; /* if != NULL, reference to the variable */ + } local; /* for local export */ + int req_module_idx; /* module for indirect export */ + } u; + JSExportTypeEnum export_type; + JSAtom local_name; /* '*' if export ns from. not used for local + export after compilation */ + JSAtom export_name; /* exported variable name */ +} JSExportEntry; + +typedef struct JSStarExportEntry { + int req_module_idx; /* in req_module_entries */ +} JSStarExportEntry; + +typedef struct JSImportEntry { + int var_idx; /* closure variable index */ + JSAtom import_name; + int req_module_idx; /* in req_module_entries */ +} JSImportEntry; + +typedef enum { + JS_MODULE_STATUS_UNLINKED, + JS_MODULE_STATUS_LINKING, + JS_MODULE_STATUS_LINKED, + JS_MODULE_STATUS_EVALUATING, + JS_MODULE_STATUS_EVALUATING_ASYNC, + JS_MODULE_STATUS_EVALUATED, +} JSModuleStatus; + +struct JSModuleDef { + JSRefCountHeader header; /* must come first, 32-bit */ + JSAtom module_name; + struct list_head link; + + JSReqModuleEntry *req_module_entries; + int req_module_entries_count; + int req_module_entries_size; + + JSExportEntry *export_entries; + int export_entries_count; + int export_entries_size; + + JSStarExportEntry *star_export_entries; + int star_export_entries_count; + int star_export_entries_size; + + JSImportEntry *import_entries; + int import_entries_count; + int import_entries_size; + + JSValue module_ns; + JSValue func_obj; /* only used for JS modules */ + JSModuleInitFunc *init_func; /* only used for C modules */ + BOOL has_tla : 8; /* true if func_obj contains await */ + BOOL resolved : 8; + BOOL func_created : 8; + JSModuleStatus status : 8; + /* temp use during js_module_link() & js_module_evaluate() */ + int dfs_index, dfs_ancestor_index; + JSModuleDef *stack_prev; + /* temp use during js_module_evaluate() */ + JSModuleDef **async_parent_modules; + int async_parent_modules_count; + int async_parent_modules_size; + int pending_async_dependencies; + BOOL async_evaluation; + int64_t async_evaluation_timestamp; + JSModuleDef *cycle_root; + JSValue promise; /* corresponds to spec field: capability */ + JSValue resolving_funcs[2]; /* corresponds to spec field: capability */ + /* true if evaluation yielded an exception. It is saved in + eval_exception */ + BOOL eval_has_exception : 8; + JSValue eval_exception; + JSValue meta_obj; /* for import.meta */ +}; + +typedef struct JSJobEntry { + struct list_head link; + JSContext *ctx; + JSJobFunc *job_func; + int argc; + JSValue argv[]; +} JSJobEntry; + +typedef struct JSProperty { + union { + JSValue value; /* JS_PROP_NORMAL */ + struct { /* JS_PROP_GETSET */ + JSObject *getter; /* NULL if undefined */ + JSObject *setter; /* NULL if undefined */ + } getset; + JSVarRef *var_ref; /* JS_PROP_VARREF */ + struct { /* JS_PROP_AUTOINIT */ + /* in order to use only 2 pointers, we compress the realm + and the init function pointer */ + uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x) + in the 2 low bits */ + void *opaque; + } init; + } u; +} JSProperty; + +#define JS_PROP_INITIAL_SIZE 2 +#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */ +#define JS_ARRAY_INITIAL_SIZE 2 + +typedef struct JSShapeProperty { + uint32_t hash_next : 26; /* 0 if last in list */ + uint32_t flags : 6; /* JS_PROP_XXX */ + JSAtom atom; /* JS_ATOM_NULL = free property entry */ +} JSShapeProperty; + +struct JSShape { + /* hash table of size hash_mask + 1 before the start of the + structure (see prop_hash_end()). */ + JSGCObjectHeader header; + /* true if the shape is inserted in the shape hash table. If not, + JSShape.hash is not valid */ + uint8_t is_hashed; + /* If true, the shape may have small array index properties 'n' with 0 + <= n <= 2^31-1. If false, the shape is guaranteed not to have + small array index properties */ + uint8_t has_small_array_index; + uint32_t hash; /* current hash value */ + uint32_t prop_hash_mask; + int prop_size; /* allocated properties */ + int prop_count; /* include deleted properties */ + int deleted_prop_count; + JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */ + JSObject *proto; + JSShapeProperty prop[]; /* prop_size elements */ +}; + +struct JSObject { + union { + JSGCObjectHeader header; + struct { + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ + + uint8_t extensible : 1; + uint8_t free_mark : 1; /* only used when freeing objects with cycles */ + uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */ + uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */ + uint8_t is_constructor : 1; /* TRUE if object is a constructor function */ + uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */ + uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ + uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */ + uint16_t class_id; /* see JS_CLASS_x */ + }; + }; + /* byte offsets: 16/24 */ + JSShape *shape; /* prototype and property names + flag */ + JSProperty *prop; /* array of properties */ + /* byte offsets: 24/40 */ + JSWeakRefRecord *first_weak_ref; + /* byte offsets: 28/48 */ + union { + void *opaque; + struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */ + struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */ + struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */ + struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */ + struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */ + struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */ + struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */ + struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */ + struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */ + struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */ + struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */ + struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */ + struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */ + struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */ + struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */ + struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */ + struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */ + /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */ + struct JSFunctionBytecode *function_bytecode; + JSVarRef **var_refs; + JSObject *home_object; /* for 'super' access */ + } func; + struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */ + JSContext *realm; + JSCFunctionType c_function; + uint8_t length; + uint8_t cproto; + int16_t magic; + } cfunc; + /* array part for fast arrays and typed arrays */ + struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + union { + uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + } u1; + union { + JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */ + void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */ + int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */ + uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */ + int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */ + uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */ + int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */ + uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */ + int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */ + uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */ + float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */ + double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */ + } u; + uint32_t count; /* <= 2^31-1. 0 for a detached typed array */ + } array; /* 12/20 bytes */ + JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */ + JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */ + } u; + /* byte sizes: 40/48/72 */ +}; + +typedef struct JSCallSiteData { + JSValue filename; + JSValue func; + JSValue func_name; + BOOL native; + int line_num; + int col_num; +} JSCallSiteData; + +enum { + __JS_ATOM_NULL = JS_ATOM_NULL, +#define DEF(name, str) JS_ATOM_ ## name, +#include "quickjs-atom.h" +#undef DEF + JS_ATOM_END, +}; +#define JS_ATOM_LAST_KEYWORD JS_ATOM_super +#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield + +static const char js_atom_init[] = +#define DEF(name, str) str "\0" +#include "quickjs-atom.h" +#undef DEF +; + +typedef enum OPCodeFormat { +#define FMT(f) OP_FMT_ ## f, +#define DEF(id, size, n_pop, n_push, f) +#include "quickjs-opcode.h" +#undef DEF +#undef FMT +} OPCodeFormat; + +typedef enum OPCodeEnum { +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) OP_ ## id, +#define def(id, size, n_pop, n_push, f) +#include "quickjs-opcode.h" +#undef def +#undef DEF +#undef FMT + OP_COUNT, /* excluding temporary opcodes */ + /* temporary opcodes : overlap with the short opcodes */ + OP_TEMP_START = OP_nop + 1, + OP___dummy = OP_TEMP_START - 1, +#define FMT(f) +#define DEF(id, size, n_pop, n_push, f) +#define def(id, size, n_pop, n_push, f) OP_ ## id, +#include "quickjs-opcode.h" +#undef def +#undef DEF +#undef FMT + OP_TEMP_END, +} OPCodeEnum; + +static int JS_InitAtoms(JSRuntime *rt); +static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, + int atom_type); +static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p); +static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); +static JSValue js_call_c_function(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, int flags); +static JSValue js_call_bound_function(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, int flags); +static JSValue JS_CallInternal(JSContext *ctx, JSValue func_obj, + JSValue this_obj, JSValue new_target, + int argc, JSValue *argv, int flags); +static JSValue JS_CallConstructorInternal(JSContext *ctx, + JSValue func_obj, + JSValue new_target, + int argc, JSValue *argv, int flags); +static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValue this_obj, + int argc, JSValue *argv); +static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValue *argv); +static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, + JSValue val, BOOL is_array_ctor); +static JSValue JS_EvalObject(JSContext *ctx, JSValue this_obj, + JSValue val, int flags, int scope_idx); +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); + +static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p); +static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); +static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); +static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); +static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValue val); +static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); +static __maybe_unused void JS_DumpShapes(JSRuntime *rt); + +static JSValue js_function_apply(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic); +static void js_array_finalizer(JSRuntime *rt, JSValue val); +static void js_array_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_object_data_finalizer(JSRuntime *rt, JSValue val); +static void js_object_data_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_c_function_finalizer(JSRuntime *rt, JSValue val); +static void js_c_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val); +static void js_bytecode_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_bound_function_finalizer(JSRuntime *rt, JSValue val); +static void js_bound_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val); +static void js_for_in_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_regexp_finalizer(JSRuntime *rt, JSValue val); +static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val); +static void js_typed_array_finalizer(JSRuntime *rt, JSValue val); +static void js_typed_array_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_proxy_finalizer(JSRuntime *rt, JSValue val); +static void js_proxy_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_map_finalizer(JSRuntime *rt, JSValue val); +static void js_map_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val); +static void js_map_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val); +static void js_array_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val); +static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_generator_finalizer(JSRuntime *rt, JSValue obj); +static void js_generator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_promise_finalizer(JSRuntime *rt, JSValue val); +static void js_promise_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val); +static void js_promise_resolve_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); + +#define HINT_STRING 0 +#define HINT_NUMBER 1 +#define HINT_NONE 2 +#define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive +static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint); +static JSValue JS_ToStringFree(JSContext *ctx, JSValue val); +static int JS_ToBoolFree(JSContext *ctx, JSValue val); +static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val); +static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val); +static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val); +static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len); +static JSValue js_compile_regexp(JSContext *ctx, JSValue pattern, + JSValue flags); +static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValue ctor, + JSValue pattern, JSValue bc); +static void gc_decref(JSRuntime *rt); +static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, + const JSClassDef *class_def, JSAtom name); +static JSValue js_array_push(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int unshift); + +typedef enum JSStrictEqModeEnum { + JS_EQ_STRICT, + JS_EQ_SAME_VALUE, + JS_EQ_SAME_VALUE_ZERO, +} JSStrictEqModeEnum; + +static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, + JSStrictEqModeEnum eq_mode); +static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2); +static BOOL js_same_value(JSContext *ctx, JSValue op1, JSValue op2); +static BOOL js_same_value_zero(JSContext *ctx, JSValue op1, JSValue op2); +static JSValue JS_ToObject(JSContext *ctx, JSValue val); +static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val); +static JSProperty *add_property(JSContext *ctx, + JSObject *p, JSAtom prop, int prop_flags); +static JSValue JS_NewBigInt(JSContext *ctx); +static inline bf_t *JS_GetBigInt(JSValue val) +{ + JSBigInt *p = JS_VALUE_GET_PTR(val); + return &p->num; +} +static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val); +static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val); +static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val); +static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValue val); +static bf_t *JS_ToBigInt1(JSContext *ctx, bf_t *buf, JSValue val); +static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf); +JSValue JS_ThrowOutOfMemory(JSContext *ctx); +static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); +static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValue obj); +static int js_proxy_setPrototypeOf(JSContext *ctx, JSValue obj, + JSValue proto_val, BOOL throw_flag); +static int js_proxy_isExtensible(JSContext *ctx, JSValue obj); +static int js_proxy_preventExtensions(JSContext *ctx, JSValue obj); +static int js_proxy_isArray(JSContext *ctx, JSValue obj); +static int JS_CreateProperty(JSContext *ctx, JSObject *p, + JSAtom prop, JSValue val, + JSValue getter, JSValue setter, + int flags); +static int js_string_memcmp(const JSString *p1, const JSString *p2, int len); +static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref); +static BOOL is_valid_weakref_target(JSValue val); +static void insert_weakref_record(JSValue target, struct JSWeakRefRecord *wr); +static JSValue js_array_buffer_constructor3(JSContext *ctx, + JSValue new_target, + uint64_t len, JSClassID class_id, + uint8_t *buf, + JSFreeArrayBufferDataFunc *free_func, + void *opaque, BOOL alloc_flag); +static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValue obj); +static JSValue js_typed_array_constructor(JSContext *ctx, + JSValue this_val, + int argc, JSValue *argv, + int classid); +static JSValue js_typed_array_constructor_ta(JSContext *ctx, + JSValue new_target, + JSValue src_obj, + int classid); +static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p); +static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p); +static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx); +static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx, + BOOL is_arg); +static JSValue js_generator_function_call(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, + int flags); +static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val); +static void js_async_function_resolve_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static JSValue JS_EvalInternal(JSContext *ctx, JSValue this_obj, + const char *input, size_t input_len, + const char *filename, int flags, int scope_idx); +static void js_free_module_def(JSContext *ctx, JSModuleDef *m); +static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, + JS_MarkFunc *mark_func); +static JSValue js_import_meta(JSContext *ctx); +static JSValue js_dynamic_import(JSContext *ctx, JSValue specifier); +static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); +static JSValue js_new_promise_capability(JSContext *ctx, + JSValue *resolving_funcs, + JSValue ctor); +static __exception int perform_promise_then(JSContext *ctx, + JSValue promise, + JSValue *resolve_reject, + JSValue *cap_resolving_funcs); +static JSValue js_promise_resolve(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic); +static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); +static int js_string_compare(JSContext *ctx, + const JSString *p1, const JSString *p2); +static JSValue JS_ToNumber(JSContext *ctx, JSValue val); +static int JS_SetPropertyValue(JSContext *ctx, JSValue this_obj, + JSValue prop, JSValue val, int flags); +static int JS_NumberIsInteger(JSContext *ctx, JSValue val); +static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValue val); +static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val); +static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, + JSObject *p, JSAtom prop); +static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc); +static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, + JS_MarkFunc *mark_func); +static void JS_AddIntrinsicBasicObjects(JSContext *ctx); +static void js_free_shape(JSRuntime *rt, JSShape *sh); +static void js_free_shape_null(JSRuntime *rt, JSShape *sh); +static int js_shape_prepare_update(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs); +static int init_shape_hash(JSRuntime *rt); +static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, + JSValue obj); +static __exception int js_get_length64(JSContext *ctx, int64_t *pres, + JSValue obj); +static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len); +static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, + JSValue array_arg); +static BOOL js_get_fast_array(JSContext *ctx, JSValue obj, + JSValue **arrpp, uint32_t *countp); +static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx, + JSValue sync_iter); +static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val); +static void js_c_function_data_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func); +static JSValue js_c_function_data_call(JSContext *ctx, JSValue func_obj, + JSValue this_val, + int argc, JSValue *argv, int flags); +static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val); +static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, + JSGCObjectTypeEnum type); +static void remove_gc_object(JSGCObjectHeader *h); +static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s); +static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); +static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, + void *opaque); +static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p, + JSAtom atom, void *opaque); +void JS_SetUncatchableError(JSContext *ctx, JSValue val, BOOL flag); + +static JSValue js_new_callsite(JSContext *ctx, JSCallSiteData *csd); +static void js_new_callsite_data(JSContext *ctx, JSCallSiteData *csd, JSStackFrame *sf); +static void js_new_callsite_data2(JSContext *ctx, JSCallSiteData *csd, const char *filename, int line_num, int col_num); +static void _JS_AddIntrinsicCallSite(JSContext *ctx); + +static const JSClassExoticMethods js_arguments_exotic_methods; +static const JSClassExoticMethods js_string_exotic_methods; +static const JSClassExoticMethods js_proxy_exotic_methods; +static const JSClassExoticMethods js_module_ns_exotic_methods; + +static inline BOOL double_is_int32(double d) +{ + uint64_t u, e; + JSFloat64Union t; + + t.d = d; + u = t.u64; + + e = ((u >> 52) & 0x7FF) - 1023; + if (e > 30) { + // accept 0, INT32_MIN, reject too large, too small, nan, inf, -0 + return !u || (u == 0xc1e0000000000000); + } else { + // shift out sign, exponent and whole part bits + // value is fractional if remaining low bits are non-zero + return !(u << 12 << e); + } +} + +static JSValue js_float64(double d) +{ + return __JS_NewFloat64(d); +} + +static int compare_u32(uint32_t a, uint32_t b) +{ + return -(a < b) + (b < a); // -1, 0 or 1 +} + +static JSValue js_int32(int32_t v) +{ + return JS_MKVAL(JS_TAG_INT, v); +} + +static JSValue js_uint32(uint32_t v) +{ + if (v <= INT32_MAX) + return js_int32(v); + else + return js_float64(v); +} + +static JSValue js_int64(int64_t v) +{ + if (v >= INT32_MIN && v <= INT32_MAX) + return js_int32(v); + else + return js_float64(v); +} + +#define JS_NewInt64(ctx, val) js_int64(val) + +static JSValue js_number(double d) +{ + if (double_is_int32(d)) + return js_int32((int32_t)d); + else + return js_float64(d); +} + +JSValue JS_NewNumber(JSContext *ctx, double d) +{ + return js_number(d); +} + +static JSValue js_bool(JS_BOOL v) +{ + return JS_MKVAL(JS_TAG_BOOL, (v != 0)); +} + +static JSValue js_dup(JSValue v) +{ + if (JS_VALUE_HAS_REF_COUNT(v)) { + JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); + p->ref_count++; + } + return v; +} + +static void js_trigger_gc(JSRuntime *rt, size_t size) +{ + BOOL force_gc; +#ifdef FORCE_GC_AT_MALLOC + force_gc = TRUE; +#else + force_gc = ((rt->malloc_state.malloc_size + size) > + rt->malloc_gc_threshold); +#endif + if (force_gc) { +#ifdef DUMP_GC + if (check_dump_flag(rt, DUMP_GC)) { + printf("GC: size=%" PRIu64 "\n", + (uint64_t)rt->malloc_state.malloc_size); + } +#endif + JS_RunGC(rt); + rt->malloc_gc_threshold = rt->malloc_state.malloc_size + + (rt->malloc_state.malloc_size >> 1); + } +} + +static size_t js_malloc_usable_size_unknown(const void *ptr) +{ + return 0; +} + +void *js_malloc_rt(JSRuntime *rt, size_t size) +{ + return rt->mf.js_malloc(&rt->malloc_state, size); +} + +void js_free_rt(JSRuntime *rt, void *ptr) +{ + rt->mf.js_free(&rt->malloc_state, ptr); +} + +void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size) +{ + return rt->mf.js_realloc(&rt->malloc_state, ptr, size); +} + +static void *js_dbuf_realloc(void *opaque, void *ptr, size_t size) +{ + JSRuntime *rt = opaque; + return js_realloc_rt(rt, ptr, size); +} + +size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr) +{ + return rt->mf.js_malloc_usable_size(ptr); +} + +void *js_mallocz_rt(JSRuntime *rt, size_t size) +{ + void *ptr; + ptr = js_malloc_rt(rt, size); + if (!ptr) + return NULL; + return memset(ptr, 0, size); +} + +/* called by libbf */ +static void *js_bf_realloc(void *opaque, void *ptr, size_t size) +{ + JSRuntime *rt = opaque; + return js_realloc_rt(rt, ptr, size); +} + +/* Throw out of memory in case of error */ +void *js_malloc(JSContext *ctx, size_t size) +{ + void *ptr; + ptr = js_malloc_rt(ctx->rt, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +/* Throw out of memory in case of error */ +void *js_mallocz(JSContext *ctx, size_t size) +{ + void *ptr; + ptr = js_mallocz_rt(ctx->rt, size); + if (unlikely(!ptr)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ptr; +} + +void js_free(JSContext *ctx, void *ptr) +{ + js_free_rt(ctx->rt, ptr); +} + +/* Throw out of memory in case of error */ +void *js_realloc(JSContext *ctx, void *ptr, size_t size) +{ + void *ret; + ret = js_realloc_rt(ctx->rt, ptr, size); + if (unlikely(!ret && size != 0)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return ret; +} + +/* store extra allocated size in *pslack if successful */ +void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack) +{ + void *ret; + ret = js_realloc_rt(ctx->rt, ptr, size); + if (unlikely(!ret && size != 0)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + if (pslack) { + size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret); + *pslack = (new_size > size) ? new_size - size : 0; + } + return ret; +} + +size_t js_malloc_usable_size(JSContext *ctx, const void *ptr) +{ + return js_malloc_usable_size_rt(ctx->rt, ptr); +} + +/* Throw out of memory exception in case of error */ +char *js_strndup(JSContext *ctx, const char *s, size_t n) +{ + char *ptr; + ptr = js_malloc(ctx, n + 1); + if (ptr) { + memcpy(ptr, s, n); + ptr[n] = '\0'; + } + return ptr; +} + +char *js_strdup(JSContext *ctx, const char *str) +{ + return js_strndup(ctx, str, strlen(str)); +} + +static no_inline int js_realloc_array(JSContext *ctx, void **parray, + int elem_size, int *psize, int req_size) +{ + int new_size; + size_t slack; + void *new_array; + /* XXX: potential arithmetic overflow */ + new_size = max_int(req_size, *psize * 3 / 2); + new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack); + if (!new_array) + return -1; + new_size += slack / elem_size; + *psize = new_size; + *parray = new_array; + return 0; +} + +/* resize the array and update its size if req_size > *psize */ +static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size, + int *psize, int req_size) +{ + if (unlikely(req_size > *psize)) + return js_realloc_array(ctx, parray, elem_size, psize, req_size); + else + return 0; +} + +static inline void js_dbuf_init(JSContext *ctx, DynBuf *s) +{ + dbuf_init2(s, ctx->rt, js_dbuf_realloc); +} + +static inline int is_digit(int c) { + return c >= '0' && c <= '9'; +} + +static inline int string_get(const JSString *p, int idx) { + return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; +} + +typedef struct JSClassShortDef { + JSAtom class_name; + JSClassFinalizer *finalizer; + JSClassGCMark *gc_mark; +} JSClassShortDef; + +static JSClassShortDef const js_std_class_def[] = { + { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */ + { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */ + { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */ + { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */ + { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */ + { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */ + { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */ + { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */ + { JS_ATOM_Arguments, NULL, NULL }, /* JS_CLASS_MAPPED_ARGUMENTS */ + { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */ + { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */ + { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */ + { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */ + { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */ + { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */ + { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */ + { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */ + { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */ + { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */ + { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */ + { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */ + { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */ + { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */ + { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */ + { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */ + { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */ + { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */ + { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */ + { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */ + { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */ + { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */ + { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */ + { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */ + { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ + { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */ + { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */ + { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */ + { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */ + { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */ + { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */ + { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */ + { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */ + { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */ +}; + +static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, + int start, int count) +{ + JSClassDef cm_s, *cm = &cm_s; + int i, class_id; + + for(i = 0; i < count; i++) { + class_id = i + start; + memset(cm, 0, sizeof(*cm)); + cm->finalizer = tab[i].finalizer; + cm->gc_mark = tab[i].gc_mark; + if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0) + return -1; + } + return 0; +} + +/* Note: OS and CPU dependent */ +static inline uintptr_t js_get_stack_pointer(void) +{ + return (uintptr_t)__builtin_frame_address(0); +} + +static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) +{ + uintptr_t sp; + sp = js_get_stack_pointer() - alloca_size; + return unlikely(sp < rt->stack_limit); +} + +JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) +{ + JSRuntime *rt; + JSMallocState ms; + + memset(&ms, 0, sizeof(ms)); + ms.opaque = opaque; + ms.malloc_limit = 0; + + rt = mf->js_malloc(&ms, sizeof(JSRuntime)); + if (!rt) + return NULL; + memset(rt, 0, sizeof(*rt)); + rt->mf = *mf; + if (!rt->mf.js_malloc_usable_size) { + /* use dummy function if none provided */ + rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown; + } + rt->malloc_state = ms; + rt->malloc_gc_threshold = 256 * 1024; + + bf_context_init(&rt->bf_ctx, js_bf_realloc, rt); + + init_list_head(&rt->context_list); + init_list_head(&rt->gc_obj_list); + init_list_head(&rt->gc_zero_ref_count_list); + rt->gc_phase = JS_GC_PHASE_NONE; + +#ifdef DUMP_LEAKS + init_list_head(&rt->string_list); +#endif + init_list_head(&rt->job_list); + + if (JS_InitAtoms(rt)) + goto fail; + + /* create the object, array and function classes */ + if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT, + countof(js_std_class_def)) < 0) + goto fail; + rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods; + rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods; + rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods; + + rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function; + rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call; + rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function; + rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call; + if (init_shape_hash(rt)) + goto fail; + + rt->js_class_id_alloc = JS_CLASS_INIT_COUNT; + + rt->stack_size = JS_DEFAULT_STACK_SIZE; +#ifdef __ASAN__ + rt->stack_size *= 2; // stack frames are bigger under AddressSanitizer +#endif + JS_UpdateStackTop(rt); + + rt->current_exception = JS_NULL; + + return rt; + fail: + JS_FreeRuntime(rt); + return NULL; +} + +void *JS_GetRuntimeOpaque(JSRuntime *rt) +{ + return rt->user_opaque; +} + +void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque) +{ + rt->user_opaque = opaque; +} + +static void *js_def_malloc(JSMallocState *s, size_t size) +{ + void *ptr; + + /* Do not allocate zero bytes: behavior is platform dependent */ + assert(size != 0); + + /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ + if (unlikely(s->malloc_size + size > s->malloc_limit - 1)) + return NULL; + + ptr = malloc(size); + if (!ptr) + return NULL; + + s->malloc_count++; + s->malloc_size += js__malloc_usable_size(ptr) + MALLOC_OVERHEAD; + return ptr; +} + +static void js_def_free(JSMallocState *s, void *ptr) +{ + if (!ptr) + return; + + s->malloc_count--; + s->malloc_size -= js__malloc_usable_size(ptr) + MALLOC_OVERHEAD; + free(ptr); +} + +static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size) +{ + size_t old_size; + + if (!ptr) { + if (size == 0) + return NULL; + return js_def_malloc(s, size); + } + old_size = js__malloc_usable_size(ptr); + if (size == 0) { + s->malloc_count--; + s->malloc_size -= old_size + MALLOC_OVERHEAD; + free(ptr); + return NULL; + } + /* When malloc_limit is 0 (unlimited), malloc_limit - 1 will be SIZE_MAX. */ + if (s->malloc_size + size - old_size > s->malloc_limit - 1) + return NULL; + + ptr = realloc(ptr, size); + if (!ptr) + return NULL; + + s->malloc_size += js__malloc_usable_size(ptr) - old_size; + return ptr; +} + +static const JSMallocFunctions def_malloc_funcs = { + js_def_malloc, + js_def_free, + js_def_realloc, + js__malloc_usable_size +}; + +JSRuntime *JS_NewRuntime(void) +{ + return JS_NewRuntime2(&def_malloc_funcs, NULL); +} + +void JS_SetMemoryLimit(JSRuntime *rt, size_t limit) +{ + rt->malloc_state.malloc_limit = limit; +} + +void JS_SetDumpFlags(JSRuntime *rt, uint64_t flags) +{ + rt->dump_flags = flags; +} + +size_t JS_GetGCThreshold(JSRuntime *rt) { + return rt->malloc_gc_threshold; +} + +/* use -1 to disable automatic GC */ +void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold) +{ + rt->malloc_gc_threshold = gc_threshold; +} + +#define malloc(s) malloc_is_forbidden(s) +#define free(p) free_is_forbidden(p) +#define realloc(p,s) realloc_is_forbidden(p,s) + +void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque) +{ + rt->interrupt_handler = cb; + rt->interrupt_opaque = opaque; +} + +void JS_SetCanBlock(JSRuntime *rt, BOOL can_block) +{ + rt->can_block = can_block; +} + +void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, + const JSSharedArrayBufferFunctions *sf) +{ + rt->sab_funcs = *sf; +} + +/* return 0 if OK, < 0 if exception */ +int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, + int argc, JSValue *argv) +{ + JSRuntime *rt = ctx->rt; + JSJobEntry *e; + int i; + + e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue)); + if (!e) + return -1; + e->ctx = ctx; + e->job_func = job_func; + e->argc = argc; + for(i = 0; i < argc; i++) { + e->argv[i] = js_dup(argv[i]); + } + list_add_tail(&e->link, &rt->job_list); + return 0; +} + +BOOL JS_IsJobPending(JSRuntime *rt) +{ + return !list_empty(&rt->job_list); +} + +/* return < 0 if exception, 0 if no job pending, 1 if a job was + executed successfully. the context of the job is stored in '*pctx' */ +int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx) +{ + JSContext *ctx; + JSJobEntry *e; + JSValue res; + int i, ret; + + if (list_empty(&rt->job_list)) { + *pctx = NULL; + return 0; + } + + /* get the first pending job and execute it */ + e = list_entry(rt->job_list.next, JSJobEntry, link); + list_del(&e->link); + ctx = e->ctx; + res = e->job_func(e->ctx, e->argc, e->argv); + for(i = 0; i < e->argc; i++) + JS_FreeValue(ctx, e->argv[i]); + if (JS_IsException(res)) + ret = -1; + else + ret = 1; + JS_FreeValue(ctx, res); + js_free(ctx, e); + *pctx = ctx; + return ret; +} + +static inline uint32_t atom_get_free(const JSAtomStruct *p) +{ + return (uintptr_t)p >> 1; +} + +static inline BOOL atom_is_free(const JSAtomStruct *p) +{ + return (uintptr_t)p & 1; +} + +static inline JSAtomStruct *atom_set_free(uint32_t v) +{ + return (JSAtomStruct *)(((uintptr_t)v << 1) | 1); +} + +/* Note: the string contents are uninitialized */ +static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char) +{ + JSString *str; + str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char); + if (unlikely(!str)) + return NULL; + str->header.ref_count = 1; + str->is_wide_char = is_wide_char; + str->len = max_len; + str->atom_type = 0; + str->hash = 0; /* optional but costless */ + str->hash_next = 0; /* optional */ +#ifdef DUMP_LEAKS + list_add_tail(&str->link, &rt->string_list); +#endif + return str; +} + +static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char) +{ + JSString *p; + p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char); + if (unlikely(!p)) { + JS_ThrowOutOfMemory(ctx); + return NULL; + } + return p; +} + +/* same as JS_FreeValueRT() but faster */ +static inline void js_free_string(JSRuntime *rt, JSString *str) +{ + if (--str->header.ref_count <= 0) { + if (str->atom_type) { + JS_FreeAtomStruct(rt, str); + } else { +#ifdef DUMP_LEAKS + list_del(&str->link); +#endif + js_free_rt(rt, str); + } + } +} + +void JS_SetRuntimeInfo(JSRuntime *rt, const char *s) +{ + if (rt) + rt->rt_info = s; +} + +void JS_FreeRuntime(JSRuntime *rt) +{ + struct list_head *el, *el1; + int i; + + JS_FreeValueRT(rt, rt->current_exception); + + list_for_each_safe(el, el1, &rt->job_list) { + JSJobEntry *e = list_entry(el, JSJobEntry, link); + for(i = 0; i < e->argc; i++) + JS_FreeValueRT(rt, e->argv[i]); + js_free_rt(rt, e); + } + init_list_head(&rt->job_list); + + JS_RunGC(rt); + +#ifdef DUMP_LEAKS + /* leaking objects */ + if (check_dump_flag(rt, DUMP_LEAKS)) { + BOOL header_done; + JSGCObjectHeader *p; + int count; + + /* remove the internal refcounts to display only the object + referenced externally */ + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + p->mark = 0; + } + gc_decref(rt); + + header_done = FALSE; + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + if (p->ref_count != 0) { + if (!header_done) { + printf("Object leaks:\n"); + JS_DumpObjectHeader(rt); + header_done = TRUE; + } + JS_DumpGCObject(rt, p); + } + } + + count = 0; + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + if (p->ref_count == 0) { + count++; + } + } + if (count != 0) + printf("Secondary object leaks: %d\n", count); + } +#endif + assert(list_empty(&rt->gc_obj_list)); + + /* free the classes */ + for(i = 0; i < rt->class_count; i++) { + JSClass *cl = &rt->class_array[i]; + if (cl->class_id != 0) { + JS_FreeAtomRT(rt, cl->class_name); + } + } + js_free_rt(rt, rt->class_array); + + bf_context_end(&rt->bf_ctx); + +#ifdef DUMP_ATOM_LEAKS + /* only the atoms defined in JS_InitAtoms() should be left */ + if (check_dump_flag(rt, DUMP_ATOM_LEAKS)) { + BOOL header_done = FALSE; + + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p) /* && p->str*/) { + if (i >= JS_ATOM_END || p->header.ref_count != 1) { + if (!header_done) { + header_done = TRUE; + if (rt->rt_info) { + printf("%s:1: atom leakage:", rt->rt_info); + } else { + printf("Atom leaks:\n" + " %6s %6s %s\n", + "ID", "REFCNT", "NAME"); + } + } + if (rt->rt_info) { + printf(" "); + } else { + printf(" %6u %6u ", i, p->header.ref_count); + } + switch (p->atom_type) { + case JS_ATOM_TYPE_STRING: + JS_DumpString(rt, p); + break; + case JS_ATOM_TYPE_GLOBAL_SYMBOL: + printf("Symbol.for("); + JS_DumpString(rt, p); + printf(")"); + break; + case JS_ATOM_TYPE_SYMBOL: + if (p->hash == JS_ATOM_HASH_SYMBOL) { + printf("Symbol("); + JS_DumpString(rt, p); + printf(")"); + } else { + printf("Private("); + JS_DumpString(rt, p); + printf(")"); + } + break; + } + if (rt->rt_info) { + printf(":%u", p->header.ref_count); + } else { + printf("\n"); + } + } + } + } + if (rt->rt_info && header_done) + printf("\n"); + } +#endif + + /* free the atoms */ + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p)) { +#ifdef DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + } + } + js_free_rt(rt, rt->atom_array); + js_free_rt(rt, rt->atom_hash); + js_free_rt(rt, rt->shape_hash); +#ifdef DUMP_LEAKS + if (check_dump_flag(rt, DUMP_LEAKS) && !list_empty(&rt->string_list)) { + if (rt->rt_info) { + printf("%s:1: string leakage:", rt->rt_info); + } else { + printf("String leaks:\n" + " %6s %s\n", + "REFCNT", "VALUE"); + } + list_for_each_safe(el, el1, &rt->string_list) { + JSString *str = list_entry(el, JSString, link); + if (rt->rt_info) { + printf(" "); + } else { + printf(" %6u ", str->header.ref_count); + } + JS_DumpString(rt, str); + if (rt->rt_info) { + printf(":%u", str->header.ref_count); + } else { + printf("\n"); + } + list_del(&str->link); + js_free_rt(rt, str); + } + if (rt->rt_info) + printf("\n"); + } + if (check_dump_flag(rt, DUMP_LEAKS)) { + JSMallocState *s = &rt->malloc_state; + if (s->malloc_count > 1) { + if (rt->rt_info) + printf("%s:1: ", rt->rt_info); + printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n", + (uint64_t)(s->malloc_size - sizeof(JSRuntime)), + (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]); + } + } +#endif + + { + JSMallocState ms = rt->malloc_state; + rt->mf.js_free(&ms, rt); + } +} + +JSContext *JS_NewContextRaw(JSRuntime *rt) +{ + JSContext *ctx; + int i; + + ctx = js_mallocz_rt(rt, sizeof(JSContext)); + if (!ctx) + return NULL; + ctx->header.ref_count = 1; + add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT); + + ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) * + rt->class_count); + if (!ctx->class_proto) { + js_free_rt(rt, ctx); + return NULL; + } + ctx->rt = rt; + list_add_tail(&ctx->link, &rt->context_list); + ctx->bf_ctx = &rt->bf_ctx; + for(i = 0; i < rt->class_count; i++) + ctx->class_proto[i] = JS_NULL; + ctx->array_ctor = JS_NULL; + ctx->regexp_ctor = JS_NULL; + ctx->promise_ctor = JS_NULL; + ctx->error_ctor = JS_NULL; + ctx->error_prepare_stack = JS_UNDEFINED; + ctx->error_stack_trace_limit = 10; + init_list_head(&ctx->loaded_modules); + + JS_AddIntrinsicBasicObjects(ctx); + return ctx; +} + +JSContext *JS_NewContext(JSRuntime *rt) +{ + JSContext *ctx; + + ctx = JS_NewContextRaw(rt); + if (!ctx) + return NULL; + + JS_AddIntrinsicBaseObjects(ctx); + JS_AddIntrinsicDate(ctx); + JS_AddIntrinsicEval(ctx); + JS_AddIntrinsicRegExp(ctx); + JS_AddIntrinsicJSON(ctx); + JS_AddIntrinsicProxy(ctx); + JS_AddIntrinsicMapSet(ctx); + JS_AddIntrinsicTypedArrays(ctx); + JS_AddIntrinsicPromise(ctx); + JS_AddIntrinsicBigInt(ctx); + JS_AddIntrinsicWeakRef(ctx); + + JS_AddPerformance(ctx); + + return ctx; +} + +void *JS_GetContextOpaque(JSContext *ctx) +{ + return ctx->user_opaque; +} + +void JS_SetContextOpaque(JSContext *ctx, void *opaque) +{ + ctx->user_opaque = opaque; +} + +/* set the new value and free the old value after (freeing the value + can reallocate the object data) */ +static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val) +{ + JSValue old_val; + old_val = *pval; + *pval = new_val; + JS_FreeValue(ctx, old_val); +} + +void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj) +{ + assert(class_id < ctx->rt->class_count); + set_value(ctx, &ctx->class_proto[class_id], obj); +} + +JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id) +{ + assert(class_id < ctx->rt->class_count); + return js_dup(ctx->class_proto[class_id]); +} + +typedef enum JSFreeModuleEnum { + JS_FREE_MODULE_ALL, + JS_FREE_MODULE_NOT_RESOLVED, +} JSFreeModuleEnum; + +/* XXX: would be more efficient with separate module lists */ +static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag) +{ + struct list_head *el, *el1; + list_for_each_safe(el, el1, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el, JSModuleDef, link); + if (flag == JS_FREE_MODULE_ALL || + (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) { + js_free_module_def(ctx, m); + } + } +} + +JSContext *JS_DupContext(JSContext *ctx) +{ + ctx->header.ref_count++; + return ctx; +} + +/* used by the GC */ +static void JS_MarkContext(JSRuntime *rt, JSContext *ctx, + JS_MarkFunc *mark_func) +{ + int i; + struct list_head *el; + + /* modules are not seen by the GC, so we directly mark the objects + referenced by each module */ + list_for_each(el, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el, JSModuleDef, link); + js_mark_module_def(rt, m, mark_func); + } + + JS_MarkValue(rt, ctx->global_obj, mark_func); + JS_MarkValue(rt, ctx->global_var_obj, mark_func); + + JS_MarkValue(rt, ctx->throw_type_error, mark_func); + JS_MarkValue(rt, ctx->eval_obj, mark_func); + + JS_MarkValue(rt, ctx->array_proto_values, mark_func); + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { + JS_MarkValue(rt, ctx->native_error_proto[i], mark_func); + } + JS_MarkValue(rt, ctx->error_ctor, mark_func); + JS_MarkValue(rt, ctx->error_prepare_stack, mark_func); + for(i = 0; i < rt->class_count; i++) { + JS_MarkValue(rt, ctx->class_proto[i], mark_func); + } + JS_MarkValue(rt, ctx->iterator_proto, mark_func); + JS_MarkValue(rt, ctx->async_iterator_proto, mark_func); + JS_MarkValue(rt, ctx->promise_ctor, mark_func); + JS_MarkValue(rt, ctx->array_ctor, mark_func); + JS_MarkValue(rt, ctx->regexp_ctor, mark_func); + JS_MarkValue(rt, ctx->function_ctor, mark_func); + JS_MarkValue(rt, ctx->function_proto, mark_func); + + if (ctx->array_shape) + mark_func(rt, &ctx->array_shape->header); +} + +void JS_FreeContext(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + int i; + + if (--ctx->header.ref_count > 0) + return; + assert(ctx->header.ref_count == 0); + +#ifdef DUMP_ATOMS + if (check_dump_flag(rt, DUMP_ATOMS)) + JS_DumpAtoms(ctx->rt); +#endif +#ifdef DUMP_SHAPES + if (check_dump_flag(rt, DUMP_SHAPES)) + JS_DumpShapes(ctx->rt); +#endif +#ifdef DUMP_OBJECTS + if (check_dump_flag(rt, DUMP_OBJECTS)) { + struct list_head *el; + JSGCObjectHeader *p; + printf("JSObjects: {\n"); + JS_DumpObjectHeader(ctx->rt); + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + JS_DumpGCObject(rt, p); + } + printf("}\n"); + } +#endif +#ifdef DUMP_MEM + if (check_dump_flag(rt, DUMP_MEM)) { + JSMemoryUsage stats; + JS_ComputeMemoryUsage(rt, &stats); + JS_DumpMemoryUsage(stdout, &stats, rt); + } +#endif + + js_free_modules(ctx, JS_FREE_MODULE_ALL); + + JS_FreeValue(ctx, ctx->global_obj); + JS_FreeValue(ctx, ctx->global_var_obj); + + JS_FreeValue(ctx, ctx->throw_type_error); + JS_FreeValue(ctx, ctx->eval_obj); + + JS_FreeValue(ctx, ctx->array_proto_values); + for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { + JS_FreeValue(ctx, ctx->native_error_proto[i]); + } + JS_FreeValue(ctx, ctx->error_ctor); + JS_FreeValue(ctx, ctx->error_prepare_stack); + for(i = 0; i < rt->class_count; i++) { + JS_FreeValue(ctx, ctx->class_proto[i]); + } + js_free_rt(rt, ctx->class_proto); + JS_FreeValue(ctx, ctx->iterator_proto); + JS_FreeValue(ctx, ctx->async_iterator_proto); + JS_FreeValue(ctx, ctx->promise_ctor); + JS_FreeValue(ctx, ctx->array_ctor); + JS_FreeValue(ctx, ctx->regexp_ctor); + JS_FreeValue(ctx, ctx->function_ctor); + JS_FreeValue(ctx, ctx->function_proto); + + js_free_shape_null(ctx->rt, ctx->array_shape); + + list_del(&ctx->link); + remove_gc_object(&ctx->header); + js_free_rt(ctx->rt, ctx); +} + +JSRuntime *JS_GetRuntime(JSContext *ctx) +{ + return ctx->rt; +} + +static void update_stack_limit(JSRuntime *rt) +{ +#if defined(__wasi__) + rt->stack_limit = 0; /* no limit */ +#else + if (rt->stack_size == 0) { + rt->stack_limit = 0; /* no limit */ + } else { + rt->stack_limit = rt->stack_top - rt->stack_size; + } +#endif +} + +void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size) +{ + rt->stack_size = stack_size; + update_stack_limit(rt); +} + +void JS_UpdateStackTop(JSRuntime *rt) +{ + rt->stack_top = js_get_stack_pointer(); + update_stack_limit(rt); +} + +static inline BOOL is_strict_mode(JSContext *ctx) +{ + JSStackFrame *sf = ctx->rt->current_stack_frame; + return (sf && (sf->js_mode & JS_MODE_STRICT)); +} + +/* JSAtom support */ + +#define JS_ATOM_TAG_INT (1U << 31) +#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1) +#define JS_ATOM_MAX ((1U << 30) - 1) + +/* return the max count from the hash size */ +#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) + +static inline BOOL __JS_AtomIsConst(JSAtom v) +{ +#ifdef DUMP_ATOM_LEAKS + return (int32_t)v <= 0; +#else + return (int32_t)v < JS_ATOM_END; +#endif +} + +static inline BOOL __JS_AtomIsTaggedInt(JSAtom v) +{ + return (v & JS_ATOM_TAG_INT) != 0; +} + +static inline JSAtom __JS_AtomFromUInt32(uint32_t v) +{ + return v | JS_ATOM_TAG_INT; +} + +static inline uint32_t __JS_AtomToUInt32(JSAtom atom) +{ + return atom & ~JS_ATOM_TAG_INT; +} + +static inline int is_num(int c) +{ + return c >= '0' && c <= '9'; +} + +/* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */ +static inline BOOL is_num_string(uint32_t *pval, const JSString *p) +{ + uint32_t n; + uint64_t n64; + int c, i, len; + + len = p->len; + if (len == 0 || len > 10) + return FALSE; + c = string_get(p, 0); + if (is_num(c)) { + if (c == '0') { + if (len != 1) + return FALSE; + n = 0; + } else { + n = c - '0'; + for(i = 1; i < len; i++) { + c = string_get(p, i); + if (!is_num(c)) + return FALSE; + n64 = (uint64_t)n * 10 + (c - '0'); + if ((n64 >> 32) != 0) + return FALSE; + n = n64; + } + } + *pval = n; + return TRUE; + } else { + return FALSE; + } +} + +/* XXX: could use faster version ? */ +static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h) +{ + size_t i; + + for(i = 0; i < len; i++) + h = h * 263 + str[i]; + return h; +} + +static inline uint32_t hash_string16(const uint16_t *str, + size_t len, uint32_t h) +{ + size_t i; + + for(i = 0; i < len; i++) + h = h * 263 + str[i]; + return h; +} + +static uint32_t hash_string(const JSString *str, uint32_t h) +{ + if (str->is_wide_char) + h = hash_string16(str->u.str16, str->len, h); + else + h = hash_string8(str->u.str8, str->len, h); + return h; +} + +static __maybe_unused void JS_DumpString(JSRuntime *rt, + const JSString *p) +{ + int i, c, sep; + + if (p == NULL) { + printf(""); + return; + } + if (p->header.ref_count != 1) + printf("%d", p->header.ref_count); + if (p->is_wide_char) + putchar('L'); + sep = '\"'; + putchar(sep); + for(i = 0; i < p->len; i++) { + c = string_get(p, i); + if (c == sep || c == '\\') { + putchar('\\'); + putchar(c); + } else if (c >= ' ' && c <= 126) { + putchar(c); + } else if (c == '\n') { + putchar('\\'); + putchar('n'); + } else { + printf("\\u%04x", c); + } + } + putchar(sep); +} + +static __maybe_unused void JS_DumpAtoms(JSRuntime *rt) +{ + JSAtomStruct *p; + int h, i; + /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */ + printf("JSAtom count=%d size=%d hash_size=%d:\n", + rt->atom_count, rt->atom_size, rt->atom_hash_size); + printf("JSAtom hash table: {\n"); + for(i = 0; i < rt->atom_hash_size; i++) { + h = rt->atom_hash[i]; + if (h) { + printf(" %d:", i); + while (h) { + p = rt->atom_array[h]; + printf(" "); + JS_DumpString(rt, p); + h = p->hash_next; + } + printf("\n"); + } + } + printf("}\n"); + printf("JSAtom table: {\n"); + for(i = 0; i < rt->atom_size; i++) { + p = rt->atom_array[i]; + if (!atom_is_free(p)) { + printf(" %d: { %d %08x ", i, p->atom_type, p->hash); + if (!(p->len == 0 && p->is_wide_char != 0)) + JS_DumpString(rt, p); + printf(" %d }\n", p->hash_next); + } + } + printf("}\n"); +} + +static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size) +{ + JSAtomStruct *p; + uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash; + + assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */ + new_hash_mask = new_hash_size - 1; + new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size); + if (!new_hash) + return -1; + for(i = 0; i < rt->atom_hash_size; i++) { + h = rt->atom_hash[i]; + while (h != 0) { + p = rt->atom_array[h]; + hash_next1 = p->hash_next; + /* add in new hash table */ + j = p->hash & new_hash_mask; + p->hash_next = new_hash[j]; + new_hash[j] = h; + h = hash_next1; + } + } + js_free_rt(rt, rt->atom_hash); + rt->atom_hash = new_hash; + rt->atom_hash_size = new_hash_size; + rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size); + // JS_DumpAtoms(rt); + return 0; +} + +static int JS_InitAtoms(JSRuntime *rt) +{ + int i, len, atom_type; + const char *p; + + rt->atom_hash_size = 0; + rt->atom_hash = NULL; + rt->atom_count = 0; + rt->atom_size = 0; + rt->atom_free_index = 0; + if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */ + return -1; + + p = js_atom_init; + for(i = 1; i < JS_ATOM_END; i++) { + if (i == JS_ATOM_Private_brand) + atom_type = JS_ATOM_TYPE_PRIVATE; + else if (i >= JS_ATOM_Symbol_toPrimitive) + atom_type = JS_ATOM_TYPE_SYMBOL; + else + atom_type = JS_ATOM_TYPE_STRING; + len = strlen(p); + if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL) + return -1; + p = p + len + 1; + } + return 0; +} + +static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v) +{ + JSAtomStruct *p; + + if (!__JS_AtomIsConst(v)) { + p = rt->atom_array[v]; + p->header.ref_count++; + } + return v; +} + +JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + if (!__JS_AtomIsConst(v)) { + rt = ctx->rt; + p = rt->atom_array[v]; + p->header.ref_count++; + } + return v; +} + +static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + rt = ctx->rt; + if (__JS_AtomIsTaggedInt(v)) + return JS_ATOM_KIND_STRING; + p = rt->atom_array[v]; + switch(p->atom_type) { + case JS_ATOM_TYPE_STRING: + return JS_ATOM_KIND_STRING; + case JS_ATOM_TYPE_GLOBAL_SYMBOL: + return JS_ATOM_KIND_SYMBOL; + case JS_ATOM_TYPE_SYMBOL: + switch(p->hash) { + case JS_ATOM_HASH_SYMBOL: + return JS_ATOM_KIND_SYMBOL; + case JS_ATOM_HASH_PRIVATE: + return JS_ATOM_KIND_PRIVATE; + default: + abort(); + } + default: + abort(); + } + return (JSAtomKindEnum){-1}; // pacify compiler +} + +static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v) +{ + return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING; +} + +static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p) +{ + uint32_t i = p->hash_next; /* atom_index */ + if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { + JSAtomStruct *p1; + + i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)]; + p1 = rt->atom_array[i]; + while (p1 != p) { + assert(i != 0); + i = p1->hash_next; + p1 = rt->atom_array[i]; + } + } + return i; +} + +/* string case (internal). Return JS_ATOM_NULL if error. 'str' is + freed. */ +static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) +{ + uint32_t h, h1, i; + JSAtomStruct *p; + int len; + + if (atom_type < JS_ATOM_TYPE_SYMBOL) { + /* str is not NULL */ + if (str->atom_type == atom_type) { + /* str is the atom, return its index */ + i = js_get_atom_index(rt, str); + /* reduce string refcount and increase atom's unless constant */ + if (__JS_AtomIsConst(i)) + str->header.ref_count--; + return i; + } + /* try and locate an already registered atom */ + len = str->len; + h = hash_string(str, atom_type); + h &= JS_ATOM_HASH_MASK; + h1 = h & (rt->atom_hash_size - 1); + i = rt->atom_hash[h1]; + while (i != 0) { + p = rt->atom_array[i]; + if (p->hash == h && + p->atom_type == atom_type && + p->len == len && + js_string_memcmp(p, str, len) == 0) { + if (!__JS_AtomIsConst(i)) + p->header.ref_count++; + goto done; + } + i = p->hash_next; + } + } else { + h1 = 0; /* avoid warning */ + if (atom_type == JS_ATOM_TYPE_SYMBOL) { + h = JS_ATOM_HASH_SYMBOL; + } else { + h = JS_ATOM_HASH_PRIVATE; + atom_type = JS_ATOM_TYPE_SYMBOL; + } + } + + if (rt->atom_free_index == 0) { + /* allow new atom entries */ + uint32_t new_size, start; + JSAtomStruct **new_array; + + /* alloc new with size progression 3/2: + 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092 + preallocating space for predefined atoms (at least 195). + */ + new_size = max_int(211, rt->atom_size * 3 / 2); + if (new_size > JS_ATOM_MAX) + goto fail; + /* XXX: should use realloc2 to use slack space */ + new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size); + if (!new_array) + goto fail; + /* Note: the atom 0 is not used */ + start = rt->atom_size; + if (start == 0) { + /* JS_ATOM_NULL entry */ + p = js_mallocz_rt(rt, sizeof(JSAtomStruct)); + if (!p) { + js_free_rt(rt, new_array); + goto fail; + } + p->header.ref_count = 1; /* not refcounted */ + p->atom_type = JS_ATOM_TYPE_SYMBOL; +#ifdef DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + new_array[0] = p; + rt->atom_count++; + start = 1; + } + rt->atom_size = new_size; + rt->atom_array = new_array; + rt->atom_free_index = start; + for(i = start; i < new_size; i++) { + uint32_t next; + if (i == (new_size - 1)) + next = 0; + else + next = i + 1; + rt->atom_array[i] = atom_set_free(next); + } + } + + if (str) { + if (str->atom_type == 0) { + p = str; + p->atom_type = atom_type; + } else { + p = js_malloc_rt(rt, sizeof(JSString) + + (str->len << str->is_wide_char) + + 1 - str->is_wide_char); + if (unlikely(!p)) + goto fail; + p->header.ref_count = 1; + p->is_wide_char = str->is_wide_char; + p->len = str->len; +#ifdef DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) + + 1 - str->is_wide_char); + js_free_string(rt, str); + } + } else { + p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */ + if (!p) + return JS_ATOM_NULL; + p->header.ref_count = 1; + p->is_wide_char = 1; /* Hack to represent NULL as a JSString */ + p->len = 0; +#ifdef DUMP_LEAKS + list_add_tail(&p->link, &rt->string_list); +#endif + } + + /* use an already free entry */ + i = rt->atom_free_index; + rt->atom_free_index = atom_get_free(rt->atom_array[i]); + rt->atom_array[i] = p; + + p->hash = h; + p->hash_next = i; /* atom_index */ + p->atom_type = atom_type; + p->first_weak_ref = NULL; + + rt->atom_count++; + + if (atom_type != JS_ATOM_TYPE_SYMBOL) { + p->hash_next = rt->atom_hash[h1]; + rt->atom_hash[h1] = i; + if (unlikely(rt->atom_count >= rt->atom_count_resize)) + JS_ResizeAtomHash(rt, rt->atom_hash_size * 2); + } + + // JS_DumpAtoms(rt); + return i; + + fail: + i = JS_ATOM_NULL; + done: + if (str) + js_free_string(rt, str); + return i; +} + +// XXX: `str` must be pure ASCII. No UTF-8 encoded strings +// XXX: `str` must not be the string representation of a small integer +static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, + int atom_type) +{ + JSString *p; + p = js_alloc_string_rt(rt, len, 0); + if (!p) + return JS_ATOM_NULL; + memcpy(p->u.str8, str, len); + p->u.str8[len] = '\0'; + return __JS_NewAtom(rt, p, atom_type); +} + +// XXX: `str` must be raw 8-bit contents. No UTF-8 encoded strings +static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, + int atom_type) +{ + uint32_t h, h1, i; + JSAtomStruct *p; + + h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING); + h &= JS_ATOM_HASH_MASK; + h1 = h & (rt->atom_hash_size - 1); + i = rt->atom_hash[h1]; + while (i != 0) { + p = rt->atom_array[i]; + if (p->hash == h && + p->atom_type == JS_ATOM_TYPE_STRING && + p->len == len && + p->is_wide_char == 0 && + memcmp(p->u.str8, str, len) == 0) { + if (!__JS_AtomIsConst(i)) + p->header.ref_count++; + return i; + } + i = p->hash_next; + } + return JS_ATOM_NULL; +} + +static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p) +{ + uint32_t i = p->hash_next; /* atom_index */ + if (p->atom_type != JS_ATOM_TYPE_SYMBOL) { + JSAtomStruct *p0, *p1; + uint32_t h0; + + h0 = p->hash & (rt->atom_hash_size - 1); + i = rt->atom_hash[h0]; + p1 = rt->atom_array[i]; + if (p1 == p) { + rt->atom_hash[h0] = p1->hash_next; + } else { + for(;;) { + assert(i != 0); + p0 = p1; + i = p1->hash_next; + p1 = rt->atom_array[i]; + if (p1 == p) { + p0->hash_next = p1->hash_next; + break; + } + } + } + } + /* insert in free atom list */ + rt->atom_array[i] = atom_set_free(rt->atom_free_index); + rt->atom_free_index = i; + if (unlikely(p->first_weak_ref)) { + reset_weak_ref(rt, &p->first_weak_ref); + } + /* free the string structure */ +#ifdef DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + rt->atom_count--; + assert(rt->atom_count >= 0); +} + +static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) +{ + JSAtomStruct *p; + + p = rt->atom_array[i]; + if (--p->header.ref_count > 0) + return; + JS_FreeAtomStruct(rt, p); +} + +/* Warning: 'p' is freed */ +static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) +{ + JSRuntime *rt = ctx->rt; + uint32_t n; + if (is_num_string(&n, p)) { + if (n <= JS_ATOM_MAX_INT) { + js_free_string(rt, p); + return __JS_AtomFromUInt32(n); + } + } + /* XXX: should generate an exception */ + return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); +} + +/* `str` may be pure ASCII or UTF-8 encoded */ +JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) +{ + JSValue val; + + if (len == 0 || !is_digit(*str)) { + // TODO(chqrlie): this does not work if `str` has UTF-8 encoded contents + // bug example: `({ "\u00c3\u00a9": 1 }).\u00e9` evaluates to `1`. + JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); + if (atom) + return atom; + } + val = JS_NewStringLen(ctx, str, len); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val)); +} + +/* `str` may be pure ASCII or UTF-8 encoded */ +JSAtom JS_NewAtom(JSContext *ctx, const char *str) +{ + return JS_NewAtomLen(ctx, str, strlen(str)); +} + +JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) +{ + if (n <= JS_ATOM_MAX_INT) { + return __JS_AtomFromUInt32(n); + } else { + char buf[16]; + size_t len = u32toa(buf, n); + JSValue val = js_new_string8_len(ctx, buf, len); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), + JS_ATOM_TYPE_STRING); + } +} + +static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) +{ + if ((uint64_t)n <= JS_ATOM_MAX_INT) { + return __JS_AtomFromUInt32((uint32_t)n); + } else { + char buf[24]; + size_t len = i64toa(buf, n); + JSValue val = js_new_string8_len(ctx, buf, len); + if (JS_IsException(val)) + return JS_ATOM_NULL; + return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), + JS_ATOM_TYPE_STRING); + } +} + +/* 'p' is freed */ +static JSValue JS_NewSymbolInternal(JSContext *ctx, JSString *p, int atom_type) +{ + JSRuntime *rt = ctx->rt; + JSAtom atom; + atom = __JS_NewAtom(rt, p, atom_type); + if (atom == JS_ATOM_NULL) + return JS_ThrowOutOfMemory(ctx); + return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]); +} + +/* descr must be a non-numeric string atom */ +static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, + int atom_type) +{ + JSRuntime *rt = ctx->rt; + JSString *p; + + assert(!__JS_AtomIsTaggedInt(descr)); + assert(descr < rt->atom_size); + p = rt->atom_array[descr]; + js_dup(JS_MKPTR(JS_TAG_STRING, p)); + return JS_NewSymbolInternal(ctx, p, atom_type); +} + +/* `description` may be pure ASCII or UTF-8 encoded */ +JSValue JS_NewSymbol(JSContext *ctx, const char *description, JS_BOOL is_global) +{ + JSAtom atom = JS_NewAtom(ctx, description); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; + return JS_NewSymbolFromAtom(ctx, atom, is_global ? JS_ATOM_TYPE_GLOBAL_SYMBOL : JS_ATOM_TYPE_SYMBOL); +} + +#define ATOM_GET_STR_BUF_SIZE 64 + +/* Should only be used for debug. */ +static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, + JSAtom atom) +{ + if (__JS_AtomIsTaggedInt(atom)) { + snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom)); + } else if (atom == JS_ATOM_NULL) { + snprintf(buf, buf_size, ""); + } else if (atom >= rt->atom_size) { + assert(atom < rt->atom_size); + snprintf(buf, buf_size, "", atom); + } else { + JSAtomStruct *p = rt->atom_array[atom]; + *buf = '\0'; + if (atom_is_free(p)) { + assert(!atom_is_free(p)); + snprintf(buf, buf_size, "", atom); + } else if (p != NULL) { + JSString *str = p; + if (str->is_wide_char) { + /* encode surrogates correctly */ + utf8_encode_buf16(buf, buf_size, str->u.str16, str->len); + } else { + /* special case ASCII strings */ + int i, c = 0; + for(i = 0; i < str->len; i++) { + c |= str->u.str8[i]; + } + if (c < 0x80) + return (const char *)str->u.str8; + utf8_encode_buf8(buf, buf_size, str->u.str8, str->len); + } + } + } + return buf; +} + +static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom) +{ + return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom); +} + +static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + + if (__JS_AtomIsTaggedInt(atom)) { + size_t len = u32toa(buf, __JS_AtomToUInt32(atom)); + return js_new_string8_len(ctx, buf, len); + } else { + JSRuntime *rt = ctx->rt; + JSAtomStruct *p; + assert(atom < rt->atom_size); + p = rt->atom_array[atom]; + if (p->atom_type == JS_ATOM_TYPE_STRING) { + goto ret_string; + } else if (force_string) { + if (p->len == 0 && p->is_wide_char != 0) { + /* no description string */ + p = rt->atom_array[JS_ATOM_empty_string]; + } + ret_string: + return js_dup(JS_MKPTR(JS_TAG_STRING, p)); + } else { + return js_dup(JS_MKPTR(JS_TAG_SYMBOL, p)); + } + } +} + +JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom) +{ + return __JS_AtomToValue(ctx, atom, FALSE); +} + +JSValue JS_AtomToString(JSContext *ctx, JSAtom atom) +{ + return __JS_AtomToValue(ctx, atom, TRUE); +} + +/* return TRUE if the atom is an array index (i.e. 0 <= index <= + 2^32-2 and return its value */ +static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom) +{ + if (__JS_AtomIsTaggedInt(atom)) { + *pval = __JS_AtomToUInt32(atom); + return TRUE; + } else { + JSRuntime *rt = ctx->rt; + JSAtomStruct *p; + uint32_t val; + + assert(atom < rt->atom_size); + p = rt->atom_array[atom]; + if (p->atom_type == JS_ATOM_TYPE_STRING && + is_num_string(&val, p) && val != -1) { + *pval = val; + return TRUE; + } else { + *pval = 0; + return FALSE; + } + } +} + +/* This test must be fast if atom is not a numeric index (e.g. a + method name). Return JS_UNDEFINED if not a numeric + index. JS_EXCEPTION can also be returned. */ +static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) +{ + JSRuntime *rt = ctx->rt; + JSAtomStruct *p1; + JSString *p; + int c, len, ret; + JSValue num, str; + + if (__JS_AtomIsTaggedInt(atom)) + return js_int32(__JS_AtomToUInt32(atom)); + assert(atom < rt->atom_size); + p1 = rt->atom_array[atom]; + if (p1->atom_type != JS_ATOM_TYPE_STRING) + return JS_UNDEFINED; + p = p1; + len = p->len; + if (p->is_wide_char) { + const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len; + if (r >= r_end) + return JS_UNDEFINED; + c = *r; + if (c == '-') { + if (r >= r_end) + return JS_UNDEFINED; + r++; + c = *r; + /* -0 case is specific */ + if (c == '0' && len == 2) + goto minus_zero; + } + /* XXX: should test NaN, but the tests do not check it */ + if (!is_num(c)) { + /* XXX: String should be normalized, therefore 8-bit only */ + const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' }; + if (!(c =='I' && (r_end - r) == 8 && + !memcmp(r + 1, nfinity16, sizeof(nfinity16)))) + return JS_UNDEFINED; + } + } else { + const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len; + if (r >= r_end) + return JS_UNDEFINED; + c = *r; + if (c == '-') { + if (r >= r_end) + return JS_UNDEFINED; + r++; + c = *r; + /* -0 case is specific */ + if (c == '0' && len == 2) { + minus_zero: + return js_float64(-0.0); + } + } + if (!is_num(c)) { + if (!(c =='I' && (r_end - r) == 8 && + !memcmp(r + 1, "nfinity", 7))) + return JS_UNDEFINED; + } + } + /* this is ECMA CanonicalNumericIndexString primitive */ + num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p)); + if (JS_IsException(num)) + return num; + str = JS_ToString(ctx, num); + if (JS_IsException(str)) { + JS_FreeValue(ctx, num); + return str; + } + ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str)); + JS_FreeValue(ctx, str); + if (ret == 0) { + return num; + } else { + JS_FreeValue(ctx, num); + return JS_UNDEFINED; + } +} + +/* return -1 if exception or TRUE/FALSE */ +static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom) +{ + JSValue num; + num = JS_AtomIsNumericIndex1(ctx, atom); + if (likely(JS_IsUndefined(num))) + return FALSE; + if (JS_IsException(num)) + return -1; + JS_FreeValue(ctx, num); + return TRUE; +} + +void JS_FreeAtom(JSContext *ctx, JSAtom v) +{ + if (!__JS_AtomIsConst(v)) + __JS_FreeAtom(ctx->rt, v); +} + +void JS_FreeAtomRT(JSRuntime *rt, JSAtom v) +{ + if (!__JS_AtomIsConst(v)) + __JS_FreeAtom(rt, v); +} + +/* return TRUE if 'v' is a symbol with a string description */ +static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) +{ + JSRuntime *rt; + JSAtomStruct *p; + + rt = ctx->rt; + if (__JS_AtomIsTaggedInt(v)) + return FALSE; + p = rt->atom_array[v]; + return (((p->atom_type == JS_ATOM_TYPE_SYMBOL && + p->hash == JS_ATOM_HASH_SYMBOL) || + p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) && + !(p->len == 0 && p->is_wide_char != 0)); +} + +static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + const char *p; + int i; + + /* XXX: should handle embedded null characters */ + /* XXX: should move encoding code to JS_AtomGetStr */ + p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom); + for (i = 0; p[i]; i++) { + int c = (unsigned char)p[i]; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0))) + break; + } + if (i > 0 && p[i] == '\0') { + printf("%s", p); + } else { + putchar('"'); + printf("%.*s", i, p); + for (; p[i]; i++) { + int c = (unsigned char)p[i]; + if (c == '\"' || c == '\\') { + putchar('\\'); + putchar(c); + } else if (c >= ' ' && c <= 126) { + putchar(c); + } else if (c == '\n') { + putchar('\\'); + putchar('n'); + } else { + printf("\\u%04x", c); + } + } + putchar('\"'); + } +} + +/* free with JS_FreeCString() */ +const char *JS_AtomToCString(JSContext *ctx, JSAtom atom) +{ + JSValue str; + const char *cstr; + + str = JS_AtomToString(ctx, atom); + if (JS_IsException(str)) + return NULL; + cstr = JS_ToCString(ctx, str); + JS_FreeValue(ctx, str); + return cstr; +} + +/* return a string atom containing name concatenated with str1 */ +/* `str1` may be pure ASCII or UTF-8 encoded */ +// TODO(chqrlie): use string concatenation instead of UTF-8 conversion +static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1) +{ + JSValue str; + JSAtom atom; + const char *cstr; + char *cstr2; + size_t len, len1; + + str = JS_AtomToString(ctx, name); + if (JS_IsException(str)) + return JS_ATOM_NULL; + cstr = JS_ToCStringLen(ctx, &len, str); + if (!cstr) + goto fail; + len1 = strlen(str1); + cstr2 = js_malloc(ctx, len + len1 + 1); + if (!cstr2) + goto fail; + memcpy(cstr2, cstr, len); + memcpy(cstr2 + len, str1, len1); + cstr2[len + len1] = '\0'; + atom = JS_NewAtomLen(ctx, cstr2, len + len1); + js_free(ctx, cstr2); + JS_FreeCString(ctx, cstr); + JS_FreeValue(ctx, str); + return atom; + fail: + JS_FreeCString(ctx, cstr); + JS_FreeValue(ctx, str); + return JS_ATOM_NULL; +} + +static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n) +{ + char buf[16]; + u32toa(buf, n); + return js_atom_concat_str(ctx, name, buf); +} + +static inline BOOL JS_IsEmptyString(JSValue v) +{ + return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0; +} + +/* JSClass support */ + +/* a new class ID is allocated if *pclass_id != 0 */ +JSClassID JS_NewClassID(JSRuntime *rt, JSClassID *pclass_id) +{ + JSClassID class_id = *pclass_id; + if (class_id == 0) { + class_id = rt->js_class_id_alloc++; + *pclass_id = class_id; + } + return class_id; +} + +JSClassID JS_GetClassID(JSValue v) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) + return JS_INVALID_CLASS_ID; + p = JS_VALUE_GET_OBJ(v); + return p->class_id; +} + +BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id) +{ + return (class_id < rt->class_count && + rt->class_array[class_id].class_id != 0); +} + +/* create a new object internal class. Return -1 if error, 0 if + OK. The finalizer can be NULL if none is needed. */ +static int JS_NewClass1(JSRuntime *rt, JSClassID class_id, + const JSClassDef *class_def, JSAtom name) +{ + int new_size, i; + JSClass *cl, *new_class_array; + struct list_head *el; + + if (class_id >= (1 << 16)) + return -1; + if (class_id < rt->class_count && + rt->class_array[class_id].class_id != 0) + return -1; + + if (class_id >= rt->class_count) { + new_size = max_int(JS_CLASS_INIT_COUNT, + max_int(class_id + 1, rt->class_count * 3 / 2)); + + /* reallocate the context class prototype array, if any */ + list_for_each(el, &rt->context_list) { + JSContext *ctx = list_entry(el, JSContext, link); + JSValue *new_tab; + new_tab = js_realloc_rt(rt, ctx->class_proto, + sizeof(ctx->class_proto[0]) * new_size); + if (!new_tab) + return -1; + for(i = rt->class_count; i < new_size; i++) + new_tab[i] = JS_NULL; + ctx->class_proto = new_tab; + } + /* reallocate the class array */ + new_class_array = js_realloc_rt(rt, rt->class_array, + sizeof(JSClass) * new_size); + if (!new_class_array) + return -1; + memset(new_class_array + rt->class_count, 0, + (new_size - rt->class_count) * sizeof(JSClass)); + rt->class_array = new_class_array; + rt->class_count = new_size; + } + cl = &rt->class_array[class_id]; + cl->class_id = class_id; + cl->class_name = JS_DupAtomRT(rt, name); + cl->finalizer = class_def->finalizer; + cl->gc_mark = class_def->gc_mark; + cl->call = class_def->call; + cl->exotic = class_def->exotic; + return 0; +} + +int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) +{ + int ret, len; + JSAtom name; + + // XXX: class_def->class_name must be raw 8-bit contents. No UTF-8 encoded strings + len = strlen(class_def->class_name); + name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); + if (name == JS_ATOM_NULL) { + name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); + if (name == JS_ATOM_NULL) + return -1; + } + ret = JS_NewClass1(rt, class_id, class_def, name); + JS_FreeAtomRT(rt, name); + return ret; +} + +// XXX: `buf` contains raw 8-bit data, no UTF-8 decoding is performed +// XXX: no special case for len == 0 +static JSValue js_new_string8_len(JSContext *ctx, const char *buf, int len) +{ + JSString *str; + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + memcpy(str->u.str8, buf, len); + str->u.str8[len] = '\0'; + return JS_MKPTR(JS_TAG_STRING, str); +} + +// XXX: `buf` contains raw 8-bit data, no UTF-8 decoding is performed +// XXX: no special case for the empty string +static inline JSValue js_new_string8(JSContext *ctx, const char *str) +{ + return js_new_string8_len(ctx, str, strlen(str)); +} + +static JSValue js_new_string16_len(JSContext *ctx, const uint16_t *buf, int len) +{ + JSString *str; + str = js_alloc_string(ctx, len, 1); + if (!str) + return JS_EXCEPTION; + memcpy(str->u.str16, buf, len * 2); + return JS_MKPTR(JS_TAG_STRING, str); +} + +static JSValue js_new_string_char(JSContext *ctx, uint16_t c) +{ + if (c < 0x100) { + char ch8 = c; + return js_new_string8_len(ctx, &ch8, 1); + } else { + uint16_t ch16 = c; + return js_new_string16_len(ctx, &ch16, 1); + } +} + +static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end) +{ + int len = end - start; + if (start == 0 && end == p->len) { + return js_dup(JS_MKPTR(JS_TAG_STRING, p)); + } + if (len <= 0) { + return JS_AtomToString(ctx, JS_ATOM_empty_string); + } + if (p->is_wide_char) { + JSString *str; + int i; + uint16_t c = 0; + for (i = start; i < end; i++) { + c |= p->u.str16[i]; + } + if (c > 0xFF) + return js_new_string16_len(ctx, p->u.str16 + start, len); + + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + for (i = 0; i < len; i++) { + str->u.str8[i] = p->u.str16[start + i]; + } + str->u.str8[len] = '\0'; + return JS_MKPTR(JS_TAG_STRING, str); + } else { + return js_new_string8_len(ctx, (const char *)(p->u.str8 + start), len); + } +} + +typedef struct StringBuffer { + JSContext *ctx; + JSString *str; + int len; + int size; + int is_wide_char; + int error_status; +} StringBuffer; + +/* It is valid to call string_buffer_end() and all string_buffer functions even + if string_buffer_init() or another string_buffer function returns an error. + If the error_status is set, string_buffer_end() returns JS_EXCEPTION. + */ +static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size, + int is_wide) +{ + s->ctx = ctx; + s->size = size; + s->len = 0; + s->is_wide_char = is_wide; + s->error_status = 0; + s->str = js_alloc_string(ctx, size, is_wide); + if (unlikely(!s->str)) { + s->size = 0; + return s->error_status = -1; + } +#ifdef DUMP_LEAKS + /* the StringBuffer may reallocate the JSString, only link it at the end */ + list_del(&s->str->link); +#endif + return 0; +} + +static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size) +{ + return string_buffer_init2(ctx, s, size, 0); +} + +static void string_buffer_free(StringBuffer *s) +{ + js_free(s->ctx, s->str); + s->str = NULL; +} + +static int string_buffer_set_error(StringBuffer *s) +{ + js_free(s->ctx, s->str); + s->str = NULL; + s->size = 0; + s->len = 0; + return s->error_status = -1; +} + +static no_inline int string_buffer_widen(StringBuffer *s, int size) +{ + JSString *str; + size_t slack; + int i; + + if (s->error_status) + return -1; + + str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack); + if (!str) + return string_buffer_set_error(s); + size += slack >> 1; + for(i = s->len; i-- > 0;) { + str->u.str16[i] = str->u.str8[i]; + } + s->is_wide_char = 1; + s->size = size; + s->str = str; + return 0; +} + +static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c) +{ + JSString *new_str; + int new_size; + size_t new_size_bytes, slack; + + if (s->error_status) + return -1; + + if (new_len > JS_STRING_LEN_MAX) { + JS_ThrowRangeError(s->ctx, "invalid string length"); + return string_buffer_set_error(s); + } + new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX); + if (!s->is_wide_char && c >= 0x100) { + return string_buffer_widen(s, new_size); + } + new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char; + new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack); + if (!new_str) + return string_buffer_set_error(s); + new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX); + s->size = new_size; + s->str = new_str; + return 0; +} + +static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c) +{ + if (unlikely(s->len >= s->size)) { + if (string_buffer_realloc(s, s->len + 1, c)) + return -1; + } + if (s->is_wide_char) { + s->str->u.str16[s->len++] = c; + } else if (c < 0x100) { + s->str->u.str8[s->len++] = c; + } else { + if (string_buffer_widen(s, s->size)) + return -1; + s->str->u.str16[s->len++] = c; + } + return 0; +} + +/* 0 <= c <= 0xff */ +static int string_buffer_putc8(StringBuffer *s, uint32_t c) +{ + if (unlikely(s->len >= s->size)) { + if (string_buffer_realloc(s, s->len + 1, c)) + return -1; + } + if (s->is_wide_char) { + s->str->u.str16[s->len++] = c; + } else { + s->str->u.str8[s->len++] = c; + } + return 0; +} + +/* 0 <= c <= 0xffff */ +static int string_buffer_putc16(StringBuffer *s, uint32_t c) +{ + if (likely(s->len < s->size)) { + if (s->is_wide_char) { + s->str->u.str16[s->len++] = c; + return 0; + } else if (c < 0x100) { + s->str->u.str8[s->len++] = c; + return 0; + } + } + return string_buffer_putc_slow(s, c); +} + +/* 0 <= c <= 0x10ffff */ +static int string_buffer_putc(StringBuffer *s, uint32_t c) +{ + if (unlikely(c >= 0x10000)) { + /* surrogate pair */ + if (string_buffer_putc16(s, get_hi_surrogate(c))) + return -1; + c = get_lo_surrogate(c); + } + return string_buffer_putc16(s, c); +} + +static int string_getc(const JSString *p, int *pidx) +{ + int idx, c, c1; + idx = *pidx; + if (p->is_wide_char) { + c = p->u.str16[idx++]; + if (is_hi_surrogate(c) && idx < p->len) { + c1 = p->u.str16[idx]; + if (is_lo_surrogate(c1)) { + c = from_surrogate(c, c1); + idx++; + } + } + } else { + c = p->u.str8[idx++]; + } + *pidx = idx; + return c; +} + +static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len) +{ + int i; + + if (s->len + len > s->size) { + if (string_buffer_realloc(s, s->len + len, 0)) + return -1; + } + if (s->is_wide_char) { + for (i = 0; i < len; i++) { + s->str->u.str16[s->len + i] = p[i]; + } + s->len += len; + } else { + memcpy(&s->str->u.str8[s->len], p, len); + s->len += len; + } + return 0; +} + +static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len) +{ + int c = 0, i; + + for (i = 0; i < len; i++) { + c |= p[i]; + } + if (s->len + len > s->size) { + if (string_buffer_realloc(s, s->len + len, c)) + return -1; + } else if (!s->is_wide_char && c >= 0x100) { + if (string_buffer_widen(s, s->size)) + return -1; + } + if (s->is_wide_char) { + memcpy(&s->str->u.str16[s->len], p, len << 1); + s->len += len; + } else { + for (i = 0; i < len; i++) { + s->str->u.str8[s->len + i] = p[i]; + } + s->len += len; + } + return 0; +} + +/* appending an ASCII string */ +static int string_buffer_puts8(StringBuffer *s, const char *str) +{ + return string_buffer_write8(s, (const uint8_t *)str, strlen(str)); +} + +static int string_buffer_concat(StringBuffer *s, const JSString *p, + uint32_t from, uint32_t to) +{ + if (to <= from) + return 0; + if (p->is_wide_char) + return string_buffer_write16(s, p->u.str16 + from, to - from); + else + return string_buffer_write8(s, p->u.str8 + from, to - from); +} + +static int string_buffer_concat_value(StringBuffer *s, JSValue v) +{ + JSString *p; + JSValue v1; + int res; + + if (s->error_status) { + /* prevent exception overload */ + return -1; + } + if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { + v1 = JS_ToString(s->ctx, v); + if (JS_IsException(v1)) + return string_buffer_set_error(s); + p = JS_VALUE_GET_STRING(v1); + res = string_buffer_concat(s, p, 0, p->len); + JS_FreeValue(s->ctx, v1); + return res; + } + p = JS_VALUE_GET_STRING(v); + return string_buffer_concat(s, p, 0, p->len); +} + +static int string_buffer_concat_value_free(StringBuffer *s, JSValue v) +{ + JSString *p; + int res; + + if (s->error_status) { + /* prevent exception overload */ + JS_FreeValue(s->ctx, v); + return -1; + } + if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) { + v = JS_ToStringFree(s->ctx, v); + if (JS_IsException(v)) + return string_buffer_set_error(s); + } + p = JS_VALUE_GET_STRING(v); + res = string_buffer_concat(s, p, 0, p->len); + JS_FreeValue(s->ctx, v); + return res; +} + +static int string_buffer_fill(StringBuffer *s, int c, int count) +{ + /* XXX: optimize */ + if (s->len + count > s->size) { + if (string_buffer_realloc(s, s->len + count, c)) + return -1; + } + while (count-- > 0) { + if (string_buffer_putc16(s, c)) + return -1; + } + return 0; +} + +static JSValue string_buffer_end(StringBuffer *s) +{ + JSString *str; + str = s->str; + if (s->error_status) + return JS_EXCEPTION; + if (s->len == 0) { + js_free(s->ctx, str); + s->str = NULL; + return JS_AtomToString(s->ctx, JS_ATOM_empty_string); + } + if (s->len < s->size) { + /* smaller size so js_realloc should not fail, but OK if it does */ + /* XXX: should add some slack to avoid unnecessary calls */ + /* XXX: might need to use malloc+free to ensure smaller size */ + str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) + + (s->len << s->is_wide_char) + 1 - s->is_wide_char); + if (str == NULL) + str = s->str; + s->str = str; + } + if (!s->is_wide_char) + str->u.str8[s->len] = 0; +#ifdef DUMP_LEAKS + list_add_tail(&str->link, &s->ctx->rt->string_list); +#endif + str->is_wide_char = s->is_wide_char; + str->len = s->len; + s->str = NULL; + return JS_MKPTR(JS_TAG_STRING, str); +} + +/* create a string from a UTF-8 buffer */ +JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) +{ + JSString *str; + size_t len; + int kind; + + if (buf_len <= 0) { + return JS_AtomToString(ctx, JS_ATOM_empty_string); + } + /* Compute string kind and length: 7-bit, 8-bit, 16-bit, 16-bit UTF-16 */ + kind = utf8_scan(buf, buf_len, &len); + if (len > JS_STRING_LEN_MAX) + return JS_ThrowRangeError(ctx, "invalid string length"); + + switch (kind) { + case UTF8_PLAIN_ASCII: + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + memcpy(str->u.str8, buf, len); + str->u.str8[len] = '\0'; + break; + case UTF8_NON_ASCII: + /* buf contains non-ASCII code-points, but limited to 8-bit values */ + str = js_alloc_string(ctx, len, 0); + if (!str) + return JS_EXCEPTION; + utf8_decode_buf8(str->u.str8, len + 1, buf, buf_len); + break; + default: + // This causes a potential problem in JS_ThrowError if message is invalid + //if (kind & UTF8_HAS_ERRORS) + // return JS_ThrowRangeError(ctx, "invalid UTF-8 sequence"); + str = js_alloc_string(ctx, len, 1); + if (!str) + return JS_EXCEPTION; + utf8_decode_buf16(str->u.str16, len, buf, buf_len); + break; + } + return JS_MKPTR(JS_TAG_STRING, str); +} + +static JSValue JS_ConcatString3(JSContext *ctx, const char *str1, + JSValue str2, const char *str3) +{ + StringBuffer b_s, *b = &b_s; + int len1, len3; + JSString *p; + + if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) { + str2 = JS_ToStringFree(ctx, str2); + if (JS_IsException(str2)) + goto fail; + } + p = JS_VALUE_GET_STRING(str2); + len1 = strlen(str1); + len3 = strlen(str3); + + if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char)) + goto fail; + + string_buffer_write8(b, (const uint8_t *)str1, len1); + string_buffer_concat(b, p, 0, p->len); + string_buffer_write8(b, (const uint8_t *)str3, len3); + + JS_FreeValue(ctx, str2); + return string_buffer_end(b); + + fail: + JS_FreeValue(ctx, str2); + return JS_EXCEPTION; +} + +/* `str` may be pure ASCII or UTF-8 encoded */ +JSValue JS_NewAtomString(JSContext *ctx, const char *str) +{ + JSAtom atom = JS_NewAtom(ctx, str); + if (atom == JS_ATOM_NULL) + return JS_EXCEPTION; + JSValue val = JS_AtomToString(ctx, atom); + JS_FreeAtom(ctx, atom); + return val; +} + +/* return (NULL, 0) if exception. */ +/* return pointer into a JSString with a live ref_count */ +/* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */ +const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValue val1, BOOL cesu8) +{ + JSValue val; + JSString *str, *str_new; + int pos, len, c, c1; + JSObject *p; + uint8_t *q; + + if (JS_VALUE_GET_TAG(val1) == JS_TAG_STRING) { + val = js_dup(val1); + goto go; + } + + val = JS_ToString(ctx, val1); + if (!JS_IsException(val)) + goto go; + + // Stringification can fail when there is an exception pending, + // e.g. a stack overflow InternalError. Special-case exception + // objects to make debugging easier, look up the .message property + // and stringify that. + if (JS_VALUE_GET_TAG(val1) != JS_TAG_OBJECT) + goto fail; + + p = JS_VALUE_GET_OBJ(val1); + if (p->class_id != JS_CLASS_ERROR) + goto fail; + + val = JS_GetProperty(ctx, val1, JS_ATOM_message); + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) { + JS_FreeValue(ctx, val); + goto fail; + } + +go: + str = JS_VALUE_GET_STRING(val); + len = str->len; + if (!str->is_wide_char) { + const uint8_t *src = str->u.str8; + int count; + + /* count the number of non-ASCII characters */ + /* Scanning the whole string is required for ASCII strings, + and computing the number of non-ASCII bytes is less expensive + than testing each byte, hence this method is faster for ASCII + strings, which is the most common case. + */ + count = 0; + for (pos = 0; pos < len; pos++) { + count += src[pos] >> 7; + } + if (count == 0) { + if (plen) + *plen = len; + return (const char *)src; + } + str_new = js_alloc_string(ctx, len + count, 0); + if (!str_new) + goto fail; + q = str_new->u.str8; + for (pos = 0; pos < len; pos++) { + c = src[pos]; + if (c < 0x80) { + *q++ = c; + } else { + *q++ = (c >> 6) | 0xc0; + *q++ = (c & 0x3f) | 0x80; + } + } + } else { + const uint16_t *src = str->u.str16; + /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may + produce 4 bytes but use 2 code points. + */ + str_new = js_alloc_string(ctx, len * 3, 0); + if (!str_new) + goto fail; + q = str_new->u.str8; + pos = 0; + while (pos < len) { + c = src[pos++]; + if (c < 0x80) { + *q++ = c; + } else { + if (is_hi_surrogate(c)) { + if (pos < len && !cesu8) { + c1 = src[pos]; + if (is_lo_surrogate(c1)) { + pos++; + c = from_surrogate(c, c1); + } else { + /* Keep unmatched surrogate code points */ + /* c = 0xfffd; */ /* error */ + } + } else { + /* Keep unmatched surrogate code points */ + /* c = 0xfffd; */ /* error */ + } + } + q += utf8_encode(q, c); + } + } + } + + *q = '\0'; + str_new->len = q - str_new->u.str8; + JS_FreeValue(ctx, val); + if (plen) + *plen = str_new->len; + return (const char *)str_new->u.str8; + fail: + if (plen) + *plen = 0; + return NULL; +} + +void JS_FreeCString(JSContext *ctx, const char *ptr) +{ + JSString *p; + if (!ptr) + return; + /* purposely removing constness */ + p = container_of(ptr, JSString, u); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); +} + +static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len) +{ + int c, i; + for(i = 0; i < len; i++) { + c = src1[i] - src2[i]; + if (c != 0) + return c; + } + return 0; +} + +static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len) +{ + int c, i; + for(i = 0; i < len; i++) { + c = src1[i] - src2[i]; + if (c != 0) + return c; + } + return 0; +} + +static int js_string_memcmp(const JSString *p1, const JSString *p2, int len) +{ + int res; + + if (likely(!p1->is_wide_char)) { + if (likely(!p2->is_wide_char)) + res = memcmp(p1->u.str8, p2->u.str8, len); + else + res = -memcmp16_8(p2->u.str16, p1->u.str8, len); + } else { + if (!p2->is_wide_char) + res = memcmp16_8(p1->u.str16, p2->u.str8, len); + else + res = memcmp16(p1->u.str16, p2->u.str16, len); + } + return res; +} + +/* return < 0, 0 or > 0 */ +static int js_string_compare(JSContext *ctx, + const JSString *p1, const JSString *p2) +{ + int res, len; + len = min_int(p1->len, p2->len); + res = js_string_memcmp(p1, p2, len); + if (res == 0) + res = compare_u32(p1->len, p2->len); + return res; +} + +static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len) +{ + if (p->is_wide_char) { + memcpy(dst, p->u.str16 + offset, len * 2); + } else { + const uint8_t *src1 = p->u.str8 + offset; + int i; + + for(i = 0; i < len; i++) + dst[i] = src1[i]; + } +} + +static JSValue JS_ConcatString1(JSContext *ctx, + const JSString *p1, const JSString *p2) +{ + JSString *p; + uint32_t len; + int is_wide_char; + + len = p1->len + p2->len; + if (len > JS_STRING_LEN_MAX) + return JS_ThrowRangeError(ctx, "invalid string length"); + is_wide_char = p1->is_wide_char | p2->is_wide_char; + p = js_alloc_string(ctx, len, is_wide_char); + if (!p) + return JS_EXCEPTION; + if (!is_wide_char) { + memcpy(p->u.str8, p1->u.str8, p1->len); + memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len); + p->u.str8[len] = '\0'; + } else { + copy_str16(p->u.str16, p1, 0, p1->len); + copy_str16(p->u.str16 + p1->len, p2, 0, p2->len); + } + return JS_MKPTR(JS_TAG_STRING, p); +} + +/* op1 and op2 are converted to strings. For convience, op1 or op2 = + JS_EXCEPTION are accepted and return JS_EXCEPTION. */ +static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) +{ + JSValue ret; + JSString *p1, *p2; + + if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) { + op1 = JS_ToStringFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + return JS_EXCEPTION; + } + } + if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) { + op2 = JS_ToStringFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } + } + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + + /* XXX: could also check if p1 is empty */ + if (p2->len == 0) { + goto ret_op1; + } + if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char + && js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) { + /* Concatenate in place in available space at the end of p1 */ + if (p1->is_wide_char) { + memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); + p1->len += p2->len; + } else { + memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len); + p1->len += p2->len; + p1->u.str8[p1->len] = '\0'; + } + ret_op1: + JS_FreeValue(ctx, op2); + return op1; + } + ret = JS_ConcatString1(ctx, p1, p2); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return ret; +} + +/* Shape support */ + +static inline size_t get_shape_size(size_t hash_size, size_t prop_size) +{ + return hash_size * sizeof(uint32_t) + sizeof(JSShape) + + prop_size * sizeof(JSShapeProperty); +} + +static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size) +{ + return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size); +} + +static inline uint32_t *prop_hash_end(JSShape *sh) +{ + return (uint32_t *)sh; +} + +static inline void *get_alloc_from_shape(JSShape *sh) +{ + return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1); +} + +static inline JSShapeProperty *get_shape_prop(JSShape *sh) +{ + return sh->prop; +} + +static int init_shape_hash(JSRuntime *rt) +{ + rt->shape_hash_bits = 4; /* 16 shapes */ + rt->shape_hash_size = 1 << rt->shape_hash_bits; + rt->shape_hash_count = 0; + rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * + rt->shape_hash_size); + if (!rt->shape_hash) + return -1; + return 0; +} + +/* same magic hash multiplier as the Linux kernel */ +static uint32_t shape_hash(uint32_t h, uint32_t val) +{ + return (h + val) * 0x9e370001; +} + +/* truncate the shape hash to 'hash_bits' bits */ +static uint32_t get_shape_hash(uint32_t h, int hash_bits) +{ + return h >> (32 - hash_bits); +} + +static uint32_t shape_initial_hash(JSObject *proto) +{ + uint32_t h; + h = shape_hash(1, (uintptr_t)proto); + if (sizeof(proto) > 4) + h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32); + return h; +} + +static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits) +{ + int new_shape_hash_size, i; + uint32_t h; + JSShape **new_shape_hash, *sh, *sh_next; + + new_shape_hash_size = 1 << new_shape_hash_bits; + new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) * + new_shape_hash_size); + if (!new_shape_hash) + return -1; + for(i = 0; i < rt->shape_hash_size; i++) { + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) { + sh_next = sh->shape_hash_next; + h = get_shape_hash(sh->hash, new_shape_hash_bits); + sh->shape_hash_next = new_shape_hash[h]; + new_shape_hash[h] = sh; + } + } + js_free_rt(rt, rt->shape_hash); + rt->shape_hash_bits = new_shape_hash_bits; + rt->shape_hash_size = new_shape_hash_size; + rt->shape_hash = new_shape_hash; + return 0; +} + +static void js_shape_hash_link(JSRuntime *rt, JSShape *sh) +{ + uint32_t h; + h = get_shape_hash(sh->hash, rt->shape_hash_bits); + sh->shape_hash_next = rt->shape_hash[h]; + rt->shape_hash[h] = sh; + rt->shape_hash_count++; +} + +static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh) +{ + uint32_t h; + JSShape **psh; + + h = get_shape_hash(sh->hash, rt->shape_hash_bits); + psh = &rt->shape_hash[h]; + while (*psh != sh) + psh = &(*psh)->shape_hash_next; + *psh = sh->shape_hash_next; + rt->shape_hash_count--; +} + +/* create a new empty shape with prototype 'proto' */ +static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto, + int hash_size, int prop_size) +{ + JSRuntime *rt = ctx->rt; + void *sh_alloc; + JSShape *sh; + + /* resize the shape hash table if necessary */ + if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) { + resize_shape_hash(rt, rt->shape_hash_bits + 1); + } + + sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size)); + if (!sh_alloc) + return NULL; + sh = get_shape_from_alloc(sh_alloc, hash_size); + sh->header.ref_count = 1; + add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); + if (proto) + js_dup(JS_MKPTR(JS_TAG_OBJECT, proto)); + sh->proto = proto; + memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) * + hash_size); + sh->prop_hash_mask = hash_size - 1; + sh->prop_size = prop_size; + sh->prop_count = 0; + sh->deleted_prop_count = 0; + + /* insert in the hash table */ + sh->hash = shape_initial_hash(proto); + sh->is_hashed = TRUE; + sh->has_small_array_index = FALSE; + js_shape_hash_link(ctx->rt, sh); + return sh; +} + +static JSShape *js_new_shape(JSContext *ctx, JSObject *proto) +{ + return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE, + JS_PROP_INITIAL_SIZE); +} + +/* The shape is cloned. The new shape is not inserted in the shape + hash table */ +static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1) +{ + JSShape *sh; + void *sh_alloc, *sh_alloc1; + size_t size; + JSShapeProperty *pr; + uint32_t i, hash_size; + + hash_size = sh1->prop_hash_mask + 1; + size = get_shape_size(hash_size, sh1->prop_size); + sh_alloc = js_malloc(ctx, size); + if (!sh_alloc) + return NULL; + sh_alloc1 = get_alloc_from_shape(sh1); + memcpy(sh_alloc, sh_alloc1, size); + sh = get_shape_from_alloc(sh_alloc, hash_size); + sh->header.ref_count = 1; + add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE); + sh->is_hashed = FALSE; + if (sh->proto) { + js_dup(JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + } + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) { + JS_DupAtom(ctx, pr->atom); + } + return sh; +} + +static JSShape *js_dup_shape(JSShape *sh) +{ + sh->header.ref_count++; + return sh; +} + +static void js_free_shape0(JSRuntime *rt, JSShape *sh) +{ + uint32_t i; + JSShapeProperty *pr; + + assert(sh->header.ref_count == 0); + if (sh->is_hashed) + js_shape_hash_unlink(rt, sh); + if (sh->proto != NULL) { + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + } + pr = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JS_FreeAtomRT(rt, pr->atom); + pr++; + } + remove_gc_object(&sh->header); + js_free_rt(rt, get_alloc_from_shape(sh)); +} + +static void js_free_shape(JSRuntime *rt, JSShape *sh) +{ + if (unlikely(--sh->header.ref_count <= 0)) { + js_free_shape0(rt, sh); + } +} + +static void js_free_shape_null(JSRuntime *rt, JSShape *sh) +{ + if (sh) + js_free_shape(rt, sh); +} + +/* make space to hold at least 'count' properties */ +static no_inline int resize_properties(JSContext *ctx, JSShape **psh, + JSObject *p, uint32_t count) +{ + JSShape *sh; + uint32_t new_size, new_hash_size, new_hash_mask, i; + JSShapeProperty *pr; + void *sh_alloc; + intptr_t h; + + sh = *psh; + new_size = max_int(count, sh->prop_size * 3 / 2); + /* Reallocate prop array first to avoid crash or size inconsistency + in case of memory allocation failure */ + if (p) { + JSProperty *new_prop; + new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); + if (unlikely(!new_prop)) + return -1; + p->prop = new_prop; + } + new_hash_size = sh->prop_hash_mask + 1; + while (new_hash_size < new_size) + new_hash_size = 2 * new_hash_size; + if (new_hash_size != (sh->prop_hash_mask + 1)) { + JSShape *old_sh; + /* resize the hash table and the properties */ + old_sh = sh; + sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); + if (!sh_alloc) + return -1; + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_del(&old_sh->header.link); + /* copy all the fields and the properties */ + memcpy(sh, old_sh, + sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + new_hash_mask = new_hash_size - 1; + sh->prop_hash_mask = new_hash_mask; + memset(prop_hash_end(sh) - new_hash_size, 0, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); + for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) { + if (pr->atom != JS_ATOM_NULL) { + h = ((uintptr_t)pr->atom & new_hash_mask); + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = i + 1; + } + } + js_free(ctx, get_alloc_from_shape(old_sh)); + } else { + /* only resize the properties */ + list_del(&sh->header.link); + sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh), + get_shape_size(new_hash_size, new_size)); + if (unlikely(!sh_alloc)) { + /* insert again in the GC list */ + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + return -1; + } + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + } + *psh = sh; + sh->prop_size = new_size; + return 0; +} + +/* remove the deleted properties. */ +static int compact_properties(JSContext *ctx, JSObject *p) +{ + JSShape *sh, *old_sh; + void *sh_alloc; + intptr_t h; + uint32_t new_hash_size, i, j, new_hash_mask, new_size; + JSShapeProperty *old_pr, *pr; + JSProperty *prop, *new_prop; + + sh = p->shape; + assert(!sh->is_hashed); + + new_size = max_int(JS_PROP_INITIAL_SIZE, + sh->prop_count - sh->deleted_prop_count); + assert(new_size <= sh->prop_size); + + new_hash_size = sh->prop_hash_mask + 1; + while ((new_hash_size / 2) >= new_size) + new_hash_size = new_hash_size / 2; + new_hash_mask = new_hash_size - 1; + + /* resize the hash table and the properties */ + old_sh = sh; + sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size)); + if (!sh_alloc) + return -1; + sh = get_shape_from_alloc(sh_alloc, new_hash_size); + list_del(&old_sh->header.link); + memcpy(sh, old_sh, sizeof(JSShape)); + list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list); + + memset(prop_hash_end(sh) - new_hash_size, 0, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); + + j = 0; + old_pr = old_sh->prop; + pr = sh->prop; + prop = p->prop; + for(i = 0; i < sh->prop_count; i++) { + if (old_pr->atom != JS_ATOM_NULL) { + pr->atom = old_pr->atom; + pr->flags = old_pr->flags; + h = ((uintptr_t)old_pr->atom & new_hash_mask); + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = j + 1; + prop[j] = prop[i]; + j++; + pr++; + } + old_pr++; + } + assert(j == (sh->prop_count - sh->deleted_prop_count)); + sh->prop_hash_mask = new_hash_mask; + sh->prop_size = new_size; + sh->deleted_prop_count = 0; + sh->prop_count = j; + + p->shape = sh; + js_free(ctx, get_alloc_from_shape(old_sh)); + + /* reduce the size of the object properties */ + new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size); + if (new_prop) + p->prop = new_prop; + return 0; +} + +static int add_shape_property(JSContext *ctx, JSShape **psh, + JSObject *p, JSAtom atom, int prop_flags) +{ + JSRuntime *rt = ctx->rt; + JSShape *sh = *psh; + JSShapeProperty *pr, *prop; + uint32_t hash_mask, new_shape_hash = 0; + intptr_t h; + + /* update the shape hash */ + if (sh->is_hashed) { + js_shape_hash_unlink(rt, sh); + new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags); + } + + if (unlikely(sh->prop_count >= sh->prop_size)) { + if (resize_properties(ctx, psh, p, sh->prop_count + 1)) { + /* in case of error, reinsert in the hash table. + sh is still valid if resize_properties() failed */ + if (sh->is_hashed) + js_shape_hash_link(rt, sh); + return -1; + } + sh = *psh; + } + if (sh->is_hashed) { + sh->hash = new_shape_hash; + js_shape_hash_link(rt, sh); + } + /* Initialize the new shape property. + The object property at p->prop[sh->prop_count] is uninitialized */ + prop = get_shape_prop(sh); + pr = &prop[sh->prop_count++]; + pr->atom = JS_DupAtom(ctx, atom); + pr->flags = prop_flags; + sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); + /* add in hash table */ + hash_mask = sh->prop_hash_mask; + h = atom & hash_mask; + pr->hash_next = prop_hash_end(sh)[-h - 1]; + prop_hash_end(sh)[-h - 1] = sh->prop_count; + return 0; +} + +/* find a hashed empty shape matching the prototype. Return NULL if + not found */ +static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto) +{ + JSShape *sh1; + uint32_t h, h1; + + h = shape_initial_hash(proto); + h1 = get_shape_hash(h, rt->shape_hash_bits); + for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { + if (sh1->hash == h && + sh1->proto == proto && + sh1->prop_count == 0) { + return sh1; + } + } + return NULL; +} + +/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if + not found */ +static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh, + JSAtom atom, int prop_flags) +{ + JSShape *sh1; + uint32_t h, h1, i, n; + + h = sh->hash; + h = shape_hash(h, atom); + h = shape_hash(h, prop_flags); + h1 = get_shape_hash(h, rt->shape_hash_bits); + for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) { + /* we test the hash first so that the rest is done only if the + shapes really match */ + if (sh1->hash == h && + sh1->proto == sh->proto && + sh1->prop_count == ((n = sh->prop_count) + 1)) { + for(i = 0; i < n; i++) { + if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) || + unlikely(sh1->prop[i].flags != sh->prop[i].flags)) + goto next; + } + if (unlikely(sh1->prop[n].atom != atom) || + unlikely(sh1->prop[n].flags != prop_flags)) + goto next; + return sh1; + } + next: ; + } + return NULL; +} + +static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) +{ + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + int j; + + /* XXX: should output readable class prototype */ + printf("%5d %3d%c %14p %5d %5d", i, + sh->header.ref_count, " *"[sh->is_hashed], + (void *)sh->proto, sh->prop_size, sh->prop_count); + for(j = 0; j < sh->prop_count; j++) { + printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), + sh->prop[j].atom)); + } + printf("\n"); +} + +static __maybe_unused void JS_DumpShapes(JSRuntime *rt) +{ + int i; + JSShape *sh; + struct list_head *el; + JSObject *p; + JSGCObjectHeader *gp; + + printf("JSShapes: {\n"); + printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS"); + for(i = 0; i < rt->shape_hash_size; i++) { + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { + JS_DumpShape(rt, i, sh); + assert(sh->is_hashed); + } + } + /* dump non-hashed shapes */ + list_for_each(el, &rt->gc_obj_list) { + gp = list_entry(el, JSGCObjectHeader, link); + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + p = (JSObject *)gp; + if (!p->shape->is_hashed) { + JS_DumpShape(rt, -1, p->shape); + } + } + } + printf("}\n"); +} + +static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id) +{ + JSObject *p; + + js_trigger_gc(ctx->rt, sizeof(JSObject)); + p = js_malloc(ctx, sizeof(JSObject)); + if (unlikely(!p)) + goto fail; + p->class_id = class_id; + p->extensible = TRUE; + p->free_mark = 0; + p->is_exotic = 0; + p->fast_array = 0; + p->is_constructor = 0; + p->is_uncatchable_error = 0; + p->tmp_mark = 0; + p->is_HTMLDDA = 0; + p->first_weak_ref = NULL; + p->u.opaque = NULL; + p->shape = sh; + p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size); + if (unlikely(!p->prop)) { + js_free(ctx, p); + fail: + js_free_shape(ctx->rt, sh); + return JS_EXCEPTION; + } + + switch(class_id) { + case JS_CLASS_OBJECT: + break; + case JS_CLASS_ARRAY: + { + JSProperty *pr; + p->is_exotic = 1; + p->fast_array = 1; + p->u.array.u.values = NULL; + p->u.array.count = 0; + p->u.array.u1.size = 0; + /* the length property is always the first one */ + if (likely(sh == ctx->array_shape)) { + pr = &p->prop[0]; + } else { + /* only used for the first array */ + /* cannot fail */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_LENGTH); + } + pr->u.value = js_int32(0); + } + break; + case JS_CLASS_C_FUNCTION: + p->prop[0].u.value = JS_UNDEFINED; + break; + case JS_CLASS_ARGUMENTS: + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: + p->is_exotic = 1; + p->fast_array = 1; + p->u.array.u.ptr = NULL; + p->u.array.count = 0; + break; + case JS_CLASS_DATAVIEW: + p->u.array.u.ptr = NULL; + p->u.array.count = 0; + break; + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_SYMBOL: + case JS_CLASS_DATE: + case JS_CLASS_BIG_INT: + p->u.object_data = JS_UNDEFINED; + goto set_exotic; + case JS_CLASS_REGEXP: + p->u.regexp.pattern = NULL; + p->u.regexp.bytecode = NULL; + goto set_exotic; + default: + set_exotic: + if (ctx->rt->class_array[class_id].exotic) { + p->is_exotic = 1; + } + break; + } + p->header.ref_count = 1; + add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT); + return JS_MKPTR(JS_TAG_OBJECT, p); +} + +static JSObject *get_proto_obj(JSValue proto_val) +{ + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) + return NULL; + else + return JS_VALUE_GET_OBJ(proto_val); +} + +/* WARNING: proto must be an object or JS_NULL */ +JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValue proto_val, + JSClassID class_id) +{ + JSShape *sh; + JSObject *proto; + + proto = get_proto_obj(proto_val); + sh = find_hashed_shape_proto(ctx->rt, proto); + if (likely(sh)) { + sh = js_dup_shape(sh); + } else { + sh = js_new_shape(ctx, proto); + if (!sh) + return JS_EXCEPTION; + } + return JS_NewObjectFromShape(ctx, sh, class_id); +} + +static int JS_SetObjectData(JSContext *ctx, JSValue obj, JSValue val) +{ + JSObject *p; + + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + switch(p->class_id) { + case JS_CLASS_NUMBER: + case JS_CLASS_STRING: + case JS_CLASS_BOOLEAN: + case JS_CLASS_SYMBOL: + case JS_CLASS_DATE: + case JS_CLASS_BIG_INT: + JS_FreeValue(ctx, p->u.object_data); + p->u.object_data = val; + return 0; + } + } + JS_FreeValue(ctx, val); + if (!JS_IsException(obj)) + JS_ThrowTypeError(ctx, "invalid object type"); + return -1; +} + +JSValue JS_NewObjectClass(JSContext *ctx, int class_id) +{ + return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id); +} + +JSValue JS_NewObjectProto(JSContext *ctx, JSValue proto) +{ + return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT); +} + +JSValue JS_NewArray(JSContext *ctx) +{ + return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape), + JS_CLASS_ARRAY); +} + +JSValue JS_NewObject(JSContext *ctx) +{ + /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */ + return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT); +} + +static void js_function_set_properties(JSContext *ctx, JSValue func_obj, + JSAtom name, int len) +{ + /* ES6 feature non compatible with ES5.1: length is configurable */ + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, js_int32(len), + JS_PROP_CONFIGURABLE); + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, + JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE); +} + +static BOOL js_class_has_bytecode(JSClassID class_id) +{ + return (class_id == JS_CLASS_BYTECODE_FUNCTION || + class_id == JS_CLASS_GENERATOR_FUNCTION || + class_id == JS_CLASS_ASYNC_FUNCTION || + class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION); +} + +/* return NULL without exception if not a function or no bytecode */ +static JSFunctionBytecode *JS_GetFunctionBytecode(JSValue val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return NULL; + p = JS_VALUE_GET_OBJ(val); + if (!js_class_has_bytecode(p->class_id)) + return NULL; + return p->u.func.function_bytecode; +} + +static void js_method_set_home_object(JSContext *ctx, JSValue func_obj, + JSValue home_obj) +{ + JSObject *p, *p1; + JSFunctionBytecode *b; + + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(func_obj); + if (!js_class_has_bytecode(p->class_id)) + return; + b = p->u.func.function_bytecode; + if (b->need_home_object) { + p1 = p->u.func.home_object; + if (p1) { + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT) + p1 = JS_VALUE_GET_OBJ(js_dup(home_obj)); + else + p1 = NULL; + p->u.func.home_object = p1; + } +} + +static JSValue js_get_function_name(JSContext *ctx, JSAtom name) +{ + JSValue name_str; + + name_str = JS_AtomToString(ctx, name); + if (JS_AtomSymbolHasDescription(ctx, name)) { + name_str = JS_ConcatString3(ctx, "[", name_str, "]"); + } + return name_str; +} + +/* Modify the name of a method according to the atom and + 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and + JS_PROP_HAS_SET. Also set the home object of the method. + Return < 0 if exception. */ +static int js_method_set_properties(JSContext *ctx, JSValue func_obj, + JSAtom name, int flags, JSValue home_obj) +{ + JSValue name_str; + + name_str = js_get_function_name(ctx, name); + if (flags & JS_PROP_HAS_GET) { + name_str = JS_ConcatString3(ctx, "get ", name_str, ""); + } else if (flags & JS_PROP_HAS_SET) { + name_str = JS_ConcatString3(ctx, "set ", name_str, ""); + } + if (JS_IsException(name_str)) + return -1; + if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str, + JS_PROP_CONFIGURABLE) < 0) + return -1; + js_method_set_home_object(ctx, func_obj, home_obj); + return 0; +} + +/* Note: at least 'length' arguments will be readable in 'argv' */ +/* `name` may be NULL, pure ASCII or UTF-8 encoded */ +static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic, + JSValue proto_val) +{ + JSValue func_obj; + JSObject *p; + JSAtom name_atom; + + func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION); + if (JS_IsException(func_obj)) + return func_obj; + p = JS_VALUE_GET_OBJ(func_obj); + p->u.cfunc.realm = JS_DupContext(ctx); + p->u.cfunc.c_function.generic = func; + p->u.cfunc.length = length; + p->u.cfunc.cproto = cproto; + p->u.cfunc.magic = magic; + p->is_constructor = (cproto == JS_CFUNC_constructor || + cproto == JS_CFUNC_constructor_magic || + cproto == JS_CFUNC_constructor_or_func || + cproto == JS_CFUNC_constructor_or_func_magic); + if (!name) + name = ""; + name_atom = JS_NewAtom(ctx, name); + js_function_set_properties(ctx, func_obj, name_atom, length); + JS_FreeAtom(ctx, name_atom); + return func_obj; +} + +/* Note: at least 'length' arguments will be readable in 'argv' */ +JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func, + const char *name, + int length, JSCFunctionEnum cproto, int magic) +{ + return JS_NewCFunction3(ctx, func, name, length, cproto, magic, + ctx->function_proto); +} + +typedef struct JSCFunctionDataRecord { + JSCFunctionData *func; + uint8_t length; + uint8_t data_len; + uint16_t magic; + JSValue data[]; +} JSCFunctionDataRecord; + +static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val) +{ + JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); + int i; + + if (s) { + for(i = 0; i < s->data_len; i++) { + JS_FreeValueRT(rt, s->data[i]); + } + js_free_rt(rt, s); + } +} + +static void js_c_function_data_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA); + int i; + + if (s) { + for(i = 0; i < s->data_len; i++) { + JS_MarkValue(rt, s->data[i], mark_func); + } + } +} + +static JSValue js_c_function_data_call(JSContext *ctx, JSValue func_obj, + JSValue this_val, + int argc, JSValue *argv, int flags) +{ + JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA); + JSValue *arg_buf; + int i; + + /* XXX: could add the function on the stack for debug */ + if (unlikely(argc < s->length)) { + arg_buf = alloca(sizeof(arg_buf[0]) * s->length); + for(i = 0; i < argc; i++) + arg_buf[i] = argv[i]; + for(i = argc; i < s->length; i++) + arg_buf[i] = JS_UNDEFINED; + } else { + arg_buf = argv; + } + + return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data); +} + +JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, + int length, int magic, int data_len, + JSValue *data) +{ + JSCFunctionDataRecord *s; + JSValue func_obj; + int i; + + func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_C_FUNCTION_DATA); + if (JS_IsException(func_obj)) + return func_obj; + s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue)); + if (!s) { + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; + } + s->func = func; + s->length = length; + s->data_len = data_len; + s->magic = magic; + for(i = 0; i < data_len; i++) + s->data[i] = js_dup(data[i]); + JS_SetOpaque(func_obj, s); + js_function_set_properties(ctx, func_obj, + JS_ATOM_empty_string, length); + return func_obj; +} + +static JSContext *js_autoinit_get_realm(JSProperty *pr) +{ + return (JSContext *)(pr->u.init.realm_and_id & ~3); +} + +static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr) +{ + return pr->u.init.realm_and_id & 3; +} + +static void js_autoinit_free(JSRuntime *rt, JSProperty *pr) +{ + JS_FreeContext(js_autoinit_get_realm(pr)); +} + +static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr, + JS_MarkFunc *mark_func) +{ + mark_func(rt, &js_autoinit_get_realm(pr)->header); +} + +static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags) +{ + if (unlikely(prop_flags & JS_PROP_TMASK)) { + if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (pr->u.getset.getter) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + free_var_ref(rt, pr->u.var_ref); + } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + js_autoinit_free(rt, pr); + } + } else { + JS_FreeValueRT(rt, pr->u.value); + } +} + +static force_inline JSShapeProperty *find_own_property1(JSObject *p, + JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + return pr; + } + h = pr->hash_next; + } + return NULL; +} + +static force_inline JSShapeProperty *find_own_property(JSProperty **ppr, + JSObject *p, + JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *prop; + intptr_t h; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + *ppr = &p->prop[h - 1]; + /* the compiler should be able to assume that pr != NULL here */ + return pr; + } + h = pr->hash_next; + } + *ppr = NULL; + return NULL; +} + +static force_inline JSShapeProperty* find_own_property_ic(JSProperty** ppr, JSObject* p, + JSAtom atom, uint32_t* offset) +{ + JSShape* sh; + JSShapeProperty *pr, *prop; + intptr_t h, i; + sh = p->shape; + h = (uintptr_t)atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h - 1]; + prop = get_shape_prop(sh); + while (h) { + i = h - 1; + pr = &prop[i]; + if (likely(pr->atom == atom)) { + *ppr = &p->prop[i]; + *offset = i; + /* the compiler should be able to assume that pr != NULL here */ + return pr; + } + h = pr->hash_next; + } + *ppr = NULL; + return NULL; +} + +/* indicate that the object may be part of a function prototype cycle */ +static void set_cycle_flag(JSContext *ctx, JSValue obj) +{ +} + +static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) +{ + if (var_ref) { + assert(var_ref->header.ref_count > 0); + if (--var_ref->header.ref_count == 0) { + if (var_ref->is_detached) { + JS_FreeValueRT(rt, var_ref->value); + remove_gc_object(&var_ref->header); + } else { + list_del(&var_ref->header.link); /* still on the stack */ + } + js_free_rt(rt, var_ref); + } + } +} + +static void js_array_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + int i; + + for(i = 0; i < p->u.array.count; i++) { + JS_FreeValueRT(rt, p->u.array.u.values[i]); + } + js_free_rt(rt, p->u.array.u.values); +} + +static void js_array_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + int i; + + for(i = 0; i < p->u.array.count; i++) { + JS_MarkValue(rt, p->u.array.u.values[i], mark_func); + } +} + +static void js_object_data_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JS_FreeValueRT(rt, p->u.object_data); + p->u.object_data = JS_UNDEFINED; +} + +static void js_object_data_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JS_MarkValue(rt, p->u.object_data, mark_func); +} + +static void js_c_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + + if (p->u.cfunc.realm) + JS_FreeContext(p->u.cfunc.realm); +} + +static void js_c_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + + if (p->u.cfunc.realm) + mark_func(rt, &p->u.cfunc.realm->header); +} + +static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p1, *p = JS_VALUE_GET_OBJ(val); + JSFunctionBytecode *b; + JSVarRef **var_refs; + int i; + + p1 = p->u.func.home_object; + if (p1) { + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1)); + } + b = p->u.func.function_bytecode; + if (b) { + var_refs = p->u.func.var_refs; + if (var_refs) { + for(i = 0; i < b->closure_var_count; i++) + free_var_ref(rt, var_refs[i]); + js_free_rt(rt, var_refs); + } + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b)); + } +} + +static void js_bytecode_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSVarRef **var_refs = p->u.func.var_refs; + JSFunctionBytecode *b = p->u.func.function_bytecode; + int i; + + if (p->u.func.home_object) { + JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object), + mark_func); + } + if (b) { + if (var_refs) { + for(i = 0; i < b->closure_var_count; i++) { + JSVarRef *var_ref = var_refs[i]; + if (var_ref && var_ref->is_detached) { + mark_func(rt, &var_ref->header); + } + } + } + /* must mark the function bytecode because template objects may be + part of a cycle */ + JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func); + } +} + +static void js_bound_function_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSBoundFunction *bf = p->u.bound_function; + int i; + + JS_FreeValueRT(rt, bf->func_obj); + JS_FreeValueRT(rt, bf->this_val); + for(i = 0; i < bf->argc; i++) { + JS_FreeValueRT(rt, bf->argv[i]); + } + js_free_rt(rt, bf); +} + +static void js_bound_function_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSBoundFunction *bf = p->u.bound_function; + int i; + + JS_MarkValue(rt, bf->func_obj, mark_func); + JS_MarkValue(rt, bf->this_val, mark_func); + for(i = 0; i < bf->argc; i++) + JS_MarkValue(rt, bf->argv[i], mark_func); +} + +static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSForInIterator *it = p->u.for_in_iterator; + JS_FreeValueRT(rt, it->obj); + js_free_rt(rt, it); +} + +static void js_for_in_iterator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSForInIterator *it = p->u.for_in_iterator; + JS_MarkValue(rt, it->obj, mark_func); +} + +static void free_object(JSRuntime *rt, JSObject *p) +{ + int i; + JSClassFinalizer *finalizer; + JSShape *sh; + JSShapeProperty *pr; + + p->free_mark = 1; /* used to tell the object is invalid when + freeing cycles */ + /* free all the fields */ + sh = p->shape; + pr = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + free_property(rt, &p->prop[i], pr->flags); + pr++; + } + js_free_rt(rt, p->prop); + /* as an optimization we destroy the shape immediately without + putting it in gc_zero_ref_count_list */ + js_free_shape(rt, sh); + + /* fail safe */ + p->shape = NULL; + p->prop = NULL; + + if (unlikely(p->first_weak_ref)) { + reset_weak_ref(rt, &p->first_weak_ref); + } + + finalizer = rt->class_array[p->class_id].finalizer; + if (finalizer) + (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p)); + + /* fail safe */ + p->class_id = 0; + p->u.opaque = NULL; + p->u.func.var_refs = NULL; + p->u.func.home_object = NULL; + + remove_gc_object(&p->header); + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) { + list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); + } else { + js_free_rt(rt, p); + } +} + +static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) +{ + switch(gp->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + free_object(rt, (JSObject *)gp); + break; + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + free_function_bytecode(rt, (JSFunctionBytecode *)gp); + break; + default: + abort(); + } +} + +static void free_zero_refcount(JSRuntime *rt) +{ + struct list_head *el; + JSGCObjectHeader *p; + + rt->gc_phase = JS_GC_PHASE_DECREF; + for(;;) { + el = rt->gc_zero_ref_count_list.next; + if (el == &rt->gc_zero_ref_count_list) + break; + p = list_entry(el, JSGCObjectHeader, link); + assert(p->ref_count == 0); + free_gc_object(rt, p); + } + rt->gc_phase = JS_GC_PHASE_NONE; +} + +/* called with the ref_count of 'v' reaches zero. */ +void __JS_FreeValueRT(JSRuntime *rt, JSValue v) +{ + uint32_t tag = JS_VALUE_GET_TAG(v); + +#ifdef DUMP_FREE + if (check_dump_flag(rt, DUMP_FREE)) { + /* Prevent invalid object access during GC */ + if ((rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) + || (tag != JS_TAG_OBJECT && tag != JS_TAG_FUNCTION_BYTECODE)) { + printf("Freeing "); + if (tag == JS_TAG_OBJECT) { + JS_DumpObject(rt, JS_VALUE_GET_OBJ(v)); + } else { + JS_DumpValue(rt, v); + printf("\n"); + } + } + } +#endif + + switch(tag) { + case JS_TAG_STRING: + { + JSString *p = JS_VALUE_GET_STRING(v); + if (p->atom_type) { + JS_FreeAtomStruct(rt, p); + } else { +#ifdef DUMP_LEAKS + list_del(&p->link); +#endif + js_free_rt(rt, p); + } + } + break; + case JS_TAG_OBJECT: + case JS_TAG_FUNCTION_BYTECODE: + { + JSGCObjectHeader *p = JS_VALUE_GET_PTR(v); + if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { + list_del(&p->link); + list_add(&p->link, &rt->gc_zero_ref_count_list); + if (rt->gc_phase == JS_GC_PHASE_NONE) { + free_zero_refcount(rt); + } + } + } + break; + case JS_TAG_MODULE: + abort(); /* never freed here */ + break; + case JS_TAG_BIG_INT: + { + JSBigInt *bf = JS_VALUE_GET_PTR(v); + bf_delete(&bf->num); + js_free_rt(rt, bf); + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p = JS_VALUE_GET_PTR(v); + JS_FreeAtomStruct(rt, p); + } + break; + default: + printf("__JS_FreeValue: unknown tag=%d\n", tag); + abort(); + } +} + +void __JS_FreeValue(JSContext *ctx, JSValue v) +{ + __JS_FreeValueRT(ctx->rt, v); +} + +/* garbage collection */ + +static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, + JSGCObjectTypeEnum type) +{ + h->mark = 0; + h->gc_obj_type = type; + list_add_tail(&h->link, &rt->gc_obj_list); +} + +static void remove_gc_object(JSGCObjectHeader *h) +{ + list_del(&h->link); +} + +void JS_MarkValue(JSRuntime *rt, JSValue val, JS_MarkFunc *mark_func) +{ + if (JS_VALUE_HAS_REF_COUNT(val)) { + switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_OBJECT: + case JS_TAG_FUNCTION_BYTECODE: + mark_func(rt, JS_VALUE_GET_PTR(val)); + break; + default: + break; + } + } +} + +static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, + JS_MarkFunc *mark_func) +{ + switch(gp->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + { + JSObject *p = (JSObject *)gp; + JSShapeProperty *prs; + JSShape *sh; + int i; + sh = p->shape; + mark_func(rt, &sh->header); + /* mark all the fields */ + prs = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JSProperty *pr = &p->prop[i]; + if (prs->atom != JS_ATOM_NULL) { + if (prs->flags & JS_PROP_TMASK) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (pr->u.getset.getter) + mark_func(rt, &pr->u.getset.getter->header); + if (pr->u.getset.setter) + mark_func(rt, &pr->u.getset.setter->header); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if (pr->u.var_ref->is_detached) { + /* Note: the tag does not matter + provided it is a GC object */ + mark_func(rt, &pr->u.var_ref->header); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + js_autoinit_mark(rt, pr, mark_func); + } + } else { + JS_MarkValue(rt, pr->u.value, mark_func); + } + } + prs++; + } + + if (p->class_id != JS_CLASS_OBJECT) { + JSClassGCMark *gc_mark; + gc_mark = rt->class_array[p->class_id].gc_mark; + if (gc_mark) + gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func); + } + } + break; + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + /* the template objects can be part of a cycle */ + { + JSShape **shape, *(*shapes)[IC_CACHE_ITEM_CAPACITY]; + JSFunctionBytecode *b = (JSFunctionBytecode *)gp; + int i; + for(i = 0; i < b->cpool_count; i++) { + JS_MarkValue(rt, b->cpool[i], mark_func); + } + if (b->realm) + mark_func(rt, &b->realm->header); + if (b->ic) { + for (i = 0; i < b->ic->count; i++) { + shapes = &b->ic->cache[i].shape; + for (shape = *shapes; shape != endof(*shapes); shape++) + if (*shape) + mark_func(rt, &(*shape)->header); + } + } + } + break; + case JS_GC_OBJ_TYPE_VAR_REF: + { + JSVarRef *var_ref = (JSVarRef *)gp; + /* only detached variable referenced are taken into account */ + assert(var_ref->is_detached); + JS_MarkValue(rt, *var_ref->pvalue, mark_func); + } + break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + { + JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp; + if (s->is_active) + async_func_mark(rt, &s->func_state, mark_func); + JS_MarkValue(rt, s->resolving_funcs[0], mark_func); + JS_MarkValue(rt, s->resolving_funcs[1], mark_func); + } + break; + case JS_GC_OBJ_TYPE_SHAPE: + { + JSShape *sh = (JSShape *)gp; + if (sh->proto != NULL) { + mark_func(rt, &sh->proto->header); + } + } + break; + case JS_GC_OBJ_TYPE_JS_CONTEXT: + { + JSContext *ctx = (JSContext *)gp; + JS_MarkContext(rt, ctx, mark_func); + } + break; + default: + abort(); + } +} + +static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p) +{ + assert(p->ref_count > 0); + p->ref_count--; + if (p->ref_count == 0 && p->mark == 1) { + list_del(&p->link); + list_add_tail(&p->link, &rt->tmp_obj_list); + } +} + +static void gc_decref(JSRuntime *rt) +{ + struct list_head *el, *el1; + JSGCObjectHeader *p; + + init_list_head(&rt->tmp_obj_list); + + /* decrement the refcount of all the children of all the GC + objects and move the GC objects with zero refcount to + tmp_obj_list */ + list_for_each_safe(el, el1, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->mark == 0); + mark_children(rt, p, gc_decref_child); + p->mark = 1; + if (p->ref_count == 0) { + list_del(&p->link); + list_add_tail(&p->link, &rt->tmp_obj_list); + } + } +} + +static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p) +{ + p->ref_count++; + if (p->ref_count == 1) { + /* ref_count was 0: remove from tmp_obj_list and add at the + end of gc_obj_list */ + list_del(&p->link); + list_add_tail(&p->link, &rt->gc_obj_list); + p->mark = 0; /* reset the mark for the next GC call */ + } +} + +static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p) +{ + p->ref_count++; +} + +static void gc_scan(JSRuntime *rt) +{ + struct list_head *el; + JSGCObjectHeader *p; + + /* keep the objects with a refcount > 0 and their children. */ + list_for_each(el, &rt->gc_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->ref_count > 0); + p->mark = 0; /* reset the mark for the next GC call */ + mark_children(rt, p, gc_scan_incref_child); + } + + /* restore the refcount of the objects to be deleted. */ + list_for_each(el, &rt->tmp_obj_list) { + p = list_entry(el, JSGCObjectHeader, link); + mark_children(rt, p, gc_scan_incref_child2); + } +} + +static void gc_free_cycles(JSRuntime *rt) +{ + struct list_head *el, *el1; + JSGCObjectHeader *p; +#ifdef DUMP_GC_FREE + BOOL header_done = FALSE; +#endif + + rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES; + + for(;;) { + el = rt->tmp_obj_list.next; + if (el == &rt->tmp_obj_list) + break; + p = list_entry(el, JSGCObjectHeader, link); + /* Only need to free the GC object associated with JS + values. The rest will be automatically removed because they + must be referenced by them. */ + switch(p->gc_obj_type) { + case JS_GC_OBJ_TYPE_JS_OBJECT: + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: +#ifdef DUMP_GC_FREE + if (check_dump_flag(rt, DUMP_GC_FREE)) { + if (!header_done) { + printf("Freeing cycles:\n"); + JS_DumpObjectHeader(rt); + header_done = TRUE; + } + JS_DumpGCObject(rt, p); + } +#endif + free_gc_object(rt, p); + break; + default: + list_del(&p->link); + list_add_tail(&p->link, &rt->gc_zero_ref_count_list); + break; + } + } + rt->gc_phase = JS_GC_PHASE_NONE; + + list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) { + p = list_entry(el, JSGCObjectHeader, link); + assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || + p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); + js_free_rt(rt, p); + } + + init_list_head(&rt->gc_zero_ref_count_list); +} + +void JS_RunGC(JSRuntime *rt) +{ + /* decrement the reference of the children of each object. mark = + 1 after this pass. */ + gc_decref(rt); + + /* keep the GC objects with a non zero refcount and their childs */ + gc_scan(rt); + + /* free the GC objects in a cycle */ + gc_free_cycles(rt); +} + +/* Return false if not an object or if the object has already been + freed (zombie objects are visible in finalizers when freeing + cycles). */ +BOOL JS_IsLiveObject(JSRuntime *rt, JSValue obj) +{ + JSObject *p; + if (!JS_IsObject(obj)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + return !p->free_mark; +} + +/* Compute memory used by various object types */ +/* XXX: poor man's approach to handling multiply referenced objects */ +typedef struct JSMemoryUsage_helper { + double memory_used_count; + double str_count; + double str_size; + int64_t js_func_count; + double js_func_size; + int64_t js_func_code_size; + int64_t js_func_pc2line_count; + int64_t js_func_pc2line_size; +} JSMemoryUsage_helper; + +static void compute_value_size(JSValue val, JSMemoryUsage_helper *hp); + +static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp) +{ + if (!str->atom_type) { /* atoms are handled separately */ + double s_ref_count = str->header.ref_count; + hp->str_count += 1 / s_ref_count; + hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) + + 1 - str->is_wide_char) / s_ref_count); + } +} + +static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp) +{ + int memory_used_count, js_func_size, i; + + memory_used_count = 0; + js_func_size = sizeof(*b); + if (b->vardefs) { + js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs); + } + if (b->cpool) { + js_func_size += b->cpool_count * sizeof(*b->cpool); + for (i = 0; i < b->cpool_count; i++) { + JSValue val = b->cpool[i]; + compute_value_size(val, hp); + } + } + if (b->closure_var) { + js_func_size += b->closure_var_count * sizeof(*b->closure_var); + } + if (b->byte_code_buf) { + hp->js_func_code_size += b->byte_code_len; + } + memory_used_count++; + js_func_size += b->source_len + 1; + if (b->pc2line_len) { + memory_used_count++; + hp->js_func_pc2line_count += 1; + hp->js_func_pc2line_size += b->pc2line_len; + } + hp->js_func_size += js_func_size; + hp->js_func_count += 1; + hp->memory_used_count += memory_used_count; +} + +static void compute_value_size(JSValue val, JSMemoryUsage_helper *hp) +{ + switch(JS_VALUE_GET_TAG(val)) { + case JS_TAG_STRING: + compute_jsstring_size(JS_VALUE_GET_STRING(val), hp); + break; + case JS_TAG_BIG_INT: + /* should track JSBigInt usage */ + break; + } +} + +void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) +{ + struct list_head *el, *el1; + int i; + JSMemoryUsage_helper mem = { 0 }, *hp = &mem; + + memset(s, 0, sizeof(*s)); + s->malloc_count = rt->malloc_state.malloc_count; + s->malloc_size = rt->malloc_state.malloc_size; + s->malloc_limit = rt->malloc_state.malloc_limit; + + s->memory_used_count = 2; /* rt + rt->class_array */ + s->memory_used_size = sizeof(JSRuntime) + sizeof(JSClass) * rt->class_count; + + list_for_each(el, &rt->context_list) { + JSContext *ctx = list_entry(el, JSContext, link); + JSShape *sh = ctx->array_shape; + s->memory_used_count += 2; /* ctx + ctx->class_proto */ + s->memory_used_size += sizeof(JSContext) + + sizeof(JSValue) * rt->class_count; + s->binary_object_count += ctx->binary_object_count; + s->binary_object_size += ctx->binary_object_size; + + /* the hashed shapes are counted separately */ + if (sh && !sh->is_hashed) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + list_for_each(el1, &ctx->loaded_modules) { + JSModuleDef *m = list_entry(el1, JSModuleDef, link); + s->memory_used_count += 1; + s->memory_used_size += sizeof(*m); + if (m->req_module_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries); + } + if (m->export_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries); + for (i = 0; i < m->export_entries_count; i++) { + JSExportEntry *me = &m->export_entries[i]; + if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) { + /* potential multiple count */ + s->memory_used_count += 1; + compute_value_size(me->u.local.var_ref->value, hp); + } + } + } + if (m->star_export_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries); + } + if (m->import_entries) { + s->memory_used_count += 1; + s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries); + } + compute_value_size(m->module_ns, hp); + compute_value_size(m->func_obj, hp); + } + } + + list_for_each(el, &rt->gc_obj_list) { + JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); + JSObject *p; + JSShape *sh; + JSShapeProperty *prs; + + /* XXX: could count the other GC object types too */ + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) { + compute_bytecode_size((JSFunctionBytecode *)gp, hp); + continue; + } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) { + continue; + } + p = (JSObject *)gp; + sh = p->shape; + s->obj_count++; + if (p->prop) { + s->memory_used_count++; + s->prop_size += sh->prop_size * sizeof(*p->prop); + s->prop_count += sh->prop_count; + prs = get_shape_prop(sh); + for(i = 0; i < sh->prop_count; i++) { + JSProperty *pr = &p->prop[i]; + if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) { + compute_value_size(pr->u.value, hp); + } + prs++; + } + } + /* the hashed shapes are counted separately */ + if (!sh->is_hashed) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + + switch(p->class_id) { + case JS_CLASS_ARRAY: /* u.array | length */ + case JS_CLASS_ARGUMENTS: /* u.array | length */ + s->array_count++; + if (p->fast_array) { + s->fast_array_count++; + if (p->u.array.u.values) { + s->memory_used_count++; + s->memory_used_size += p->u.array.count * + sizeof(*p->u.array.u.values); + s->fast_array_elements += p->u.array.count; + for (i = 0; i < p->u.array.count; i++) { + compute_value_size(p->u.array.u.values[i], hp); + } + } + } + break; + case JS_CLASS_NUMBER: /* u.object_data */ + case JS_CLASS_STRING: /* u.object_data */ + case JS_CLASS_BOOLEAN: /* u.object_data */ + case JS_CLASS_SYMBOL: /* u.object_data */ + case JS_CLASS_DATE: /* u.object_data */ + case JS_CLASS_BIG_INT: /* u.object_data */ + compute_value_size(p->u.object_data, hp); + break; + case JS_CLASS_C_FUNCTION: /* u.cfunc */ + s->c_func_count++; + break; + case JS_CLASS_BYTECODE_FUNCTION: /* u.func */ + { + JSFunctionBytecode *b = p->u.func.function_bytecode; + JSVarRef **var_refs = p->u.func.var_refs; + /* home_object: object will be accounted for in list scan */ + if (var_refs) { + s->memory_used_count++; + s->js_func_size += b->closure_var_count * sizeof(*var_refs); + for (i = 0; i < b->closure_var_count; i++) { + if (var_refs[i]) { + double ref_count = var_refs[i]->header.ref_count; + s->memory_used_count += 1 / ref_count; + s->js_func_size += sizeof(*var_refs[i]) / ref_count; + /* handle non object closed values */ + if (var_refs[i]->pvalue == &var_refs[i]->value) { + /* potential multiple count */ + compute_value_size(var_refs[i]->value, hp); + } + } + } + } + } + break; + case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */ + { + JSBoundFunction *bf = p->u.bound_function; + /* func_obj and this_val are objects */ + for (i = 0; i < bf->argc; i++) { + compute_value_size(bf->argv[i], hp); + } + s->memory_used_count += 1; + s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv); + } + break; + case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */ + { + JSCFunctionDataRecord *fd = p->u.c_function_data_record; + if (fd) { + for (i = 0; i < fd->data_len; i++) { + compute_value_size(fd->data[i], hp); + } + s->memory_used_count += 1; + s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data); + } + } + break; + case JS_CLASS_REGEXP: /* u.regexp */ + compute_jsstring_size(p->u.regexp.pattern, hp); + compute_jsstring_size(p->u.regexp.bytecode, hp); + break; + + case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */ + { + JSForInIterator *it = p->u.for_in_iterator; + if (it) { + compute_value_size(it->obj, hp); + s->memory_used_count += 1; + s->memory_used_size += sizeof(*it); + } + } + break; + case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */ + case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */ + { + JSArrayBuffer *abuf = p->u.array_buffer; + if (abuf) { + s->memory_used_count += 1; + s->memory_used_size += sizeof(*abuf); + if (abuf->data) { + s->memory_used_count += 1; + s->memory_used_size += abuf->byte_length; + } + } + } + break; + case JS_CLASS_GENERATOR: /* u.generator_data */ + case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */ + case JS_CLASS_DATAVIEW: /* u.typed_array */ + case JS_CLASS_MAP: /* u.map_state */ + case JS_CLASS_SET: /* u.map_state */ + case JS_CLASS_WEAKMAP: /* u.map_state */ + case JS_CLASS_WEAKSET: /* u.map_state */ + case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */ + case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */ + case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */ + case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */ + case JS_CLASS_PROXY: /* u.proxy_data */ + case JS_CLASS_PROMISE: /* u.promise_data */ + case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */ + case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */ + case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */ + case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */ + case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */ + case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */ + /* TODO */ + default: + /* XXX: class definition should have an opaque block size */ + if (p->u.opaque) { + s->memory_used_count += 1; + } + break; + } + } + s->obj_size += s->obj_count * sizeof(JSObject); + + /* hashed shapes */ + s->memory_used_count++; /* rt->shape_hash */ + s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size; + for(i = 0; i < rt->shape_hash_size; i++) { + JSShape *sh; + for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) { + int hash_size = sh->prop_hash_mask + 1; + s->shape_count++; + s->shape_size += get_shape_size(hash_size, sh->prop_size); + } + } + + /* atoms */ + s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */ + s->atom_count = rt->atom_count; + s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size + + sizeof(rt->atom_hash[0]) * rt->atom_hash_size; + for(i = 0; i < rt->atom_size; i++) { + JSAtomStruct *p = rt->atom_array[i]; + if (!atom_is_free(p)) { + s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) + + 1 - p->is_wide_char); + } + } + s->str_count = round(mem.str_count); + s->str_size = round(mem.str_size); + s->js_func_count = mem.js_func_count; + s->js_func_size = round(mem.js_func_size); + s->js_func_code_size = mem.js_func_code_size; + s->js_func_pc2line_count = mem.js_func_pc2line_count; + s->js_func_pc2line_size = mem.js_func_pc2line_size; + s->memory_used_count += round(mem.memory_used_count) + + s->atom_count + s->str_count + + s->obj_count + s->shape_count + + s->js_func_count + s->js_func_pc2line_count; + s->memory_used_size += s->atom_size + s->str_size + + s->obj_size + s->prop_size + s->shape_size + + s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size; +} + +void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) +{ + fprintf(fp, "QuickJS-ng memory usage -- %s version, %d-bit, %s Endian, malloc limit: %"PRId64"\n\n", + JS_GetVersion(), (int)sizeof(void *) * 8, is_be() ? "Big" : "Little", s->malloc_limit); + if (rt) { + static const struct { + const char *name; + size_t size; + } object_types[] = { + { "JSRuntime", sizeof(JSRuntime) }, + { "JSContext", sizeof(JSContext) }, + { "JSObject", sizeof(JSObject) }, + { "JSString", sizeof(JSString) }, + { "JSFunctionBytecode", sizeof(JSFunctionBytecode) }, + }; + int i, usage_size_ok = 0; + for(i = 0; i < countof(object_types); i++) { + unsigned int size = object_types[i].size; + void *p = js_malloc_rt(rt, size); + if (p) { + unsigned int size1 = js_malloc_usable_size_rt(rt, p); + if (size1 >= size) { + usage_size_ok = 1; + fprintf(fp, " %3u + %-2u %s\n", + size, size1 - size, object_types[i].name); + } + js_free_rt(rt, p); + } + } + if (!usage_size_ok) { + fprintf(fp, " malloc_usable_size unavailable\n"); + } + { + int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 }; + int class_id; + struct list_head *el; + list_for_each(el, &rt->gc_obj_list) { + JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link); + JSObject *p; + if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + p = (JSObject *)gp; + obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++; + } + } + fprintf(fp, "\n" "JSObject classes\n"); + if (obj_classes[0]) + fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none"); + for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) { + if (obj_classes[class_id]) { + char buf[ATOM_GET_STR_BUF_SIZE]; + fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id, + JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name)); + } + } + if (obj_classes[JS_CLASS_INIT_COUNT]) + fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other"); + } + fprintf(fp, "\n"); + } + fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE"); + + if (s->malloc_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n", + "memory allocated", s->malloc_count, s->malloc_size, + (double)s->malloc_size / s->malloc_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n", + "memory used", s->memory_used_count, s->memory_used_size, + MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) / + s->memory_used_count)); + } + if (s->atom_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n", + "atoms", s->atom_count, s->atom_size, + (double)s->atom_size / s->atom_count); + } + if (s->str_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n", + "strings", s->str_count, s->str_size, + (double)s->str_size / s->str_count); + } + if (s->obj_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", + "objects", s->obj_count, s->obj_size, + (double)s->obj_size / s->obj_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n", + " properties", s->prop_count, s->prop_size, + (double)s->prop_count / s->obj_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n", + " shapes", s->shape_count, s->shape_size, + (double)s->shape_size / s->shape_count); + } + if (s->js_func_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", + "bytecode functions", s->js_func_count, s->js_func_size); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", + " bytecode", s->js_func_count, s->js_func_code_size, + (double)s->js_func_code_size / s->js_func_count); + if (s->js_func_pc2line_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n", + " pc2line", s->js_func_pc2line_count, + s->js_func_pc2line_size, + (double)s->js_func_pc2line_size / s->js_func_pc2line_count); + } + } + if (s->c_func_count) { + fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count); + } + if (s->array_count) { + fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count); + if (s->fast_array_count) { + fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count); + fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n", + " elements", s->fast_array_elements, + s->fast_array_elements * (int)sizeof(JSValue), + (double)s->fast_array_elements / s->fast_array_count); + } + } + if (s->binary_object_count) { + fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n", + "binary objects", s->binary_object_count, s->binary_object_size); + } +} + +JSValue JS_GetGlobalObject(JSContext *ctx) +{ + return js_dup(ctx->global_obj); +} + +/* WARNING: obj is freed */ +JSValue JS_Throw(JSContext *ctx, JSValue obj) +{ + JSRuntime *rt = ctx->rt; + JS_FreeValue(ctx, rt->current_exception); + rt->current_exception = obj; + return JS_EXCEPTION; +} + +/* return the pending exception (cannot be called twice). */ +JSValue JS_GetException(JSContext *ctx) +{ + JSValue val; + JSRuntime *rt = ctx->rt; + val = rt->current_exception; + rt->current_exception = JS_NULL; + return val; +} + +static void dbuf_put_leb128(DynBuf *s, uint32_t v) +{ + uint32_t a; + for(;;) { + a = v & 0x7f; + v >>= 7; + if (v != 0) { + dbuf_putc(s, a | 0x80); + } else { + dbuf_putc(s, a); + break; + } + } +} + +static void dbuf_put_sleb128(DynBuf *s, int32_t v1) +{ + uint32_t v = v1; + dbuf_put_leb128(s, (2 * v) ^ -(v >> 31)); +} + +static int get_leb128(uint32_t *pval, const uint8_t *buf, + const uint8_t *buf_end) +{ + const uint8_t *ptr = buf; + uint32_t v, a, i; + v = 0; + for(i = 0; i < 5; i++) { + if (unlikely(ptr >= buf_end)) + break; + a = *ptr++; + v |= (a & 0x7f) << (i * 7); + if (!(a & 0x80)) { + *pval = v; + return ptr - buf; + } + } + *pval = 0; + return -1; +} + +static int get_sleb128(int32_t *pval, const uint8_t *buf, + const uint8_t *buf_end) +{ + int ret; + uint32_t val; + ret = get_leb128(&val, buf, buf_end); + if (ret < 0) { + *pval = 0; + return -1; + } + *pval = (val >> 1) ^ -(val & 1); + return ret; +} + +static int find_line_num(JSContext *ctx, JSFunctionBytecode *b, + uint32_t pc_value, int *col) +{ + const uint8_t *p_end, *p; + int new_line_num, new_col_num, line_num, col_num, pc, v, ret; + unsigned int op; + + *col = 1; + p = b->pc2line_buf; + p_end = p + b->pc2line_len; + pc = 0; + line_num = b->line_num; + col_num = b->col_num; + while (p < p_end) { + op = *p++; + if (op == 0) { + uint32_t val; + ret = get_leb128(&val, p, p_end); + if (ret < 0) + goto fail; + pc += val; + p += ret; + ret = get_sleb128(&v, p, p_end); + if (ret < 0) + goto fail; + p += ret; + new_line_num = line_num + v; + } else { + op -= PC2LINE_OP_FIRST; + pc += (op / PC2LINE_RANGE); + new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE; + } + ret = get_sleb128(&v, p, p_end); + if (ret < 0) + goto fail; + p += ret; + new_col_num = col_num + v; + if (pc_value < pc) + break; + line_num = new_line_num; + col_num = new_col_num; + } + *col = col_num; + return line_num; +fail: + /* should never happen */ + return b->line_num; +} + +/* in order to avoid executing arbitrary code during the stack trace + generation, we only look at simple 'name' properties containing a + string. */ +static const char *get_func_name(JSContext *ctx, JSValue func) +{ + JSProperty *pr; + JSShapeProperty *prs; + JSValue val; + + if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT) + return NULL; + prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name); + if (!prs) + return NULL; + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) + return NULL; + val = pr->u.value; + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) + return NULL; + return JS_ToCString(ctx, val); +} + +#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0) +/* only taken into account if filename is provided */ +#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1) + +/* if filename != NULL, an additional level is added with the filename + and line number information (used for parse error). */ +static void build_backtrace(JSContext *ctx, JSValue error_obj, + const char *filename, int line_num, int col_num, + int backtrace_flags) +{ + JSStackFrame *sf; + JSValue stack, prepare, saved_exception; + DynBuf dbuf; + const char *func_name_str; + const char *str1; + JSObject *p; + JSFunctionBytecode *b; + BOOL backtrace_barrier, has_prepare; + JSRuntime *rt; + JSCallSiteData csd[64]; + uint32_t i; + int stack_trace_limit; + + stack_trace_limit = ctx->error_stack_trace_limit; + stack_trace_limit = min_int(stack_trace_limit, countof(csd)); + stack_trace_limit = max_int(stack_trace_limit, 0); + rt = ctx->rt; + has_prepare = FALSE; + i = 0; + + if (!rt->in_prepare_stack_trace && !JS_IsNull(ctx->error_ctor)) { + prepare = js_dup(ctx->error_prepare_stack); + has_prepare = JS_IsFunction(ctx, prepare); + rt->in_prepare_stack_trace = TRUE; + } + + if (has_prepare) { + saved_exception = rt->current_exception; + rt->current_exception = JS_NULL; + if (stack_trace_limit == 0) + goto done; + if (filename) + js_new_callsite_data2(ctx, &csd[i++], filename, line_num, col_num); + } else { + js_dbuf_init(ctx, &dbuf); + if (stack_trace_limit == 0) + goto done; + if (filename) { + i++; + dbuf_printf(&dbuf, " at %s", filename); + if (line_num != -1) + dbuf_printf(&dbuf, ":%d:%d", line_num, col_num); + dbuf_putc(&dbuf, '\n'); + } + } + + if (filename && (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)) + goto done; + + for (sf = rt->current_stack_frame; sf != NULL && i < stack_trace_limit; sf = sf->prev_frame) { + if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) { + backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL; + continue; + } + + p = JS_VALUE_GET_OBJ(sf->cur_func); + b = NULL; + backtrace_barrier = FALSE; + + if (js_class_has_bytecode(p->class_id)) { + b = p->u.func.function_bytecode; + backtrace_barrier = b->backtrace_barrier; + } + + if (has_prepare) { + js_new_callsite_data(ctx, &csd[i], sf); + } else { + /* func_name_str is UTF-8 encoded if needed */ + func_name_str = get_func_name(ctx, sf->cur_func); + if (!func_name_str || func_name_str[0] == '\0') + str1 = ""; + else + str1 = func_name_str; + dbuf_printf(&dbuf, " at %s", str1); + JS_FreeCString(ctx, func_name_str); + + if (b) { + const char *atom_str; + int line_num1, col_num1; + + /* Bytecode functions must have cur_pc set in the stack frame. */ + if (sf->cur_pc == NULL) + abort(); + + line_num1 = find_line_num(ctx, b, + sf->cur_pc - b->byte_code_buf - 1, + &col_num1); + atom_str = JS_AtomToCString(ctx, b->filename); + dbuf_printf(&dbuf, " (%s", atom_str ? atom_str : ""); + JS_FreeCString(ctx, atom_str); + if (line_num1 != -1) + dbuf_printf(&dbuf, ":%d:%d", line_num1, col_num1); + dbuf_putc(&dbuf, ')'); + } else { + dbuf_printf(&dbuf, " (native)"); + } + dbuf_putc(&dbuf, '\n'); + } + i++; + + /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */ + if (backtrace_barrier) + break; + } + done: + if (has_prepare) { + int j = 0; + stack = JS_NewArray(ctx); + if (JS_IsException(stack)) { + stack = JS_NULL; + } else { + for (; j < i; j++) { + JSValue v = js_new_callsite(ctx, &csd[j]); + if (JS_IsException(v)) + break; + if (JS_DefinePropertyValueUint32(ctx, stack, j, v, JS_PROP_C_W_E) < 0) { + JS_FreeValue(ctx, v); + break; + } + } + } + // Clear the csd's we didn't use in case of error. + for (int k = j; k < i; k++) { + JS_FreeValue(ctx, csd[k].filename); + JS_FreeValue(ctx, csd[k].func); + JS_FreeValue(ctx, csd[k].func_name); + } + JSValue args[] = { + error_obj, + stack, + }; + JSValue stack2 = JS_Call(ctx, prepare, ctx->error_ctor, countof(args), args); + JS_FreeValue(ctx, stack); + if (JS_IsException(stack2)) + stack = JS_NULL; + else + stack = stack2; + JS_FreeValue(ctx, prepare); + JS_FreeValue(ctx, rt->current_exception); + rt->current_exception = saved_exception; + } else { + if (dbuf_error(&dbuf)) + stack = JS_NULL; + else + stack = JS_NewStringLen(ctx, (char *)dbuf.buf, dbuf.size); + dbuf_free(&dbuf); + } + + rt->in_prepare_stack_trace = FALSE; + JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, stack, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); +} + +/* Note: it is important that no exception is returned by this function */ +static BOOL is_backtrace_needed(JSContext *ctx, JSValue obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != JS_CLASS_ERROR) + return FALSE; + if (find_own_property1(p, JS_ATOM_stack)) + return FALSE; + return TRUE; +} + +JSValue JS_NewError(JSContext *ctx) +{ + return JS_NewObjectClass(ctx, JS_CLASS_ERROR); +} + +/* fmt and arguments may be pure ASCII or UTF-8 encoded contents */ +static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, + const char *fmt, va_list ap, BOOL add_backtrace) +{ + char buf[256]; + JSValue obj, ret, msg; + + vsnprintf(buf, sizeof(buf), fmt, ap); + if (error_num == JS_PLAIN_ERROR) + obj = JS_NewError(ctx); + else + obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num], + JS_CLASS_ERROR); + if (unlikely(JS_IsException(obj))) { + /* out of memory: throw JS_NULL to avoid recursing */ + obj = JS_NULL; + } else { + msg = JS_NewString(ctx, buf); + if (JS_IsException(msg)) + msg = JS_NewString(ctx, "Invalid error message"); + if (!JS_IsException(msg)) { + JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } + } + if (add_backtrace) { + build_backtrace(ctx, obj, NULL, 0, 0, 0); + } + ret = JS_Throw(ctx, obj); + return ret; +} + +static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, + const char *fmt, va_list ap) +{ + JSRuntime *rt = ctx->rt; + JSStackFrame *sf; + BOOL add_backtrace; + + /* the backtrace is added later if called from a bytecode function */ + sf = rt->current_stack_frame; + add_backtrace = !rt->in_out_of_memory && + (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL)); + return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace); +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowPlainError(JSContext *ctx, const char *fmt, ...) +{ + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_PLAIN_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...) +{ + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...) +{ + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); + va_end(ap); + return val; +} + +static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...) +{ + va_list ap; + + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + va_start(ap, fmt); + JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap); + va_end(ap); + return -1; + } else { + return FALSE; + } +} + +/* never use it directly */ +static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowTypeError(ctx, fmt, + JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); +} + +/* never use it directly */ +static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowSyntaxError(ctx, fmt, + JS_AtomGetStr(ctx, buf, sizeof(buf), atom)); +} + +/* %s is replaced by 'atom'. The macro is used so that gcc can check + the format string. */ +#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "") +#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "") + +static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) +{ + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom); + return -1; + } else { + return FALSE; + } +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...) +{ + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...) +{ + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...) +{ + JSValue val; + va_list ap; + + va_start(ap, fmt); + val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap); + va_end(ap); + return val; +} + +JSValue JS_ThrowOutOfMemory(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + if (!rt->in_out_of_memory) { + rt->in_out_of_memory = TRUE; + JS_ThrowInternalError(ctx, "out of memory"); + rt->in_out_of_memory = FALSE; + } + return JS_EXCEPTION; +} + +static JSValue JS_ThrowStackOverflow(JSContext *ctx) +{ + return JS_ThrowRangeError(ctx, "Maximum call stack size exceeded"); +} + +static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not an object"); +} + +static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx) +{ + return JS_ThrowTypeError(ctx, "not a symbol"); +} + +static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowReferenceError(ctx, "%s is not defined", + JS_AtomGetStr(ctx, buf, sizeof(buf), name)); +} + +static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + return JS_ThrowReferenceError(ctx, "%s is not initialized", + name == JS_ATOM_NULL ? "lexical variable" : + JS_AtomGetStr(ctx, buf, sizeof(buf), name)); +} + +static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx, + JSFunctionBytecode *b, + int idx, BOOL is_ref) +{ + JSAtom atom = JS_ATOM_NULL; + if (is_ref) { + atom = b->closure_var[idx].var_name; + } else { + /* not present if the function is stripped and contains no eval() */ + if (b->vardefs) + atom = b->vardefs[b->arg_count + idx].var_name; + } + return JS_ThrowReferenceErrorUninitialized(ctx, atom); +} + +static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) +{ + JSRuntime *rt = ctx->rt; + JSAtom name; + name = rt->class_array[class_id].class_name; + return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); +} + +static no_inline __exception int __js_poll_interrupts(JSContext *ctx) +{ + JSRuntime *rt = ctx->rt; + ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; + if (rt->interrupt_handler) { + if (rt->interrupt_handler(rt, rt->interrupt_opaque)) { + /* XXX: should set a specific flag to avoid catching */ + JS_ThrowInternalError(ctx, "interrupted"); + JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE); + return -1; + } + } + return 0; +} + +static inline __exception int js_poll_interrupts(JSContext *ctx) +{ + if (unlikely(--ctx->interrupt_counter <= 0)) { + return __js_poll_interrupts(ctx); + } else { + return 0; + } +} + +/* return -1 (exception) or TRUE/FALSE */ +static int JS_SetPrototypeInternal(JSContext *ctx, JSValue obj, + JSValue proto_val, + BOOL throw_flag) +{ + JSObject *proto, *p, *p1; + JSShape *sh; + + if (throw_flag) { + if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL || + JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED) + goto not_obj; + } else { + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + goto not_obj; + } + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) { + if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) { + not_obj: + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + proto = NULL; + } else { + proto = JS_VALUE_GET_OBJ(proto_val); + } + + if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return TRUE; + + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag); + sh = p->shape; + if (sh->proto == proto) + return TRUE; + if (p == JS_VALUE_GET_OBJ(ctx->class_proto[JS_CLASS_OBJECT])) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "'Immutable prototype object \'Object.prototype\' cannot have their prototype set'"); + return -1; + } + return FALSE; + } + if (!p->extensible) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "object is not extensible"); + return -1; + } else { + return FALSE; + } + } + if (proto) { + /* check if there is a cycle */ + p1 = proto; + do { + if (p1 == p) { + if (throw_flag) { + JS_ThrowTypeError(ctx, "circular prototype chain"); + return -1; + } else { + return FALSE; + } + } + /* Note: for Proxy objects, proto is NULL */ + p1 = p1->shape->proto; + } while (p1 != NULL); + js_dup(proto_val); + } + + if (js_shape_prepare_update(ctx, p, NULL)) + return -1; + sh = p->shape; + if (sh->proto) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); + sh->proto = proto; + return TRUE; +} + +/* return -1 (exception) or TRUE/FALSE */ +int JS_SetPrototype(JSContext *ctx, JSValue obj, JSValue proto_val) +{ + return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE); +} + +/* Only works for primitive types, otherwise return JS_NULL. */ +static JSValue JS_GetPrototypePrimitive(JSContext *ctx, JSValue val) +{ + switch(JS_VALUE_GET_NORM_TAG(val)) { + case JS_TAG_BIG_INT: + val = ctx->class_proto[JS_CLASS_BIG_INT]; + break; + case JS_TAG_INT: + case JS_TAG_FLOAT64: + val = ctx->class_proto[JS_CLASS_NUMBER]; + break; + case JS_TAG_BOOL: + val = ctx->class_proto[JS_CLASS_BOOLEAN]; + break; + case JS_TAG_STRING: + val = ctx->class_proto[JS_CLASS_STRING]; + break; + case JS_TAG_SYMBOL: + val = ctx->class_proto[JS_CLASS_SYMBOL]; + break; + case JS_TAG_OBJECT: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + default: + val = JS_NULL; + break; + } + return val; +} + +/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */ +JSValue JS_GetPrototype(JSContext *ctx, JSValue obj) +{ + JSValue val; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) { + val = js_proxy_getPrototypeOf(ctx, obj); + } else { + p = p->shape->proto; + if (!p) + val = JS_NULL; + else + val = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + } + } else { + val = js_dup(JS_GetPrototypePrimitive(ctx, obj)); + } + return val; +} + +static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj) +{ + JSValue obj1; + obj1 = JS_GetPrototype(ctx, obj); + JS_FreeValue(ctx, obj); + return obj1; +} + +int JS_GetLength(JSContext *ctx, JSValue obj, int64_t *pres) { + return js_get_length64(ctx, pres, obj); +} + +/* return TRUE, FALSE or (-1) in case of exception */ +static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValue val, + JSValue obj) +{ + JSValue obj_proto; + JSObject *proto; + const JSObject *p, *proto1; + BOOL ret; + + if (!JS_IsFunction(ctx, obj)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_BOUND_FUNCTION) { + JSBoundFunction *s = p->u.bound_function; + return JS_IsInstanceOf(ctx, val, s->func_obj); + } + + /* Only explicitly boxed values are instances of constructors */ + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype); + if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) { + if (!JS_IsException(obj_proto)) + JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object"); + ret = -1; + goto done; + } + proto = JS_VALUE_GET_OBJ(obj_proto); + p = JS_VALUE_GET_OBJ(val); + for(;;) { + proto1 = p->shape->proto; + if (!proto1) { + /* slow case if proxy in the prototype chain */ + if (unlikely(p->class_id == JS_CLASS_PROXY)) { + JSValue obj1; + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p)); + for(;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsException(obj1)) { + ret = -1; + break; + } + if (JS_IsNull(obj1)) { + ret = FALSE; + break; + } + if (proto == JS_VALUE_GET_OBJ(obj1)) { + JS_FreeValue(ctx, obj1); + ret = TRUE; + break; + } + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + ret = -1; + break; + } + } + } else { + ret = FALSE; + } + break; + } + p = proto1; + if (proto == p) { + ret = TRUE; + break; + } + } +done: + JS_FreeValue(ctx, obj_proto); + return ret; +} + +/* return TRUE, FALSE or (-1) in case of exception */ +int JS_IsInstanceOf(JSContext *ctx, JSValue val, JSValue obj) +{ + JSValue method; + + if (!JS_IsObject(obj)) + goto fail; + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance); + if (JS_IsException(method)) + return -1; + if (!JS_IsNull(method) && !JS_IsUndefined(method)) { + JSValue ret; + ret = JS_CallFree(ctx, method, obj, 1, &val); + return JS_ToBoolFree(ctx, ret); + } + + /* legacy case */ + if (!JS_IsFunction(ctx, obj)) { + fail: + JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand"); + return -1; + } + return JS_OrdinaryIsInstanceOf(ctx, val, obj); +} + +/* return the value associated to the autoinit property or an exception */ +typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque); + +static JSAutoInitFunc *js_autoinit_func_table[] = { + js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */ + js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */ + JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */ +}; + +/* warning: 'prs' is reallocated after it */ +static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop, + JSProperty *pr, JSShapeProperty *prs) +{ + JSValue val; + JSContext *realm; + JSAutoInitFunc *func; + + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + + realm = js_autoinit_get_realm(pr); + func = js_autoinit_func_table[js_autoinit_get_id(pr)]; + /* 'func' shall not modify the object properties 'pr' */ + val = func(realm, p, prop, pr->u.init.opaque); + js_autoinit_free(ctx->rt, pr); + prs->flags &= ~JS_PROP_TMASK; + pr->u.value = JS_UNDEFINED; + if (JS_IsException(val)) + return -1; + pr->u.value = val; + return 0; +} + +static JSValue JS_GetPropertyInternal2(JSContext *ctx, JSValue obj, + JSAtom prop, JSValue this_obj, + JSInlineCache* ic, BOOL throw_ref_error) +{ + JSObject *p; + JSProperty *pr; + JSShapeProperty *prs; + uint32_t tag, offset, proto_depth; + + offset = proto_depth = 0; + tag = JS_VALUE_GET_TAG(obj); + if (unlikely(tag != JS_TAG_OBJECT)) { + switch(tag) { + case JS_TAG_NULL: + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop); + case JS_TAG_UNDEFINED: + return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop); + case JS_TAG_EXCEPTION: + return JS_EXCEPTION; + case JS_TAG_STRING: + { + JSString *p1 = JS_VALUE_GET_STRING(obj); + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx, ch; + idx = __JS_AtomToUInt32(prop); + if (idx < p1->len) { + ch = string_get(p1, idx); + return js_new_string_char(ctx, ch); + } + } else if (prop == JS_ATOM_length) { + return js_int32(p1->len); + } + } + break; + default: + break; + } + /* cannot raise an exception */ + p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj)); + if (!p) + return JS_UNDEFINED; + } else { + p = JS_VALUE_GET_OBJ(obj); + } + + for(;;) { + prs = find_own_property_ic(&pr, p, prop, &offset); + if (prs) { + /* found */ + if (unlikely(prs->flags & JS_PROP_TMASK)) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + if (unlikely(!pr->u.getset.getter)) { + return JS_UNDEFINED; + } else { + JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter); + /* Note: the field could be removed in the getter */ + func = js_dup(func); + return JS_CallFree(ctx, func, this_obj, 0, NULL); + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + JSValue val = *pr->u.var_ref->pvalue; + if (unlikely(JS_IsUninitialized(val))) + return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return js_dup(val); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return JS_EXCEPTION; + continue; + } + } else { + if (ic && proto_depth == 0 && p->shape->is_hashed) { + ic->updated = TRUE; + ic->updated_offset = add_ic_slot(ctx, ic, prop, p, offset); + } + return js_dup(pr->u.value); + } + } + if (unlikely(p->is_exotic)) { + /* exotic behaviors */ + if (p->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + /* we avoid duplicating the code */ + return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + return JS_UNDEFINED; + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + int ret; + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return JS_EXCEPTION; + return JS_UNDEFINED; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em) { + if (em->get_property) { + JSValue obj1, retval; + /* XXX: should pass throw_ref_error */ + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + retval = em->get_property(ctx, obj1, prop, this_obj); + JS_FreeValue(ctx, obj1); + return retval; + } + if (em->get_own_property) { + JSPropertyDescriptor desc; + int ret; + JSValue obj1; + + /* Note: if 'p' is a prototype, it can be + freed in the called function */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->get_own_property(ctx, &desc, obj1, prop); + JS_FreeValue(ctx, obj1); + if (ret < 0) + return JS_EXCEPTION; + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JS_FreeValue(ctx, desc.setter); + return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL); + } else { + return desc.value; + } + } + } + } + } + } + proto_depth++; + p = p->shape->proto; + if (!p) + break; + } + if (unlikely(throw_ref_error)) { + return JS_ThrowReferenceErrorNotDefined(ctx, prop); + } else { + return JS_UNDEFINED; + } +} + +static JSValue JS_GetPropertyInternal(JSContext *ctx, JSValue obj, + JSAtom prop, JSValue this_obj, + BOOL throw_ref_error) +{ + return JS_GetPropertyInternal2(ctx, obj, prop, this_obj, NULL, throw_ref_error); +} + +JSValue JS_GetProperty(JSContext *ctx, JSValue this_obj, JSAtom prop) +{ + return JS_GetPropertyInternal2(ctx, this_obj, prop, this_obj, NULL, FALSE); +} + +static JSValue JS_GetPropertyInternalWithIC(JSContext *ctx, JSValue obj, + JSAtom prop, JSValue this_obj, + JSInlineCache *ic, int32_t offset, + BOOL throw_ref_error) +{ + uint32_t tag; + JSObject *p; + tag = JS_VALUE_GET_TAG(obj); + if (unlikely(tag != JS_TAG_OBJECT)) + goto slow_path; + p = JS_VALUE_GET_OBJ(obj); + offset = get_ic_prop_offset(ic, offset, p->shape); + if (likely(offset >= 0)) + return js_dup(p->prop[offset].u.value); +slow_path: + return JS_GetPropertyInternal2(ctx, obj, prop, this_obj, ic, throw_ref_error); +} + +static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom) +{ + return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist", + atom); +} + +/* Private fields can be added even on non extensible objects or + Proxies */ +static int JS_DefinePrivateField(JSContext *ctx, JSValue obj, + JSValue name, JSValue val) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { + JS_ThrowTypeErrorNotASymbol(ctx); + goto fail; + } + prop = js_symbol_to_atom(ctx, name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists", + prop); + goto fail; + } + pr = add_property(ctx, p, prop, JS_PROP_C_W_E); + if (unlikely(!pr)) { + fail: + JS_FreeValue(ctx, val); + return -1; + } + pr->u.value = val; + return 0; +} + +static JSValue JS_GetPrivateField(JSContext *ctx, JSValue obj, + JSValue name) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return JS_ThrowTypeErrorNotAnObject(ctx); + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) + return JS_ThrowTypeErrorNotASymbol(ctx); + prop = js_symbol_to_atom(ctx, name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + return JS_EXCEPTION; + } + return js_dup(pr->u.value); +} + +static int JS_SetPrivateField(JSContext *ctx, JSValue obj, + JSValue name, JSValue val) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSAtom prop; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto fail; + } + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) { + JS_ThrowTypeErrorNotASymbol(ctx); + goto fail; + } + prop = js_symbol_to_atom(ctx, name); + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, prop); + if (!prs) { + JS_ThrowTypeErrorPrivateNotFound(ctx, prop); + fail: + JS_FreeValue(ctx, val); + return -1; + } + set_value(ctx, &pr->u.value, val); + return 0; +} + +static int JS_AddBrand(JSContext *ctx, JSValue obj, JSValue home_obj) +{ + JSObject *p, *p1; + JSShapeProperty *prs; + JSProperty *pr; + JSValue brand; + JSAtom brand_atom; + + if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(home_obj); + prs = find_own_property(&pr, p, JS_ATOM_Private_brand); + if (!prs) { + brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(brand)) + return -1; + /* if the brand is not present, add it */ + pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E); + if (!pr) { + JS_FreeValue(ctx, brand); + return -1; + } + pr->u.value = js_dup(brand); + } else { + brand = js_dup(pr->u.value); + } + brand_atom = js_symbol_to_atom(ctx, brand); + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + JS_ThrowTypeErrorNotAnObject(ctx); + JS_FreeAtom(ctx, brand_atom); + return -1; + } + p1 = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p1, brand_atom); + if (unlikely(prs)) { + JS_FreeAtom(ctx, brand_atom); + JS_ThrowTypeError(ctx, "private method is already present"); + return -1; + } + pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E); + JS_FreeAtom(ctx, brand_atom); + if (!pr) + return -1; + pr->u.value = JS_UNDEFINED; + return 0; +} + +static int JS_CheckBrand(JSContext *ctx, JSValue obj, JSValue func) +{ + JSObject *p, *p1, *home_obj; + JSShapeProperty *prs; + JSProperty *pr; + JSValue brand; + + /* get the home object of 'func' */ + if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) { + not_obj: + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p1 = JS_VALUE_GET_OBJ(func); + if (!js_class_has_bytecode(p1->class_id)) + goto not_obj; + home_obj = p1->u.func.home_object; + if (!home_obj) + goto not_obj; + prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand); + if (!prs) { + JS_ThrowTypeError(ctx, "expecting private field"); + return -1; + } + brand = pr->u.value; + /* safety check */ + if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL)) + goto not_obj; + + /* get the brand array of 'obj' */ + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + goto not_obj; + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, brand)); + if (!prs) { + JS_ThrowTypeError(ctx, "invalid brand on object"); + return -1; + } + return 0; +} + +static uint32_t js_string_obj_get_length(JSContext *ctx, + JSValue obj) +{ + JSObject *p; + JSString *p1; + uint32_t len = 0; + + /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ + p = JS_VALUE_GET_OBJ(obj); + if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { + p1 = JS_VALUE_GET_STRING(p->u.object_data); + len = p1->len; + } + return len; +} + +static int num_keys_cmp(const void *p1, const void *p2, void *opaque) +{ + JSContext *ctx = opaque; + JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom; + JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom; + uint32_t v1, v2; + BOOL atom1_is_integer, atom2_is_integer; + + atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1); + atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2); + assert(atom1_is_integer && atom2_is_integer); + if (v1 < v2) + return -1; + else if (v1 == v2) + return 0; + else + return 1; +} + +static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) +{ + uint32_t i; + if (tab) { + for(i = 0; i < len; i++) + JS_FreeAtom(ctx, tab[i].atom); + js_free(ctx, tab); + } +} + +/* return < 0 in case if exception, 0 if OK. ptab and its atoms must + be freed by the user. */ +static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, + JSPropertyEnum **ptab, + uint32_t *plen, + JSObject *p, int flags) +{ + int i, j; + JSShape *sh; + JSShapeProperty *prs; + JSPropertyEnum *tab_atom, *tab_exotic; + JSAtom atom; + uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count; + uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count; + BOOL is_enumerable, num_sorted; + uint32_t num_key; + JSAtomKindEnum kind; + + /* clear pointer for consistency in case of failure */ + *ptab = NULL; + *plen = 0; + + /* compute the number of returned properties */ + num_keys_count = 0; + str_keys_count = 0; + sym_keys_count = 0; + exotic_keys_count = 0; + exotic_count = 0; + tab_exotic = NULL; + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + atom = prs->atom; + if (atom != JS_ATOM_NULL) { + is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + /* need to raise an exception in case of the module + name space (implicit GetOwnProperty) */ + if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) && + (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) { + JSVarRef *var_ref = p->prop[i].u.var_ref; + if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + } + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + num_keys_count++; + } else if (kind == JS_ATOM_KIND_STRING) { + str_keys_count++; + } else { + sym_keys_count++; + } + } + } + } + + if (p->is_exotic) { + if (p->fast_array) { + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += p->u.array.count; + } + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->get_own_property_names) { + if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count, + JS_MKPTR(JS_TAG_OBJECT, p))) + return -1; + for(i = 0; i < exotic_count; i++) { + atom = tab_exotic[i].atom; + kind = JS_AtomGetKind(ctx, atom); + if (((flags >> kind) & 1) != 0) { + is_enumerable = FALSE; + if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) { + JSPropertyDescriptor desc; + int res; + /* set the "is_enumerable" field if necessary */ + res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom); + if (res < 0) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + return -1; + } + if (res) { + is_enumerable = + ((desc.flags & JS_PROP_ENUMERABLE) != 0); + js_free_desc(ctx, &desc); + } + tab_exotic[i].is_enumerable = is_enumerable; + } + if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) { + exotic_keys_count++; + } + } + } + } + } + } + + /* fill them */ + + atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count; + /* avoid allocating 0 bytes */ + tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1)); + if (!tab_atom) { + js_free_prop_enum(ctx, tab_exotic, exotic_count); + return -1; + } + + num_index = 0; + str_index = num_keys_count; + sym_index = str_index + str_keys_count; + + num_sorted = TRUE; + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + atom = prs->atom; + if (atom != JS_ATOM_NULL) { + is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0); + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) { + j = num_index++; + num_sorted = FALSE; + } else if (kind == JS_ATOM_KIND_STRING) { + j = str_index++; + } else { + j = sym_index++; + } + tab_atom[j].atom = JS_DupAtom(ctx, atom); + tab_atom[j].is_enumerable = is_enumerable; + } + } + } + + if (p->is_exotic) { + int len; + if (p->fast_array) { + if (flags & JS_GPN_STRING_MASK) { + len = p->u.array.count; + goto add_array_keys; + } + } else if (p->class_id == JS_CLASS_STRING) { + if (flags & JS_GPN_STRING_MASK) { + len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + add_array_keys: + for(i = 0; i < len; i++) { + tab_atom[num_index].atom = __JS_AtomFromUInt32(i); + if (tab_atom[num_index].atom == JS_ATOM_NULL) { + js_free_prop_enum(ctx, tab_atom, num_index); + return -1; + } + tab_atom[num_index].is_enumerable = TRUE; + num_index++; + } + } + } else { + /* Note: exotic keys are not reordered and comes after the object own properties. */ + for(i = 0; i < exotic_count; i++) { + atom = tab_exotic[i].atom; + is_enumerable = tab_exotic[i].is_enumerable; + kind = JS_AtomGetKind(ctx, atom); + if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) && + ((flags >> kind) & 1) != 0) { + tab_atom[sym_index].atom = atom; + tab_atom[sym_index].is_enumerable = is_enumerable; + sym_index++; + } else { + JS_FreeAtom(ctx, atom); + } + } + js_free(ctx, tab_exotic); + } + } + + assert(num_index == num_keys_count); + assert(str_index == num_keys_count + str_keys_count); + assert(sym_index == atom_count); + + if (num_keys_count != 0 && !num_sorted) { + rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp, + ctx); + } + *ptab = tab_atom; + *plen = atom_count; + return 0; +} + +int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, + uint32_t *plen, JSValue obj, int flags) +{ + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen, + JS_VALUE_GET_OBJ(obj), flags); +} + +/* Return -1 if exception, + FALSE if the property does not exist, TRUE if it exists. If TRUE is + returned, the property descriptor 'desc' is filled present. */ +static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc, + JSObject *p, JSAtom prop) +{ + JSShapeProperty *prs; + JSProperty *pr; + +retry: + prs = find_own_property(&pr, p, prop); + if (prs) { + if (desc) { + desc->flags = prs->flags & JS_PROP_C_W_E; + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + desc->value = JS_UNDEFINED; + if (unlikely(prs->flags & JS_PROP_TMASK)) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + desc->flags |= JS_PROP_GETSET; + if (pr->u.getset.getter) + desc->getter = js_dup(JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + desc->setter = js_dup(JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + JSValue val = *pr->u.var_ref->pvalue; + if (unlikely(JS_IsUninitialized(val))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + desc->value = js_dup(val); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return -1; + goto retry; + } + } else { + desc->value = js_dup(pr->u.value); + } + } else { + /* for consistency, send the exception even if desc is NULL */ + if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) { + if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* nothing to do: delay instantiation until actual value and/or attributes are read */ + } + } + return TRUE; + } + if (p->is_exotic) { + if (p->fast_array) { + /* specific case for fast arrays */ + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx; + idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + if (desc) { + desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | + JS_PROP_CONFIGURABLE; + desc->getter = JS_UNDEFINED; + desc->setter = JS_UNDEFINED; + desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); + } + return TRUE; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->get_own_property) { + return em->get_own_property(ctx, desc, + JS_MKPTR(JS_TAG_OBJECT, p), prop); + } + } + } + return FALSE; +} + +int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc, + JSValue obj, JSAtom prop) +{ + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop); +} + +void JS_FreePropertyEnum(JSContext *ctx, JSPropertyEnum *tab, + uint32_t len) +{ + js_free_prop_enum(ctx, tab, len); +} + +/* return -1 if exception (Proxy object only) or TRUE/FALSE */ +int JS_IsExtensible(JSContext *ctx, JSValue obj) +{ + JSObject *p; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_isExtensible(ctx, obj); + else + return p->extensible; +} + +/* return -1 if exception (Proxy object only) or TRUE/FALSE */ +int JS_PreventExtensions(JSContext *ctx, JSValue obj) +{ + JSObject *p; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_preventExtensions(ctx, obj); + p->extensible = FALSE; + return TRUE; +} + +/* return -1 if exception otherwise TRUE or FALSE */ +int JS_HasProperty(JSContext *ctx, JSValue obj, JSAtom prop) +{ + JSObject *p; + int ret; + JSValue obj1; + + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + for(;;) { + if (p->is_exotic) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->has_property) { + /* has_property can free the prototype */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + ret = em->has_property(ctx, obj1, prop); + JS_FreeValue(ctx, obj1); + return ret; + } + } + /* JS_GetOwnPropertyInternal can free the prototype */ + js_dup(JS_MKPTR(JS_TAG_OBJECT, p)); + ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ret != 0) + return ret; + if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return -1; + return FALSE; + } + } + p = p->shape->proto; + if (!p) + break; + } + return FALSE; +} + +/* val must be a symbol */ +static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val) +{ + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + return js_get_atom_index(ctx->rt, p); +} + +/* return JS_ATOM_NULL in case of exception */ +JSAtom JS_ValueToAtom(JSContext *ctx, JSValue val) +{ + JSAtom atom; + uint32_t tag; + tag = JS_VALUE_GET_TAG(val); + if (tag == JS_TAG_INT && + (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) { + /* fast path for integer values */ + atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val)); + } else if (tag == JS_TAG_SYMBOL) { + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p)); + } else { + JSValue str; + str = JS_ToPropertyKey(ctx, val); + if (JS_IsException(str)) + return JS_ATOM_NULL; + if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) { + atom = js_symbol_to_atom(ctx, str); + } else { + atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str)); + } + } + return atom; +} + +static BOOL js_get_fast_array_element(JSContext *ctx, JSObject *p, + uint32_t idx, JSValue *pval) +{ + switch(p->class_id) { + case JS_CLASS_ARRAY: + case JS_CLASS_ARGUMENTS: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_dup(p->u.array.u.values[idx]); + return TRUE; + case JS_CLASS_INT8_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_int32(p->u.array.u.int8_ptr[idx]); + return TRUE; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_UINT8_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_int32(p->u.array.u.uint8_ptr[idx]); + return TRUE; + case JS_CLASS_INT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_int32(p->u.array.u.int16_ptr[idx]); + return TRUE; + case JS_CLASS_UINT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_int32(p->u.array.u.uint16_ptr[idx]); + return TRUE; + case JS_CLASS_INT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_int32(p->u.array.u.int32_ptr[idx]); + return TRUE; + case JS_CLASS_UINT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_uint32(p->u.array.u.uint32_ptr[idx]); + return TRUE; + case JS_CLASS_BIG_INT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); + return TRUE; + case JS_CLASS_BIG_UINT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); + return TRUE; + case JS_CLASS_FLOAT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_float64(p->u.array.u.float_ptr[idx]); + return TRUE; + case JS_CLASS_FLOAT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) return FALSE; + *pval = js_float64(p->u.array.u.double_ptr[idx]); + return TRUE; + default: + return FALSE; + } +} + +static JSValue JS_GetPropertyValue(JSContext *ctx, JSValue this_obj, + JSValue prop) +{ + JSAtom atom; + JSValue ret; + + if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && + JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { + JSObject *p = JS_VALUE_GET_OBJ(this_obj); + uint32_t idx = JS_VALUE_GET_INT(prop); + JSValue val; + /* fast path for array and typed array access */ + if (js_get_fast_array_element(ctx, p, idx, &val)) + return val; + } + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) + return JS_EXCEPTION; + ret = JS_GetProperty(ctx, this_obj, atom); + JS_FreeAtom(ctx, atom); + return ret; +} + +JSValue JS_GetPropertyUint32(JSContext *ctx, JSValue this_obj, + uint32_t idx) +{ + return JS_GetPropertyInt64(ctx, this_obj, idx); +} + +/* Check if an object has a generalized numeric property. Return value: + -1 for exception, *pval set to JS_EXCEPTION + TRUE if property exists, stored into *pval, + FALSE if property does not exist. *pval set to JS_UNDEFINED. + */ +static int JS_TryGetPropertyInt64(JSContext *ctx, JSValue obj, int64_t idx, JSValue *pval) +{ + JSValue val; + JSAtom prop; + int present; + + if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT && + (uint64_t)idx <= INT32_MAX)) { + /* fast path for array and typed array access */ + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (js_get_fast_array_element(ctx, p, idx, pval)) + return TRUE; + } + val = JS_EXCEPTION; + present = -1; + prop = JS_NewAtomInt64(ctx, idx); + if (likely(prop != JS_ATOM_NULL)) { + present = JS_HasProperty(ctx, obj, prop); + if (present > 0) { + val = JS_GetProperty(ctx, obj, prop); + if (unlikely(JS_IsException(val))) + present = -1; + } else if (present == FALSE) { + val = JS_UNDEFINED; + } + JS_FreeAtom(ctx, prop); + } + *pval = val; + return present; +} + +JSValue JS_GetPropertyInt64(JSContext *ctx, JSValue obj, int64_t idx) +{ + JSAtom prop; + JSValue val; + + if (likely(JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT && + (uint64_t)idx <= INT32_MAX)) { + /* fast path for array and typed array access */ + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (js_get_fast_array_element(ctx, p, idx, &val)) + return val; + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) + return JS_EXCEPTION; + + val = JS_GetProperty(ctx, obj, prop); + JS_FreeAtom(ctx, prop); + return val; +} + +/* `prop` may be pure ASCII or UTF-8 encoded */ +JSValue JS_GetPropertyStr(JSContext *ctx, JSValue this_obj, + const char *prop) +{ + JSAtom atom; + JSValue ret; + atom = JS_NewAtom(ctx, prop); + ret = JS_GetProperty(ctx, this_obj, atom); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* Note: the property value is not initialized. Return NULL if memory + error. */ +static JSProperty *add_property(JSContext *ctx, + JSObject *p, JSAtom prop, int prop_flags) +{ + JSShape *sh, *new_sh; + + sh = p->shape; + if (sh->is_hashed) { + /* try to find an existing shape */ + new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags); + if (new_sh) { + /* matching shape found: use it */ + /* the property array may need to be resized */ + if (new_sh->prop_size != sh->prop_size) { + JSProperty *new_prop; + new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) * + new_sh->prop_size); + if (!new_prop) + return NULL; + p->prop = new_prop; + } + p->shape = js_dup_shape(new_sh); + js_free_shape(ctx->rt, sh); + return &p->prop[new_sh->prop_count - 1]; + } else if (sh->header.ref_count != 1) { + /* if the shape is shared, clone it */ + new_sh = js_clone_shape(ctx, sh); + if (!new_sh) + return NULL; + /* hash the cloned shape */ + new_sh->is_hashed = TRUE; + js_shape_hash_link(ctx->rt, new_sh); + js_free_shape(ctx->rt, p->shape); + p->shape = new_sh; + } + } + assert(p->shape->header.ref_count == 1); + if (add_shape_property(ctx, &p->shape, p, prop, prop_flags)) + return NULL; + return &p->prop[p->shape->prop_count - 1]; +} + +/* can be called on Array or Arguments objects. return < 0 if + memory alloc error. */ +static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, + JSObject *p) +{ + JSProperty *pr; + JSShape *sh; + JSValue *tab; + uint32_t i, len, new_count; + + if (js_shape_prepare_update(ctx, p, NULL)) + return -1; + len = p->u.array.count; + /* resize the properties once to simplify the error handling */ + sh = p->shape; + new_count = sh->prop_count + len; + if (new_count > sh->prop_size) { + if (resize_properties(ctx, &p->shape, p, new_count)) + return -1; + } + + tab = p->u.array.u.values; + for(i = 0; i < len; i++) { + /* add_property cannot fail here but + __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E); + pr->u.value = *tab++; + } + js_free(ctx, p->u.array.u.values); + p->u.array.count = 0; + p->u.array.u.values = NULL; /* fail safe */ + p->u.array.u1.size = 0; + p->fast_array = 0; + return 0; +} + +static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom) +{ + JSShape *sh; + JSShapeProperty *pr, *lpr, *prop; + JSProperty *pr1; + uint32_t lpr_idx; + intptr_t h, h1; + + redo: + sh = p->shape; + h1 = atom & sh->prop_hash_mask; + h = prop_hash_end(sh)[-h1 - 1]; + prop = get_shape_prop(sh); + lpr = NULL; + lpr_idx = 0; /* prevent warning */ + while (h != 0) { + pr = &prop[h - 1]; + if (likely(pr->atom == atom)) { + /* found ! */ + if (!(pr->flags & JS_PROP_CONFIGURABLE)) + return FALSE; + /* realloc the shape if needed */ + if (lpr) + lpr_idx = lpr - get_shape_prop(sh); + if (js_shape_prepare_update(ctx, p, &pr)) + return -1; + sh = p->shape; + /* remove property */ + if (lpr) { + lpr = get_shape_prop(sh) + lpr_idx; + lpr->hash_next = pr->hash_next; + } else { + prop_hash_end(sh)[-h1 - 1] = pr->hash_next; + } + sh->deleted_prop_count++; + /* free the entry */ + pr1 = &p->prop[h - 1]; + free_property(ctx->rt, pr1, pr->flags); + JS_FreeAtom(ctx, pr->atom); + /* put default values */ + pr->flags = 0; + pr->atom = JS_ATOM_NULL; + pr1->u.value = JS_UNDEFINED; + + /* compact the properties if too many deleted properties */ + if (sh->deleted_prop_count >= 8 && + sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) { + compact_properties(ctx, p); + } + return TRUE; + } + lpr = pr; + h = pr->hash_next; + } + + if (p->is_exotic) { + if (p->fast_array) { + uint32_t idx; + if (JS_AtomIsArrayIndex(ctx, &idx, atom) && + idx < p->u.array.count) { + if (p->class_id == JS_CLASS_ARRAY || + p->class_id == JS_CLASS_ARGUMENTS) { + /* Special case deleting the last element of a fast Array */ + if (idx == p->u.array.count - 1) { + JS_FreeValue(ctx, p->u.array.u.values[idx]); + p->u.array.count = idx; + return TRUE; + } + if (convert_fast_array_to_array(ctx, p)) + return -1; + goto redo; + } else { + return FALSE; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em && em->delete_property) { + return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom); + } + } + } + /* not found */ + return TRUE; +} + +static int call_setter(JSContext *ctx, JSObject *setter, + JSValue this_obj, JSValue val, int flags) +{ + JSValue ret, func; + if (likely(setter)) { + func = JS_MKPTR(JS_TAG_OBJECT, setter); + /* Note: the field could be removed in the setter */ + func = js_dup(func); + ret = JS_CallFree(ctx, func, this_obj, 1, &val); + JS_FreeValue(ctx, val); + if (JS_IsException(ret)) + return -1; + JS_FreeValue(ctx, ret); + return TRUE; + } else { + JS_FreeValue(ctx, val); + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeError(ctx, "no setter for property"); + return -1; + } + return FALSE; + } +} + +/* set the array length and remove the array elements if necessary. */ +static int set_array_length(JSContext *ctx, JSObject *p, JSValue val, + int flags) +{ + uint32_t len, idx, cur_len; + int i, ret; + + /* Note: this call can reallocate the properties of 'p' */ + ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE); + if (ret) + return -1; + /* JS_ToArrayLengthFree() must be done before the read-only test */ + if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE))) + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + + if (likely(p->fast_array)) { + uint32_t old_len = p->u.array.count; + if (len < old_len) { + for(i = len; i < old_len; i++) { + JS_FreeValue(ctx, p->u.array.u.values[i]); + } + p->u.array.count = len; + } + p->prop[0].u.value = js_uint32(len); + } else { + /* Note: length is always a uint32 because the object is an + array */ + JS_ToUint32(ctx, &cur_len, p->prop[0].u.value); + if (len < cur_len) { + uint32_t d; + JSShape *sh; + JSShapeProperty *pr; + + d = cur_len - len; + sh = p->shape; + if (d <= sh->prop_count) { + JSAtom atom; + + /* faster to iterate */ + while (cur_len > len) { + atom = JS_NewAtomUInt32(ctx, cur_len - 1); + ret = delete_property(ctx, p, atom); + JS_FreeAtom(ctx, atom); + if (unlikely(!ret)) { + /* unlikely case: property is not + configurable */ + break; + } + cur_len--; + } + } else { + /* faster to iterate thru all the properties. Need two + passes in case one of the property is not + configurable */ + cur_len = len; + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; + i++, pr++) { + if (pr->atom != JS_ATOM_NULL && + JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { + if (idx >= cur_len && + !(pr->flags & JS_PROP_CONFIGURABLE)) { + cur_len = idx + 1; + } + } + } + + for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; + i++, pr++) { + if (pr->atom != JS_ATOM_NULL && + JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) { + if (idx >= cur_len) { + /* remove the property */ + delete_property(ctx, p, pr->atom); + /* WARNING: the shape may have been modified */ + sh = p->shape; + pr = get_shape_prop(sh) + i; + } + } + } + } + } else { + cur_len = len; + } + set_value(ctx, &p->prop[0].u.value, js_uint32(cur_len)); + if (unlikely(cur_len > len)) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable"); + } + } + return TRUE; +} + +/* return -1 if exception */ +static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len) +{ + uint32_t new_size; + size_t slack; + JSValue *new_array_prop; + /* XXX: potential arithmetic overflow */ + new_size = max_int(new_len, p->u.array.u1.size * 3 / 2); + new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack); + if (!new_array_prop) + return -1; + new_size += slack / sizeof(*new_array_prop); + p->u.array.u.values = new_array_prop; + p->u.array.u1.size = new_size; + return 0; +} + +/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array = + TRUE and p->extensible = TRUE */ +static int add_fast_array_element(JSContext *ctx, JSObject *p, + JSValue val, int flags) +{ + uint32_t new_len, array_len; + /* extend the array by one */ + /* XXX: convert to slow array if new_len > 2^31-1 elements */ + new_len = p->u.array.count + 1; + /* update the length if necessary. We assume that if the length is + not an integer, then if it >= 2^31. */ + if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) { + array_len = JS_VALUE_GET_INT(p->prop[0].u.value); + if (new_len > array_len) { + if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + } + p->prop[0].u.value = js_int32(new_len); + } + } + if (unlikely(new_len > p->u.array.u1.size)) { + if (expand_fast_array(ctx, p, new_len)) { + JS_FreeValue(ctx, val); + return -1; + } + } + p->u.array.u.values[new_len - 1] = val; + p->u.array.count = new_len; + return TRUE; +} + +static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) +{ + JS_FreeValue(ctx, desc->getter); + JS_FreeValue(ctx, desc->setter); + JS_FreeValue(ctx, desc->value); +} + +/* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is + freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD, + JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set, + the new property is not added and an error is raised. + 'obj' must be an object when obj != this_obj. + */ +static int JS_SetPropertyInternal2(JSContext *ctx, JSValue obj, + JSAtom prop, JSValue val, JSValue this_obj, + int flags, JSInlineCache *ic) +{ + JSObject *p, *p1; + JSShapeProperty *prs; + JSProperty *pr; + JSPropertyDescriptor desc; + int ret; + uint32_t offset = 0; + + switch(JS_VALUE_GET_TAG(this_obj)) { + case JS_TAG_NULL: + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop); + goto fail; + case JS_TAG_UNDEFINED: + JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop); + goto fail; + case JS_TAG_OBJECT: + p = JS_VALUE_GET_OBJ(this_obj); + p1 = JS_VALUE_GET_OBJ(obj); + if (p == p1) + break; + goto retry2; + default: + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + obj = JS_GetPrototypePrimitive(ctx, obj); + p = NULL; + p1 = JS_VALUE_GET_OBJ(obj); + goto prototype_lookup; + } + +retry: + prs = find_own_property_ic(&pr, p1, prop, &offset); + if (prs) { + if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | + JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { + /* fast case */ + if (ic && p->shape->is_hashed) { + ic->updated = TRUE; + ic->updated_offset = add_ic_slot(ctx, ic, prop, p, offset); + } + set_value(ctx, &pr->u.value, val); + return TRUE; + } else if (prs->flags & JS_PROP_LENGTH) { + assert(p->class_id == JS_CLASS_ARRAY); + assert(prop == JS_ATOM_length); + return set_array_length(ctx, p, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + /* JS_PROP_WRITABLE is always true for variable + references, but they are write protected in module name + spaces. */ + if (p->class_id == JS_CLASS_MODULE_NS) + goto read_only_prop; + set_value(ctx, pr->u.var_ref->pvalue, val); + return TRUE; + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry (potentially useless) */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + goto fail; + goto retry; + } else { + goto read_only_prop; + } + } + + for(;;) { + if (p1->is_exotic) { + if (p1->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx < p1->u.array.count) { + if (unlikely(p == p1)) + return JS_SetPropertyValue(ctx, this_obj, js_int32(idx), val, flags); + else + break; + } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && + p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { + goto typed_array_oob; + } + } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY && + p1->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + goto fail; + typed_array_oob: + // per spec: evaluate value for side effects + if (p1->class_id == JS_CLASS_BIG_INT64_ARRAY || + p1->class_id == JS_CLASS_BIG_UINT64_ARRAY) { + int64_t v; + if (JS_ToBigInt64Free(ctx, &v, val)) + return -1; + } else { + val = JS_ToNumberFree(ctx, val); + JS_FreeValue(ctx, val); + if (JS_IsException(val)) + return -1; + } + return TRUE; + } + } + } else { + const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic; + if (em) { + JSValue obj1; + if (em->set_property) { + /* set_property can free the prototype */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p1)); + ret = em->set_property(ctx, obj1, prop, + val, this_obj, flags); + JS_FreeValue(ctx, obj1); + JS_FreeValue(ctx, val); + return ret; + } + if (em->get_own_property) { + /* get_own_property can free the prototype */ + obj1 = js_dup(JS_MKPTR(JS_TAG_OBJECT, p1)); + ret = em->get_own_property(ctx, &desc, + obj1, prop); + JS_FreeValue(ctx, obj1); + if (ret < 0) + goto fail; + if (ret) { + if (desc.flags & JS_PROP_GETSET) { + JSObject *setter; + if (JS_IsUndefined(desc.setter)) + setter = NULL; + else + setter = JS_VALUE_GET_OBJ(desc.setter); + ret = call_setter(ctx, setter, this_obj, val, flags); + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + return ret; + } else { + JS_FreeValue(ctx, desc.value); + if (!(desc.flags & JS_PROP_WRITABLE)) + goto read_only_prop; + if (likely(p == p1)) { + ret = JS_DefineProperty(ctx, this_obj, prop, val, + JS_UNDEFINED, JS_UNDEFINED, + JS_PROP_HAS_VALUE); + JS_FreeValue(ctx, val); + return ret; + } else { + break; + } + } + } + } + } + } + } + p1 = p1->shape->proto; + prototype_lookup: + if (!p1) + break; + + retry2: + prs = find_own_property(&pr, p1, prop); + if (prs) { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry (potentially useless) */ + if (JS_AutoInitProperty(ctx, p1, prop, pr, prs)) + return -1; + goto retry2; + } else if (!(prs->flags & JS_PROP_WRITABLE)) { + goto read_only_prop; + } + } + } + + if (unlikely(flags & JS_PROP_NO_ADD)) { + JS_ThrowReferenceErrorNotDefined(ctx, prop); + goto fail; + } + + if (unlikely(!p)) { + ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object"); + goto done; + } + + if (unlikely(!p->extensible)) { + ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); + goto done; + } + + if (p == JS_VALUE_GET_OBJ(obj)) { + if (p->is_exotic) { + if (p->class_id == JS_CLASS_ARRAY && p->fast_array && + __JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); + if (idx == p->u.array.count) { + /* fast case */ + return add_fast_array_element(ctx, p, val, flags); + } + } + goto generic_create_prop; + } else { + pr = add_property(ctx, p, prop, JS_PROP_C_W_E); + if (!pr) + goto fail; + pr->u.value = val; + return TRUE; + } + } + + // TODO(bnoordhuis) return JSProperty slot and update in place + // when plain property (not is_exotic/setter/etc.) to avoid + // calling find_own_property() thrice? + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop); + if (ret < 0) + goto fail; + + if (ret) { + JS_FreeValue(ctx, desc.value); + if (desc.flags & JS_PROP_GETSET) { + JS_FreeValue(ctx, desc.getter); + JS_FreeValue(ctx, desc.setter); + ret = JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden"); + goto done; + } else if (!(desc.flags & JS_PROP_WRITABLE) || + p->class_id == JS_CLASS_MODULE_NS) { + read_only_prop: + ret = JS_ThrowTypeErrorReadOnly(ctx, flags, prop); + goto done; + } + ret = JS_DefineProperty(ctx, this_obj, prop, val, + JS_UNDEFINED, JS_UNDEFINED, + JS_PROP_HAS_VALUE); + } else { + generic_create_prop: + ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, + flags | + JS_PROP_HAS_VALUE | + JS_PROP_HAS_ENUMERABLE | + JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_CONFIGURABLE | + JS_PROP_C_W_E); + } + +done: + JS_FreeValue(ctx, val); + return ret; +fail: + JS_FreeValue(ctx, val); + return -1; +} + +static int JS_SetPropertyInternal(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue val, int flags) +{ + return JS_SetPropertyInternal2(ctx, this_obj, prop, val, this_obj, + flags, NULL); +} + +int JS_SetProperty(JSContext *ctx, JSValue this_obj, JSAtom prop, JSValue val) +{ + return JS_SetPropertyInternal2(ctx, this_obj, prop, val, this_obj, JS_PROP_THROW, NULL); +} + +static int JS_SetPropertyInternalWithIC(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue val, int flags, + JSInlineCache *ic, int32_t offset) { + uint32_t tag; + JSObject *p; + tag = JS_VALUE_GET_TAG(this_obj); + if (unlikely(tag != JS_TAG_OBJECT)) + goto slow_path; + p = JS_VALUE_GET_OBJ(this_obj); + offset = get_ic_prop_offset(ic, offset, p->shape); + if (likely(offset >= 0)) { + set_value(ctx, &p->prop[offset].u.value, val); + return TRUE; + } +slow_path: + return JS_SetPropertyInternal2(ctx, this_obj, prop, val, this_obj, + flags, ic); +} + +/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */ +static int JS_SetPropertyValue(JSContext *ctx, JSValue this_obj, + JSValue prop, JSValue val, int flags) +{ + if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT && + JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) { + JSObject *p; + uint32_t idx; + double d; + int32_t v; + + /* fast path for array access */ + p = JS_VALUE_GET_OBJ(this_obj); + idx = JS_VALUE_GET_INT(prop); + switch(p->class_id) { + case JS_CLASS_ARRAY: + if (unlikely(idx >= (uint32_t)p->u.array.count)) { + JSObject *p1; + JSShape *sh1; + + /* fast path to add an element to the array */ + if (idx != (uint32_t)p->u.array.count || + !p->fast_array || !p->extensible) + goto slow_path; + /* check if prototype chain has a numeric property */ + p1 = p->shape->proto; + while (p1 != NULL) { + sh1 = p1->shape; + if (p1->class_id == JS_CLASS_ARRAY) { + if (unlikely(!p1->fast_array)) + goto slow_path; + } else if (p1->class_id == JS_CLASS_OBJECT) { + if (unlikely(sh1->has_small_array_index)) + goto slow_path; + } else { + goto slow_path; + } + p1 = sh1->proto; + } + /* add element */ + return add_fast_array_element(ctx, p, val, flags); + } + set_value(ctx, &p->u.array.u.values[idx], val); + break; + case JS_CLASS_ARGUMENTS: + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto slow_path; + set_value(ctx, &p->u.array.u.values[idx], val); + break; + case JS_CLASS_UINT8C_ARRAY: + if (JS_ToUint8ClampFree(ctx, &v, val)) + goto ta_cvt_fail; + /* Note: the conversion can detach the typed array, so the + array bound check must be done after */ + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint8_ptr[idx] = v; + break; + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint8_ptr[idx] = v; + break; + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint16_ptr[idx] = v; + break; + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + if (JS_ToInt32Free(ctx, &v, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint32_ptr[idx] = v; + break; + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + /* XXX: need specific conversion function */ + { + int64_t v; + if (JS_ToBigInt64Free(ctx, &v, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.uint64_ptr[idx] = v; + } + break; + case JS_CLASS_FLOAT32_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) + goto ta_cvt_fail; + if (unlikely(idx >= (uint32_t)p->u.array.count)) + goto ta_out_of_bound; + p->u.array.u.float_ptr[idx] = d; + break; + case JS_CLASS_FLOAT64_ARRAY: + if (JS_ToFloat64Free(ctx, &d, val)) { + ta_cvt_fail: + if (flags & JS_PROP_REFLECT_DEFINE_PROPERTY) { + JS_FreeValue(ctx, JS_GetException(ctx)); + return FALSE; + } + return -1; + } + if (unlikely(idx >= (uint32_t)p->u.array.count)) { + ta_out_of_bound: + if (typed_array_is_detached(ctx, p)) + if (!(flags & JS_PROP_DEFINE_PROPERTY)) + return TRUE; // per spec: no OOB exception + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index"); + } + p->u.array.u.double_ptr[idx] = d; + break; + default: + goto slow_path; + } + return TRUE; + } else { + JSAtom atom; + int ret; + slow_path: + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; + } +} + +int JS_SetPropertyUint32(JSContext *ctx, JSValue this_obj, + uint32_t idx, JSValue val) +{ + return JS_SetPropertyValue(ctx, this_obj, js_uint32(idx), val, + JS_PROP_THROW); +} + +int JS_SetPropertyInt64(JSContext *ctx, JSValue this_obj, + int64_t idx, JSValue val) +{ + JSAtom prop; + int res; + + if ((uint64_t)idx <= INT32_MAX) { + /* fast path for fast arrays */ + return JS_SetPropertyValue(ctx, this_obj, js_int32(idx), val, + JS_PROP_THROW); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) { + JS_FreeValue(ctx, val); + return -1; + } + res = JS_SetProperty(ctx, this_obj, prop, val); + JS_FreeAtom(ctx, prop); + return res; +} + +/* `prop` may be pure ASCII or UTF-8 encoded */ +int JS_SetPropertyStr(JSContext *ctx, JSValue this_obj, + const char *prop, JSValue val) +{ + JSAtom atom; + int ret; + atom = JS_NewAtom(ctx, prop); + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* compute the property flags. For each flag: (JS_PROP_HAS_x forces + it, otherwise def_flags is used) + Note: makes assumption about the bit pattern of the flags +*/ +static int get_prop_flags(int flags, int def_flags) +{ + int mask; + mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E; + return (flags & mask) | (def_flags & ~mask); +} + +static int JS_CreateProperty(JSContext *ctx, JSObject *p, + JSAtom prop, JSValue val, + JSValue getter, JSValue setter, + int flags) +{ + JSProperty *pr; + int ret, prop_flags; + + /* add a new property or modify an existing exotic one */ + if (p->is_exotic) { + if (p->class_id == JS_CLASS_ARRAY) { + uint32_t idx, len; + + if (p->fast_array) { + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx == p->u.array.count) { + if (!p->extensible) + goto not_extensible; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) + goto convert_to_array; + prop_flags = get_prop_flags(flags, 0); + if (prop_flags != JS_PROP_C_W_E) + goto convert_to_array; + return add_fast_array_element(ctx, p, + js_dup(val), flags); + } else { + goto convert_to_array; + } + } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { + /* convert the fast array to normal array */ + convert_to_array: + if (convert_fast_array_to_array(ctx, p)) + return -1; + goto generic_array; + } + } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) { + JSProperty *plen; + JSShapeProperty *pslen; + generic_array: + /* update the length field */ + plen = &p->prop[0]; + JS_ToUint32(ctx, &len, plen->u.value); + if ((idx + 1) > len) { + pslen = get_shape_prop(p->shape); + if (unlikely(!(pslen->flags & JS_PROP_WRITABLE))) + return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length); + /* XXX: should update the length after defining + the property */ + len = idx + 1; + set_value(ctx, &plen->u.value, js_uint32(len)); + } + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + ret = JS_AtomIsNumericIndex(ctx, prop); + if (ret != 0) { + if (ret < 0) + return -1; + return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array"); + } + } else if (!(flags & JS_PROP_NO_EXOTIC)) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + if (em) { + if (em->define_own_property) { + return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), + prop, val, getter, setter, flags); + } + ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); + if (ret < 0) + return -1; + if (!ret) + goto not_extensible; + } + } + } + + if (!p->extensible) { + not_extensible: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible"); + } + + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | + JS_PROP_GETSET; + } else { + prop_flags = flags & JS_PROP_C_W_E; + } + pr = add_property(ctx, p, prop, prop_flags); + if (unlikely(!pr)) + return -1; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + pr->u.getset.getter = NULL; + if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) { + pr->u.getset.getter = + JS_VALUE_GET_OBJ(js_dup(getter)); + } + pr->u.getset.setter = NULL; + if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) { + pr->u.getset.setter = + JS_VALUE_GET_OBJ(js_dup(setter)); + } + } else { + if (flags & JS_PROP_HAS_VALUE) { + pr->u.value = js_dup(val); + } else { + pr->u.value = JS_UNDEFINED; + } + } + return TRUE; +} + +/* return FALSE if not OK */ +static BOOL check_define_prop_flags(int prop_flags, int flags) +{ + BOOL has_accessor, is_getset; + + if (!(prop_flags & JS_PROP_CONFIGURABLE)) { + if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) == + (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) { + return FALSE; + } + if ((flags & JS_PROP_HAS_ENUMERABLE) && + (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE)) + return FALSE; + } + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if (!(prop_flags & JS_PROP_CONFIGURABLE)) { + has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0); + is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET); + if (has_accessor != is_getset) + return FALSE; + if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) { + /* not writable: cannot set the writable bit */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == + (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) + return FALSE; + } + } + } + return TRUE; +} + +/* ensure that the shape can be safely modified */ +static int js_shape_prepare_update(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs) +{ + JSShape *sh; + uint32_t idx = 0; /* prevent warning */ + + sh = p->shape; + if (sh->is_hashed) { + if (sh->header.ref_count != 1) { + if (pprs) + idx = *pprs - get_shape_prop(sh); + /* clone the shape (the resulting one is no longer hashed) */ + sh = js_clone_shape(ctx, sh); + if (!sh) + return -1; + js_free_shape(ctx->rt, p->shape); + p->shape = sh; + if (pprs) + *pprs = get_shape_prop(sh) + idx; + } else { + js_shape_hash_unlink(ctx->rt, sh); + sh->is_hashed = FALSE; + } + } + return 0; +} + +static int js_update_property_flags(JSContext *ctx, JSObject *p, + JSShapeProperty **pprs, int flags) +{ + if (flags != (*pprs)->flags) { + if (js_shape_prepare_update(ctx, p, pprs)) + return -1; + (*pprs)->flags = flags; + } + return 0; +} + +/* allowed flags: + JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE + JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE, + JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE, + JS_PROP_THROW, JS_PROP_NO_EXOTIC. + If JS_PROP_THROW is set, return an exception instead of FALSE. + if JS_PROP_NO_EXOTIC is set, do not call the exotic + define_own_property callback. + return -1 (exception), FALSE or TRUE. +*/ +int JS_DefineProperty(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue val, + JSValue getter, JSValue setter, int flags) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int mask, res; + + if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + return -1; + } + p = JS_VALUE_GET_OBJ(this_obj); + + redo_prop_update: + prs = find_own_property(&pr, p, prop); + if (prs) { + /* the range of the Array length property is always tested before */ + if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) { + uint32_t array_length; + if (JS_ToArrayLengthFree(ctx, &array_length, + js_dup(val), FALSE)) { + return -1; + } + /* this code relies on the fact that Uint32 are never allocated */ + val = js_uint32(array_length); + /* prs may have been modified */ + prs = find_own_property(&pr, p, prop); + assert(prs != NULL); + } + /* property already exists */ + if (!check_define_prop_flags(prs->flags, flags)) { + not_configurable: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable"); + } + + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + /* Instantiate property and retry */ + if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) + return -1; + goto redo_prop_update; + } + + if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | + JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + JSObject *new_getter, *new_setter; + + if (JS_IsFunction(ctx, getter)) { + new_getter = JS_VALUE_GET_OBJ(getter); + } else { + new_getter = NULL; + } + if (JS_IsFunction(ctx, setter)) { + new_setter = JS_VALUE_GET_OBJ(setter); + } else { + new_setter = NULL; + } + + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) { + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + /* convert to getset */ + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + free_var_ref(ctx->rt, pr->u.var_ref); + } else { + JS_FreeValue(ctx, pr->u.value); + } + prs->flags = (prs->flags & + (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) | + JS_PROP_GETSET; + pr->u.getset.getter = NULL; + pr->u.getset.setter = NULL; + } else { + if (!(prs->flags & JS_PROP_CONFIGURABLE)) { + if ((flags & JS_PROP_HAS_GET) && + new_getter != pr->u.getset.getter) { + goto not_configurable; + } + if ((flags & JS_PROP_HAS_SET) && + new_setter != pr->u.getset.setter) { + goto not_configurable; + } + } + } + if (flags & JS_PROP_HAS_GET) { + if (pr->u.getset.getter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (new_getter) + js_dup(getter); + pr->u.getset.getter = new_getter; + } + if (flags & JS_PROP_HAS_SET) { + if (pr->u.getset.setter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + if (new_setter) + js_dup(setter); + pr->u.getset.setter = new_setter; + } + } else { + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + /* convert to data descriptor */ + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + if (pr->u.getset.getter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter)); + if (pr->u.getset.setter) + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter)); + prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); + pr->u.value = JS_UNDEFINED; + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + /* Note: JS_PROP_VARREF is always writable */ + } else { + if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 && + (flags & JS_PROP_HAS_VALUE)) { + if (!js_same_value(ctx, val, pr->u.value)) { + goto not_configurable; + } else { + return TRUE; + } + } + } + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + if (flags & JS_PROP_HAS_VALUE) { + if (p->class_id == JS_CLASS_MODULE_NS) { + /* JS_PROP_WRITABLE is always true for variable + references, but they are write protected in module name + spaces. */ + if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue)) + goto not_configurable; + } + /* update the reference */ + set_value(ctx, pr->u.var_ref->pvalue, + js_dup(val)); + } + /* if writable is set to false, no longer a + reference (for mapped arguments) */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) { + JSValue val1; + if (js_shape_prepare_update(ctx, p, &prs)) + return -1; + val1 = js_dup(*pr->u.var_ref->pvalue); + free_var_ref(ctx->rt, pr->u.var_ref); + pr->u.value = val1; + prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE); + } + } else if (prs->flags & JS_PROP_LENGTH) { + if (flags & JS_PROP_HAS_VALUE) { + /* Note: no JS code is executable because + 'val' is guaranted to be a Uint32 */ + res = set_array_length(ctx, p, js_dup(val), + flags); + } else { + res = TRUE; + } + /* still need to reset the writable flag if + needed. The JS_PROP_LENGTH is kept because the + Uint32 test is still done if the length + property is read-only. */ + if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == + JS_PROP_HAS_WRITABLE) { + prs = get_shape_prop(p->shape); + if (js_update_property_flags(ctx, p, &prs, + prs->flags & ~JS_PROP_WRITABLE)) + return -1; + } + return res; + } else { + if (flags & JS_PROP_HAS_VALUE) { + JS_FreeValue(ctx, pr->u.value); + pr->u.value = js_dup(val); + } + if (flags & JS_PROP_HAS_WRITABLE) { + if (js_update_property_flags(ctx, p, &prs, + (prs->flags & ~JS_PROP_WRITABLE) | + (flags & JS_PROP_WRITABLE))) + return -1; + } + } + } + } + mask = 0; + if (flags & JS_PROP_HAS_CONFIGURABLE) + mask |= JS_PROP_CONFIGURABLE; + if (flags & JS_PROP_HAS_ENUMERABLE) + mask |= JS_PROP_ENUMERABLE; + if (js_update_property_flags(ctx, p, &prs, + (prs->flags & ~mask) | (flags & mask))) + return -1; + return TRUE; + } + + /* handle modification of fast array elements */ + if (p->fast_array) { + uint32_t idx; + uint32_t prop_flags; + if (p->class_id == JS_CLASS_ARRAY) { + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); + if (idx < p->u.array.count) { + prop_flags = get_prop_flags(flags, JS_PROP_C_W_E); + if (prop_flags != JS_PROP_C_W_E) + goto convert_to_slow_array; + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) { + convert_to_slow_array: + if (convert_fast_array_to_array(ctx, p)) + return -1; + else + goto redo_prop_update; + } + if (flags & JS_PROP_HAS_VALUE) { + set_value(ctx, &p->u.array.u.values[idx], js_dup(val)); + } + return TRUE; + } + } + } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY && + p->class_id <= JS_CLASS_FLOAT64_ARRAY) { + JSValue num; + int ret; + + if (!__JS_AtomIsTaggedInt(prop)) { + /* slow path with to handle all numeric indexes */ + num = JS_AtomIsNumericIndex1(ctx, prop); + if (JS_IsUndefined(num)) + goto typed_array_done; + if (JS_IsException(num)) + return -1; + ret = JS_NumberIsInteger(ctx, num); + if (ret < 0) { + JS_FreeValue(ctx, num); + return -1; + } + if (!ret) { + JS_FreeValue(ctx, num); + return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array"); + } + ret = JS_NumberIsNegativeOrMinusZero(ctx, num); + JS_FreeValue(ctx, num); + if (ret) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array"); + } + if (!__JS_AtomIsTaggedInt(prop)) + goto typed_array_oob; + } + idx = __JS_AtomToUInt32(prop); + /* if the typed array is detached, p->u.array.count = 0 */ + if (idx >= typed_array_get_length(ctx, p)) { + typed_array_oob: + return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array"); + } + prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) || + prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags"); + } + if (flags & JS_PROP_HAS_VALUE) { + return JS_SetPropertyValue(ctx, this_obj, js_int32(idx), js_dup(val), flags); + } + return TRUE; + typed_array_done: ; + } + } + + return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags); +} + +static int JS_DefineAutoInitProperty(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSAutoInitIDEnum id, + void *opaque, int flags) +{ + JSObject *p; + JSProperty *pr; + + if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) + return FALSE; + + p = JS_VALUE_GET_OBJ(this_obj); + + if (find_own_property(&pr, p, prop)) { + /* property already exists */ + abort(); + return FALSE; + } + + /* Specialized CreateProperty */ + pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT); + if (unlikely(!pr)) + return -1; + pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx); + assert((pr->u.init.realm_and_id & 3) == 0); + assert(id <= 3); + pr->u.init.realm_and_id |= id; + pr->u.init.opaque = opaque; + return TRUE; +} + +/* shortcut to add or redefine a new property value */ +int JS_DefinePropertyValue(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue val, int flags) +{ + int ret; + ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED, + flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE); + JS_FreeValue(ctx, val); + return ret; +} + +int JS_DefinePropertyValueValue(JSContext *ctx, JSValue this_obj, + JSValue prop, JSValue val, int flags) +{ + JSAtom atom; + int ret; + atom = JS_ValueToAtom(ctx, prop); + JS_FreeValue(ctx, prop); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, val); + return -1; + } + ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; +} + +int JS_DefinePropertyValueUint32(JSContext *ctx, JSValue this_obj, + uint32_t idx, JSValue val, int flags) +{ + return JS_DefinePropertyValueValue(ctx, this_obj, js_uint32(idx), + val, flags); +} + +int JS_DefinePropertyValueInt64(JSContext *ctx, JSValue this_obj, + int64_t idx, JSValue val, int flags) +{ + return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx), + val, flags); +} + +/* `prop` may be pure ASCII or UTF-8 encoded */ +int JS_DefinePropertyValueStr(JSContext *ctx, JSValue this_obj, + const char *prop, JSValue val, int flags) +{ + JSAtom atom; + int ret; + atom = JS_NewAtom(ctx, prop); + ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags); + JS_FreeAtom(ctx, atom); + return ret; +} + +/* shortcut to add getter & setter */ +int JS_DefinePropertyGetSet(JSContext *ctx, JSValue this_obj, + JSAtom prop, JSValue getter, JSValue setter, + int flags) +{ + int ret; + ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter, + flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE); + JS_FreeValue(ctx, getter); + JS_FreeValue(ctx, setter); + return ret; +} + +static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValue this_obj, + int64_t idx, JSValue val, int flags) +{ + return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx), + val, flags | JS_PROP_CONFIGURABLE | + JS_PROP_ENUMERABLE | JS_PROP_WRITABLE); +} + + +/* return TRUE if 'obj' has a non empty 'name' string */ +static BOOL js_object_has_name(JSContext *ctx, JSValue obj) +{ + JSProperty *pr; + JSShapeProperty *prs; + JSValue val; + JSString *p; + + prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name); + if (!prs) + return FALSE; + if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL) + return TRUE; + val = pr->u.value; + if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING) + return TRUE; + p = JS_VALUE_GET_STRING(val); + return (p->len != 0); +} + +static int JS_DefineObjectName(JSContext *ctx, JSValue obj, + JSAtom name, int flags) +{ + if (name != JS_ATOM_NULL + && JS_IsObject(obj) + && !js_object_has_name(ctx, obj) + && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) { + return -1; + } + return 0; +} + +static int JS_DefineObjectNameComputed(JSContext *ctx, JSValue obj, + JSValue str, int flags) +{ + if (JS_IsObject(obj) && + !js_object_has_name(ctx, obj)) { + JSAtom prop; + JSValue name_str; + prop = JS_ValueToAtom(ctx, str); + if (prop == JS_ATOM_NULL) + return -1; + name_str = js_get_function_name(ctx, prop); + JS_FreeAtom(ctx, prop); + if (JS_IsException(name_str)) + return -1; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0) + return -1; + } + return 0; +} + +#define DEFINE_GLOBAL_LEX_VAR (1 << 7) +#define DEFINE_GLOBAL_FUNC_VAR (1 << 6) + +static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop) +{ + return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop); +} + +/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */ +/* XXX: could support exotic global object. */ +static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags) +{ + JSObject *p; + JSShapeProperty *prs; + + p = JS_VALUE_GET_OBJ(ctx->global_obj); + prs = find_own_property1(p, prop); + /* XXX: should handle JS_PROP_AUTOINIT */ + if (flags & DEFINE_GLOBAL_LEX_VAR) { + if (prs && !(prs->flags & JS_PROP_CONFIGURABLE)) + goto fail_redeclaration; + } else { + if (!prs && !p->extensible) + goto define_error; + if (flags & DEFINE_GLOBAL_FUNC_VAR) { + if (prs) { + if (!(prs->flags & JS_PROP_CONFIGURABLE) && + ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET || + ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) != + (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) { + define_error: + JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'", + prop); + return -1; + } + } + } + } + /* check if there already is a lexical declaration */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property1(p, prop); + if (prs) { + fail_redeclaration: + JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop); + return -1; + } + return 0; +} + +/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) | + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */ +/* XXX: could support exotic global object. */ +static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + JSValue val; + int flags; + + if (def_flags & DEFINE_GLOBAL_LEX_VAR) { + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) | + JS_PROP_CONFIGURABLE; + val = JS_UNINITIALIZED; + } else { + p = JS_VALUE_GET_OBJ(ctx->global_obj); + flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | + (def_flags & JS_PROP_CONFIGURABLE); + val = JS_UNDEFINED; + } + prs = find_own_property1(p, prop); + if (prs) + return 0; + if (!p->extensible) + return 0; + pr = add_property(ctx, p, prop, flags); + if (unlikely(!pr)) + return -1; + pr->u.value = val; + return 0; +} + +/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */ +/* XXX: could support exotic global object. */ +static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop, + JSValue func, int def_flags) +{ + + JSObject *p; + JSShapeProperty *prs; + int flags; + + p = JS_VALUE_GET_OBJ(ctx->global_obj); + prs = find_own_property1(p, prop); + flags = JS_PROP_HAS_VALUE | JS_PROP_THROW; + if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) { + flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags | + JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE; + } + if (JS_DefineProperty(ctx, ctx->global_obj, prop, func, + JS_UNDEFINED, JS_UNDEFINED, flags) < 0) + return -1; + return 0; +} + +static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop, + BOOL throw_ref_error) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_TMASK properties */ + if (unlikely(JS_IsUninitialized(pr->u.value))) + return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return js_dup(pr->u.value); + } + return JS_GetPropertyInternal(ctx, ctx->global_obj, prop, + ctx->global_obj, throw_ref_error); +} + +/* construct a reference to a global variable */ +static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_AUTOINIT properties? */ + /* XXX: conformance: do these tests in + OP_put_var_ref/OP_get_var_ref ? */ + if (unlikely(JS_IsUninitialized(pr->u.value))) { + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { + return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); + } + sp[0] = js_dup(ctx->global_var_obj); + } else { + int ret; + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + if (ret) { + sp[0] = js_dup(ctx->global_obj); + } else { + sp[0] = JS_UNDEFINED; + } + } + sp[1] = JS_AtomToValue(ctx, prop); + return 0; +} + +/* use for strict variable access: test if the variable exists */ +static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop) +{ + JSObject *p; + JSShapeProperty *prs; + int ret; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property1(p, prop); + if (prs) { + ret = TRUE; + } else { + ret = JS_HasProperty(ctx, ctx->global_obj, prop); + if (ret < 0) + return -1; + } + return ret; +} + +/* flag = 0: normal variable write + flag = 1: initialize lexical variable + flag = 2: normal variable write, strict check was done before +*/ +static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, + int flag) +{ + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + int flags; + + /* no exotic behavior is possible in global_var_obj */ + p = JS_VALUE_GET_OBJ(ctx->global_var_obj); + prs = find_own_property(&pr, p, prop); + if (prs) { + /* XXX: should handle JS_PROP_AUTOINIT properties? */ + if (flag != 1) { + if (unlikely(JS_IsUninitialized(pr->u.value))) { + JS_FreeValue(ctx, val); + JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); + return -1; + } + if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop); + } + } + set_value(ctx, &pr->u.value, val); + return 0; + } + flags = JS_PROP_THROW_STRICT; + if (is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD; + return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags); +} + +/* return -1, FALSE or TRUE. return FALSE if not configurable or + invalid object. return -1 in case of exception. + flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */ +int JS_DeleteProperty(JSContext *ctx, JSValue obj, JSAtom prop, int flags) +{ + JSValue obj1; + JSObject *p; + int res; + + obj1 = JS_ToObject(ctx, obj); + if (JS_IsException(obj1)) + return -1; + p = JS_VALUE_GET_OBJ(obj1); + res = delete_property(ctx, p, prop); + JS_FreeValue(ctx, obj1); + if (res != FALSE) + return res; + if ((flags & JS_PROP_THROW) || + ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) { + JS_ThrowTypeError(ctx, "could not delete property"); + return -1; + } + return FALSE; +} + +int JS_DeletePropertyInt64(JSContext *ctx, JSValue obj, int64_t idx, int flags) +{ + JSAtom prop; + int res; + + if ((uint64_t)idx <= JS_ATOM_MAX_INT) { + /* fast path for fast arrays */ + return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags); + } + prop = JS_NewAtomInt64(ctx, idx); + if (prop == JS_ATOM_NULL) + return -1; + res = JS_DeleteProperty(ctx, obj, prop, flags); + JS_FreeAtom(ctx, prop); + return res; +} + +BOOL JS_IsFunction(JSContext *ctx, JSValue val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + switch(p->class_id) { + case JS_CLASS_BYTECODE_FUNCTION: + return TRUE; + case JS_CLASS_PROXY: + return p->u.proxy_data->is_func; + default: + return (ctx->rt->class_array[p->class_id].call != NULL); + } +} + +BOOL JS_IsCFunction(JSContext *ctx, JSValue val, JSCFunction *func, int magic) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + if (p->class_id == JS_CLASS_C_FUNCTION) + return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic); + else + return FALSE; +} + +BOOL JS_IsConstructor(JSContext *ctx, JSValue val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + return p->is_constructor; +} + +BOOL JS_SetConstructorBit(JSContext *ctx, JSValue func_obj, BOOL val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(func_obj); + p->is_constructor = val; + return TRUE; +} + +BOOL JS_IsError(JSContext *ctx, JSValue val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + return (p->class_id == JS_CLASS_ERROR); +} + +/* used to avoid catching interrupt exceptions */ +BOOL JS_IsUncatchableError(JSContext *ctx, JSValue val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error; +} + +void JS_SetUncatchableError(JSContext *ctx, JSValue val, BOOL flag) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(val); + if (p->class_id == JS_CLASS_ERROR) + p->is_uncatchable_error = flag; +} + +void JS_ResetUncatchableError(JSContext *ctx) +{ + JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE); +} + +void JS_SetOpaque(JSValue obj, void *opaque) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(obj); + p->u.opaque = opaque; + } +} + +/* return NULL if not an object of class class_id */ +void *JS_GetOpaque(JSValue obj, JSClassID class_id) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return NULL; + p = JS_VALUE_GET_OBJ(obj); + if (p->class_id != class_id) + return NULL; + return p->u.opaque; +} + +void *JS_GetOpaque2(JSContext *ctx, JSValue obj, JSClassID class_id) +{ + void *p = JS_GetOpaque(obj, class_id); + if (unlikely(!p)) { + JS_ThrowTypeErrorInvalidClass(ctx, class_id); + } + return p; +} + +void *JS_GetAnyOpaque(JSValue obj, JSClassID *class_id) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) { + *class_id = 0; + return NULL; + } + p = JS_VALUE_GET_OBJ(obj); + *class_id = p->class_id; + return p->u.opaque; +} + +static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) +{ + int i; + BOOL force_ordinary; + + JSAtom method_name; + JSValue method, ret; + if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) + return val; + force_ordinary = hint & HINT_FORCE_ORDINARY; + hint &= ~HINT_FORCE_ORDINARY; + if (!force_ordinary) { + method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive); + if (JS_IsException(method)) + goto exception; + /* ECMA says *If exoticToPrim is not undefined* but tests in + test262 use null as a non callable converter */ + if (!JS_IsUndefined(method) && !JS_IsNull(method)) { + JSAtom atom; + JSValue arg; + switch(hint) { + case HINT_STRING: + atom = JS_ATOM_string; + break; + case HINT_NUMBER: + atom = JS_ATOM_number; + break; + default: + case HINT_NONE: + atom = JS_ATOM_default; + break; + } + arg = JS_AtomToString(ctx, atom); + ret = JS_CallFree(ctx, method, val, 1, &arg); + JS_FreeValue(ctx, arg); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, val); + if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) + return ret; + JS_FreeValue(ctx, ret); + return JS_ThrowTypeError(ctx, "toPrimitive"); + } + } + if (hint != HINT_STRING) + hint = HINT_NUMBER; + for(i = 0; i < 2; i++) { + if ((i ^ hint) == 0) { + method_name = JS_ATOM_toString; + } else { + method_name = JS_ATOM_valueOf; + } + method = JS_GetProperty(ctx, val, method_name); + if (JS_IsException(method)) + goto exception; + if (JS_IsFunction(ctx, method)) { + ret = JS_CallFree(ctx, method, val, 0, NULL); + if (JS_IsException(ret)) + goto exception; + if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) { + JS_FreeValue(ctx, val); + return ret; + } + JS_FreeValue(ctx, ret); + } else { + JS_FreeValue(ctx, method); + } + } + JS_ThrowTypeError(ctx, "toPrimitive"); +exception: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue JS_ToPrimitive(JSContext *ctx, JSValue val, int hint) +{ + return JS_ToPrimitiveFree(ctx, js_dup(val), hint); +} + +void JS_SetIsHTMLDDA(JSContext *ctx, JSValue obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return; + p = JS_VALUE_GET_OBJ(obj); + p->is_HTMLDDA = TRUE; +} + +static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValue obj) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) + return FALSE; + p = JS_VALUE_GET_OBJ(obj); + return p->is_HTMLDDA; +} + +static int JS_ToBoolFree(JSContext *ctx, JSValue val) +{ + uint32_t tag = JS_VALUE_GET_TAG(val); + switch(tag) { + case JS_TAG_INT: + return JS_VALUE_GET_INT(val) != 0; + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + return JS_VALUE_GET_INT(val); + case JS_TAG_EXCEPTION: + return -1; + case JS_TAG_STRING: + { + BOOL ret = JS_VALUE_GET_STRING(val)->len != 0; + JS_FreeValue(ctx, val); + return ret; + } + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + BOOL ret; + ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN; + JS_FreeValue(ctx, val); + return ret; + } + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(val); + BOOL ret; + ret = !p->is_HTMLDDA; + JS_FreeValue(ctx, val); + return ret; + } + break; + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d = JS_VALUE_GET_FLOAT64(val); + return !isnan(d) && d != 0; + } else { + JS_FreeValue(ctx, val); + return TRUE; + } + } +} + +int JS_ToBool(JSContext *ctx, JSValue val) +{ + return JS_ToBoolFree(ctx, js_dup(val)); +} + +/* pc points to pure ASCII or UTF-8, null terminated contents */ +static int skip_spaces(const char *pc) +{ + const uint8_t *p, *p_next, *p_start; + uint32_t c; + + p = p_start = (const uint8_t *)pc; + for (;;) { + c = *p++; + if (c < 0x80) { + if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20))) + break; + } else { + c = utf8_decode(p - 1, &p_next); + /* no need to test for invalid UTF-8, 0xFFFD is not a space */ + if (!lre_is_space(c)) + break; + p = p_next; + } + } + return p - 1 - p_start; +} + +static inline int to_digit(int c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'A' && c <= 'Z') + return c - 'A' + 10; + else if (c >= 'a' && c <= 'z') + return c - 'a' + 10; + else + return 36; +} + +/* XXX: remove */ +static double js_strtod(const char *str, int radix, BOOL is_float) +{ + double d; + int c; + + if (!is_float || radix != 10) { + const char *p = str; + uint64_t n_max, n; + int int_exp, is_neg; + + is_neg = 0; + if (*p == '-') { + is_neg = 1; + p++; + } + + /* skip leading zeros */ + while (*p == '0') + p++; + n = 0; + if (radix == 10) + n_max = ((uint64_t)-1 - 9) / 10; /* most common case */ + else + n_max = ((uint64_t)-1 - (radix - 1)) / radix; + /* XXX: could be more precise */ + int_exp = 0; + while ((c = to_digit(*p)) < radix) { + if (n <= n_max) { + n = n * radix + c; + } else { + if (radix == 10) + goto strtod_case; + int_exp++; + } + p++; + } + d = n; + if (int_exp != 0) { + d *= pow(radix, int_exp); + } + if (is_neg) + d = -d; + } else { + strtod_case: + d = strtod(str, NULL); + } + return d; +} + +static JSValue js_string_to_bigint(JSContext *ctx, const char *buf, int radix) +{ + bf_t *a; + int ret; + JSValue val; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ); + if (ret & BF_ST_MEM_ERROR) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + return JS_CompactBigInt1(ctx, val); +} + +/* `js_atof(ctx, p, len, pp, radix, flags)` + Convert the string pointed to by `p` to a number value. + Return an exception in case of memory error. + Return `JS_NAN` if invalid syntax. + - `p` points to a null terminated UTF-8 encoded char array, + - `len` the length of the array, + - `pp` if not null receives a pointer to the next character, + - `radix` must be in range 2 to 36, else return `JS_NAN`. + - `flags` is a combination of the flags below. + There is a null byte at `p[len]`, but there might be embedded null + bytes between `p[0]` and `p[len]` which must produce `JS_NAN` if + the `ATOD_NO_TRAILING_CHARS` flag is present. + */ + +#define ATOD_TRIM_SPACES (1 << 0) /* trim white space */ +#define ATOD_ACCEPT_EMPTY (1 << 1) /* accept an empty string, value is 0 */ +#define ATOD_ACCEPT_FLOAT (1 << 2) /* parse decimal floating point syntax */ +#define ATOD_ACCEPT_INFINITY (1 << 3) /* parse Infinity as a float point number */ +#define ATOD_ACCEPT_BIN_OCT (1 << 4) /* accept 0o and 0b prefixes */ +#define ATOD_ACCEPT_HEX_PREFIX (1 << 5) /* accept 0x prefix for radix 16 */ +#define ATOD_ACCEPT_UNDERSCORES (1 << 6) /* accept _ between digits as a digit separator */ +#define ATOD_ACCEPT_SUFFIX (1 << 7) /* allow 'n' suffix to produce BigInt */ +#define ATOD_WANT_BIG_INT (1 << 8) /* return type must be BigInt */ +#define ATOD_DECIMAL_AFTER_SIGN (1 << 9) /* only accept decimal number after sign */ +#define ATOD_NO_TRAILING_CHARS (1 << 10) /* do not accept trailing characters */ + +static JSValue js_atof(JSContext *ctx, const char *p, size_t len, + const char **pp, int radix, int flags) +{ + const char *p_start; + const char *end = p + len; + int sep; + BOOL is_float; + char buf1[64], *buf = buf1; + size_t i, j; + JSValue val = JS_NAN; + double d; + char sign; + + if (radix < 2 || radix > 36) + goto done; + + /* optional separator between digits */ + sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256; + sign = 0; + if (flags & ATOD_TRIM_SPACES) + p += skip_spaces(p); + if (p == end && (flags & ATOD_ACCEPT_EMPTY)) { + if (pp) *pp = p; + if (flags & ATOD_WANT_BIG_INT) + return JS_NewBigInt64(ctx, 0); + else + return js_int32(0); + } + if (*p == '+' || *p == '-') { + sign = *p; + p++; + if (flags & ATOD_DECIMAL_AFTER_SIGN) + flags &= ~(ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT); + } + if (p[0] == '0') { + if ((p[1] == 'x' || p[1] == 'X') && + ((flags & ATOD_ACCEPT_HEX_PREFIX) || radix == 16)) { + p += 2; + radix = 16; + } else if (flags & ATOD_ACCEPT_BIN_OCT) { + if (p[1] == 'o' || p[1] == 'O') { + p += 2; + radix = 8; + } else if (p[1] == 'b' || p[1] == 'B') { + p += 2; + radix = 2; + } + } + } else { + if (*p == 'I' && (flags & ATOD_ACCEPT_INFINITY) && strstart(p, "Infinity", &p)) { + d = INF; + if (sign == '-') + d = -d; + val = js_float64(d); + goto done; + } + } + is_float = FALSE; + p_start = p; + while (to_digit(*p) < radix) { + p++; + if (*p == sep && to_digit(p[1]) < radix) + p++; + } + if ((flags & ATOD_ACCEPT_FLOAT) && radix == 10) { + if (*p == '.' && (p > p_start || to_digit(p[1]) < radix)) { + is_float = TRUE; + p++; + while (to_digit(*p) < radix) { + p++; + if (*p == sep && to_digit(p[1]) < radix) + p++; + } + } + if (p > p_start && (*p == 'e' || *p == 'E')) { + i = 1; + if (p[1] == '+' || p[1] == '-') { + i++; + } + if (is_digit(p[i])) { + is_float = TRUE; + p += i + 1; + while (is_digit(*p) || (*p == sep && is_digit(p[1]))) + p++; + } + } + } + if (p == p_start) + goto done; + + len = p - p_start; + if (unlikely((len + 2) > sizeof(buf1))) { + buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */ + if (!buf) { + if (pp) *pp = p; + return JS_ThrowOutOfMemory(ctx); + } + } + /* remove the separators and the radix prefix */ + j = 0; + if (sign == '-') + buf[j++] = '-'; + for (i = 0; i < len; i++) { + if (p_start[i] != '_') + buf[j++] = p_start[i]; + } + buf[j] = '\0'; + + if (flags & ATOD_ACCEPT_SUFFIX) { + if (*p == 'n') { + p++; + flags |= ATOD_WANT_BIG_INT; + } + } + + if (flags & ATOD_WANT_BIG_INT) { + if (!is_float) + val = js_string_to_bigint(ctx, buf, radix); + } else { + d = js_strtod(buf, radix, is_float); + val = js_number(d); /* return int or float64 */ + } + + done: + if (flags & ATOD_NO_TRAILING_CHARS) { + if (flags & ATOD_TRIM_SPACES) + p += skip_spaces(p); + if (p != end) { + JS_FreeValue(ctx, val); + val = JS_NAN; + } + } + if (buf != buf1) + js_free_rt(ctx->rt, buf); + if (pp) *pp = p; + return val; +} + +typedef enum JSToNumberHintEnum { + TON_FLAG_NUMBER, + TON_FLAG_NUMERIC, +} JSToNumberHintEnum; + +static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, + JSToNumberHintEnum flag) +{ + uint32_t tag; + JSValue ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_BIG_INT: + if (flag != TON_FLAG_NUMERIC) { + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert BigInt to number"); + } + ret = val; + break; + case JS_TAG_FLOAT64: + case JS_TAG_INT: + case JS_TAG_EXCEPTION: + ret = val; + break; + case JS_TAG_BOOL: + case JS_TAG_NULL: + ret = js_int32(JS_VALUE_GET_INT(val)); + break; + case JS_TAG_UNDEFINED: + ret = JS_NAN; + break; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + return JS_EXCEPTION; + goto redo; + case JS_TAG_STRING: + { + const char *str; + size_t len; + int flags; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + flags = ATOD_TRIM_SPACES | ATOD_ACCEPT_EMPTY | + ATOD_ACCEPT_FLOAT | ATOD_ACCEPT_INFINITY | + ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT | + ATOD_DECIMAL_AFTER_SIGN | ATOD_NO_TRAILING_CHARS; + ret = js_atof(ctx, str, len, NULL, 10, flags); + JS_FreeCString(ctx, str); + } + break; + case JS_TAG_SYMBOL: + JS_FreeValue(ctx, val); + return JS_ThrowTypeError(ctx, "cannot convert symbol to number"); + default: + JS_FreeValue(ctx, val); + ret = JS_NAN; + break; + } + return ret; +} + +static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val) +{ + return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER); +} + +static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val) +{ + return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC); +} + +static JSValue JS_ToNumeric(JSContext *ctx, JSValue val) +{ + return JS_ToNumericFree(ctx, js_dup(val)); +} + +static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, + JSValue val) +{ + double d; + uint32_t tag; + + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = JS_FLOAT64_NAN; + return -1; + } + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + d = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + d = JS_VALUE_GET_FLOAT64(val); + break; + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + /* XXX: there can be a double rounding issue with some + primitives (such as JS_ToUint8ClampFree()), but it is + not critical to fix it. */ + bf_get_float64(&p->num, &d, BF_RNDN); + JS_FreeValue(ctx, val); + } + break; + default: + abort(); + } + *pres = d; + return 0; +} + +static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) +{ + uint32_t tag; + + tag = JS_VALUE_GET_TAG(val); + if (tag <= JS_TAG_NULL) { + *pres = JS_VALUE_GET_INT(val); + return 0; + } else if (JS_TAG_IS_FLOAT64(tag)) { + *pres = JS_VALUE_GET_FLOAT64(val); + return 0; + } else { + return __JS_ToFloat64Free(ctx, pres, val); + } +} + +int JS_ToFloat64(JSContext *ctx, double *pres, JSValue val) +{ + return JS_ToFloat64Free(ctx, pres, js_dup(val)); +} + +static JSValue JS_ToNumber(JSContext *ctx, JSValue val) +{ + return JS_ToNumberFree(ctx, js_dup(val)); +} + +/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ +static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) +{ + uint32_t tag; + JSValue ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = js_int32(JS_VALUE_GET_INT(val)); + break; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + ret = js_int32(0); + } else { + /* convert -0 to +0 */ + d = trunc(d) + 0.0; + ret = js_number(d); + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return val; + goto redo; + } + return ret; +} + +/* Note: the integer value is satured to 32 bits */ +static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val) +{ + uint32_t tag; + int ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_EXCEPTION: + *pres = 0; + return -1; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + ret = 0; + } else { + if (d < INT32_MIN) + ret = INT32_MIN; + else if (d > INT32_MAX) + ret = INT32_MAX; + else + ret = (int)d; + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValue val) +{ + return JS_ToInt32SatFree(ctx, pres, js_dup(val)); +} + +int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValue val, + int min, int max, int min_offset) +{ + int res = JS_ToInt32SatFree(ctx, pres, js_dup(val)); + if (res == 0) { + if (*pres < min) { + *pres += min_offset; + if (*pres < min) + *pres = min; + } else { + if (*pres > max) + *pres = max; + } + } + return res; +} + +static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) +{ + uint32_t tag; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + *pres = JS_VALUE_GET_INT(val); + return 0; + case JS_TAG_EXCEPTION: + *pres = 0; + return -1; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + *pres = 0; + } else { + if (d < INT64_MIN) + *pres = INT64_MIN; + else if (d >= 0x1p63) + *pres = INT64_MAX; + else + *pres = (int64_t)d; + } + } + return 0; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } +} + +int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValue val) +{ + return JS_ToInt64SatFree(ctx, pres, js_dup(val)); +} + +int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValue val, + int64_t min, int64_t max, int64_t neg_offset) +{ + int res = JS_ToInt64SatFree(ctx, pres, js_dup(val)); + if (res == 0) { + if (*pres < 0) + *pres += neg_offset; + if (*pres < min) + *pres = min; + else if (*pres > max) + *pres = max; + } + return res; +} + +/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0) + in case of exception */ +static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val) +{ + uint32_t tag; + int64_t ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + double d; + int e; + d = JS_VALUE_GET_FLOAT64(val); + u.d = d; + /* we avoid doing fmod(x, 2^64) */ + e = (u.u64 >> 52) & 0x7ff; + if (likely(e <= (1023 + 62))) { + /* fast case */ + ret = (int64_t)d; + } else if (e <= (1023 + 62 + 53)) { + uint64_t v; + /* remainder modulo 2^64 */ + v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + ret = v << ((e - 1023) - 52); + /* take the sign into account */ + if (u.u64 >> 63) + if (ret != INT64_MIN) + ret = -ret; + } else { + ret = 0; /* also handles NaN and +inf */ + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValue val) +{ + return JS_ToInt64Free(ctx, pres, js_dup(val)); +} + +int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValue val) +{ + if (JS_IsBigInt(ctx, val)) + return JS_ToBigInt64(ctx, pres, val); + else + return JS_ToInt64(ctx, pres, val); +} + +/* return (<0, 0) in case of exception */ +static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val) +{ + uint32_t tag; + int32_t ret; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + ret = JS_VALUE_GET_INT(val); + break; + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + double d; + int e; + d = JS_VALUE_GET_FLOAT64(val); + u.d = d; + /* we avoid doing fmod(x, 2^32) */ + e = (u.u64 >> 52) & 0x7ff; + if (likely(e <= (1023 + 30))) { + /* fast case */ + ret = (int32_t)d; + } else if (e <= (1023 + 30 + 53)) { + uint64_t v; + /* remainder modulo 2^32 */ + v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52); + v = v << ((e - 1023) - 52 + 32); + ret = v >> 32; + /* take the sign into account */ + if (u.u64 >> 63) + if (ret != INT32_MIN) + ret = -ret; + } else { + ret = 0; /* also handles NaN and +inf */ + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = ret; + return 0; +} + +int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValue val) +{ + return JS_ToInt32Free(ctx, pres, js_dup(val)); +} + +static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val) +{ + return JS_ToInt32Free(ctx, (int32_t *)pres, val); +} + +static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) +{ + uint32_t tag; + int res; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + res = JS_VALUE_GET_INT(val); + res = max_int(0, min_int(255, res)); + break; + case JS_TAG_FLOAT64: + { + double d = JS_VALUE_GET_FLOAT64(val); + if (isnan(d)) { + res = 0; + } else { + if (d < 0) + res = 0; + else if (d > 255) + res = 255; + else + res = lrint(d); + } + } + break; + default: + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) { + *pres = 0; + return -1; + } + goto redo; + } + *pres = res; + return 0; +} + +static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, + JSValue val, BOOL is_array_ctor) +{ + uint32_t tag, len; + + tag = JS_VALUE_GET_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + { + int v; + v = JS_VALUE_GET_INT(val); + if (v < 0) + goto fail; + len = v; + } + break; + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + bf_t a; + BOOL res; + bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD); + bf_init(ctx->bf_ctx, &a); + bf_set_ui(&a, len); + res = bf_cmp_eq(&a, &p->num); + bf_delete(&a); + JS_FreeValue(ctx, val); + if (!res) + goto fail; + } + break; + default: + if (JS_TAG_IS_FLOAT64(tag)) { + double d; + d = JS_VALUE_GET_FLOAT64(val); + if (!(d >= 0 && d <= UINT32_MAX)) + goto fail; + len = (uint32_t)d; + if (len != d) + goto fail; + } else { + uint32_t len1; + + if (is_array_ctor) { + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len, val, TRUE)) + return -1; + } else { + /* legacy behavior: must do the conversion twice and compare */ + if (JS_ToUint32(ctx, &len, val)) { + JS_FreeValue(ctx, val); + return -1; + } + val = JS_ToNumberFree(ctx, val); + if (JS_IsException(val)) + return -1; + /* cannot recurse because val is a number */ + if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE)) + return -1; + if (len1 != len) { + fail: + JS_ThrowRangeError(ctx, "invalid array length"); + return -1; + } + } + } + break; + } + *plen = len; + return 0; +} + +#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1) + +static BOOL is_safe_integer(double d) +{ + return isfinite(d) && floor(d) == d && + fabs(d) <= (double)MAX_SAFE_INTEGER; +} + +int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValue val) +{ + int64_t v; + if (JS_ToInt64Sat(ctx, &v, val)) + return -1; + if (v < 0 || v > MAX_SAFE_INTEGER) { + JS_ThrowRangeError(ctx, "invalid array index"); + *plen = 0; + return -1; + } + *plen = v; + return 0; +} + +/* convert a value to a length between 0 and MAX_SAFE_INTEGER. + return -1 for exception */ +static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, + JSValue val) +{ + int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0); + JS_FreeValue(ctx, val); + return res; +} + +/* Note: can return an exception */ +static int JS_NumberIsInteger(JSContext *ctx, JSValue val) +{ + double d; + if (!JS_IsNumber(val)) + return FALSE; + if (unlikely(JS_ToFloat64(ctx, &d, val))) + return -1; + return isfinite(d) && floor(d) == d; +} + +static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValue val) +{ + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + { + int v; + v = JS_VALUE_GET_INT(val); + return (v < 0); + } + case JS_TAG_FLOAT64: + { + JSFloat64Union u; + u.d = JS_VALUE_GET_FLOAT64(val); + return (u.u64 >> 63); + } + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + /* Note: integer zeros are not necessarily positive */ + return p->num.sign && !bf_is_zero(&p->num); + } + default: + return FALSE; + } +} + +static JSValue js_bigint_to_string1(JSContext *ctx, JSValue val, int radix) +{ + JSValue ret; + bf_t a_s, *a; + char *str; + int saved_sign; + size_t len; + + a = JS_ToBigInt(ctx, &a_s, val); + if (!a) + return JS_EXCEPTION; + saved_sign = a->sign; + if (a->expn == BF_EXP_ZERO) + a->sign = 0; + str = bf_ftoa(&len, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC | + BF_FTOA_JS_QUIRKS); + a->sign = saved_sign; + JS_FreeBigInt(ctx, a, &a_s); + if (!str) + return JS_ThrowOutOfMemory(ctx); + ret = js_new_string8_len(ctx, str, len); + bf_free(ctx->bf_ctx, str); + return ret; +} + +static JSValue js_bigint_to_string(JSContext *ctx, JSValue val) +{ + return js_bigint_to_string1(ctx, val, 10); +} + +/*---- floating point number to string conversions ----*/ + +/* JavaScript rounding is specified as round to nearest tie away + from zero (RNDNA), but in `printf` the "ties" case is not + specified (in most cases it is RNDN, round to nearest, tie to even), + so we must round manually. We generate 2 extra places and make + an extra call to snprintf if these are exactly '50'. + We set the current rounding mode to FE_DOWNWARD to check if the + last 2 places become '49'. If not, we must round up, which is + performed in place using the string digits. + + Note that we cannot rely on snprintf for rounding up: + the code below fails on macOS for `0.5.toFixed(0)`: gives `0` expected `1` + fesetround(FE_UPWARD); + snprintf(dest, size, "%.*f", n_digits, d); + fesetround(FE_TONEAREST); + */ + +/* `js_fcvt` minimum buffer length: + - up to 21 digits in integral part + - 1 potential decimal point + - up to 102 decimals + - 1 null terminator + */ +#define JS_FCVT_BUF_SIZE (21+1+102+1) + +/* `js_ecvt` minimum buffer length: + - 1 leading digit + - 1 potential decimal point + - up to 102 decimals + - 5 exponent characters (from 'e-324' to 'e+308') + - 1 null terminator + */ +#define JS_ECVT_BUF_SIZE (1+1+102+5+1) + +/* `js_dtoa` minimum buffer length: + - 8 byte prefix + - either JS_FCVT_BUF_SIZE or JS_ECVT_BUF_SIZE + - JS_FCVT_BUF_SIZE is larger than JS_ECVT_BUF_SIZE + */ +#define JS_DTOA_BUF_SIZE (8+JS_FCVT_BUF_SIZE) + +/* `js_ecvt1`: compute the digits and decimal point spot for a double + - `d` is finite, positive or zero + - `n_digits` number of significant digits in range 1..103 + - `buf` receives the printf result + - `buf` has a fixed format: n_digits with a decimal point at offset 1 + and exponent 'e{+/-}xx[x]' at offset n_digits+1 + Return n_digits + Store the position of the decimal point into `*decpt` + */ +static int js_ecvt1(double d, int n_digits, + char dest[minimum_length(JS_ECVT_BUF_SIZE)], + size_t size, int *decpt) +{ + /* d is positive, ensure decimal point is always present */ + snprintf(dest, size, "%#.*e", n_digits - 1, d); + /* dest contents: + 0: first digit + 1: '.' decimal point (locale specific) + 2..n_digits: (n_digits-1) additional digits + n_digits+1: 'e' exponent mark + n_digits+2..: exponent sign, value and null terminator + */ + /* extract the exponent (actually the position of the decimal point) */ + *decpt = 1 + atoi(dest + n_digits + 2); + return n_digits; +} + +/* `js_ecvt`: compute the digits and decimal point spot for a double + with proper javascript rounding. We cannot use `ecvt` for multiple + resasons: portability, because of the number of digits is typically + limited to 17, finally because the default rounding is inadequate. + `d` is finite and positive or zero. + `n_digits` number of significant digits in range 1..101 + or 0 for automatic (only as many digits as necessary) + Return the number of digits produced in `dest`. + Store the position of the decimal point into `*decpt` + */ +static int js_ecvt(double d, int n_digits, + char dest[minimum_length(JS_ECVT_BUF_SIZE)], + size_t size, int *decpt) +{ + if (n_digits == 0) { + /* find the minimum number of digits (XXX: inefficient but simple) */ + // TODO(chqrlie) use direct method from quickjs-printf + unsigned int n_digits_min = 1; + unsigned int n_digits_max = 17; + for (;;) { + n_digits = (n_digits_min + n_digits_max) / 2; + js_ecvt1(d, n_digits, dest, size, decpt); + if (n_digits_min == n_digits_max) + return n_digits; + /* dest contents: + 0: first digit + 1: '.' decimal point (locale specific) + 2..n_digits: (n_digits-1) additional digits + n_digits+1: 'e' exponent mark + n_digits+2..: exponent sign, value and null terminator + */ + if (strtod(dest, NULL) == d) { + unsigned int n0 = n_digits; + /* enough digits */ + /* strip the trailing zeros */ + while (dest[n_digits] == '0') + n_digits--; + if (n_digits == n_digits_min) + return n_digits; + /* done if trailing zeros and not denormal or huge */ + if (n_digits < n0 && d > 3e-308 && d < 8e307) + return n_digits; + n_digits_max = n_digits; + } else { + /* need at least one more digit */ + n_digits_min = n_digits + 1; + } + } + } else { +#if defined(FE_DOWNWARD) && defined(FE_TONEAREST) + /* generate 2 extra digits: 99% chances to avoid 2 calls */ + js_ecvt1(d, n_digits + 2, dest, size, decpt); + if (dest[n_digits + 1] < '5') + return n_digits; /* truncate the 2 extra digits */ + if (dest[n_digits + 1] == '5' && dest[n_digits + 2] == '0') { + /* close to half-way: try rounding toward 0 */ + fesetround(FE_DOWNWARD); + js_ecvt1(d, n_digits + 2, dest, size, decpt); + fesetround(FE_TONEAREST); + if (dest[n_digits + 1] < '5') + return n_digits; /* truncate the 2 extra digits */ + } + /* round up in the string */ + for(int i = n_digits;; i--) { + /* ignore the locale specific decimal point */ + if (is_digit(dest[i])) { + if (dest[i]++ < '9') + break; + dest[i] = '0'; + if (i == 0) { + dest[0] = '1'; + (*decpt)++; + break; + } + } + } + return n_digits; /* truncate the 2 extra digits */ +#else + /* No disambiguation available, eg: __wasi__ targets */ + return js_ecvt1(d, n_digits, dest, size, decpt); +#endif + } +} + +/* `js_fcvt`: convert a floating point value to %f format using RNDNA + `d` is finite and positive or zero. + `n_digits` number of decimal places in range 0..100 + Return the number of characters produced in `dest`. + */ +static size_t js_fcvt(double d, int n_digits, + char dest[minimum_length(JS_FCVT_BUF_SIZE)], size_t size) +{ +#if defined(FE_DOWNWARD) && defined(FE_TONEAREST) + int i, n1; + /* generate 2 extra digits: 99% chances to avoid 2 calls */ + n1 = snprintf(dest, size, "%.*f", n_digits + 2, d) - 2; + if (dest[n1] >= '5') { + if (dest[n1] == '5' && dest[n1 + 1] == '0') { + /* close to half-way: try rounding toward 0 */ + fesetround(FE_DOWNWARD); + n1 = snprintf(dest, size, "%.*f", n_digits + 2, d) - 2; + fesetround(FE_TONEAREST); + } + if (dest[n1] >= '5') { /* number should be rounded up */ + /* d is either exactly half way or greater: round the string manually */ + for (i = n1 - 1;; i--) { + /* ignore the locale specific decimal point */ + if (is_digit(dest[i])) { + if (dest[i]++ < '9') + break; + dest[i] = '0'; + if (i == 0) { + dest[0] = '1'; + dest[n1] = '0'; + dest[n1 - n_digits - 1] = '0'; + dest[n1 - n_digits] = '.'; + n1++; + break; + } + } + } + } + } + /* truncate the extra 2 digits and the decimal point if !n_digits */ + n1 -= !n_digits; + //dest[n1] = '\0'; // optional + return n1; +#else + /* No disambiguation available, eg: __wasi__ targets */ + return snprintf(dest, size, "%.*f", n_digits, d); +#endif +} + +static JSValue js_dtoa_infinite(JSContext *ctx, double d) +{ + // TODO(chqrlie) use atoms for NaN and Infinite? + if (isnan(d)) + return js_new_string8(ctx, "NaN"); + if (d < 0) + return js_new_string8(ctx, "-Infinity"); + else + return js_new_string8(ctx, "Infinity"); +} + +#define JS_DTOA_TOSTRING 0 /* use as many digits as necessary */ +#define JS_DTOA_EXPONENTIAL 1 /* use exponential notation either fixed or variable digits */ +#define JS_DTOA_FIXED 2 /* force fixed number of fractional digits */ +#define JS_DTOA_PRECISION 3 /* use n_digits significant digits (1 <= n_digits <= 101) */ + +/* `js_dtoa`: convert a floating point number to a string + - `mode`: one of the 4 supported formats + - `n_digits`: digit number according to mode + - TOSTRING: 0 only. As many digits as necessary + - EXPONENTIAL: 0 as many decimals as necessary + - 1..101 number of significant digits + - FIXED: 0..100 number of decimal places + - PRECISION: 1..101 number of significant digits + */ +// XXX: should use libbf or quickjs-printf. +static JSValue js_dtoa(JSContext *ctx, double d, int n_digits, int mode) +{ + char buf[JS_DTOA_BUF_SIZE]; + size_t len; + char *start; + int sign, decpt, exp, i, k, n, n_max; + + if (!isfinite(d)) + return js_dtoa_infinite(ctx, d); + + sign = (d < 0); + start = buf + 8; + d = fabs(d); /* also converts -0 to 0 */ + + if (mode != JS_DTOA_EXPONENTIAL && n_digits == 0) { + /* fast path for exact integers in variable format: + clip to MAX_SAFE_INTEGER because to ensure insignificant + digits are generated as 0. + used for JS_DTOA_TOSTRING and JS_DTOA_FIXED without decimals. + */ + if (d <= (double)MAX_SAFE_INTEGER) { + uint64_t u64 = (uint64_t)d; + if (d == u64) { + len = u64toa(start, u64); + goto done; + } + } + } + if (mode == JS_DTOA_FIXED) { + len = js_fcvt(d, n_digits, start, sizeof(buf) - 8); + // TODO(chqrlie) patch the locale specific decimal point + goto done; + } + + n_max = (n_digits > 0) ? n_digits : 21; + /* the number has k digits (1 <= k <= n_max) */ + k = js_ecvt(d, n_digits, start, sizeof(buf) - 8, &decpt); + /* buffer contents: + 0: first digit + 1: '.' decimal point + 2..k: (k-1) additional digits + */ + n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */ + if (mode != JS_DTOA_EXPONENTIAL) { + /* mode is JS_DTOA_PRECISION or JS_DTOA_TOSTRING */ + if (n >= 1 && n <= n_max) { + /* between 1 and n_max digits before the decimal point */ + if (k <= n) { + /* all digits before the point, append zeros */ + start[1] = start[0]; + start++; + for(i = k; i < n; i++) + start[i] = '0'; + len = n; + } else { + /* k > n: move digits before the point */ + for(i = 1; i < n; i++) + start[i] = start[i + 1]; + start[i] = '.'; + len = 1 + k; + } + goto done; + } + if (n >= -5 && n <= 0) { + /* insert -n leading 0 decimals and a '0.' prefix */ + n = -n; + start[1] = start[0]; + start -= n + 1; + start[0] = '0'; + start[1] = '.'; + for(i = 0; i < n; i++) + start[2 + i] = '0'; + len = 2 + k + n; + goto done; + } + } + /* exponential notation */ + exp = n - 1; + /* count the digits and the decimal point if at least one decimal */ + len = k + (k > 1); + start[1] = '.'; /* patch the locale specific decimal point */ + start[len] = 'e'; + start[len + 1] = '+'; + if (exp < 0) { + start[len + 1] = '-'; + exp = -exp; + } + len += 2 + 1 + (exp > 9) + (exp > 99); + for (i = len - 1; exp > 9;) { + int quo = exp / 10; + start[i--] = (char)('0' + exp % 10); + exp = quo; + } + start[i] = (char)('0' + exp); + + done: + start[-1] = '-'; /* prepend the sign if negative */ + return js_new_string8_len(ctx, start - sign, len + sign); +} + +/* `js_dtoa_radix`: convert a floating point number using a specific base + - `d` must be finite + - `radix` must be in range 2..36 + */ +static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix) +{ + char buf[2200], *ptr, *ptr2, *ptr3; + int sign, digit; + double frac, d0; + int64_t n0; + + if (!isfinite(d)) + return js_dtoa_infinite(ctx, d); + + sign = (d < 0); + d = fabs(d); + d0 = trunc(d); + n0 = 0; + frac = d - d0; + ptr2 = buf + 1100; /* ptr2 points to the end of the string */ + ptr = ptr2; /* ptr points to the beginning of the string */ + if (d0 <= MAX_SAFE_INTEGER) { + int64_t n = n0 = (int64_t)d0; + while (n >= radix) { + digit = n % radix; + n = n / radix; + *--ptr = digits36[digit]; + } + *--ptr = digits36[(size_t)n]; + } else { + /* no decimals */ + while (d0 >= radix) { + digit = fmod(d0, radix); + d0 = trunc(d0 / radix); + if (d0 >= MAX_SAFE_INTEGER) + digit = 0; + *--ptr = digits36[digit]; + } + *--ptr = digits36[(size_t)d0]; + goto done; + } + if (frac != 0) { + double log2_radix = log2(radix); + double prec = 1023 + 51; // handle subnormals + *ptr2++ = '.'; + while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) { + frac *= radix; + digit = trunc(frac); + frac -= digit; + *ptr2++ = digits36[digit]; + n0 = n0 * radix + digit; + prec -= log2_radix; + } + if (frac * radix >= radix / 2) { + /* round up the string representation manually */ + char nine = digits36[radix - 1]; + while (ptr2[-1] == nine) { + /* strip trailing '9' or equivalent digits */ + ptr2--; + } + if (ptr2[-1] == '.') { + /* strip the 'decimal' point */ + ptr2--; + /* increment the integral part */ + for (ptr3 = ptr2;;) { + if (ptr3[-1] != nine) { + ptr3[-1] = (ptr3[-1] == '9') ? 'a' : ptr3[-1] + 1; + break; + } + *--ptr3 = '0'; + if (ptr3 <= ptr) { + /* prepend a '1' if number was all nines */ + *--ptr = '1'; + break; + } + } + } else { + /* increment the last fractional digit */ + ptr2[-1] = (ptr2[-1] == '9') ? 'a' : ptr2[-1] + 1; + } + } else { + /* strip trailing fractional zeros */ + while (ptr2[-1] == '0') + ptr2--; + /* strip the 'decimal' point if last */ + ptr2 -= (ptr2[-1] == '.'); + } + } +done: + ptr[-1] = '-'; + ptr -= sign; + return js_new_string8_len(ctx, ptr, ptr2 - ptr); +} + +JSValue JS_ToStringInternal(JSContext *ctx, JSValue val, BOOL is_ToPropertyKey) +{ + uint32_t tag; + char buf[32]; + size_t len; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_STRING: + return js_dup(val); + case JS_TAG_INT: + len = i32toa(buf, JS_VALUE_GET_INT(val)); + return js_new_string8_len(ctx, buf, len); + case JS_TAG_BOOL: + return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ? + JS_ATOM_true : JS_ATOM_false); + case JS_TAG_NULL: + return JS_AtomToString(ctx, JS_ATOM_null); + case JS_TAG_UNDEFINED: + return JS_AtomToString(ctx, JS_ATOM_undefined); + case JS_TAG_EXCEPTION: + return JS_EXCEPTION; + case JS_TAG_OBJECT: + { + JSValue val1, ret; + val1 = JS_ToPrimitive(ctx, val, HINT_STRING); + if (JS_IsException(val1)) + return val1; + ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey); + JS_FreeValue(ctx, val1); + return ret; + } + break; + case JS_TAG_FUNCTION_BYTECODE: + return js_new_string8(ctx, "[function bytecode]"); + case JS_TAG_SYMBOL: + if (is_ToPropertyKey) { + return js_dup(val); + } else { + return JS_ThrowTypeError(ctx, "cannot convert symbol to string"); + } + case JS_TAG_FLOAT64: + return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 0, JS_DTOA_TOSTRING); + case JS_TAG_BIG_INT: + return js_bigint_to_string(ctx, val); + default: + return js_new_string8(ctx, "[unsupported type]"); + } +} + +JSValue JS_ToString(JSContext *ctx, JSValue val) +{ + return JS_ToStringInternal(ctx, val, FALSE); +} + +static JSValue JS_ToStringFree(JSContext *ctx, JSValue val) +{ + JSValue ret; + ret = JS_ToString(ctx, val); + JS_FreeValue(ctx, val); + return ret; +} + +static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val) +{ + if (JS_IsUndefined(val) || JS_IsNull(val)) + return JS_ToStringFree(ctx, val); + return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL); +} + +JSValue JS_ToPropertyKey(JSContext *ctx, JSValue val) +{ + return JS_ToStringInternal(ctx, val, TRUE); +} + +static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValue val) +{ + uint32_t tag = JS_VALUE_GET_TAG(val); + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) + return JS_ThrowTypeError(ctx, "null or undefined are forbidden"); + return JS_ToString(ctx, val); +} + +static JSValue JS_ToQuotedString(JSContext *ctx, JSValue val1) +{ + JSValue val; + JSString *p; + int i; + uint32_t c; + StringBuffer b_s, *b = &b_s; + char buf[16]; + + val = JS_ToStringCheckObject(ctx, val1); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_STRING(val); + + if (string_buffer_init(ctx, b, p->len + 2)) + goto fail; + + if (string_buffer_putc8(b, '\"')) + goto fail; + for(i = 0; i < p->len; ) { + c = string_getc(p, &i); + switch(c) { + case '\t': + c = 't'; + goto quote; + case '\r': + c = 'r'; + goto quote; + case '\n': + c = 'n'; + goto quote; + case '\b': + c = 'b'; + goto quote; + case '\f': + c = 'f'; + goto quote; + case '\"': + case '\\': + quote: + if (string_buffer_putc8(b, '\\')) + goto fail; + if (string_buffer_putc8(b, c)) + goto fail; + break; + default: + if (c < 32 || is_surrogate(c)) { + snprintf(buf, sizeof(buf), "\\u%04x", c); + if (string_buffer_write8(b, (uint8_t*)buf, 6)) + goto fail; + } else { + if (string_buffer_putc(b, c)) + goto fail; + } + break; + } + } + if (string_buffer_putc8(b, '\"')) + goto fail; + JS_FreeValue(ctx, val); + return string_buffer_end(b); + fail: + JS_FreeValue(ctx, val); + string_buffer_free(b); + return JS_EXCEPTION; +} + +static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) +{ + printf("%14s %4s %4s %14s %10s %s\n", + "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS"); +} + +/* for debug only: dump an object without side effect */ +static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) +{ + uint32_t i; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + JSShape *sh; + JSShapeProperty *prs; + JSProperty *pr; + BOOL is_first = TRUE; + + /* XXX: should encode atoms with special characters */ + sh = p->shape; /* the shape can be NULL while freeing an object */ + printf("%14p %4d ", + (void *)p, + p->header.ref_count); + if (sh) { + printf("%3d%c %14p ", + sh->header.ref_count, + " *"[sh->is_hashed], + (void *)sh->proto); + } else { + printf("%3s %14s ", "-", "-"); + } + printf("%10s ", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name)); + if (p->is_exotic && p->fast_array) { + printf("[ "); + for(i = 0; i < p->u.array.count; i++) { + if (i != 0) + printf(", "); + switch (p->class_id) { + case JS_CLASS_ARRAY: + case JS_CLASS_ARGUMENTS: + JS_DumpValue(rt, p->u.array.u.values[i]); + break; + case JS_CLASS_UINT8C_ARRAY: + case JS_CLASS_INT8_ARRAY: + case JS_CLASS_UINT8_ARRAY: + case JS_CLASS_INT16_ARRAY: + case JS_CLASS_UINT16_ARRAY: + case JS_CLASS_INT32_ARRAY: + case JS_CLASS_UINT32_ARRAY: + case JS_CLASS_BIG_INT64_ARRAY: + case JS_CLASS_BIG_UINT64_ARRAY: + case JS_CLASS_FLOAT32_ARRAY: + case JS_CLASS_FLOAT64_ARRAY: + { + int size = 1 << typed_array_size_log2(p->class_id); + const uint8_t *b = p->u.array.u.uint8_ptr + i * size; + while (size-- > 0) + printf("%02X", *b++); + } + break; + } + } + printf(" ] "); + } + + if (sh) { + printf("{ "); + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + if (prs->atom != JS_ATOM_NULL) { + pr = &p->prop[i]; + if (!is_first) + printf(", "); + printf("%s: ", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom)); + if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) { + printf("[getset %p %p]", (void *)pr->u.getset.getter, + (void *)pr->u.getset.setter); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) { + printf("[varref %p]", (void *)pr->u.var_ref); + } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) { + printf("[autoinit %p %d %p]", + (void *)js_autoinit_get_realm(pr), + js_autoinit_get_id(pr), + (void *)pr->u.init.opaque); + } else { + JS_DumpValue(rt, pr->u.value); + } + is_first = FALSE; + } + } + printf(" }"); + } + + if (js_class_has_bytecode(p->class_id)) { + JSFunctionBytecode *b = p->u.func.function_bytecode; + JSVarRef **var_refs; + if (b->closure_var_count) { + var_refs = p->u.func.var_refs; + printf(" Closure:"); + for(i = 0; i < b->closure_var_count; i++) { + printf(" "); + JS_DumpValue(rt, var_refs[i]->value); + } + if (p->u.func.home_object) { + printf(" HomeObject: "); + JS_DumpValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object)); + } + } + } + printf("\n"); +} + +static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) +{ + if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { + JS_DumpObject(rt, (JSObject *)p); + } else { + printf("%14p %4d ", + (void *)p, + p->ref_count); + switch(p->gc_obj_type) { + case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: + printf("[function bytecode]"); + break; + case JS_GC_OBJ_TYPE_SHAPE: + printf("[shape]"); + break; + case JS_GC_OBJ_TYPE_VAR_REF: + printf("[var_ref]"); + break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + printf("[async_function]"); + break; + case JS_GC_OBJ_TYPE_JS_CONTEXT: + printf("[js_context]"); + break; + default: + printf("[unknown %d]", p->gc_obj_type); + break; + } + printf("\n"); + } +} + +static __maybe_unused void JS_DumpValue(JSRuntime *rt, JSValue val) +{ + uint32_t tag = JS_VALUE_GET_NORM_TAG(val); + const char *str; + + switch(tag) { + case JS_TAG_INT: + printf("%d", JS_VALUE_GET_INT(val)); + break; + case JS_TAG_BOOL: + if (JS_VALUE_GET_BOOL(val)) + str = "true"; + else + str = "false"; + goto print_str; + case JS_TAG_NULL: + str = "null"; + goto print_str; + case JS_TAG_EXCEPTION: + str = "exception"; + goto print_str; + case JS_TAG_UNINITIALIZED: + str = "uninitialized"; + goto print_str; + case JS_TAG_UNDEFINED: + str = "undefined"; + print_str: + printf("%s", str); + break; + case JS_TAG_FLOAT64: + printf("%.14g", JS_VALUE_GET_FLOAT64(val)); + break; + case JS_TAG_BIG_INT: + { + JSBigInt *p = JS_VALUE_GET_PTR(val); + char *str; + str = bf_ftoa(NULL, &p->num, 10, 0, + BF_RNDZ | BF_FTOA_FORMAT_FRAC); + printf("%sn", str); + bf_realloc(&rt->bf_ctx, str, 0); + } + break; + case JS_TAG_STRING: + { + JSString *p; + p = JS_VALUE_GET_STRING(val); + JS_DumpString(rt, p); + } + break; + case JS_TAG_FUNCTION_BYTECODE: + { + JSFunctionBytecode *b = JS_VALUE_GET_PTR(val); + char buf[ATOM_GET_STR_BUF_SIZE]; + if (b->func_name) { + printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name)); + } else { + printf("[bytecode (anonymous)]"); + } + } + break; + case JS_TAG_OBJECT: + { + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAtom atom = rt->class_array[p->class_id].class_name; + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + printf("[%s %p]", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p); + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p = JS_VALUE_GET_PTR(val); + char atom_buf[ATOM_GET_STR_BUF_SIZE]; + printf("Symbol(%s)", + JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p))); + } + break; + case JS_TAG_MODULE: + printf("[module]"); + break; + default: + printf("[unknown tag %d]", tag); + break; + } +} + +/* return -1 if exception (proxy case) or TRUE/FALSE */ +int JS_IsArray(JSContext *ctx, JSValue val) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(val); + if (unlikely(p->class_id == JS_CLASS_PROXY)) + return js_proxy_isArray(ctx, val); + else + return p->class_id == JS_CLASS_ARRAY; + } else { + return FALSE; + } +} + +static double js_math_pow(double a, double b) +{ + if (unlikely(!isfinite(b)) && fabs(a) == 1) { + /* not compatible with IEEE 754 */ + return JS_FLOAT64_NAN; + } else { + return pow(a, b); + } +} + +JSValue JS_NewBigInt64(JSContext *ctx, int64_t v) +{ + JSValue val; + bf_t *a; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + if (bf_set_si(a, v)) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + return val; +} + +JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) +{ + JSValue val; + bf_t *a; + val = JS_NewBigInt(ctx); + if (JS_IsException(val)) + return val; + a = JS_GetBigInt(val); + if (bf_set_ui(a, v)) { + JS_FreeValue(ctx, val); + return JS_ThrowOutOfMemory(ctx); + } + + return val; +} + +/* if the returned bigint is allocated it is equal to + 'buf'. Otherwise it is a pointer to the bigint in 'val'. Return + NULL in case of error. */ +// TODO(bnoordhuis) Merge with JS_ToBigInt() +static bf_t *JS_ToBigInt1(JSContext *ctx, bf_t *buf, JSValue val) +{ + uint32_t tag; + bf_t *r; + JSBigInt *p; + + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_BOOL: + case JS_TAG_NULL: + r = buf; + bf_init(ctx->bf_ctx, r); + if (bf_set_si(r, JS_VALUE_GET_INT(val))) + goto fail; + break; + case JS_TAG_FLOAT64: + r = buf; + bf_init(ctx->bf_ctx, r); + if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) { + fail: + bf_delete(r); + return NULL; + } + break; + case JS_TAG_BIG_INT: + p = JS_VALUE_GET_PTR(val); + r = &p->num; + break; + case JS_TAG_UNDEFINED: + default: + r = buf; + bf_init(ctx->bf_ctx, r); + bf_set_nan(r); + break; + } + return r; +} + +/* return NaN if bad bigint literal */ +static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) +{ + const char *str; + size_t len; + int flags; + + str = JS_ToCStringLen(ctx, &len, val); + JS_FreeValue(ctx, val); + if (!str) + return JS_EXCEPTION; + flags = ATOD_WANT_BIG_INT | + ATOD_TRIM_SPACES | ATOD_ACCEPT_EMPTY | + ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT | + ATOD_DECIMAL_AFTER_SIGN | ATOD_NO_TRAILING_CHARS; + val = js_atof(ctx, str, len, NULL, 10, flags); + JS_FreeCString(ctx, str); + return val; +} + +static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val) +{ + val = JS_StringToBigInt(ctx, val); + if (JS_VALUE_IS_NAN(val)) + return JS_ThrowSyntaxError(ctx, "invalid BigInt literal"); + return val; +} + +/* if the returned bigint is allocated it is equal to + 'buf'. Otherwise it is a pointer to the bigint in 'val'. */ +static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) +{ + uint32_t tag; + bf_t *r; + JSBigInt *p; + + redo: + tag = JS_VALUE_GET_NORM_TAG(val); + switch(tag) { + case JS_TAG_INT: + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + case JS_TAG_FLOAT64: + goto fail; + case JS_TAG_BOOL: + r = buf; + bf_init(ctx->bf_ctx, r); + bf_set_si(r, JS_VALUE_GET_INT(val)); + break; + case JS_TAG_BIG_INT: + p = JS_VALUE_GET_PTR(val); + r = &p->num; + break; + case JS_TAG_STRING: + val = JS_StringToBigIntErr(ctx, val); + if (JS_IsException(val)) + return NULL; + goto redo; + case JS_TAG_OBJECT: + val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER); + if (JS_IsException(val)) + return NULL; + goto redo; + default: + fail: + JS_FreeValue(ctx, val); + JS_ThrowTypeError(ctx, "cannot convert to BigInt"); + return NULL; + } + return r; +} + +static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValue val) +{ + return JS_ToBigIntFree(ctx, buf, js_dup(val)); +} + +static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val) +{ + if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) { + return val; + } else { + bf_t a_s, *a, *r; + int ret; + JSValue res; + + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) + return JS_EXCEPTION; + a = JS_ToBigIntFree(ctx, &a_s, val); + if (!a) { + JS_FreeValue(ctx, res); + return JS_EXCEPTION; + } + r = JS_GetBigInt(res); + ret = bf_set(r, a); + JS_FreeBigInt(ctx, a, &a_s); + if (ret) { + JS_FreeValue(ctx, res); + return JS_ThrowOutOfMemory(ctx); + } + return JS_CompactBigInt(ctx, res); + } +} + +/* free the bf_t allocated by JS_ToBigInt */ +static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf) +{ + if (a == buf) { + bf_delete(a); + } else { + JSBigInt *p = (JSBigInt *)((uint8_t *)a - offsetof(JSBigInt, num)); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_INT, p)); + } +} + +/* XXX: merge with JS_ToInt64Free with a specific flag */ +static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val) +{ + bf_t a_s, *a; + + a = JS_ToBigIntFree(ctx, &a_s, val); + if (!a) { + *pres = 0; + return -1; + } + bf_get_int64(pres, a, BF_GET_INT_MOD); + JS_FreeBigInt(ctx, a, &a_s); + return 0; +} + +int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValue val) +{ + return JS_ToBigInt64Free(ctx, pres, js_dup(val)); +} + +int JS_ToBigUint64(JSContext *ctx, uint64_t *pres, JSValue val) +{ + return JS_ToBigInt64Free(ctx, (int64_t *)pres, js_dup(val)); +} + +static JSValue JS_NewBigInt(JSContext *ctx) +{ + JSBigInt *p; + p = js_malloc(ctx, sizeof(*p)); + if (!p) + return JS_EXCEPTION; + p->header.ref_count = 1; + bf_init(ctx->bf_ctx, &p->num); + return JS_MKPTR(JS_TAG_BIG_INT, p); +} + +static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val) +{ + if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT) + return val; /* fail safe */ + bf_t *a = JS_GetBigInt(val); + if (a->expn == BF_EXP_ZERO && a->sign) { + assert(((JSBigInt*)JS_VALUE_GET_PTR(val))->header.ref_count == 1); + a->sign = 0; + } + return val; +} + +/* Nnormalize the zero representation. Could also be used to convert the bigint + to a short bigint value. The reference count of the value must be + 1. Cannot fail */ +static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val) +{ + return JS_CompactBigInt1(ctx, val); +} + +static JSValue throw_bf_exception(JSContext *ctx, int status) +{ + const char *str; + if (status & BF_ST_MEM_ERROR) + return JS_ThrowOutOfMemory(ctx); + if (status & BF_ST_DIVIDE_ZERO) { + str = "division by zero"; + } else if (status & BF_ST_INVALID_OP) { + str = "invalid operation"; + } else { + str = "integer overflow"; + } + return JS_ThrowRangeError(ctx, "%s", str); +} + +static int js_unary_arith_bigint(JSContext *ctx, + JSValue *pres, OPCodeEnum op, JSValue op1) +{ + bf_t a_s, *r, *a; + int ret, v; + JSValue res; + + if (op == OP_plus) { + JS_ThrowTypeError(ctx, "BigInt argument with unary +"); + JS_FreeValue(ctx, op1); + return -1; + } + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) { + JS_FreeValue(ctx, op1); + return -1; + } + r = JS_GetBigInt(res); + a = JS_ToBigIntFree(ctx, &a_s, op1); // infallible, always a bigint + ret = 0; + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ); + break; + case OP_plus: + ret = bf_set(r, a); + break; + case OP_neg: + ret = bf_set(r, a); + bf_neg(r); + break; + case OP_not: + ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ); + bf_neg(r); + break; + default: + abort(); + } + JS_FreeBigInt(ctx, a, &a_s); + if (unlikely(ret)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); + return -1; + } + res = JS_CompactBigInt(ctx, res); + *pres = res; + return 0; +} + +static no_inline __exception int js_unary_arith_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1; + int v; + uint32_t tag; + + op1 = sp[-1]; + /* fast path for float64 */ + if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) + goto handle_float64; + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) + goto exception; + tag = JS_VALUE_GET_TAG(op1); + switch(tag) { + case JS_TAG_INT: + { + int64_t v64; + v64 = JS_VALUE_GET_INT(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + v64 += v; + break; + case OP_plus: + break; + case OP_neg: + if (v64 == 0) { + sp[-1] = js_float64(-0.0); + return 0; + } else { + v64 = -v64; + } + break; + default: + abort(); + } + sp[-1] = JS_NewInt64(ctx, v64); + } + break; + case JS_TAG_BIG_INT: + if (js_unary_arith_bigint(ctx, sp - 1, op, op1)) + goto exception; + break; + default: + handle_float64: + { + double d = JS_VALUE_GET_FLOAT64(op1); + switch(op) { + case OP_inc: + case OP_dec: + v = 2 * (op - OP_dec) - 1; + d += v; + break; + case OP_plus: + break; + case OP_neg: + d = -d; + break; + default: + abort(); + } + sp[-1] = js_float64(d); + } + break; + } + return 0; + exception: + sp[-1] = JS_UNDEFINED; + return -1; +} + +static __exception int js_post_inc_slow(JSContext *ctx, + JSValue *sp, OPCodeEnum op) +{ + JSValue op1; + + /* XXX: allow custom operators */ + op1 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + sp[-1] = JS_UNDEFINED; + return -1; + } + sp[-1] = op1; + sp[0] = js_dup(op1); + return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec); +} + +static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1; + + op1 = JS_ToNumericFree(ctx, sp[-1]); + if (JS_IsException(op1)) + goto exception; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) { + if (js_unary_arith_bigint(ctx, sp - 1, OP_not, op1)) + goto exception; + } else { + int32_t v1; + if (unlikely(JS_ToInt32Free(ctx, &v1, op1))) + goto exception; + sp[-1] = js_int32(~v1); + } + return 0; + exception: + sp[-1] = JS_UNDEFINED; + return -1; +} + +static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, + JSValue *pres, JSValue op1, JSValue op2) +{ + bf_t a_s, b_s, *r, *a, *b; + int ret; + JSValue res; + + a = JS_ToBigIntFree(ctx, &a_s, op1); + if (!a) { + JS_FreeValue(ctx, op2); + return -1; + } + b = JS_ToBigIntFree(ctx, &b_s, op2); + if (!b) { + JS_FreeBigInt(ctx, a, &a_s); + return -1; + } + res = JS_NewBigInt(ctx); + if (JS_IsException(res)) { + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + return -1; + } + r = JS_GetBigInt(res); + ret = 0; + switch(op) { + case OP_add: + ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_sub: + ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_mul: + ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ); + break; + case OP_div: + { + bf_t rem_s, *rem = &rem_s; + bf_init(ctx->bf_ctx, rem); + ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ); + bf_delete(rem); + } + break; + case OP_mod: + ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, + BF_RNDZ) & BF_ST_INVALID_OP; + break; + case OP_pow: + if (b->sign) { + ret = BF_ST_INVALID_OP; + } else { + ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS); + } + break; + + /* logical operations */ + case OP_shl: + case OP_sar: + { + slimb_t v2; +#if LIMB_BITS == 32 + bf_get_int32(&v2, b, 0); + if (v2 == INT32_MIN) + v2 = INT32_MIN + 1; +#else + bf_get_int64(&v2, b, 0); + if (v2 == INT64_MIN) + v2 = INT64_MIN + 1; +#endif + if (op == OP_sar) + v2 = -v2; + ret = bf_set(r, a); + ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ); + if (v2 < 0) { + ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR); + } + } + break; + case OP_and: + ret = bf_logic_and(r, a, b); + break; + case OP_or: + ret = bf_logic_or(r, a, b); + break; + case OP_xor: + ret = bf_logic_xor(r, a, b); + break; + default: + abort(); + } + JS_FreeBigInt(ctx, a, &a_s); + JS_FreeBigInt(ctx, b, &b_s); + if (unlikely(ret)) { + JS_FreeValue(ctx, res); + throw_bf_exception(ctx, ret); + return -1; + } + *pres = JS_CompactBigInt(ctx, res); + return 0; +} + +static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + double d1, d2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* fast path for float operations */ + if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + d2 = JS_VALUE_GET_FLOAT64(op2); + goto handle_float64; + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + int32_t v1, v2; + int64_t v; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + switch(op) { + case OP_sub: + v = (int64_t)v1 - (int64_t)v2; + break; + case OP_mul: + v = (int64_t)v1 * (int64_t)v2; + if (v == 0 && (v1 | v2) < 0) { + sp[-2] = js_float64(-0.0); + return 0; + } + break; + case OP_div: + sp[-2] = js_float64((double)v1 / (double)v2); + return 0; + case OP_mod: + if (v1 < 0 || v2 <= 0) { + sp[-2] = js_number(fmod(v1, v2)); + return 0; + } else { + v = (int64_t)v1 % (int64_t)v2; + } + break; + case OP_pow: + sp[-2] = js_number(js_math_pow(v1, v2)); + return 0; + default: + abort(); + } + sp[-2] = JS_NewInt64(ctx, v); + } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + if (js_binary_arith_bigint(ctx, op, sp - 2, op1, op2)) + goto exception; + } else { + double dr; + /* float64 result */ + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + handle_float64: + switch(op) { + case OP_sub: + dr = d1 - d2; + break; + case OP_mul: + dr = d1 * d2; + break; + case OP_div: + dr = d1 / d2; + break; + case OP_mod: + dr = fmod(d1, d2); + break; + case OP_pow: + dr = js_math_pow(d1, d2); + break; + default: + abort(); + } + sp[-2] = js_float64(dr); + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + /* fast path for float64 */ + if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) { + double d1, d2; + d1 = JS_VALUE_GET_FLOAT64(op1); + d2 = JS_VALUE_GET_FLOAT64(op2); + sp[-2] = js_float64(d1 + d2); + return 0; + } + + if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + } + + if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) { + sp[-2] = JS_ConcatString(ctx, op1, op2); + if (JS_IsException(sp[-2])) + goto exception; + return 0; + } + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + int32_t v1, v2; + int64_t v; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + v = (int64_t)v1 + (int64_t)v2; + sp[-2] = JS_NewInt64(ctx, v); + } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + if (js_binary_arith_bigint(ctx, OP_add, sp - 2, op1, op2)) + goto exception; + } else { + double d1, d2; + /* float64 result */ + if (JS_ToFloat64Free(ctx, &d1, op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (JS_ToFloat64Free(ctx, &d2, op2)) + goto exception; + sp[-2] = js_float64(d1 + d2); + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline __exception int js_binary_logic_slow(JSContext *ctx, + JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + uint32_t tag1, tag2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + + tag1 = JS_VALUE_GET_TAG(op1); + tag2 = JS_VALUE_GET_TAG(op2); + if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + if (tag1 != tag2) { + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + JS_ThrowTypeError(ctx, "both operands must be BigInt"); + goto exception; + } else if (js_binary_arith_bigint(ctx, op, sp - 2, op1, op2)) { + goto exception; + } + } else { + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) { + JS_FreeValue(ctx, op2); + goto exception; + } + if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2))) + goto exception; + switch(op) { + case OP_shl: + r = v1 << (v2 & 0x1f); + break; + case OP_sar: + r = (int)v1 >> (v2 & 0x1f); + break; + case OP_and: + r = v1 & v2; + break; + case OP_or: + r = v1 | v2; + break; + case OP_xor: + r = v1 ^ v2; + break; + default: + abort(); + } + sp[-2] = js_int32(r); + } + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static int js_compare_bigint(JSContext *ctx, OPCodeEnum op, + JSValue op1, JSValue op2) +{ + bf_t a_s, b_s, *a, *b; + int res; + + a = JS_ToBigInt1(ctx, &a_s, op1); + if (!a) { + JS_FreeValue(ctx, op2); + return -1; + } + b = JS_ToBigInt1(ctx, &b_s, op2); + if (!b) { + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, op1); + return -1; + } + switch(op) { + case OP_lt: + res = bf_cmp_lt(a, b); /* if NaN return false */ + break; + case OP_lte: + res = bf_cmp_le(a, b); /* if NaN return false */ + break; + case OP_gt: + res = bf_cmp_lt(b, a); /* if NaN return false */ + break; + case OP_gte: + res = bf_cmp_le(b, a); /* if NaN return false */ + break; + case OP_eq: + res = bf_cmp_eq(a, b); /* if NaN return false */ + break; + default: + abort(); + } + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + return res; +} + +static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, + OPCodeEnum op) +{ + JSValue op1, op2; + int res; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) { + JSString *p1, *p2; + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + res = js_string_compare(ctx, p1, p2); + switch(op) { + case OP_lt: + res = (res < 0); + break; + case OP_lte: + res = (res <= 0); + break; + case OP_gt: + res = (res > 0); + break; + default: + case OP_gte: + res = (res >= 0); + break; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) && + (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) { + /* fast path for float64/int */ + goto float64_compare; + } else { + if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) || + (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING))) { + if (tag1 == JS_TAG_STRING) { + op1 = JS_StringToBigInt(ctx, op1); + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + goto invalid_bigint_string; + } + if (tag2 == JS_TAG_STRING) { + op2 = JS_StringToBigInt(ctx, op2); + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + invalid_bigint_string: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = FALSE; + goto done; + } + } + } else { + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + } + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + + if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) { + res = js_compare_bigint(ctx, op, op1, op2); + if (res < 0) + goto exception; + } else { + double d1, d2; + + float64_compare: + /* can use floating point comparison */ + if (tag1 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + } else { + d1 = JS_VALUE_GET_INT(op1); + } + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else { + d2 = JS_VALUE_GET_INT(op2); + } + switch(op) { + case OP_lt: + res = (d1 < d2); /* if NaN return false */ + break; + case OP_lte: + res = (d1 <= d2); /* if NaN return false */ + break; + case OP_gt: + res = (d1 > d2); /* if NaN return false */ + break; + default: + case OP_gte: + res = (d1 >= d2); /* if NaN return false */ + break; + } + } + } + done: + sp[-2] = js_bool(res); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static BOOL tag_is_number(uint32_t tag) +{ + return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT || + tag == JS_TAG_FLOAT64); +} + +static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, + BOOL is_neq) +{ + JSValue op1, op2; + int res; + uint32_t tag1, tag2; + + op1 = sp[-2]; + op2 = sp[-1]; + redo: + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + if (tag_is_number(tag1) && tag_is_number(tag2)) { + if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) { + res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); + } else if ((tag1 == JS_TAG_FLOAT64 && + (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) || + (tag2 == JS_TAG_FLOAT64 && + (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) { + double d1, d2; + if (tag1 == JS_TAG_FLOAT64) { + d1 = JS_VALUE_GET_FLOAT64(op1); + } else { + d1 = JS_VALUE_GET_INT(op1); + } + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else { + d2 = JS_VALUE_GET_INT(op2); + } + res = (d1 == d2); + } else { + res = js_compare_bigint(ctx, OP_eq, op1, op2); + if (res < 0) + goto exception; + } + } else if (tag1 == tag2) { + res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); + } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) || + (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) { + res = TRUE; + } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) || + (tag2 == JS_TAG_STRING && tag_is_number(tag1))) { + + if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT)) { + if (tag1 == JS_TAG_STRING) { + op1 = JS_StringToBigInt(ctx, op1); + if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT) + goto invalid_bigint_string; + } + if (tag2 == JS_TAG_STRING) { + op2 = JS_StringToBigInt(ctx, op2); + if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) { + invalid_bigint_string: + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + res = FALSE; + goto done; + } + } + } else { + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + } + res = js_strict_eq(ctx, op1, op2); + } else if (tag1 == JS_TAG_BOOL) { + op1 = js_int32(JS_VALUE_GET_INT(op1)); + goto redo; + } else if (tag2 == JS_TAG_BOOL) { + op2 = js_int32(JS_VALUE_GET_INT(op2)); + goto redo; + } else if ((tag1 == JS_TAG_OBJECT && + (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) || + (tag2 == JS_TAG_OBJECT && + (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) { + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + goto redo; + } else { + /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */ + if ((JS_IsHTMLDDA(ctx, op1) && + (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) || + (JS_IsHTMLDDA(ctx, op2) && + (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) { + res = TRUE; + } else { + res = FALSE; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + } + done: + sp[-2] = js_bool(res ^ is_neq); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + uint32_t v1, v2, r; + + op1 = sp[-2]; + op2 = sp[-1]; + op1 = JS_ToNumericFree(ctx, op1); + if (JS_IsException(op1)) { + JS_FreeValue(ctx, op2); + goto exception; + } + op2 = JS_ToNumericFree(ctx, op2); + if (JS_IsException(op2)) { + JS_FreeValue(ctx, op1); + goto exception; + } + + if ((JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT || + JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) { + JS_ThrowTypeError(ctx, "BigInt operands are forbidden for >>>"); + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + goto exception; + } + /* cannot give an exception */ + JS_ToUint32Free(ctx, &v1, op1); + JS_ToUint32Free(ctx, &v2, op2); + r = v1 >> (v2 & 0x1f); + sp[-2] = js_uint32(r); + return 0; + exception: + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +/* XXX: Should take JSValue arguments */ +static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, + JSStrictEqModeEnum eq_mode) +{ + BOOL res; + int tag1, tag2; + double d1, d2; + + tag1 = JS_VALUE_GET_NORM_TAG(op1); + tag2 = JS_VALUE_GET_NORM_TAG(op2); + switch(tag1) { + case JS_TAG_BOOL: + if (tag1 != tag2) { + res = FALSE; + } else { + res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2); + goto done_no_free; + } + break; + case JS_TAG_NULL: + case JS_TAG_UNDEFINED: + res = (tag1 == tag2); + break; + case JS_TAG_STRING: + { + JSString *p1, *p2; + if (tag1 != tag2) { + res = FALSE; + } else { + p1 = JS_VALUE_GET_STRING(op1); + p2 = JS_VALUE_GET_STRING(op2); + res = (js_string_compare(ctx, p1, p2) == 0); + } + } + break; + case JS_TAG_SYMBOL: + { + JSAtomStruct *p1, *p2; + if (tag1 != tag2) { + res = FALSE; + } else { + p1 = JS_VALUE_GET_PTR(op1); + p2 = JS_VALUE_GET_PTR(op2); + res = (p1 == p2); + } + } + break; + case JS_TAG_OBJECT: + if (tag1 != tag2) + res = FALSE; + else + res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2); + break; + case JS_TAG_INT: + d1 = JS_VALUE_GET_INT(op1); + if (tag2 == JS_TAG_INT) { + d2 = JS_VALUE_GET_INT(op2); + goto number_test; + } else if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + goto number_test; + } else { + res = FALSE; + } + break; + case JS_TAG_FLOAT64: + d1 = JS_VALUE_GET_FLOAT64(op1); + if (tag2 == JS_TAG_FLOAT64) { + d2 = JS_VALUE_GET_FLOAT64(op2); + } else if (tag2 == JS_TAG_INT) { + d2 = JS_VALUE_GET_INT(op2); + } else { + res = FALSE; + break; + } + number_test: + if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) { + JSFloat64Union u1, u2; + /* NaN is not always normalized, so this test is necessary */ + if (isnan(d1) || isnan(d2)) { + res = isnan(d1) == isnan(d2); + } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) { + res = (d1 == d2); /* +0 == -0 */ + } else { + u1.d = d1; + u2.d = d2; + res = (u1.u64 == u2.u64); /* +0 != -0 */ + } + } else { + res = (d1 == d2); /* if NaN return false and +0 == -0 */ + } + goto done_no_free; + case JS_TAG_BIG_INT: + { + bf_t a_s, *a, b_s, *b; + if (tag1 != tag2) { + res = FALSE; + break; + } + a = JS_ToBigInt1(ctx, &a_s, op1); + b = JS_ToBigInt1(ctx, &b_s, op2); + res = bf_cmp_eq(a, b); + if (a == &a_s) + bf_delete(a); + if (b == &b_s) + bf_delete(b); + } + break; + default: + res = FALSE; + break; + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + done_no_free: + return res; +} + +static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2) +{ + return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); +} + +static BOOL js_same_value(JSContext *ctx, JSValue op1, JSValue op2) +{ + return js_strict_eq2(ctx, js_dup(op1), js_dup(op2), JS_EQ_SAME_VALUE); +} + +static BOOL js_same_value_zero(JSContext *ctx, JSValue op1, JSValue op2) +{ + return js_strict_eq2(ctx, js_dup(op1), js_dup(op2), JS_EQ_SAME_VALUE_ZERO); +} + +static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp, + BOOL is_neq) +{ + BOOL res; + res = js_strict_eq(ctx, sp[-2], sp[-1]); + sp[-2] = js_bool(res ^ is_neq); + return 0; +} + +static __exception int js_operator_in(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + JSAtom atom; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + + if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "invalid 'in' operand"); + return -1; + } + atom = JS_ValueToAtom(ctx, op1); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + ret = JS_HasProperty(ctx, op2, atom); + JS_FreeAtom(ctx, atom); + if (ret < 0) + return -1; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static __exception int js_has_unscopable(JSContext *ctx, JSValue obj, + JSAtom atom) +{ + JSValue arr, val; + int ret; + + arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables); + if (JS_IsException(arr)) + return -1; + ret = 0; + if (JS_IsObject(arr)) { + val = JS_GetProperty(ctx, arr, atom); + ret = JS_ToBoolFree(ctx, val); + } + JS_FreeValue(ctx, arr); + return ret; +} + +static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + BOOL ret; + + op1 = sp[-2]; + op2 = sp[-1]; + ret = JS_IsInstanceOf(ctx, op1, op2); + if (ret < 0) + return ret; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static __exception int js_operator_typeof(JSContext *ctx, JSValue op1) +{ + JSAtom atom; + uint32_t tag; + + tag = JS_VALUE_GET_NORM_TAG(op1); + switch(tag) { + case JS_TAG_BIG_INT: + atom = JS_ATOM_bigint; + break; + case JS_TAG_INT: + case JS_TAG_FLOAT64: + atom = JS_ATOM_number; + break; + case JS_TAG_UNDEFINED: + atom = JS_ATOM_undefined; + break; + case JS_TAG_BOOL: + atom = JS_ATOM_boolean; + break; + case JS_TAG_STRING: + atom = JS_ATOM_string; + break; + case JS_TAG_OBJECT: + { + JSObject *p; + p = JS_VALUE_GET_OBJ(op1); + if (unlikely(p->is_HTMLDDA)) + atom = JS_ATOM_undefined; + else if (JS_IsFunction(ctx, op1)) + atom = JS_ATOM_function; + else + goto obj_type; + } + break; + case JS_TAG_NULL: + obj_type: + atom = JS_ATOM_object; + break; + case JS_TAG_SYMBOL: + atom = JS_ATOM_symbol; + break; + default: + atom = JS_ATOM_unknown; + break; + } + return atom; +} + +static __exception int js_operator_delete(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + JSAtom atom; + int ret; + + op1 = sp[-2]; + op2 = sp[-1]; + atom = JS_ValueToAtom(ctx, op2); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT); + JS_FreeAtom(ctx, atom); + if (unlikely(ret < 0)) + return -1; + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = js_bool(ret); + return 0; +} + +static JSValue js_throw_type_error(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + return JS_ThrowTypeError(ctx, "invalid property access"); +} + +/* XXX: not 100% compatible, but mozilla seems to use a similar + implementation to ensure that caller in non strict mode does not + throw (ES5 compatibility) */ +static JSValue js_function_proto_caller(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) { + return js_throw_type_error(ctx, this_val, 0, NULL); + } + return JS_UNDEFINED; +} + +static JSValue js_function_proto_fileName(JSContext *ctx, + JSValue this_val) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (b) { + return JS_AtomToString(ctx, b->filename); + } + return JS_UNDEFINED; +} + +static JSValue js_function_proto_int32(JSContext *ctx, + JSValue this_val, + int magic) +{ + JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val); + if (b) { + int *field = (int *) ((char *)b + magic); + return js_int32(*field); + } + return JS_UNDEFINED; +} + +static int js_arguments_define_own_property(JSContext *ctx, + JSValue this_obj, + JSAtom prop, JSValue val, + JSValue getter, JSValue setter, int flags) +{ + JSObject *p; + uint32_t idx; + p = JS_VALUE_GET_OBJ(this_obj); + /* convert to normal array when redefining an existing numeric field */ + if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) && + idx < p->u.array.count) { + if (convert_fast_array_to_array(ctx, p)) + return -1; + } + /* run the default define own property */ + return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter, + flags | JS_PROP_NO_EXOTIC); +} + +static const JSClassExoticMethods js_arguments_exotic_methods = { + .define_own_property = js_arguments_define_own_property, +}; + +static JSValue js_build_arguments(JSContext *ctx, int argc, JSValue *argv) +{ + JSValue val, *tab; + JSProperty *pr; + JSObject *p; + int i; + + val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_ARGUMENTS); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + + /* add the length field (cannot fail) */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + pr->u.value = js_int32(argc); + + /* initialize the fast array part */ + tab = NULL; + if (argc > 0) { + tab = js_malloc(ctx, sizeof(tab[0]) * argc); + if (!tab) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + for(i = 0; i < argc; i++) { + tab[i] = js_dup(argv[i]); + } + } + p->u.array.u.values = tab; + p->u.array.count = argc; + + JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, + js_dup(ctx->array_proto_values), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + /* add callee property to throw a TypeError in strict mode */ + JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED, + ctx->throw_type_error, ctx->throw_type_error, + JS_PROP_HAS_GET | JS_PROP_HAS_SET); + return val; +} + +#define GLOBAL_VAR_OFFSET 0x40000000 +#define ARGUMENT_VAR_OFFSET 0x20000000 + +/* legacy arguments object: add references to the function arguments */ +static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, + JSValue *argv, + JSStackFrame *sf, int arg_count) +{ + JSValue val; + JSProperty *pr; + JSObject *p; + int i; + + val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], + JS_CLASS_MAPPED_ARGUMENTS); + if (JS_IsException(val)) + return val; + p = JS_VALUE_GET_OBJ(val); + + /* add the length field (cannot fail) */ + pr = add_property(ctx, p, JS_ATOM_length, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + pr->u.value = js_int32(argc); + + for(i = 0; i < arg_count; i++) { + JSVarRef *var_ref; + var_ref = get_var_ref(ctx, sf, i, TRUE); + if (!var_ref) + goto fail; + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); + if (!pr) { + free_var_ref(ctx->rt, var_ref); + goto fail; + } + pr->u.var_ref = var_ref; + } + + /* the arguments not mapped to the arguments of the function can + be normal properties */ + for(i = arg_count; i < argc; i++) { + if (JS_DefinePropertyValueUint32(ctx, val, i, + js_dup(argv[i]), + JS_PROP_C_W_E) < 0) + goto fail; + } + + JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator, + js_dup(ctx->array_proto_values), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + /* callee returns this function in non strict mode */ + JS_DefinePropertyValue(ctx, val, JS_ATOM_callee, + js_dup(ctx->rt->current_stack_frame->cur_func), + JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE); + return val; + fail: + JS_FreeValue(ctx, val); + return JS_EXCEPTION; +} + +static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValue *argv) +{ + JSValue val; + int i, ret; + + val = JS_NewArray(ctx); + if (JS_IsException(val)) + return val; + for (i = first; i < argc; i++) { + ret = JS_DefinePropertyValueUint32(ctx, val, i - first, + js_dup(argv[i]), + JS_PROP_C_W_E); + if (ret < 0) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + } + return val; +} + +static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) +{ + JSObject *p; + JSPropertyEnum *tab_atom; + int i; + JSValue enum_obj, obj1; + JSForInIterator *it; + uint32_t tag, tab_atom_count; + + tag = JS_VALUE_GET_TAG(obj); + if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) { + obj = JS_ToObjectFree(ctx, obj); + } + + it = js_malloc(ctx, sizeof(*it)); + if (!it) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR); + if (JS_IsException(enum_obj)) { + js_free(ctx, it); + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + it->is_array = FALSE; + it->obj = obj; + it->idx = 0; + p = JS_VALUE_GET_OBJ(enum_obj); + p->u.for_in_iterator = it; + + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) + return enum_obj; + + /* fast path: assume no enumerable properties in the prototype chain */ + obj1 = js_dup(obj); + for(;;) { + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + if (tab_atom_count != 0) { + JS_FreeValue(ctx, obj1); + goto slow_path; + } + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + } + + p = JS_VALUE_GET_OBJ(obj); + + if (p->fast_array) { + JSShape *sh; + JSShapeProperty *prs; + /* check that there are no enumerable normal fields */ + sh = p->shape; + for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) { + if (prs->flags & JS_PROP_ENUMERABLE) + goto normal_case; + } + /* for fast arrays, we only store the number of elements */ + it->is_array = TRUE; + it->array_length = p->u.array.count; + } else { + normal_case: + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) + goto fail; + for(i = 0; i < tab_atom_count; i++) { + JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + } + return enum_obj; + + slow_path: + /* non enumerable properties hide the enumerables ones in the + prototype chain */ + obj1 = js_dup(obj); + for(;;) { + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(obj1), + JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + for(i = 0; i < tab_atom_count; i++) { + JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL, + (tab_atom[i].is_enumerable ? + JS_PROP_ENUMERABLE : 0)); + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + obj1 = JS_GetPrototypeFree(ctx, obj1); + if (JS_IsNull(obj1)) + break; + if (JS_IsException(obj1)) + goto fail; + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) { + JS_FreeValue(ctx, obj1); + goto fail; + } + } + return enum_obj; + + fail: + JS_FreeValue(ctx, enum_obj); + return JS_EXCEPTION; +} + +/* obj -> enum_obj */ +static __exception int js_for_in_start(JSContext *ctx, JSValue *sp) +{ + sp[-1] = build_for_in_iterator(ctx, sp[-1]); + if (JS_IsException(sp[-1])) + return -1; + return 0; +} + +/* enum_obj -> enum_obj value done */ +static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) +{ + JSValue enum_obj; + JSObject *p; + JSAtom prop; + JSForInIterator *it; + int ret; + + enum_obj = sp[-1]; + /* fail safe */ + if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT) + goto done; + p = JS_VALUE_GET_OBJ(enum_obj); + if (p->class_id != JS_CLASS_FOR_IN_ITERATOR) + goto done; + it = p->u.for_in_iterator; + + for(;;) { + if (it->is_array) { + if (it->idx >= it->array_length) + goto done; + prop = __JS_AtomFromUInt32(it->idx); + it->idx++; + } else { + JSShape *sh = p->shape; + JSShapeProperty *prs; + if (it->idx >= sh->prop_count) + goto done; + prs = get_shape_prop(sh) + it->idx; + prop = prs->atom; + it->idx++; + if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE)) + continue; + } + // check if the property was deleted unless we're dealing with a proxy + JSValue obj = it->obj; + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_PROXY) + break; + } + ret = JS_HasProperty(ctx, obj, prop); + if (ret < 0) + return ret; + if (ret) + break; + } + /* return the property */ + sp[0] = JS_AtomToValue(ctx, prop); + sp[1] = JS_FALSE; + return 0; + done: + /* return the end */ + sp[0] = JS_UNDEFINED; + sp[1] = JS_TRUE; + return 0; +} + +static JSValue JS_GetIterator2(JSContext *ctx, JSValue obj, + JSValue method) +{ + JSValue enum_obj; + + enum_obj = JS_Call(ctx, method, obj, 0, NULL); + if (JS_IsException(enum_obj)) + return enum_obj; + if (!JS_IsObject(enum_obj)) { + JS_FreeValue(ctx, enum_obj); + return JS_ThrowTypeErrorNotAnObject(ctx); + } + return enum_obj; +} + +static JSValue JS_GetIterator(JSContext *ctx, JSValue obj, BOOL is_async) +{ + JSValue method, ret, sync_iter; + + if (is_async) { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator); + if (JS_IsException(method)) + return method; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return method; + sync_iter = JS_GetIterator2(ctx, obj, method); + JS_FreeValue(ctx, method); + if (JS_IsException(sync_iter)) + return sync_iter; + ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter); + JS_FreeValue(ctx, sync_iter); + return ret; + } + } else { + method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator); + if (JS_IsException(method)) + return method; + } + if (!JS_IsFunction(ctx, method)) { + JS_FreeValue(ctx, method); + return JS_ThrowTypeError(ctx, "value is not iterable"); + } + ret = JS_GetIterator2(ctx, obj, method); + JS_FreeValue(ctx, method); + return ret; +} + +/* return *pdone = 2 if the iterator object is not parsed */ +static JSValue JS_IteratorNext2(JSContext *ctx, JSValue enum_obj, + JSValue method, + int argc, JSValue *argv, int *pdone) +{ + JSValue obj; + + /* fast path for the built-in iterators (avoid creating the + intermediate result object) */ + if (JS_IsObject(method)) { + JSObject *p = JS_VALUE_GET_OBJ(method); + if (p->class_id == JS_CLASS_C_FUNCTION && + p->u.cfunc.cproto == JS_CFUNC_iterator_next) { + JSCFunctionType func; + JSValue args[1]; + + /* in case the function expects one argument */ + if (argc == 0) { + args[0] = JS_UNDEFINED; + argv = args; + } + func = p->u.cfunc.c_function; + return func.iterator_next(ctx, enum_obj, argc, argv, + pdone, p->u.cfunc.magic); + } + } + obj = JS_Call(ctx, method, enum_obj, argc, argv); + if (JS_IsException(obj)) + goto fail; + if (!JS_IsObject(obj)) { + JS_FreeValue(ctx, obj); + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto fail; + } + *pdone = 2; + return obj; + fail: + *pdone = FALSE; + return JS_EXCEPTION; +} + +static JSValue JS_IteratorNext(JSContext *ctx, JSValue enum_obj, + JSValue method, + int argc, JSValue *argv, BOOL *pdone) +{ + JSValue obj, value, done_val; + int done; + + obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done); + if (JS_IsException(obj)) + goto fail; + if (done != 2) { + *pdone = done; + return obj; + } else { + done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); + if (JS_IsException(done_val)) + goto fail; + *pdone = JS_ToBoolFree(ctx, done_val); + value = JS_UNDEFINED; + if (!*pdone) { + value = JS_GetProperty(ctx, obj, JS_ATOM_value); + } + JS_FreeValue(ctx, obj); + return value; + } + fail: + JS_FreeValue(ctx, obj); + *pdone = FALSE; + return JS_EXCEPTION; +} + +/* return < 0 in case of exception */ +static int JS_IteratorClose(JSContext *ctx, JSValue enum_obj, + BOOL is_exception_pending) +{ + JSValue method, ret, ex_obj; + int res; + + if (is_exception_pending) { + ex_obj = ctx->rt->current_exception; + ctx->rt->current_exception = JS_NULL; + res = -1; + } else { + ex_obj = JS_UNDEFINED; + res = 0; + } + method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return); + if (JS_IsException(method)) { + res = -1; + goto done; + } + if (JS_IsUndefined(method) || JS_IsNull(method)) { + goto done; + } + ret = JS_CallFree(ctx, method, enum_obj, 0, NULL); + if (!is_exception_pending) { + if (JS_IsException(ret)) { + res = -1; + } else if (!JS_IsObject(ret)) { + JS_ThrowTypeErrorNotAnObject(ctx); + res = -1; + } + } + JS_FreeValue(ctx, ret); + done: + if (is_exception_pending) { + JS_Throw(ctx, ex_obj); + } + return res; +} + +/* obj -> enum_rec (3 slots) */ +static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, + BOOL is_async) +{ + JSValue op1, obj, method; + op1 = sp[-1]; + obj = JS_GetIterator(ctx, op1, is_async); + if (JS_IsException(obj)) + return -1; + JS_FreeValue(ctx, op1); + sp[-1] = obj; + method = JS_GetProperty(ctx, obj, JS_ATOM_next); + if (JS_IsException(method)) + return -1; + sp[0] = method; + return 0; +} + +/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset' + objs. If 'done' is true or in case of exception, 'enum_rec' is set + to undefined. If 'done' is true, 'value' is always set to + undefined. */ +static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) +{ + JSValue value = JS_UNDEFINED; + int done = 1; + + if (likely(!JS_IsUndefined(sp[offset]))) { + value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done); + if (JS_IsException(value)) + done = -1; + if (done) { + /* value is JS_UNDEFINED or JS_EXCEPTION */ + /* replace the iteration object with undefined */ + JS_FreeValue(ctx, sp[offset]); + sp[offset] = JS_UNDEFINED; + if (done < 0) { + return -1; + } else { + JS_FreeValue(ctx, value); + value = JS_UNDEFINED; + } + } + } + sp[0] = value; + sp[1] = js_bool(done); + return 0; +} + +static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValue obj, + BOOL *pdone) +{ + JSValue done_val, value; + BOOL done; + done_val = JS_GetProperty(ctx, obj, JS_ATOM_done); + if (JS_IsException(done_val)) + goto fail; + done = JS_ToBoolFree(ctx, done_val); + value = JS_GetProperty(ctx, obj, JS_ATOM_value); + if (JS_IsException(value)) + goto fail; + *pdone = done; + return value; + fail: + *pdone = FALSE; + return JS_EXCEPTION; +} + +static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) +{ + JSValue obj, value; + BOOL done; + obj = sp[-1]; + if (!JS_IsObject(obj)) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + return -1; + } + value = JS_IteratorGetCompleteValue(ctx, obj, &done); + if (JS_IsException(value)) + return -1; + JS_FreeValue(ctx, obj); + sp[-1] = value; + sp[0] = js_bool(done); + return 0; +} + +static JSValue js_create_iterator_result(JSContext *ctx, + JSValue val, + BOOL done) +{ + JSValue obj; + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) { + JS_FreeValue(ctx, val); + return obj; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value, + val, JS_PROP_C_W_E) < 0) { + goto fail; + } + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done, + js_bool(done), JS_PROP_C_W_E) < 0) { + fail: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static JSValue js_array_iterator_next(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + BOOL *pdone, int magic); + +static JSValue js_create_array_iterator(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, int magic); + +static BOOL js_is_fast_array(JSContext *ctx, JSValue obj) +{ + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + return TRUE; + } + } + return FALSE; +} + +/* Access an Array's internal JSValue array if available */ +static BOOL js_get_fast_array(JSContext *ctx, JSValue obj, + JSValue **arrpp, uint32_t *countp) +{ + /* Try and handle fast arrays explicitly */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { + JSObject *p = JS_VALUE_GET_OBJ(obj); + if (p->class_id == JS_CLASS_ARRAY && p->fast_array) { + *countp = p->u.array.count; + *arrpp = p->u.array.u.values; + return TRUE; + } + } + return FALSE; +} + +static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) +{ + JSValue iterator, enumobj, method, value; + int is_array_iterator; + JSValue *arrp; + uint32_t i, count32, pos; + + if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) { + JS_ThrowInternalError(ctx, "invalid index for append"); + return -1; + } + + pos = JS_VALUE_GET_INT(sp[-2]); + + /* XXX: further optimisations: + - use ctx->array_proto_values? + - check if array_iterator_prototype next method is built-in and + avoid constructing actual iterator object? + - build this into js_for_of_start and use in all `for (x of o)` loops + */ + iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator); + if (JS_IsException(iterator)) + return -1; + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft = { .generic_magic = js_create_array_iterator }; + is_array_iterator = JS_IsCFunction(ctx, iterator, + ft.generic, + JS_ITERATOR_KIND_VALUE); + JS_FreeValue(ctx, iterator); + + enumobj = JS_GetIterator(ctx, sp[-1], FALSE); + if (JS_IsException(enumobj)) + return -1; + method = JS_GetProperty(ctx, enumobj, JS_ATOM_next); + if (JS_IsException(method)) { + JS_FreeValue(ctx, enumobj); + return -1; + } + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft2 = { .iterator_next = js_array_iterator_next }; + if (is_array_iterator + && JS_IsCFunction(ctx, method, ft2.generic, 0) + && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) { + uint32_t len; + if (js_get_length32(ctx, &len, sp[-1])) + goto exception; + /* if len > count32, the elements >= count32 might be read in + the prototypes and might have side effects */ + if (len != count32) + goto general_case; + /* Handle fast arrays explicitly */ + for (i = 0; i < count32; i++) { + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, + js_dup(arrp[i]), JS_PROP_C_W_E) < 0) + goto exception; + } + } else { + general_case: + for (;;) { + BOOL done; + value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done); + if (JS_IsException(value)) + goto exception; + if (done) { + /* value is JS_UNDEFINED */ + break; + } + if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0) + goto exception; + } + } + /* Note: could raise an error if too many elements */ + sp[-2] = js_int32(pos); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return 0; + +exception: + JS_IteratorClose(ctx, enumobj, TRUE); + JS_FreeValue(ctx, enumobj); + JS_FreeValue(ctx, method); + return -1; +} + +static __exception int JS_CopyDataProperties(JSContext *ctx, + JSValue target, + JSValue source, + JSValue excluded, + BOOL setprop) +{ + JSPropertyEnum *tab_atom; + JSValue val; + uint32_t i, tab_atom_count; + JSObject *p; + JSObject *pexcl = NULL; + int ret, gpn_flags; + JSPropertyDescriptor desc; + BOOL is_enumerable; + + if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT) + return 0; + + if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT) + pexcl = JS_VALUE_GET_OBJ(excluded); + + p = JS_VALUE_GET_OBJ(source); + + gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY; + if (p->is_exotic) { + const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic; + /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it + introduces a visible change */ + if (em && em->get_own_property_names) { + gpn_flags &= ~JS_GPN_ENUM_ONLY; + } + } + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p, + gpn_flags)) + return -1; + + for (i = 0; i < tab_atom_count; i++) { + if (pexcl) { + ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom); + if (ret) { + if (ret < 0) + goto exception; + continue; + } + } + if (!(gpn_flags & JS_GPN_ENUM_ONLY)) { + /* test if the property is enumerable */ + ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom); + if (ret < 0) + goto exception; + if (!ret) + continue; + is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0; + js_free_desc(ctx, &desc); + if (!is_enumerable) + continue; + } + val = JS_GetProperty(ctx, source, tab_atom[i].atom); + if (JS_IsException(val)) + goto exception; + if (setprop) + ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val); + else + ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val, + JS_PROP_C_W_E); + if (ret < 0) + goto exception; + } + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + return 0; + exception: + js_free_prop_enum(ctx, tab_atom, tab_atom_count); + return -1; +} + +/* only valid inside C functions */ +static JSValue JS_GetActiveFunction(JSContext *ctx) +{ + return ctx->rt->current_stack_frame->cur_func; +} + +static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, + int var_idx, BOOL is_arg) +{ + JSVarRef *var_ref; + struct list_head *el; + + list_for_each(el, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) { + var_ref->header.ref_count++; + return var_ref; + } + } + /* create a new one */ + var_ref = js_malloc(ctx, sizeof(JSVarRef)); + if (!var_ref) + return NULL; + var_ref->header.ref_count = 1; + var_ref->is_detached = FALSE; + var_ref->is_arg = is_arg; + var_ref->var_idx = var_idx; + list_add_tail(&var_ref->header.link, &sf->var_ref_list); + if (is_arg) + var_ref->pvalue = &sf->arg_buf[var_idx]; + else + var_ref->pvalue = &sf->var_buf[var_idx]; + var_ref->value = JS_UNDEFINED; + return var_ref; +} + +static JSValue js_closure2(JSContext *ctx, JSValue func_obj, + JSFunctionBytecode *b, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSObject *p; + JSVarRef **var_refs; + int i; + + p = JS_VALUE_GET_OBJ(func_obj); + p->u.func.function_bytecode = b; + p->u.func.home_object = NULL; + p->u.func.var_refs = NULL; + if (b->closure_var_count) { + var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count); + if (!var_refs) + goto fail; + p->u.func.var_refs = var_refs; + for(i = 0; i < b->closure_var_count; i++) { + JSClosureVar *cv = &b->closure_var[i]; + JSVarRef *var_ref; + if (cv->is_local) { + /* reuse the existing variable reference if it already exists */ + var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg); + if (!var_ref) + goto fail; + } else { + var_ref = cur_var_refs[cv->var_idx]; + var_ref->header.ref_count++; + } + var_refs[i] = var_ref; + } + } + return func_obj; + fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) +{ + JSValue obj, this_val; + int ret; + + this_val = JS_MKPTR(JS_TAG_OBJECT, p); + obj = JS_NewObject(ctx); + if (JS_IsException(obj)) + return JS_EXCEPTION; + set_cycle_flag(ctx, obj); + set_cycle_flag(ctx, this_val); + ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor, + js_dup(this_val), + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + if (ret < 0) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + return obj; +} + +static const uint16_t func_kind_to_class_id[] = { + [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION, + [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION, + [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION, + [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION, +}; + +static JSValue js_closure(JSContext *ctx, JSValue bfunc, + JSVarRef **cur_var_refs, + JSStackFrame *sf) +{ + JSFunctionBytecode *b; + JSValue func_obj; + JSAtom name_atom; + + b = JS_VALUE_GET_PTR(bfunc); + func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]); + if (JS_IsException(func_obj)) { + JS_FreeValue(ctx, bfunc); + return JS_EXCEPTION; + } + func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf); + if (JS_IsException(func_obj)) { + /* bfunc has been freed */ + goto fail; + } + name_atom = b->func_name; + if (name_atom == JS_ATOM_NULL) + name_atom = JS_ATOM_empty_string; + js_function_set_properties(ctx, func_obj, name_atom, + b->defined_arg_count); + + if (b->func_kind & JS_FUNC_GENERATOR) { + JSValue proto; + int proto_class_id; + /* generators have a prototype field which is used as + prototype for the generator object */ + if (b->func_kind == JS_FUNC_ASYNC_GENERATOR) + proto_class_id = JS_CLASS_ASYNC_GENERATOR; + else + proto_class_id = JS_CLASS_GENERATOR; + proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]); + if (JS_IsException(proto)) + goto fail; + JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto, + JS_PROP_WRITABLE); + } else if (b->has_prototype) { + /* add the 'prototype' property: delay instantiation to avoid + creating cycles for every javascript function. The prototype + object is created on the fly when first accessed */ + JS_SetConstructorBit(ctx, func_obj, TRUE); + JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype, + JS_AUTOINIT_ID_PROTOTYPE, NULL, + JS_PROP_WRITABLE); + } + return func_obj; + fail: + /* bfunc is freed when func_obj is freed */ + JS_FreeValue(ctx, func_obj); + return JS_EXCEPTION; +} + +#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0) + +static int js_op_define_class(JSContext *ctx, JSValue *sp, + JSAtom class_name, int class_flags, + JSVarRef **cur_var_refs, + JSStackFrame *sf, BOOL is_computed_name) +{ + JSValue bfunc, parent_class, proto = JS_UNDEFINED; + JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED; + JSFunctionBytecode *b; + + parent_class = sp[-2]; + bfunc = sp[-1]; + + if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) { + if (JS_IsNull(parent_class)) { + parent_proto = JS_NULL; + parent_class = js_dup(ctx->function_proto); + } else { + if (!JS_IsConstructor(ctx, parent_class)) { + JS_ThrowTypeError(ctx, "parent class must be constructor"); + goto fail; + } + parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype); + if (JS_IsException(parent_proto)) + goto fail; + if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) { + JS_ThrowTypeError(ctx, "parent prototype must be an object or null"); + goto fail; + } + } + } else { + /* parent_class is JS_UNDEFINED in this case */ + parent_proto = js_dup(ctx->class_proto[JS_CLASS_OBJECT]); + parent_class = js_dup(ctx->function_proto); + } + proto = JS_NewObjectProto(ctx, parent_proto); + if (JS_IsException(proto)) + goto fail; + + b = JS_VALUE_GET_PTR(bfunc); + assert(b->func_kind == JS_FUNC_NORMAL); + ctor = JS_NewObjectProtoClass(ctx, parent_class, + JS_CLASS_BYTECODE_FUNCTION); + if (JS_IsException(ctor)) + goto fail; + ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf); + bfunc = JS_UNDEFINED; + if (JS_IsException(ctor)) + goto fail; + js_method_set_home_object(ctx, ctor, proto); + JS_SetConstructorBit(ctx, ctor, TRUE); + + JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length, + js_int32(b->defined_arg_count), + JS_PROP_CONFIGURABLE); + + if (is_computed_name) { + if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3], + JS_PROP_CONFIGURABLE) < 0) + goto fail; + } else { + if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0) + goto fail; + } + + /* the constructor property must be first. It can be overriden by + computed property names */ + if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor, + js_dup(ctor), + JS_PROP_CONFIGURABLE | + JS_PROP_WRITABLE | JS_PROP_THROW) < 0) + goto fail; + /* set the prototype property */ + if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype, + js_dup(proto), JS_PROP_THROW) < 0) + goto fail; + set_cycle_flag(ctx, ctor); + set_cycle_flag(ctx, proto); + + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, parent_class); + + sp[-2] = ctor; + sp[-1] = proto; + return 0; + fail: + JS_FreeValue(ctx, parent_class); + JS_FreeValue(ctx, parent_proto); + JS_FreeValue(ctx, bfunc); + JS_FreeValue(ctx, proto); + JS_FreeValue(ctx, ctor); + sp[-2] = JS_UNDEFINED; + sp[-1] = JS_UNDEFINED; + return -1; +} + +static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) +{ + struct list_head *el, *el1; + JSVarRef *var_ref; + int var_idx; + + list_for_each_safe(el, el1, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + var_idx = var_ref->var_idx; + if (var_ref->is_arg) + var_ref->value = js_dup(sf->arg_buf[var_idx]); + else + var_ref->value = js_dup(sf->var_buf[var_idx]); + var_ref->pvalue = &var_ref->value; + /* the reference is no longer to a local variable */ + var_ref->is_detached = TRUE; + add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + } +} + +static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg) +{ + struct list_head *el, *el1; + JSVarRef *var_ref; + int var_idx = idx; + + list_for_each_safe(el, el1, &sf->var_ref_list) { + var_ref = list_entry(el, JSVarRef, header.link); + if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) { + var_ref->value = js_dup(sf->var_buf[var_idx]); + var_ref->pvalue = &var_ref->value; + list_del(&var_ref->header.link); + /* the reference is no longer to a local variable */ + var_ref->is_detached = TRUE; + add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); + } + } +} + +#define JS_CALL_FLAG_COPY_ARGV (1 << 1) +#define JS_CALL_FLAG_GENERATOR (1 << 2) + +static JSValue js_call_c_function(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, int flags) +{ + JSRuntime *rt = ctx->rt; + JSCFunctionType func; + JSObject *p; + JSStackFrame sf_s, *sf = &sf_s, *prev_sf; + JSValue ret_val; + JSValue *arg_buf; + int arg_count, i; + JSCFunctionEnum cproto; + + p = JS_VALUE_GET_OBJ(func_obj); + cproto = p->u.cfunc.cproto; + arg_count = p->u.cfunc.length; + + /* better to always check stack overflow */ + if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count)) + return JS_ThrowStackOverflow(ctx); + + prev_sf = rt->current_stack_frame; + sf->prev_frame = prev_sf; + rt->current_stack_frame = sf; + ctx = p->u.cfunc.realm; /* change the current realm */ + + sf->js_mode = 0; + sf->cur_func = func_obj; + sf->arg_count = argc; + arg_buf = argv; + + if (unlikely(argc < arg_count)) { + /* ensure that at least argc_count arguments are readable */ + arg_buf = alloca(sizeof(arg_buf[0]) * arg_count); + for(i = 0; i < argc; i++) + arg_buf[i] = argv[i]; + for(i = argc; i < arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + sf->arg_count = arg_count; + } + sf->arg_buf = arg_buf; + + func = p->u.cfunc.c_function; + switch(cproto) { + case JS_CFUNC_constructor: + case JS_CFUNC_constructor_or_func: + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { + if (cproto == JS_CFUNC_constructor) { + not_a_constructor: + ret_val = JS_ThrowTypeError(ctx, "must be called with new"); + break; + } else { + this_obj = JS_UNDEFINED; + } + } + /* here this_obj is new_target */ + /* fall thru */ + case JS_CFUNC_generic: + ret_val = func.generic(ctx, this_obj, argc, arg_buf); + break; + case JS_CFUNC_constructor_magic: + case JS_CFUNC_constructor_or_func_magic: + if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) { + if (cproto == JS_CFUNC_constructor_magic) { + goto not_a_constructor; + } else { + this_obj = JS_UNDEFINED; + } + } + /* fall thru */ + case JS_CFUNC_generic_magic: + ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf, + p->u.cfunc.magic); + break; + case JS_CFUNC_getter: + ret_val = func.getter(ctx, this_obj); + break; + case JS_CFUNC_setter: + ret_val = func.setter(ctx, this_obj, arg_buf[0]); + break; + case JS_CFUNC_getter_magic: + ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic); + break; + case JS_CFUNC_setter_magic: + ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic); + break; + case JS_CFUNC_f_f: + { + double d1; + + if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { + ret_val = JS_EXCEPTION; + break; + } + ret_val = js_number(func.f_f(d1)); + } + break; + case JS_CFUNC_f_f_f: + { + double d1, d2; + + if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) { + ret_val = JS_EXCEPTION; + break; + } + if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) { + ret_val = JS_EXCEPTION; + break; + } + ret_val = js_number(func.f_f_f(d1, d2)); + } + break; + case JS_CFUNC_iterator_next: + { + int done; + ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf, + &done, p->u.cfunc.magic); + if (!JS_IsException(ret_val) && done != 2) { + ret_val = js_create_iterator_result(ctx, ret_val, done); + } + } + break; + default: + abort(); + } + + rt->current_stack_frame = sf->prev_frame; + return ret_val; +} + +static JSValue js_call_bound_function(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, int flags) +{ + JSObject *p; + JSBoundFunction *bf; + JSValue *arg_buf, new_target; + int arg_count, i; + + p = JS_VALUE_GET_OBJ(func_obj); + bf = p->u.bound_function; + arg_count = bf->argc + argc; + if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count)) + return JS_ThrowStackOverflow(ctx); + arg_buf = alloca(sizeof(JSValue) * arg_count); + for(i = 0; i < bf->argc; i++) { + arg_buf[i] = bf->argv[i]; + } + for(i = 0; i < argc; i++) { + arg_buf[bf->argc + i] = argv[i]; + } + if (flags & JS_CALL_FLAG_CONSTRUCTOR) { + new_target = this_obj; + if (js_same_value(ctx, func_obj, new_target)) + new_target = bf->func_obj; + return JS_CallConstructor2(ctx, bf->func_obj, new_target, + arg_count, arg_buf); + } else { + return JS_Call(ctx, bf->func_obj, bf->this_val, + arg_count, arg_buf); + } +} + +/* argument of OP_special_object */ +typedef enum { + OP_SPECIAL_OBJECT_ARGUMENTS, + OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS, + OP_SPECIAL_OBJECT_THIS_FUNC, + OP_SPECIAL_OBJECT_NEW_TARGET, + OP_SPECIAL_OBJECT_HOME_OBJECT, + OP_SPECIAL_OBJECT_VAR_OBJECT, + OP_SPECIAL_OBJECT_IMPORT_META, +} OPSpecialObjectEnum; + +#define FUNC_RET_AWAIT 0 +#define FUNC_RET_YIELD 1 +#define FUNC_RET_YIELD_STAR 2 + +#if defined(DUMP_BYTECODE_FINAL) || \ + defined(DUMP_BYTECODE_PASS2) || \ + defined(DUMP_BYTECODE_PASS1) || \ + defined(DUMP_BYTECODE_STACK) || \ + defined(DUMP_BYTECODE_STEP) || \ + defined(DUMP_READ_OBJECT) +#define DUMP_BYTECODE +#endif + +#ifdef DUMP_BYTECODE +static void dump_single_byte_code(JSContext *ctx, const uint8_t *pc, + JSFunctionBytecode *b, int start_pos); +static void print_func_name(JSFunctionBytecode *b); +#endif + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, + JSValue this_obj, JSValue new_target, + int argc, JSValue *argv, int flags) +{ + JSRuntime *rt = caller_ctx->rt; + JSContext *ctx; + JSObject *p; + JSFunctionBytecode *b; + JSStackFrame sf_s, *sf = &sf_s; + uint8_t *pc; + int opcode, arg_allocated_size, i; + JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval; + JSVarRef **var_refs; + size_t alloca_size; + JSInlineCache *ic; + +#ifdef DUMP_BYTECODE_STEP +#define DUMP_BYTECODE_OR_DONT(pc) \ + if (check_dump_flag(ctx->rt, DUMP_BYTECODE_STEP)) dump_single_byte_code(ctx, pc, b, 0); +#else +#define DUMP_BYTECODE_OR_DONT(pc) +#endif + +#if !DIRECT_DISPATCH +#define SWITCH(pc) DUMP_BYTECODE_OR_DONT(pc) switch (opcode = *pc++) +#define CASE(op) case op +#define DEFAULT default +#define BREAK break +#else + __extension__ static const void * const dispatch_table[256] = { +#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id, +#define def(id, size, n_pop, n_push, f) +#include "quickjs-opcode.h" + [ OP_COUNT ... 255 ] = &&case_default + }; +#define SWITCH(pc) DUMP_BYTECODE_OR_DONT(pc) __extension__ ({ goto *dispatch_table[opcode = *pc++]; }); +#define CASE(op) case_ ## op +#define DEFAULT case_default +#define BREAK SWITCH(pc) +#endif + + if (js_poll_interrupts(caller_ctx)) + return JS_EXCEPTION; + if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) { + if (flags & JS_CALL_FLAG_GENERATOR) { + JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj); + /* func_obj get contains a pointer to JSFuncAsyncState */ + /* the stack frame is already allocated */ + sf = &s->frame; + p = JS_VALUE_GET_OBJ(sf->cur_func); + b = p->u.func.function_bytecode; + ctx = b->realm; + var_refs = p->u.func.var_refs; + local_buf = arg_buf = sf->arg_buf; + var_buf = sf->var_buf; + stack_buf = sf->var_buf + b->var_count; + sp = sf->cur_sp; + sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */ + pc = sf->cur_pc; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; + ic = b->ic; + if (s->throw_flag) + goto exception; + else + goto restart; + } else { + goto not_a_function; + } + } + p = JS_VALUE_GET_OBJ(func_obj); + if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { + JSClassCall *call_func; + call_func = rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeError(caller_ctx, "not a function"); + } + return call_func(caller_ctx, func_obj, this_obj, argc, + argv, flags); + } + b = p->u.func.function_bytecode; + + if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) { + arg_allocated_size = b->arg_count; + } else { + arg_allocated_size = 0; + } + + alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count + + b->stack_size); + if (js_check_stack_overflow(rt, alloca_size)) + return JS_ThrowStackOverflow(caller_ctx); + + sf->js_mode = b->js_mode; + arg_buf = argv; + sf->arg_count = argc; + sf->cur_func = func_obj; + init_list_head(&sf->var_ref_list); + var_refs = p->u.func.var_refs; + + local_buf = alloca(alloca_size); + if (unlikely(arg_allocated_size)) { + int n = min_int(argc, b->arg_count); + arg_buf = local_buf; + for(i = 0; i < n; i++) + arg_buf[i] = JS_DupValue(caller_ctx, argv[i]); + for(; i < b->arg_count; i++) + arg_buf[i] = JS_UNDEFINED; + sf->arg_count = b->arg_count; + } + var_buf = local_buf + arg_allocated_size; + sf->var_buf = var_buf; + sf->arg_buf = arg_buf; + + for(i = 0; i < b->var_count; i++) + var_buf[i] = JS_UNDEFINED; + + stack_buf = var_buf + b->var_count; + sp = stack_buf; + pc = b->byte_code_buf; + /* sf->cur_pc must we set to pc before any recursive calls to JS_CallInternal. */ + sf->cur_pc = NULL; + sf->prev_frame = rt->current_stack_frame; + rt->current_stack_frame = sf; + ctx = b->realm; /* set the current realm */ + ic = b->ic; + +#ifdef DUMP_BYTECODE_STEP + if (check_dump_flag(ctx->rt, DUMP_BYTECODE_STEP)) + print_func_name(b); +#endif + + restart: + for(;;) { + int call_argc; + JSValue *call_argv; + + SWITCH(pc) { + CASE(OP_push_i32): + *sp++ = js_int32(get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_push_const): + *sp++ = js_dup(b->cpool[get_u32(pc)]); + pc += 4; + BREAK; + CASE(OP_push_minus1): + CASE(OP_push_0): + CASE(OP_push_1): + CASE(OP_push_2): + CASE(OP_push_3): + CASE(OP_push_4): + CASE(OP_push_5): + CASE(OP_push_6): + CASE(OP_push_7): + *sp++ = js_int32(opcode - OP_push_0); + BREAK; + CASE(OP_push_i8): + *sp++ = js_int32(get_i8(pc)); + pc += 1; + BREAK; + CASE(OP_push_i16): + *sp++ = js_int32(get_i16(pc)); + pc += 2; + BREAK; + CASE(OP_push_const8): + *sp++ = js_dup(b->cpool[*pc++]); + BREAK; + CASE(OP_fclosure8): + *sp++ = js_closure(ctx, js_dup(b->cpool[*pc++]), var_refs, sf); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + BREAK; + CASE(OP_push_empty_string): + *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string); + BREAK; + CASE(OP_get_length): + { + JSValue val; + + sf->cur_pc = pc; + val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + CASE(OP_push_atom_value): + *sp++ = JS_AtomToValue(ctx, get_u32(pc)); + pc += 4; + BREAK; + CASE(OP_undefined): + *sp++ = JS_UNDEFINED; + BREAK; + CASE(OP_null): + *sp++ = JS_NULL; + BREAK; + CASE(OP_push_this): + /* OP_push_this is only called at the start of a function */ + { + JSValue val; + if (!(b->js_mode & JS_MODE_STRICT)) { + uint32_t tag = JS_VALUE_GET_TAG(this_obj); + if (likely(tag == JS_TAG_OBJECT)) + goto normal_this; + if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) { + val = js_dup(ctx->global_obj); + } else { + val = JS_ToObject(ctx, this_obj); + if (JS_IsException(val)) + goto exception; + } + } else { + normal_this: + val = js_dup(this_obj); + } + *sp++ = val; + } + BREAK; + CASE(OP_push_false): + *sp++ = JS_FALSE; + BREAK; + CASE(OP_push_true): + *sp++ = JS_TRUE; + BREAK; + CASE(OP_object): + *sp++ = JS_NewObject(ctx); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + BREAK; + CASE(OP_special_object): + { + int arg = *pc++; + switch(arg) { + case OP_SPECIAL_OBJECT_ARGUMENTS: + *sp++ = js_build_arguments(ctx, argc, argv); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: + *sp++ = js_build_mapped_arguments(ctx, argc, argv, + sf, min_int(argc, b->arg_count)); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_THIS_FUNC: + *sp++ = js_dup(sf->cur_func); + break; + case OP_SPECIAL_OBJECT_NEW_TARGET: + *sp++ = js_dup(new_target); + break; + case OP_SPECIAL_OBJECT_HOME_OBJECT: + { + JSObject *p1; + p1 = p->u.func.home_object; + if (unlikely(!p1)) + *sp++ = JS_UNDEFINED; + else + *sp++ = js_dup(JS_MKPTR(JS_TAG_OBJECT, p1)); + } + break; + case OP_SPECIAL_OBJECT_VAR_OBJECT: + *sp++ = JS_NewObjectProto(ctx, JS_NULL); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + case OP_SPECIAL_OBJECT_IMPORT_META: + *sp++ = js_import_meta(ctx); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + break; + default: + abort(); + } + } + BREAK; + CASE(OP_rest): + { + int first = get_u16(pc); + pc += 2; + *sp++ = js_build_rest(ctx, first, argc, argv); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + } + BREAK; + + CASE(OP_drop): + JS_FreeValue(ctx, sp[-1]); + sp--; + BREAK; + CASE(OP_nip): + JS_FreeValue(ctx, sp[-2]); + sp[-2] = sp[-1]; + sp--; + BREAK; + CASE(OP_nip1): /* a b c -> b c */ + JS_FreeValue(ctx, sp[-3]); + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp--; + BREAK; + CASE(OP_dup): + sp[0] = js_dup(sp[-1]); + sp++; + BREAK; + CASE(OP_dup2): /* a b -> a b a b */ + sp[0] = js_dup(sp[-2]); + sp[1] = js_dup(sp[-1]); + sp += 2; + BREAK; + CASE(OP_dup3): /* a b c -> a b c a b c */ + sp[0] = js_dup(sp[-3]); + sp[1] = js_dup(sp[-2]); + sp[2] = js_dup(sp[-1]); + sp += 3; + BREAK; + CASE(OP_dup1): /* a b -> a a b */ + sp[0] = sp[-1]; + sp[-1] = js_dup(sp[-2]); + sp++; + BREAK; + CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = js_dup(sp[0]); + sp++; + BREAK; + CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = js_dup(sp[0]); + sp++; + BREAK; + CASE(OP_insert4): /* this obj prop a -> a this obj prop a */ + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = js_dup(sp[0]); + sp++; + BREAK; + CASE(OP_perm3): /* obj a b -> a obj b (213) */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = tmp; + } + BREAK; + CASE(OP_rot3l): /* x a b -> a b x (231) */ + { + JSValue tmp; + tmp = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot4l): /* x a b c -> a b c x */ + { + JSValue tmp; + tmp = sp[-4]; + sp[-4] = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot5l): /* x a b c d -> a b c d x */ + { + JSValue tmp; + tmp = sp[-5]; + sp[-5] = sp[-4]; + sp[-4] = sp[-3]; + sp[-3] = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_rot3r): /* a b x -> x a b (312) */ + { + JSValue tmp; + tmp = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = tmp; + } + BREAK; + CASE(OP_perm4): /* obj prop a b -> a obj prop b */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = tmp; + } + BREAK; + CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-3]; + sp[-3] = sp[-4]; + sp[-4] = sp[-5]; + sp[-5] = tmp; + } + BREAK; + CASE(OP_swap): /* a b -> b a */ + { + JSValue tmp; + tmp = sp[-2]; + sp[-2] = sp[-1]; + sp[-1] = tmp; + } + BREAK; + CASE(OP_swap2): /* a b c d -> c d a b */ + { + JSValue tmp1, tmp2; + tmp1 = sp[-4]; + tmp2 = sp[-3]; + sp[-4] = sp[-2]; + sp[-3] = sp[-1]; + sp[-2] = tmp1; + sp[-1] = tmp2; + } + BREAK; + + CASE(OP_fclosure): + { + JSValue bfunc = js_dup(b->cpool[get_u32(pc)]); + pc += 4; + *sp++ = js_closure(ctx, bfunc, var_refs, sf); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + } + BREAK; + CASE(OP_call0): + CASE(OP_call1): + CASE(OP_call2): + CASE(OP_call3): + call_argc = opcode - OP_call0; + goto has_call_argc; + CASE(OP_call): + CASE(OP_tail_call): + { + call_argc = get_u16(pc); + pc += 2; + goto has_call_argc; + has_call_argc: + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, + JS_UNDEFINED, call_argc, call_argv, 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + if (opcode == OP_tail_call) + goto done; + for(i = -1; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 1; + *sp++ = ret_val; + } + BREAK; + CASE(OP_call_constructor): + { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallConstructorInternal(ctx, call_argv[-2], + call_argv[-1], + call_argc, call_argv, 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + for(i = -2; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 2; + *sp++ = ret_val; + } + BREAK; + CASE(OP_call_method): + CASE(OP_tail_call_method): + { + call_argc = get_u16(pc); + pc += 2; + call_argv = sp - call_argc; + sf->cur_pc = pc; + ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2], + JS_UNDEFINED, call_argc, call_argv, 0); + if (unlikely(JS_IsException(ret_val))) + goto exception; + if (opcode == OP_tail_call_method) + goto done; + for(i = -2; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 2; + *sp++ = ret_val; + } + BREAK; + CASE(OP_array_from): + { + int i, ret; + + call_argc = get_u16(pc); + pc += 2; + ret_val = JS_NewArray(ctx); + if (unlikely(JS_IsException(ret_val))) + goto exception; + call_argv = sp - call_argc; + for(i = 0; i < call_argc; i++) { + ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i], + JS_PROP_C_W_E | JS_PROP_THROW); + call_argv[i] = JS_UNDEFINED; + if (ret < 0) { + JS_FreeValue(ctx, ret_val); + goto exception; + } + } + sp -= call_argc; + *sp++ = ret_val; + } + BREAK; + + CASE(OP_apply): + { + int magic; + magic = get_u16(pc); + pc += 2; + sf->cur_pc = pc; + + ret_val = js_function_apply(ctx, sp[-3], 2, &sp[-2], magic); + if (unlikely(JS_IsException(ret_val))) + goto exception; + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 3; + *sp++ = ret_val; + } + BREAK; + CASE(OP_return): + ret_val = *--sp; + goto done; + CASE(OP_return_undef): + ret_val = JS_UNDEFINED; + goto done; + + CASE(OP_check_ctor_return): + /* return TRUE if 'this' should be returned */ + if (!JS_IsObject(sp[-1])) { + if (!JS_IsUndefined(sp[-1])) { + JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined"); + goto exception; + } + sp[0] = JS_TRUE; + } else { + sp[0] = JS_FALSE; + } + sp++; + BREAK; + CASE(OP_check_ctor): + if (JS_IsUndefined(new_target)) { + JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'"); + goto exception; + } + BREAK; + CASE(OP_check_brand): + if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0) + goto exception; + BREAK; + CASE(OP_add_brand): + if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + BREAK; + + CASE(OP_throw): + JS_Throw(ctx, *--sp); + goto exception; + + CASE(OP_throw_error): +#define JS_THROW_VAR_RO 0 +#define JS_THROW_VAR_REDECL 1 +#define JS_THROW_VAR_UNINITIALIZED 2 +#define JS_THROW_ERROR_DELETE_SUPER 3 +#define JS_THROW_ERROR_ITERATOR_THROW 4 + { + JSAtom atom; + int type; + atom = get_u32(pc); + type = pc[4]; + pc += 5; + if (type == JS_THROW_VAR_RO) + JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom); + else + if (type == JS_THROW_VAR_REDECL) + JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom); + else + if (type == JS_THROW_VAR_UNINITIALIZED) + JS_ThrowReferenceErrorUninitialized(ctx, atom); + else + if (type == JS_THROW_ERROR_DELETE_SUPER) + JS_ThrowReferenceError(ctx, "unsupported reference to 'super'"); + else + if (type == JS_THROW_ERROR_ITERATOR_THROW) + JS_ThrowTypeError(ctx, "iterator does not have a throw method"); + else + JS_ThrowInternalError(ctx, "invalid throw var type %d", type); + } + goto exception; + + CASE(OP_eval): + { + JSValue obj; + int scope_idx; + call_argc = get_u16(pc); + scope_idx = get_u16(pc + 2) - 1; + pc += 4; + call_argv = sp - call_argc; + sf->cur_pc = pc; + if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) { + if (call_argc >= 1) + obj = call_argv[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, + JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED, + JS_UNDEFINED, call_argc, call_argv, 0); + } + if (unlikely(JS_IsException(ret_val))) + goto exception; + for(i = -1; i < call_argc; i++) + JS_FreeValue(ctx, call_argv[i]); + sp -= call_argc + 1; + *sp++ = ret_val; + } + BREAK; + /* could merge with OP_apply */ + CASE(OP_apply_eval): + { + int scope_idx; + uint32_t len; + JSValue *tab; + JSValue obj; + + scope_idx = get_u16(pc) - 1; + pc += 2; + sf->cur_pc = pc; + tab = build_arg_list(ctx, &len, sp[-1]); + if (!tab) + goto exception; + if (js_same_value(ctx, sp[-2], ctx->eval_obj)) { + if (len >= 1) + obj = tab[0]; + else + obj = JS_UNDEFINED; + ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj, + JS_EVAL_TYPE_DIRECT, scope_idx); + } else { + ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, + tab); + } + free_arg_list(ctx, tab, len); + if (unlikely(JS_IsException(ret_val))) + goto exception; + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + *sp++ = ret_val; + } + BREAK; + + CASE(OP_regexp): + { + sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED, + sp[-2], sp[-1]); + sp--; + } + BREAK; + + CASE(OP_get_super): + { + JSValue proto; + proto = JS_GetPrototype(ctx, sp[-1]); + if (JS_IsException(proto)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = proto; + } + BREAK; + + CASE(OP_import): + { + JSValue val; + sf->cur_pc = pc; + val = js_dynamic_import(ctx, sp[-1]); + if (JS_IsException(val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + + CASE(OP_check_var): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_CheckGlobalVar(ctx, atom); + if (ret < 0) + goto exception; + *sp++ = js_bool(ret); + } + BREAK; + + CASE(OP_get_var_undef): + CASE(OP_get_var): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + + val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_put_var): + CASE(OP_put_var_init): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + + ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var); + sp--; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_var_strict): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + + /* sp[-2] is JS_TRUE or JS_FALSE */ + if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + goto exception; + } + ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_check_define_var): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_CheckDefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_var): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalVar(ctx, atom, flags)) + goto exception; + } + BREAK; + CASE(OP_define_func): + { + JSAtom atom; + int flags; + atom = get_u32(pc); + flags = pc[4]; + pc += 5; + if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp--; + } + BREAK; + + CASE(OP_get_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = js_dup(var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], js_dup(sp[-1])); + } + BREAK; + CASE(OP_get_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + sp[0] = js_dup(arg_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_arg): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &arg_buf[idx], js_dup(sp[-1])); + } + BREAK; + + CASE(OP_get_loc8): *sp++ = js_dup(var_buf[*pc++]); BREAK; + CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK; + CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], js_dup(sp[-1])); BREAK; + + // Observation: get_loc0 and get_loc1 are individually very + // frequent opcodes _and_ they are very often paired together, + // making them ideal candidates for opcode fusion. + CASE(OP_get_loc0_loc1): + *sp++ = js_dup(var_buf[0]); + *sp++ = js_dup(var_buf[1]); + BREAK; + + CASE(OP_get_loc0): *sp++ = js_dup(var_buf[0]); BREAK; + CASE(OP_get_loc1): *sp++ = js_dup(var_buf[1]); BREAK; + CASE(OP_get_loc2): *sp++ = js_dup(var_buf[2]); BREAK; + CASE(OP_get_loc3): *sp++ = js_dup(var_buf[3]); BREAK; + CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK; + CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK; + CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK; + CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK; + CASE(OP_set_loc0): set_value(ctx, &var_buf[0], js_dup(sp[-1])); BREAK; + CASE(OP_set_loc1): set_value(ctx, &var_buf[1], js_dup(sp[-1])); BREAK; + CASE(OP_set_loc2): set_value(ctx, &var_buf[2], js_dup(sp[-1])); BREAK; + CASE(OP_set_loc3): set_value(ctx, &var_buf[3], js_dup(sp[-1])); BREAK; + CASE(OP_get_arg0): *sp++ = js_dup(arg_buf[0]); BREAK; + CASE(OP_get_arg1): *sp++ = js_dup(arg_buf[1]); BREAK; + CASE(OP_get_arg2): *sp++ = js_dup(arg_buf[2]); BREAK; + CASE(OP_get_arg3): *sp++ = js_dup(arg_buf[3]); BREAK; + CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK; + CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK; + CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK; + CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK; + CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], js_dup(sp[-1])); BREAK; + CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], js_dup(sp[-1])); BREAK; + CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], js_dup(sp[-1])); BREAK; + CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], js_dup(sp[-1])); BREAK; + CASE(OP_get_var_ref0): *sp++ = js_dup(*var_refs[0]->pvalue); BREAK; + CASE(OP_get_var_ref1): *sp++ = js_dup(*var_refs[1]->pvalue); BREAK; + CASE(OP_get_var_ref2): *sp++ = js_dup(*var_refs[2]->pvalue); BREAK; + CASE(OP_get_var_ref3): *sp++ = js_dup(*var_refs[3]->pvalue); BREAK; + CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK; + CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK; + CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, js_dup(sp[-1])); BREAK; + CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, js_dup(sp[-1])); BREAK; + CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, js_dup(sp[-1])); BREAK; + CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, js_dup(sp[-1])); BREAK; + + CASE(OP_get_var_ref): + { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + sp[0] = js_dup(val); + sp++; + } + BREAK; + CASE(OP_put_var_ref): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_var_ref): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, var_refs[idx]->pvalue, js_dup(sp[-1])); + } + BREAK; + CASE(OP_get_var_ref_check): + { + int idx; + JSValue val; + idx = get_u16(pc); + pc += 2; + val = *var_refs[idx]->pvalue; + if (unlikely(JS_IsUninitialized(val))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); + goto exception; + } + sp[0] = js_dup(val); + sp++; + } + BREAK; + CASE(OP_put_var_ref_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_var_ref_check_init): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) { + JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE); + goto exception; + } + set_value(ctx, var_refs[idx]->pvalue, sp[-1]); + sp--; + } + BREAK; + CASE(OP_set_loc_uninitialized): + { + int idx; + idx = get_u16(pc); + pc += 2; + set_value(ctx, &var_buf[idx], JS_UNINITIALIZED); + } + BREAK; + CASE(OP_get_loc_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, + FALSE); + goto exception; + } + sp[0] = js_dup(var_buf[idx]); + sp++; + } + BREAK; + CASE(OP_put_loc_check): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, + FALSE); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_put_loc_check_init): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(!JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceError(caller_ctx, + "'this' can be initialized only once"); + goto exception; + } + set_value(ctx, &var_buf[idx], sp[-1]); + sp--; + } + BREAK; + CASE(OP_close_loc): + { + int idx; + idx = get_u16(pc); + pc += 2; + close_lexical_var(ctx, sf, idx, FALSE); + } + BREAK; + + CASE(OP_make_loc_ref): + CASE(OP_make_arg_ref): + CASE(OP_make_var_ref_ref): + { + JSVarRef *var_ref; + JSProperty *pr; + JSAtom atom; + int idx; + atom = get_u32(pc); + idx = get_u16(pc + 4); + pc += 6; + *sp++ = JS_NewObjectProto(ctx, JS_NULL); + if (unlikely(JS_IsException(sp[-1]))) + goto exception; + if (opcode == OP_make_var_ref_ref) { + var_ref = var_refs[idx]; + var_ref->header.ref_count++; + } else { + var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref); + if (!var_ref) + goto exception; + } + pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom, + JS_PROP_WRITABLE | JS_PROP_VARREF); + if (!pr) { + free_var_ref(rt, var_ref); + goto exception; + } + pr->u.var_ref = var_ref; + *sp++ = JS_AtomToValue(ctx, atom); + } + BREAK; + CASE(OP_make_var_ref): + { + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + if (JS_GetGlobalVarRef(ctx, atom, sp)) + goto exception; + sp += 2; + } + BREAK; + + CASE(OP_goto): + pc += (int32_t)get_u32(pc); + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_goto16): + pc += (int16_t)get_u16(pc); + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_goto8): + pc += (int8_t)pc[0]; + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + BREAK; + CASE(OP_if_true): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 4; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int32_t)get_u32(pc - 4) - 4; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_true8): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (res) { + pc += (int8_t)pc[-1] - 1; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_if_false8): + { + int res; + JSValue op1; + + op1 = sp[-1]; + pc += 1; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1); + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp--; + if (!res) { + pc += (int8_t)pc[-1] - 1; + } + if (unlikely(js_poll_interrupts(ctx))) + goto exception; + } + BREAK; + CASE(OP_catch): + { + int32_t diff; + diff = get_u32(pc); + sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf); + sp++; + pc += 4; + } + BREAK; + CASE(OP_gosub): + { + int32_t diff; + diff = get_u32(pc); + /* XXX: should have a different tag to avoid security flaw */ + sp[0] = js_int32(pc + 4 - b->byte_code_buf); + sp++; + pc += diff; + } + BREAK; + CASE(OP_ret): + { + JSValue op1; + uint32_t pos; + op1 = sp[-1]; + if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT)) + goto ret_fail; + pos = JS_VALUE_GET_INT(op1); + if (unlikely(pos >= b->byte_code_len)) { + ret_fail: + JS_ThrowInternalError(ctx, "invalid ret value"); + goto exception; + } + sp--; + pc = b->byte_code_buf + pos; + } + BREAK; + + CASE(OP_for_in_start): + sf->cur_pc = pc; + if (js_for_in_start(ctx, sp)) + goto exception; + BREAK; + CASE(OP_for_in_next): + sf->cur_pc = pc; + if (js_for_in_next(ctx, sp)) + goto exception; + sp += 2; + BREAK; + CASE(OP_for_of_start): + sf->cur_pc = pc; + if (js_for_of_start(ctx, sp, FALSE)) + goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_for_of_next): + { + int offset = -3 - pc[0]; + pc += 1; + sf->cur_pc = pc; + if (js_for_of_next(ctx, sp, offset)) + goto exception; + sp += 2; + } + BREAK; + CASE(OP_for_await_of_start): + sf->cur_pc = pc; + if (js_for_of_start(ctx, sp, TRUE)) + goto exception; + sp += 1; + *sp++ = JS_NewCatchOffset(ctx, 0); + BREAK; + CASE(OP_iterator_get_value_done): + sf->cur_pc = pc; + if (js_iterator_get_value_done(ctx, sp)) + goto exception; + sp += 1; + BREAK; + CASE(OP_iterator_check_object): + if (unlikely(!JS_IsObject(sp[-1]))) { + JS_ThrowTypeError(ctx, "iterator must return an object"); + goto exception; + } + BREAK; + + CASE(OP_iterator_close): + /* iter_obj next catch_offset -> */ + sp--; /* drop the catch offset to avoid getting caught by exception */ + JS_FreeValue(ctx, sp[-1]); /* drop the next method */ + sp--; + if (!JS_IsUndefined(sp[-1])) { + sf->cur_pc = pc; + if (JS_IteratorClose(ctx, sp[-1], FALSE)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + } + sp--; + BREAK; + CASE(OP_nip_catch): + { + JSValue ret_val; + /* catch_offset ... ret_val -> ret_eval */ + ret_val = *--sp; + while (sp > stack_buf && + JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) { + JS_FreeValue(ctx, *--sp); + } + if (unlikely(sp == stack_buf)) { + JS_ThrowInternalError(ctx, "nip_catch"); + JS_FreeValue(ctx, ret_val); + goto exception; + } + sp[-1] = ret_val; + } + BREAK; + + CASE(OP_iterator_next): + /* stack: iter_obj next catch_offset val */ + { + JSValue ret; + sf->cur_pc = pc; + ret = JS_Call(ctx, sp[-3], sp[-4], 1, (sp - 1)); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + } + BREAK; + + CASE(OP_iterator_call): + /* stack: iter_obj next catch_offset val */ + { + JSValue method, ret; + BOOL ret_flag; + int flags; + flags = *pc++; + sf->cur_pc = pc; + method = JS_GetProperty(ctx, sp[-4], (flags & 1) ? + JS_ATOM_throw : JS_ATOM_return); + if (JS_IsException(method)) + goto exception; + if (JS_IsUndefined(method) || JS_IsNull(method)) { + ret_flag = TRUE; + } else { + if (flags & 2) { + /* no argument */ + ret = JS_CallFree(ctx, method, sp[-4], + 0, NULL); + } else { + ret = JS_CallFree(ctx, method, sp[-4], + 1, (sp - 1)); + } + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret; + ret_flag = FALSE; + } + sp[0] = js_bool(ret_flag); + sp += 1; + } + BREAK; + + CASE(OP_lnot): + { + int res; + JSValue op1; + + op1 = sp[-1]; + if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { + res = JS_VALUE_GET_INT(op1) != 0; + } else { + res = JS_ToBoolFree(ctx, op1); + } + sp[-1] = js_bool(!res); + } + BREAK; + + CASE(OP_get_field): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + val = JS_GetPropertyInternal2(ctx, sp[-1], atom, sp[-1], ic, FALSE); + if (unlikely(JS_IsException(val))) + goto exception; + if (ic && ic->updated == TRUE) { + ic->updated = FALSE; + put_u8(pc - 5, OP_get_field_ic); + put_u32(pc - 4, ic->updated_offset); + JS_FreeAtom(ctx, atom); + } + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + + CASE(OP_get_field_ic): + { + JSValue val; + JSAtom atom; + int32_t ic_offset; + ic_offset = get_u32(pc); + atom = get_ic_atom(ic, ic_offset); + pc += 4; + sf->cur_pc = pc; + val = JS_GetPropertyInternalWithIC(ctx, sp[-1], atom, sp[-1], ic, ic_offset, FALSE); + ic->updated = FALSE; + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = val; + } + BREAK; + CASE(OP_get_field2): + { + JSValue val; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + val = JS_GetPropertyInternal2(ctx, sp[-1], atom, sp[-1], NULL, FALSE); + if (unlikely(JS_IsException(val))) + goto exception; + if (ic != NULL && ic->updated == TRUE) { + ic->updated = FALSE; + put_u8(pc - 5, OP_get_field2_ic); + put_u32(pc - 4, ic->updated_offset); + JS_FreeAtom(ctx, atom); + } + *sp++ = val; + } + BREAK; + + CASE(OP_get_field2_ic): + { + JSValue val; + JSAtom atom; + int32_t ic_offset; + ic_offset = get_u32(pc); + atom = get_ic_atom(ic, ic_offset); + pc += 4; + sf->cur_pc = pc; + val = JS_GetPropertyInternalWithIC(ctx, sp[-1], atom, sp[-1], ic, ic_offset, FALSE); + ic->updated = FALSE; + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_put_field): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + sf->cur_pc = pc; + ret = JS_SetPropertyInternal2(ctx, + sp[-2], atom, + sp[-1], sp[-2], + JS_PROP_THROW_STRICT, ic); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + if (ic != NULL && ic->updated == TRUE) { + ic->updated = FALSE; + put_u8(pc - 5, OP_put_field_ic); + put_u32(pc - 4, ic->updated_offset); + JS_FreeAtom(ctx, atom); + } + } + BREAK; + + CASE(OP_put_field_ic): + { + int ret; + JSAtom atom; + int32_t ic_offset; + ic_offset = get_u32(pc); + atom = get_ic_atom(ic, ic_offset); + pc += 4; + sf->cur_pc = pc; + ret = JS_SetPropertyInternalWithIC(ctx, sp[-2], atom, sp[-1], JS_PROP_THROW_STRICT, ic, ic_offset); + ic->updated = FALSE; + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_private_symbol): + { + JSAtom atom; + JSValue val; + + atom = get_u32(pc); + pc += 4; + val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE); + if (JS_IsException(val)) + goto exception; + *sp++ = val; + } + BREAK; + + CASE(OP_get_private_field): + { + JSValue val; + sf->cur_pc = pc; + val = JS_GetPrivateField(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_put_private_field): + { + int ret; + sf->cur_pc = pc; + ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-1]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_private_field): + { + int ret; + ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_field): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1], + JS_PROP_C_W_E | JS_PROP_THROW); + sp--; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_set_name): + { + int ret; + JSAtom atom; + atom = get_u32(pc); + pc += 4; + + ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE); + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_name_computed): + { + int ret; + ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE); + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + CASE(OP_set_proto): + { + JSValue proto; + proto = sp[-1]; + if (JS_IsObject(proto) || JS_IsNull(proto)) { + if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0) + goto exception; + } + JS_FreeValue(ctx, proto); + sp--; + } + BREAK; + CASE(OP_set_home_object): + js_method_set_home_object(ctx, sp[-1], sp[-2]); + BREAK; + CASE(OP_define_method): + CASE(OP_define_method_computed): + { + JSValue getter, setter, value; + JSValue obj; + JSAtom atom; + int flags, ret, op_flags; + BOOL is_computed; +#define OP_DEFINE_METHOD_METHOD 0 +#define OP_DEFINE_METHOD_GETTER 1 +#define OP_DEFINE_METHOD_SETTER 2 +#define OP_DEFINE_METHOD_ENUMERABLE 4 + + is_computed = (opcode == OP_define_method_computed); + if (is_computed) { + atom = JS_ValueToAtom(ctx, sp[-2]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + opcode += OP_define_method - OP_define_method_computed; + } else { + atom = get_u32(pc); + pc += 4; + } + op_flags = *pc++; + + obj = sp[-2 - is_computed]; + flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE | + JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW; + if (op_flags & OP_DEFINE_METHOD_ENUMERABLE) + flags |= JS_PROP_ENUMERABLE; + op_flags &= 3; + value = JS_UNDEFINED; + getter = JS_UNDEFINED; + setter = JS_UNDEFINED; + if (op_flags == OP_DEFINE_METHOD_METHOD) { + value = sp[-1]; + flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE; + } else if (op_flags == OP_DEFINE_METHOD_GETTER) { + getter = sp[-1]; + flags |= JS_PROP_HAS_GET; + } else { + setter = sp[-1]; + flags |= JS_PROP_HAS_SET; + } + ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj); + if (ret >= 0) { + ret = JS_DefineProperty(ctx, obj, atom, value, + getter, setter, flags); + } + JS_FreeValue(ctx, sp[-1]); + if (is_computed) { + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-2]); + } + sp -= 1 + is_computed; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_define_class): + CASE(OP_define_class_computed): + { + int class_flags; + JSAtom atom; + + atom = get_u32(pc); + class_flags = pc[4]; + pc += 5; + if (js_op_define_class(ctx, sp, atom, class_flags, + var_refs, sf, + (opcode == OP_define_class_computed)) < 0) + goto exception; + } + BREAK; + + CASE(OP_get_array_el): + { + JSValue val; + + sf->cur_pc = pc; + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + JS_FreeValue(ctx, sp[-2]); + sp[-2] = val; + sp--; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_get_array_el2): + { + JSValue val; + + sf->cur_pc = pc; + val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]); + sp[-1] = val; + if (unlikely(JS_IsException(val))) + goto exception; + } + BREAK; + + CASE(OP_get_ref_value): + { + JSValue val; + sf->cur_pc = pc; + if (unlikely(JS_IsUndefined(sp[-2]))) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-1]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } + val = JS_GetPropertyValue(ctx, sp[-2], + js_dup(sp[-1])); + if (unlikely(JS_IsException(val))) + goto exception; + sp[0] = val; + sp++; + } + BREAK; + + CASE(OP_get_super_value): + { + JSValue val; + JSAtom atom; + sf->cur_pc = pc; + atom = JS_ValueToAtom(ctx, sp[-1]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + val = JS_GetPropertyInternal2(ctx, sp[-2], atom, sp[-3], NULL, FALSE); + JS_FreeAtom(ctx, atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + JS_FreeValue(ctx, sp[-2]); + JS_FreeValue(ctx, sp[-3]); + sp[-3] = val; + sp -= 2; + } + BREAK; + + CASE(OP_put_array_el): + { + int ret; + sf->cur_pc = pc; + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_ref_value): + { + int ret, flags; + sf->cur_pc = pc; + flags = JS_PROP_THROW_STRICT; + if (unlikely(JS_IsUndefined(sp[-3]))) { + if (is_strict_mode(ctx)) { + JSAtom atom = JS_ValueToAtom(ctx, sp[-2]); + if (atom != JS_ATOM_NULL) { + JS_ThrowReferenceErrorNotDefined(ctx, atom); + JS_FreeAtom(ctx, atom); + } + goto exception; + } else { + sp[-3] = js_dup(ctx->global_obj); + } + } else { + if (is_strict_mode(ctx)) + flags |= JS_PROP_NO_ADD; + } + ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags); + JS_FreeValue(ctx, sp[-3]); + sp -= 3; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_put_super_value): + { + int ret; + JSAtom atom; + sf->cur_pc = pc; + if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) { + JS_ThrowTypeErrorNotAnObject(ctx); + goto exception; + } + atom = JS_ValueToAtom(ctx, sp[-2]); + if (unlikely(atom == JS_ATOM_NULL)) + goto exception; + ret = JS_SetPropertyInternal2(ctx, + sp[-3], atom, + sp[-1], sp[-4], + JS_PROP_THROW_STRICT, NULL); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, sp[-4]); + JS_FreeValue(ctx, sp[-3]); + JS_FreeValue(ctx, sp[-2]); + sp -= 4; + if (ret < 0) + goto exception; + } + BREAK; + + CASE(OP_define_array_el): + { + int ret; + ret = JS_DefinePropertyValueValue(ctx, sp[-3], js_dup(sp[-2]), sp[-1], + JS_PROP_C_W_E | JS_PROP_THROW); + sp -= 1; + if (unlikely(ret < 0)) + goto exception; + } + BREAK; + + CASE(OP_append): /* array pos enumobj -- array pos */ + { + sf->cur_pc = pc; + if (js_append_enumerate(ctx, sp)) + goto exception; + JS_FreeValue(ctx, *--sp); + } + BREAK; + + CASE(OP_copy_data_properties): /* target source excludeList */ + { + /* stack offsets (-1 based): + 2 bits for target, + 3 bits for source, + 2 bits for exclusionList */ + int mask; + + mask = *pc++; + sf->cur_pc = pc; + if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)], + sp[-1 - ((mask >> 2) & 7)], + sp[-1 - ((mask >> 5) & 7)], 0)) + goto exception; + } + BREAK; + + CASE(OP_add): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2); + if (unlikely((int)r != r)) + goto add_slow; + sp[-2] = js_int32(r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) + + JS_VALUE_GET_FLOAT64(op2)); + sp--; + } else { + add_slow: + sf->cur_pc = pc; + if (js_add_slow(ctx, sp)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_add_loc): + { + JSValue *pv; + int idx; + idx = *pc; + pc += 1; + + pv = &var_buf[idx]; + if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(*pv) + + JS_VALUE_GET_INT(sp[-1]); + if (unlikely((int)r != r)) + goto add_loc_slow; + *pv = js_int32(r); + sp--; + } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { + JSValue op1; + op1 = sp[-1]; + sp--; + sf->cur_pc = pc; + op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); + if (JS_IsException(op1)) + goto exception; + op1 = JS_ConcatString(ctx, js_dup(*pv), op1); + if (JS_IsException(op1)) + goto exception; + set_value(ctx, pv, op1); + } else { + JSValue ops[2]; + add_loc_slow: + /* In case of exception, js_add_slow frees ops[0] + and ops[1], so we must duplicate *pv */ + sf->cur_pc = pc; + ops[0] = js_dup(*pv); + ops[1] = sp[-1]; + sp--; + if (js_add_slow(ctx, ops + 2)) + goto exception; + set_value(ctx, pv, ops[0]); + } + } + BREAK; + CASE(OP_sub): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int64_t r; + r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2); + if (unlikely((int)r != r)) + goto binary_arith_slow; + sp[-2] = js_int32(r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) - + JS_VALUE_GET_FLOAT64(op2)); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mul): + { + JSValue op1, op2; + double d; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int32_t v1, v2; + int64_t r; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + r = (int64_t)v1 * v2; + if (unlikely((int)r != r)) { + d = (double)r; + goto mul_fp_res; + } + /* need to test zero case for -0 result */ + if (unlikely(r == 0 && (v1 | v2) < 0)) { + d = -0.0; + goto mul_fp_res; + } + sp[-2] = js_int32(r); + sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { + d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); + mul_fp_res: + sp[-2] = js_float64(d); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_div): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + sp[-2] = js_number((double)v1 / (double)v2); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_mod): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + int v1, v2, r; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2); + /* We must avoid v2 = 0, v1 = INT32_MIN and v2 = + -1 and the cases where the result is -0. */ + if (unlikely(v1 < 0 || v2 <= 0)) + goto binary_arith_slow; + r = v1 % v2; + sp[-2] = js_int32(r); + sp--; + } else { + goto binary_arith_slow; + } + } + BREAK; + CASE(OP_pow): + binary_arith_slow: + sf->cur_pc = pc; + if (js_binary_arith_slow(ctx, sp, opcode)) + goto exception; + sp--; + BREAK; + + CASE(OP_plus): + { + JSValue op1; + uint32_t tag; + op1 = sp[-1]; + tag = JS_VALUE_GET_TAG(op1); + if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) { + } else { + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_neg): + { + JSValue op1; + uint32_t tag; + int val; + double d; + op1 = sp[-1]; + tag = JS_VALUE_GET_TAG(op1); + if (tag == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + /* Note: -0 cannot be expressed as integer */ + if (unlikely(val == 0)) { + d = -0.0; + goto neg_fp_res; + } + if (unlikely(val == INT32_MIN)) { + d = -(double)val; + goto neg_fp_res; + } + sp[-1] = js_int32(-val); + } else if (JS_TAG_IS_FLOAT64(tag)) { + d = -JS_VALUE_GET_FLOAT64(op1); + neg_fp_res: + sp[-1] = js_float64(d); + } else { + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_inc): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MAX)) + goto inc_slow; + sp[-1] = js_int32(val + 1); + } else { + inc_slow: + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_dec): + { + JSValue op1; + int val; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MIN)) + goto dec_slow; + sp[-1] = js_int32(val - 1); + } else { + dec_slow: + sf->cur_pc = pc; + if (js_unary_arith_slow(ctx, sp, opcode)) + goto exception; + } + } + BREAK; + CASE(OP_post_inc): + CASE(OP_post_dec): + sf->cur_pc = pc; + if (js_post_inc_slow(ctx, sp, opcode)) + goto exception; + sp++; + BREAK; + CASE(OP_inc_loc): + { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MAX)) + goto inc_loc_slow; + var_buf[idx] = js_int32(val + 1); + } else { + inc_loc_slow: + sf->cur_pc = pc; + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = js_dup(op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc)) + goto exception; + set_value(ctx, &var_buf[idx], op1); + } + } + BREAK; + CASE(OP_dec_loc): + { + JSValue op1; + int val; + int idx; + idx = *pc; + pc += 1; + + op1 = var_buf[idx]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + val = JS_VALUE_GET_INT(op1); + if (unlikely(val == INT32_MIN)) + goto dec_loc_slow; + var_buf[idx] = js_int32(val - 1); + } else { + dec_loc_slow: + sf->cur_pc = pc; + /* must duplicate otherwise the variable value may + be destroyed before JS code accesses it */ + op1 = js_dup(op1); + if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec)) + goto exception; + set_value(ctx, &var_buf[idx], op1); + } + } + BREAK; + CASE(OP_not): + { + JSValue op1; + op1 = sp[-1]; + if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) { + sp[-1] = js_int32(~JS_VALUE_GET_INT(op1)); + } else { + sf->cur_pc = pc; + if (js_not_slow(ctx, sp)) + goto exception; + } + } + BREAK; + + CASE(OP_shl): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v1, v2; + v1 = JS_VALUE_GET_INT(op1); + v2 = JS_VALUE_GET_INT(op2) & 0x1f; + sp[-2] = js_int32(v1 << v2); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_shr): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v2; + v2 = JS_VALUE_GET_INT(op2); + v2 &= 0x1f; + sp[-2] = js_uint32((uint32_t)JS_VALUE_GET_INT(op1) >> v2); + sp--; + } else { + sf->cur_pc = pc; + if (js_shr_slow(ctx, sp)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_sar): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + uint32_t v2; + v2 = JS_VALUE_GET_INT(op2); + if (unlikely(v2 > 0x1f)) { + v2 &= 0x1f; + } + sp[-2] = js_int32((int)JS_VALUE_GET_INT(op1) >> v2); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_and): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = js_int32(JS_VALUE_GET_INT(op1) & JS_VALUE_GET_INT(op2)); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_or): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = js_int32(JS_VALUE_GET_INT(op1) | JS_VALUE_GET_INT(op2)); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + CASE(OP_xor): + { + JSValue op1, op2; + op1 = sp[-2]; + op2 = sp[-1]; + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { + sp[-2] = js_int32(JS_VALUE_GET_INT(op1) ^ JS_VALUE_GET_INT(op2)); + sp--; + } else { + sf->cur_pc = pc; + if (js_binary_logic_slow(ctx, sp, opcode)) + goto exception; + sp--; + } + } + BREAK; + + +#define OP_CMP(opcode, binary_op, slow_call) \ + CASE(opcode): \ + { \ + JSValue op1, op2; \ + op1 = sp[-2]; \ + op2 = sp[-1]; \ + if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \ + sp[-2] = js_bool(JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \ + sp--; \ + } else { \ + sf->cur_pc = pc; \ + if (slow_call) \ + goto exception; \ + sp--; \ + } \ + } \ + BREAK + + OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode)); + OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0)); + OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1)); + OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0)); + OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1)); + + CASE(OP_in): + sf->cur_pc = pc; + if (js_operator_in(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_instanceof): + sf->cur_pc = pc; + if (js_operator_instanceof(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_typeof): + { + JSValue op1; + JSAtom atom; + + op1 = sp[-1]; + atom = js_operator_typeof(ctx, op1); + JS_FreeValue(ctx, op1); + sp[-1] = JS_AtomToString(ctx, atom); + } + BREAK; + CASE(OP_delete): + sf->cur_pc = pc; + if (js_operator_delete(ctx, sp)) + goto exception; + sp--; + BREAK; + CASE(OP_delete_var): + { + JSAtom atom; + int ret; + + atom = get_u32(pc); + pc += 4; + + sf->cur_pc = pc; + ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0); + if (unlikely(ret < 0)) + goto exception; + *sp++ = js_bool(ret); + } + BREAK; + + CASE(OP_to_object): + if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) { + sf->cur_pc = pc; + ret_val = JS_ToObject(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + } + BREAK; + + CASE(OP_to_propkey): + switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + sf->cur_pc = pc; + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; + + CASE(OP_to_propkey2): + /* must be tested first */ + if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) { + JS_ThrowTypeError(ctx, "value has no property"); + goto exception; + } + switch (JS_VALUE_GET_TAG(sp[-1])) { + case JS_TAG_INT: + case JS_TAG_STRING: + case JS_TAG_SYMBOL: + break; + default: + sf->cur_pc = pc; + ret_val = JS_ToPropertyKey(ctx, sp[-1]); + if (JS_IsException(ret_val)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = ret_val; + break; + } + BREAK; + CASE(OP_with_get_var): + CASE(OP_with_put_var): + CASE(OP_with_delete_var): + CASE(OP_with_make_ref): + CASE(OP_with_get_ref): + CASE(OP_with_get_ref_undef): + { + JSAtom atom; + int32_t diff; + JSValue obj, val; + int ret, is_with; + atom = get_u32(pc); + diff = get_u32(pc + 4); + is_with = pc[8]; + pc += 9; + sf->cur_pc = pc; + + obj = sp[-1]; + ret = JS_HasProperty(ctx, obj, atom); + if (unlikely(ret < 0)) + goto exception; + if (ret) { + if (is_with) { + ret = js_has_unscopable(ctx, obj, atom); + if (unlikely(ret < 0)) + goto exception; + if (ret) + goto no_with; + } + switch (opcode) { + case OP_with_get_var: + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + set_value(ctx, &sp[-1], val); + break; + case OP_with_put_var: + /* XXX: check if strict mode */ + ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], + JS_PROP_THROW_STRICT); + JS_FreeValue(ctx, sp[-1]); + sp -= 2; + if (unlikely(ret < 0)) + goto exception; + break; + case OP_with_delete_var: + ret = JS_DeleteProperty(ctx, obj, atom, 0); + if (unlikely(ret < 0)) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = js_bool(ret); + break; + case OP_with_make_ref: + /* produce a pair object/propname on the stack */ + *sp++ = JS_AtomToValue(ctx, atom); + break; + case OP_with_get_ref: + /* produce a pair object/method on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + *sp++ = val; + break; + case OP_with_get_ref_undef: + /* produce a pair undefined/function on the stack */ + val = JS_GetProperty(ctx, obj, atom); + if (unlikely(JS_IsException(val))) + goto exception; + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_UNDEFINED; + *sp++ = val; + break; + } + pc += diff - 5; + } else { + no_with: + /* if not jumping, drop the object argument */ + JS_FreeValue(ctx, sp[-1]); + sp--; + } + } + BREAK; + + CASE(OP_await): + ret_val = js_int32(FUNC_RET_AWAIT); + goto done_generator; + CASE(OP_yield): + ret_val = js_int32(FUNC_RET_YIELD); + goto done_generator; + CASE(OP_yield_star): + CASE(OP_async_yield_star): + ret_val = js_int32(FUNC_RET_YIELD_STAR); + goto done_generator; + CASE(OP_return_async): + CASE(OP_initial_yield): + ret_val = JS_UNDEFINED; + goto done_generator; + + CASE(OP_nop): + BREAK; + CASE(OP_is_undefined_or_null): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED || + JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } else { + goto free_and_set_false; + } + CASE(OP_is_undefined): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) { + goto set_true; + } else { + goto free_and_set_false; + } + CASE(OP_is_null): + if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) { + goto set_true; + } else { + goto free_and_set_false; + } + /* XXX: could merge to a single opcode */ + CASE(OP_typeof_is_undefined): + /* different from OP_is_undefined because of isHTMLDDA */ + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) { + goto free_and_set_true; + } else { + goto free_and_set_false; + } + CASE(OP_typeof_is_function): + if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) { + goto free_and_set_true; + } else { + goto free_and_set_false; + } + free_and_set_true: + JS_FreeValue(ctx, sp[-1]); + set_true: + sp[-1] = JS_TRUE; + BREAK; + free_and_set_false: + JS_FreeValue(ctx, sp[-1]); + sp[-1] = JS_FALSE; + BREAK; + CASE(OP_invalid): + DEFAULT: + JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x", + (int)(pc - b->byte_code_buf - 1), opcode); + goto exception; + } + } + exception: + if (is_backtrace_needed(ctx, rt->current_exception)) { + /* add the backtrace information now (it is not done + before if the exception happens in a bytecode + operation */ + sf->cur_pc = pc; + build_backtrace(ctx, rt->current_exception, NULL, 0, 0, 0); + } + if (!JS_IsUncatchableError(ctx, rt->current_exception)) { + while (sp > stack_buf) { + JSValue val = *--sp; + JS_FreeValue(ctx, val); + if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) { + int pos = JS_VALUE_GET_INT(val); + if (pos == 0) { + /* enumerator: close it with a throw */ + JS_FreeValue(ctx, sp[-1]); /* drop the next method */ + sp--; + JS_IteratorClose(ctx, sp[-1], TRUE); + } else { + *sp++ = rt->current_exception; + rt->current_exception = JS_NULL; + pc = b->byte_code_buf + pos; + goto restart; + } + } + } + } + ret_val = JS_EXCEPTION; + /* the local variables are freed by the caller in the generator + case. Hence the label 'done' should never be reached in a + generator function. */ + if (b->func_kind != JS_FUNC_NORMAL) { + done_generator: + sf->cur_pc = pc; + sf->cur_sp = sp; + } else { + done: + if (unlikely(!list_empty(&sf->var_ref_list))) { + /* variable references reference the stack: must close them */ + close_var_refs(rt, sf); + } + /* free the local variables and stack */ + for(pval = local_buf; pval < sp; pval++) { + JS_FreeValue(ctx, *pval); + } + } + rt->current_stack_frame = sf->prev_frame; + return ret_val; +} + +JSValue JS_Call(JSContext *ctx, JSValue func_obj, JSValue this_obj, + int argc, JSValue *argv) +{ + return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, + argc, argv, JS_CALL_FLAG_COPY_ARGV); +} + +static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValue this_obj, + int argc, JSValue *argv) +{ + JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, + argc, argv, JS_CALL_FLAG_COPY_ARGV); + JS_FreeValue(ctx, func_obj); + return res; +} + +/* warning: the refcount of the context is not incremented. Return + NULL in case of exception (case of revoked proxy only) */ +static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValue func_obj) +{ + JSObject *p; + JSContext *realm; + + if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT) + return ctx; + p = JS_VALUE_GET_OBJ(func_obj); + switch(p->class_id) { + case JS_CLASS_C_FUNCTION: + realm = p->u.cfunc.realm; + break; + case JS_CLASS_BYTECODE_FUNCTION: + case JS_CLASS_GENERATOR_FUNCTION: + case JS_CLASS_ASYNC_FUNCTION: + case JS_CLASS_ASYNC_GENERATOR_FUNCTION: + { + JSFunctionBytecode *b; + b = p->u.func.function_bytecode; + realm = b->realm; + } + break; + case JS_CLASS_PROXY: + { + JSProxyData *s = p->u.opaque; + if (!s) + return ctx; + if (s->is_revoked) { + JS_ThrowTypeErrorRevokedProxy(ctx); + return NULL; + } else { + realm = JS_GetFunctionRealm(ctx, s->target); + } + } + break; + case JS_CLASS_BOUND_FUNCTION: + { + JSBoundFunction *bf = p->u.bound_function; + realm = JS_GetFunctionRealm(ctx, bf->func_obj); + } + break; + default: + realm = ctx; + break; + } + return realm; +} + +static JSValue js_create_from_ctor(JSContext *ctx, JSValue ctor, + int class_id) +{ + JSValue proto, obj; + JSContext *realm; + + if (JS_IsUndefined(ctor)) { + proto = js_dup(ctx->class_proto[class_id]); + } else { + proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype); + if (JS_IsException(proto)) + return proto; + if (!JS_IsObject(proto)) { + JS_FreeValue(ctx, proto); + realm = JS_GetFunctionRealm(ctx, ctor); + if (!realm) + return JS_EXCEPTION; + proto = js_dup(realm->class_proto[class_id]); + } + } + obj = JS_NewObjectProtoClass(ctx, proto, class_id); + JS_FreeValue(ctx, proto); + return obj; +} + +/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ +static JSValue JS_CallConstructorInternal(JSContext *ctx, + JSValue func_obj, + JSValue new_target, + int argc, JSValue *argv, int flags) +{ + JSObject *p; + JSFunctionBytecode *b; + + if (js_poll_interrupts(ctx)) + return JS_EXCEPTION; + flags |= JS_CALL_FLAG_CONSTRUCTOR; + if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) + goto not_a_function; + p = JS_VALUE_GET_OBJ(func_obj); + if (unlikely(!p->is_constructor)) + return JS_ThrowTypeError(ctx, "not a constructor"); + if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) { + JSClassCall *call_func; + call_func = ctx->rt->class_array[p->class_id].call; + if (!call_func) { + not_a_function: + return JS_ThrowTypeError(ctx, "not a function"); + } + return call_func(ctx, func_obj, new_target, argc, + argv, flags); + } + + b = p->u.func.function_bytecode; + if (b->is_derived_class_constructor) { + return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags); + } else { + JSValue obj, ret; + /* legacy constructor behavior */ + obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT); + if (JS_IsException(obj)) + return JS_EXCEPTION; + ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags); + if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT || + JS_IsException(ret)) { + JS_FreeValue(ctx, obj); + return ret; + } else { + JS_FreeValue(ctx, ret); + return obj; + } + } +} + +JSValue JS_CallConstructor2(JSContext *ctx, JSValue func_obj, + JSValue new_target, + int argc, JSValue *argv) +{ + return JS_CallConstructorInternal(ctx, func_obj, new_target, + argc, argv, + JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_CallConstructor(JSContext *ctx, JSValue func_obj, + int argc, JSValue *argv) +{ + return JS_CallConstructorInternal(ctx, func_obj, func_obj, + argc, argv, + JS_CALL_FLAG_COPY_ARGV); +} + +JSValue JS_Invoke(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValue *argv) +{ + JSValue func_obj; + func_obj = JS_GetProperty(ctx, this_val, atom); + if (JS_IsException(func_obj)) + return func_obj; + return JS_CallFree(ctx, func_obj, this_val, argc, argv); +} + +static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, + int argc, JSValue *argv) +{ + JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv); + JS_FreeValue(ctx, this_val); + return res; +} + +/* JSAsyncFunctionState (used by generator and async functions) */ +static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, + JSValue func_obj, JSValue this_obj, + int argc, JSValue *argv) +{ + JSObject *p; + JSFunctionBytecode *b; + JSStackFrame *sf; + int local_count, i, arg_buf_len, n; + + sf = &s->frame; + init_list_head(&sf->var_ref_list); + p = JS_VALUE_GET_OBJ(func_obj); + b = p->u.func.function_bytecode; + sf->js_mode = b->js_mode; + sf->cur_pc = b->byte_code_buf; + arg_buf_len = max_int(b->arg_count, argc); + local_count = arg_buf_len + b->var_count + b->stack_size; + sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1)); + if (!sf->arg_buf) + return -1; + sf->cur_func = js_dup(func_obj); + s->this_val = js_dup(this_obj); + s->argc = argc; + sf->arg_count = arg_buf_len; + sf->var_buf = sf->arg_buf + arg_buf_len; + sf->cur_sp = sf->var_buf + b->var_count; + for(i = 0; i < argc; i++) + sf->arg_buf[i] = js_dup(argv[i]); + n = arg_buf_len + b->var_count; + for(i = argc; i < n; i++) + sf->arg_buf[i] = JS_UNDEFINED; + return 0; +} + +static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s, + JS_MarkFunc *mark_func) +{ + JSStackFrame *sf; + JSValue *sp; + + sf = &s->frame; + JS_MarkValue(rt, sf->cur_func, mark_func); + JS_MarkValue(rt, s->this_val, mark_func); + if (sf->cur_sp) { + /* if the function is running, cur_sp is not known so we + cannot mark the stack. Marking the variables is not needed + because a running function cannot be part of a removable + cycle */ + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) + JS_MarkValue(rt, *sp, mark_func); + } +} + +static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) +{ + JSStackFrame *sf; + JSValue *sp; + + sf = &s->frame; + + /* close the closure variables. */ + close_var_refs(rt, sf); + + if (sf->arg_buf) { + /* cannot free the function if it is running */ + assert(sf->cur_sp != NULL); + for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) { + JS_FreeValueRT(rt, *sp); + } + js_free_rt(rt, sf->arg_buf); + } + JS_FreeValueRT(rt, sf->cur_func); + JS_FreeValueRT(rt, s->this_val); +} + +static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) +{ + JSValue func_obj; + + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); + + /* the tag does not matter provided it is not an object */ + func_obj = JS_MKPTR(JS_TAG_INT, s); + return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, + s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR); +} + + +/* Generators */ + +typedef enum JSGeneratorStateEnum { + JS_GENERATOR_STATE_SUSPENDED_START, + JS_GENERATOR_STATE_SUSPENDED_YIELD, + JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_GENERATOR_STATE_EXECUTING, + JS_GENERATOR_STATE_COMPLETED, +} JSGeneratorStateEnum; + +typedef struct JSGeneratorData { + JSGeneratorStateEnum state; + JSAsyncFunctionState func_state; +} JSGeneratorData; + +static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s) +{ + if (s->state == JS_GENERATOR_STATE_COMPLETED) + return; + async_func_free(rt, &s->func_state); + s->state = JS_GENERATOR_STATE_COMPLETED; +} + +static void js_generator_finalizer(JSRuntime *rt, JSValue obj) +{ + JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR); + + if (s) { + free_generator_stack_rt(rt, s); + js_free_rt(rt, s); + } +} + +static void free_generator_stack(JSContext *ctx, JSGeneratorData *s) +{ + free_generator_stack_rt(ctx->rt, s); +} + +static void js_generator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSGeneratorData *s = p->u.generator_data; + + if (!s || s->state == JS_GENERATOR_STATE_COMPLETED) + return; + async_func_mark(rt, &s->func_state, mark_func); +} + +/* XXX: use enum */ +#define GEN_MAGIC_NEXT 0 +#define GEN_MAGIC_RETURN 1 +#define GEN_MAGIC_THROW 2 + +static JSValue js_generator_next(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + BOOL *pdone, int magic) +{ + JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR); + JSStackFrame *sf; + JSValue ret, func_ret; + + *pdone = TRUE; + if (!s) + return JS_ThrowTypeError(ctx, "not a generator"); + sf = &s->func_state.frame; + switch(s->state) { + default: + case JS_GENERATOR_STATE_SUSPENDED_START: + if (magic == GEN_MAGIC_NEXT) { + goto exec_no_arg; + } else { + free_generator_stack(ctx, s); + goto done; + } + break; + case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR: + case JS_GENERATOR_STATE_SUSPENDED_YIELD: + /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */ + ret = js_dup(argv[0]); + if (magic == GEN_MAGIC_THROW && + s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) { + JS_Throw(ctx, ret); + s->func_state.throw_flag = TRUE; + } else { + sf->cur_sp[-1] = ret; + sf->cur_sp[0] = js_int32(magic); + sf->cur_sp++; + exec_no_arg: + s->func_state.throw_flag = FALSE; + } + s->state = JS_GENERATOR_STATE_EXECUTING; + func_ret = async_func_resume(ctx, &s->func_state); + s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD; + if (JS_IsException(func_ret)) { + /* finalize the execution in case of exception */ + free_generator_stack(ctx, s); + return func_ret; + } + if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + /* get the returned yield value at the top of the stack */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) { + s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR; + /* return (value, done) object */ + *pdone = 2; + } else { + *pdone = FALSE; + } + } else { + /* end of iterator */ + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + JS_FreeValue(ctx, func_ret); + free_generator_stack(ctx, s); + } + break; + case JS_GENERATOR_STATE_COMPLETED: + done: + /* execution is finished */ + switch(magic) { + default: + case GEN_MAGIC_NEXT: + ret = JS_UNDEFINED; + break; + case GEN_MAGIC_RETURN: + ret = js_dup(argv[0]); + break; + case GEN_MAGIC_THROW: + ret = JS_Throw(ctx, js_dup(argv[0])); + break; + } + break; + case JS_GENERATOR_STATE_EXECUTING: + ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator"); + break; + } + return ret; +} + +static JSValue js_generator_function_call(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, + int flags) +{ + JSValue obj, func_ret; + JSGeneratorData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->state = JS_GENERATOR_STATE_SUSPENDED_START; + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->state = JS_GENERATOR_STATE_COMPLETED; + goto fail; + } + + /* execute the function up to 'OP_initial_yield' */ + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) + goto fail; + JS_FreeValue(ctx, func_ret); + + obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR); + if (JS_IsException(obj)) + goto fail; + JS_SetOpaque(obj, s); + return obj; + fail: + free_generator_stack_rt(ctx->rt, s); + js_free(ctx, s); + return JS_EXCEPTION; +} + +/* AsyncFunction */ + +static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s) +{ + if (s->is_active) { + async_func_free(rt, &s->func_state); + s->is_active = FALSE; + } +} + +static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s) +{ + js_async_function_terminate(rt, s); + JS_FreeValueRT(rt, s->resolving_funcs[0]); + JS_FreeValueRT(rt, s->resolving_funcs[1]); + remove_gc_object(&s->header); + js_free_rt(rt, s); +} + +static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s) +{ + if (--s->header.ref_count == 0) { + js_async_function_free0(rt, s); + } +} + +static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + js_async_function_free(rt, s); + } +} + +static void js_async_function_resolve_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSObject *p = JS_VALUE_GET_OBJ(val); + JSAsyncFunctionData *s = p->u.async_function_data; + if (s) { + mark_func(rt, &s->header); + } +} + +static int js_async_function_resolve_create(JSContext *ctx, + JSAsyncFunctionData *s, + JSValue *resolving_funcs) +{ + int i; + JSObject *p; + + for(i = 0; i < 2; i++) { + resolving_funcs[i] = + JS_NewObjectProtoClass(ctx, ctx->function_proto, + JS_CLASS_ASYNC_FUNCTION_RESOLVE + i); + if (JS_IsException(resolving_funcs[i])) { + if (i == 1) + JS_FreeValue(ctx, resolving_funcs[0]); + return -1; + } + p = JS_VALUE_GET_OBJ(resolving_funcs[i]); + s->header.ref_count++; + p->u.async_function_data = s; + } + return 0; +} + +static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) +{ + JSValue func_ret, ret2; + + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) { + JSValue error; + fail: + error = JS_GetException(ctx); + ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, + 1, &error); + JS_FreeValue(ctx, error); + js_async_function_terminate(ctx->rt, s); + JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ + } else { + JSValue value; + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + if (JS_IsUndefined(func_ret)) { + /* function returned */ + ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, + 1, &value); + JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ + JS_FreeValue(ctx, value); + js_async_function_terminate(ctx->rt, s); + } else { + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; + + /* await */ + JS_FreeValue(ctx, func_ret); /* not used */ + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, &value, 0); + JS_FreeValue(ctx, value); + if (JS_IsException(promise)) + goto fail; + if (js_async_function_resolve_create(ctx, s, resolving_funcs)) { + JS_FreeValue(ctx, promise); + goto fail; + } + + /* Note: no need to create 'thrownawayCapability' as in + the spec */ + for(i = 0; i < 2; i++) + resolving_funcs1[i] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + resolving_funcs, + resolving_funcs1); + JS_FreeValue(ctx, promise); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (res) + goto fail; + } + } +} + +static JSValue js_async_function_resolve_call(JSContext *ctx, + JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, + int flags) +{ + JSObject *p = JS_VALUE_GET_OBJ(func_obj); + JSAsyncFunctionData *s = p->u.async_function_data; + BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; + JSValue arg; + + if (argc > 0) + arg = argv[0]; + else + arg = JS_UNDEFINED; + s->func_state.throw_flag = is_reject; + if (is_reject) { + JS_Throw(ctx, js_dup(arg)); + } else { + /* return value of await */ + s->func_state.frame.cur_sp[-1] = js_dup(arg); + } + js_async_function_resume(ctx, s); + return JS_UNDEFINED; +} + +static JSValue js_async_function_call(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, int flags) +{ + JSValue promise; + JSAsyncFunctionData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->header.ref_count = 1; + add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); + s->is_active = FALSE; + s->resolving_funcs[0] = JS_UNDEFINED; + s->resolving_funcs[1] = JS_UNDEFINED; + + promise = JS_NewPromiseCapability(ctx, s->resolving_funcs); + if (JS_IsException(promise)) + goto fail; + + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + fail: + JS_FreeValue(ctx, promise); + js_async_function_free(ctx->rt, s); + return JS_EXCEPTION; + } + s->is_active = TRUE; + + js_async_function_resume(ctx, s); + + js_async_function_free(ctx->rt, s); + + return promise; +} + +/* AsyncGenerator */ + +typedef enum JSAsyncGeneratorStateEnum { + JS_ASYNC_GENERATOR_STATE_SUSPENDED_START, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD, + JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR, + JS_ASYNC_GENERATOR_STATE_EXECUTING, + JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN, + JS_ASYNC_GENERATOR_STATE_COMPLETED, +} JSAsyncGeneratorStateEnum; + +typedef struct JSAsyncGeneratorRequest { + struct list_head link; + /* completion */ + int completion_type; /* GEN_MAGIC_x */ + JSValue result; + /* promise capability */ + JSValue promise; + JSValue resolving_funcs[2]; +} JSAsyncGeneratorRequest; + +typedef struct JSAsyncGeneratorData { + JSObject *generator; /* back pointer to the object (const) */ + JSAsyncGeneratorStateEnum state; + JSAsyncFunctionState func_state; + struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ +} JSAsyncGeneratorData; + +static void js_async_generator_free(JSRuntime *rt, + JSAsyncGeneratorData *s) +{ + struct list_head *el, *el1; + JSAsyncGeneratorRequest *req; + + list_for_each_safe(el, el1, &s->queue) { + req = list_entry(el, JSAsyncGeneratorRequest, link); + JS_FreeValueRT(rt, req->result); + JS_FreeValueRT(rt, req->promise); + JS_FreeValueRT(rt, req->resolving_funcs[0]); + JS_FreeValueRT(rt, req->resolving_funcs[1]); + js_free_rt(rt, req); + } + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && + s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { + async_func_free(rt, &s->func_state); + } + js_free_rt(rt, s); +} + +static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR); + + if (s) { + js_async_generator_free(rt, s); + } +} + +static void js_async_generator_mark(JSRuntime *rt, JSValue val, + JS_MarkFunc *mark_func) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR); + struct list_head *el; + JSAsyncGeneratorRequest *req; + if (s) { + list_for_each(el, &s->queue) { + req = list_entry(el, JSAsyncGeneratorRequest, link); + JS_MarkValue(rt, req->result, mark_func); + JS_MarkValue(rt, req->promise, mark_func); + JS_MarkValue(rt, req->resolving_funcs[0], mark_func); + JS_MarkValue(rt, req->resolving_funcs[1], mark_func); + } + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED && + s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) { + async_func_mark(rt, &s->func_state, mark_func); + } + } +} + +static JSValue js_async_generator_resolve_function(JSContext *ctx, + JSValue this_obj, + int argc, JSValue *argv, + int magic, JSValue *func_data); + +static int js_async_generator_resolve_function_create(JSContext *ctx, + JSValue generator, + JSValue *resolving_funcs, + BOOL is_resume_next) +{ + int i; + JSValue func; + + for(i = 0; i < 2; i++) { + func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1, + i + is_resume_next * 2, 1, &generator); + if (JS_IsException(func)) { + if (i == 1) + JS_FreeValue(ctx, resolving_funcs[0]); + return -1; + } + resolving_funcs[i] = func; + } + return 0; +} + +static int js_async_generator_await(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue value) +{ + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int i, res; + + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, &value, 0); + if (JS_IsException(promise)) + goto fail; + + if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator), + resolving_funcs, FALSE)) { + JS_FreeValue(ctx, promise); + goto fail; + } + + /* Note: no need to create 'thrownawayCapability' as in + the spec */ + for(i = 0; i < 2; i++) + resolving_funcs1[i] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + resolving_funcs, + resolving_funcs1); + JS_FreeValue(ctx, promise); + for(i = 0; i < 2; i++) + JS_FreeValue(ctx, resolving_funcs[i]); + if (res) + goto fail; + return 0; + fail: + return -1; +} + +static void js_async_generator_resolve_or_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue result, + int is_reject) +{ + JSAsyncGeneratorRequest *next; + JSValue ret; + + next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); + list_del(&next->link); + ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1, + &result); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, next->result); + JS_FreeValue(ctx, next->promise); + JS_FreeValue(ctx, next->resolving_funcs[0]); + JS_FreeValue(ctx, next->resolving_funcs[1]); + js_free(ctx, next); +} + +static void js_async_generator_resolve(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue value, + BOOL done) +{ + JSValue result; + result = js_create_iterator_result(ctx, js_dup(value), done); + /* XXX: better exception handling ? */ + js_async_generator_resolve_or_reject(ctx, s, result, 0); + JS_FreeValue(ctx, result); + } + +static void js_async_generator_reject(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue exception) +{ + js_async_generator_resolve_or_reject(ctx, s, exception, 1); +} + +static void js_async_generator_complete(JSContext *ctx, + JSAsyncGeneratorData *s) +{ + if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) { + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + async_func_free(ctx->rt, &s->func_state); + } +} + +static int js_async_generator_completed_return(JSContext *ctx, + JSAsyncGeneratorData *s, + JSValue value) +{ + JSValue promise, resolving_funcs[2], resolving_funcs1[2]; + int res; + + // Can fail looking up JS_ATOM_constructor when is_reject==0. + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &value, + /*is_reject*/0); + // A poisoned .constructor property is observable and the resulting + // exception should be delivered to the catch handler. + if (JS_IsException(promise)) { + JSValue err = JS_GetException(ctx); + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &err, + /*is_reject*/1); + JS_FreeValue(ctx, err); + if (JS_IsException(promise)) + return -1; + } + if (js_async_generator_resolve_function_create(ctx, + JS_MKPTR(JS_TAG_OBJECT, s->generator), + resolving_funcs1, + TRUE)) { + JS_FreeValue(ctx, promise); + return -1; + } + resolving_funcs[0] = JS_UNDEFINED; + resolving_funcs[1] = JS_UNDEFINED; + res = perform_promise_then(ctx, promise, + resolving_funcs1, + resolving_funcs); + JS_FreeValue(ctx, resolving_funcs1[0]); + JS_FreeValue(ctx, resolving_funcs1[1]); + JS_FreeValue(ctx, promise); + return res; +} + +static void js_async_generator_resume_next(JSContext *ctx, + JSAsyncGeneratorData *s) +{ + JSAsyncGeneratorRequest *next; + JSValue func_ret, value; + + for(;;) { + if (list_empty(&s->queue)) + break; + next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link); + switch(s->state) { + case JS_ASYNC_GENERATOR_STATE_EXECUTING: + /* only happens when restarting execution after await() */ + goto resume_exec; + case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN: + goto done; + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START: + if (next->completion_type == GEN_MAGIC_NEXT) { + goto exec_no_arg; + } else { + js_async_generator_complete(ctx, s); + } + break; + case JS_ASYNC_GENERATOR_STATE_COMPLETED: + if (next->completion_type == GEN_MAGIC_NEXT) { + js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE); + } else if (next->completion_type == GEN_MAGIC_RETURN) { + s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN; + js_async_generator_completed_return(ctx, s, next->result); + } else { + js_async_generator_reject(ctx, s, next->result); + } + goto done; + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD: + case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR: + value = js_dup(next->result); + if (next->completion_type == GEN_MAGIC_THROW && + s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) { + JS_Throw(ctx, value); + s->func_state.throw_flag = TRUE; + } else { + /* 'yield' returns a value. 'yield *' also returns a value + in case the 'throw' method is called */ + s->func_state.frame.cur_sp[-1] = value; + s->func_state.frame.cur_sp[0] = + js_int32(next->completion_type); + s->func_state.frame.cur_sp++; + exec_no_arg: + s->func_state.throw_flag = FALSE; + } + s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING; + resume_exec: + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) { + value = JS_GetException(ctx); + js_async_generator_complete(ctx, s); + js_async_generator_reject(ctx, s, value); + JS_FreeValue(ctx, value); + } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + int func_ret_code, ret; + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + func_ret_code = JS_VALUE_GET_INT(func_ret); + switch(func_ret_code) { + case FUNC_RET_YIELD: + case FUNC_RET_YIELD_STAR: + if (func_ret_code == FUNC_RET_YIELD_STAR) + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR; + else + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD; + js_async_generator_resolve(ctx, s, value, FALSE); + JS_FreeValue(ctx, value); + break; + case FUNC_RET_AWAIT: + ret = js_async_generator_await(ctx, s, value); + JS_FreeValue(ctx, value); + if (ret < 0) { + /* exception: throw it */ + s->func_state.throw_flag = TRUE; + goto resume_exec; + } + goto done; + default: + abort(); + } + } else { + assert(JS_IsUndefined(func_ret)); + /* end of function */ + value = s->func_state.frame.cur_sp[-1]; + s->func_state.frame.cur_sp[-1] = JS_UNDEFINED; + js_async_generator_complete(ctx, s); + js_async_generator_resolve(ctx, s, value, TRUE); + JS_FreeValue(ctx, value); + } + break; + default: + abort(); + } + } + done: ; +} + +static JSValue js_async_generator_resolve_function(JSContext *ctx, + JSValue this_obj, + int argc, JSValue *argv, + int magic, JSValue *func_data) +{ + BOOL is_reject = magic & 1; + JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR); + JSValue arg = argv[0]; + + /* XXX: what if s == NULL */ + + if (magic >= 2) { + /* resume next case in AWAITING_RETURN state */ + assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN || + s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED); + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + if (is_reject) { + js_async_generator_reject(ctx, s, arg); + } else { + js_async_generator_resolve(ctx, s, arg, TRUE); + } + } else { + /* restart function execution after await() */ + assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING); + s->func_state.throw_flag = is_reject; + if (is_reject) { + JS_Throw(ctx, js_dup(arg)); + } else { + /* return value of await */ + s->func_state.frame.cur_sp[-1] = js_dup(arg); + } + js_async_generator_resume_next(ctx, s); + } + return JS_UNDEFINED; +} + +/* magic = GEN_MAGIC_x */ +static JSValue js_async_generator_next(JSContext *ctx, JSValue this_val, + int argc, JSValue *argv, + int magic) +{ + JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR); + JSValue promise, resolving_funcs[2]; + JSAsyncGeneratorRequest *req; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + if (!s) { + JSValue err, res2; + JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); + err = JS_GetException(ctx); + res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, &err); + JS_FreeValue(ctx, err); + JS_FreeValue(ctx, res2); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; + } + req = js_mallocz(ctx, sizeof(*req)); + if (!req) + goto fail; + req->completion_type = magic; + req->result = js_dup(argv[0]); + req->promise = js_dup(promise); + req->resolving_funcs[0] = resolving_funcs[0]; + req->resolving_funcs[1] = resolving_funcs[1]; + list_add_tail(&req->link, &s->queue); + if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) { + js_async_generator_resume_next(ctx, s); + } + return promise; + fail: + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + JS_FreeValue(ctx, promise); + return JS_EXCEPTION; +} + +static JSValue js_async_generator_function_call(JSContext *ctx, JSValue func_obj, + JSValue this_obj, + int argc, JSValue *argv, + int flags) +{ + JSValue obj, func_ret; + JSAsyncGeneratorData *s; + + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return JS_EXCEPTION; + s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START; + init_list_head(&s->queue); + if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) { + s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; + goto fail; + } + + /* execute the function up to 'OP_initial_yield' (no yield nor + await are possible) */ + func_ret = async_func_resume(ctx, &s->func_state); + if (JS_IsException(func_ret)) + goto fail; + JS_FreeValue(ctx, func_ret); + + obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR); + if (JS_IsException(obj)) + goto fail; + s->generator = JS_VALUE_GET_OBJ(obj); + JS_SetOpaque(obj, s); + return obj; + fail: + js_async_generator_free(ctx->rt, s); + return JS_EXCEPTION; +} + +/* JS parser */ + +enum { + TOK_NUMBER = -128, + TOK_STRING, + TOK_TEMPLATE, + TOK_IDENT, + TOK_REGEXP, + /* warning: order matters (see js_parse_assign_expr) */ + TOK_MUL_ASSIGN, + TOK_DIV_ASSIGN, + TOK_MOD_ASSIGN, + TOK_PLUS_ASSIGN, + TOK_MINUS_ASSIGN, + TOK_SHL_ASSIGN, + TOK_SAR_ASSIGN, + TOK_SHR_ASSIGN, + TOK_AND_ASSIGN, + TOK_XOR_ASSIGN, + TOK_OR_ASSIGN, + TOK_POW_ASSIGN, + TOK_LAND_ASSIGN, + TOK_LOR_ASSIGN, + TOK_DOUBLE_QUESTION_MARK_ASSIGN, + TOK_DEC, + TOK_INC, + TOK_SHL, + TOK_SAR, + TOK_SHR, + TOK_LT, + TOK_LTE, + TOK_GT, + TOK_GTE, + TOK_EQ, + TOK_STRICT_EQ, + TOK_NEQ, + TOK_STRICT_NEQ, + TOK_LAND, + TOK_LOR, + TOK_POW, + TOK_ARROW, + TOK_ELLIPSIS, + TOK_DOUBLE_QUESTION_MARK, + TOK_QUESTION_MARK_DOT, + TOK_ERROR, + TOK_PRIVATE_NAME, + TOK_EOF, + /* keywords: WARNING: same order as atoms */ + TOK_NULL, /* must be first */ + TOK_FALSE, + TOK_TRUE, + TOK_IF, + TOK_ELSE, + TOK_RETURN, + TOK_VAR, + TOK_THIS, + TOK_DELETE, + TOK_VOID, + TOK_TYPEOF, + TOK_NEW, + TOK_IN, + TOK_INSTANCEOF, + TOK_DO, + TOK_WHILE, + TOK_FOR, + TOK_BREAK, + TOK_CONTINUE, + TOK_SWITCH, + TOK_CASE, + TOK_DEFAULT, + TOK_THROW, + TOK_TRY, + TOK_CATCH, + TOK_FINALLY, + TOK_FUNCTION, + TOK_DEBUGGER, + TOK_WITH, + /* FutureReservedWord */ + TOK_CLASS, + TOK_CONST, + TOK_ENUM, + TOK_EXPORT, + TOK_EXTENDS, + TOK_IMPORT, + TOK_SUPER, + /* FutureReservedWords when parsing strict mode code */ + TOK_IMPLEMENTS, + TOK_INTERFACE, + TOK_LET, + TOK_PACKAGE, + TOK_PRIVATE, + TOK_PROTECTED, + TOK_PUBLIC, + TOK_STATIC, + TOK_YIELD, + TOK_AWAIT, /* must be last */ + TOK_OF, /* only used for js_parse_skip_parens_token() */ +}; + +#define TOK_FIRST_KEYWORD TOK_NULL +#define TOK_LAST_KEYWORD TOK_AWAIT + +/* unicode code points */ +#define CP_NBSP 0x00a0 +#define CP_BOM 0xfeff + +#define CP_LS 0x2028 +#define CP_PS 0x2029 + +typedef struct BlockEnv { + struct BlockEnv *prev; + JSAtom label_name; /* JS_ATOM_NULL if none */ + int label_break; /* -1 if none */ + int label_cont; /* -1 if none */ + int drop_count; /* number of stack elements to drop */ + int label_finally; /* -1 if none */ + int scope_level; + int has_iterator; +} BlockEnv; + +typedef struct JSGlobalVar { + int cpool_idx; /* if >= 0, index in the constant pool for hoisted + function defintion*/ + uint8_t force_init : 1; /* force initialization to undefined */ + uint8_t is_lexical : 1; /* global let/const definition */ + uint8_t is_const : 1; /* const definition */ + int scope_level; /* scope of definition */ + JSAtom var_name; /* variable name */ +} JSGlobalVar; + +typedef struct RelocEntry { + struct RelocEntry *next; + uint32_t addr; /* address to patch */ + int size; /* address size: 1, 2 or 4 bytes */ +} RelocEntry; + +typedef struct JumpSlot { + int op; + int size; + int pos; + int label; +} JumpSlot; + +typedef struct LabelSlot { + int ref_count; + int pos; /* phase 1 address, -1 means not resolved yet */ + int pos2; /* phase 2 address, -1 means not resolved yet */ + int addr; /* phase 3 address, -1 means not resolved yet */ + RelocEntry *first_reloc; +} LabelSlot; + +typedef struct SourceLocSlot { + uint32_t pc; + int line_num; + int col_num; +} SourceLocSlot; + +typedef enum JSParseFunctionEnum { + JS_PARSE_FUNC_STATEMENT, + JS_PARSE_FUNC_VAR, + JS_PARSE_FUNC_EXPR, + JS_PARSE_FUNC_ARROW, + JS_PARSE_FUNC_GETTER, + JS_PARSE_FUNC_SETTER, + JS_PARSE_FUNC_METHOD, + JS_PARSE_FUNC_CLASS_STATIC_INIT, + JS_PARSE_FUNC_CLASS_CONSTRUCTOR, + JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR, +} JSParseFunctionEnum; + +typedef enum JSParseExportEnum { + JS_PARSE_EXPORT_NONE, + JS_PARSE_EXPORT_NAMED, + JS_PARSE_EXPORT_DEFAULT, +} JSParseExportEnum; + +typedef struct JSFunctionDef { + JSContext *ctx; + struct JSFunctionDef *parent; + int parent_cpool_idx; /* index in the constant pool of the parent + or -1 if none */ + int parent_scope_level; /* scope level in parent at point of definition */ + struct list_head child_list; /* list of JSFunctionDef.link */ + struct list_head link; + + BOOL is_eval; /* TRUE if eval code */ + int eval_type; /* only valid if is_eval = TRUE */ + BOOL is_global_var; /* TRUE if variables are not defined locally: + eval global, eval module or non strict eval */ + BOOL is_func_expr; /* TRUE if function expression */ + BOOL has_home_object; /* TRUE if the home object is available */ + BOOL has_prototype; /* true if a prototype field is necessary */ + BOOL has_simple_parameter_list; + BOOL has_parameter_expressions; /* if true, an argument scope is created */ + BOOL has_use_strict; /* to reject directive in special cases */ + BOOL has_eval_call; /* true if the function contains a call to eval() */ + BOOL has_arguments_binding; /* true if the 'arguments' binding is + available in the function */ + BOOL has_this_binding; /* true if the 'this' and new.target binding are + available in the function */ + BOOL new_target_allowed; /* true if the 'new.target' does not + throw a syntax error */ + BOOL super_call_allowed; /* true if super() is allowed */ + BOOL super_allowed; /* true if super. or super[] is allowed */ + BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */ + BOOL is_derived_class_constructor; + BOOL in_function_body; + BOOL backtrace_barrier; + JSFunctionKindEnum func_kind : 8; + JSParseFunctionEnum func_type : 8; + uint8_t js_mode; /* bitmap of JS_MODE_x */ + JSAtom func_name; /* JS_ATOM_NULL if no name */ + + JSVarDef *vars; + int var_size; /* allocated size for vars[] */ + int var_count; + JSVarDef *args; + int arg_size; /* allocated size for args[] */ + int arg_count; /* number of arguments */ + int defined_arg_count; + int var_object_idx; /* -1 if none */ + int arg_var_object_idx; /* -1 if none (var object for the argument scope) */ + int arguments_var_idx; /* -1 if none */ + int arguments_arg_idx; /* argument variable definition in argument scope, + -1 if none */ + int func_var_idx; /* variable containing the current function (-1 + if none, only used if is_func_expr is true) */ + int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */ + int this_var_idx; /* variable containg the 'this' value, -1 if none */ + int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */ + int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */ + int home_object_var_idx; + BOOL need_home_object; + + int scope_level; /* index into fd->scopes if the current lexical scope */ + int scope_first; /* index into vd->vars of first lexically scoped variable */ + int scope_size; /* allocated size of fd->scopes array */ + int scope_count; /* number of entries used in the fd->scopes array */ + JSVarScope *scopes; + JSVarScope def_scope_array[4]; + int body_scope; /* scope of the body of the function or eval */ + + int global_var_count; + int global_var_size; + JSGlobalVar *global_vars; + + DynBuf byte_code; + int last_opcode_pos; /* -1 if no last opcode */ + int last_opcode_line_num; + int last_opcode_col_num; + BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */ + + LabelSlot *label_slots; + int label_size; /* allocated size for label_slots[] */ + int label_count; + BlockEnv *top_break; /* break/continue label stack */ + + /* constant pool (strings, functions, numbers) */ + JSValue *cpool; + int cpool_count; + int cpool_size; + + /* list of variables in the closure */ + int closure_var_count; + int closure_var_size; + JSClosureVar *closure_var; + + JumpSlot *jump_slots; + int jump_size; + int jump_count; + + SourceLocSlot *source_loc_slots; + int source_loc_size; + int source_loc_count; + int line_number_last; + int line_number_last_pc; + int col_number_last; + + /* pc2line table */ + JSAtom filename; + int line_num; + int col_num; + DynBuf pc2line; + + char *source; /* raw source, utf-8 encoded */ + int source_len; + + JSModuleDef *module; /* != NULL when parsing a module */ + BOOL has_await; /* TRUE if await is used (used in module eval) */ + JSInlineCache *ic; /* inline cache for field op */ +} JSFunctionDef; + +typedef struct JSToken { + int val; + int line_num; /* line number of token start */ + int col_num; /* column number of token start */ + const uint8_t *ptr; + union { + struct { + JSValue str; + int sep; + } str; + struct { + JSValue val; + } num; + struct { + JSAtom atom; + BOOL has_escape; + BOOL is_reserved; + } ident; + struct { + JSValue body; + JSValue flags; + } regexp; + } u; +} JSToken; + +typedef struct JSParseState { + JSContext *ctx; + int last_line_num; /* line number of last token */ + int last_col_num; /* column number of last token */ + int line_num; /* line number of current offset */ + int col_num; /* column number of current offset */ + const char *filename; + JSToken token; + BOOL got_lf; /* true if got line feed before the current token */ + const uint8_t *last_ptr; + const uint8_t *buf_start; + const uint8_t *buf_ptr; + const uint8_t *buf_end; + const uint8_t *eol; // most recently seen end-of-line character + const uint8_t *mark; // first token character, invariant: eol < mark + + /* current function code */ + JSFunctionDef *cur_func; + BOOL is_module; /* parsing a module */ + BOOL allow_html_comments; +} JSParseState; + +typedef struct JSOpCode { +#ifdef DUMP_BYTECODE + const char *name; +#endif + uint8_t size; /* in bytes */ + /* the opcodes remove n_pop items from the top of the stack, then + pushes n_push items */ + uint8_t n_pop; + uint8_t n_push; + uint8_t fmt; +} JSOpCode; + +static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { +#define FMT(f) +#ifdef DUMP_BYTECODE +#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f }, +#else +#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f }, +#endif +#include "quickjs-opcode.h" +#undef DEF +#undef FMT +}; + +/* After the final compilation pass, short opcodes are used. Their + opcodes overlap with the temporary opcodes which cannot appear in + the final bytecode. Their description is after the temporary + opcodes in opcode_info[]. */ +#define short_opcode_info(op) \ + opcode_info[(op) >= OP_TEMP_START ? \ + (op) + (OP_TEMP_END - OP_TEMP_START) : (op)] + +static __exception int next_token(JSParseState *s); + +static void free_token(JSParseState *s, JSToken *token) +{ + switch(token->val) { + case TOK_NUMBER: + JS_FreeValue(s->ctx, token->u.num.val); + break; + case TOK_STRING: + case TOK_TEMPLATE: + JS_FreeValue(s->ctx, token->u.str.str); + break; + case TOK_REGEXP: + JS_FreeValue(s->ctx, token->u.regexp.body); + JS_FreeValue(s->ctx, token->u.regexp.flags); + break; + case TOK_IDENT: + case TOK_PRIVATE_NAME: + JS_FreeAtom(s->ctx, token->u.ident.atom); + break; + default: + if (token->val >= TOK_FIRST_KEYWORD && + token->val <= TOK_LAST_KEYWORD) { + JS_FreeAtom(s->ctx, token->u.ident.atom); + } + break; + } +} + +static void __attribute((unused)) dump_token(JSParseState *s, + const JSToken *token) +{ + printf("%d:%d ", token->line_num, token->col_num); + switch(token->val) { + case TOK_NUMBER: + { + double d; + JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */ + printf("number: %.14g\n", d); + } + break; + case TOK_IDENT: + dump_atom: + { + char buf[ATOM_GET_STR_BUF_SIZE]; + printf("ident: '%s'\n", + JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom)); + } + break; + case TOK_STRING: + { + const char *str; + /* XXX: quote the string */ + str = JS_ToCString(s->ctx, token->u.str.str); + printf("string: '%s'\n", str); + JS_FreeCString(s->ctx, str); + } + break; + case TOK_TEMPLATE: + { + const char *str; + str = JS_ToCString(s->ctx, token->u.str.str); + printf("template: `%s`\n", str); + JS_FreeCString(s->ctx, str); + } + break; + case TOK_REGEXP: + { + const char *str, *str2; + str = JS_ToCString(s->ctx, token->u.regexp.body); + str2 = JS_ToCString(s->ctx, token->u.regexp.flags); + printf("regexp: '%s' '%s'\n", str, str2); + JS_FreeCString(s->ctx, str); + JS_FreeCString(s->ctx, str2); + } + break; + case TOK_EOF: + printf("eof\n"); + break; + default: + if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) { + goto dump_atom; + } else if (s->token.val >= 256) { + printf("token: %d\n", token->val); + } else { + printf("token: '%c'\n", token->val); + } + break; + } +} + +int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...) +{ + JSContext *ctx = s->ctx; + va_list ap; + int backtrace_flags; + + va_start(ap, fmt); + JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE); + va_end(ap); + backtrace_flags = 0; + if (s->cur_func && s->cur_func->backtrace_barrier) + backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL; + build_backtrace(ctx, ctx->rt->current_exception, s->filename, + s->line_num, s->col_num, backtrace_flags); + return -1; +} + +static int js_parse_expect(JSParseState *s, int tok) +{ + char buf[ATOM_GET_STR_BUF_SIZE]; + + if (s->token.val == tok) + return next_token(s); + + switch(s->token.val) { + case TOK_EOF: + return js_parse_error(s, "Unexpected end of input"); + case TOK_NUMBER: + return js_parse_error(s, "Unexpected number"); + case TOK_STRING: + return js_parse_error(s, "Unexpected string"); + case TOK_TEMPLATE: + return js_parse_error(s, "Unexpected string template"); + case TOK_REGEXP: + return js_parse_error(s, "Unexpected regexp"); + case TOK_IDENT: + return js_parse_error(s, "Unexpected identifier '%s'", + JS_AtomGetStr(s->ctx, buf, sizeof(buf), + s->token.u.ident.atom)); + case TOK_ERROR: + return js_parse_error(s, "Invalid or unexpected token"); + default: + return js_parse_error(s, "Unexpected token '%.*s'", + (int)(s->buf_ptr - s->token.ptr), + (const char *)s->token.ptr); + } +} + +static int js_parse_expect_semi(JSParseState *s) +{ + if (s->token.val != ';') { + /* automatic insertion of ';' */ + if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) { + return 0; + } + return js_parse_error(s, "expecting '%c'", ';'); + } + return next_token(s); +} + +static int js_parse_error_reserved_identifier(JSParseState *s) +{ + char buf1[ATOM_GET_STR_BUF_SIZE]; + return js_parse_error(s, "'%s' is a reserved identifier", + JS_AtomGetStr(s->ctx, buf1, sizeof(buf1), + s->token.u.ident.atom)); +} + +static __exception int js_parse_template_part(JSParseState *s, + const uint8_t *p) +{ + const uint8_t *p_next; + uint32_t c; + StringBuffer b_s, *b = &b_s; + + /* p points to the first byte of the template part */ + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + for(;;) { + if (p >= s->buf_end) + goto unexpected_eof; + c = *p++; + if (c == '`') { + /* template end part */ + break; + } + if (c == '$' && *p == '{') { + /* template start or middle part */ + p++; + break; + } + if (c == '\\') { + if (string_buffer_putc8(b, c)) + goto fail; + if (p >= s->buf_end) + goto unexpected_eof; + c = *p++; + } + /* newline sequences are normalized as single '\n' bytes */ + if (c == '\r') { + if (*p == '\n') + p++; + c = '\n'; + } + if (c == '\n') { + s->line_num++; + s->eol = &p[-1]; + s->mark = p; + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) { + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + } + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + s->token.val = TOK_TEMPLATE; + s->token.u.str.sep = c; + s->token.u.str.str = string_buffer_end(b); + s->buf_ptr = p; + return 0; + + unexpected_eof: + js_parse_error(s, "unexpected end of string"); + fail: + string_buffer_free(b); + return -1; +} + +static __exception int js_parse_string(JSParseState *s, int sep, + BOOL do_throw, const uint8_t *p, + JSToken *token, const uint8_t **pp) +{ + const uint8_t *p_next; + int ret; + uint32_t c; + StringBuffer b_s, *b = &b_s; + + /* string */ + if (string_buffer_init(s->ctx, b, 32)) + goto fail; + for(;;) { + if (p >= s->buf_end) + goto invalid_char; + c = *p; + if (c < 0x20) { + if (sep == '`') { + if (c == '\r') { + if (p[1] == '\n') + p++; + c = '\n'; + } + /* do not update s->line_num */ + } else if (c == '\n' || c == '\r') + goto invalid_char; + } + p++; + if (c == sep) + break; + if (c == '$' && *p == '{' && sep == '`') { + /* template start or middle part */ + p++; + break; + } + if (c == '\\') { + c = *p; + switch(c) { + case '\0': + if (p >= s->buf_end) { + if (sep != '`') + goto invalid_char; + if (do_throw) + js_parse_error(s, "Unexpected end of input"); + goto fail; + } + p++; + break; + case '\'': + case '\"': + case '\\': + p++; + break; + case '\r': /* accept DOS and MAC newline sequences */ + if (p[1] == '\n') { + p++; + } + /* fall thru */ + case '\n': + /* ignore escaped newline sequence */ + p++; + if (sep != '`') { + s->line_num++; + s->eol = &p[-1]; + s->mark = p; + } + continue; + default: + if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) { + /* accept isolated \0 */ + p++; + c = '\0'; + } else + if ((c >= '0' && c <= '9') + && ((s->cur_func->js_mode & JS_MODE_STRICT) || sep == '`')) { + if (do_throw) { + js_parse_error(s, "%s are not allowed in %s", + (c >= '8') ? "\\8 and \\9" : "Octal escape sequences", + (sep == '`') ? "template strings" : "strict mode"); + } + goto fail; + } else if (c >= 0x80) { + c = utf8_decode(p, &p_next); + if (p_next == p + 1) { + goto invalid_utf8; + } + p = p_next; + /* LS or PS are skipped */ + if (c == CP_LS || c == CP_PS) + continue; + } else { + ret = lre_parse_escape(&p, TRUE); + if (ret == -1) { + if (do_throw) { + js_parse_error(s, "Invalid %s escape sequence", + c == 'u' ? "Unicode" : "hexadecimal"); + } + goto fail; + } else if (ret < 0) { + /* ignore the '\' (could output a warning) */ + p++; + } else { + c = ret; + } + } + break; + } + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) + goto invalid_utf8; + p = p_next; + } + if (string_buffer_putc(b, c)) + goto fail; + } + token->val = TOK_STRING; + token->u.str.sep = c; + token->u.str.str = string_buffer_end(b); + *pp = p; + return 0; + + invalid_utf8: + if (do_throw) + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + invalid_char: + if (do_throw) + js_parse_error(s, "unexpected end of string"); + fail: + string_buffer_free(b); + return -1; +} + +static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) { + return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom && + !s->token.u.ident.has_escape; +} + +static __exception int js_parse_regexp(JSParseState *s) +{ + const uint8_t *p, *p_next; + BOOL in_class; + StringBuffer b_s, *b = &b_s; + StringBuffer b2_s, *b2 = &b2_s; + uint32_t c; + + p = s->buf_ptr; + p++; + in_class = FALSE; + if (string_buffer_init(s->ctx, b, 32)) + return -1; + if (string_buffer_init(s->ctx, b2, 1)) + goto fail; + for(;;) { + if (p >= s->buf_end) { + eof_error: + js_parse_error(s, "unexpected end of regexp"); + goto fail; + } + c = *p++; + if (c == '\n' || c == '\r') { + goto eol_error; + } else if (c == '/') { + if (!in_class) + break; + } else if (c == '[') { + in_class = TRUE; + } else if (c == ']') { + /* XXX: incorrect as the first character in a class */ + in_class = FALSE; + } else if (c == '\\') { + if (string_buffer_putc8(b, c)) + goto fail; + c = *p++; + if (c == '\n' || c == '\r') + goto eol_error; + else if (c == '\0' && p >= s->buf_end) + goto eof_error; + else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) { + goto invalid_utf8; + } + p = p_next; + if (c == CP_LS || c == CP_PS) + goto eol_error; + } + } else if (c >= 0x80) { + c = utf8_decode(p - 1, &p_next); + if (p_next == p) { + invalid_utf8: + js_parse_error(s, "invalid UTF-8 sequence"); + goto fail; + } + p = p_next; + /* LS or PS are considered as line terminator */ + if (c == CP_LS || c == CP_PS) { + eol_error: + js_parse_error(s, "unexpected line terminator in regexp"); + goto fail; + } + } + if (string_buffer_putc(b, c)) + goto fail; + } + + /* flags */ + for(;;) { + c = utf8_decode(p, &p_next); + /* no need to test for invalid UTF-8, 0xFFFD is not ident_next */ + if (!lre_js_is_ident_next(c)) + break; + if (string_buffer_putc(b2, c)) + goto fail; + p = p_next; + } + + s->token.val = TOK_REGEXP; + s->token.u.regexp.body = string_buffer_end(b); + s->token.u.regexp.flags = string_buffer_end(b2); + s->buf_ptr = p; + return 0; + fail: + string_buffer_free(b); + string_buffer_free(b2); + return -1; +} + +static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, + char *static_buf) +{ + char *buf, *new_buf; + size_t size, new_size; + + buf = *pbuf; + size = *psize; + if (size >= (SIZE_MAX / 3) * 2) + new_size = SIZE_MAX; + else + new_size = size + (size >> 1); + if (buf == static_buf) { + new_buf = js_malloc(ctx, new_size); + if (!new_buf) + return -1; + memcpy(new_buf, buf, size); + } else { + new_buf = js_realloc(ctx, buf, new_size); + if (!new_buf) + return -1; + } + *pbuf = new_buf; + *psize = new_size; + return 0; +} + +/* 'c' is the first character. Return JS_ATOM_NULL in case of error */ +static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, + BOOL *pident_has_escape, int c, BOOL is_private) +{ + const uint8_t *p, *p_next; + char ident_buf[128], *buf; + size_t ident_size, ident_pos; + JSAtom atom = JS_ATOM_NULL; + + p = *pp; + buf = ident_buf; + ident_size = sizeof(ident_buf); + ident_pos = 0; + if (is_private) + buf[ident_pos++] = '#'; + for(;;) { + if (c < 0x80) { + buf[ident_pos++] = c; + } else { + ident_pos += utf8_encode((uint8_t*)buf + ident_pos, c); + } + c = *p; + p_next = p + 1; + if (c == '\\' && *p_next == 'u') { + c = lre_parse_escape(&p_next, TRUE); + *pident_has_escape = TRUE; + } else if (c >= 0x80) { + c = utf8_decode(p, &p_next); + /* no need to test for invalid UTF-8, 0xFFFD is not ident_next */ + } + if (!lre_js_is_ident_next(c)) + break; + p = p_next; + if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { + if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) + goto done; + } + } + /* buf is pure ASCII or UTF-8 encoded */ + atom = JS_NewAtomLen(s->ctx, buf, ident_pos); + done: + if (unlikely(buf != ident_buf)) + js_free(s->ctx, buf); + *pp = p; + return atom; +} + + +static __exception int next_token(JSParseState *s) +{ + const uint8_t *p, *p_next; + int c; + BOOL ident_has_escape; + JSAtom atom; + int flags, radix; + + if (js_check_stack_overflow(s->ctx->rt, 1000)) { + JS_ThrowStackOverflow(s->ctx); + return -1; + } + + free_token(s, &s->token); + + p = s->last_ptr = s->buf_ptr; + s->got_lf = FALSE; + s->last_line_num = s->token.line_num; + s->last_col_num = s->token.col_num; + redo: + s->token.line_num = s->line_num; + s->token.col_num = s->col_num; + s->token.ptr = p; + c = *p; + switch(c) { + case 0: + if (p >= s->buf_end) { + s->token.val = TOK_EOF; + } else { + goto def_token; + } + break; + case '`': + if (js_parse_template_part(s, p + 1)) + goto fail; + p = s->buf_ptr; + break; + case '\'': + case '\"': + if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p)) + goto fail; + break; + case '\r': /* accept DOS and MAC newline sequences */ + if (p[1] == '\n') { + p++; + } + /* fall thru */ + case '\n': + p++; + line_terminator: + s->eol = &p[-1]; + s->mark = p; + s->got_lf = TRUE; + s->line_num++; + goto redo; + case '\f': + case '\v': + case ' ': + case '\t': + s->mark = ++p; + goto redo; + case '/': + if (p[1] == '*') { + /* comment */ + p += 2; + for(;;) { + if (*p == '\0' && p >= s->buf_end) { + js_parse_error(s, "unexpected end of comment"); + goto fail; + } + if (p[0] == '*' && p[1] == '/') { + p += 2; + break; + } + if (*p == '\n') { + s->line_num++; + s->got_lf = TRUE; /* considered as LF for ASI */ + s->eol = p++; + s->mark = p; + } else if (*p == '\r') { + s->got_lf = TRUE; /* considered as LF for ASI */ + p++; + } else if (*p >= 0x80) { + c = utf8_decode(p, &p); + /* ignore invalid UTF-8 in comments */ + if (c == CP_LS || c == CP_PS) { + s->got_lf = TRUE; /* considered as LF for ASI */ + } + } else { + p++; + } + } + s->mark = p; + goto redo; + } else if (p[1] == '/') { + /* line comment */ + p += 2; + skip_line_comment: + for(;;) { + if (*p == '\0' && p >= s->buf_end) + break; + if (*p == '\r' || *p == '\n') + break; + if (*p >= 0x80) { + c = utf8_decode(p, &p); + /* ignore invalid UTF-8 in comments */ + /* LS or PS are considered as line terminator */ + if (c == CP_LS || c == CP_PS) { + break; + } + } else { + p++; + } + } + s->mark = p; + goto redo; + } else if (p[1] == '=') { + p += 2; + s->token.val = TOK_DIV_ASSIGN; + } else { + p++; + s->token.val = c; + } + break; + case '\\': + if (p[1] == 'u') { + const uint8_t *p1 = p + 1; + int c1 = lre_parse_escape(&p1, TRUE); + if (c1 >= 0 && lre_js_is_ident_first(c1)) { + c = c1; + p = p1; + ident_has_escape = TRUE; + goto has_ident; + } else { + /* XXX: syntax error? */ + } + } + goto def_token; + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '_': + case '$': + /* identifier */ + p++; + ident_has_escape = FALSE; + has_ident: + atom = parse_ident(s, &p, &ident_has_escape, c, FALSE); + if (atom == JS_ATOM_NULL) + goto fail; + s->token.u.ident.atom = atom; + s->token.u.ident.has_escape = ident_has_escape; + s->token.u.ident.is_reserved = FALSE; + // TODO(bnoordhuis) accept await when used in a function expression + // inside the static initializer block + if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT && + (atom == JS_ATOM_arguments || atom == JS_ATOM_await)) { + s->token.u.ident.is_reserved = TRUE; + s->token.val = TOK_IDENT; + } else if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD || + (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD && + (s->cur_func->js_mode & JS_MODE_STRICT)) || + (s->token.u.ident.atom == JS_ATOM_yield && + ((s->cur_func->func_kind & JS_FUNC_GENERATOR) || + (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && + !s->cur_func->in_function_body && s->cur_func->parent && + (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) || + (s->token.u.ident.atom == JS_ATOM_await && + (s->is_module || + (((s->cur_func->func_kind & JS_FUNC_ASYNC) || + (s->cur_func->func_type == JS_PARSE_FUNC_ARROW && + !s->cur_func->in_function_body && s->cur_func->parent && + (s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) { + if (ident_has_escape) { + s->token.u.ident.is_reserved = TRUE; + s->token.val = TOK_IDENT; + } else { + /* The keywords atoms are pre allocated */ + s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD; + } + } else { + s->token.val = TOK_IDENT; + } + break; + case '#': + /* private name */ + { + p++; + c = *p; + p_next = p + 1; + if (c == '\\' && *p_next == 'u') { + c = lre_parse_escape(&p_next, TRUE); + } else if (c >= 0x80) { + c = utf8_decode(p, &p_next); + if (p_next == p + 1) + goto invalid_utf8; + } + if (!lre_js_is_ident_first(c)) { + js_parse_error(s, "invalid first character of private name"); + goto fail; + } + p = p_next; + ident_has_escape = FALSE; /* not used */ + atom = parse_ident(s, &p, &ident_has_escape, c, TRUE); + if (atom == JS_ATOM_NULL) + goto fail; + s->token.u.ident.atom = atom; + s->token.val = TOK_PRIVATE_NAME; + } + break; + case '.': + if (p[1] == '.' && p[2] == '.') { + p += 3; + s->token.val = TOK_ELLIPSIS; + break; + } + if (p[1] >= '0' && p[1] <= '9') { + flags = ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_FLOAT; + radix = 10; + goto parse_number; + } + goto def_token; + case '0': + if (is_digit(p[1])) { /* handle legacy octal */ + if (s->cur_func->js_mode & JS_MODE_STRICT) { + js_parse_error(s, "Octal literals are not allowed in strict mode"); + goto fail; + } + /* Legacy octal: no separators, no suffix, no floats, + base 8 unless non octal digits are detected */ + flags = 0; + radix = 8; + while (is_digit(*p)) { + if (*p >= '8' && *p <= '9') + radix = 10; + p++; + } + p = s->token.ptr; + goto parse_number; + } + if (p[1] == '_') { + js_parse_error(s, "Numeric separator can not be used after leading 0"); + goto fail; + } + flags = ATOD_ACCEPT_HEX_PREFIX | ATOD_ACCEPT_BIN_OCT | + ATOD_ACCEPT_FLOAT | ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX; + radix = 10; + goto parse_number; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': + /* number */ + { + JSValue ret; + const char *p1; + + flags = ATOD_ACCEPT_FLOAT | ATOD_ACCEPT_UNDERSCORES | ATOD_ACCEPT_SUFFIX; + radix = 10; + parse_number: + p1 = (const char *)p; + ret = js_atof(s->ctx, p1, s->buf_end - p, &p1, radix, flags); + p = (const uint8_t *)p1; + if (JS_IsException(ret)) + goto fail; + /* reject `10instanceof Number` */ + if (JS_VALUE_IS_NAN(ret) || + lre_js_is_ident_next(utf8_decode(p, &p_next))) { + JS_FreeValue(s->ctx, ret); + js_parse_error(s, "invalid number literal"); + goto fail; + } + s->token.val = TOK_NUMBER; + s->token.u.num.val = ret; + } + break; + case '*': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_MUL_ASSIGN; + } else if (p[1] == '*') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_POW_ASSIGN; + } else { + p += 2; + s->token.val = TOK_POW; + } + } else { + goto def_token; + } + break; + case '%': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_MOD_ASSIGN; + } else { + goto def_token; + } + break; + case '+': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_PLUS_ASSIGN; + } else if (p[1] == '+') { + p += 2; + s->token.val = TOK_INC; + } else { + goto def_token; + } + break; + case '-': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_MINUS_ASSIGN; + } else if (p[1] == '-') { + if (s->allow_html_comments && p[2] == '>' && + (s->got_lf || s->last_ptr == s->buf_start)) { + /* Annex B: `-->` at beginning of line is an html comment end. + It extends to the end of the line. + */ + goto skip_line_comment; + } + p += 2; + s->token.val = TOK_DEC; + } else { + goto def_token; + } + break; + case '<': + if (p[1] == '=') { + p += 2; + s->token.val = TOK_LTE; + } else if (p[1] == '<') { + if (p[2] == '=') { + p += 3; + s->token.val = TOK_SHL_ASSIGN; + } else { + p += 2; + s->token.val = TOK_SHL; + } + } else if (s->allow_html_comments && + p[1] == '!' && p[2] == '-' && p[3] == '-') { + /* Annex B: handle `w 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

~3TyrZV^}D!_Z|LmCU+L_=60 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%0A-xm%#ZY=y!qf*LA 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≈){*#T%2ZZR3vm?09mxtgTIiL8l0Nn}ej-GiZ@6 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 (Vqxhq*bot|YpJ6TQkbIY*IZBRe@W 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

dWs3irT(}ZRauEd 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�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

(mdsFkQ3a`)Kw(bm}q4if2uf86Sq@E zmlx;AG$0A!Z?OQdB0PX3RPb7NA=!UZ7m;u7lx`A1xMix%z9ijHR4nkuOJmT?t%`xw zMG88@W|7n^YJ-~3zd+-YX(9%;=bulPUj^xV-3m~?vqD&iIhf_$0JBpdvfOj;ChN78zU5fKV-_^EYz&bO)cl+-EwY|*c zCb5+e0Uo&E08_X_dvmw40o#!TLu8(Kwt$cD+JBOs<6rJn0sUib@Ev77iuB|3id=Z4 zMx{SekRFUs3Q=Qpo)0xuiQ04XT%<1u+2SPo61|P;6&@b% z193Vgp$~&q%2F-*Ge5MY!C#fM!WwF$P4GK6-k&MUg(9xAbVp*rY^9H}Ds^t9Z1KYH zs^NG%4&dxKDF9v^0i1g$GslvXG^Y%3zfaCV+Gri+hlHngd>pV$waLB&nBvJK72e=; zl)&z%;v{zaXM*)g`eiV0!KYAq0ldOhf^l9LjnBi+$;&&5vl^t*DMpx6>H0`HV)0Ud zS8JK38JzerLo1YCSE|GbapGhp!fZJhkUa(RA;{Nfrn`v04Um6M@U62k;@xgMS@z7% ziztcM!zIpR#1^y#NtvLTs$dWNb12hsEM+7zd0mU?LP8R67M1B-M`U26K1SW+TFf zIdaOu^t^WYm>QdTOdGniw~?E@o9Y>rVomS=IWM70cya;JUSvcxz>&~Z0E~a>IOX3{ zc{jijul{Rpe}T)tnOMl+n8EK&35)$ZesBA2Zr1_Gw%vF`9Ygen-fSA3+it{@3$!qu zMP6Rx_8KNpP7tQ&rth7|jH*OhvPj1+p03PMrluKNu|u^9d2?x37ulkC0E&m$SFH2P z=`C>O-_f~vq?b7Xt=B(6#{vJXsz7dD^uAC3bs<$vAD^r zUk|(L(EVr)3DLD+N$w=jD@L1|lbrdHO2 zH0{4Oc7r^Wp9SJAqM=u?XPJJ?V2sa4Fo@5jy)MOp2ZZ&7ankOLjS~Bt5XBo@%xFiW z1awxQDC$r#VlM9mqq-s02l6Dz7PXcYF4r!Vu;&3jMp=DfQlmHX1q)b`v!}~{)l7w=_rKQoUe^!{&pgsRyx=ta~fmJAUMyx;lz?~?6O^tWRxlMo=pBlMk*59z= zPaAY7c&MmtgE-DBM1v50C%1ptFsJWYuv{|_6;!8LjZn9xOs?+JfD0~@ThqxQYx;zf ze{4zYh}VQdTH`82Bz~sX_2;L#a8bR|ZfpJB zmf{Lr01nZ$#G8smBN)h$X&ZIg)jMS%3zZUUF|lHNuzr9Gz~XgEE)u9qolNI%(ikQ` z>sGpx%$Dx~$;y6&>x@d4Z-4eo4T=9rXF#Tt>foGVjw2Es4?`$29=vNb^-bD4AX84D zad~0P+C$!kXdyC+-?HB9C(+S!DEBX|58m8$PS}>OV-z;?x;7IclaZlr9ROexDoJK{ z>K&jPLm01lmWSR57BKvuB=J)EzH58drk4%^(VP6&vBhi*4kGxbWOO{UqHqT-*9vZk z9}C6iaqH31f;Cnfq>C^_6W?3%CUs^YT%QWa`|Co?bV3T^9Gcz+E+CqLf$#0Bw+%|C z3{1Z@fA$guR??7agX3xCqKl(8JbxVM&YCGcUQR8THAGOMAzBG;d{g8DU-0e`&U!hX#T{>C(%f>F@Ib3;{zh`0V+jOIt?u}O z)&2hA&SxTe)8hqgY2KVYnTnxU=IdHsI|Y;*jGK%x($e4$?>HV>Y8N1b(;nz!9MXEn z=Vj8TvF9p;lY{}z3@f-+QHw;7%v@N;$qj6T17iRq#!uLrhgMzsy(}7vm4cFPUUAp^hb8wLy4Z z`){e4epDmiuu72%kxH7{3Kgf#-q5CbQkri1lCoNllRw%LX~abAzy&O0!=`NrHmfnj zHX$9)Jm}_oo4U{`4fp6L@ROd<2s?Bv!jy`7AJnhj2bGobTNhYVk>JrPN0}V-bn5tv zqoFqOF$Cz=c?DawO&>poIPdO)yvH@c8#|%tx%|qsmg~^{1@)d8*J($KtLy_C|6=c< zvAjuugX^t}`7_LGe~Ky+818Z9+jMx6%sx27M!7z?@p=c!-PCdejuUnqNJL^{X>kIr z!{{kGl~w3pTYepa8d@};87v0fAO@fVYo9u?m3zP)hR zeL6@l9!vvEYBWp3C6u0@rmdt1R{}} zMl3cNJNwG>L|1)ps)tp~exCatpl^-EeQO56$8SOQBco}2hKLeAM+M>kjMGeW@GJQa z&k`gR_h|TYWp>W?^RW!x9ngfAk{O))JE%U^-y-`N_TX&e!I4e4!GD^N{Qq7@3O+f^ ze3gk?0k$uy)>6yLMfCF(!OPrxkf16=yTjBk@s8wN2HLEF~|JRkS8a<49MS739YhQjfW%h2$OfOAj zPyW(^#A;)nQ$$80{SMbH^f(EUigpNz% z`-;Sc=b0hqb$ANzT;qq#4~tc6(JMKm-?inN?x$_pr(jJL3Jjt1U;QgGo#?w9r6b@-jk+`rrI0S^a=}5Z4IE-K;=#X zRb;ios5Xz5;%zP{raU2`5JoEq3q{2`38vIWxUt%GR@(%9BBa`BI>B*ojSlbLrO()g z>87sg6)6h*k#01n`}iyLn7o6sIi+8(Gx6n3__DF=c8Fuw5;-Pl;U_Z?f^~T9w=zvO z@46^VXH{mTd~Znbcb6^dP}U{B!F#Ofp?fI#4WMKiXyJQVe!U;fPN0ku1gezX7|9Yd3Pu^BLrIlw-ABD8o0TGMc z_HGNG0YAV?=+wTE^F?ctcd0AHA2{q-EAQqy8o{LIbg@7 z?%JJh9NdM&3OdMa@Y-F}X=d3M~>3 zD_Nfv_9Nn1LSVB=i*e;ivuib zN9iglj8nDgqT5s1YTZLEosl=|{JJh|Gx57#BP0I&zhdo&y;6Imt!BKJ{(qbYrn2r$ z`ouZ;Z4`TIuxdZ(KN2M3oh~P1r0vZ>Cr1jOs{Su&Zvr1zRsR1cw9o*B8>C{us!@X9 zl*MnVt)#M;fdnQx!LXGA1%j;*5iv;8hOJG_r0qCNTBKk_l&UBwh$t0WL2OIug2=uJ zd}*m*&uxH~f~AxtzxU@k_s)`{-{1fL%j?z7%(>^BdzNQE&+{Cka^^g|&P-rN@Z1;J zrlO#mhnSBY4IaTvfEDUTgmFVEm#*otMPKLMhuC4hp@OZ#i?lM1H`+5@B?8bMP@%OJs-Rf2TP=QR@xS9 zG2NS0pCboyMQs-awLOX;B?{OyP>X~vKyFfOvv>dVCvW4?ku*pUq`ymtCgdb}f02W) zk`5as*ptBLP>zN%{}XlG;NE(Y;xlZ_+FP~*sYR=I*Eb^gVsVq36_ z5Vb#5fPhA@^De(+-Tvoe-`}b+2BW=@uI%XXW89;7xp`1glxMOtT8%3Qvu`$%#ugW! zfCcl2ek)KqooIbjBfiW^8L!=&{b!@dCqZeYCu?4SiClofCv^H1>&KKE!bq@tgeT^d zK4d-aR(TUW`SU<`vwrVmzjc1rz(4E6zc^Hu|5!e>=Qh7mLF{fVM10svKKW9Pr)4sh zWkU;Zu~WVt3d{2EvpI&B*sGvJRt|(L(X2HdGirTLKX$XKQ3%b9y=_OsFImHQT{gHU zA7@cs)_p$r`U2jB}7S9?~=ehm)I|sP7#F2K91pVCug_+;oaO^oO!nQUF6pbSOH_O za;Pr#k&t~n(g_9<&=EPZn^JtXSBm8y!MUX=#UFB)BOa*ZBYE6O85b4AUJ`R$rBK3> zlq_<$iQwI+cj-{RN#)3FJ#d$#kqX4ArSkxtmdr!R47WGx_Lm1N>GWRj;h0=>vz}tQ zP+L=3-SIfNrXuK543-iQ+hq2$$@HzGESWvd6a_+bE|;J~8f=-WajQj7z<{5G>&&H| z*B-F_x4JUI_%+_M&q4OgzXHFroz$JD)sSGxpl<*G0~XYbT#f*_oy-^T*=Xk~Dizq% zyu0}+=RX@(iV3?_j}* ze<$c`uMj=D*E$s2_b$z_ub9Ni6!?8m@j+n{jq~!+o$aM z;>H8{{M?#LzAwQ;3#31N*PKAnvMBikqw*sP?mlj}-tD_`kC?5-@F2a_zA*StOXS-A z=^!l@n#$c2&48p4q=qoz3#fO0B{heM4u6%@Y;vzVpMVR^4pUg))g&axj0~Y|A#p7c zR>KGyf|GD77PDvfBG)hz1FNOvuD#4l!R-=h+hSoSmM8695A4OzuB?A2NH?-~>ZXnN zb&H40If?O$g13vv&>W$1*I~CEC=2O%6Kl)dmsonq$@OMjv+nk%J4>}ioqxP)))jXN z*)~;e5}h~bp&f_v=VNo)BzF>3_qe*5xQY3SWBa_!UciR** z==VJtLEe35Qjtp|+$GbPPw%}}u`K9+njE7ZG9xC~;AY*%!bD)<3a z?0sig7l~#DO?;Kyjdj61SBNU%UF%#QqL`rdCjw${QZH*(?kSMBdBUn7cNs&x?@$fl z;4b1=bnNf>S#~0zeH^Zzk(VQ~`68+bVuW)>Hj14x{(%il*?*r7iHA}AEy>(i6=HT6 zWRMschb!oF&4a#q3b*&zN#KNN=E5XmY#3}Lq#{2EAMYpwEeXX%MFg_im&(~He zChY@84@lT#ajgkNpk#z%JApBRx@85{Q#W!x#}+am3PM`VH}}zL`=_GDiR>O^C`e?? zSo>*u2c~SDYi%1uxIvE;fg|cn%hDi@L-z<U`w1wNOaz|2T>#pm4-3O$$ z)h~53Nt5hRX;sv&-LUNSRMvNIAZZhL&ixBGZ$!q!sg=dkhT6G_-(M&M(h>*)c=CVp z2oJA8?r^P-CL7FFSXFT{$$1~Az)QkfC%0fVOnZbuvBo6omE70Q#{{~+v;)*aE~NkbkhFE@|7 zxjs%%IbW|X?V#D$z&1V9y6`ccF!>5Z9O7CagOA8Ph?I(-m_fU>>A6gxl&84**iHg180!b*np+!np5nI1Tds zNx$WAOw3}0RpMK?j^61DEO0;gJV7NZ*s09$choG+WS9=)yM<4dcPFsi8T<^u!h1|M z_3aB*5!}#Lg;T8SM&Wi--P;Yr*|3>G->s}A1+aZ{T=l*~4gBbk0ApfHeQ((=vDm?9 z2M-CLGpPco*Ed?HYkC-J_zc~}I@(7@Cv-x9eVM+yv-{lN3ow5 zJl~V}D_i}P(fjr(0g5X?(EG0Bm9CZ)xkZAXS!M;Xm4DOw31-x(S3dPk|Fh8R__e+7 z!^DXVL{G${{lkcw5(4Z*)RfWMQ>{Dv<5Hqlczm;eTuRgmk3Z=jL(~AKR86~4w8G;m z|5*HE3Ym{fLyPCNQslSK!1tu(?BJ1OPGhp-uFS#Bo5|>c!7Tco!+Me`R-@mDlk{CJ-GY0v&OjZSyae#EH|B+ z@f-C1nAPOjlz)>PGKS*Wh0B=b+WZ4kNFH_*Rr+{bDy3(WJ4R)rqB7CLu__oFSCU92 z9Q?RSWzF($iZFIpl^#oJet1X?p5H1|APu7UN#6$1jU|d`(rXv_Nrz`sD{fD#cx`<* z;U7VSVt`JYU+eGl1ICfg+-fUa_g_33SA<9V5nX}Mmj~hevKFmqEC}qpvUveuCte@c zFd+Kq{@*$uDsB9Smqbzqhai57Jpqol+m8Oi--@`C1>@=X6n*W!1!*VTi>C~#qj*r^ zJV4NWWG=RfIG)lnFyd-~*UYR}bodIN=gr5$8#_>H zP7)3D7mG*g1Uo5HqR6`w>(LVZ-Z`c-ncFGsd$O2$bl~46B0o?Da&U$v#zTUg8%XPi zR)KmXouJB$dg*60>l|V0jhU@vCW>^-tI4@8#)#Y_Jq2}C8G7!a15FJ2P?N~4NX*(G z8RYofxX(!4SKg6dn^2NIiB9PcIj34^bgZ294!6ZFc!w*|v zz6&_@r}Ag{^b4P^aeQ}+mdYxkwF)0$NRd1NvT238Nuz^Bvb&m_8H5+z>IOzwJM^A) z^OzGjp*O$o9{Q7fKN@1&v_y4bU6F0MayY}8wNgl0VqT8qk=*#FA2a`e6#-ec{^G&f zed97Sqhw|y_YC~P?3U=;!QBb769?&|n3x%6`u5~oGMg}?CJ@lZRCK{`8kcYLAZgb; zn9o$dM68*{a|x82rj>v!5T_Ns1Me&=@5iSB3psWKJW;fUIJGn@k5ckYVU_G)-`3e${VmxvB-=+mE+*qPh#NX>=#AUu-eU;7u6t| zq3(1?etFyNLc&YkxiD0D+d+pmP>E+l2Wg2LJXDq*O6Vq6_4?{Bu?|hM1?!=xWakYO zv_c*P?y_b0|Gtv@#4Gus1KfkB5q7xnHMTjwq=qjy+|81n%wFeOC3)Rl zq~guR@RV<6&ri^EZD$_d6dN5K0$tXf%ne^%bg&@w2wyPBgFryohc1w<3lG|uLs_^h z(&Bt$074%zWD6>Zw^e&;yKauOrQ^EmF2*IYmoK(i-l*br6uYLLD4zUO%XFQ2CCc>l z-Tz8v_r75G*vAK_)hS_BWA+5Z*W)VOTg1VM?5~aT@Nq@X__kL>IKnXpTCz5eA0_@N z$~&0K{1x=BBI}WO9RT|gCU;)%jNJ|i`bO|Il|8R6vASF{wkt0c)tbo40Sr6WT@(j% zM7lV9)~yh8R`R`tEN6D%yP{8~s36B77!pI*9`GMxM_!>!nf}`?wND3G^p~MmwYy{{ zxrLmnB9K_Rc%EQoN9$JtW+=MErD1RvnQ1HBODvSi>5Nh84Kmk06Qmm;5ZdQ#J3ak2f#aqWGrXl~pCgmY`u;*yW?J5Psm-@?O(?<{pKP?8Yqs!EDj9xv zcRIf34M;Qh6hF@{76V~ix9TC5@`N44r!v1#!zg!j1Fys2v49nc>=(s*F#Eqi9INn2 zzC`wy-1u|fGrn@c?lPmwM<_~K)I-0#5Breqn-(jrb8*CaM1zOjkCsBnS@+?p#x7Qv z`&IN_8=!FzrEf~nq&fS;c#7mmxw~YFLBGgjOU6oDGQU$r+jA|png3Y8ZPSm@s}r~n z4iD02s#`{}Z_y~i<0$m=(!kh$_DlbVhw;GpIM)QzN0jb$=AAl^mv|~T8gr-A3Famk z8dw;uj|v2A%+K8nosVZM-oiQSj__HCC|;Ppk5~Sn15Vn9|E(o1*e_yyi;|oC>%I<6 zkx-T=y+b>wm&}Z;k!(KF{dc{Tr;#HcH_64EP0_uI-PJG>-6Ivd$_|C;9_!yaao3{qgFc=0g2X+ub-5D; zGZlSnXC#*%q7LoARM42)4pX8Y_VvN8-zGEdbptos`u1K~HeB!RoXxOOnbqzRk=}0= z;7#@+#L0Vqk6*-ZhOSR@QT~^I-8Yw4mM)Q&r@~b#?16cnwYs4~W7jFRr4CEx8ldG} zmB=Wp3o$I+jFniQ9x6r4R*9(INz~?%pYe+&k*=b1?CK^d1280w?saz`HiLh+T%LYv z5!xaAT73I4oELPjwezAVWh;*vob*nAdFJt?o*5kH2SdZ0(^+=r7 zl36X!jfbOLIL(>2ID@eKu(jDaYd{R06tp#pe9G!#MTjT;-{n!Fca6f%pyHNvH#dIV zikdQAFScuCkQSLHVxY0c%ZPm$ev1m%ZEM&cqV*da`wlxJM9MTW%b}H)zOvEKVE(pu z37G`^l{5tiz!8QD_-+L0YW3ZUZ4760Zc0hORtOT^tkZI#P^~-VEzh5HUf>D!lf(=0 zO7WcZZ2MAbmQwsL^cJarJ98c_P+C&=H%2duUUG7r_ExWRo8I(qQrnVNJ-kHc2!R|~ ztYhxPE7SfPay+&XX^jhg4X&3xRrd`+ z+{lE;O1sqU-d(vOyhAk7o||3U{Glt{p*%N|X6g96{XpVA0SH2uZ$spGRaJhzF+Gct zw;Nnm!R*D}P>Fqjky4^njD5bD_{?UmtLuGdml?-5lZCn$7MT{Du?yG#MfMHdN*I#9 zRMtM)=N)dY^^0E2ggsquk%!AaDRufg z7L~CrajNzN(n*-jX15R#&#S4xF7P-C3!ys@Yxn6S0s`F5HYbGWecA5n#J1yit?bH% zc8l+=+*LY+zH7Y_85G(#h?e9G?@{+f-eAjyKNe4vyIUs6Q%=qNjaue60^u*R#eh^} zm&L82v2bKgv!B#OBD-}0U!wG`V-yPdlr02gcefKc)Okos@PM-ttIgW^weOmt7Z-pc zk_QPxCP?{I`F;BUV|g4y&l3tS?%F?f#JHL(?_gUx-}3GSr;wagbjsuvc^5)!63Xv4 zW3LE?b@b9;l+!5w2KUmfkg^T#`&}$h-v6-xFU^_v6Q|_!7xundTQdDB5+P;QXVye6 z3d3t8fE$%}O95P44yAbLJbUbG#O%)8ci>ddcSv*7dw*qO!mFK)J4N8i6-q}xN;1^D zR5QKw5wioAOlWL8q1{c+VoA3O9NC@-9J{K8ZADA!Z?>~TrB{E$r0;Xrc1H5L_YbnU ziori2*wA&}*^{7pgI(R80Qb6&qMh$C{hJAHl8rL@gV}Dzi?VdV?QN=uCvsoo8lz(& zwZl568CGw0w}O$tD_#4U6otynxXi;Zqpc_!t*sMFfDd2qpV-~p_&I!lcpZagDvAY! zq9%p?YYOg8)$FDo>0#`)GtIyItT?rEc;$B^`O4pOhT^)_{r%T)e##Ag3i}WAM|^Cj zNP6*rz8^sE}IWs70%uZ`aqCHz$aj*!q;6wu!2{dzOxd7WuJM29RqYnC} ziV^i*Sv9N?c0aoFi)uMqn?Us{tKL+Zm`Dc@0-pxcO`x&lC+-cm*U>;kTM4wVIg4Us z2s)Uum&D}NfMAeTY;AD&vAZR6d%f_-8b&DpQ1{W~LN4Mep9|bdFevrKdW$ASJ-U}i zs=NEGyF1j~T@b(}e9@eEW#b`;fl%=J`y|md2FW#vCtgWrM%5&9L^d3oYdi|-IT_bW zSVIE|Vq}1XMM>ur@fG$Y)tjDU9z&TmwN<4FD}h)pty>< zeNKF8coWx|@69HlW(>4M`iRV`2tseWGxo75rehL_DI+D3+2%mZK*xU z=R1DPT{T*FP%QV+kMP}9yvyESZUlh`8kpws19_un9?*8^LM!G?_KU%;X;8>hYIe&V z!v;Vh&kycy*-hwp-h{`xzBDlHBvzgd5=+Y(NW8;fti!k6xZ5Knp6dXzgeA#8v?l16 z5lhXfnaHJf1%l&s{Q1w#F|g~mp&7bxa0Znb{sny4@LlT;;*4lJup+qov1Sy8IuL%~ z_cdhq38?=?g#9Xoj;YGQP(0BUj8JBo|(E^>1MvRkZc~>2y2sVS0325vzrqq3U7ol z(rl~To)g}aN1IYqutP!L5$Z++R*!2tzo1UI?lU5=N@ob5oLHc)f31HHPbuxP@{p*L z+SDQV!y2t)^ML9W;=7l~W0i&NQC)ENCMgJPL*_KT zL1y-&9dLfkZV7z%`YQO^HnKEGlS08T z9W3ZOMSb^NKD?}}rFqJ*)C3n?^>-^`!wapuzSKNJ8=@o9^J4#xn?zO*u zxC3nv9k1^e+JYLB{t&Z(@oyK8!g8J*ROHS@78kRhFpUS@+MgK_^8LlyH7_fsL&q}K ze?y1HZvcl_2&nyEw!tY@8hvUs4b>gBJ z4>B>a1n?>|Yl0%VkP9|i)UWNFc#Y8Mw=_Q_{M1Z@pAz!cyKH1(v5~Cs^^U-BBZcsNwE7)t* zq%)G)adbj&G^QI}zh*RUCh#irbU@C21Ko>+!n!L^5}un(epwNl;k=B(Y6`I+Y<7)N z`K$B;iNY#*s&d#xy3oGK%&|y_O|=wP7m81=jOKG}l_WZnq7+?z!#&~jW%goI!RqrW z7!L3}0g@VGwpzGP&CeZGCsK4ayG=ikX_c(4Hu1ug`xKGjElhDzv>D`s#(a$SQi$w2 zI>IQsSMk#Q2RYHN<+7vs_gfN#0TAX znDsK~u6eUSsg@Pf4`^|oqs*M+9t!`{Zf59 zUd`7xC6Rp0<)**mSStBYA@L7ORr?$*J$;Iy$frPW07qt4xgZ|HKSW!8|BYB4G4GOC zo2x4!8(6r2hE61<@w@*9*>B_YM1tu)|pS8yopF>D$FYms*5V^d{)&}d8YPn-XbPhM9K z>l*nX8`ZVPw&So3_wC9OOTzWJdGUf#Zr&|*THdG8dJV5Sc|KCPopj=c#5;2AYmK?~ z!GTd`2g)^>A*Ol4Yn_){c-fF_XvTmgyR*X|w3juACTw)?v!v3!Fa3)FYN^gFvQQ*1 z2!vvU(+L|OR3C#7H&J*vJ}C0M|@4lx5Z+b9&Z=8>T3x; z(z-oHDG_ye)F2tn<`nvpxZRMSki}?za|M&iE%yGm z8lGvtCm$+%BAs4%#_8&hPU$J=JCjc3$JAX=QJ$xJZ38iQ zwIM9kU|0;TE*kSDgI@uOJAjZsL2lut$UAd~+iRhb2m5x`o%NK=-mwS?kekzP;lq9P zzvbl>kC(+Ux#NM7yjkoe`=pieJuu65cMO*oT0_l~qY2HoO^mQTNPo=UE(KW{Z7S0Z zC+|R&;?yiy^=4FBovyAe$fT1YOG;8cd38fA-^wx_NT7pV zoD(GKaEYlfr(%><@^H&U$Jv=B;+AhHj|r1NX_-vq_^|k~$GX?^Y`eH1ZI7DzgEf5)%vL~CO^lN?r`%&CSX74SGLZ?M>6Al-n&jnAPh{NhuYpcZjr#XYP9}9$AFzue znZ&p11cbHjdsGJik`V?23cwz?!?SQ1T(DTrB5vb3`T-Kr@advfT~xR)`!4DQNOiQX zRqKlIdRoW&uEya-#mtK|rXiD}hYGigZ%i9XXj~CqM!UE^_`8a#74G>T*f1+XllmN#WyPCFvdKyNuX)kJu3zI&W2{mZ(^|WYa1%Y8o1}hh6i;+9{YsL4KhrkVGVi6 z*%)nm*sh}X+P$!YC-MgT6+dG18fpC8&C3>&u1_?J0MZ%9n>mA{18r<-8ySfI# zi}*EmFii51A22?fA$brfA6R0mmf;9e3;XM<#mcmx#9ihWv=!^Sf1b%d@8tRFhHAzt zB%7?ykSuhAZof0pSq#2D?7U$Tem6z@6d02EUvk@~U6Pq`PzjRr{Q~6SjIx4^zqw=0 zA$rcU^l$Q=L^~PpH@ma2hN)>4SXl*M0Tjr$PE)0^fzSbriCOPGLc<^}4G-3o<cL}`UY z8O$l;q22p~9$IQv`Lmvsns*~OP+~t_V8weP2L7Duxd)3Z$q4d?Z9)1rQ^GIbg zG*9u30(_bL%t95-jtNzm@+Ks1b=MGGkKHi*rXe`%BfG0+@SCs6b@kFe7#XZCI$4bG ziBWsEV4eixyx(keHB*BOw5v@1c^Ts2WA=L(Bl}b=RR{B4jd0U_m5wZfNK7BJdC2R( zDs3_II>Ds-1hogU;8`4Q-OtN3>fx757RDfHX8NVtd6=8F7N)8if(i&rpGL#3%ROg> zZE~xteNn-pevf6b=QdeJS0v*H^M*ykjof>$>u1nPl;)0{RASK4c`~x1KVM`Es)Zsj zTUd)J;ooM%m1A|R(yJD-tj?wwDIt9VpW68?5T2z+Xrye+W;bUk8;@(Q9X(=xk;rex zn_H+N9(ww=25AYuvldI09fD@0v6LX_lf`dv$IwakN;-e~jMJ5S&|iYl$Q$trmjV%_ zzrzo{;Q{ynUGS3g=9^Vl<`SLOv-8|`L?ya^2Tcw@&=MkXzw*K$2Yh~fG5^dTNqhb& znaF3?%O6kPkNrc2gw5_1WlYx!=p3$j0^-g%;F7y;+f&(w4)eIhVPdm;|4uU>rT61$ z(aGve>_zgcz@o6hy>nHOJ=4q}dY&LVEd*-KJL@OFdp1&XkVIU(X#z{Lw_D0nR z4l5X%-BqGVM69~d*!XY${j#pPF2<0|lKD*fU;I4MKThXa_V=wF7%{4b+Px8xntxrG+|^mt@;2^;*_hn>6C-x`J8^ODd0ERiyIVNI5Ex##W;8q$yB? zku@bzWHbL52R79y-09FP^Z$9#{7Krrw=4v0b{s-}J_T1m>w+pX(QIFl;X7kR6eu{OR(ox2wIcq2U~&P<#I zH1sL@Nqu(cw8XA36whnaoLVs61j#K01;a8NpkV0y8U^F=#1g5R9$a;u0pid)r8s!Z z{TcC3vV|(kcWY|=A1yG`>0NHj-Ujt;nnl3SNu{0 zNobRW8TDH5TKCjV@A*@xOMmL=xShF&Yax;TcaxIii#{cnuo?V)D!*K~q)PVg<{_xR zJec=qzUTyU{k0qti7x@Fk3!jTTw{nh5V`B{@cWQu^2A%QAwxnB@1;m!Ws>a<_sm={ z@D|Ese>KlS(T(Ml3Yf{e$gsn&YwSEnw`v}v!n3NlP0?Bh&vIf0#N5l&ecXqjMPyB`-LY?6z|i0LT=n1&0|(2 zC#>lFPDAk1Ri-cs`st`4xNBRk^NYB+46jI*KgJqOY#Fnh9dXmHQo?NM#chZR2m8BR zO}@uT2J~&2`4lVLNDz~3R6MrKB!cDomdpn&m@A}o z4^%eijw^4OK=!L!fK(VfGR`i#6`P~Q6gQ!#ve2J6p}!w{{N7ag8}4S;a1#wLZia^zyLpe!gh8YqJf*!NBA}s&$mFbhGwJSf%KQD6l(e z*XOJ<1bP>&lV1Uwh!Wkr{2#+p!_GpxVMkM$_ggZrwtz)UsCHmXB;O^$!RFqT<;e*b z3J>3Ae4u7!@`wvDQlxcS*pmGk4(TI`rpK;Wx=t2$tL->@=RR^vI&RI@WuACljD(5C`epS5tuD z+aZhoXr+ix&ZxHSGCT-5wuREoIAkSV zMv@r~Y%Fpg+$ml&^EbQRm+MBL0madpkO}zfPm}{Err&iEIZaO6!G2l_P7`-FLd)C} z|MD3+jufRwQujH%|3F13<=js~$&>^XIWQ#*w^Gut++P>T-h-zExIEXw*ej2hb~u zR5h$cep_1Z$`n`8r?R!=jj%N5x|cgLH@>m-NeH)TD*&jh$B(dYc|)RQwZ%uE?{vE2 zaQ##hf_Hc!c*mhZdY0x6A^0?13R06ORyftFqy0W^(n()GFG${}dE3kx4>@c`)g~uw z4d$Ih^RdsTa>U6XNzU9q)IFHktM13{jT0i5d?tf_-GhmjuAM*$jnw(+vyI=4_xw(4 z1iu?0Dn|cpB9-k0g6nPt-_8a%F2zk8Zf^*mHx1=a1%m!-7%JsQZkyhimyJsA0 z>W{@M(Ndrk>bNdk$S&J^?2N`tU)8dVVfvbROFlRWN$Ewm%lClr1s=fML0(SI+(!Fw z5|hsJe}LW$D3FqE?%`Q>5z&$Gywj*O@FVOY@v@w>nf~s9m0kTK9bs?Og*rf=d+uqN z=N{B9LU27$4$%pZPWxsN7Gg_>R{`DvifuzU6_J>rI{}<0S zu-QDsM^8;fPSP?-K@XmHYj0tMz+(f13GE4)8O=x$lRZ@)D(itfhBlg5N6+oSTHzIw zo+>Gm-ITXug#vQ#^pg83-|Qb*3Kx#EBUI7w!_}8%bDNK{&I=TYb}{4 z2Y%s&XbvR@7Djj_sgO0_T5wY7VZP8hm7A&K*OW)@aScV~vK&^pt9YoztOy?l zc->*i5_3RlyuUw@o%O1t{}ScwSqcj0M;Bkk`BCQ+QXp@zAn5$u$l|25T(efHhZ1E9 z(TMY-r}O7WoGKz(xf4F?*%6uq`)#b~5OJ3HBnb!o{7KT!fA3F{>Q%SF>Yj!Uktgn@ zXGvS#)?4ytNzZV%2XqD3dNj&LqPpp!U6&i2aklpkalAB}jDm!Fx^=qn1^OQQc;DKt z{lg4TWBg3^t|*`ISkS)`t+D5d%Brre*VGvyJjq-u5g4iR{Rb-Td|?GN{W0RrXx#Iq zOS>Rj(KUeui;oXF*R6u0RXUDZ!>2!vQc0`X<3v_k(KS&r+$SCqSDvc786m_<+c-IK zwynj-rN(Bv;>%Gt>j3sUWPZ^7AZ@Bm7l47jRa=9PSc9(;T>*w1W%XF-GpJv zz56S+J?r&KGppHY+bfmGj3kj@9(~ucvn(kP?njAkaV4RWQO?24qddEivY<~o5XjDj z2#|KzQucU1QEu+JGVo_mS2A0}(^T$~^2Q@NS%KbTZDANb;kB7JIZ8HJ7+a?em|-d@ z+~$ey>Pz1nY~ps4Uatq~@(9V0ob!RHMRv_#or;EV+|Rc-9^ z;8UKuw=r@(8mCXGbCcpPNeZl_K_x!%zG5!#jVn~g73_G1>HC8%O<&xuR@@zK7xaH0 z3hbF-kR7JmY%M3w4ZgI^`>)3-rG2g(YLBy1GQ=W(X&WI<{^;qKG>h*S!Fie!DZ3k2 ze$|rds*SaVS;TB6(+;#T_a2Q@3P*0&Ij0>ANWpUyTDxu1fn{#oL7uX3Y7EYE^|Sz} zPidaCEbI{da#*+1qJ?2$U9DE;{{P!m4R5C@In2|iGrX~niwra%yLx(YFQV#sy;rnC zFkUSs7|8<$K31?FQGXYs8wb+KdA$&h*Fw&Z^YVcQ15AY*-0!;q1E1TP_sG5{F{e>y z2wJX6H=hcPcD{8ABlG;s*Moo}lBsH?yCAAn88%WY4`!SE(^USca^=Y0;Lo?9p{sn2 zgYz|xpoSrj9|ztKo88Fo%kI4l&C4)&f7?)9ENU9S=Dh6RrXg3Qau=7o@H%kSZpK3= zXQMz<(HwO0AbmW`7~`$^fnz;p&wb7`9sJLwCSZD@>kYi{BPS50hP;JO?x2;0A@&B) z6@(Cye+`^gy#6vRY;MVQard4Vv$d>#cr=#MWTh!iywpgIf@A-U((9 z@Y~+1y{I-UaX*^ox4k;V#URELpuSD;Y`s|*;UHi&UxHtIc$8_U78iMs(FG$1q1;a* zJoELALwWXaM6wNS3Ka&!ZM4qwM`XF3>G-e6&X}j;o!?E!7X&N5_JkSQaBN`FO*W0i z;~YYx!f!I{qWv;`0RWH`*a#=pr`zvTj!LKaAK^F9pcD6K^5IW_(0H{=Ud+;m#1o)o zV@HT|kT(I2eVi%6Jfu<(XT!R;yU)&1)F$89PY>-uTA8u*bZRN6U*-Kk z3NW?f8n5fB{nQ|%gQrmUEJF6C3Y!=jcDv&q9hhr_%MYrbRd;6{{TBLS@e0;6)*=Hz ztWTK#KSG#~i}r~dbSr7;!6s33?u-Y$Xm4?r*{`yh`x2OqXYR?BxrEN|rjvz%GZ^!4 zl-@DQ0NhURIF>l#L>(}$Mt1M~mTrjkfs>qsP<-B<6Ix*#9C zNoO!!Vl0I)?#b^Xw^}r%8OsqdM|$zO1AbW?OBcasq{9w87X2Rb8s)lhl}mhNuYcXZ z)uPWA>;i-K!;un*ac8sDefe%*cc90;bEF*(r-20TABV6f>5SuE^M+DvmC1C?q@f72 z?B9p1f3bhocBn&%eC;S0yDT$G{wAGA!NT^Qg^0b+8ZTn&X_1J1brsZR)=G6!B4YdT zDFGJ|aB#h`+r~al)_`Q@-kS9cRuWf(rQiZ}w`lnMsjybLjJEsVW$uNuM5WS}&`A1M zde1#I(Jzww-tL6ioV2MUtI!y-#VAz$QGFuy&F7Qup5;S->c7s&vYtmY!yfQ`k-qTf zk{I+%I0bHuOB~Me1C+2pX~8 z2z$L?<4*=+A*WsM9r&{3bP%yYbhFkU(BHfNdxJE*N)`Ci1llDV%2zwexobo0WDpmW znFAY~6yeT}GCcsf={IN*wX{s2&hq>Q=?hphPc?X3wP3t~EJdmY9iha!ZK4v22t=*g z=kt*=f~In$j)H=@x3e%(N`iDJ3o5mFAn!-v<&3wWbJ9!BPh_6%+QbMiV&Dvqb5@x% zafvWy^wf{@%;e*@3+_kKd)x{DUORtgWD)dO4aacmQ^094*pG3;_iOIU@F3IKNUo$%)W3fXp~(MxVyze#L=`so36y zesAUBFadG$zM}f$YM?gXD1B2O?V>li>oN0jEXJ#Bkp}~*5rbWOp4Z#G+eot5+i%$J z*BEc!mpYi&z)#6L?)dM(QF#mSlFv^fb1Wxv=^F1nfk=zxhx-a!WT5&7hd{BsrSo-> zJ|k@A!LX3yd-Fs0T207S8arIZ?!t0}JRFAfJ;rBp$^wsDVnzHuVnNN5d|c73upm1l z!C~XE?Mc91sMwfm+~re=-u0WCr##vSWVI%jZy26LEve(C8urQMq!BV24g%D=Zy9S6 zq#rTRjHlw?lz;PbH#||ktPv2=n{&sMTc71yNv9&X5$2~dd!#a7uOM$V&}YV)p)O=i zyd0l#!!dTny^_nr-CM%9TGs9b+3 z4jU$78%9Q-%TH4fF=^YOd!4jRf)0ZIMrNV=|AyS3*rH-tv6U+f$&*%Qg7b}z_fK2R zrLo8~7xW7rX_vHiBr-(4_U+7P^UMAzmtk6pw6@Zx6a3S9ui`R7%WDwyCny&o^x9XL zApPfg^SPN|e+0{&aqdh&O||Q|j;bQqXKy|xRR;TR_LXs`=7g7mUGf73?#k zE<6)q-s@kn4LpUb@@*H$zQ;GaKk&Ty>_@%!eaMCvSYh#=d)T1Kkqlh`_x}QT+EZ~! zbtSN9=hMDa$0Z0Z2-9}vo{ul-xfEktM&5riu|QCvf7F7o=)<)%BZf`6*M1mWyKhY^ zSk%MmT9X*w4LQOyk$<7nFF3<1RjQL{KM!vw z=tkynBlP4}p2YS82%p}?qgT)8(e0)0H#HXW?jF%Y`5+YZy?;l~41$Yc`~MY~F*=pP zEn)MFpM{kRnEv?s9v`;l&%LE-LwB<`L>w_s<^ea%rTuS8n80JeEwI zmnG=;4)xEU@MtK_#}ma5tU&x}vBo!GO=F`n>Aq6P{)^NNG+AAW9NVklllUkkhd~wq zkres%lG#dgijduC&D$_905_Q#Sp)5z`<##fCk7+2t)(RP`r+!fv!*g)?VH>lv-7O| z9IUPy&@Fnm@dJ}d_?9$5tYQzzhce|(v>HS!sIThClv{VT`1)c?x>{iE2fT|k7nyQj z(s2WmTV#`4VEjEcM~kWF&SK~qff#?346}p}yunfqwVF?O550>v3+WV9Q8XIuG($VZ zqyCLjI0t8dd+N=anr`^PoFzR{h!CC4M!Tv54Ad95FElQ;YGu^O6E)7+SUjKgclw!; zMI1O%$`+zZQQtPZs=&-eyuR}!y$EBPy~$h8I8_t&-HAJbnWp}`DR-LP5#Kl=qMNhl z@IKa*nxM9NJTGllIeP56PN%s^jRKj_gA3E@p#`>!)|ia4ZwiKyi$yXVVYJHApCpHd znV%yliTrDo^=DDWo-Rvl4p)O63G;}Z&$N4}VYf~ofdWkFnujRJGy0z)1T$6v#cawM zZ6kV@swkMoz(Z#KQS&qd(hfWtYiB4tTx}xM>Nw-{My?W28*Y>VKN};h(DF7OjBU-5 zPws9;HnZ);^k}oYBCECbcfo96TEHp{gHz(@Dk6s4CYWL|+5v-ZT*J~e$!OlX#jDz$ z5?LS+QbdKIWWLShs+wB+eO1jOe(SiWRljLeXXQ8uD)a56sIZ<-UaLhFBDY7y4fdI? z>E_d?x$hVvEBzP&K#bIe!fsb^bq-^#Wb03<>4qNd6yb3yEi@b8``Z;7vVU!IiYF|R zf&0z$+Up4kywQJ=NvdOl*8+lh3uHlyfXIUt205Fnz$=mc8fk2As#(wE*&b0Wh&;#a zUek3ak^z%nF7q`m#t`gfZ}NuUoR^cmM$L=Wqoz`+l8y*kB|?`#=}kDW#MbJhxYn$5 z;U=vNQKcSL-M3W+LD8u|rO2vO-q#X;(O1hK*=X+((oK0#%J7H=jaq0f$mHFXY(rD- zvU2zNp7@BV5k6TN%oSHy+RUw*BT@yk!yN8G~jOlDl*tM#C9=VTmA&)Y8Y$Cha+ zLAdP3@^>X5ceG?)kfoy)k8X`KIq)TN?rbAzZjio;0c4LIPQ*}Vh1a8- z}IimP+B_})_Ts1-q1B@nQxOlrM^N!L3ntkSV*S==EoO2Z#(kJTNOhV$8 z>~2c#J7#^$nDxmgwl_bqJvrr3R$4AxqWD@Cy)O4{=A>mvHfEMLW-uJRC=c^GH;iyY zmZ|SuM%l;#iyBJMuM3Z+1v9AZ3>qhFnzl#Jj%%o@52D9&3 z*Rp`QAI?Ptv0FEFfYbSL5nBz&A3?hAt@i44GyH=cg9bE^d!VVxYm>F5+T^b_VUn!^ z?@G2RShpPkHw>x@nzMvKQ?bWBZ-GLw02%4@v7%qB(*M8cbK&Q~9ZxDZ0hsQX$poem zF_3%J7n^!ylYThn(C(AeiCJ&>MZ^T(9*fuxw+qk0AEE}S%urlJ`Bn6_@N1N=9$q88 zHD(`J24Pg;#i%Sh3n-%X;Y8DBfLk3OwaGiVLzb0RQMng@Y0J$V-8Rz=DY#Q`4gko1 zR!wTnX+aE0>ka70dmG=n7u}j+9(0an|7`Q7?Ux;z^g(5wY&!L?o|0>F~O2nm4v^!M(zLFLE z%^9p(B(%2a_vpJuZlCQ!iqgJ@Mzk&ClBOaVbH+i=Q_7w!aIWy2S}4jVgeEV!#atF z7IyMZWk6xDh@g?O0{XI(2n^vRuyC{LVu>~b8#4$eFc^+VdasSM`h0VZ0E@K|LxLJI zM0f4!l%1vZ&#yzM=s)w9#r@(H#X&V^2BJ_XWhJ5I*`y&owW{c7rfH4RO5&7yWo2Xb zlIo`NhnuIY($+MO;qYu3lHtncSudA?C@o_)>RzQIu3yh|gYS)J)lw5)?))g%r<7H) z@x9#JfSFl^T>}Tn0-YSQ(sot(_)}9(u1@8q)&$MHFO#41@fn9U<<20Ja5JaM^42cs zzc}MH4y#QBV}=j60Zh2R>s8Xd%{Vy0sZ=x8UNu@?-cr7ydHGAj<=^$4kt3)+5}||f zPFUXc+P0>u^3Ky>wy8|h5;7;dq}f24mS995k;)$7Py4>0OP4JLs?xUM<+|A|y4m8; zvMp;0$8ldz=DzXlnQ=bHv+n0AXEpRKlG(2UgU42pnDi7oRqMYYI#v5qY`;kMriO1P zaS~f)`ZGHl&Xzx?@-_XAoT>x!T=%Dk`bTm{wGU(an+Ep~8>&R~I?rq`+CAJ}W81$Yfli>Yx$}GCi z@-sm$t8gd#r@+5-d7ijff*cEDYl2JH(?R-1sy{H1+wV;aG)x&lNrOpO@wf z*ZZY`Xqw9pp{=Bm5qmyFqH>Tm71JX2uhHIfL(-I(g53NY5n23%?%!!u<&5N4Ras}^ zRUJ`qZ2Gi1%RC!O2iAFFZhYDKalc>l{UYXsv+Z#M(39V=I4e`of3tulH~y~9xZ!>@ z3{R#B{x)Ni*%LuTQYR)RVxmD50ptvu8h+%b9FDQhd*}uI#lv9uk!U=o>#WcUSGbv{ z=tP-s;r~FhJ-*p<`H}i6lk-&`qYC!x=cE3l4BL4r=V6CaDO6%<+4DYhe6I5sI@W*k zjMKvm{fJ?iuD8{_dope1yynYRxJ@efMO5Hk(64j&<-Yd`j@?V*8+^w5S+7d!CnVe7 zGbJ4aTxu=CdvAFGgWY$^;G;j4$y zI2C-*e~Kv*m&ERz>*UV4D3oD}?L#17ZGr6|JrYIC!pi1sU;yI4i+I~uE`eF6;1_>c zk`9}&AZWI!9Cy^XiTijr(i9~Bs8HoGJ%-Fo)#2mynildpu?B!ILXP*;!e{y`AhHgM zyGhncoF4M3R*RYPN~t1?_$AHGY#wNO>fLqiDR79~z`CxN?5J`ECPUW&aeWJfr5|_g zCrLstd#W7t?h{dkvZQuSO_9^2qt3L1m?-_5^GaXPe|l6$!_3!N#E+snXIUK&IMBx7 zQC==v(^)ImP}wK>DI${5`7T#lWgc<|k%N{z*kf_v#INamk<@HcwZMUOgg!|BBR!nH z)Q2w`@}+rDG{11X;gg3GEx}ft6{FoIr1}A1JY_q+$xubEyg6LA@PD-Raz2J_FMBgS zt4rwHC%KHW$ISTwkKAErK{D9mI2YDBy`jV6qMs78;VZhgh;J*~rH7N;rH0C)*meY8 z8ew}Iy+Rd%MA`RHL6dA$d)drH_&uJGKhQE){*-45Amx-vnv4G9I&*5ZT%ir_gA=0DIxe-xqFS6+ASNLiU7ZFc;L#|< z-yt*S&wK3Vmv9uKYy^LCJ5^^*%EBI1&!4YJw5|y6D%7`l(p9dLk%V|)fMq>;i*dHc z@jA8!XgU5+bKVsuSLb65j#9IB%3^w5udd_(2rKy20(E(sQhC1L#1#!4^tL zKU6e6ovM4l%dfRkTmfy%Vq%~b!3&1{5;)pQcDR=piJ%B5!z;z8;_*;7C2|4Agv*$o zpZ{O{5<*Wj4h#L< zNfV6}U)7w&QK@^k!-Bp(4ZpX$uB>xL3t^#BY9QguU=By{L|IOc?G}8Cu9Le`sS7sv z(1aK5Ie{}E4tLLk6K>%p@`fSU{a^uTMK*SanR$Ru-MoQys&GF@`9v}vPNYA!Gh!X` zKsPw-u_Vf$xJ}ClR*9Q*ekX~pkys+=ll|&#x*v^teadJ_@U|VNn&7c|zK{V@CS)+A ze5P0uEyzqZcq(#j(e`cAPli!JD6Xb^v8?Ij31?E4bfh>L;&VKwA(YmH@I;&e&fua_!U^;7JOBz!+G~FL7sCl=A3(Y8mi`Ez z?l+^5Yx4N%^Kn>W=6~s~O);qwQATVMQ6_gBRQawREsb_JIbloZ*}|I!Qfnr2pJ+)+ zbd<^OR0Ql@gE@aV;H^QZhNV3mB!;&+&-eu*PrV2a6^a*1I-Ey6%3fz8CinfGV>g|TM zx#L009`-7Re-GnP<~5@QgRYy7!o3)ct?}rhtgtf!-xu5Wpi9t~-WSZ?#&}EK8O&3L z(Un6eR}kfT-~EW=2q4PpEc<9;K!u=TDZU9w&o$?69VWi{u_b-zx@hq zXt_q=cG3G=fe`!kRwKVYS%8;#EXCBTbKfDookMT_)#27iu8QVi``p0Dq7C-kx_Fxb z2%A^1&)gx?li8`k4a1y{7@+MSW{R7Tw@rifBmd@CdqyjBC5Y-}&Gkj##l!LOL=_1vCp2o~LD3!5wO=WDAV+!&qEY`LXn~r6Pe#tqQ5?&cFKTC%hH=WwKpBtle#89lRydFGqwT3h8M%Ocw!c3WF~9SXQo8=D%&uSEyxGDV6CIjhnOd~Ty7tDG@=tb zUHbTIv>898!Sj> zbBma-;1p0gaf`?X{)!2BZNhl1nKG2;L7)?43#N>aGMOFn3|J$~-Z_`vlG$tK^MPNl z>%~Vz?%=>*750rhMJT>5;cj|D2StX|RS3!KC0I~TZe2Q7J)t7r@f;Io-D^=nYdsGS z(kDY{{B4<+B$s~P*A!?}t7(!82;HSovwE7iXMN!W3gN*8PaIjm9>y-Yz(K?ja|$B%n=R-`^?jarv+ZDPp+O5Q#B?b%&$bW zKh322H`4FKJ5aA9CuDo=!q-IwAO)9K6(|8|?(0FAlj4aL<&{vpzt$H+eBO+*ix#uD zs@FOW{3L>NON3bEr-HWuY4)QDu@!7dl^Mz1 z=a_=VjUvU+r=Mino*?Q&!D%}$$Trn$A-@78)~nx#EJv>nzepMS1^TRj*HT@ePuP0v z6cAJL%wM=e89sM@=H+>NBjCHBiM!M$!Q zM_K+}gk<&rZr;URpt0r@Y&=PWv|+u&FgSLROz%2Dv4+T%uy+Z_^ROB8&*Eou=BFxc z$=t4r1E0y;2b#0q(wpxunVGZ&s=A%s7YTm>wshv$WoH?|_s3cg{D!i4AOQw(OArz2 zBA)Y5DSdx&@xbrAysKP7l}E9my2xy&crd%Kv^lfwzFQbwbWx|bZgUPP|Ah)=$Q$L( zUcn{_K37u@MLWr1nkX>1yQ+y3z#K|ut_xN-cRm$XL#nLaiI{C$cw(h|tz2rvDKlTS z#y%eG(^SR3+6d5-tDCAl@oF-|ldHHvx(!GbnFYqkItBoawuCg;54swl*o>joR32CR z+Z|>R?mWgr1tK{V%X{N96+sW~(=)E<{dQS-=S1+cHj36OvZluLfHC)Jr1mi0=pz-0hlb77Y5`EH5%vHe_*RPitwwkj}x>4 zY#~*NQB;5mgYKrY5foP^#$TKE*|8YMclAN<;yI{@Z9Pp=|nRF-ow< z#fBT*#mx3Zh8r&@mB{2X|LPBYOf}m&f!+C&3fxA%#-j~ppUe7$&C&kY^VQLF5v5@E zIeME%^(3&L1#^_=)L$6GY>2Q>dfr!Y! z*RaD=2qb+$2YGaK1aTu~$w0cvXB<`?UTf)rm(H*s+k7u-vVD)@9Kogv7#tLfVmIk6 zdGlQ@%-%0b(08kz^p@PtA^gnhc8mBhb)I;u5E~Ze!owkxa>lX!%#B}qpw?`in{~#{ zBk$9`Yb#4Hu^FRHKt=(H!V9TU%+KJvbM%H2RPOn?F2mU1E~a!~l{=uk*d7!SWVqUIANJXsm3tTaBc$Z-n2!AwB+82{zrbC-E(5ZRvcOxF z{OM@H$y%oeK5QqO-3$gNcB1nbNRb6im}62Q|A(;v-Ebs66WfAy#I}YBdHIW;5HU@S z+*w%;#`q&ZpT$O`Ch}icT(h1ZE_<15$7$JdrA%!JuNJkl)5AOX zQAF^bi0Wwi`8vx=>#VjqjnXLuf$u}%WBQ`=$VI9h8|j029|I7TK{wKO8=M;_kU^ga z-;*pB*(bQsNd#Pm_|5L4D@35cV<^yKmRB3cvVW9_`-F4>>(uTn_C4fVV($s;v z2C`J=e3X*P$bILJ+pU%vZXaN4FC7CX`1x{_20?q6$C4>9IlUOsC6dvy zx=^dlt_^wGOZFx19LmdQt>;Ir;oL-Acy~?F&^k4Klf(*g99>9f1R>}HUC)m%NXrfi zP+mnT_|Z3wpWu}D16MZg5D{on9{Qz3tK)8A7jJ{KyE~^#dwM(uCtX$obQ?peX2W+r zm!7wK9u6kU-I=pR_u}Ve?bT2EYzvhqJ(B0+O+&JK*xgOFaBrYY@~7X-I=STCsPcVP zp4Ue_pc%cJrKd7;YQI(PzN3;)?Nr~Lrt(7y{VQN@hfAoGoJH2&=hJu+b6O0qjKk(| z0M)c4ZQ=P0pJ5qK6SrX({``T&o)?*7UHC&9&yRnQCdQ8|dbkhoaWmh2vsiZ4xC`_- z7S_1$hTUq+j4aM}iBkfFqR~ak4*0&E4fe7wtd;Ko&S2*e(C!<)4DG&gwuVj;7uv$n ze8RS%?^VK-pxt^+k=Pl_mE>A@CYf7o4&o4Ts4;OiZsmMWylqjG*{t(dBHqN|zhiXU z+!|R`q&fhNb94M_i|BV<;!LC8rh%F80D^6H_c1^aYm#XAR)!-wpy}{gAAOFCro+oqMSrV%IkoYPjTpODw&s+FWM8enj9tgz1 z22*6JSmw*gNi{TFeO1(J6~oQe4#5TOA0h5TY_%WXRXU=os^2 z^E)o$3(pB%V!nXG^<$qCG~#jXZ31aE18?(uK>+bS>8gZw-eik$_6q6%4VK|*D0GOl zmjXlhx{V0=j1?x)7yM!}LDWSnJaaz{$%Zcqa};Cp`agYDX+Y}Kywbx7o?qzXK4KY4 z?KsuK@<~4-k?ZW!J>O^)zAZ)##W{p)wM5f(6UuW+efF(JK5*4DbbgM$@GRXIV=4w@jtV z6P>xuc##qgP-zLp;oJNd%MW@_kiHAdRXCJj4Ib7*1<@%%TVv)XFPcdxmVrb(sY4qU z`}k~j8{r8W`qc)Go#Hk-jJ-1R6qBf|v zd4s6UBP#!-%JcG!p1ZqMesD>BP9C?p8}sEQ^@R*c%qVWAp4aT$-}x%9%lGF_({C)n z@pvTvY;Xyco)VpV0X~*zfluSWf+wS`;g1R7C>k$5l#rObbMplwkWJfT>L0_A`z1ui zXWy}#3EAu_wnGc21yr?4ezR*m{FE(V_H4e8xE(o~be(o``*c`t!kMbr`nq|wj9B(r zt(rH`>bcZL<#MWGKQ)W}@c9Zf?oAXai0{pW2`wC^6wdn}Ab6n*|5e)(V zRwP(Y-;_r!kF=2#sGI)W-EVQ=ehE#BXcce__z)~z+SVQ7^Z3neqr@B8c_~2Xqyubo~P{dcc+pyX8 zz6&G$7HUaZ5}fR5GHoO!`WCpH-0qfP{U7}AWHF)fynZ*-06z4?ci#H2WweJL+W zAWP8BXD1Hkofi*AvbxSb&ee!oDRm>ZylGn_K9S4;I`MF#4Ry1Rr0PL&2h8RmwUtdZ z#@|VrW1!OMR=U^|xkyqI5d47@IZ6~4-3vL@!gWilDWdA*yh>bE%wLRFn`e=^eqW09 z0o_-MtiTY0RuKe5EU+mGkw5zuxr~BAtR+sF(6c)&}Y4AbE%=Q+)Vy7iP=1Ol^%LRZShNDE$E9qRtuzjoOk(P`>ErY1 z;djdRJgS0e?PSz4d?~g2-W%O-is5&wd*?vxZokn6RB4r!oKx#p7WALPON}w>C%N)5 z5KIC)#u0a*nCe<0qk{AwXjGCP|3}A4ar$?fy+RF^&|S|`TdVSC70f>8C)UY2_lY|3 zbAZq%EB`5#+3V_9jEiA;VwDflOh(qdK+dIc5~V&`Qv6jFr>AdyL;0qehWxUZ$Z^*Jg#>;uyUcm zD|gg_YMcP444;F?H;@wPrz~@8JSI~K!#J9QUd7PJ5LlMgUtpDT{{)FetF1|+^k*ouo5s`d8@y`1%OZItG2aLX85<7ec7Xzyj=kZ& ze796TMZ{olIS6?$e1QLUy63v$E>F8P0;B8=COrR4Fq|iD^A1EZ$`dzfU5j|5@L!jd z>D9ImBgsp3n>~`2cneR&U+&N+SY$BGclQ*h&v{nB+fwKh{?Y|5;%b|)_)Bt`0OG|T>lo`e6GY;JOahJyBM{u zuJ`rwOdYya|6j{NL6M`2c&C^T29{w)t}I6SDIRM(ASD+nGqsF@Wc(iluC?6B+yZ+| zVFCUxX8t?}>PDU*QEj&3w%Fr%RncPK#dyMdSn=p`iU{)D`~A3AEmOKg4M59T5+pPG zW=j0QM+|eT6}PfH54-Ljdf4gxU1`HiZxsEyVz_t0`*4)DWv5~x7(*`$|3xeCBq2ZX zj~cZ0P#nSQs?l~2%2Y(li>Md=l>gkhWAd5M9t|gg)x}J90WHGq2CV}4KK;a%xB!9S z0)Cx0--evXtPXEO-i?q6Ss!Ab1Ug&`?Sw`+Hv<8jeAa{`L3>}3a>NSs1}fHA#ZmsT zTlETtTayDy<+CT_5WZ(^YWWbp(&)W4JjBBCPC4S+Xf0W-RzhE@{TFXLiof5&7cpBQ zh!918F|rsF?w`(wQs1^@J4n1p1fTr66ukF2<4y(R>1eg($U_l(cocBhrbs@rCSj*t z=N29JZhYoHo`&Y|)=1aleZ>xM&%&56cMf(1(y?0o-Iv!;h{sw%fs- zS~#2TL_2Zn62seKQGKr)TQjYX^X3WgHM^!Om}_U1x|0Yyh+gkw`^1feFw$KY+2;Zy z8}3I>L}ybzGHa{MFO4;w8r($Q%lFZE@bpDNT5*IHGxDyzqH{g!4>B}gpOFO4SH2Gs zs4P-zQn3tGh}6PvL)e--_-t9BmBNJcmqMhLoP;+U$Jz}|*y4BPAV2SU%KS+*c6%~X zMUh&k>@UE#-xjI0m}-)DXbXRE5Mzu5l`tTNG zYw#as7m9+!{qp4nr$w|X62RZ2LrFkd@_ZfbMo}H5DUxAG2vg*4_msq+Su2HOKEkHx z)Pe=2QJFv40_t|QeSe?l`B9>4vu^TQR*scwu7L2Cc}b)5CRj1?R0y!u;E4MaHk|N4 zN{j3tV67cZaLOSJwu4GO4b#G_A#{;`EfmvLif#vzidK)k^J%!o3c}gi>SPW<+0vb@ zb2t4?tog_q#xYkX`x;)i+!}h8@M|zxzC%}d%sGt7xcV9i`P_>1gaWm^`BW@!t`=e_ zp6*WzvsH}UW zCEfBaSmhi69C9l;N&2-HLfSU2b^D6fn~a@Kp-vuOeF!dC8{w<1(#r9%Ja@SD2exuk zUw6>IMT}4zV$FXB1h<`0rS6gwl~ zHXlDJ%$y|6CdSGLwGA<^K0}C!NLGrNa@pC9E#qjW1TnX2UgjcVA(ZWgn62)fk9x%9 zH;CC9--9AI-S9zJTBP3s=VqY#d?Zh5A_j+oJ!81^1OK~V7p^;p2I708BnwK4EMohx zyOW#cv1PKQlge%(ZahB&XU?=|CeC*N{je_n9WGMfql zi%(8mN2%pb@q|8cO5#-av3)@?s3U-xA@}-V_@>@v^F#PqYw~vDo>&5UL>)aCuArhI zzs)Zg%OqK`-JctJ!T89GtL{+IrB0Cah`r488f?ea^Xknb!NrM&5v`EuVRwZpM<^+F6kEM8jag&H&Ml}dS%%AJ{VNHOv{r-g+olZ{X#OAO{sp|M>e~Ot6OuqwaEA&STQ6HpYiP9%Em$Z}vmt@C zHPI*{RHfQtky5p>B!cxqbSJ>-a%+ytskXMYt!=H^Q`&k#YcU`O?8PG9s2&xu>e@yH z#Y+G+@8>(_T01*{=lq`k|9#)*<{eA^P!lJ zlFQ-!@K@`QbJt4mQU{~$PI_^SiOAYS~>NLLqJU2u0-; ziDSzPj_H1ZmIQ?bC+`uG_J`=LivnD0>;pd7zg}Luf@j*W8D2abic`i8=37*qdD{CU z+!pU|1sf?C&3ig8Q1^(5wsN12%W23HOMXSw1v^j4j2N}d8pk{g->abgHnneGzp7h& zD9m@=n@RAs@G(7gN7UCMz5@1xdh%~R3%~Wub_Z81Uq|PAJUMz}92Y?ltJ_X0DOw_7 zQ$*+ZYiVJCS@cFw$9N~O;A;LlC+Rm>cNNr{Ar(W%x!fpe-j_HZ)7IlNYdoC-*1%0Y zs$lyZmNJt6Gz2HIv*x4X`9&)aqEcjOv0?1JiTd>@xJupmH8y4`fve~s+Rr%`i6uK( zhl?bZY~h;49FSY82>JaI>`c=ubvj$1CHQp!;?`8+XR8%|nPh*(N4CF0aMx-%^JJb8 z|EWMIFJ=XHxxg1ttu})*-L!$^9l&J~J_gZEzDXKsfK+QDZjImZZewP_a*3qjE335_ zu!1@>;n5OnfezDImgM2)56cJh0-Y9{MOtJ!$PG63krO%ldU=-|6j<)V+eb^j!C)a7 z0JkO2FfIU{NO7D<%qY92pKh=Djc_Xi3TaV$C4nL$w^6B z4-8~Rx5SZ{1Qx?76({t3_|7mFcNQ_A+&jQ-_C&Ck9eeTVRYs3<)R)VR|G;T%H_J-l zH8FgtxK6=t79|L(l|$VzDDws(29>fBomM)FJuWMS9kC?`2y-_lU*JpX=|>N2c5h zdUCDG75Hxn6|-HX?o5#9!+ldOPO3(eNVEC9i$7^bCp&Z&mD>nR4Rlf+lWZK`yh94% z%iU>0VVnAE@SUf2zrsTYUpQ0uL-Y3$yeU21WEIuA@4lcXC?W&kRsZ#zSs&GhI4N+7 z_4E=XE!STQSg!GcCgYch_I;rlO)q(ul;@$=DBU<(4m)WipY3JpS_UeJZSTC}U)ITw zo7j&;Z}(29_wm*(Xfx=H+>eUB>YvVuYbV+B2;*DA!}g_%s6+zR(UWGuI3by?C6-9W zm2y(R+^#*GXW`5HxS5_)+@J6hVPl7Hx=s65 z!=K)uE!B%i;zuSue5B74grO^Op? zR)_3q-B!$hDPHhjaaU@euC|>V96IvbulDvG+*QY?msVAmiT)s&%fiVozYnU2XtwDD zg^;RYl(X3Bw~KP&{%2XBQv0vDF*j)t8Nf)8YROUYobpIzqokX0skS{InK3E)5L|8y z-|tJWtEZeg-pDC1!_$#@Db0FauIGCR+?Ne|PakJ~JjBOqS({LKK_I>V&MUoaazoCa z#b1;i&LgkX;=~Ec$o`2@urqdvREA&r_xpXThN?|q`^+ov_t7sc1iD?f56Lr~ADVqr zUme|5$PZmYXzi>At>wYct2mnL9JW{dHC!Dxt@N?zVc0-mJPFRBmDKVThRsVkt9$mt;Qja3`@0O!w@45w`(|&1{ zVn1%PYjv7pUhXUT^~8~iibkZ;^BjE-{|2fp5HmdDpW9tNP*%^}tr6d``8C6I(9>qb z=Wk#6x`e@k>BAI}>{oi=+|0C^;^LWOJY3ZkE=6*+zdrM7@#|n%-BpTMYYQ=tEyktP&Jj|<@gu+u0NdBv@vnbn-F8-o&&w? zRP*sAmz`FXXxlk7(eW-HDuE~msI*j#!Qm4wnC0AgyLiLkk5MSx;e4)lM?Z-ez(dq> zX@Z`%4ZTJ=Z;ts?S|P%E!zWbHLWqKL5MwR+Iv5^NH+~N z4uv*N@{NM{`)nR%$DZ9_Vq@iLk}Vy-%v&<8zv^knfwnk69*#HU4v?S{m$&-7)vpW+ zw=9G6GX~?7V{8_)V~nB?{sMhbXV2)k|KNn4EvgJRezb!b)$(~;@h1DFw=17n9Y(n^ zi6yf63Gu5qehcdnHMVx=zCpV>ucww@)#>cQx#gQ7vwXdl>ng zJh0&ze`gWPmlyrw($9@C5ofn}d2MFe8ztAvLMin*m90h3`9Kkl{@;+M%Nnv{zgJYn zWP2CUxj1PoC(?(%qz=8Y->$y4OZVBQE^*KFa{uAP(&IOxnBFsGpY)v4B@M;f@^J`P zeyAX=MEk7>w^x4nB-bnXzo;%zw?WIPa>BitI@OdYvg(xhdc4OT=}7hDsin; zDo85nO+XMybX)=+H0cY;%Kc2z@jh6Vzv^cQ=CS zIiP<&^v7O8W%RSZ*J1I&AM|0{{i$T@{QMzZafOQo@A-$5(=V+MrN-eyy%s*y%@fz< z|K4wh>#`*Tl)DLIX_w9xb^ND}`dD9j+9G~onUp@s%3~=)@vKEX$1EhW(Sfnm;g>&a z$Foq|Ea>JeMSKaVQTMMYhVX4X>g0UI2%;V1=3cJ|&)Oy&)5f`s>zD@^p)V zWL~dG+>_d8A{l{i0Amx&TbsOKS{#>tnbz!Pm}gUE_$~)LO05N5tOI%@Yl{}A@GEFc zkR((VVqdUS(+~BJS35Gy2IfWLo-{QrsO($VWD~V-p$AWzGTaLe2rE%BTxDpyyVc|c z7muU8e#7$23SIFr;8QlRX*vne#6J1w$en!G--vMa`vR3>2HFaJzv&!{t zyh&rXNT#6;M0@vrlJD&e-&eCqoho$eWiuL}G~Svne#s>Mh0JTn3Zc#&a|buRmLGSe zUW@Y$iRp4;QtPWC2xBOm4M^Nb)}8H=N(?7b<$jg2%SHTkMkl+d6ngYTrXNYu%_^CW z%vABU`WRm)DHvbZa4puHqWn0n*=}V$*r3Yj$tHfo$8Xq4Fb5zpn-EVbjF(_4`tggs zES_DSKZ6B*}#$Fk33Wi7!?f<7jY}vOj#@ZdS5?V8KahdpISZt2jVEy z>h#R)vFHD2G*7SEAP|^&wc>A>A1lM@v>zLhhD}{RS|54-6s-6*WYfo`v(AzOP1iuv zsZwQJ5`*Rsn;pogQo zq9^F!;#bN!atqXunyEGwfZvun-L;I8ZB6Ug?005(*<-JM|EQvf15_HHY`bEg^2E|p zAKBiQO58JbA4KSmBCj~Kl#B39^rD#Gy^!a2L#~rw<)n&wd@JT|WE0c~GDe`qgeMYL z?ga|DLWjp6;BQC=2R8Un{wov>&nk9jQ^+xu&U7am$G@5Aun=pE6miE2{6UR0bHAKv zJ$Ku@$B$5P9|PZaLbmjH*0P0Hga?;JaYS0WR`+DBTs_}$USV50APtxjkB+GdoBkrj za14OHpcshaU5Z{=o%lx4#Dgd83notDs~0$RX6kTisO($N)VIK+BjMF7x_mV2 zYv$0K+bMQq@LAT^oMbv{pK+=z7(D9{!)5uvwXVPg=9h#Q61Ad|1MMR+rC1O+Nj#M4 zD;-n}(A~xei4C@e5k1s1phUuHfJg_V@EP*xNz zR6@u}5=T;rA9iuz+*KcN?RSrQYLa&1T>R~x84;c4gQy3l%y@?4bbyo@k^c_fi83?1 z=*pe?n^<}^1yRB^Z77fXgNE=o+^FzP8C-s5jYkz{nvud1M$Bww;@ z6NbXgF}ex+UUVuA+8pc4x4|_m&J0Brh}1(DjGWtPCqzqa`{gfo_MxZ^UHFE2|L#lJ z%#P;+>MGw290DcuBzGRKJ4M6(n$hpsi4Nha2X`~D*?KwyApp96nPAP?-l(^|37wmP zE^(dq#ywPEEz|tc{Iudy*3PML1Jka;TMj7Fi*?QrGVqLVj6G^FF3n4 z`XbNg7GEQt&JohAo5Q|$yEs2NRx(Mg3_(4`uh)7;MmzVUQ+MI*Z)<|;tvnUq9$8gn z{FB9J#eXUoq)mkzf@M~?ah%|^^eiQ?JkM`kqhvvHXCTql~zr_&HPrMW^Um&g7r4LT;ZN{+8xa9%-Ol#w=}5=^l zIKv)l?g}P}v#XHo6v3msg9J7&+o>;yRADQGU%OPMD*>)Z|BG{Bc_=+l7mMt1;^Yl7 zYczW+llel}fxJ0nwuyp5itWgOWE^S&WeH_P;i$Wfe~;DNOls5kjR&&yce#@}0=LyN z?=ES*7~`CPk660O&m#~y@568TEG~fL?IZy-`LX9OPVSZYX6&C1u#-l@_g7ep&zaL| z*#O6_+r1LyjNTGX+K&1|NJC%v(gX6qM)Dz|q5r5Qjgh{Qmk|j7n}Hb7SYCixq;j?0 zg1VHP%v!IRcryK#*#Mr&O7MaN!0fQigV~YZ$P($|qGYG>O9}FQIvj_U-3v;I6pnI4 z!yvHJf5ce@zq6~$EAiO=f=k-3NZdPl@Z3!AuojF}y~iXv6bIo2lWP+9)Di&Faz)_^ z)flu^G|GVz%jL5JQ5}+rb9|a#i^+7KL=g7$0`Gh>hHLa$n^x=>oeXdWdJXYPR?0ly z_-Q!cKb%D^}#?Aa6M-~jx;*l;S2XX1O z@TX{}@%S3O^!z%O6OiGc&^9ml-XebS_bsX{a(QoaO6MBSkAj*L*PhKl7t~%W-B-t6 zSe!1Q9Y0b)tYz028u^(QFLmXyer*XWRGxT}zVM#=O+`W;F#U-15mF`6Snm5xjGNSD zrc9-i?}7(x%b$E56()OW6ILaJQb0@X7WpS~!b&I$M>BPpsbi7k=>cwHWNTSP;^)G> z{>9Oj@$*~kse67K*Y;f61lQr=&nfoG?Rg=bPn7Bc6qT>anex;AY~=p>3RS~zrGCSu z&q`9jq$pzlEYb)Zct9X*_Y#qr3ptA-<_D|BHoi`@w*iC&A^el>rxN$pRT)7i+7B#K zPy$5V{$Z&qJ+U%z?*<+ab~55R?Zytiy@)j>ojvVIsOkF*c*jc=6cp_?1x>_tld2IOEtA1)N^2ehvNs{qZo zY}t~txriE4<}=+~{9F8~xRWb1C23i71+u7HVk8yMkzkh-5%ThiU`SN=k&+9NH(TcDgn^ zbm_sdHZG&N zR8S_aXPK*K;W)XG)^k(fVi-cV@?q1chTJGEuov@+(e*S6l}~_V%GTCnZ~b{ zDZV3DY!SQAv)nqns?1KKY%J1hJZw_uG1kV}!$g}W5xTA2pce2Ayxp{O^?);bOT)+9&o$qy;Y3yidm6=PMkAY6<7-H$7VC81|sPNk?Px@M$ zOw%$0;MTrY(u<89)*%g!>BZ7Ka^G?!=Fjc(0WKriWwo)queG$zE5mjSH9TYL6O)6o z!`5O33t#xRfY*+g&3K;XbH?ZL8*N6S*$sPO2xAo7Epux4N|js%;s0EyXDcX;a!q1>b%I_}gveM(N=G)#2I3~3|D3n_WfHlM@n2FZGa`qh=3@mB}L%2^hRbryZ^};9mzu2SPi${~HW>-n; zU4oRt!@UhYs1U0=%=4yD{hEj%A7-A4O#y6^Q`+T^u!A~br;^YMeRm)&IVxW zD$vwN71sH{b#a|CiAQxVa&_tnb?PoWjXLGb>`$A`?y<~(l-XhPe{Hw?$z~T5ZGS@X z?8mlpy!h!v<8Evj6bve~hQ#(06+PM&O~yr8NGD^aKGlN8WSWTVPw3fN-63KF6pMZ`~A=4}ckyL%%()Avk>=Z8pOgEdp~sg2N+X zQcs0%{TeiC^L6*EGg6t}!7VNP?Q5OnAU%%O=u}tBvE(z515xnwuewVV-lgoJcf7{(mD?P#CGva6%xXQcwlGzFPs4x4b> zW;&R->N`+F;+`cLZc^HQ@@}^21LCR@OC+btlXf(}-S)nuE|{7mm9!TiHvd}q;YVv~ zDzmnD-4d$S$$VTa;I42?V}Bi>k@$vb zl^;JvgruA%1M(fbM98?l7mzd=WoA$N!(H8JKUJP+EfxPAau zKL(@Wq@6Wqc7Bv--wNO!37ClIoQZa_@QYycr+r5;60gEtP8lN< z4&1kMH&h8T!QQXj@(*#IK%P?(%ET!#|INWFJuCYKr2PBDHKT=B?H_qHJdkWL#EE_r z-&9pn{!l%Y0VpY4$`UrlZ4X{Hk_Hp^bclqIbSD-sgxr22@?wP6^CQKwq=o$wnJyKv z_~W8tZ9?o&TvBIbcg=k%!cW@&2hOPQ$w69!6;Y9sCkD81*KZPN&mO9fom@#&UR$jG zw1KLl%_pyhw@(t!w8k@k76>p;YrN%p6-FYF(HVo{^XiZYTlfqgLUN_c#|If>PFM!Y z9G$6(-N5%B%Y7wk-;lcWIo62PA`|HIG|3fIED586BbHfWPUy#XcbiaE*4-vH0v_Uc z>=hyx&+jzi@o#h(Y}34@g4Rd!pbCSDf9sFst?t<$>e&!EiA40rYAzmBR z(fLQ991(&8N~tV5UNV20%C$-@Wx*feAO4$O>EP`A!0V#A#M&^N)6^zwDmA0$I``3K z`sh2DJ|MIx!}*dy2yDnsZCCZLCFD5a~U z>>?DYmPn#=1ryht2Pley7M(}VceCRCbE2k(d5a2@`Oi`hMYkfZJ+{4_5WTI=zfBg< z-$dTZX1?$<1Eou*x4z4)ZH}!R&Ocs|Aa(ISoN>T5Yt%ghUwVM~!+>}nNR*PiA-LR$ z6frU!MGLndyE9PvQ;3E>#PVBnvZQRKZjWGi;bD&U31NuvP!VObGQ2Ie22mDlVrVgx zJG#HO*H+piED+281(~}nZ05U(s3W{w&t;A1ho92t>FJLm{=ofA-QU)KKTY@d^xuD3 z_h#6C?iT|boCD9R#$_Y%ndy>DW38x0+)fTyOz;FXf(%Zi!gie5u_Mv`5A`rRcGVG8 z%&9lR{VfG-l#AU|&iJSb1XwWqF*(>H43iQ@R5Jj>Lk}(FF0d+{cPU9Zam|Z`*j=hL z{*4`PwCu;4Ok?3gTV=DUGcQf4>7C{`3Bw0faEw z2!X9QS3ga;G2G?L@o=FY>afo6(4X&h{A{t}WX+g+Ju!NtNdye#xB(+rJ-W+$qi=?f z9~T{#b(N6fKW^rE90jb*biF&=&ibWEEZV&ot)u|lD>!1w&F+QO9Pc1Yg~$cdV=7td3+`bv+{B+66%;`Mfv+RvV1lK#cy%4pqI)JGctp?M-)EY|z$!|Gea%H8~p z?GF~(=He#)79Dxu`6MD_Js!vYCG!@Wp5Ea96Xgf%!Qi^!R`b{vAS6B-0YyVRvj%Um zmwlk>768yr6VF=-)6o*jE!H%!4;Y}~FUil9ZOQ_9`KSsd9=SxG%PT0k_copmX1rz-# z=VJ0zo2$dYYyfZpdJdxh<)rBNtnJeu+@3=$d*JWgi z(ZBaxy={q(EZ_H6qzxW5wll;Tx5JZGadK2M&X-`&d_FwtkL;)6p?Vq~_#Zp_>f70sEWcooG1HYrwxpJ| zSG7?nJQs7Yxl}fAmuh>U>Pmjm!LYz6Vz9Y2jwajhY$tu(rgE9ms^VxViN+wAXn%w@ zh;Pf?NQq7`$u^aBQ!X=5G#-#ccgFe#WU0jRZak8h`USa52hGjjxe0&%z{d|($1iLw z+GpF+RVNe>ey*-ws_hllx3C$ryY|SyvN3mVHUD$O2jl>fCG*#K#gTm{aqrC9y5cPgBw4)$gThQU zo}5|=>0L3~n-koqD)0H;(1svTi*>=EY^pdr@osC-n)bJ4UMjuVj)W2p# z?a`0SC4WP0QOlW;UOwk~!GIX5_Op{|WW)yFTK32~YBrE*HQj@--9We}4BEZ954QC} zB==6M^)jom>P#*jzkcx@qc^IN$skLX5ohO8nc@wkAC0OE7L|7NwSJRoF@ZIq(K%#f zFDmx`B#1b1Q4o3Ap^ql+X&yS)KV^`=Ey9dL2Y3M+AB%`@0+|qD<4QC!>2p4_#Akut6#HzW! z3G^XAga=iQ*~ZqJJ%F&uvXakBe$OEg&_{RWcgWouO& z_hZI+Emf-IQ~6$5MeMbskqF zqb=^vh|fkVM?aeF}HNZ>sf9A-HKR4>sTG>v{oFigQz32NZ3%- z7AeY`^S%l5$^8vnb*e9peQ3M4oNS1#IK5@`s0^Qfv#-yxxvb6YYZZvwuG$YE!|g1+ zy3vNy@RjIhmTvyV&Hot>Kbs_Us$+^i`vsq^I-V;5SoUUbUk=Lxq24y(G#3a7>65>8 zA^hM`aS+1m(u||`7RC!9dcN!=QJITt3*@c(O&IQidZ@qB z*-LRYA57nzVWp%yW5$fM|KU2bny9mhj!_iA5y4U9^|$CrWJ6>_fo^%KrH+%jZ`;gI z;wtS+_p*(*;N}P~%qZmyrL=2T^*>7zodfFZM zh+QtPzc6y)I4aEj)mB~#j!o~Bg-`y21pWi7T&+b7AW?E^Y2KdVLuZso(At%k@~>cT;f$50`yK+B(Z?0}cZvLQ zLg7t+g^XkuvNL`H?t0dRW8i7AKgjd%t<<3UKNROFwAMKNHA)whEBb7oJFim#je%I| zuFU1l$Ks!5;e+fJ)dkDSB|RdI|ae+t&ru6%J7qOmpjL!f@_+qzkC{Mmp_p}~m&X^H`P5&^7Mq>J z>fFnKb*eT~?SVZ%U>@JZ1NNv6{LYTtFBBnk)bb`8uqVTU0lO^*q+q~Q7DtXM3zw~R zgq632F@X8FUv({Q)(GsMj4uPqpXsAr+VX(mrznhO-6ZG!6;wRG9W%- z9|q$7V>KFxG1;*WNPnxi+Jo4!ghy)a;~tM)2czdWu%?JDV+NVdN>x z^l;gY?qQev3GHX)I3`kVn3g%>f&lhRz{dQOJG48B7mJ?rINXP4xhvpqG5<-OHu^n_ zk}+;f7K;zGu2ooqzw&VY&|Q7x_QMZ=w9>M8ZsH5W&cj2+1jnlKz5RJrb~g7sU%gd)gS_yzfma{k9Ov+{~Cl%WM=09WD>EwfvH@zdwb_E*|;+<9>&X%@ARR0&;y!pquqVdcvx>K-*f4vqv7=nA!sk^Xx zjqq+e5G+p~!@KNham0~dZC9*ln+n6etnl9yfRR~?yOma;qM|c3tFyzV%pYAeJNF!k zt@ePID36!l(AX~xncaJ?4>LZ#KgY)XQA@Nkdye&|!uXH%r_wH0xF?;u3uggD+#ed` zyRlmyxBmRsh!52t6{bH{co*T6`Tp=c?$2~unw=ZAxBjGNGNWXOe1vUXtlyksLYg$~ z<&?n-(wkKvQq@&?qr2Ee$_7&6gAj6C+2f0WVZk2Z2=f0EFNfIjEtq%oA`0!o!F|xLOod4{;hPY_| z=LMf#<36jgsCeQl3lI3XW)p#EoMC8#6^$6DJsGez@xqEvfXY4H4R`UKh9mwazgRS# z$yP%krdEf58@X|Y8NZ{G>+th&3Gj6DOJbtnsYBAX8{g%(=`uU=^mjz39{NRXML4RA z09nfx2f^I=A0imO>JC#6ITM~7WTr%QX~oV{@LfiF_h5U&iV-IJv_}#v>(1` zoeDcj;C;lS(H+(3g3(rx(Jy72>!QUaJF@ZMQAOd6YJh1+^&OQ$04><`g=hUlBG{Qu zZsAmsptc6QjH5~dz_P>k=W`;%UO1Z$eS#*V@TuT#t0q{kk14h=Ye;Lz9y<^C!tzyE z%(I_90&qI}?p7HBv~UU|ANSdp@!GeQXpM5~L-@{%jEJJzg?ew!L&)W_ED&^6b_;zZ zxT;L{t?+6bzp1Y^1{d~nVB4-Ctv_=OL}C!K>}ydI#+hO~j9tDMQLR&L1Rdpu0*dAu zf^3D|kWW<(W#VRLosn7GJT9^1b*(eOG(^m{F4h`FuC#BmXR`|kGup3uFABM1{2=4@ zQz8n=w84ZNqOV_Omn+mJ= zN>VG2R(Ye*Z!a+#O|l8NI=n0@z$@2KcI1u?%=PTpS6)Dk2x^l`gAjh2?&BqZW5gnC zgWo#OB_4CabAx|Dv#Wm}fs{S;7k@d5G|mDuJcvfDcAJ$5P7Hu;>GMEMExcWwy&^kv*%VxNSmF>)#EBe23P8+w;Dsqn@%CSY4c7 z9NzmhABtnlcdyhX(3#Xp=~2?hb91&9^M%{m<}aTo6uKxngFQ_IgWqwEJ+)}MaP#RJ zR`GZS;_>!qATn({iHFrLSGXsg%eV{o`?H4C!Zw927txtonQ*oIj#VNI?^hA%$dnQl zisuZnus9i9|J7FQob_9VGqag`Pz7yn+H7sEAtxV%LSmBIrts6W>BJL;di~-& zLMBBZr&f;1BYe~))+bm0Ay(jHcTN7}v;q=Zu_&rH)7ei#H#!n>PdarMt|!zl+>SpX zw&KO~M0lDT0pf?HD<$gI%Fb-4#vXkHBe^!_filq+}ri<(+*iY7YP66eelgF@MH!iuHlJcaRmbd%HnI1Zt7fY>MCVz}<|CdN$>^{@Q#wg?u=Cxal#`RQ5(bIGgobk{V&qmnkak%zx*4TBJXw0oJwgAc$-S6~mc{S7FqROMyMKL4BE zJ{kR~$L%Ax+U6HEh3|Y6Ral^1#rm(}u@@`K`GD3%7}mb7Ov)|aZo9O!2+Rais&N&x zu>DFY#TO(Mz}B~c$JlG9APIpW(uk~^vQ3xWQ{Ex(|E88>^7ck}L&e<8_Xj6i66+?{ zs*Ja8vd{|U)|OMY^|><@KfwQOdTRdv0#%5pgMuvw1gHRPqT{O!xdBWTj6_EbcM;f$ z_M^CIyR;0u@_4S`D`fB3FO^wDO7o82tJygv=Acpc5*?H5Q(~@^&d&BdH_SOoIinlK zZ%MT4oM?RfBHl6e5?@xE*Tjg*oDkz*Oe}SGU~H(AlDp09xeM%FoB9CNzxAOwlI+R{ zvB5j1LpS7)dMvlU6>C>9mHWiQak;I^WLm9qd3&|lzzHVX8eS*ADma4KNN+8>>ek-A z`NfghS>gB*L`}qVIqc+~!)P4tdj<0*U!Um1smbIMr?$+FzSb{kOL-9Q4{IQd!C5=ciFKgtd09|H2v`0Sn4CCF)5@^ z&6o2M_ntmDJGqB8_g#2~7hGJ!{s8pHrkYgS_i6XLUdJOXgM%xQo72ChugaTxO~V2CX0iu!=lpHr|~ytmL2({hbT-iFF%aOQXBx$ zmCVX3 z6NAZQ?HC+PPM+zNq*Q$^jgLIg!SG23LkZ19I08SE$;+pgs91^^dJug=vylx$LA*d_G+qKKxrHi`_$tXS#x!VZW)Dt>=i} zBfl8`nrW+tbCEe>z4CQwtIGLNz|Y(cczMhsd4KT<5OgesPEF*kd+|P)7c$UMu@Dm18Bha%-@0A*tfuwSPWaYkKAn`ygd^2vLJAC?g zu1khS^((fpeBD5*av!|q#R8;wT~Z!!B4x_Mo8qsJQ#t!ODGgApmwf*yfZ<#c0OXG2~1c~4WwcrHye7Qd7BORJbYFaG%`!cvAZ_oW@t zMCt*J!U@5cniGQ4YBU)i|F@m1vLpQd>l!z=yU)u8R|~1JY`*^nG4MWDksre z`!&h_6x_;yG*S=0x>bY(QOGGt%Pur@(?V!yLn`qw~2M>S4 z_p{D$AH;FE99F!UV{x{2d5^v3?XyOhkQ=2d_XgzEzn(fgzcQP?$VQuc;0`jp^uLbf z`#qv^V~eVSSdHRSPSq(c)+Q5(UpdkVb5vAI`DYd4*Lny&+%51{sUgSNdW^Qs_OH$1 z>n#I@*sVWt)p4WDq1;1{+I~Z=Pq|kg({&p|V)AzGyp03yBS%C-a9(AK_{iqt5=&+< z{MypLDClfP=$EiOSrj3wl{klyMzo6EH$|iYg^Q~n^VN9LoC9BVri1I}+2soNq*Hg{ zzkidTG_Jzz$j9D?d1c35`#0Go+f*2zixpn_2IDWtzMNR{W7=_$wcRMlkcu^t#0LS& z>ay^23q21H1vVb%S@y6=qgfN$tC}O=(uDCYa?rU)ow^IBM$n zY~WLI;8St@jxWPBO&&J@?*FnOW9;IImZmYBb|0$b84TduPWRqcD(vdf`=j;VwvoB< zJS{A{Q`<*eZchnXp1&^q16$X%L1&b9jc3c&Y_wbTCS*v$tNx^j3zs03Vrk`2;dy@2 zo;n+)_5$|2s*W=JRQmle`W^eeyU#xFewWjR-am&Buiv}jS;UyK|Gb~}o&FI_D-Q-q z;$1u)vLn0k=x4=u8nPpn5SE-BTHG*x^@8yY!4N_=hnA<74IQNH@C|(Ck)@*bxnNK# zn=Y-(PG^(eBfRkk=>xV&KHl?Z7b(2pG9*$-Q3r=KigWt_C`J!Kdd-R z3R$^5Cw18y+!svuygrX=%m(4(!^Skta(w_wUP3&I+ zt>&y5kEP25wmjIEBKfG*}LHTMmEHqA_9^AWLd-zPXy7xWLhtFT=cH|SBkXw%4PyUxEw-*spd+{rK zNHbP?w|0u-qO@wNfW$t~rrk*s%?QiLAFk^#` z=V7ET->ke+V18$!{UiI(D09YU5n(cD+}1FP&d=ixVlzyCrPRFlf--DitN&*gV zn}V=7N0j7f_!@gB8iu~1#6mhI;&l%?_A%8r7{!UM-2WFy>h7o>g!*>Q)oZZd}#E$bykh#HHl6ja2SO6pf)IR01sXyrahVYu`a!O>+$L0bq#*6 z_gJqKvTAR9cY=jR1X+4@*U29izE{JkZd= z#fUS#)i0K6V?ty0++jrx#aO8EYCdJ_LaOg6V;v`FOBc6yHQ+`egK|%r9-N%LtPF}H zHKNRp8XB<}59;jDZaIzN5pEtFDp`qXUmh-i^;j{Aiv>fbZaCBM)!b)nG+7Vh@r)6W zHhK8{T$vT9g`u3!sWi-M3XHq0xb;xXonGcOHZyJTR)@!X#cRCBI7;`mMEf8$jb_{= zRI~g^N9H5Q9x1sCKC~v$Zu^=5rbZp+_34a@(r+`hcvKvuU>B#9cc&_JB+xuUVok^* zJA;vGkWQvozka+v@zTXu7aFS0Nk8_1P@ha(Q!0Uv4K=kr&4O2R>jfK)dVnX^)Sto1 zJB8nRH_FOC;rSChA2B&H50eP6s?S!^q+g#5rd0X$RoL78dU(W!1`^U*k2gSU4dc(H z&8+O+h=C_&4>=d6?5%#Ow4u0%^-x2ZJ^z9t<=kl~egh>1)U;Nx^nO=sdiF%P3FiCp z>k}Q*qXATwHCEs{V`P}PA-(YT=i4$2+QbK9v2J?iF?ovB3Xlj>z12 zJyxJTGT8y$x?!9d(MLQ5Va0F*mTII(q{-2S!{%Sm#1QSKo)k3C1qwM9Cf8o8H0F#Efz zuHmSnsc!MNb9Hj(8Z&ip#&{NjfHETR0(xJUdx}P&z+;uZ?}8$TVzK31GOBn(gki(@ zC6>r&_`BV?2Sm>^J^tCUfb_rgksBM(>H5`Zw9Gd;g=iu=-7ATb#VvOko!J}V<#wtk z^t{QjW+(!rbZN0slZDN(B>U@nmW!ugp-1jHG_0OqK!$mIPjK#u0eD6M?)g@~$g>jq zEs)NocNEA!E~&8RBRE6tr;NQeXAcNO5WFM%~ z_;?&Z1Iu8WJv^KX3Fy!#>{>}SvD2};M@cy$Taqyz!!Es*vBc9b>aB+C1#mMHX-K|< zbd!Uzakbvdy!erp7H;6%joGUGuegFpNds_!kl#>MiqrgXcF==L}>_Iw!PZ?E}#{ z@eT7&2-D$(=F)R>(Y_j<$)h|*zOLFFMivmn_x}J!ZdSGbH(_KOu@_>U3`K+% zFw*{}W>L_(xezNR4!&s9OX!I+TEj&bF$h>M6Hxs6cev7!`t__a zcvim&hI0B7+r@Z)8}AGF>o4gd{BErMxZIigLXkwzoQ`|rGyqlMMIagB zIK?$WcpvN5@?%-I*pUwXW93i#Z{+3Utd7P|{7U%qt&W(dFdB{7Z*8#k#Qdr6H~KH9 zGq2Z#S6GqO*Ba2)GLMP4QE%K78UIkC{Wp3-qf8jMtQ!PMAiAEpjT$SuOYHR`i7Amz zL@B`@Oc}wINCZcJelY5b8pdx$~Lwdu`EqN<3Ysn(Qh!@eoI-FjfpVpQ$alg zaCwo>8Ad^Ul|N+!Kh?gD)QM-kn#hy4XuVU8y|mxRFuO(RYliT>(e`c# zM@Voq%^vUw-JdxIbY~u_$^C>mVt5=-yRj1uCu(rduk5`Re3Hv;Z26QE2c7^<>A_de zal*hN^$XDQ8cTCd=NEL6;heP68(R)`pLN)0_?TV@PvuK{>re0PE#Lx*#mKSgihXnXH!>S!-fNL1FEncop)mogHSXGp0e_vh( z|K;5GDHP3PrbtrwsFFNQPR{|u#P5<3#rL3 z8+^c7D=Zzh{V(Q`#xM~Nsr>jdF`bL4nacc=#_>U|&EGHb;{TMKrZ(ltY3eNFkzWPV zIsHNIcazPSUdOWxn^O2(h)j29RU44!$a>}It+3onJNGEZK^U&w?sRuEkedDF8xh@U z8D$~d6l==OIXsg6`I2`3{(MpGSyHjg&r&%YX?{}LwBck?C>u+CXHq^;}03Ni?yYHQ*$pnfFEf16m z2-?@i$Ap2vx4XemT9qrYI%>hyOWx|nK^Rz58Rq3j-2yXA){MA^IdK_ctSK zedHBy^Q44o-+K1^!4)}02=?16I59yV)kccr<-Ml8T(B)@9KmkF?s`W^9OtV^-3t<2?2k{eYh01{A0cR7t6I|u8FSfpid zITtAMiV^j&zXASlOS)TCjz3y#4i=ziwJn(ZkUYgu%fc#q^+pXM8ViJK!d}{O7()-| znN9r|YtE`_vQx4pPuYp8O^H#rR<+T56g5-_AXeK`pTT~VWvXU+k{-)r?l`8gO z98*Xdj%|IJ>ed&8RJXp=8s^sm{hpW?{df(dNZJhQ%^UriHlDxlFatCunVjRT-dUQ* zxE4@b8iBQ2*pHZ5zhn=AZ3ut-#(*KY^V3pL84ZWHpXe6zxtIB{CUMQr-cwnv*cuR4 ze#tl;?6}pS?5BKRP0@km8V?K?iBV2U$xSd8&3G^)ZM}&M>i!+797S4 z&fwgtm>&%kY8-b0%t)#}7Wa?SYYSQD?xof-=fv->fZsW_D0eVDGCI)ni+O%mRU07X z{sX;aZ_k5w3Z56#^K)Cd=pXCWR9m-BFSne3WmfyAhM(knRnc{I+m>E^X2gjkL ztK_~%^R7R;eGe7c@EChp@v-060cP~QzdhCgbEM-&$#ru%T0g~|bF&NhnbZGM%cfQs zXUi$sf#%n4-UCvzo&k)im5-j{X`9g55B^K@UheT zHRF3{_g_E8b!RMZ${!pIY6u3`vjr^}(}p^l^506_+wBiYt5L&Q7-eu_3q|O5WtHV*K^5 zL_QbTSp{F0$$o3r19?%}cM0lMn_aoZUUmd*5*3Oy(=@qk2 zjJalOgJ3?N1oXqJ#&NXvxQd4HyISb(YC`wbFBlkTT9YJM3Vlj#=Kh3jiQ#Ej+~ciV z8bK23zhW+7?w|ht1u2#%=~DqenTGgK5>7-jc-9TPUG;yr+&T3QDh% zYk1H7t6l)nWj-ohNQgJS#5SNRn&D;MEW2NHWKoF29wLY`Jh}030vuZQ8{L(UN7csc z?4-Fv(#7HE>cfjxiWm#?MF`TSSxnL|0m0}lf#wAx$#z4G_zt&QYt)I```lH6s{zwX z;|PB!yN2?&mA8~+62-R_>!QskY}u`ePhc}Jh@LgD+Rtm{G{G? z9bzxLNRZW)A`GF<*U&h8G|e(}fz-h?<&YY_l#a3Z2v-niI~BV4n$%2+&i`9g|B0<6 z*$%%)M<1+FtNz2`3~nSy+0o~7!3{^64^eyC_lPav?#AtXN=Ck|E<0z?=#93oF07%U z#yS6%F>oZ`#|aHTB?d8P7v{%Rq6W;X%*k;3$=my|33TM_(bg&5b(07ZdE=Dreaag5 zJDw`|Y4jT;3|m+BN5)&p7Tz6&eQe9 zABal^90OMTW?C+$e|rAVFg)|L?R}HGOKXbi#_zg(2pbEIO_A?`qsTj+UHnI-N0|@N z=`-d=Wx>pi=0P0{XJ{(3jscjI1gf6DyLqopCu!WN#9P;u^_8Usv6My69~6$D)}Gr` z|Gug}JE@D{%24NIaQ#zVZ*kX)bo~_9u3piL#faD>F8dv|5?pq;f>_#AJq++UBshW@ z{|L&*A9^(f7Qd!>%`g!lax+g0$TNxuoAmb*pqtTp1dTt#CG@c1HyqTb;ot84svti- z!6G+NAY6+9T`2~b?uU9ojgGs2h>w4wce7Ryvfyu>ojVx@MO7=HS))jztk0ST2b!N=d-j-^kOK{hEwTpGZNcb_Qsk{#mGohDWc#pgQalzGjUY+Vd~71H5mq{`+87 zE~xGz4ijMWc3OO(+JrX2$)BN}i`w<{kbk^IC+WV%aPI?MBKejL5wu$bN6{5E<+)beg` z?v36c#O(IwVSA&;&ScwKo*vHv>tdA{Z2Sbs@(*gvN$e;&X@r|^#3~b{NpUjs;h%uh zuHIac+HbJn^d_|8y5KNRx)8UhLX2f8cs&gsS?S*Bi6*6bm>U1}ii^jY@<_cw>5Xw! zt>i$}PL#NC*!*i?qV&Rz{rEPox8#isKo{2`ehl?Stw9kiOOJmlaaAMJV&HHlI<^6z zhP2M_-IIU0Cc`qC$|-?)19k_zz9U2<+kPeUDk^!ajbl7C9Xgn(u-tkpH)fA=Gj}U@ zi~8MeUZv_6Tq!0qj<$<%3$ zoFIt2q75emR@??+f&($Y!5epXxXIYT%86G$JK~2BNJW{ji11g*O_FOfsaNYFJpo0s z{Zee2Zm@~vDkqu-Hqwmf$PQ4p_GCvMK$z${&N|jVFZ;chyli__o36F0g4*VA^_>!& z;i^i_5>9_wBS{^KF{DXv!o(`eo_70;_)4Z{CbR)-}zS$&ouD-r44Xr8G zh@Md^P}isn{MPbg(0g{FE}pi$h-d?hA_SPk0AEVrXdd7IAhX{K9iWv10KNUG0f6FH zH$bNk095nbiRBK^xdPP0^9v2o{*i2#{-D6jzMKq8g(!u$r^P#0ze?V^iB$$@3}H~> z;P#)X^DpZt3P%8y@RaEq1oqBx=u#XZ?aK0F=$a}h2%1O?KPiF&o*-o=l|8xGdoXX9 z!(gXlLlVDV=Mn39esF+j223^$rb2$t;m0sK&n_a23Y)WxI698gwDg}rAwuS;3V-+u ziU&ih++H*%?=$I|Bn~|LOJ?E%$~*oe-+^ecpFPjSw-bD5CyhhKzqvw}v%A{DDT|94pOdg!vZ4GJ&K3%-aD=t`>V+~QBX;7d|fYc-+3(EBJnGW67%)>Hg;_yc5iJ$8H2Y$ta3)K?UtLm`%b zXL?-w30VYBtNN&sgCZM7z0F#skdw6Jh9hyLtCdk})KIeofO8bgbxuPuUvEh#raeJ2 z#qW~5UJZC;yVs6Z8Y<-V$SfPRiN$!4L+ z=*414)a3a0nc$xSP*8HznNkFF(0k|~FRZjqQnN1T^ZntJfiRYVr#xXKI=-U%-L4Fs z+R~q1T=75Iv0dE`%j2!S$d1XOzGqlV;gQkfLF!L-?1k64$NRa*9fwkRO4fQuow5E< ztE|BfYP<^ZfBJY@L?1}Bzi8N*&KVWwAYMI@So$M-U_)9XOeckDRQI$j*I(nP=;U4Esg7WlRj4pQ#5RlA_$A_`bmPb`viYD>&SfxDQzv@PaFL zA)J#-6n=M*#gSaDZ=Pe$=^KP4#r+n1lgxb;P&(c65cknhrHB-Y&)pCBd%jPaVMp&j zRHFpBG&|#;!DWXrLl1ghC zRM5F(LO)IuZJFdAsGwS}l0I9P{pm?BQ(umhxSI}!G7xj=21RH-*Es6K2GW0peV3;;i> z7cZ4>7C5ySmWR*&st{PYbn&`EXuDiotYzcu?fFVPUp&l>91ZCCCtx=6FspRYsWmm+ zZwkC^{t4Rx?;ZbycLJ~1KY?)chgy#FvJ}mde^6gy|9Y$S&lsy}D-y#eSM>bZi!vaX zrtV_k+wRZu zP3~sOIciy#JCm6c>1DAlI8aiXXH%Y2TlsAWNRY34WHu-R3TX)Ls=AFQ=^#PceuOzQ z%(Yi(A3_5+I1TQslI2O~ymr~A1aFFgZisGbb+akD@pQ8}x@po)7~NpV%Ww8?w-ox! zgYg_g@hE%|6Lla*6)^(Xs{13zEiXUtwejxJ|fv#2KS?$17f=Y7Lnjo)~Ssx`;VWTT)B}MR{C_#XbQ4ApoL-e} zE{2p+{)l$Z?`8809an-cH&0~%#&mp@*$a&Q`#p`?n)h-Y;V0)d1Ypo+OTZ9GUvBdBzB4`evHEC@V_@B!nkZh13(Xe%V|3)y?Ow5{?KOmCV7}TQ24<=!95F75eA4B2wFQQ}Z@1If~(LkELTrpi6=1Ro!Qpmxy-!n3A ze^T)N-uZ~|Mo6?c$>;I9um!2TDiA!`kxxA+c?03&>Ii4!LCSu#a0Poo63ZyMlY0I{R_c z#ZBB^yp|}IH`|bPoWmV{4(DK7`tF<~o-(z5MFBUUMm7V%UuOU(18`RXe~mbrTFrwL z>=%qYZmSM?bj=!Hpf|?2t~PG!mjLPpJ`&tqZssS3I_A6ru(5#eE&{9KyW%OH4;Rm) zPwdwK{BJjdZY^NB5xY_Yd3HsE+qlo;tcbCXTkq$#18^2`+y4P)O#^VI>I|ey97uOr zivxyHt@h)rq&{dZ3P!&726)qUnG~9<5VsFCIo_@bBd!PZVwOo-a2VJ-zpV3HjHmxk zGds##cY1ah9cMbrR3_(| z9y5A-C^+8igD>|**g1pk181|#2pt&sEPK4nAV%DagW_2VEafVFE{7`3T?G4c!h5lw_Tf9y@v zY>RF%@;WYe6MCQ?*R`c!VgP#Ue3DM=4a0%W+WdUYO~-R*dN5QG!xcd*>R=J}M;=1U z`F`vT-a=Z4CvTFk3m6`E4pjL-j3f6`Ld22X7{oFr%amhhxrK%J7xKjkjsEdt<-P>b zGcTfXS>awu%SlRcsR%zuq5f4-l6g`>dqwUx#Uyn`-)(T;@fivmCmycx#-Fb+ z>y>IGpb^EsBZL93g9Hzg3_0OJcp8Gnr0n_&%V8d(4($hMuX@L>s+qAXxSGcQJk-jM z>V-KuuX?Eosa}Dkm@aPO#V{7QjLD?LwWZoeOuSwTa(cqTibTf< zgvV=14&m^a_ty)bknjJ1&(tbg_8`P^p?I@8?e9*ZO|d=CRt3?<09mPv2ToSnD_+06 za{yldSNdG|AOA1n^$1}9U+DA8evDV6Pb`^>FiegkRRhwaFkHzTAN@wG^J|FBC`_eqxl`qZBoX9sqg2=S=6mcf!OKZQqoT)|%3S9YewA!3CVT9SW zoH~I10C(@7FL9;+T_x{qwzX>;AtR@qyv9~+g~SEpWXXl)d8nEVs`jcjK9w{eDPkGF z`d0|sBIg8tbjbg&upLE(25UNd8O~T%XSN}TGqj&UoxqA2-@!U{Gj~^4wedlo1!mFM z!7*W!?4~a8 zYW+^0P`1pr)D!Vj{C;Tk5^F zi8`ey2qsq5`A1JEn?}Jz`>#RHgtE`swPO4xlpST)|FG*%+4Udnde;jo|0}!RZr6|5 zb&p-o1gPNuqV8SbqpGg<;Rz%V1e{O-<9*PmNxcODO&XC2ByvWF8U>V!X#6XP7c|NQ zuqXs)hH!d3npRQjD}8CzO1-qSHHt_e+%n)b)oQ7tAYM4ffTUOnEO8X&3iJkA3^n6i-zK>Jl2)`wP857{oQB(QXWtI{e6Zc4kZQB+VE?JjEt#{yDl# zZT8d08TOWY3TC0NIU{>>CyH}rlgy<)SvB|#ysEWuZ8=qVHE{ZYelp0Nf#ayb#3?<_ z9gG!oJ4i1|c{G6`T(h>n1@xeb=KOwT5Uz~Mf6GorwQbEHQ%#PnhUy?zgcQY~ydfr^;(6bq7 zTzYZ|PSwx11b-d9fF(H9P2UncZ7*~rts`Af6xy%^Khgn!J)*l4!97BJC2zk)+y7^C z0jTY?>|vCFmZ>$T60weUL_A#WD2=DI9}y1mK^B=5Ji2>$D!S7-XvY5-PmJx1IV=y~ zsz=s>qQLtxpH}BANSl$y)jsTXviJ!=J{>UbMN(0o;B|%0vYem4O9_N4Pdn}5X?q5m z>9U(~Xk(&kGafnR#K30jN5G%RH}!%EAp4F$4YhgxaFVYqXWu(r!*eD4-2=k=Ncidl z!i6kgWks4Nkm_GEtGyJfNczXri0v7=wRKe;dJ_|A$o`!eO2QaN%4;XP!1`bgBXO9l zK;jap1OdojCHawpF1V6nW%_)QFaNLI{2F3i{gKN#5*S55;|y6_@cyG9&|6Lzi9M>p z7r)Ytze#)-9Q-6}Im(x;{}h{pgP-6p@q^s>J0+_VFZQecXpve^b3W@C-`)X!>B;%b zK270)=^@wNA}|^$lS-cbO2&5|d!_Fi<(w7Y-imV0l5+5_DD4O+r`UeLOD8i1h-Ag@ zf=-!}vHo`GVr>NC_OI3YTj;sN8qd_{BZ%~K=wmHmx_1=6r^`K!DJT2NRsC&EXX5=p z1Q>?sFD z;|Fc0Qhz=a9@uQ@ipS6Yc2GQi;>V+Dfe(-Mb{jbLKnfh$=DR=t5Og?5{e2H!|KA_? z(P4PHemEQ)NQ#O7Nqp{j`=IoQ`0;tB-lYfj&&#vc+kb~S%sMjUomSg&st33E;I9BT zIPclUjfXxtxn%=bNMMUdn9)6;DL&y# zu)_lbE7RHXJTTt{^<*8=f*cb_4n0}TA2T^ieJxON3)EMGid(9_maCs;6}VY_)u;@X z`f68kxvNnon&sA+N)~%yi3f%~u*w4`cwn^yrli*#VpO3L2b}7FDZ$?ftn=a*rQ_KK zCw`d+ZcN9sIZpgG59~pDOF3%4pPB;TBVkYx|2@MGEqK+!&GLx+c!dHp<3ahVb^;M(3CaAA~ zDqmAy#p{pON*JjVtvNrOJXxd&BEqk0i z6zf@zI}}3GZ(Uu8^kZQ8?H)KXpic;CMibG%_7Kk7wS;lB9S0Guz4JMc@rF6J>wa`9 z;L?RsN-yy~+l*~SbwbN98O%GBV!u8TszY)SKDBs~eVxSxd^sFz3$i%hsJL?TD%?B! zJ|`QHqk*Y$3RDhHt_$9MLMOhBJl={QUJ6*cKA6DtA_$p%+kPk5!g%d-oh$l~ zr!pO6GYs_x&3v zE_>F8;t`oB{&OjtEhy@^|D2~m;iGZ#dk7XgIr@NS()(uO*~kG5QgTQ~^u3YNf4X>n z4kVX2WqvOtd;5|6@!=VCyx2qXqbEtn_hTmk$r3?Q>1z(2lM$ro`9A5{D14Jj)_CG% zmIE3Wz>bf51yjj7CnS}88e|8x1A1~9J0O*8@bWG4z$G5I%mbHuV6zLBU53$HT0~|@ zE&=bhh_rwke=12S%eq>UHH3hM69E1#2G}-7=pX+U@83L&WkS5;oM2)WmS^#fU@$S} zSgf=p3m1g(j+|hkH`YC7)wl7ly3Obn@8}Us{t}A?cj(aOrAA6zYR)oDB5<(rvL+Z0 z_D0S%ALK;Mp@*rVSdIt6zoZ=o3%20=bAgZ1NQC3xb_*saV0f8TFYsQ%`7x|S>YAiU zIMiz-Cx8@MX|f7Gv9m#iW(pqDT+YhPg`}@$Hj?f{iF|R*6XgDxK!%U^~Znu@7KSa zZK?=XSPF^F+cS~)ugYFKD-$@2@HQ4c7%a1hNm(PuLoo6AARKYTWe~U?+mXI>X`8W4 zT^vCfOyh>8OD_ArW$9F*xHCBy{(LlF$Is6WCcgR+*-#Z;>J;7`4iYSApoZtbNLW~# zUB(ptR%0lYpHKdB`D zMg8Ekg|h%g&Bv=54H4x;R2WkW5m~1~7dW9jM|vH`O?wma=sTkNr^NW*(jKY=>H>gdnLOAhk#XRR@=OoQFBF=*SW(n6!0oPQ6YmzFf;$IPg z2dW1fgY(B6k9r(&2kvoXiX&ktcaz-zb~}_O@f9sHDdA87@8UU4Ho}FDY=cCjzyqO@ zE1ln0oZpSk?|SFA+4+6e`CaDxR-yHsjm7v=2Ge26ga!w2On|eUkckOnbo|#_+g7Fw@B1XL?|}Jt$I)!}QNhHokTu>>7ECMzW#b+2P51>`8Sf|$CT=~G!0=$=tYQM^2NM%9 zf5$sU*B;AB))<+M>um6Xzz_xw3MLLi6_kf+h{~0WTdgyv!Kl>^69p<)`eM3&ds9`< z&{NbseChuEw{(?f*89YN0m$Y`i|;G`jR}X4{!pk1|#lPwwOq_<^VF%XNlX|OQ z`%`-z!ADzy@#zS|7?m;74GGh%_&6A+t5oY2Qhaz$u-`cxD08T(bWwjP=^PbUX)v-Y ze2~@S#$f#ikW3AD{|K_hovt1Euap<@Dsc1A2LhT%_?b!qQW|H_2P? zAzjpXhEZ5XF&FRQY>O6sh*fN44Yle$Kfr>zbZ0P;briezoxxlW)t#Y=O|@O zVW5Q-`kQ)wFnJiBVuWIvhaenD>v!Ub0c(AZI`1tv{Ono9IRYkitjVn(1$ppvhxMDj zpLg0@QJJha(h7GOG&+~$_Yd;va~CS^?kbyq3nxQT`_Qv8uFUQic13~PfJ4v9Kcf=w z%_YtPqkbQ*1j|(Z1#cyRCEVGvu#b#XhaUp<1&ocC}pFD(xc zZ~v0<-!gs<;|Fw!-{i$##rQGpzWiy9Jc7N|Mcu7&=B*nM_qFsP{8BV4t$S@nw#qvHoxUB6&&R4T=ZO;jHc*ZDp6g)dLGy7inVbqmi z=|uji!=7<1>_(!C>3=a~sS7k&j^bLF1+b!|UIu^$$yqLli~LPo5@z0p%NK>2$KZ&7 zEGRW0l z1Z?m=h_SexF~&0;E`nkgIv9fP{BcP9K7?TVzsK(kKYkVZz-qMy7Q#U_Yj4{?F$S6waJBlk zGy!TlCOD(1iiAdysW4`BtmeZd0ju;jNEv8PG!9NY1w2@~koe%mG89PzVUjH#+-Zyq zm6ukA!ogppY}^m5U_4n}L%P&t>y2D>853}G<0kBPoeuuTV^?^H6&qliK$qKuE)lG< zHjyrpE!LwP;nPRCQ-{%1LWHo$LQ#067!zizKp#K^eQa>gM|#w{@D`E-H#4P|FXj4A z3hdr!9`zf+Zz1smUkHA=x-1VtzuEyd?J#N|%sY2fcA?+jjZ*%J73JGEHXkTmgR2}p?d*Q7Y7$09~e;R`4V z1RuUTnZ6K>H|sz4EvP0urziWMJe!xsID7;V+zR>Pj{iJ;Ar%s1LUx5wjX@y70P;=* zdMddQaWZC;%*26NJJ6oi%Nd9NQceUD>(4@Q!Q=KYO)kc{0YKe@iNbzxrcQoT zJKGy`)k4BgtmLhj3ppuXfU_Nt`z7FEt!!}^Kj0t80iL{veT0pK-CN_?`$lOvOn$zT8Z!2`xIv#FDp!@MSI2z14aU&hR<_hif`H=xCEPyBHK*5!#3 z(9MtHl23@Dl9!Y>5TNxJeWK^c>c9Wotb~h_sW0w z0r{&Bk>ALLPrevKj>ir&uX#*Vf_>s0(6*mN9=-3q&udxs?*KNiz37S3zV{;tPsbt5 z%f23hMzY@{%T;8rmFyFl{YHTFPnY>96ii;htN}geLPn0kM|yt2JqMVwLPjy3vZk>z zIT`{OgEcV%zxuF2%y6t^2%}$s4@${nU{49G<0zDHj97PKa(V_j8%M`u(Dxx~*lTz= zr){85e*4a&mR;eW7x)?$R19Q+e-`qWU4^M(8i2Yv)E54PN)4&b>CU!1&NE*BEqdIowLcV#N8uKfW^0`GXiVE2$H@zVS7IM0KWd|M(h|@W$8t zDL|K=ATo#RR~2xXX=@)Mr*TnwJZ0%3n4M8&+?I_+eK~po!>hj?$NVXJ#$VIths$pF z_ZLz+B*wXDBrb=G33}`zf^~Nmu+NtJ`s^9@Y=!=1TR-7Lzr4=uw{rb;sCG^@ zql00duBIF|2TZz~2d_V{TCQ{YZ($e_2KtX^e1f%-+xEtMhnzn+P)>Kn<8|P{(5xyTvZTEOvN@GzCZpG{Szl*-p9ul`KZPIDL&rhN3sa^ zJcX{FvltoEY8hnyo0#$;IU>%&+O~8mi1Z@yGDA3D(u@ z)D}ElE}aEP1ZSh;fBX!(f7Qe3+%UN|f3MrLPD|IdV)f9y^`G(VL!NhY4cdDI^Hu|2+A|cFP*4rDXgf+=1 z=jFxL1{J`aEV<3tdR7HAqyrvR0X6A>Ur2!3)iatnbgk8wla7r_i4%!pJ*|S&`b?I5 z)-P4iGR{LrlXSp*6(m!W(KLZ&O_v}MsVI_amX#+#vWE_pbpkQLG8uOuor`;z9Dcyz zAv`q;0}O7ANugmo*AE1Hv?HJxiIA9f7*zz9*lhbIBtcj)%X2hgRV;GwtxP@$nJ(`U zDX&A7$I7KV$?KMPhbk{mm6z|KNesH;7dHNzmR_Ro>?BF7N4smM3}L@^mS$F{Jvl3guZn#*+~$H~ReiFTzHKW=`Hbkd(RfsE+Vn?{Ex-wSitmeSlr{i;s-eI4(hXX52#D&ej?|#Cex05FyxS?=4 zyFu7zZ=nR<5z)8c{deJM#Trm8KicfbVeYeT!v~iW zoAIj;d+kZA99{)@D3h~Vm$>_8u#0T9rc}Y6zWiORhlri)3KA@8qy%NqoV@s~LOg)g z#11gTd<-?M2bM=xH^;A_i(g4x9V>CvFJEirkj698k>XsERvPS)K)@jk_CK)K;R<{1 zc?^Vh196%Mf@9C)xL(-vXx+J|)9$U-*V3uMqGw!$`EIA6nF?ov9dJ9B!o+R1c)NA7 z*=yb*m3QOGHtWAv4-D8>c94}W$Tjkfs%$JImC_D|e!9H_tnOd$ie1s}CKZGa==bep_nBkR@0*yf zLGSysCoo3JLmZmmy9)j8lJYz(9IA-#&GLt_Kqp_=d&H{?m-;SQ)>2KWg zTG6%#{4>5dV%~+L$REJoIe4#C``6_X<+S{N=NWAuA)O`VyZwLN<6ZjyO+|0L?`4Rwr{NRbU4jmR&O-G_Zt^u7 zWRxq@v~oBEZtMwpIjR>}&c5VrG_y>$D_pPYm}7z7VLkK4cW$Jtca?nENNI<@=q`-D z08ME~=OT1Dx>V^6 zR>=?#WHb9{q|y8S@lkZza(Ght7*I_G_Lq?N5O?{g&b|a&y@00@Zo(cK;01&~!^i;K z02q!CRp@T(q$d&nt=-v~xluI$xw?#jizKr++ksc4jI_tJRTEHg7ZT(;-+tG#fFS^TdP=WQ$FB+fqG zaT~@2_r3171$|!Op3p(*!#&+gL7#(-MDc*w35Jf~lpS<|ud&eE_8fHqnfKoUXdk_w zL{$qvkK?e_=qR?}xT1|N`#Fany71n7n81X**#M&E>EJypIR9;SsgL*Gvc7ng_kAF0tS3+A0Xxm($z8LsP5`-(Nuf16U1!Bk+&wzI!4rDDe)NG0C4TmvfI z&ij^K@*D;&rd2ylbd?N-giY{bH=B3@B@30KVi{>72T(NG2LaeH+ zFdkpVtWYtL3bmw?1j!OJ!=MAzSx`Nlf5DgMV?I zM1}sCLZqsj?yN_uvg6I&-4)12CRW}w);ps-q@Hn*%0m@`6oKJ>q(TT#>YbSn(CAzC z2sDILvJf8`NR6jIkBiOI4mRE@2pB4wO!QUn(xe!LN%aIWXc9(%Lz5DKX_{#G$fzFf z4BwL`ulQ)vGSZ_-qeGJ_R3S(a$e>9z0vwu50BH0*^}mEBQ}L03)Mb8}IIQAZ8K!A6 z+E=|xlNuBz)f1TK?~*zMI5e3B(CEALVX1xrJ~FBYO?-Wl=8y{A?H-RsD)eVn_pAi@ zU6ysZqX7jADg=^#RF)vXL1hs@qwjGFm8JN|K;@v^(amF94-;b{UFuzCJ;R5NOPOUT zT+ktq!7a@QaL`!}FwHF+@sWYf_vaS>2wD_&X#_Ml@KHQO2>zuAf#3k71PAU^>+?{$ zm6vuCms4{y;weP%BUp)<0KKAm;794weL{mdL}&oW;KcxzOAaq~AkOIf&>~@mT!c&E zKA~|CX7C26u}~z6BP1F}xJ@)ffJ)`n5Ou1BwD$N~=!rqqKw3In=5|(q*m+UH$Y%ku zE=DbOzfI`wzFQuGLce1}TK_*#@gF3sS=vdc8?Apv>?F7^-g*I`y$6~;u!Zz2uxg!ZX80O>X8x!meXrD!B;QlXNY~C4^+_FaV7iQig`8Bw8B$J;InTi_22Lm9%INGOIA_pC z;j+MIO->_eKS($ZwxctZJ0xG=0&_?ThAs?l`N&wiNs%lnPYE2rz0^}qX*Gx zZa2BlK|F~wQ*4szcC^gxJ@mHQfqYdOPen^d-iH>bjvOfM$mqx05&7NyIQ&`Fj~DTX z5c^D#3_U{Hr+X9~NO6!Jz(XH&IwRfn^mv7qA(Q#P7?;vzdLxIoBW!eYIkH2AKoEO6 zm82LD$(Kr|N4_(ZZ@d6qo9B2AtNw5}2ZOq+m#ww3%pLfYZGJ~%iXG1as+oNr1peu{ z9Budc>_msVHXr3_J5I{+$mmBv*2v%mj`YwhdR+UnK1IbAF5D)ubY zD(|0^ugoGb7Gamw*Ydiu|nvf&leu%3`};Cu!Jih2p9CKhN$A`I@i@$3A5VHKySE|%W1Xd=UDWClMnWBP_*NAWaDMLw zM_X6pIT-8D?_kf{9Dd$JydU!gA5t}k7ZbJ>TtN6ez<5+D8XUbna?}1|`@eyZ|GNhe z_J9AK%N}#Mbp`VNTJpYyyq`vH`li?}#e}HdqgiKPvwctcZJHS$y6`$X;tT()7ml`b zNI>)!gB8&5I{A_lMfS*ao^^IFAN-V;-?l$+>qyy4ec|;!c&`us)Ca%s!3V_yfy%Hf z=s0V5duzJJb@p^0yef@h%3kD^Y+sBtBL7JuJme(z*`UF#=r_4r7-m7f{8K(|Ebg(9 zOM~(9C&c`61Zw)i2Kh1LBJYXO_3DkK!u%|$9?34X52yzd5+*k**{hxK97 z+J+zS800G}C1ib`1haO4$E+JxVur-M7kZ2Jepc_m7-KTd7fsF$Cg~n#zY76Le_%P@ zTDe3sCoQWSys5KMA=0!A<-s1TnZw?=N6V_ZU!F(=ljH-~WUKb({1MT)vA)i0(Oa#OLA?X3;UF2X=B$veMrxwWqZj(2xHGyJ=eQ8zV?>A>+A49DuhlK2EhB1&6j`)UPaZy0@k`e z^bTC{lSFfE&z77rum*R>Sg9bQU5mTHaAQ@(ys{tsId`LQ zHY;%h3c>T|!DKOjr9Fa)c1%rs*TYjTB>-aH1`#7h4?O17S%tUAnq*wXD#c$u$svcE zeR1ZaGu9U`2QdjQn_wuewgCsmpJDne%YxyZOj7-8wonyPy9#{NifG=n;=3;u=``aQ zYZ#IurnVi@!0$y;#D-^jk*M`UOHK>ABFlaZGpMpNr>yX`n0TL&01kMonBUK<@4Dd+ zB0Yv2&t=pNU!XpI#Zi`$enZPe4yay?7%N-kCUm~jNbge0QnnQ0Velq*?Bh&m7_351 zS16N+W+4DOJTQQG3DqYpp{PJCvp5Mg53KUQ2_9JOfm1!O#sg=i!OA*SXypRux54>c zB)_Zhg2i5kf)Gx^`$I_+&uj$>sE~+~s zB$*F%!4j+@CnaAB$z;fFhn_5C8rYWkgUh3wh~WGzbVEwS0D!>iF#f>-iaf+y^I_gM z7K(&0l;ozoUvK^XPB0vHp;6r!-G<>5>l~SA3^fCpG9Ovj|4Fc7vOuh_5_)DGzH~hB zgDY^A-`LN8CEE$BSh4j0K0rXCO?QKw!9d2C+z>UUEuga=?&3=z8AsDyuux*^6YsPi zBUdE8A%rBgA4}TgE8PBkTSiABGD)cQm0z|#f?i_U?M`S8WNPhQM$U!I^&Z$bGVVL8_lbjr1#+@8|V521`M{?XG z6@*1zwWrXO0SKV*`j8(pKzx3@&5z6Rf%6rnj88LX`=uuZjO+!vj0k%dh-TgQy3!k= z>e#14U#4Ri}l!ikj0#~C~8h#+5$}yvV@Tk1yOzS zB1|w#h__zZU>)}8@g(UYJiECZ*F83C$#;W^-BMm7n+m0xIpJf^)&>*rKsz)l;dpda ze#-^c z`WTUdVsqp&?pdyPDspN@xzVf-VH61V;j*&G_Gudvc}=Ld{u_Fl`V zd`xDu@pj;BOb4@b4eSw(lK#T^^IW~VmHlgWn54yhTDZckyb$L(&`~N#9|g^6R4er3 zj!5aNwZAij%s9czp#v<43?@PuVeouops_GO42{NI{8V)|a@c`I^YGQ#xEUXvjoa|A zb@U#FOHV+BlU8A+Lw%30(Zb(GB(gO151^vQwaG;WqxrQlN> zvBOAW^xZxO0t9ch*n?180}kF2c{J8VFTNV9HWq+gA##pNLxj?i#^ioL1k36zVD zTvuWZ`6;j{T})(Z;Xbhyv?eAp1z3~L#hn$Z<<<{pK`%*JzbS)J>HyV9o(M((nd*uP;rnx4iV@0B4B%u*m!J%U9=Os6_O?DM63*{uf-;izCKoC6CC+O9s z?}M9La^6I*1?KgA`c5q?1dLKSzXgO00c*q4pm_=qG0Axd!J7U^K^;GnyKv7A#U=RU zwRJoXu|v~8!#&+O0qd&Mh{_J@lv?F6d@lz$-Vc(DqP9Hpz`%g@P`juT%ACme?ZehM zOwCwHFJ3V?u!7?hTnH+o0elHcGy7)!@)TaO6iPN%1TTI}LI*cj>f0>iba-hmhjT;# zdN;ky#zlDMYPZ$x>Ej^+@S3e=R$;=qneWQyYNda{`@EQzNM8i-rRXS<;0PswshW

HIxv{lG-PeC2i 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

d2#u47FM;V;i)WeHr5YN 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

_D~z8HGN5+ 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ȳ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

utCOq|U3-TK`XYwY!2Tbu!cR1r#rS z@ReY8>_RmKTQlW&puBU4des$^(XnKtckCYZw$tYA{uB1irBNxyr*&qJY1=y^Z_pZ* z`oGW17{NxXjNQ#}gUXtfC}YQARw{_=GBYUuBg)e*R&wsDrR=jv!#c(XIj zgy*+&#S;>;m%80eOKxOpNS(y}euOU#Ked4fKvy*+o+Sv-kqxOqi!|3Oj01V_z3b?^ zUdr0Gg+!s3aQwYuf8BTFng(OgsT??yj^7ZkWc5gka9}5LRH!0?{jN%UuP@c;IL-j} zN5akp?6-|1$(QoBnZbi?Tw5>|9m}Dvb1KLAmswL$=1gL>I|JmHi#SS|PW(8|>`wmZ zeKxh}cPYOL&Zye;k7$ePOp$*S0B9Cs%}IkyPXC~w@s}7?Oe-ERW`?@oYZZsO&kZWP z!7B6*dk(uG?pDo;2o-)PgJdt>Hqzn8e{oM@!6R}QZWuko4cgq+HeFx+zpNx6?nf_B}n*Z#7&-`qGOXue2hkyI8 znV%=^@%88DK106J{CxM@YHCZ)&p*|FgZcUL(tkHU2LkZdoS$Vt23`OA`MC;4`S0iF z-Mjy%=I5ET|IOy-FIvC){Ootef7$%3z~?15KaczCf6e^-`)*%HSy8x4V>o{rTB;_*a^rx9_s0*Uz&6;A_jbFE9Fc^K;|QUweKo zJM2r&&)byo-_FmU2$KKE{M?)Nzv241@TpDWcud(YoLcMRWD>*{FZ?*e4+J{e&+CIF*ZksqrsOht z`?U^I<*WVcU(p2si^CX%U$@%p5`p1?VbpjKIGxsLw}8FZpGuC+d{HTH*MZw7tIKpROh?wvK0*QtY8xz(>prPd3s7;UDJnF3t~ zqcaY4^}Y0R0DWsvpP;2^9<+B+4tEs?tvYuT38GOElb{z7g{$9>?j6D8qK~peSv#x! zxktEmqM3GCr9)Zw!wiAFRWGQWd$#o z{;LQz?+@dmZSnR7PoYh)5L{->Drp35USrMaZ8YC*u=c!x=U}sMwoQ3xa)8}h@V@^P zU~5~xcMg(fT`=$LvGIl6v#@ddQ1iDiuUb>m`o@H-xf|n`!0RR2oI7&PA{XgR(h9}i z#4fjB4kK0Mg&u72;%koRy?`51z2fD5PsuFPHag4Sld}A!Zqy5aplbM!-^u6dmqSet z@k#qTI_b7z7K!nN_&vTHTb=$rUvl2_W$uldT!Tszf6zZQww_<56;VC^*|SzD{Zo2b@XVtYE&PA% z(w>UEEP_b?7^l8GavpJRZq?^;DRQN3z%?@I2>+SE2GnAegK5IO4-*yWk{4X zvv#J`6&C?d{Gyt}LsuND@i8_-BfkfhI+*1_RxovT71g#5);${F;GNdNo}Or}b?_iP zwGNh-6h9IDFyL}Q(Yajh0Hylh6ViF9b^fV~b-IdB!K6i%#w96Y?pl#wXK%$OLc9Mw zkK{OH2bF5~m+DnRBIuUb{V~PDMDr*b2sj*AHh%M|*;Kz4Yu*!|<}7Qzm3j(U^Y1jI z!vC+_=30}6i}8BZA8-p_3~}Pm7wU6%Kgjj}SK9~uU$m(`y%83ZlSd3cM+M&x=X(Y} zCnzlsKMN*i@$+{c!Ot$;9e!r%Rrm>TlkP(c{_DeYzLAm3bI-$h&cXNJv|n|XBGzcX z&GxHa|Bd?lU!l(rE&clX{B8)oT5i6iz3rtgTUwvLem|)6>+V;jFsgj+(qXJFzob zXWD9k3V$76q{4sx-pI^;dFKAeL+bmX@1QABB2TS9Fa)vSibQIiyBE23bnU4Yq_D~O z9joFkgILPyo8FAgjU+Fcq;23a4bAP*yS-FXWd#K_g;Tv=9Ia*Ed{Kt`Ly}@xo$vgK z-6~ndLZg2Ya;yLal_n7iMWe}t2mP7^#K)1r$1)|3vzI2b1*pjxdS~UA2x7JJ>;=y} zs%$2COy2XPTo%=k$ucwZ3@oNwUPpqOy-B~+H!G8Y=SC~$(r4s7&&+$iDeqY_qyDL$ zjEJb`7E5*MSuu|Ej0|T6jG+D5{07$^wgjN~w{>z-=2GPWB|g8B5GwQz>}xFjc9T6K z$RdqFy7DRi0LdogD>WIV^Q7~fh`Wr>`X>-q76BE-Dbs)bTNv3H91PyIB1Dd@jYDTe-rr zLXY}|PV_6Geks4EF>c&e3G4i~2P1(qL&ga;Wy)>t?34NI+zt2V33tPtXIx{In!6!A zQqIKt=5&|^!dOIwfBFu1(;PHY5|5D9kc^c{C+1#A;jGyIXfUEYlJVI)$vct5KT+vT z>!e#$JN(8LO4J_!Dvid4j)7+o6#KyRA*5yOO?Z-ys}h*bUjgIbDu3_CWQ$nj4TS%M znsi@>{D6^}Pteg?*MFq)Fl}Ow4Df?3;vufr(YjP8Cwnd>q)F+hI=`W043}n}2wu&f z0sDklE3D<5X>jU0?vnY}miU|tIDjm*lMU-kaT z+R!zN?n=S=EWLt1ne{^Bp?*b@7+U$|W~6AeA@QzB>g#g0kA~hjey#js z+s90fHixHu>TDmirBs&sgR_0C!(GL{8!@_}`mJaW=hwQ*U-?Tdnu|t~C_U{~WBnT2 zn*c;(TNc|t{b!!dPwRbu=3Z`GXOU#%ddwr)apgSBG?KsExUM3uhz8IOPh>n$Zpe07JS0zP5gc%eEuU3pRZxTD8%Q@*LA?>%xgQ~ z^ExP9_-qkC;Iq6CpEbQ4K9};@@Y%jJi_h|}g3n(*;_!)Qk>-i&lZRAhp5QrU7MlQ{ zuobiP1o+e<*b$#Q16dBAs!#ZA;rE;1bLES9_?*_cBR=Qx zbiUk3jg1lg0{2a%(`CJ8kx?4F7&6Luu?{UDXHH)Yd^`VE)d3HxPk2y!@QYAWQVFU4 zmu&|gj?0zL!$)6#|D7E^$_yX32KX2wd~h8$T?};}Cw$x+;A6t)@Ui=Mz7js}HQEQt|tVb!4n@kf2vKY!l(ZA}^z_9Vu5`h~Q|WKbzg4JT0+W*+T3`L0tw z-p5&f_F{Bx`53*VcK`5nDJqC&!8gvqqfGFkfBJA&@L_;H49NR1+di1JwI-8HmXUeS z6L_Y3Ci_jwOK%LG$v(p#ooqKVFUxhoGqT-HJR%C;VB{f*N1Fxi&EJl14w*tk1&F-= zhkIeP{3iwMR%MBL{#*RfcZ5I$PayK2wv@S*ErJg9t+U^KJ00;2Ot_ZPzO74X%;tH| zQrzCE>Kov{Sf-lYhOJWi`VU)*jC>^bRa<{oN~-g9H%O+2_DxA-So!t;+~d>s^iO4a zi(kyKhEVh0XiSGNED(xw`5t-=QpvB8d7RYYw_eHnK%|u5ed4cqe{mh}!Hmj?xH?a< z+GqiB&ab$W<6#WwQcz}x4TAYb(h4OZ?y)Vr)}LJ~`*LP7;&IBy^dN+nOtqXN?Yf|x zI#g@RpZ#_}c9{9dw>=Oz$SY@s=$b4TJ0Z&8rLSHZgO>s6-7m@+=AOJT_xyD3Sp_s= zs%m6fA9CExop)ZCOR)X_|F-wpKwlKx=PdlhKkGKN)H{9clDb1qDb52{a-Y6;%%7LV z`q%M$$M+G?TI4j%YKv%C>LpJb=Mi|4Q>$1IUiFf@`MpY^?B*9^YdE4YhRa4r=4~%j zAE4o%jMG8$3=w{7{RDpWJI;QqTbV3xcG1)rJ%E^zoSSQl?vm9ntp3INT-`?f@kOPh zMyvBpDs26mSIR^BuVe|%s|Ux_na1YKZd_64tGwh^UgB9coi6R{Lh~jYCx{XhQl-tl zKX{$}bZH+BOIv7L@N+EX>YMDRo;r>zZ9KFndLnD}Bnq=8demkIBLA*D@duT)DAY8a zzn1Lt;3X}YHJ<4sr7;=5$&zbI8wV7{PSw#C+n;n20DN>SYPFzn3Cq_!OFtaG!h1we zn4rM-*kr1E$QoH1^r1LS%Y=$M^tTR;B~H#s>g-ytJ!K+MQp;X(%3mRALRMI(9R>) z080N1D31x~)QE9wkKC#>x?SUi#a(3ZD2mxxD(f!2X*vL8IE?FrRh6N}(XZX7Zs)Gn zYa3&x!+cGR0rOj{ynY;n)+N*g+zbrm-y>&+{w;s1$L~J_qsf{YNI7<~;9py_RYj<& zlpAx3#PruUPT^mPv<=dw5;`*oY z7ysxwYOkUoZUp>eyyGA5MZ1F)FL`c}|7IsKkoUAtId}|_mGT+L`hCSfE`kK&GlY@W z!2tsqJdRhUuNcTJsvvnb3}hh$@N#sQEId|!lRj&|KVTpo;XCK#FNUw~3*a01W#H@d z#qhnk)fdC}*!RB}zFSrPU*K*8 ztG=u@bwP2M!*#v6s|f#)>LsDLW7LB_bPW>9LEQ4m_-wKClGqa0?o2hOU>CQL8ct1L z$xkHd!H>@_3s-ZH_MLEO$WuBow!4=+Umd`?uWP0MW`CUr?pldyrGC9 zxg0)qCJEkb{!Hb64pq+&U48-&^*RHq+j}1>C#!#DxiEZ+yagw*Znk=<^9T5mz9hy6 zdpO4wL>A7_CstI>L!L|}j-FlI*5%pxr}?Eo$j{nYL1x;%*KgX`u2{&xK863KQ<@^E zgZ6g-hsSl`;pF6wpW^l@xDI^ym%2XQ-3K1+6Q9qu<{R3heddVu6)&q^8rzcqj0Dgs zskDp0wPe?xQp>W<{j+4${n#Msw=Hfm+NXd&(b)@KXC42L7Q8U6U2xa`e#_f_lUt44 zdJlU^U5R)SZ!Pr_L)WKgp_66sXX{U@^SOLiL}o9Aq(mOSi9GP0dEz9axj|kIHCh)9 zpC^VBPl%vv3FCachv&nwBJ9K_q9(kH*xL5390b(i5BNJ{$cf?V6;WL1ERGWA%cF0y zE_iijXHYLz`(@dDu@$x=vmL9xvrf}^j$i6VHfvvDq^*6Tj;VdtY3Dw0u4g;Bt0YUz zoJkj)JAyWM<*qPOB(D~Kb6*Cs*pgA$6G!J-4DfCJ4dySCZS=;C?QW&E5I zZjsa6!>d!4auQ>aWV*3uYeIC5iO5?izwSwW+%bY zc@zs1y7mL&Wxtkp4=6W)656F)h16pCBCsedU&wJFX!+ub9p`O8RI;F z?s=)n6`JCy9*fqR05IG>4b`W3YuVVuX+3L`L&oU7y%C7Bl8;h75>ym_V`_d~YsHPT zbxq2zYrCMX#ERr;v z|DmA%^8ETAuunsYxjMXj?6lX5)@<{_*u-!u9x_f~56^qa08&_ggbl-=Va8`K>kWLqb`(9(lYkM3?`L=TO0WafkfJx_5D|*7QAD zINbZ{y!Z5xGZzS@6P6GsZ=s51ZQ zPuN;SqkJPPur-2L)ZT2@v-}$V|BK_X(dZJ2NifXRY4G?i0X~xZAQnZLC9yI!aWPHI z>q|v?nW{Eg;YhqD&z_)+duB2opP%tW%NU5A$v>}D0Ww!+GVhh2xwmDGE0g|t@dt)O zG)2+vywnRdNc9_`=2IwXszp9OmcYF5xV_+B<0qNs1g6v}E$WFb0iAtU&VGAp z4-Na(Dqr#6tYdol!}@H;BFWQS2p*s{M<)d?l03d zyve>#*ED!wdfqZH;ADXGw$zau#LW5=Nlh#bWJq1(0LRoGYbk6q)O0BbizLs(>2WOl z`=hq5&~b)epAb%t$C&;O&T3w0$g|?tWdXnT`y%(krT63%^lYkVjPy5!pCZAGz86vgq;^kwh|v>+mSH%_mp+la;_seCuOZ`_sW( zD9YULBK$HNIv*|dC;4}cqt4!7eM&tfM(X1ULzQ;0gv(tf`4{Ou)%oJ-vW+TIbEi@1 zUlvIqY$UtMcYOX^gXFsWTzi|B1;^?{f11fsWckJ_f(LIhzUEGVRnQdOD^GhSe>j(r;96gaq<$neRdWa=( z7<8e;U^F{m&iXgShIL#6XNKMVxW^4)T9E@tNJXF^CX4PL))s6-~EDa!TJkw6n28BH>m{vs|ML(kv@k zMZ>A76;k`5+H)!^xPh0@7^l3#4il#x23WKaJ0zz+5EHh3FB6Ky@2_kU(t+ONWDWZ& z+R@FN=%VnrBERCE5;CbUPX4GSb$BgRKV=leZPBz5NqNQL>Zd}}Peo{5Qc^rnxe?tX zm3^1gZB@*z090C=y!HfEqv{3GLu(V4>M`C@T${L7OLJ}Or6c*-o?gY*cbmGacU?F> zXh{*bLnZ735!;LTFePoSOFi~~UlQjGsYl@yY6DF-??cmh>d|HTmZyZHmwew)H8f8{ zI=Mp*i6p8ps3)T&`P8L-6b(Qg{cTl3^0Y-7tGtCN;CJEE4RD!J7ll(LO9+%Tp(uL4 zmmDA$ktyrZL{7g}eGVsgdM*3Yl_Nc57mvjf}=ObYfFVQhz^XZly&?M7py9IVI8 z);#vAUtk~@tA^Au3|DW?oi$>8=DrEcUABTSJ3CY+qbz85%AE*zGgn-Cy{TQ10uzSyd&DeCty zXIg4!lS%i1xK2U5@NoYVHqwkjT_`R(UYZ_n(smu)kUd8t_0x##Hm?bch1&FH+s5`Z z-s)L63u&f1R27c5_N{H4+M7MSwPf^C5OaM$<~t3v9Ub&9L-kA`5Pbrt3dPus`u#De zJhESDOtVwV24;!XUL20MV=RZawAWY9)69sbhs7`c41#%WSaR-XD=8-od7RZ4mb};A zz0`?zkf~)eB;!$oNX9_$e|V?pMTbwp$QUg)#I9Xz^ASa2=G!!Tz_~4;@a~X5!XCNT+Vw9y4#me z5m1Uk0y`p}G*kh=tQ)*W5=;D_lj2xas{dOe(w~u#M{J^_oP3*sr$q4d$-&cac|bgb z=HrNoO^Q-~9<(Q(#hi*jH4Ae29nt)iy4_QZPgXZ}&h*=in15u2;w75=Axmuh-9kyD z6iEY1)&W{fSxAwkA=i(+6l=PU44T}KJia_q{rXRbAuz=DiX=`iW9shBpg~|+eiJ*J zwa#6U=k}{&r6yJk0mJ?i-!)5fsX~3Z^&UyZlL@S{(*Izlwu=YD`mq6*5V>wJ7MC}p zzifMed0_O=Hcj%w4EWAPrRj_5s(dX*-?nJU;Bq9e)h)Jobh19F zS(<7cTn5qNbTOFYm4gTHzvt^xBb+W8s`xWv0Dne%q3*-@uE0*CJi4bH#(-`z^4#+| z*SxH;Tann3LqX;KrmJWomPdN~8K*sO&gke<^}CFYa=2z$sOcTe2AO|}O$O$5xRJ@& zw2L6CpHK9v?qdX5m;`%q02xk5%vb$4pnb+wQ$gvx}MWj_1-CdOB?lC|(n(W`@UZ5T%#9gFYr_ohKduS=VsW_l8h)1S`7yz2FF3cR>AmQ8hNTYbJuEe-UpS6A*zV(t zBo^4t%gNtzJ1;m)N6W+U&-lEHD%DXYIOmi8_u&!pLj@`bc}WS5Byp+fEAynGtiItD zF9=sZKQZYJ>{1UPg)Z5|u(_(=<%vpN?eg%3R!S=UXLt46fioh$CDu$d*l4m4=69^5 zMOdW_TShli#xiT)NX^7g1j!A>k3(>W5P3Hg*STk%8lOH-?WYb3IT++HZ2&y!PyV*w zp5B9E8GhQ3SQANnB7+C&!xgysq>K5QTW_Rq)y^nRw;5s9IDISK!ke4d51GF8gY0{d zpT32fMpBchV(?i(68&oq$FZfcTkz1LZ&# z6@jgOjBxKr?{~cThT>>XY&D^#!Bpx0a(61P<|%-tZJ8|t>AlpR6w2cCZagxlwtt=l z_YX6qy14a4#9ezL0@NEXQJg)SNNQ|Vo^7%3SJWC~(6=W^J|;c6eK_0^r5||$d!RzR zODywhOwzxZVQO^;H`#jk$aDpn%m9CZebTNK6i}!w?xHCaU(`24oPoZm#w^aM_Cx)> zo{~Cr5n4-SIjcjjKfH#b0Ub)_Lok8XP2&x?P*zN5TeFGcPUh;uZq>Y3%p~rjY}-8} zn8dyK5mbIcWm%Y*h8A}izyI3Vy%|-GeLwo_Zk!GF-`ptBF9LlzEr9C#=c=dAuHJqw z4dr%k5#_gbgzSklJX^iz-&AiOEnh_bpDqtB1W28DI3Xd8_(xfd_`YYH-rMJJ$u-)= z^7rHsfovK>uGby(nyi;boW2ceaCE2PapRJXkT_F#T$Y}m{cd>8%XA!H#K*166|T#r zuFD_j5P-Uz>hXt_!kc<-n@TuUoaO}=6PV%a{FyU2eR>F8WWG45?B;B{?)`!e z?~6OUKb-g8cB?;iXHb_qtoS+>j`__w{hTnck9nzMxZ*ak@gZ=?2xAF`AMPn}{F7hX z7IKxgkb!IEC^@QR<_FQsqc8MpPaauOTaCtD6S|?b?U5Y2$i}y^yFD;NnYsDdjA1^K z`~fD|H$zRGrGG%lh#K59EV<=CQX&J#;J02aif*m31rZZNg_k_OZy3+*3az0PVMvw> z8khtasVw5eKiKl()`wcsykw_vW-Zl2Iep{g2%?CLhP9%2dbwjnVJtCPwfk5_U?-Xc zSP&b~{S*q@SE1y&zy%ZgjnI>sMB##ez&4{JsJ0DiMT9^`;b z5M8}fCQt{z1|TNDZ~}95B4%I;#2o<9&YZOSe_~9j$sm-ATtk?cNC9-sa;|!4IB@uJ znbLKQoI}{Zg5&n}B9r-qt?K6JuH zoPZ)72HkH%olJMj(z(|v`YlR7jnJlf_&GS|@Ii2TiDSPMoIfYO!D-^Bti=dal}_eq zZQD2VaCqk7sLaFh{+c&51grgVuOdZt`iMWTkRo1lqEbHikUxXoRGwG;MfL^O)$Tv+ zvt&J^k9P*y_g3}=O1UwpvWHUcBE_|~Fay!zwq7n}aofR}hl)&|tNbUd_^WL*tv$hZ z!)H6=?;F&5gAhbc|nBpg*^&v}oiGxtU zG@o^6iTN$+mbKO)r5YX4M`!z-o1NJ3m{SPs`cn}}4ldKTDrMJsr>3~dC0;{A76N~- z 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%0De?{3uc#4CN9$!J}3w-ipz$%0*W9ON_u zuk1L8TSw3z7tdcfh^~frOL2+k6IwytATH1p8HdAxv#<|;agubStnfdJ>8vZ9j`H^n zIa@FJ_V(WRvclJ-w&_YdSZG4`r*qF5Z{CXf#9itAwsSZAzT3k(Qmj0_pgC@qQF+AF z=9E9aDs=>k0z3NDbKPKIVXlg(xhqoG9rirT&Sr%}W@4!gKKDv);;ZKh&4pn5XiYi0V-wz#$i1U6L;m4>?p07LF8`@sQeZ}!nW=85A~NRs ziu!z+ptvy$@n4*m=@twdqM6j7OU2n^Hn@YPUvLm1noXGa3$y8iEt*ZmZsYoU{U{B= zk+3AmyTKC|8!r|T?mly<1hzNsVU7Tv>x*Zzd77L1TwF`O$*?rdX=PiL)8=kP@Uy{$ zW%?C}FYIG!%F5r=(M9rpvgsfEX-guz2P;j)n@0S)&jGgHpROu4L~z;oh3f#{m8Y_Q z^Qk~j5hXYIXU|UF9dGMcpp_#A5LHH736H_k7Bg(Nl|BbxnKqh=X+K(P^2_*|_MYG} zyxZ;R4o#pY@AW3{0SMw2UOKCy{xta)a?tDTmMKksD_VdJFsC1wzX*sHG8HG6s}RRF z1dlB>`S4H_1PNSi$s_Wr7dM{rkgUt+{7%7BXaAaTCsdCY4Ro6J9W1^9XEO341JgI) z(!PO}s(r4diHoL)+WG==Tr8s=2$=xF8{ATDtYAElaCe`cr7Wd}yb#NW$9Izt@%G7S z0zgk{EtS6vmx;WHKAq2P)Q9}Q%CGGws!(F^PxGnDDK91U<1Z4f$<`B!_9oRC4{uv9 zN|L|uzai#Q8Kpt*;@1OzaE17F-E7PDswJ07C|=Y^B%^H(_I^}tE)o~@9L#xJ@R+1S z8Qq8t@?&qr>dZE4B4OCM`A=fF)`5cg>n#Cj=C6l&N-P~PKf2X$p^cd~7rfj^>K*nF z!mXlfMjQe;x4{k4`2+$rK)`&^uu9O`QZfl*I3xJg21_u$karxw{6_X@6H|2y^Qd+; zhUmL_t`Z|Q!vY^KRCUdMr5$W}VtwB+o5Hhmj+Pl@Wx=b91+MOMCm}5p@xJy%@UZ1_hPWmT~fB)2j8TY@D17_zSijP9)tbu z@PnQGt=>;~E9?^a{kFk`w}toO?k9Q{9}jn@2=xlo@CW>{)adV_5SxHQjGo!e-haT;oqb0w8x%%@c3tYoP}<~sqN&bMQWq< zSG21k7i{|R7uipKMAuABdCpsop|>>yky?tF2w5 z4l=75hqiVbXl|$`P18A%?MpJ^$udS!PHB@DH_8TPghlcok#GJEQ^e56C`F=aA1vJN zEmA@zxi?8(6jw8zF?k_Xb1%~LBuvCojMeJ9ri$v_$PwGR+@kHhG_0e+Z(l}Trrnv+ zhr0K~-2E#Wy^n&k{*0D)*?CA7-gD+{W-BfY8k|ld_Sqm?(K~;2r0A_V)bzZN!(CP{ z+TL%SvbP-H2q}Ayd8_LxRwpyh^-2z53v?^W-qTDISr(+nZLLgXR_)C}sTGKd|2jyb zBDXUv#l_W@ObLrEpG8XSwH;0&I1mcLpZUcb!L zKZHPsG>H=QNZ2L9rqlR}svejYKMt}7Y5LjVn&Q{4wZycnSO<(WzOSe&-v3SG>seip zs$z}rqd91NT?c+jjqfJS5HBy8bq9@aW2Eu%UBVxJCI6MWcda$KM6=dueOjjhilquBat>oD~>&VG~C z?8MRhQ3qQg4#L+>GONuER(n06!|L`-`TI><9#G-QlQQdf!r;w%nzG{z`W@VYs1XTiQuW9eca_%d$&&b z6RV@a@f@3TJRFNvV`H_~SUs%6t>PxijkvAF@KQkBX;>i25V!f#a>N}*2BM->OYO;z z;EqJFB}ovRer#)rHsW_;t1=3}US%_}jEhAa(Qz^}%T1Anibk*DH= z8~J_EPnmX2o{Py?AkQ+oB-Fnn&9D9B+Z17JMKKDtByx8-6pWW(lE`{0j6Kwr zKwI%Ra7I{#e4l^Kj;x#rV_IK>vwo-bZ>o`y){}Sz@Zxn)D-pXCBhH20A zznv|I%PW?<)o`m@4M&0i8IyCzXzp}d&^o7-ENIqn{k^3NnzLsjL+spi$r86nUxGUa z#cPS_SGzW0z6r-gj zfY`}`#*oAlsRb>3@)SrpIjjaX*`=Y6K!{Bc)(4|ZsBZQ_Vh{eA{; zjwkG0S~HBbXSbUBpIurr^XSi_9%IVmn^VhQ?*n7_`UQli=rtW*A#OpICsbsjy2&x= zf{7|ZYH;1Ym~!5Gzr>sT0-RD`PYBSenv=--@q>JFa z-!RwuPcEHnN{8`F!;3YLc&gQVD|R{68oY&?m!D>;-72P|sdl5;$jfENwr_TyYSC^1 zFq!aJHp!e=S(F^7Iax9|e&DB^XeSv6|2N5T4Z21&(PGK*{uU(12lt7AaMNm+dj+ov zAv>LDwg`V3k@0le3Lo8~<*bgls9`@I3-in3qlQni7&^JlEC^WR!(WjU+Zn*G1~6=X zS7~i^*8f{E~C#VpCU_wZ<>hP*f4u6qC^b{yCh3HjgDtsZ^44ehi`B=T&g%*S=^qK9ESw1VYu^)VK(O$n; z`5Zn;FWT!5FxjI``EB(aIPE-})ssx|^~8d-=0_2y2%lowXLyX94etYA`iDJuQzDxr zYF31unBV6&exCT5dqWe$adFY85TAo5H~!vQ0`TpoO})=-$&cx>&At|_&6)LE8hl$Z zz2#ht?R8-DI3eEOTXV@fEMrC@Ni$W~L9?u@F8d1?fqBEABVR zvV=IMdF``7QA{>v7WrMvoO|s>JbJ%%kX(2^i!2*~miBKKX!V;LD=S-e&s;T_$XWN= z5aqTAD9>y}7?@=?LJn{fev#AlB0J6^CE^>@ip^;BcMp;MufMbZF&~4GKPj=Vc3@S- zy|A|cF#GBd+JedNJKy#LF$~b6TbCn~^HtMjUJZIaY(2K-bsqI40vNlQ89Z~o%z1gI zEE>L}PBrCQ+30!~g8*cnxoS|4Ifo|CsjtICB+})qQ@q{fPaae|X|VTPqN#bX*F0Fr zK(c!je$m!GovfH$R`9a(!c^zRxO}fHTtJBeKe~~-df`V3?YBI5sN^Y<#yeO zJ^aeDy7^WpFBVtiXRE_uSlUb9FE`$1aiblxyN-luE`D^Ff)ZVO5cPTP#nlfDiS9{m zv;2kjwz&36_@s_jFW4Ep`+{KXN@=eyo30NT{0c(^pTbTu4!g&zMZW0RK&&g`Ghl|kLVm6lP z*qM%({Ri@7`RbS(7i)WFY>2GB<;Di=?Xov~>)N8d!NhUDS9lvJ;J_N8@x&tWQhI_> ziyc)?T~KXPIZa$tCb=EU?I-Vmy@5oiNX z_z$gHY=%et8ME!}(i}2Zm@OE0d781%LVuEQS}6DQ)TmsveYFsUk>e93Mhi|ZhHP+A z#^zti_KOJ^LG}V|=i4(}&N01`ofvUnPKM~D<@Xnr?!+i(NH|+nEW&}fUkRX#gtsT_ zB3m$@*%qFwO}ZEllbVCzBt($a<~N~1r@gh51l}rA*GHtlV-P(`7TtXSMgznyVW+`RmVpRy^Yjr3$MU@hNBKg6{D;zoKj7C}HXqG%(1 zgSNve+hm~Zn!Ps=t`pc!TpQQx*u^du-RN;@#0(^;XdNtVENHkfW1wUPwMp}{HA#7vo5yl%3j;{YZ*b0web~-3V zR5&P*vQX!CH^Uxo>WQVz2FNSNDMFM(QKo$)Wm(h|q zn}9QSiqKJ`XRV8wWWT|>Vt%r2-d%fxw#MDOLr{TGH&HulcH){>3nI%JH^NojMHa~^ zO@8BSt5|W-z=uW@?k*}#dw1MyZ)S^t_(c=|D0DY@|77$X`rg} zH$|o~%ZSd2cWx)E$$O=VW1_x6h|7e!5*2Rnqpmo-u?f#NydZQkWX9m8T0)Gz2b7O# zOXf)Di|`B!pt_Hbg(?dEx=?txXRxI&&c*oMl)G6ras7jIg%@?`N<4Y-kVtsiUNnc) z;N01WfcfN5JcNl5QVrsbXR=Y49U>bd#~nx$+vIU5j~WoJzL~DW5cR9a)ln0}c@f; zt+VpivRm`YlqtN7H?OE{!2#QFm(wp=aS9iiJ3c~yc zM&3IU+c$;J@(q`buC?BZ@N2B3ETPY#u$#SlcdFloy*m6XoTNB`zK#?J2_%oWlqFJk z6Uc%gdk;toz6#3|Ir0pA&8v+aiL_Q5yU~-%`nLMwxVesuF2<~dg^m0%7HM!H5pmLJ zDUEPJG>w#^zPtTJx7c>hmi(B^kE_beuIl1Isl04P|LrtextisDmGjju88}bk_+GRk zaXegp5S!-3LK*1-X^hwVm%R-36jz|cMI6vW*1~gp66|j#mdtT>H*2ei!7I2|4?iAQ!I` zC!=A+;+gkS*W(`1oqL4?BnU~O%xXp;Q4ZgUM8+f#D+t~@yC9Llp_IfIqwrD}87G)M ztm6pp{%{Au>`CaT2{(Y;=V%b_9bSvMfhklX{#niOB%b5iv~e!9sdZ{EqMEyGp8Qpfy!xE^uNNXqC zIoUpZ8}_`k`5RZ-H2FOVg>4%rh}O%kv0T=dpnqi>H06>bXB5rX<^a)&foYsr?M z-Q_3oSYD6)7#rI%Iv?BQ^c?<%bf0Z~+r;-0=tqdVyKM_=*DE9QIegcsAg<>(s0D@j zDtWxqtUVVm(HPcqk?zn?^QD;i6TQKAhQMoI4<671pnM-5bylWjdxsveTuJUB06dz~g z{8D%n!v~iM@3SefF+7}haaO=Vj)F*g)l18R0`1x#Hu_J#k`_OJ5 z2whOJ^!%1Iv9s@zIgS@lh@a2NmXGn_s?-p9w(6oo+;%c*um2~R$=FC53BX>yH-C5V z;OYrSbK>{EWPHz$sUrA}v#ceG=FaSGd_+gp>27#Pl@Q>YTcS?Xp(!;RN^;fui41OD zZm>(#Hyln|_hmSDt714iKH5+c??EERn(vvtElEy^LuPNwsx7m(={`l)e2()*(#XQ7 zW@EYj617Ua?;2-yyLPF7YmXqR6bz%ptdVOai9aIV^dzLZ`9#H^Prvl9|IFUMuL1$a)$VaNC2w>+D15oYYyy=9C z)u(cQU+uO;_99j0JMVdaLNmfz75U`xF2v+}N9MmCQ_-LNjv@CG9s%|TOH z)yfwBkRrtsiI8cXkm$%XJcUW=&a3(6u_U9VjeNMTeE}Ya;u1ZKvr0#BX4t5rL=W3U zJM|KBT+n>y{8b>Q*_;(ecrwZ!F}^hQ#&v{+?A3y?U&l~?hYj1B<6n2db;BAxj>@=N`aqo-59UY7;l)UO93^-GAAsW5v! zgOlQ{cXiTlY}13ZFJTyGt`w(}t5-F)iHs{rqIC_Bat$sV6=ctOdQ3j}K?=A0OL|r! z`1=DIKu72~-5-(2R?=crp&cw-6`Tr_;8Qf-2u?Io?~Si(s(nkU*BimYlS^3HR18Js z?3S2&d0X$_2rjs~gc9*O6wi7_;-gVw6HoB0eQL^?t8*8hq)xl}TEC2E)2P_N)>3k} z-p{8ew!A>{Fgh`cV2_)-y3U&79N(&xa6U!pox`tGsb^0T?2bhjc=H36xMUpiK(_Flvd2kjP4r$Hq69>;x_%l$@>#cGp~RB>poj zzJfjY=FimIxF7eWIjN~5r|QajQGuMI*9g?YImZ_E*CKdFYzN+?z>IFcz%F)$7G}Rb zGR4lQ^!Fs*DaSCA9HA$)>PNk~_%3nXU#UA2_LMTDOGcCV^q$o*7+ln(OJW@Vm#GUN zTILq+!)1~_g_zJTs_=aQ<-_D=WgtCSAB5fDO zxBGhCIpM!ruQ_bLA=r{Dt+H8FZbg_cUpK=m7|m()H>T||$!LyBf`qEC152X6qTG0+ zO|I~EFi@nw0>0{%tozs-+fZI$vmt)9XfK4!bLz{Vo80;6-|`#2YbdJ&tuek``Qm%0 z!3Vz*M-B&)zxZvsuvsYCn+(DGZR)g%hGB!*R5yn1+6tHEme7r1hHtSDUpTFCOq_x- zr-ZY0rFX*F-c%yu`(M6evc5(Psju~o&NpS>P(@|uGqx*I=mbRi!I*A#5ZJFVO$v|j zhGhQw+l6t3a(q}JEu#4?OM%7TAvCG(fGUaq1C!<&sNIvbmvaSFFBAC9^COkN?-r^; z+L#IslgKzM*NjeZ(C;)LxD)noP3c}A5N7u|*@aJ9R8#q1s3ggo^po%Z0z{#Fx)Dki zu%`TZ)$x?h58Nn{(OCjTPZIykbPUz)S}Fe~N#oHaTLUzGD&kAF;DDM<`FT3r3g-nj zJYJbF^VTSrmy-v?<}sDkiR@*{2usfQKX}%D75A{JRt(!5>>l7$MqH-99Mcy|=oWfN zZKE3+0R=%x9nM=SZvk&9pNoD<%v$j-$uWS;N=Zx^bE2FH`WlHylzWp#gHx^U*$gcz z#T^obsm*@e4ij%W%*l^YqC0j7ctEm9AZU*T5;^Fs?XLa&!0b59uA+Hy1??3sO9GBA zuU#GIwxKT?#QD*eG^m8uH!Nd&vo%@*?QMvj{t^{10pmF-Grzpw&F3=hA^Um3ROVj} zrYF%y7uc2_MA&!ArC~q3iV2nW+3j)qQxOGGr-caVko9|#?2_BtoHeWqUFcHo) z0?0(yT&}S+5oI-KqAT&{g}<;AlfGdA^qI@W^|w=zQ>P_(j%rs{B#!e^CM~J|ketpA zsmW9=vR4c3aQvt_bLj z9jn2J8VxxSCD!z0Cunb#)*^7&=jfU1ML5EWj^0>-UVQtT&IqwK3;{w{ixC=mH#0Bj z3bK`Y`5|9Kb=mL7l$sxS#uyv-!8WlTGv8sZbk!k^ym{ReMx)(2_&H`_x|&5?SrzfuMU4v>M@}22`R-2x;*~h_|RpIkfL}Yo+CFkFcVq(X`kdvO2swDoE(;%Py7e zeob_7SYBzWeLX(ox{r3hz_aoP)2@hjz(Uij(nDU}$qv|`Z3pc4DOg;z`(@n+I#uFy zDFe_q=%7FY#y4Qlrv{U&<>DRmsg|!fUFw;nmv{Zkt2^{X1%ih!7jo?xbm&X2Ls~Nr zt^23FHOV1sTri%*^^b_7jpk3=m6g)B?&HzvTdk8>%ky~Kddt&Z9sPE*W|WO=B73y< z9=&>{JHe-3N|#W-y1tG|;7KP{JDtM1sq}!b)#5ZhiLz|@4|_-xf{imF#p1nf zJRck_5iWwmzc0{i_Hr`#oihRtDIwyzMV9;Tu_g)`XefCUgb%-DiEahK_=?_9ejeE3 zyJ0}IRyXLpl|*1nIZDXiTE(mnj+i10x`o?cq7_?KfXF0IRc?%JO3c7F{DE{dNHBIq_Yc*2&g)%Skw$8>42;!z2T+>mynzo#38nr@%;N_)6 z2!be{jOAVJg57{1eGQJV_Lhjmjo{Hzcnli2vWhHL59R@qU<=V|5ts=%cRPt08}=S< zI!ZBDEGxffuES*L#ECtTajrd|NfHiVN5n0}Z+-w!?CDb+fu!X!7140x^G6V=Q+<$sh0xN4#kxDKx}kYhH@O)_4~Y zwq_MQDTb{%7WzCkQTyHwVQc8O%}Q@I;#F`WC3hLQ=4>5gEsb0gY?`_^{MwF>6H-92 zYJvlPuGIoT%~I3(<{5xl2bfK@PbRXv^^b*1qKHLn{SMOQXSxNdNo1$v84=w1b+q2I zc#<%ZTVDk*jvT^fr`n1cYkKjcCLhkP7es6xMy$gP+M*)goSp@^SU}f^0lR$a$*orr)_CI`xh?K(l6zaLw_PkSmJ{kzPGWB;XAfd~+!_%>lJsEoW^uP% zv+`)9-;z||J18TuC_iEl7vVVxFF^i&9L^`)p~H&%T%Awtp^8kNMs+u z7@J$gg|Xl}=D!gExkNBYV`rDho}5Ev6q(Le$JwL@*L+~hcR&U4@_oLzZ>D`Ptc~US zn{;EfEzpL?eETwrMT>O9{PqfsiPvs3#x3zp$PBFSs7RtR?Cb&#+`FG>;VE_R1jIIh z7;##hmh7$jI{w1HKX%JDy;5ia6F7^3<5 z?>c}SEK+c?mhU=_Tyn@dllHP-mYf4~Jj`36!zG!MaP3tPswwz3!}Q%+p4hyCwOmPJ zUtokVUz(s5!Ig*ow`+NH{&I&ET}LfvQ%R6~Nkq4#QYhBS>dvsaQ;rp6C@iV1S_b|n zt9c>j7_OLRola!mpnu)g_4coW@|7=s%60uElS%UHYl3U!Tg+YH zfDOS6xkC_q#_-T>P2UGsIVPs)Ib(#(nl82yuj#g)H~4#VNGe*@ze!eZ6L!SyFEK`w zF0Tx+@MbcCw`$tziZh3J|fe~cYS z*gQ32a*H9rxv-#BqDA=9aNwvXZXy9kh*YoMHKl?CnwUD3 z$iB?*1=GID;-^r`$@>jB^Iax<**$^_i#f=M=FEr}m*Km7)AJv9g|8tFV6E8;aYGD} z(sp@Q>IlWvj+V3xsW4b|Uhz3gIVLiHy|H%Vo8?1ZbcFxIMq7|19@>Jmz-FH!%KZS3 zB^Oan?UgS>F3?1A8IpdXka90R$Ju&sD@(G8MGrA98bj^KzmM2t5gOkt3evwY`?jLU z$9+;skiKR}3zvNFhq~%cCV{9$H>+|Sh8-s7raM&Q+a$ZS&DcH1xv&z&FX#Yj4 zR;4OsvfEE`w(UVpWNfiaR8kl-i-sI$DooefY9y~C#YO8#AeJrL%WHT_L%HN9%p305 z5K0GWMWfSn91YpUU(W}XP9?^(*S$h>nD}b)ynaz9fG85YF_ zko6dpiE@1(4bA004EUSo{7hT?DencdL;aBaYPrc&+sQTr;bu2lt!pXE`TFsFm@kU_8iK z@7337xHNyj9{Qd1tUAKZIuHv~_F*uZAl^jGQMX-aH#m}H>u45BtaEP$QncL5bn^#v zY~pxl9wO^Y@BJ}}UG2lQToFT5QfF0g<{W3sX_A7TF)4-n4&2UoulcU(GLc@v-Zo;# zaR8b9hWbVVg0pzqT2y}$a*BFd#@5H#%lSEl` zQVNJ)7Uu2Sh7M<8E6fXIWIOLroE2`AoUPZ7X2l`K=-sSv0-VBzdt6?*NGlvO#RLeO z5gICVu_o`S|G^v}=6GI=GI;e{ngjOyznKFVWHiZ|K!S1YhPA^hn7XC3@sBwU1rGT^c51gx^zMH=)sH zG=`KMY(EP{HB#75M$QNi(2LO7DIp}zvqZSUP8%GpJZ86Yh5>R^^NCHI*S^v%L}H0W zptGHEjv9@!FZnOBy9F*kx^~096~TLtGL-qzN59JRJKXWd^TA4gj&y&Idbu?Z*j5i- zwuS$R!z`!G2W+3F_wC^ga-hwRe*YD+->IUmJK>p}iVaRV+Y;2V@aG(GP+k3Mxjy;Ox~QrH5bsp8(-!|&JuP$RYQyHutZxMsh0VsTM>@BK73rdHZ{gz$e58ae2?ewG8YLnYE+{GC zc2eEdthev1&<|ar*dNg!Tkp{z?#QVx+*#2{#iFs8n43jCq5c$I1q<=?WB&y1yyBFG zg`m$3Q3!guDFi6uJGd#x-AN%}?CkB?TCGaLj`d$QSb9(~cvG`7LAZGm97l%4(r@(j9-g0{4Mi+}rFR=AeXo;aUA#%2A9#{V$-6+F6D3D+vcDi3 zVo7Hmen%<)Z#B1AF(NBXWh%8c+15-6yg%l1SvH(+9}r_aqdaHJon}fh>sM4+%bm`9 zLO>>cI(5KXt}EuBo#!A|?Q_5f$|5hBOwfltnVHyN%eLWbs)doVZTP}@aCT5>GSo>( z`m=fL?p2ALb6{#)r;g@KnCn;r)r!SMekJMJ8Kr(DNuoQbzw#|f*It5e$BjZ4RUkL1w*0^zhB_T)cF}|=_laC+^Y`9DKeb!azjk8e z=MmJbG8=;{+W%CVff};&&y^Ode{C|y?hWIoSPE4AIVCXzbt|? z{DQ$bKZf%Lvmr(i?zA>dx5M^qD)=+1hCO7Kix>?<+@0^Q!f-bg}Bg6$$2lN$RDLH8l9SHLbz&iavG34cE zy?aw)sN*(8CR@U2py!_(EfxvnKuwtZ6c7(v9I>F0MM#^imCeH3eUtouG==id`t zXs@&U%{E}vRk;>nH zi^e}=K2vzh->TH$oeZC}(_LVvtzR$cVOMj^N~f3SN@NujJ!YrjMqaw{yUvc{IIazM zE6}YQ=YLUP=iiZQ9Zx6&+shfa#~HX~i`7!apDbCd4%ov{yPFHl%IC>Y7?S><92gG? z9*{Hm6b(rt`-ISA!}e?;55x9lCacRs_}@QS9yc`T$rXP9ZQ*K%soGFfS?iNM~I!pqqg`KWy+gi6I2|toj$tp#E$IGbF1nB^F{Xxv3!Ig zj7AyEwQv$VXr2vSzW*3<>4*VzntAm&w3WntL57#AciP_P;)w}ddh*I=_Sg_$Am~8o6(?utxmAUnCtO>Dg{OTSD5m!Bat|Z+IJ?Bdo|1;BaME0dY*- zkc}H3ZgG`ZLko7@x$HD7=3^q8rIFYUSpA!Usx6poVu-Z`&eeCNj<-}AS!mXZyxl9p zn~Z(tgdY18+@v1vbOXdZ2zGR7CJr(vp{NL~*sZS9T?f4U zNK)8NBRC6)l>ZAc5;8b_zvy1Ec`O0B_QuUdPC1$N#~n*xt~^e6$$UnBpP+b>Mj{HV z;ztfFww#hUL%1j`m{O^@@5179J@yPb-uJmHP5#nC=glXe8#)%A@;aRPz~c6qd((f= z7KVHsX|Xz^xf!#jC2m4r;f@PVo}&?PdQ;?UHL6{68E>Uw2g#siy~X}LQt;)+&QQbZ z2DWaaD|39Xt2s_0hrs)&sRX>}l;G<^ne)El=~IJu-&G~xtwdRiK)dNcR^m~M1V|!# zgOKZdwNn0R&4^ltXATWU#{?sY^S#@icm<0(9<9B&8Yg4?p-0%0E%~B6;x^|sJHqAE z&Upq-e?N?`3>FtP9-d4uk*~}v;Ws7eKh>N#h-~c+!M4K6F(GoyTNRvrh#m18Ttu6x zPL@$4Z|LKc--h74iBkUEhsclgd8@u|C-uAGz;myM)gQe&e-z}FsG7KbLJ(~TcH6Q3 ze?ha(AK%3VOae_vowx?^ScSECO)`!Gpyy^bI1%L>LJio7jI9 zwaWY#j&nW8u6-XRZ^y)D;C&g~nOzQ|@l8#103l&)jU^tjf9HV^If%g-z9E zrD48=4`oPu1h?MPE-p%U3=>{4BJ=364Ewo7AUnIC;Sz*Zm)^^cckU_5Wk$45HB72P z@M4^#O(VLr_haom1_M+IN?Fz>vTh<71P^@*0A_@Xa?m%_HAbiniJPt?@s#D3W*sG} zV)y0%91rXShds!m)V;>V`(yLWz7BWqWDI&t+12<#2k?jDD3U-AVW%9Tg(W6TzwF#I z%((!i$qL16Hs=0^s*Q-<6$nA>4!}J}$qgz69QMmY6cboV?7?Zk=lJj~1l`pAM(98# zh?Q%Og$Pi(AATNNy?6pXeP-jK=U#I(ogxz$xK#}gi2yX3&}7}4L7(}gw4U^3uljS5+TkTq&xXo|VvV2Kz7 z@q0?XSjb~D4~|?&nITI9xYOFcw=I8-C(78*;EKxf*RF$;Dgm?B)Ab|Ydxea_{>_b> zNMgMMREZBQ?%@+jb{MU@)R6%DpKGTUE1&L;bQdxU`cixa9zfN8!hy&l(U8R+u(!o} zkT7`ff{o=MFr!TfLUEnPAFbPB6`{TusYhpwi_o;~v^QWt;B2QAEb32c>3hk4N0PDN zRA8v}6IPG0CN6Ws+c4&jqh=l-G>`Nxe@|E6hz1m|;VRwPKvcU&@zT=)B@12jqTy!X z8rMR%iYLT4BZK9^1Zhs3ts0BbS%lw9JbbIxo3F8Kn3LD81vS7esWJ9OvC7z0js9)J(k%P+xnN%YQ9Y!2KX zmC+Iw^JBE9v`;Ch189c{pCl+4@>)z|i&<@|FlxeBTPTXC5)Dd3VqB7a-TIvGLvibJ(}1R_y@fJxPlzZ$BR zbPt|>UhqQ>d*w+15I3O<5tMO^_fGAgsl1=HMqjdH=m;&!(2I%6kzPzh&m_0i!&ZMHBW8D>2419?4APFTgYtRu5{) z!g>OGC1DKDGUohT;B7L`5h{|*vL!~rJw;@N54{Ux72fWFvrg0sTUkx`vB!Jt*~$De zxn8e~^}8FAz+6`ylUYiQ{(s&|qN+ylkz5f3F1y0D#?7CKKK!hr`(kD|^kwtCM8o0i z+H{jJ?6=r&bIB0V*Ctc;VE4Stsa5R;@GRj44$CEAc>ZDUWs?!{LesGKT}p1F{nduV z`e$H&)dW}!n&*ikN}Qk^t}+X6-OHF$Kve9pK#G<=IPhnJ`d6T)7s$9a+5tn-biLa*#_>8Ax-JYpi*aoip9GSnc30jHehUb*LlYIGlF(upV%1Z@nz8Z3>sF486yl2>q;;joF{Bbl{sx*!e8dxY;W2T=->IcY8?9SK2^#SS#-Hhm z-$Ad}8s-*;;lOS`1$!xB4+pGm+Tvv(04H{3DyFr4k{zSXUs~HIBeV2%?~Yuod$I6e zKE!SQ!bW0C^&=*_7san4f@`BY1X}5r!$cLW{aK=3Mal__+TaMaL%(d_%>ULZ{x`n~ zC*e~yoa_@i08g7FdxdhAQx6S)@aV8yKE2AN<-mu#u%iwEqEm-%=+hUy@z zCx_q-9>(x|D3ws-B;5duh&89 zG_2D6H2i6Ash@IySp@~a-`8($41cmU_uq~FzRA;Z2D_44b3`^9X{A!IK&qUwv-li! z55O^{l+e4<{5;uIC{a~}o~NK2rGcxJTN^-&w22WKWFk0pg`9IH`6l`%CEM)voIi#v z%#F!vzhBiNdUqNmImE)1SoRy(HG3P4g1PRpEPr`RZM#3b5*G45z|>$caE87=w7LCYr(N(OrO6pT5&JL8 z{ct)K-ho*ObLUDFij!AdCS~1g*I1t-UaxaELgJve<;C+9%R*|2V;+aTpsj+7LxfDl z`xpGil*HvZ>Eoo&Nlzb`nLb<674=4Clcno5)oVi-!}Fz+QkBK(6gdFgX`XZ;>}wRR z9P{EN^aS8)PH`G)L|33NYYYC9`dJ6CY-u+uzuY>DS?iXxn@;GTQ^g;qZ+yUf+cypa z_0_TFw;Za8mEYuemORNSnyrZm-=Z@yA3fy7Z98@QmKI&D^mvXr7l&E|jQ4!R?^T

HbJ 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�S8 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%0AVm#FDL%?d%%F_3!X?Qc!&15P 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 y7Ik45^a$9B{~c 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
`?G!TEV)1POW_>gGboKHyM}t#-ioTM>$vLKA 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%0D+t)uT2oqcl%y-1u%EyFTx_ArVJbW^o&WCuP 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

Msx0P;Jk_!TPI0@0$ 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 z zt7M5YPn?(;CoT61)xJSjt}`lMWfQqIU8&UaTyH7t_#IJ;`&~JJ$v(_e!V1g=CQ;G@_VPe!D0XX!4ZF5aLmti%`zGG z9#<0J$@5HO}lcv$?KZ^LoE0{%Y|! zBbAkx1y6dl%NW{Ma)FoO^-h)u6@$8*i#jNe_z0V%xtvVC zOZEl(>R2I9k#@0%uusYo=P&U}`t1i6^{;d77`sZZS|i)OH!hZ@-RG*V&M&&g&=q*p z92@li!?IUo?!E1^*8l83$ej^P1kHw-Zt)X_kBqG{2<0E1KvxW1ACY{iamM7%IE%^m zu*Y+hFtrD((m|PXf3QXN8Cvkov_$bnL!Gki4d~cF$D@|-t>#y|K9y}Z*KPeJ-esAy zitADB8;M;QMPK-F_xw%~HcHrkH@`9TZ=z!v9XZ=#jvMluW^KXV3hi{JGE4i^z6$&B z!9{(R>%z8HeMq&n>7&rr>?*kwd9U(W2hlZxuIm44e{|nKvijpHdbL}AeZgVO?nGF-UoC`dppt}FJZaAX zYJb+ekJW@I3-YjR=ZWvc@)*T&zv!JL-W2gh^Yk+IOB>W&HAi^$h&^|U8DD{mI5?@7 zb+{_H&xjs1pBIc%2Fu-TZKEqhz%7rhO{-5{3CjC{yy%WPKdB+ z!u}`H=|)#*bWu-Bno^!e?eYu~HbU55ao2viN6kgc=qmr?MZH^` zTcxYT+6OA|+wrzayyu;bvSNQi`RyrGgkQ&c@6(I=YkWC<${%lWIkzSHv^DlgTG=N# zsP;=By0~b8Xam^@FJZgDAX-hryW&0_8h-Eu>7V%QqT{R-K5g1d`a?ddMeb|bZZNAC z85_rm)APAS{UNTU4$QTk+B;)9*El_4n;7CqKPaujpCpdD=O*pbZMRP)Vd2j&>i^r+ z=e$2w)ZeB`G)|Pb+KpP`_n>POU3*0r=M)YEE0}j_Wn(|AKDQsXSM7)G4R-Gh_B5O8 z&kS*o{5RvCl=2aG-;U|-4aWBdTiw#Fd_DdGarc{XFIRhb>c&MidwAq|=eV83JwaTx zzd?C#7S9Za4KIybA3MkGBCT=amQI>BN%`fry~fWo_wLj_Ln0V!4#qDJwl)OYFLT*P ztY@CXi7vJR(k8x3XkXI#ujWzW#)#YS--s)7REoIW#8oz2bn!O=cNFg1T+=_QAC-{o5L9hNgGPQRJ<9pKDptE_2V?vP5J zQs_8|jsu#Z!~Oo8nWrA_SH(^gn<_Tu_(-|4qVG$KymiO;$hcmEI{`=cry}H@<#lS$ zo~h8YP1qJ;;+q(D0>@)W&epaDE0Eobr0A`Tp#RH@y5w0t!qvhh;Ka5Z=^j7xDcG=C*g=&4L4aJ*8n$Bz_r7T7jQ|qu>!6iZnS_Kg&Qg0 zj=-fIoYc(>+%O!=SyeX&sT<*soAK421U9GD~Jx_B#ggW!V^=A`W~(sr(wgBGWtOqVf}=?Q^NR28r^UiIJTbDN4ON+ zG+fZZjldm+Q)PmW^EU}M3wOX8yF2d1aXwr7s_XQ_{YvTc8|a$*`l9}4lBU}Gq0bv< zSwEgTg(736;!TwAsOhgLIdyIh|5C%yhD%0<8Kz0h*dqn3!*-yIpoM(fj`~7zZ zWn0bDMY!i1W`CzWZlEIB#-~+`x)~%qOgO_)eI%b_a8bA$gi-0M`wepDmID(;@{-S4 zuNx0V6lOui}(sKtl!CBoH= z|8Fkp9~J#7j#UqwRFRAjX}?4=c6<8}Ya>oSaW1p$nmTu>)?dojG7Xt$-MNQ}W9u?6 zv7vLn*gKZdY@Mhn)7;YF$l~QH26t+myivww(r%kx)UPVa?|!3aj0v~q3xxK8vUHSk{goJhE>R{BJ*}rW=OgLXFp_YVNN`8!nX-eiSaFH2d`|> zN>!<}@h1`KLq73Wi^lzF)xMWWKa~FUxFM|W>9U_{Ha2mjUrZ6N{JW+-xBCTmYN%uS z7O_-*SC9)KCwVj4jppL?PYdBP_O=L*5PqSA^ARra7REK4jBoh}7lvzaaGh{9aIJ7Z zmo>4P`$*e7o1)HKsku+9FIfIH`q>Z8ySd)v_nuE9%TZoyNkx52`eUS#on6##=UV(k zb>I1*aewwGVd3vFUabD5d~&;gk-pWxTD^ZK{ixXUsaN;bJ#VG`pIOv@E9uznrhH{> zHP>o}O^hJBf^3_}s(vC?uTk{@Y10JZ6?3M|pkAEMps4d*WJ0@LRRKoF*r1a!JV?AA z;*E=5HOACDEPdfBtJAWhFKJ96d!jfEWQTmx!wszsem3FdNTy8Ts^6R>Ui1fx`e{i+ z^&89X{X?#cF?2j-BvSsYz0YHxy-QhHiTgRnNt=jr>0b@R=_JnQ%>IyPgX)alOxIWE z;A)ZYMSksvi~7e+d+w|!?FYyk8Q;&@WUEASEG3E_3Ux(y=fO)oDdk|^jE9=#fmyGIv6*wpB&n+5fc2zo99qLkiff~Z9 zf3&EdXXd|X-<@_I*zNtTbZr{O>e}_{HY<~*#T@X*(KjfL&`(%ny7XtY2d?^aT-NiP z$MLSY_vgLF`WIa%e!8e%YU(;9_pO{~49T0Z*}ht7CGfP4X80Bj(GW zaX(W00Q#o6_H!F+2RLMT(EWB)qq?V)L|6OYEb^X5LzjJj#%Y_i!@15S=}!=6lQ>zI zy0QIsRW~yCe}ti4>>{(q6ifl_{%=-U&S*C(-OCbJXAf(c2tt3qXq!SWe7rV}C*F1B``3qGjs}rlbjT3iueNlhcSw`DFX`D55yI;zj@BO+Q zQDb?h7SZ;2*r)c>M$xtW?~D4sqsnuWnva(m)X$Xjb&9Z~oAz3jn~tS2PtC(`!{^pM zVwYCow&31q_8sdUcg%Y}^KX6Y5VHRR@AfLtlgdSGi&@5$G9b)mj$+_nswO#g|p0 zF%KUlteP;kxy-mslhmj@Jt6YP36JH4b4;#up9-%ce3S52!s{iz(#_a4&zIls=oex$ z!tY|fLRQV2$U67A?J*%wvgDci7Jnk25M4=hRsC*J|0gqDbBwszQaN||4Oh2?HAe&S zr_q%~*I!z?TJrZfvGdmRq@CJRZf23`5vPFp^!JPU&88mnSxn_m>=8S*KUn>+DvD*q zRR60bUW#~_AF~b#SL>*oasx~GkDbVkA{P)jK2ra^a3gRugB^EB&Q=WgI80?Y$^=RO zoj`UHS(R7SR=G7xhb1XG=aHMq)2Z52`EiC$W4$FhH<4XN_93o~_E+N*FO=O{b5M$0 z=-uRZ`^<77*AwYxlGkZX7~{*g@}%am=(-lOzL*ipiIT*BgnEfY2>X~-N#k)LFO$ejbV{RMSj zR-Wa4rT9|C4nv(<8-(7=IP)y&F;qG1gv#&jc6L=ijU$^y_JGK$KERqt{L<Uq-{rHv>#kyyG#BugiX7I5u~0a%@V#s_~(USE$6(Cn0uD=4u-=scx2BqAhN8; zj|EGwz*{$;qUq)6T0tnPBwtl^%tzXi{wmR}`n|o6+CW&0uw0)XlD%NHCb>3Obk#?a z$hE%!xl!bLUVz*Ta>Fk`?gVlZ$VodHeKVIodv`(XU4RMk$n(faJJcXIg`BeAsxA`2 z@NJIg{|H|uoMpP1o;eo6`Sp=P?&R~xN!?E(=kYEXd%!BacFPvb5>~!L*eYRFguTYh zM{e(1bZ!wA-65=e5B>pR+)6aty2DDZny{`N!V-k_?GV;YSeh_yLzp^whKozd%OGJB zJA_RTc9gJJTk(vt%lMfgY@RUDAs^wE;f@z@C*aoLE>u#6+!owAoY+kHNZddJ_2pYK zo^LX_Fx()V@_XULhSk6=!+oCXB0K)1ZH&Z*bt2ogYsq;29~D5P-eJ_5- zSxb7CFe-mI^9An>dBB)dnXHUCbPrx?-s9&)l{QGAD~_(KM3?HPk6}NJGaB85r3q7G zhG5B03T_(ifXMTa`Wk`Tgu7FSo$8B+*KyFS{D`i3;zdfA^cP7yKEkcSRTXd>aFqp| zuMt1DfUAHD!3C8zLtg~0qJWFRmBUGT@)2xw5ud%po^%hBQtPK1(=T0cINaTWNS&@Ye~$Z7%3{cUa}9 zBB%(gPs+_(}b&WlGi>DTIC9($AiGp9rQ@B*oaDGmXUe65PXEIgdSHEk`^1#Z~GKWdFEw~8O#ip$F@TyD{DGZMb| zS*N}Jo8gPM4|xRt69lm6D%e+S@wZqBIBzyJsOJWJY(h;1myGi-ELF_43w>MTc#^Sp z!EAUU$el#)OTJuL)p;DZ{To?ZzT16m8CQFpedtP@zoh@N1YO>a67F!RK`CQkl(6sx zq{FpG<%c~FpROF}Q8G zQaBmk@)5bR1GHxWR|yw`dqPPWar@$|7@yqiN#{CvdJ`+;h&wg$m(Z)_Rqow1%;d0yFN*N8IYgICTW|xtkm1fRPiR+hKGeD?(abY9Qeul3CW$T5OTK1FzuxK>jGMz=#!b=l zZR$3bSX2&bS~_?=!p|tHJT6aPa?lz#Q%zxO(~OI`*qrypIiq<`Nm zyK~=0wY8kK|DsWJ%N}ssM*7YyagP)Ce2L3PeDLFNYj8IUp?nqN(3^y95LWXBr=M^5 z=b|gXA~#N0ZoZW<8HQ_vdmYy@CNZ%pjav95{QbgnTVqJm)Pi2pXd`TyFs4S+r+Q5F zcM0nu%oj29tNar67`NIdP7_v2m^qfhJ!V>y@YV2du=GhT7}xyG64pRiM8e3Q?8r$v zEASoga%LKEm^f* zQvRTM?#PnuLbmmdOUApLlwOCd;Vvs#$=e9BN0D{bx9~^cv+!!&Kslw=gr9}qERe5- zUx5!?x@5dRku;tA4ysQvo|xV9rWm4mV*Hg~2@?13git;W=LU+#hI&7SHeKXv81}s# z9joa0k?7zf^_zqnd()C}&j!8b^B?r?UiL_hMW#G}Dt&Pb*+})0b#Ac+E(2EuH_5f} z{vhrbq~$$~Lq5&}5A5P>x#KXnJXNf8ogm%>@t%-)d>Z)Mf(yNQN&lG;s*K8)!Av;E zzk27Xytm_vZuTxt)Yw(D7kYB>Ikj z8>m5^xqVuUZLxQVwYiIcVmDLn^op(DB%RrsCH-sOT-mWn&i8Mjb7;3ZSAChhPvwyk zUR!XG*A}#bbueEp&ib3M^L)8_X>qB4l`r03RO0=T`(Xmhu6LlX>OD*P?@=dx%=vyMa}2g> zKPs^6^abfxW5iu2uG(W%{ZqA(@>|q1A=JP5jx^2_+_Xc9jd6DZU9Imm?@cIwnlmwK zf1cS>tQwmulIbYvmR*B?itHVx-(k+fj0^L9X$RzNkH$;Z@+M++wn_SMEAb~|?4QWI z$~_3@`!-a*iEq8zt<(FjQiX0k)%yptb2Gs@=UquCjB5Ghzq2{mIdIm{?)0uhzQf)p zHL)RLbJ{PT&s(EzkiWsbOZwxYfKLT~zH9l{Xzp85QhDQ?oS~9-2ou&o*wefDqS}Ea zk#nr5zIs5;CUMx^dy$+~fuebxZb73zOmz7a z6s}+{yVvMV_?>lrIq5<3D<4$qv72}^#5*qW?Eb9Ayy_RuG2f$2x>h445TE-fx?0=p z`*1_@TpGvgIIOW{1`Cb$j*-{(Edr|Oy8N5^$-<6l=b72LaP_Fq%*q7yYIj-qA@oq?;kCvrAl z?DnwNMb7r%d5|ghj-8MNhD%>#CxRZ}GO}Cz-hDE%i^qJkT4Rk|&-(v{CF{I|^utNG z5x5s|EgxyeX}C08h13c68|BdQ=Qxb|4DTZu;04&F(4G9PAvbvAl1^1R%jwqdWS+m$ zwKVm9UQa69Dh1v?XHlp|Ps0uLE7I^tUQ}J;H#%*E(^hwCM_r3Ai$<3uRQb_A(rMr4 zl72T0HxBm{*D?;x^`Y{GIC^8e@16I6U*_0C5FdGic)pvKjQ2&V@r!RaBt8`9<-bCg zr7CeoDVbpd!Z%XiH=Fkm;iT?s;9_u(aqW4Z)Hg>%xIk*Y9w>r2LxGdbA z4sKoab(wqC@Df)#Nf+D&a?MA$@=n$%a4Ov7s^F3ZTokU`!HJz~g%f>QuBBZ}f6(r4 z^df8C?$JK!+83~MV)w_sT zo9GXB*!wW5{BRz)ARr&%s^GSXo8xNXqHv<`+a-KaeUSW__YN5;(g!&D^P@cYpm8jxATEz&=P?a_T-`jT#V=a|&fA0aR{2SDI%)QO z@}SDkB6;&J?gvReOs*WR^&WF=XL40=S-2NFlN zM9%ILwyjn6!JNNi+K1$|XoVhxN^>9@iI^yW) z=v~s^Z|P8LTzP+Qr!f)Y3hb5El9wxCVfOiX!d50o_&w^Hn&CRmtH4EsEi}E%A2lz9ui;}0z9pU$s0dz@PJ_CaIk#_7&+7+=PEv&#@QR!3r9=~!QNXloCesmmtWrvsaV_ZAhUg=ZsAyRYj zURkrLcRsR@?otc&e({&W-T0&EYPal=?n&F9koM|=TZa4YZoXXQ z(ywj%FZa#fCdI>*ujY_m6^Hb~9MY?(e~v!*BeA*HxH->tM&~~m%v(^B+$eu1NWbh8 zOZup!Z~OWMwocV=6K_8FQ<5)s&)M+)rh7ZmT@plTDVnpI`Qd>bbg8*Fbvx_t@qAx@ z1g;z|=j%)UC*jKAVm`jOjb&vA&HmA|!&}$5F>tMGW>}H@#C?DyWLk2m3fZHqCM(f9 zu+N`a(*K2P`F#z04W|v#GgyhR;A{5Zkz4zP5bGgM?}Yo>RqWyj;e&+BwR{98;rbn% zjG1Y;6kH_V2H0c9t`lCZ!}|o?$Bf#mileXePUi2=FX=zv+EK@9KeS+dF7L{I%C%w` z(W0vjU2Evd=IgTi+Y9u8rR)>v>YB9cL&|;>t`qLH(w|j-u;;XbysCY=``r7jD!Ytv zUy^z3OH2BfxfXqC?xDu+490H^wq75M*Kv)%gWn(qOY&5M{4Da)p7N1C8i&imUHQWL z=pNVlW594)*u@s9M@Ld`W+W*e^aE{B6Qb{Kb+!FNE88 zvgZ$Z$MQ!=z?co4KG0rfG?&e1)&6%;Pg9&HFm2|O^7|u)<@cTr`MBkf-i3Q>u7MhP z_w%k2H~nNy>Uy2cr%%7f-VYmrJ1Y9Wx}-0h#TVP2*?xj;PkGC@=Jl8GdR<;u&eILU z%%}aDcBM(&k|Tc01@CLsgy4ng+79io*YEuyFaOi7N0e=7&Zza{S5=aY_}y%GPGe)a z<~~$RcKY#g6Y8ZZm#bc+=FT4E=8!vDn#(%}|K0can`H0Z=iYa%xpNkMHQ!s(KVkJd z?uiubJ(z0?_HlN2X}B5-lJ~NEus=WGyn@}yFi*36icHj=z?s_I$`>1Ja3${}B zp25~8$k!3_m7HJFZ<2Brw7EUzb98hMDRBWLyon=pzYsVNU@DcXO?S;FG z-NPpJyBX0Q=C5P;xi6%yI(K+2P`%&#plO>6szmt|GF~T1r)-(MQ!5?Y-?GbY&fnO2 zZ;AQ(X?YEG9oApgaxOzyUM4H+c;CIuJ3nQ8YOWE?XS^6s=AOSS4|vy}tg0Ozt+Mqo z;`)C6KQnKn(KY-(piBD83c3RSXG#A@DN{jzv3*Lrzo>UI7W>Gmzx1P`>OR(If4!uC z!?I~^<3et{<*ga;jas01KESHDgr5@vRlG``MkHfu`OAVjFB-1-Wx*;o<*ZoJ4>IK8 zbJ2&)fN9pK}&2or>L-7}49& z6MS4932(EV0~ou`+_=m&QtVz zHRg*wSJj5y=vzl$?b?#AOP&h)gc?UqeY_X(lf2DcoTn?*3+P8mZgeehi~l~tA3^R4 zQ%>5dy%&Grm#hP15<7ES$-Luz(0zYB?)Uz>l$XNuQh6p(c{w}_`4B3n(VzacxnICu zDm6npsbd6cK3XxuFGH63DBLbLq^d7h^(=4hkO@TQEzkRzPtn7%5G8kmbi`_QkZF&c zoU}z1a_h)_McU%|$}?NyjZ1lo+d^JrpngxK4}FQ>F6pcJYd-fme67U$a*bE!H*wdM zm)l~~)=I82zh7^D_rA;B!(|^t+T$eY&HRpYZux5i7BbxK(9^ujzBK-aIZS%UACHeI8H~0YSX-`()FPp0c{Zfr%-;_fDz# zz+15SUKkuYX^45*6nb&6Z*X8?fJiOujjbT$4k@vH3p`&#QPys6>g_nW%NAEenTXy&su(hy_QsaMb0|gC{ZVCiU*qUevbsY zhA+(;_as^UJKKzfNQw4u`Hd#^(odY;H)Zv&T7B3~zo^f#9=P0nq^mhDg1%++ov6va zptWoidCw!Puiut6et(oUJ|wrQt=}JQy))Q;Yq0Z%V0)eVp$_|Wl~kbE)fn+l5dW6v zx8p6?Oz#4|DX&48;uh@@tS8KryM-VDv6mC*-+X6QFPFUX5$-6Q=UrLrUGOqq=HUWx z-{)HTzF7`tz9FByOkj+vXJurK7tym#oFl}!MB?xft}Mm+4DLmWF}O;&SvaX*`G{OC z+$!9Q6=QG-xRY>I4z3f<7tLDd*d%T*oCnUSZx}8F=hQb2R|O~iT|N?b3a$(8Rf;jV zIkc!wti+-RI1o z(y8sSGH8w68RX}Y?>6O&en-;w=lDf!4%EB5tExPuk5WJA>da4Do#!-P^hSZ2)N}DQ z$cEpOHSTZNWmmEshcN4|(Jy_r6WKOoReLEv%y@rEAAA!2QY$_DWA!dN&iomVtEqE} z6Ug-<_Z8F5Ip5XJ{R#8;E3tE4?+s$-3Os4GCtg7sC(*O|UbC-r?skC9kX-xt9^?Kb zVQ-ea@R9bZfD6U4#<>ZTi@>eGT`zetxfon!eb#vH9WKUS8(b$`;EZ{ettD!7iW|Ad?1+H;G#Z+5F3l#p@X*N^|(Wcmg?Ug#V@TCdfKlwFrJk02k7+v7#J z8Mt=1O0MN2xZF>Ffm6Q+s{OfaN~;G#G}2%KY?r**$2`buor))I~Of>|BBD9J19evMb1b${|}# zem9VL= zUrJ)$m%`K6*us#qVF_4))kuU7vHrX=Yy2(`noYlnxopU%-hZThmKw2AWoO3d2erft zU4?%^9m_L%1K7~89G_EG#<&mEflU1Bto|D>GIvWp&@T14F+-pBn|ujXd5&?CBHL)7;T_^0{0?0pP-{U9&H{=I9qs8!LGM%R%} z>eR=V=PP56chY?+(BPMLT|rl-E31D?@^y#IFT7t>`4^n?5Z`^PKYO zM1BN$Uw78{-Ag6!cvj}-e0iy}S>)T0|6}QgZu5_-fAuVb+-thYeOYO|H@zKa?7>Rg z4oN+Rhq-@tyPbdMJ2Z1=Zy(4%Z>ILt+leuJubDTW=PNIcA^9(M3e;Yq=$k~}@_no^Eq&@9SHT!l=YnMHdvB0|QCuIYk4b%OqHp7Y ztnpqcm47*xdZm9K|KsXyPt4i$r_dAF(+9KqTO?g2$Jr0{95OB58mzudQi&PAUs_-M z`=$J-r+RLMc5ATAuDs!|iZ?D@q1wvkIUq!5AV59yZC(Vccv|9G)koxk3qlY2>y3+j z{$_OIs47Eh=;YUB=uNgxxkW1)xbfOWEUp^)xiUNFWQ-dRa!h}}+^f`5!7AQ%N2!cJ(#so znCM5*HG!^gh%Pn1OD)orMrTlS$qF*j4`g-CnwRV`Sg=nk+ld>lBakMXAc@LD{Cg~` z9~VbP<(+xlyvM9`sqt*?-ErR}#(j{z+z+|MydQEo$`L6rPka2{AOqhJ}8m zim+eE1aFt$w!^CYYOZCw)8~!vcFnc2>oQ(b=$rmpR{tE=qR)AjQjIzE4eb5^Bi1?C zjPW#0+{o9n#<^g(yf7rJcc6EAE8ORO6b2h>whq1l$;0)ANsyW_Ese_z`mBT?=FA8vkB4?;Ko4 z^ut}CQZ(v%7H$%5k8tz@>zU%PEOm^H9GUnff9uH3BYRS0RXZy?=H7o>wQ+iH@f}Qd z)Wk2WeTx0N?`N&|0Xg^jIcI1(bo+SqfV|D!*bA*9UJvmGiT6p#gX$Zs#f^8HaLTO0 zcwmec>m+y_*)3$JMAq)(s$bgU+O#(@`OOAq?m{zLr=DFZHouI*nCSgDzRojQ{V}r- zIp;llJt`|>{0h+_A2fA1-yLD+Q`sN(VYQ5rPga-CSJCWO@3bkd zZ_X1)+Z`c|%n!2qpK>j2=d5pXrG9&v!$?)QAl5d2!hanMo37SuANO|>eG^?}&;D2Q z%S-2+`EA)bztYa_qs$*a%o^|Hv&SawBd$ws`DMJPe-POTWZ9xs?d)7*UXiMI~&Jr0(K{kc#tMbP+w$3`!Rm~GA z>hVy^k0qY;nesouP95XEI@d;@p(4!pU`Pk%sR0>M#ufs$O45rHCrg~TnV!q|K-dY*^CG|KZN_e!+)$!JtEHvcY%(XRx?v(ZO1wX};yLX@_5E_TIM$;EHOGPMBFXAb zY(&!4oGZ-V8vkHHr(zXOlyWw0Vn5C5=UM%c5#oHVQTij< z&+CueExIh&&WVn@gYAS%TXmqP;<)J}(N6cS~ zL%W)SX8nEGs07P^H2K$w(nkCZ;(va3EtD>X1N3G2bt_((ujb|k!pi=Y@n-5jWB=>)4!CzeEvBBa=Tz3zsV#RNA5UsJ;=RU<4eJKu;*zt zzN9^H5?u@t2Dggn;ZM__|3A}T&!wpyv=TKgn*A|k6UfdZ8#B{&`qH$pxrc-qR8Qj+ zR&j3LsRMs6LsDA&e10@sUWB@bq*HQNf|f#2BYW<+is zt^#gb;!rQneK@%r&q-z5!0!=zCf52sWv!(TL_Wj3N_-x_khv$U?$6?%%X}CoEb{Nn zr)D2;>NZyowr+eNiIXBu?I!oCMW4!#ntON#oC2G3R)YD!?99geN1Qg|h<)`iO^Qve z<#Har1O99?{X=rw+iA09p=7*2-|vrdisz}o+{QObNMJ?vxJFlG3v!WpOA8lZGPg6k;uf3Sc1Ro1nCp+ z+gwXLS0DKn>VOrb<$|?{t;ute)qa7LHS}5L+tcPeAkVb0SdfZP{tscHt*qmjab<%{ z|EE*!>B1O3BD)o$v-=2kN6IuooH^qBPn1dOb_0Dizx%J% zt>nGxbL5|Rcb4$Q?yF{bl{;YP9or#kbr9#s?}@{;%=3q|7VG(5?lYTv1eeJ&gI*_Z zuc1rvH%h!U;;FTcipM^M^SNN`HKRSx{n+Ej5LUG~8aD2iF~5echV875yIZi|VEzCc zeY;VfNHG1(eKOv7W*gcQBTD%5^mpR_O5#)QqPauu(TVkVj=6(1y>own`7uniE_Ag1 zC-;yXb=Tu9Wf~BxR_B*~K8#G_4_SR~mywQh4z>3X)ZXHLd8X^r?z_c}e(yyZ4`%T? zFpbxNal){l7kmx#f|wCr(rtWK{c4N!GTvkQelt&Ixwr?M_L)A8yyxnRR4Q$3&CF!B zfWd}{{fSSa&v#7!E3RF~o>bA31&zj7iXlEo-0EG&^k0~9-QRsF3Z5#SpQ+E7-L`N?r!2{&OTuYA)j<}yD&)4!Hg&H9(+}?SUf}MB@DS#t+Y^<>eoHkQP;>rchVP~33iU&%s*YsL zw-R0xIA;CEmGd5yT6-{dHJoleC`MrPmPzVKV!d_be{E* zPC@^&$EVr9cp{iugM42o5&=)xGnDKsce%I}gydxql@B1I}UGpr3G=H2%WG9iW zMz)V@!`Gqe)Nc#PffJcyuvW}kF@l$o9Ypqx`Qw`UF!w!_uPbgJcW0{c$_ngN2Rh1+ z;6I*!%;8Hg{++&ryqhL&^mD5bo8*RUo=N6Zi!2f;5+>DXw7u#bSH0*DmmkY}*ZDA9 z2=2wCFCQ5T<8W04+!S05+-@ah$j!l3!@b|+#O|-ajlkRSx~baA~+) z`*#vH4mSq(U6H%isvot7r{*Z5emIQatPRx+&#~b@uzP4%+IPr%SmPI;s`fq0G?t|f zk6}KZQ@(~O|CiYhC5_ih8hljw;mR&NW}L%@tCBysBwSX6)Vf5Sh0tzi5@+;rma9cw zNc=X5UvVt&S)y*Z0Gvt_8Nn1>DV(z&M&K$7iiaC;g-gZUk!HE@+-N=H!ey`>E|_!(T>B>U^AdM~HWm#8dZTaYZ;A zTt<81v>BU(`<^k@Kgg~ld%MWmW2vyO7)L}bcg85M4@Wb6QGPpAz(pNg615SycDV0J z8uUl^`PE!k$Fx4YiJe|!QZL(lbYHr$vsLQFggo~n)JvU@lioDx<;JGe$rRii-1F6m z#9u|WG<3}PO;5hdnq~v83r@Yiht(6uo}ZGzAYlPELEB$`OdsM}-m^&9`{X@GF}_w2 zmL=@r627RhuIwodG9>;Y?;ifDv3fCVRGl0q!nqQY#hW7tY=xOQPmGu z4);T@9djFcH8v+Lr@SjS%}KeA5@(7ymq;8w!p%$m;LcTy!L7p07jPSJa|N95FYqG@ zxC*!#IH?!;NE#71(RaRL3@!$D94>cXTIAZ`*5F>^kn4t9hdal?rQl8$a3gRh3b;vA zpEK?>+$wTsJM?AYR^Tcf+#1{_+=UKq6V4OPudC82&X*#W>uX|TLU5(X4NKge=1>lD zVSTMRWv}#LF}~(6>d9Q(g`QUQsIiG2bKOfc&SLI4=Na1eS&VX4`U#2*8z8T^Q{$HDY?~LQFQ~N06uJ}IcKEL;+C2o)V8SPm5Rm%UWWBR*EPd;V*h2W}Qea!d` zaFeTs>xKJ<2=NK?*8ms3_?YpmxAKXb&)vQAkbN>ZbrN_DbBJ-of34fjW_>yCK+KPiSYtCJAFOca-I=|6pi4;e!jX3?p zc|z8rcHOdLXujW@h(o*8yVaXG!=?SLYZW%C{m=@#&6j1UFMuhxK6lx zxi)+sl?OR=htF@WKg_+vR%DMN`-qHHJHH|;%QzTgtx`*H3fa;(9n)t-FZE=u`Bzrxgrir_1#&zz?;OdyaRW3WPHx=(0n0;^?9aYuGtaqeJos}`L z2jOyga@s0!XUCoG&FGeLG!Uok&Bu&)-`M5AzL|5QWW(jGv(#-raeH%dRejN$jI|xR z5q5n^+hmYEiLBE`&BB$x#cp?zJ1+St;7-B?3b<{!(gLpRo9sUnaFuYL08E^N&)Bj7WGxY zmBTF;a8+>G0xk+S@8HU*gI2gXxOZ^vxiJ`)UoDrq>4ul`T_rpp;Zkt5)T>;}N9_0r zTn*fHLNJ#M$XUE(XTGxJsSt`R;*_&pNN zN4QP6@LP}RHz>y7N~f`xaPM+(A-FNP-7=Q>NdBtfMr+J9pWFkvfdsL8F~Smrog@rb zT#e_$@?4?wz9+M?+-K|5*n1V%ml0yQ;=Jug@-~8wwzuW4ohIR0;hg^7G+Yu+#*lm@ zZWeAB?k>fc<%j$KS$iM&sID^qKbc8ebm)i`HLIwxqDDnGt69w|>I6bEgaCmCnnK!! zHf>Xy)>N^^N;L{1YG_kM%~sU7VnszatNdL2iMvs|7L_JLz!i7VMa31Bb=m!*vMamy z`~E!V%)NJJZs`8Lzwf+W$=rGFInVj`oaf(p&N-)nZHmFp1IxYgF&A&-^UnDy^9$Ja zSh@wk>R@2Wfdsv?DL%0 z>~sEK`tLPf_FkWz$D$z03qE=F@fY{^)i)6){O_f5Hb=gNiysSq1`d6sdv*bd5v8I7VOpnA_J* z+qZFCj!jI-TKcDfG-pV2mNb9Iv&xCnI6H6*><^(NljmsuH;&_R@h0_Ja`tgJ{Fwwct*6=wL|AUVdJp7!1wI( z41rNv?J}ddxE+xnD-@=xL_90jN&U44oClYe^1=yV2Y|(8;v>K&fGLkY`aS_{Kd|^X zo%Q&#mdf>i6$>`P*B}@HbR#nPd1R_rE#Tc@-r+K^zNzr<$q)SQGX2ccPB(cMx8r9} zUOuYR0I()t@j49yYXLUbQn_@yf#u3z`#ipQyAOGMe-z7GNWgbG0pA6W?`#rvR zA540D&-dw6&*Q)bDZ}QT*`w`OUcLfrly|~&p7+DNul0EZn=^?%4eV(#*aBc=3YG86Bh>0CpJI{c04v^a1!3HfMXd?=-p_r=2*Z z^ktV$<2EdF*PpYwCOej1pB?9(hcV84Vh_W|Wt~ZWYuFLMPc|h!2_0w2= z=9iJKQzzKBm=9M>h36h1DUeu1xN)+aPM#YiO($t~ljfa5ggmK;2ZiRM`!CY%)!Cs| zjz(%>ucU;`Dh>qvej;UZBw}}I+H0~DsCwyB+NSJa{0K3%r=Gu)XC4jtv3_5qu0v0p za%=Y1cV^dX0O9HqM(7XTR<3^pd7hanp{2#Z>VXw$STds%SR=5zd9K256_*+2CzcV- z@p>&>)q^`HT<26BN60%w-l7jzJYyW#7GOURKKe7hE*#<`D5%Uwa1#*P5o%<3C*bY; zodaLZ+$k5QZR1ns_cEU0bxu>d5_D$YCyP<0=3F@y&L!2i6WDxUf5meZy5$23Opl*O z8}TIVhbOYQk_oNws{pF+IQX`MZ*L7hqWYSS9IZRsnm#f2F0_>*%9Q-JOb^-5g6qta zr-BE0j?pFyw7Y8TJhOP)EgUYPa_w5VsC2J(fwJS<(8Epd?hIBa{B5VDW0nW~ifBeahxoY=P1749%(c{5;|rE&T3fVPpQ& zrh@0G?#R5jKPh!E)C-qo+IV(h>Up)sUmiwup~F4-N@Q;gx5_#M?la(ih-b(9!ucyL z6K??Xmpw}A!!xAqdHR%#eT0rm&fEDxSPwkR@O1ua55wB+R zU)eJ72<(Lx**nos@!F!kRMBVtAR*27h5T(g9r?XQ@1vBvmvZesnlgH42hs?4>O}h* zbLm$~j8{@)vz=3Q5?qJ>Xv(cmmh!g_*JApIhz#+qIp2Z5J!2}ogSMo99t!)XCDY8a z+Z2NZ0k7k)g}meB-NdujDWm(tWqanmLCJ20B2R7q*0+^!EBQ{4?|sT=veSHjvVsVl z@Z%S5TA;3QG37fzzJ_N`xick7@)4YskWECSd}qnmO+KTSl6*P_$B|1%29LI9{bHKE z+)5%+VM3Z4 z`^nq=>?!A)PRgqV3B2qjb!p3R3mOY1p(u2A-vw|T0oUtQ5Bh+6Kw+Woe7E^WkdwT7 zquv?MqECzGgRRTfa?PIwVAbOvUc1FZO@xmT&;Pyc{6yVbT`$JB{Bku;ak2 ze{JrF{MpJmuMH=R=8Txw(r;r(vs&i*@Bn!_>(CQKkH(uU9*&II+V63tnui5DUnIb@ z#a)RXpCj+W=S~GL@p-*|MmXB8no9nX`mdx_FY&IX$KV%NPlfui+R_269@zi1Eu#Bj z^6VwgO+1&&2knY-tY)dPog%aCBI!Z$oh08;<(rKzw~LO-$dE%Z$|$5+?qaDNYQwxK z?3L$D1wRyC&W4k3tWFn^*?27VEp*m5Gb3-2kxFdTw--L z7guO0Mq_Y@axYNsl|1VsSOM61V08lUk&HPFtm^qwZVk`mfbm)Bxx+;sp|O}dja^B) ztvrja5_MM&h^9v%auPsTDzk-r6Xa7_`Uuty>f&GwbH* zRd|_7CUD+-|NF?h=tX|Mf^!jnhk$hh+pIFmb&FLY?VC*3Ts=$rG16}c)7!kZc|)*@ zpDJy3eo}3L`jhtkN;|vkBnr?oS!S5p-ATTMFP;j|0Tkcr1J(;{k?4bZdYzb6VSnCK zsr*RxQzMKdrzgPG@{+0Gey`j4F*5#H^v9-mrpo$oiRlAp$vgJ4DR<6=@eOOg`RKM3 zn@<_iU22(Fhe&$|&yGK8OQW?HR*GjRZw>GqC34ufmt<=!?0_k8|N0viMNGM=?I zbQAJVG^6Fm=7KE+mNUC3X5R+jsS_JfmGqJgmwfGj-n{)v|WaKL#HGPtm_8-cS$hB(V4K zEW1{BV z*%hwP*BeV9^Q=L)pZFxO&RQ+YTS=*JDx80xK zRWowgw#(B`Ov{;rgIsMpMB1?>w3}yr8u%*!+Y9WV0H)WV6b)pXt}Pzv#NkO0WpTLe z-uasElXvMKsgs$rh;+R z&FFJowqc86l5+02DPK2e()f({-8gyrmf=qk@3gtfeL36kV=7}uEMw}!A$yU&Uq|wj z!6V2hXA|3@Mw^iW%kEtLi5G zV)9-f?_J7kyuikuGU97joFj^oE0J<@7&uyCuUNpBHcg5OZ zTlQdGM|Sk;^h@yEUVUwL{OV4=@!VdGMl+%7*Bw`c3_V2o;k zp90s?OWFgZT`&42ZG8Q-UE5pPlUNtpCd)FCy}QYC>RPYs*gSz~;LbPD zCcQtv`>O8vd)Cfa*3TW$FaBC(Xp@;HYMgpVe1<%` zl}9)wm{5v@Q+3(O`~DtpyK^X~0uA!OcLSdU{wd`{KHV=Ls-H9FzcW9g7EiT}i2ikR zgTt&!0V?AZxE5YN6)X|1f$TiR;|P8Mcn|Qkf^UuB^ZpU}0lZysZfq^4uLph-_>F?& zz@EpI-@=mvzVL=hJl((tfSc}x5qG;UcM$mY1o%$idw{PIe$I*&Eo%(Nfu9Dh{?tdX z1HjG!yVW3m{sZf{amt;m#A$`|xB^~t;S})Wz$cW}$y1nv6M)S!1?RQPuY{sHZ>c@l z!j1ENEI7%q^)eSq=dS5z5cK+N)5dJB+gL35qglKpa~(g?Dc!heYM5Vk0DRq>rhECu7Xv#H%PV^51a=%) zv-0*BnHbJ~;B#-D3SJq*xfNIwuz1;{z#4%`chpDq*$ZqimQLjy1U3Nd9wp-V4~@6r z1>nbl7j^zyX#HZ~XMmptev8r@9T*Q|{;*c3KBb$Mm=oGFNO>zc2S~lAI@JFdyQnYH zwH5$d46Gj5O5voZ@pj^9ar=0cr-%35u{^5ZAh14QHz<#vUqd|Z1il~mQPG;|r0|zn zeT?a6ON}IM1$Ka{8H&dhnJ(ZQ;#p=RwPtPyj#^s*SDkA9iGIFy%I!~qC-&0jI^YX{ z=YdO(J9?$B5Nwzfww~AsI7gb@q`6)7j`~%;1;uY-DbRzg-O1tXh;gaOUZE`0DNG!x z-|G0g{;^1p5S{O%oISTq1s~$smD5fU@{@s|P0PiJtg$|>_VoXsjddOQ=bxfoLn#YcTgF7NOO=hlRU@EP=6de1$}Lv3Wj-h`L*sll76e~gJqeO`jQf@{ycpOxk=iW zDlH%3zHkbA5!mel;NzMH;S|fA{}bwXM`gLv1sizx2=7cT8^3WhagSwhHD05+(M`I! z1D>YL4uPF9J8mRJ1Mi1;znJ$+c^19kziNIH}IhTCjaa3e>dNJ5Ov|8S9XL!=w6CKLQP?w5(qZ85(-L7V9f z$);ONM(xS}6npGWuXEX$QhFL+k@aeo_nubmNHZt63Y zP`XO2e=Sp(-gg9iJMWrGj0rcr0p|&O+Sz@Kj0ky#lXY`YkQcpSD!luz!Ul9Y$6c=M zFf*rwFs>ykdW)Uu)eXLr;2SS(o5@aVn_|M>NLH#6s=xR#MBBT`n;Z1>XMO4Yl?^eU zI%@)&>yxBCPTGf6j_FF!L_B^?dvPu=MxTfEHha5Ac}}D6-aQrEoZ{JNfHcW`*ZdQw zlZl`?b4&Jc^>Pjg>f*Z@Zx!f7i@o6Qdec;}j%P>b$Q9EYqx0?5=AEP)A)WbGtj$H* zPNA)M0fpV4C+z{!o+B+wn!e5^EA0Gwqua=Droc(WQd6b-f*xp6{+}a;woJJ_eGv`D zeNZ`_WLGjyXf&$FV)D*;GqS#hA4xLH>fz@Prp|%HinQ524^^d19!$)3esAlek!B25vRH_Hih~~&b7*y`Qso>k;9Eo(3sEwhYHvN(0IRfk2&*WIcBw5RMi%k;I{&w(B zykjc(SDu|L^R(ZfBVs-6GvCECCrGXk1~3X=D#YwBY8O7In-6Zq(65|xif@~ z|CNm`oMXr3=Fb+c`ea;rJHw>|To>Lm6MhuojLxG^;tw1eQVCXFx_^ycv5RtjT?88dmIoHE-!QN) zU>8)s)zZNtT8?;27+diW^{C{~%{pJ!Swrz^``zUEY-g#nvX`PRQ0~e5(Z9ofiOP+1 zKOa}tiB8(oUePTOr|MHFTNzNbx92SO@dHtOoM?6s*lA$D;n~Rx^Z~DLMD4Y^XQUy0 z0{oQ>8~>TnKe%>|P6eNl{I$6owIRag+c21Zh30N0vgLM8)Et}=XLnDzy{EW$;(RdL z2aw8@@m15KRF^#Y7k_BV?J0uJD%K_C0co4hbwMSbD$R-^B?m5M1G5pZUNQ_ z%yi;n9aOY=;Bfw7MoK>JA>B^WnIGHQYq~Qn+ExQslq=gjVXO#2aXgYov~Zk!6Xe^< zv+JiQpI9>M3Sz!arZRfHB%5;%dLYjqe4gZc?27yVV?JTpV+@H;c2qD5=uNuz0A-AQ zG>Sis%L`9$xfBP2NW}<+gRkE{)sMVi5pB^I$@TNPb|**@*<^Zwct9nmbayJu{Wtjb z$EJd}gnEVP0+FnMetqn=U1y6Sn$QdGF5GT#jeKG%INwS@NRULoz|4#pd?=3u6QR{8^*-zT0 zPsM#k#repZnNMo-pLBzyd#T5{EoI9X=Jz?ryY0L)8Eoy)%8K&|&HZ1@H{LA?^Kq36 zFGaicygS6Zujw7~VOt89xAz6+6-j`T4=JfoWccg1n2+1=D!DbAKJw=FK7Jb)fO?d) zr%5}ia_r0-o&N;)dP8=O`gnr(U7z+bfW{M;Dc*;!xxFJkx4m6;nmnh-^T#1A;13G# zIBrNkg-H>gaB6SudGv#ai81wdL44iL<|;&-zK@J0lXaQ-KDx+T^*O%=PaWg57s?e_ zNJ)e~l%{e=$-AAr%fq@N(@W1D%cWmbUT$+HN8Tgk9sB%L@Kc_}BYgZh1Q%bAUd8NV zozv^oU?(NK6Fwt)ocCKq#(p1%8r9u+E=ywCx<}%6^LZ^M?LN}JKCCPJ!s5ByU5dNn z-3kqB{07N$;0y3EUq>HjZ1_d=ACHfpuhU-gI2hB0#l-$)Le1=1cT z?dL=MCG-JfjI$jEK?Bb`a6LddjYY zOiYFQS}XR!pj(9dU^=;Ads()ZyR@3KgR5{8^d)lEmJAH5sZ!n40@D~}9-+*Al$pAm zABhCrh=!;FV-!REm7V4-9e+(sej^vr#y#)Y_N-- z*nK*mIH&~emFuCd*7Z=!NK5Dfe-msX8m-;}tPR-4V*a^GB}krBx+ZQdson@|C9p2O z37Dv+3*5a@Lba4c8vynJQ&ra4I}$mZuDoUvT}L8LlPt@sTHFmxW%lrG_&>{oa9v}G z!S#FHWYrEt)enF72m#k6&OO93~c` zVqW@lMZ_qH4Jlz=EHL)@QzG-rvYlLShqI{Un9642U|3tqxlk6goIGXa{)6(qGZj2t zHi+4mWj2WU4@@svksVCG5YClAMN2Kl^T?l8Rc-rn)Rr75$HBk*u}k=Sz;8YU+s>j> z-s1k5Hse#L!7(}I*E3DVqIsBao=)(qQo47*a}u+yYX1}dPoB3^2hF95^Q2j1GiwOW z73yj&mG(>{+55mXZ`!YEg3I$f6z^>{3ejds0QN(SbMcWp;>CN&S5H2TX_eKR6w<$Q zu~a2KAz%*+VC9luzUM*3a@IPo=VQs{p!i-SEv&>o!npw0 z1z=eZlMd1V>^!jV2xfYK1mz}NW0so>q3k0t|>@R=RMrSVs#uS%YfhA$6_y8psn z`w@E;qE+!S=i)DFPbj^{so4g%A^Lwu-H?HS+>zO&fTue(gJ|H$H}KHBpRw zM+vV|9rEOx`_EJETo~);vc781HfUPL{hrMVyD3qE-M+Qm;L3q32i{n_O-H<=*j*}< zze&<|k@m@cj4JG6_>8}6R-^T<#MuLZ3fJ7K>ZuTyah%d;G&QL{WE%HsI)B|k zaP|ChD%j&?{D|Vv!&suG>$AD#*(PFG)6HOsbX+*0<69TVf9O|H?5gOq4#HIZ+Z6{R zpEF!QJZfPzh5Q;h5Y{`1zKXhADt&7*PYKtlw}5Z*>{Re4zV3-UTyrumTk&6YQ@=_kc@`~tn~DL6&WT4`*Z9*ZZv5L7|E0=@kd>z241lZYU#G&eK8*hZYXtTPb#Qan zXae1!VsD7{K%_sE%>R+%uD%8E9r*qK|N8cVZvl&nb1qB;uhF=htjGR<^D6!)pJ7=# ze3ma@nKn2=GPuB?AomPG@r-+sSQPhvGhrRyVl1R5h+|YyO>@CS0gd1ZZp) z)KpiUxCsBhf*-u7u%}G7wfSo2b-bfyHtvE=35o&mok>r-_&ao^Yau+rw*#M7Qwkpd zz6W>{@E2TGEI-L+@@pfUqxiYR;+JL8wQ1}IU8d8;_Y&-g)bH(KqWT6iD8{Qzmd?Gb zy6VE5>F|69I0|&`)|~pN!Xp2Eq4$YY^oQ|U>kro1lf8pnlf~|U73>aJn;pX1O$FJZ zoA`B2c4(Q-a~PtW-IOz#nGWrjigKcUhcH%TyI0Cy?QYI?-fYj@!=<(M2k4GUS7)n* z$W>}k@Rwf7p2uNt2j)(@bITa>_!?#;1KPz~bDUrahh1w{(>l}22yreE4ND~&n9Cu> znvC5-8K)@YG0Jm#J?Et^!EwU&&0e`TSx(hg3-@x^7s(d$x1W4NS5AlLg^ds{?_x=` zCq2hSJssK3u57+Jn<(cJO+6 zN_thtN${S2^0bSmESaNm{&ADz$DxE?TCwRu{WEtC^bf9A@~lq}e|5kXUNs$jC8YUi z4wkL4nNR#M6|a=pgdco^6Hac$Y+uDUx00w!Rpv1Gw?B0{JeNc|?QUShz>0PVHX@p$ zZaQ*&*$(YZUZbh1C(yp9O$Sf) zviK4-W%omcK6~bi>YpQj&C?^A5}kGds{{6ZUk`bL8?=QMLu{Jwg0-eh{5-}p(k*O79SVmlTbNA0~p}bR+ zx5>+R{M_*ixB+=ZV3 z84myR{?dv-!DfX@+Y%1WrYK$p2Bbxqu*}v>FKG8s##zef@VqFlo6|8x-F&ocq7&o- zB+h|5@UNAXAY)PdZ|+>={j;Zo|HqdRr-KSxH<#L%I4ny=*NJjR?xKY8YRlA7wmxMs zL?v6kHP`C-yK_9UDWnfiQs>jpnGW*ePsUe`M@MUR5$)plNnaycs^m{gGPh@kxL1jp zA)ySa9DfTqVBo~n)4>-#o%%kv`+hhp!1uY1@uA6;?i#CaFF0DB5B~(8lXd2MV>{hy zr>w&hWXr34BPx%yPgYt!wfv0(I}PmZ0-zPzE@Whi`r;7p@-LVUKB#xb?+mxpJ6K9B z{w9}~TwA?+d3zNd6-%lgT$pW>JOIsHV}mCd>bI)6`EpJJHJJe2G-IV{q6^ zs#`Z{wvy%(s$1l9q}}MjzG|YoCSGqR?K#q(P+BIMzyFy@5IaA4J472_7xq3aWvQT> zvbd$!eLSKW_49ek>wVF5c-F1@c@7sMi~z%&HXqA9$Q&7ql1B@9H~8Y|@GjjR-gS6+ zBpKDh`%&IMHrw2!BjuxI`|B9=S57H%51Af30-oAM%yE8eZ-n$?J?{nH3H(qEKTO7( zJc#@orgyewo3>qHHqw{UvkV^D&+>O3e3K2+!GB4wj^+%E+S^>VHj@{=Roow@J5Ca4 zfk3W@xKV5kH;b{0l2wm!9QUODBtVWbW0T@ewlNN;?tUT z3(2(ruLX#1&dcG{TY+&*A25d_bqfzFiJhI~8b?th=X(L-#7p&C>R|S*HWdDT^gKb;Ql=UMY)8>o5}O0D?;z1sNWKw13CX?N!<*LkPD%pfx{^=={W&hgIl z3eCYKjyNtCYC8COIInDMBKy_GW`+7vm%OH5BL-ACv8aXPIiuj)+cX`tg|vje z<>R>t+|%8&(2~wjhMSgC5(JwA&v9@}{MmFsvny=&IF1|wv>b(qQ8;S3{CDfCr-P~x z2Y%FwJ$RTOI3vq(h@S~Z4>+1$GaXzb{u$Y}k?+9fg-n%^N+hgZtAEGA(e>Kt;AI|% zmq}%`AM&mAtCRZ|F6j8_Iq)^Ue%jp&&RC+fiv(<(=RO5~nv3z;US856CVP0dpLfp( zk3OP{#lQ{&yGwu=Z&vzl-Ysa3(yJ{4z?y*Fpth9g>TzF(_kY1G)+Dz@Y{_`~A@Jpw zP6s`qZXVeurbij=d3&@sb!s-slizdBeEeyx)9wz?5`MiFe(mC$SnGuKDb~mdEwwX8 zzDLMclv#Dax_}jcZRgqXe%O9-Zx6(g13W~U#ck8UXG8jm^sh*tVRl!i5-2R6G5H6M zeR;1F#pPd4;ci~YFt|#4hO|ePPrEhk(zJWRwCMCoJ0D|UvVGc}5mcJ?^I=+yO3^@$ zw2!Qa(aa`j2EnF2?crU0$F$oYN3X?wb$2>McpH~sBde}Fl1H}UZt~^Gce~a^j6avz zT+EwL|L;wmOdMB2gf?aS^Vz{pvlRzGbr!tcYo~*c`aX~A-DS2MX9?b9xv$CgFUj`e z-%4OwvhJf2QXjD&>#aB&}bdwhH>TG1nh&hn%^McUn@{g~Q(={YAS+rQ4zP~%05o{s-&-E{CR-+$ic zZ0k30vFozkbX7~XALmaexU_$jB@H*Ut@M@5BX{k!o4;GZ*MBYiBjjx+J0dx3^VjpX z!E|dfJ7}}mm&4#1xo+Csa|+Fr`}G$v940INa`FdlxEwDlV!S`vL!5#-Vc%;tpA2ZmRLkH_=m&eYcu z=>xtYTpi%r*@OS2bbi@*Mll{mek|*?QW22sl&ly7*VgN&-8wV%^0o&)0PlA<*@@Q+ zMYU4?5=5tw?Xjz?jZd8c-$n5KvDyroVGpxo*y-Oj&%pnB!*uX%y|c1N5cffAO_`M= z>ECc^QrIGi>Bc$o?j>)#@>>6+g~0%I14Rx{FYk}@p0GQ^?RlWBr)lE5*|9VetqC?~ zBHq|)@nn0!F?yqyCsALZ1B5=V-V}sVQa>LTaEiRA$y=09l3C}0or}SGfz5d){R8Y> zp&c3V+_JHg@->|8UtVObx7oAfdsIDPP`#i2;5VwO{`$el7U`zkValCrR4W{_WGjKl(XWu|8$}hd2`fm-m#Xk(y_rFK?a>V?Q*PYk}SJ@(_UfX+z9Hn9Y@xQ>6cqi@N3!~7 zqtD(s9XuTBn$i5I@DcXfm6HiRJA$mvBv#CHTJgP6%IO=N4xXu?8`FL8X;kpzqQ0L_ zzwC-ce5UUU-zo4lzI{6Q2ieYsFPZ}tIu8?Smh-91Mi(77JO_R6UDIJqko>(Zz$Sqm z=DFy1wlgS@J^9SN&dyvWwkB!B0m|+{V#&_M*iQb=ZPURywZQ5f(z7nc5Aw3N2;DDzjUY&kvG9L-YWDEG}-UF`<`BFqnk)P3zF?{Lg z$@oG$=n?Ssz84>U$h)I4uD~}@t)STiCe3OSZJqaA=w$?Z)AN&f>@s$V_N3=hf9Iw> zy;q#}3~5Oq2jO%;m{DOGWI#Rm2f<&n3*R@-8tb@Cpz9FaSi3pDhqQUpKHt~H+rG*D z-m9SIgl3^oG*Q$Ua4ou@wba?#B{O^*b&mVk82=o37hYXmRrdhA(C78Ki1BH&)r?O| zN%&qRkEPuzzaJcnMOV$0GWqFwilCP)8+xLO#U{zRm zXf5u?@bV#dS2n*=VV~$tIrJu064%;u3j4QQZXjWWd-3y-UmuL>HEX}TA)8;F?ObE{ zZnbh&JKUX>xM{O+kAb^q_jGV;SZ`=LewS9U&u`1-muEY>9X?_b%Ihk_LoIWyvH5%+ z+=CyQ4%UYAFPbkAFE8@g>hzt-^Uvn9WK+vR*6ThJU$43ZzQOcAB%>pE6kMa=I{oL< z?!14ycN<*MUIlhZ)h+c~$MTPfy{5QiEu3U6Y#U?Adq>Skn$7U?-JSy9mWQU@eQd__ zklC!lvEF9ytC(E7dB2skoLyUYdv?y9**dq@sJ7Md{ygtz{5V8Jw(P$s*P55cu|eY&v*aD3hapFXMaK_m4fAfX(|c z=D--Xj!(OLmCy;crL+*fBGXGb3wbxmyN^kRM*QFET_!`N8^1Z*PpI>n?2s0O+gR?E z7l#+D+TIl))YxDtAyaESyet5D6T~h{hLIS2a959h?nmVwH3h z>yK#t(%X!MYA!1{QGGk>ve)q4jONarw@HgoCeOBje>eCiKQkSCZLX_-1>cYS0xiTH zq}!6|q&<^fS5172j@fI+c+mMBYnA@}3Wrgw{pw=$3qEp+np=;(Z~%GpM1CZVv&nfM z=Y|~pCP=m5undc23GKRG7Rdw2mEro`p=#2vA^m#3RXhRr2di%c&<+B5$5#qLO375$ z4SZ`mL+ZmktN^f)Z_D>(9`XQ|_=1w2^H$ea&$Y3=%~`fGuAbS?~GOk$UX_K)8Cp7{(@(x-$uGrSz9=d z7w^*wZAy{X4yge*O_-6(GuI}8;e`~@@M7X69zj1>c@|U1mM9o7dV`kMt9AqKB267> z{#$8G=Z@-B;g{~*knQGpsCM}oFIlI=y0-bK1DtF4JJ7ij+GBD`GW9I=+DX0M5zgC) zU$&RtEg@~>U$fn739>0xjJUp8knaR{{w3Ipe}`QV;1y8h(}}mEsq&Pv4x$S* zKFf+lDu@DcRu)8o0}Xg(-~*f-{gS`q)TQCuaedfqMT;L<5w_Fwr*qZt&(`)gg74Hmq zzq2UXwW1a|ee!0vKpz-4Xg$sr8U@ep?_ke+JQa7yXk>EP?)61aYfE-WE2~hm0w>}g zDzmR7cTa<}?z_|OJhC#K8((9-xTS{x796-7!dv2WY-2~KGz>Q>w}Y?orRbyJYYxXc zvc=2hoaEt(Y(G~uwVGc%dB(MB6L|u0A>Y~@36zaQyWNX(hv-OY{T*V14W~g|@S<-ito?~AYv9*n5Nl$R;hGb6phtSW#=Ep7cqDy|+??tQBy`vpO(eNM`ngln?mR@^+|MwfU!xLu|BiD)mxudUyBhzP z@h9&C8h`Ru$Y*PppI^PHpVF+P$z-^O!L@yQI$(=#T;>|zFrF00)hP`qp}(0u(shr3 ztEn*U?s0=RvrCj1ib(F9_3xh>(rp=!GQGmryHnBR3B!S1rT%Mbz#nvUI{0f}?_D~# zBz~7cClti`sb*xTo+GvTNWX==pdTlUQ`tP#+E1SLR&v#R=Iz<;joIGLY&VILB@8yU*6lj} zgm}?o({Asr(I7l9zIPeDb3?X?4ahx45io<2?pekmY9&4caBAM}1@AfVzEkbMhKcL5 z7OO@FZM*ztu%R5I3Kzrs6^&aOmoE=e36I1fw>jIyF4Y^XBk|W$#Qc}D9`OCh2XaaJ zw$srPXmUm7jfwq9UrC1^qKuQ2aVO8^Izrjlp!>*UQ#mwT2V_Bp?WJPMd=@9be-`|Y zdi=?LVfieO#pO0L8bd$%7QBLZmu+kzXrp6k#9ct=1LST8P+(<@tRkt zf$EluDfP!r$~r_@7AFSn#K#!sZ*d>2XqDJ3D>E#Om)I;zif*S+W*2RiGvHtFgX!Rf zAus;FXS38&|9m6;eF8s=`U)D|W(P>O*;%K3ysP`+w7U=5-f547J1bzAcP+fTRr!+I zZM01cQAwY*WQIt7h`dAOH9kzbDdMn<$g_`HXPw2UqvW$cLBQ z-U!!Ta8(MPwoEU$E`V$Ck0KpCPH*VwKF-KEc-$y9ovnn|HrtsY@kNku{i%nA&=Ztl z{$_kRzKm%8*x2fhkg~xQD~pG~(&rlQ{oot=F>84tKacDR;~C)d_f{Yp`b>RUW$Mdy z**vFUb=b4md}R+i6|9oS$k`(OqW)FPpMPQvN4^&GBdrea8ST_Pql_GT70lKUeGFj8 zfB((V8rVX?z;XDe)4>X!9S)eaohNj6$=>VTq&r8tcPbtFg^!1%kHdJ#h9V~qWh;@s zr7mcEu4F$2uEw8DyF1@(j+FITB=fNkug!LMXM0=Cm`YYvIqxV*D=<0L(L_80Wo^Es zAKvIwehY=sB`6qene=m0I3|kwb()oQCxATGz4q%Y17LITkjne#>2N<;C+#f&>jU;K z-*@r(Rw-9-isBk|&e&^;0qf9b7<_GK$SwRc^c8S&^rDSNTwjS|#W@P@x@@lBn&c&W z$b~teIoq^eG+9(;;u2tahML6%HN|xBw}*0Te=+U${hFQ`*>T1@AmeCnCrkcIwF4-D zxIMnJ;A;Y(=)&o(k^a{%yD-AXF$b5%*A0rruZACi@1vTJMq5}M6>D{Vya!VwF2s6B z2HT)EQzQ(&*=#MK8w2;AGt=&TbJNe;6a4m@V@jY@#5fzuXZC^T6nKtc@x)#_XMzJ}k$J!TvEt@JnXZ8W_7i5^nK zu%6#q#qWT}NCy~s4dZi`_3%(eM|F*4g~>Ao1k9qKW2N7Y2do4CSf_WGfm?lg7W^lE zvC-|xn^e!#fNRhg%Im&DwV%f+dZ-9+Y_>7~3u%G&#H ztT%*n#>TizAMj(`Ng%X>#)v6nS3>0JQ0#J3o?8ikv4Rj-FX{9!tHLx`_TK1Mo)1@IrLo&{g(B3|N^c!}498{EVr zd%=JDzbn=sO$Ul>w>QDN;H7)$mWW8R6~yTkpEZWrzVqu*me1h*A;L%PKF_}9Ons_Nf1M|E!ke;@eA=M@~!qwYTD!RRzK6kF5K$)>f= z&nmrJeR1%ws;VB2;j0IB0DOff7u-F#B;TR+6Y0?nYPS#&W`p6Yww)qv-&KX+-b?a! zqgO3uDM~$IVA{<7TC^1Z&iq2q8Pb=HcQWtk)X`Q6sE`Dz*ATe&)E0uzhHFq|eQtcm z(`s+}Ny%)?om=?`xT>CA2zE%O7;Tj4brBDfKl(!A@WBzKbIxmlKRl-pyguZI(fp41 ztMyka;x7I1G+)Fv* z3k$*Zo`&Q4R%Cy$=SoY>V@&OFf?H^fj9KbvPOikfCzX%gd#mt&CSayp+0$hDXpwSU+Md1C51K z2H0=OIH6G1;z&S2?a-<4j3JLzqhvWu{t@yQ_u$3tzDVcsJWPtIb|;sy$JHz*${xf| zuq2ziJr*{j&5eFLGqar;K@BN+`7Gyx;{h3_PCabmI{czS@a|H+8m$RNd{yh+=@(S9 zp3U8U=^CFDV3{ z5{=Sd73U5__P=QK4YmMzqd5TBLz*za`OIeEm5vfWj-7epabNm>HjbLNqvUTPe=X1Y zNUrY%)(Px@Z^LZ9+|Jb5N@`!KMOE$=T4`*6V9cG2ba;Gc<|>eMux*2IK}}dZbCmZOU1Ls!Q1sG zh2S=x-Mqj?^Xsab4UQo#gL1XXwc2J0hexm|?+!46p>gnmW{J~x&w+0X_#O=T`z7_= z)bUxUE&cUs9~BYlyL&04vz`8IECl-{BaPl7`4RPl*ALMpE`TKuqq@dx&B+Ta=)=hx z(~8W!WR%*y$so+PK}D?Qbt0#bG!*RxscHtPtcq4OGZ0WSz$uin@Ge(D=*-7r^&$ z>_^oe>NyV1M_yA1KB4h4*`Cy&rYE#B`Ax`FatFBY7(7IGd@2~pf{;deSP5W-2ekSG zYx!0*&uYFqWA9Uc=M${qTNq5v<~pd~>k2L&geqzdcL6H^Yv5TQ!TN!n1y-Ebf^7wM zAqG=>M}gJ6z7T9G)r%sYX)@L52QSjw!lQHu9>Jn49;~BZZSL0%rs!^Vz9)a?4wkew z`){-}cceiiS}3VWm#nBN8!u{D2|rz02)<5TW8*=XSbXm^M-AMZ9a={jEfAd~a|;4l z&I2L=yaOceipXcw_7QM5wG;wE)*o-~z^f$(DrHz3Ylz)l+1^#g?&OT9^mQ-MHoF8F zVi9i%tX+Nu*?x;wp+8Z-KZLv^qQ#21lJOMhlI+9inLsJoXV!lDo5{+e6cWj*5%72C z3&Hmj@R#W$t#oO+HW?kv;vRZi=fz4L5-ZSYxH@EZJ>OD`Tzf83-r*I6;KL!$j@lFP zDz7u3OTC-iE5O2bcf(>1xJpTAzJ*5G)3ciRj8%nzr52-=_0k~;pBcSL4-z4~KuSg^VB0H%>=MXJcb|TkNEvWVf z%81RbLpdQ*jE1hUhAJ)wG-Vnb0W-aph$W0A*-q-B{ya^6PIVQ6x2r!R9$MxX@coHQ z`ltyGCqN`HuID$`LF4JwTqeOkCU&Ns?xPn0wr;l0O=5Dnjplh0yN7x6UUjP}G8`|whG;*!rA8v@rwa6JpW`gHI&0&GrC zA^3dA_t15HTy>f6L83IhBANf2eJefVB>4JoC_uvwRAp}@l-E6d(&AB zRVBZ6cd%^-SK*dI@K8AJMY{4jmC1|9|M=y^iSG6)6u`BxzYshxs_ej3L0lK0_GRHtrY{ck0nmYe-D zYx*Bzfh3jdZ;G6%pLa;^loAqHd zmm{6pj}7*!%+a+lzz)7;b*3BT>88*!0jmNu_7mW594@%CjiAT4pRdeDke_8u%+GQ& zh%0pq95|XYb2eh1g7@;rw>!-w%q5;5+!}{+f9v-Kq z-qfeE{cE#>`RtIKD!Q$EXJQ|)C9lhReYJT>C`V=W_n?owzYv_7tt_+aL%v*Fo$X&K zt-l`!Sqil|VdR>L%=I;rmQIZ*XOO?+l+}5EA^4K#fAM%7$jYC`DDoieUW68(XZg$) z^Xs(P!8hHpXJ}QHhzsPu!47@kbR6n8Oqu!XSxxF_&?;Bw}Nw_s&$&k>VV4oSb3)gstG;w}&|w z^0KJD5q?h-gLXu9LZ4yWj%A0~zCw>g_|+dL!QcIfLh$8Mnk$=!eoXKu%yMqK+#lxO zTXZA-f=?EL!ye!4boGlQx=P;-QR6*ic0e@Xly7?&oGp8?l}qaw%|V+Np)C7dT$atU z6oAF!;nLv7Z*V33p6=9t|0_*sV7geVb)x zq%>?x???%E9e=-E5w&LvzlXuycc2jbS(ZIE^R2&=^`4n@y5xTN_`xXW8*GIf;zu%>R%G; z56!-d6p9a9>4_6rmP&t#NK2fW6R;hh{|+7$Cble|Y?qQP2dc0&xRSY<5sbiGwoQC* z;I+xx$KJY=&Q@7vtQD39cof5NauDA1dU8MZ^iko&1i+tEp%rI0;YlHT{`S z(8IS-T+jD15)@tFrfY7wh57TWqd|kGrCqwa#IHG`6A*~JB%8DSXAN()bJ}pwF#HN` ziEJ$N)TbSuAKXfxNWy8BWiT|Gz106a^&fx!(csEZSG$DXF;UHuU1FErf&!*( z>1+A6ed_%@$R51aG&8s9eigr0+KPMuu{nYMrtRrPtI@K_j9g2yd@PyW6}jg&{9!LS z>dqH6xnq80T)=qyY6;*)?6lx6^e$yP=dD1V7V><9c2>2?vqabhPA}<0XL)ylclU*~ z67lUaeM?JL>CYzfXt{Z9zv1G3&eQwjqrtmX)> z(kX_3b^Phk;J_99D9WGZ52Wv}*>#zTpDBr-Wp-ZIUecagd^DIZ{ljDrkx2foqYj){ zD;Xy-23Ksm#j2ek&ym-`vo%K}xdZLQeQI{bvjq+%FKM{;F5HYiY3b2m?8@Z2rr%br z%9`w!-Oxq);m)J(d<-kQY<`(tX=ftqA`}jX-pLJ-qXaGad5`*>FyopC90e7b%L*EJ$2K# zM*VIy&s_i`_{-R{Vg25b9Zjd7sX10jXy#85?Tmx(c+b)Bta6oc09e)aM}rmOUr`y! zI`D9HdQq~@XnyiKP}B^7?`HadXGe>&jnQP>^f5iKo-`e#*`_pna{Mg@b{Lr5b3r}4 z?-&}hI85g`UapOhweERIh69iMGqbu59K+z~>pL2}kSauF_l9LR0NzWQ!CQ_7Kk#X| z!~N_t(bbw0G1jb$OZR4jc`-OXq$WeR!Jgdex>mhb{~q--q=t#dvLv+J_G zth%uRmwtNum+`i)yYRmZ9t|d_i|8|=3$%}r6j}>Qfu)GQtpoOlCpg$LUILFF(Or*DSxMaFIc^Wro?lX(G!<<)bXS+&+VWb0Dt%Hqru-iuZTa=8_VoB z_BW-{*CsarW%X1VOMo+eP{t3l6Qy8wK>F&Z1C{B!!t8_zH2#A;Pokd7_MK3-4e@uL zdW`<{(V$cEtgM}p9^~!U-t=v=u}@rtxO3Wz2axopSGh4=;zECLNT&amymEN}T4rku zgLlL49yuDkWPY*i$WK_-2NvV<5~<}I(0$wZHmz(ZnbeR#2SHf?f8&Xx!OW;wrJQ&A-$dWolY>ZIPwx>#X)=(=3E2I%pcRoV)ZXx9nrk>r13YR&;H_Qa1+mtm!PxmNVAH@Wnt@z zY~1MuM@YMqwEvaB_oBY_zO>nVPk8$lP*g37;^MPMgEp0G`kBr(UY%)0zKB0G@qU2! z4=0or(NL&2U^zOif4R2}-p;o+9O#fMooSUGPVppbLFR-YY-3Agb&fwAr2IYSjs`la zOP{^`9RW55?1vIWM%PL6%i7j@7{5|AzN^>m` zW|vhM%d9NPkC>*GX^l?;51?vWwy@q?H52?M)VU(wN}PB6{-;*a%O53Az~!0rWuiD) zi1JX125FA_yXpYtou<6bnwj9ca?nNdJxRu3-bmp8A~$ME9U_10xHjo?mcn#)t)!we_t@; z_AnR?>fUR@Jl3Fh(3C^GKgj!K>PPd-MB{BV80oHd_sEl~S!8R)CR8-S60D|fMT{o9 zU}faBfVS=){Grd83Fe3TS-ab7ljFclA`iHI(9TTCNp+%*7+fr#-)YOg=q+0QHD7-} zc(&Hf1T0_L80?~*#kd$&Iu*;%xjq&Nq{ybYs$yUP6zd7p;!WYiG1!PEUve~-R}_1J|ovGsQNT|9n>m=AY^s5?#+ zExoLtkOAO10iH$Ar){BMzD&*-ZFpWdTD=)Y3~j@F#-I%B-(T_oodR@#Tlr>6u)BH8 zIJY)LRJtUWW;nFc=TxkvxtuofpkT}nz76*`eTr*}fa`g{;fSn}pGU#qQQxl-G!xtr(tFZ=JL9=l@CMMRQN<|G)F(XNRlXeC3RT$(IUK~}7rd477R>~;bNG?u z_go=c&g%&i)u~;c+sQFxO!I}wjEfTv-Xi8H=TVWtZsE7QLZ~jrOeOpFQ2x%wncx!& zk}{gcN8K6o5qv>rcboaM50m!dnwj9R=9l?KNW0{L^s1smA^lV{(zUqPMUo4o-*e4O zkdjhF+bZJU=@#8t#kPIcvakW-Yoy#QlFJT>H%q_vy0pXFc`s`!>t=#<0$#BAwO8T& zXaxqH|65*^^zkiJaUXc+UON*!KOFlcJ0YZJ9gmSfpJ=BdK2f69 z18_h^d%AM#-o|{qZYDUTeof-BrgM857)48R*l}G)*ps8JxfAmpiNzY<9i!Yu8)w`Z zbCItLK19glwYJ+GwUTXEcqhI8Z%L*<+kH>A7nTf*qPmOtI|ZJ`o|#}wdP9d-Uef23zHcTtuJq9yiDYXegS1YOlC@bm z_;b1lfZ>m}zt znpm>r+%RR#xsUy0cgzG=&E^x1w_`Y^({U5pfz`Z{MHGo}^klwCxrYZyd3FHsH_mzj z3or#O^T66HnHFx;&JpTRfA>tVCxK^|$#eB(`t#*oh8sMUenYZW$9lnD$~Z?EefP`+ z?+oYBC3FUO+N9@GU7jz!m2WA1#iLhQKdDZA1)!MMYQ8n6r0hypEM}4~DY3WiEyJes zGaTKHy&ZqkTW5k_sGdc-%@=v!ifvb<*QASdI7X7gq@R1=Oz_o^SC_4$+B`McV08FH zKN`pe8wYI}FvSd^VdIxxt=`o#Sdk#DcVC#?yR2pXnB;N#RdN@P()SMH>)$;SylFPt zig*Yj!$wtu-yxMK@x@VYpxBF*;kB9b1r018_!P@s7An> z|J+P$pT?{@t1fgIH+PzkN82mDulg~4sCH+&wQl4pN@GCRxN8J!xlQUCt3uy1Fj3v>Z(R!Hd3 zw#=OtJ9w*I1aIY@0q>suGr@CwxvV%dFFNpMxG|lx-M#;v-p}egU?E!fSaO*F#*23V zCrflKVB|s73*MG5%mlv{-iQVxpJIGY&US|dD?dYI-w&=c;F_zxiR}0YSH$CWu%pJI zQq^eXQu(BDTVYd)@y7!JMtYqRi0Qx>kk6w2-Po63oGHJD(#kI5Wr}f;V3T@ZbbMUT zZ%N3ab0^)NJfSW2PPWGwjHaSjpkt?OFkrjA=M zGVzR8^Hm-PH5lE+?R;&r#kyYbXz&ar=F`Dn(|gd*4>CTbI%yd_c>kY#SjnxmHB;f+ z3BI#moeBQj&-)5{jCmq^Oq-I+8^i8iPAMl`R)M!H-N{}$0lvjw!;dfAT=DqZ1{p2oZ!?oI?i}_SKce20y&`i)in+%TVrcLYiOA^bXMXOoJ zXOk{^*#&eAQnvG;3xj+83jd(J7$yq;0)$M0>v>>38A-g;G&nn(e1yI@O`RIPg^d!9 z)g^c;Gh!?B*9)nN4sD7gJ3HjbaSjF%zpUWf`QAvzR{uNodmcJGz&SETsn3@8vOfNI zGr@XK$MO9calcdN7IQh_c*0WzFJ}jwyFT*Gb|SG8)tjIP9B6Tth4&u+RQbwGd*)_< z<`0Xwo5d)cpiXn%hra#jOwbpOTbb;Qcx$_&N_3b>C38h5T8_=7kuC0ty4U|+V?IZ6 zp5;NCrg}Xf{U&XrQV_>-8_8i%oT`2su=T(?_|}FgmueM|;+Cm5@uq{{A>x|>tObyd zp)r~~P1{aAhHs>lmqv5d=7Nv?n5agvI0!GBgqJyA45a6{chUEq+7CeNz39E4<=7ww z;)Kt-oZueqZ%_l;v#*!D8-SRfP*e5xY^dn$1-B!a6B z^LLuE>W<9>-+ux>%4Bev>_Ud5W}lNBX^6r+!6_m8P`XL^As!Th*T=WQ-BRa$ivA)% zNp+x`BK%s=rkM??^zS&|6P7i>0*=r3Rvr|)SB%w5zoM8VjGS}+n28bm87I-LOZi$P z7i}!PUDKO-qlN6dod^U7B#>;Llf?T8>t7b?hpXaxrFIp_e5v1sefcB&Pdw{GHL4Z^ zs|WVL9|3s0ex0b9#V5rw#$9*=YfukrxZ|;grG=W*+dN>C2S9-}kR*u?lb}#Tr`Ob@F;Wg=R6!8*b=91jN1F>c`oU2C?hbO)83ZN>U05;2*de8%e zT9^ntaOJb2>Ja_d-b~iq<^k!oED%}gJ@#h8-mC*Dt3rlDO;L4m3K)v?cI8m9LTaUa z&;yuf21tKKNtTg>D`v=4psiGfHA{yICxk)aVv87pBlj~NznuvViZ+TqP#;reF}>-Q z(AOoO!9~&^{GDHivhj<2A;yQ}v@~9w`Yv-%^LL4#d(wAiaRed2h=Q$5eF7lypfC?N zeQ_A*Tmc7M%eTTDhpF4x1JK)l&cyD3PLf&PpE2w_0W7`ZU6Swv@Hrmjf>`bi`kUA{D33?%z32rUrQ8w2iz7gs;M4!9G-Y)M2NbTl9+^gtR zu=vgE5~raSWn?dmQs(*UW5FvVhst#Ps7ybMrt*KjBy~EZsS&4a>rFdv%U$`ahVa#>|UO zwJxBk)rl=zp4m)dU7A!(EQ*8|QPdI2-FM}&;I}-BA0_LMsSi`@_%2k_0S3r2_XDgq zW{(BpZH}MWeBeBRio4DfAok&GKVda4fJKYdmA3Hmj2sEfqKY698|$r>>4!cG#Yf8C7BCpRBU+fPX0y_N9WS%-*HZ-CELt5ns^$QmrJ(M z1Jd7D5@qDH8~#RX?oq~Pq<}KzYc}vL{OOyE>52uQqN9_kuXpaoUU}ZJ;Gx-cl!zCz z44F!APPV67GwFHiNA+%tS2?s1CiMV7X&m960&SP+2cV#TQ(d<6aBD1u^GVfx2>->R zW5L=vv-Gvq%kyE@Hy-kCWUb`Ozn?;JgAeQITjy6!eUb;Q0~FoLH<2n`rRG#}XVyi& zEH|_eV2wD8?=3@ci;8(4K-M}5b@Hjc%mcT*(P*{LM=J=2aIpXiQ2yy&npN{*;*(!- zEcnC!kk9`rkyWkxD5ob=^#C8L3#86@-yHRGzB&2s7WwzD@bB@ccVz3XF&~=VcksTR{IAmc4cb!#4_E9x zDLMcR@IKG`)unbwneOUo2^!y+OylOekWP9?_;Sa>y=syH1z;_}zT(Gn7ZLF#@dG-t zJiSwV6j>HVlHy@=zK(fgtamPna6STjA+Skcx0U*WBKs+_4RwxT>I2w13OHG38yY67 zxuQN{P*9Q9lR!rnZnAH3ZsbARtFPl5j)xWIe&dg~1wC11sm%a&O5$q1F)6B{W|vAf zmQFrQ-Ro0q@BrU7-M#zy`H`IO24023rK|N=@J&4$Ys_&rK}c2l3bLK?Y5j6+T#cRKZ- z;}(dtVTX2}YcA*yzH+I%>Gn$5iyvCFc!6^EEI;P%L?is!pX)#)c3@msJWc~4J-S^c zBoaGa2+bh`G%Nntt#RcaVm$+V=5Jc1efhTD#F_HD=tt(a@kxt&*PizWsaw+V4lJp3 zFU|IL*b?^5N&a{%b81@j-<7Mb`uSDSc-8_dP><8?$AYKx%pB%iDmQ`a`FWOiXLyII z5Z^x{UFI`k^b%IH=5p;v;a45Ug6Hz=YzfwIT3Ec{r^;^P{Q&P3W~q;09l-j5X)C8b z8sA=ETY%MuWp?_rpg8)lmW53MrlfD~6uy3|GW|V3y*h9Q-=ds}Pmy~^;XCuT8r4cJS zshzM+6EBf1jYwIkn-y53Ac6Qdnk8|G5jmLs#DZT0G$nN+Vsp#plgG)4Xd|!W? z9pZd<`1Fu;_k~30D?}6B&$a+}kcrb&hopxuv3sIMC}-bI$DEIaxy$8!CVcxTb|3E! z@$P5pf9epoZKAj$ErCEi`%nxfs&Pu0Ih}qsR3{T8z5qZZ(3^O#u_~JLvc+wm|NKvI zzRWGhf-n2I828Vber>!=Y>cbnT4S*|noZ>%Rt(!bRqhAp{#%a)XLxqD74tiOSBkE! zF2<4#>O|~Bky$lN_5SCZizQE;{Zb?dTgLz9`rb zusy)^tWPh0Bf!Rhb=2@9nx_mKvEKJ&>$!~UM)P#z+`xiuz~iqmv5)Ny-oj(sh;ke6Mgek|csa-v!J4m{?JQxJ_$m8VgfbPaf*D-V~_-`p@^u+^= z0rX;bxR%*?T7T2B+fwKkZQq=W{H6Zmt9$@#B=N17KymT&F~m}9tmb?Qf7`ZW!PE4fk7UgPV70*hL;&)v%GPy?#0V#z zBVA)L@0)o4UcFzVJMZ8rb6URD&ChP$?H4{TFCx7%^dEJmzL4$iGxh<0B$SV3rV{|2}ZfP3)W$AY)2+$c5>eV&j6(JHLIEi+&T5%5?wpJZpqbBH_- zD^F?*``EH`>h!!9{ddQ)U{ufG+LmJVkAuzG-NS!J?+ZyYNt)ODG&^7olcPUvDAZInovTT79__*u|1E-fS{mzJa~Gt9uWA7|$0T zK*1>Coe{sYIQL=2|H(+y5|LH`-+{HT99SOM4Sd`A$2xK9jR5ZQrr<3ky4@#QUrdB^ zD1oZ*nDtSw#h=FBd;hVZE7aLcF2a&_)UX6CWdaUGMQg<`twxLL)CuDsT(;KY~dL?%%wImIMhLXImx^2yepogDA;jeqrl`!(MPZ| zz(!)QMZhis+X)Oec%0vwUc@FpD#T_*_BhJm)O=Xx{t~fgJ?) zo68>!nw5zDjGtR5rtdiRvJAG0dnB1?^c?x-gDawC?h&waJZ>va9V@f;Gw5Ta%L+fA zdj1vxs|U7R0FzJHI6Kmq7}6spXCQE1shn=o&ZXT;m6jUpz~4hTu$}&*HfHm3m~@S# zE8eHR8VqXl81Tiw!VzxvzDs-F2w%oN<{ z7DueQ%m>d6@S&O&|B^1(oluMXeY?bDr@xS(DYazOc70;ytcl9Wmc&bl*EI0=!OI@C z`CapqvuWiu|HnS)=YeDHyeG!2La(xU?AH^J5-&~GQqU^gc}!Kbao-1y1wVmUYTijX z5zfe`65*6@BK?DO;*7R949y9BV(q;BeAu7lE;}DbZ51tkz2;H#8P&{nZS4ex$x(Ij zBiNxBh&2~~9j{xm-{f3%dgcGOx;^&4qPkVwkyNbPtErp%zGfG7;3L{P|5^0^hmQqZ ztz#d-=6nu%|4hV_)rMYZrU}?hqMdc*H$7xlUo%e4>hwQBK?xj_-H9%)R|UrU_X)8! zjL+7;-%NX2I{7PWgW7hG`p*6AvEX?^h#k1W&zmFl*btUg(>ch@T~AN5ruut{J~HHd@dfgLG>^#D5o z%;vW8@iz#puj_cg_S-nmbvo`v7?;ylTjPvNW$q*2esF0l^-;YK0XqZig#x&GuT{PC z{5-+Ci@c-hR?a2PveY?QvxJM~Z5?gle$M~-{4sadaHMY_C+@WRO2??9m=4nIB%SFc z^xzI@(?|~%ha|4-yt6^loF>gz!g?jqlV4NmWgC(-9|DhLIj+H9!J(=N(&qgBn`!fiV@!ef2C)BsKUx5FTuSxk#Zrw&JUB5T-u9tVM z{@sq0(^b^(oxI!4yR~Xx=i~6W&eTJR&4C*U*B)>U?Z-}M{w2;mvqSw_Xm5H{Qx6a< z-IO!0X1Ke9&w#6E-SL1B+hkl3pIkG0ys_J-+xSKNDeI31e+nvn#5eQ6jsRODzzWG@ z+ox-EeNRyax8X13bv=JWq`ye|W~Iku^70WK_J1??2H<%V#pC~*_dQDB4JBaJC{=?7 z2~tats8On>ZQ7=3C;^K^2oRuPfS?f~6sa0atyR+!rDDM-5u;Wu5}``LC{b#t8Z^S2 zG$c)}P@qc1Dpjji?*EzDz4z|D_ue#o<>&Li=Xu-9-0sZo?Ck99?CdT!qcLGQ-N2;n zA#E|^-l5V;Ifh7EMcS#9L;6w`#!4&cd(kY;r=Olg$61+dNjw>?;uh*zEp<3RQ|G1J zA*q+z2+9Ywsl=bm2?)h$n*;+}%CL$&Wp_R1o+8>UY7Fz+S-gOW2cpMCo$J(rLiVc_ z_CDq=aj4VtPaUdG;&4yg9W6YHQ}`-RKyRP*Cx7f2%B8n!UKlVozzvjvr;J|bi4I5N;xTsM<8nR~V(>QIi*HcItD~Xm^U@!4eO{vX zICUdJo3MKq4_NdUkzIKLyjZZ;=S5x6M38Yx>RkVy0ry?}?|f;u@7>BA}12>IWn?QG=4$kzK}6y&(9unKXwefe$SJ({i9-f z>>Aq;yQQLW>Ka>w+`s>iep?TP&r6i{mNDlW{~l2N<2?ODy1kolCU(GwskHZc((WVe z|DwG`7M;zs_XSfcTN!EhllFg6Hj$BT=&ipnmG+xjzb`^;?;K#*k-1gi+yC3g++Se~ zIIjbr>L>QzUJ>x84zns>{w!doIj71xBt9c)E7IP7UcoD5Kx57J@rgl z<_e}2qF~}3)7Bog&be8H%agfJ{&PUjad!Sp8F(uB6*GA)ElRmhpcfsvx!x3(wDHX; zJ)_>4?$wF5RDLa7d)UH`X;mzbVZI81kQY8SNQD8cn1g{;{O&ivc>1|_s99Wb~b2~MhW9vA2g1Imh&pV z{Dp;#e{aPuqn^%@f$|6Tj3@pBjl_L%8u~d0thz02L2{`e7v8ji`x?fpV`L0H-QX?< z-VE;A%9Q(gfzJxyUTEYT*1(wu@@Ch?*9O~qi1eqq1MW58aE>wfgw6_rG;3QPc7MgY z%-`ZanVv8GDFvNX^HT1eNU`%Qd{Pj__y?nR>{7YWh{4p!E4v1ZqNVLeDKD%tW{0;wn(l6Uk zB1hSNP1!ytWjocBt%dh(_cOjUrrh6J8zFc$HIDApa_$^@@^Z8yHhEf4RwXe=GayUk zg7yr^q6moU(zauXBe*!_)`(2py(@9(`A%Yev|Y6@SxFr_9$>#By8c?;jqQw|*Ur_L zNs3XVHp^A%_jyEJ6qy+_St|Jrfx|(+i@XJJsQqmXD{w4IUJ4FGi+7#-nFHX+g80Q| z{0RU0?ZfH$jrb5sfpr0sV|>kg5XNCY$=p}wi`9Ieb7{(boBiIQhR)oiz4gQ;cqI;f0YtlMD^)9hEFRHlUY2>#BTpeE0}th;aL`}0}G@WkBc>5bUG5fcDViX6=RF}@_^ zyOei%1Xc!YE3o$~#K5Y66<(4u=d{{%^}w2eeJqf!8Q3VWB>`9)uo=h=DqEih{#`Hl zfITJ<__U72Tkn1uf2~{1`(R(LNwRbk>bbNv0XymLZpynK{GH&RuM`a||0m4fft?(H z%>=dw7{jtYT7O`>fxXkhM82zl6)sPiJ4&$w+SR{S#q|uvm%W_tjeKvmc?8c2V12;U zSH#~^?+#!UwCQgooyv!=TYbH+GKhR=tY5ZfJu|vVMgI1Je_v%Kmj_jzl5NiA|HW<;qLB;Oke=S;}LXoaBh z+0x!k7)G1EJ?vi1yF6+`0?Yr-usPS3`mL39JsVfTSlUj^SPO@F7@?+)#N|b6v?=SdRFf$aFwAKa9u^qQcy1I%#pF#BExZ{WS_w%)%EnxYsE&H|9H^% z6KZR#%dw%4i(Yroy~tt@tz>|E`tR7AygUCIl^Ux)pYt>NYwz$>eVx$hRr=o=S28meSUJ|9W#drQP-nYfBhR)TZ2Ye{Km*;gi~TO);`4g z;Jw4{hj|yBkP&B=wpkV>KjL>+t@|qSwUF;@Y5zm}jC1m_k?cd@TD5uDeOaa~F<*|@ z!%-PuNqyi(XwUmXIo~$oECkw0=BMRbiI4ih@RWS4GIlN}eGBR5@*bPS*DE~iB<*_A z`u=*~j|}sRj19ObsPc60OTlXxuP?*?mAT_ZBP#cHKZ*Lu)Nh3YHgO{-zRDn()+feB zt0n)8{R8eV;hQs`{B$tySY4?@>Cf?>PoWNtq+LeZma9_kpPB2Ku_4ON(f-G=6^XAL z7AaKRB^4!@pcH#^3p5V4q|Dlq&bksbRO@o&HH*-V%QQlNZGQPA+D&XvOSXW%hs8zY;W<(_T-~lwmIOcIG%OE z)|7j-$mwIi)%Kul*7nf&cSh~_KSQblPjdI=*nOI#q#D8hQ9D- z3-pIRl5*cI?e-x<-^N0Oc*AY1?T72#2xmp}M%UGa7So~Fbsnu#46Rz=C9h}w;Cf?Q zIIqi~6*X59Tjiu^QcI%xDF3z9--1HbQhbguI06u$w#$PFp?YeMJzDbdMsrR-v*69dnA9-Y_ubk;zp z@J8c%agLr6o%-ndR4JV%=p5Z{e=4(RddPB#q0Wol<*PJS+*gsDAtuG^$;r?+p^kuJx^Lj(SjQ90} z#HU~n;ZnD2V+_{q68n0tu>f$oEaYr_F$To{B4sXMveWhPl)F#Lyz@7~J{>G`W8xif zBx9Y*@ItKGozGcb+(UjI!Mhy1rl>73TSRzi;BLf;ZD%DW_Zq$=`7kzFx^c*W?e*4`r=+PjKE6t-C(+8^3MX zo3uL7fliV>CA#Z{KMlBV;(zCbUn@U^ZtEMdZ$6oF?-BTOz>&cqT~xY6kjfIHG1ah* z1>Z`3qu`U68E2cpCw70^ukojV?^vNQxjf509XaERnA6bcg=1gZs*pgK4WCNsJ@!ts zDO)h^*4luTx>^)uDYH~5V;^;|@yhsaQ%2F(P2eqFja@?-o!JKO&FoDf2GyTa=~$-Y zY?Z`8eJ^^#r(>DW5_#Rf*UH;-kEHpVf|m1{l$%gF2Cn?|ZD3h?eIVGb)LroHAblVB zv@dP;@s(h_ZUtXb@bzTlllJ;;5FfUj;F|?6R(be7W$+1)Tfw*LvniclI3F?iB6Y)X zme?xZV){dshj+e@cM!a_pG&!)l5(Dwf!DX+x$H&`+|K9@R=ze6$KiPVoM!M!+m)O| zc{@_>^Md#9gIPLLZKR+bf{m2JNv&(b!kbdo(|Vt^ZPy1*o+^Hw&?;J&a{nl_HW^w$ z`xm>yzk4*Xkd7^aj35TgVA!Bfz`l6uSAHLDY>%*c=Xhl~*OWzkE2As~th_np4hS}6 zDA-2ne!wo13|`U?%1K^Nr!VZZ{U~Yb5$xllTQ&q`2HhfjX{2qp{n7UEr+%e;k-5t9 zNAZ7yZwc>ao$yNR*u}|W{%qr0if3uh}{nu!fTsElGu>@oEUw^P|{{~Y>q16Gcnd?*T zGNJV{kCyLWR5`=>zIvL@`}ACUHpxWM(s%};)d{U{dbA$`WU zkN)s4(tNFZGra9exg}E9p92r^RsEzPJ>MT)*EC=E)0gIXv{w7H`k>W!N6MWkw62>P ztsM8GrTN;3Ts-%T#`xTIkX0@@d6R!LR*IY#`xbheyjFmqYX4 zDjApOMbk?AI(v|}zdmhwSnJbjhF0IVQ+n5eGiPeFYNBar{JQjmLS*2p9<759r{!=I zTHTw7t&n={1+F~w`&>Buk3y_mXpR#o?;1MREqh%rYutDau%?cJ&@8nv7K74&f1p#+J*AhzauF6tUojN zE@x5To6tdIW6tqDoi6BX>Pxw2N}Z3%pc5K5(t1GepU&p3nV-d=^FipmTj)IfbLFkn z$2pDp;!jfURYGS3c&M*Lwc+n0+tAF{95!oKK}Xy0`-YD6foAA*^{3pGB9~wD=!Ezd zv?Y*B?f=;oO80>jb$W%zyKDS9jY6yUp_F^8)ajB8S~+?5o5(uNSGk-+opwHHc{j(W zb8sC0B5Q(w#cm7m&DYafpQULZ)K{YWnU1T~pr~aMGRk@$DrFLU+TIdZhmjX;X$ik~ zl!Nc*Dfdfa*LQBsYFB^F((1sWyu^=j?MGmgursw!x1rl-z=xao+qnUpJR(EY1hTgc zvghnD;8$eO3h~0yYD>tvNwzhGSo^Iu!3XKt7Rpq#&|J zDytE7I_p8dPMx{xbRl)RlJYqBQwAQ9k(QmT?;s=J5P;uTqxdXq@N#rab`N3%EtC8l zv~vJlc{1O+$ka2q<`%y*yyl7qa_zH@lS$wpYud`2 z`+&pi$#^4k`#SP$yo>dC;msW;kDWJS6j;CC5?V#(jm*l^OK9*y`~NQX|F9;W0+*3j z{1%ya7nRA0Aolc2Jgv-=|!*45H(`vcT6&`LB~OtBekSY?KIn zoP%# zfj^(LTGp?FL;LE~$|`nXFTdx0XUlNCDTCnM1>Vx#7VigMJl=eG_dO5q=?1UJYz4tP zZBNYXWca%JfiwS_cnOrdiLV z+!v(1?*;Db1|9RV=3b47%IGP|5*_n4;T(8k=YY;7olhD%b(FLGbo^m^Qto%9zh2|f z@yBuhtfI8dmpCta+)LZm6>RYPCbS@lf6%W}H?;DfO_}{Dug#$4kK`%>-+LhEgY);b0|Z!KAU zX^af!j;dc^GMj;r(s(-nbjzwwFK@mbkJCC}NXq|D{Cq&1$AJ4fAT&3zysG1b<48d^bB0aWbp_Co zJ1?9bLr2=Ag3bSp2U)KZI_o_;ew$>?8L;IK6GGXRNZQZS{aWUDAt@yCJPw4yxxUVO2wPUo6Zhh_tp>uG|(z(T_vjIAFhYh<;LZ|(Z zbmTHPfASM zL#vGUQD}7^Gpu{8ocn=me2?ryS;wBos`kU;SIjPq-{ftxh#G^xe{XsWmOVp*monjvaPu;kR?PS4Zs^A%5ySNcnk$6WMZUelB}GV-vJg z7H1e*BJ+LFnprgL{wWVyjz>$|PW7)p-m4!1tu?v+?JCBm@#t)OK-*1pV#$1T@T-Q6 zZMFqC?Pk{meVtgtQC<;tr0}ZtNc6ggx5LM~61)drZSk&r@pyZ{8}sni8N6accRtJc zCo_lB>o!5V%;$-oqbQm@`n*%PFSvGqYZP4S+Y9ye2wZpOYDsCYW(MWv*9^P!rB83X zKPdmcZq~8iwpUAHUbJpD_E`n>FB-LS(F{Hwp_78nveymk8SYL)4m#S;>=@9PsE(#% zd>@;Uzirzso#TBva~7aeiih1Br9Q`abo?>E=TDl>>S#K~4>t1+j9ng`U0bw1V#oAA zXY1>SjW1^iIPGBf=D@e0?~n8F61PJl?E9-$`(hP%$~^pC2EXVfXCc26hTSWm=yVwT z(K4fbR@Z0T=!&H6+2#|Foe|5kGYl=U^JhIrymslZIm?oI=zLPn9xRKqpjTwp`>|4_ z9lI#kLDIG>e>2ME&-H`rVU4-Mzl?8nWeH=cSEgO}rRBw0L?1tS*ga0@3;|a;&p8H2 zM0s1T>PvqgD!`8N=zP(q(+r&*vxZ$AoW=nT^}Qt-K?Focy!z!rujAp1u?4(U!BO!pMi(? z=F7jzO%3|DE1Hh+o6dYA>#ZK0&4!N1Od|m~J*N$ueK73vlUvk@;OE~u?_afpL|=7~ z_5f+0=G~OXAA9`rSlw3_zbBWz5?rI;+VR!@-WMUbO8&z6Fy-u>u;mJ_rE|V&`(X?5 ziQo~Gtu|@DhHOpCa7>u_z~;rQkAdqW!uRz*uzb(yf6~~$&S;w@-Cws5chL8km8EmQ z$s@XPaw!8}uFW!o%iT5F)y1TFq!J=V~w=e_!`?2mgV z^2EEe-CBu{$vh*pOs)aVV9AnXV+c}{yf`z7)MF3zC_a7IefxB1_H4F1BUZ}B--PNR z$A%~Vb$Dbw((}$b+ToXvTAp1FP9CAPYzga6)x&yMkn=u|Rus?D>m!NVqT46E{#1W5 zv6&vNnLe$4Xw}yayB|G_dKGxIeBGro@9UIBa`wkZqxWj4y){cJ!4vSseEZvJJCNK# zXAJ2?-<9+G>u>G+_&fKe`Ott8pL5o*dv88vyB)adhfsgivZ?HF)NMSmZW4PpqHIS6 z%GRIjEQR%y?MBMz)S0r0O?QB@HNJn?T_w7>(v;2Gbodchb0v)K9cpAleMr2_TzxC; zu?4(3&rKS6%-tBpgm!PP0-YsMKEWTHqpQWEv|JS6~WYu1f^^Na& zQyPqC8r8TZM8>w9!F&%qNr&>^a!;1-_2t>x9{5q7i|&_cd6syBgG07X=Yf+)Z0=o@ zdDTUXj{*qn6Ogt)DD^$WJ_xPW6Td9UZ)(9_sWSyVnh5WH!^0}0+NB-twR6h3D2@VAs<1F)6A z{=~b;UPpX>dM~itV3M{r=JGyYH}YyMw8%%%nb=CHVfWo4AD4b#`5TlE8(Ucx9}I~H z+(fiu^^$*h*yWfs&Ji*BZx-K^J+IeqAC-NHOB8=vq}G)2WBEVWe{dUiIAeMYG02K_ z_U&1jQ_0aP5tHG0y1#2bW!uW0-&62yI1l#*WyiOx{C>&W&S8ntX#JFHZRaE3@O4J# zVL5chw|+gKXQDY5PzD~6r`iMf|4+@7C#^s5PWX9?BvcvnWI}di(|E{1O;W_eyw6QC z8ME)z$)(<_%5yKcc7rRt&st<}kZ+}lktxfbUbu8 zi}=P!`7^q+hy8{5j|`~JaN5AfBXl~T)39^c{roiOwB(=@9Lu1yE0T_}_X|*1j}F@L z`E7=d=!)Kd<;6C`N7XO{E$ zgucj#GsDoA_f}{LeO*^O+|ZhAT}|?ABv0pIHqW2$(l(eZkMMH`c@FL!c2ANq;(3!N zOK* z>BlEaE8bi_NHl^FIVLoz6eLo9+XN9S+jEDOUqCZ|3 zHfIm3E;4r@3y%u^!~FKmnfgoYf)e1}hmW|RRQ>Jm4>tBw72kKgeZ;J3bMB#STOAqW zpGK8MY+5V@)k^Z!SB$uu1h1cul~v9=|14{aI;>%L@OdL?n<>LDBrR7ZLIj4}0Xcq! zM`MAUuqWf^DCrCGN8AmPo>L)%=keK_0`x46D~@C_bMC@8fXGl;aswM2I^O8&j%-$;Hvb0ByAGcxm6Y5q#` z&vH2rf&3G^3t!ej%3F`Z=1iZXTN&@ocH>u0|c)W_;$^=*DT z@9yLp$7q{hZ9kC>c>*^7555u@znvW8_XFDo++UN=`4(m1k+Rj*BRA7W+-HC|U&<(3 zs2^Gzm$FqymrZR8=~LbOX8wYGwfygV*wAX_eMKxDD|p+8+mjv-L-QWrCK6d`j2aJf z?9qNIQV+s^(X0EQyZe|Cw-WwK8wTw{J8vjUT+QH+F;QKlC+8t(OlplCe`eCevsC8A zWBaMcsMG^|(K-@8A4F62Cy$D)U@g~{lknKP1! zMzIta?5QXXn(iUoKzfZ#mvQ7_8VA2{GFhYV;1AAYWKtxyT_qb`QzwnMGsU*`%N&vy z>tCo#{4tlUB06j6JoMbjBkpdoAV9S8*1IDyI4Qm3nHz&d4{?a@KNKJhuLwBd(;8xe!wyhI!MM zI{3DMZ@1WQtGR%MeUHqYU)(%N`sFCN_JZrnQ^v({H+IgoC@|*|TqV=uvC7k58eA>l zS`V(5qKG?SRt9W0uxojjM_^UJ>fSx#60W9?^slZ) z>|0><(!X}zY5SM&!;wku>R<@^GI2SD9iHq zjHqwVgY5>^0!(7zJ=g(Y9l&rY>my~ZyO>x$V6T@l&-Kczwy%yqY>xj(u*kBJL;CR+ zetUju%Ti>@QUT?C(%0MN$S!PUY-rBHoUi{93(7ke29aLulN4p>ALF#WpdK>LI2S{k zNBGio33d}@`ZQ|UIm?u(gZrSov-zYgzZhhUjYAoNk1zBOxDA5u9D}cp_buQnn?K^- z?Eqik)zP>A)R*edZ8esnJlfbvpMNmtWMU|xr9R!G3@sV!OD<*3zKAha+Vnr&UMz*V z^P?{Q2{VO33*)UXCcQiXeqHeepI>$DiPdr8lkoL+XepoW1t*Wp6Kj+4SW)#z`kefB zSr*l6Uh;JxNH1ln=6jy_``gKfy%gLZ8k|G4#Q&BZ(?%Z=`kmk@^Xhtzp)a~(Q0RYQ z#GE6j@^+odmGrAUe4E3!3wbv_IHr$!?}1gx=o~ey~XFr zHfWST^?dK+`&{|XTr1c4?m}}FX?IP5<|fh=-4abxY@LkL!sW7r3Zy<{oG(bm2O>XIC#5|7lxNwO{aS9p5Pi?pbaH|z9Ztp}?f;}Lsu^)_ zB`Ig>7Bg6hT2p_}9=>()?G=KDY!UO`%J@8{y}`JCPAjM&c}XGm%$G;py@Hp#8EY}? z)7^?gxp)t(#Yo;p@>X@d_`K4eyU3fr#QNQou8zgpFS+i`_U46L%4=WABmFr=zJ6%_ zi1&1P_3ov31KG@Zu{O3a#zDvX;me(pHr_G4kV|27?7o6I!dI+c^xwA!=Lg0=cW1f) z8UtEI_D=E?eRaf~H}j;)V`G+?I(NvrWP1O9WxO-nM+7FJy`C}^Qtx+3nY7JSS2GV_ z0bQy}n^z^z_FiL^B-=)wndJG7x(}I{kn)AQ#gFZUTId@DGX{s4~W z1&1$dvX;pqS60~U*j_=}hFV+3X1ylNl>JK9r{ID5#}iZI+cxra-(mUo>}_^jq|R&Q ze2uB`P3505Z28;c@n#tSrEYup*37qN`KJC*->$oi%6VcdcxJqe`C9zFDWl8q16wq zHRq1F#ZvbaaCo^++Hh?GTi?okjoex`fbo)1XZ3qU)cO8T`*rTm^N+_*=iMAxk(&n8dTo$HnT#r0XCy%jt&x~;xAZECa+kY^ux z?qn=;j{CpS7Tz7?a?aBF5j`&`pzj!4dj1c9+FD6g|UbJvYe` zQU*kQ{N*jk`1|pHi_HDSD>LmDyo)$A_u7Hz{Z5#15O^{PdBtm&mVEm zm-)_}9xa{c`Eyy{r@csS2tU_%HJ8JtfkvGGnYL7O6)Q2fFTS9C2l5u5+YUkDNTy_2r`Ib&<4QUxux)k#e0S z<$C6e%ClPDQ_yPt{)oF++Gjg(bVAmBr#k;n$DTYE**dJiNZCsm44vg8ZmH9XgHn?a;v zT^yLO;sdPL<(7M6vj*cXX*9_@O5VPv5pxErj#1Rv?nR|ua<+6O_rGO?Y);sAM!PNs#Hi1eWk>OTgm77Q0_e;H)8{4&6Y=m}4&!@BI ztO&ixL=WkEueN+xmO1Ct@rV=A>^?PYH84c=R=Zdx`1XTu*9Tb}&dt{{2?EK`9_SMm zuQH|w!Y{E!*0eI7wv4!s@a}BCMP=5W<*hb>*dkwxun9WzSwFeg+5~rzk4N;~DENB8 zccb*TuNZvM>uP6m@k`+nGJa3r zGvGS##W};2NBjRr&}G-6=cNz5YqIjFPj1olwnQSw9zL~IgjQ)KXOd9&*9om-eOf7K zm8=+X@0Gg8bI=O*|K51wKhXl1OJ{!Z6Fv`mbLq_Z<^lIo%ISRh3+Z-gxsLX^am1Bq z3g>gc)n1`*1nm{dCVMiw^AcCvcA1K;mKP{fca*I*0m}IzcAg}>-c7pR+oLy`>jF@tOQsc zeOPq0Jd&;gSQoIR3Nf%+V57ifkAOUqt`S(z4~%`|!CHZJ|8T^;4ju2o)&MKGpLw!! z+vM#6wx@5z+|5lw!Pg6H`Hx3*KH%lu3alU42?F4ebfds_0P7CW*bA(wf5fc|lrM(y z+4Iv8w=4jg0j&RLBW^4JD+RXcp-j4SfaPz^0pJnYSBQIk4V9y3N3bfCYKK4Oqdzh`TM2ZU?Y!z}f<^eS+`d%z8M-ADV)PIT!m(xo2SKGn0YIrym}+Xso(5?r%h zk34RjGOjO2;=-pS{dfhq4*q<*`Uy-uSsRkRTN!UR9tw4GToZWLGW3$qq24b%0}{23 za*oZ|lSh9vR~f`Vco19_`45}>bMWORo{Fo#oKM=0Gv7dc7msDE+XYN? zjXWX)^}zN5D^Q4mHCuc~1Ym6zUl6w5;tRqy0^0$;AgmAAXb3h0EER(71~wFe9RM~6 z?ASnA3Q7zU%U5g$U+j(On^4{vz#2oaQeaZPAdNY|>O-(PV9S670W zhq}5g1p}XY-R{H2&@R0 z9g0+5b^)6ag6#)Z7=q=OGJXO(GSEgdf#ru_Wx$*etO{5x1gi&jkiH+p*9=VbK@ipk zY<~#09@xJA?e43s{ZT}jHVVFnu)_8xHcGFHA*_<;#v*HSqTofRHy6D&>=u4LaO_)>VbgM89{RlJMc+b+jx;r5qr z2lyuP9rbO-n~Xm3zkP^&i}eh`GQKSz*zTT}F3S=bw>f9##x#AIcoajUfwU{2Aw#tp z<8e|^r}zi?4{w6-sf{#MOUB$mNfR2+L;eDd1C1QdAy`WGL2zvYm#*W&K>Ys_UID>t z`R@$55>}c={UUSPNnbnWuBUyRXFg^9EIDlnozUdiH!ag(v;KcF{jY9J&rFy4w#$|$ z$jh3z%vYNEz6Rd?kaxq6i;kK$zav4k_h-Dr97H#+CtdNw+tdA|4A@3sd(Rx3^4=Da zn;qn-pgdReF1)q-R$0GF?~!9qX67_sWMjrGi#at zvz$ELFCveWeFJ&+lIK&rOW8AG>TTdT9b|D}DPQOBL*%PEYwG0_TF#rX|H%_c>jOrt zp_OsZn$&9!`S!d>S}o)$KRXAl9QBeiZX{m^`7YxO}Z!7s+ zn=i|c;`@%c4_%IekBB9r)6|lG)@)<{pCAKF)IK&nqsEI3&r2L7N-cXfp|RnDuYWb` zmcQ7p@fyyxpPb~rcI%tepqPmA(f?_0ZZpw%z?OL!$GSn}T%;tpP5Ob2T-#>6PNY z!EX}sx2pd}XcbY3YLC`E;N+>`*LMo~ZpU`BUzPT-=R?|cIOF4#z^srT&*;B3_!cL& zyK-Wyb9r_-Lw?07_J!p-J2`!)K|kdjxO2djwsjVmau)NxybOJ^YrEMuK{>_WSsX`b z3t5Fm8YImc(lq~myZZy)MYc?u%S{?6NIUMM%=u4jckh?@@ISQ$d8_LQI$o%6Ol5GX zteka6`^M7kxS#gAkTT1>#rZind8GUepW(M>ySaz>2idej>wvm%?hKkSJG$PSL$U+s zQ2}(sci>!W=rr=a6*@i7Zg=aYPM2oW;byiDeAW1O7a4h>PJ80hx8=>7K9skV|L0Q6 zk4MUn_A`;6QTX5!qx*Dvt#j5V z@qa*zZGw!epHO~^9ntg_?6-excYn^iw6B#_=&W`$x&RnTt5$B9EUMM_MahM{Gmau9 z>NhPWYcF_K9oTN-n$vP}v5^z2r;0vD!hdde%h7?(Rg{5rR_{gCOIBDV7Bx~;Y5#*y zw|CpUfNwQ*lKwa6H0H~(QMXwpV_GNdhtL{>Z05~ynp;`!ql^X9N8R?rDdQhL9xS6T z%fWRx)@4>j>j_;ulya6_6^p%|a%v3Z!{Fp8<+q!1_Mb6!$XL8`(iWEE-yp5px;pN% zeq--owiX`ElzIQ2(5NO~!O}z1Xe4bbY0F6a|LTsyYG{l?V=pv5B6e!XuFR6_I`mA0 z0v079xJurNJ%%lEw%|H+JXcP?S1Z|Dz*UaTaH`}J(h_T1>o_y?nU)_gApG9wCEk5|vO7#Inq4x6A`x@^q7a?&XYgX|LhN6ZglLfk?lwW z(S0%n)C8ex0=bYbGAn-ijS&$&yX`sX3LuLBz8i{ zx>)QQXq_gsj`wMmb}}D(-Kcvv6?cxwK}&UwrNuh@L$bei5?URvi^bjvt#=Boe4o~O zXe~cw)O}vYlz)FTYhLA#(RK~E?XbkhBqk!e)ph)uB@E*UtllvK4Yvr z{{bfA>SH7}@`&a7O2a+%MiC_p8}6o^eFzH3S)kRSVSB}X$&PIhw}8$eR-KRh-fr!*$5yE9i0&ypop^$C zr>sA7jsb$J6X1|KuZCb|nS$y9zchoHw{fnFhW5nbTIQ%RAhB-8#)U}a3o56Xum9?z- zr(f5zIAiR$(M`Tk??c@r<-2gq{V{lreV#GCjVh_nUwt@ijp~_LWMRfdqAMy6o z95t=_=EOQ@?O`3$s@e}v{5?Lq@Y2~axk;$ta4bgCzjm#O#a{cR0lnMNd62w3qMHZ7 zxp(2H`#I^$cLNXEY&veJufooQu!nCFVae{x>3MTG@d^iSwf69phL-Sk|GTk=myfwW zosu1qc;96GuktXY!@4dUD1N1log zj=9q)qjQnT^J4BN*h?MrKQw0MpgQhR$V1;x@KGJ%Fau3}%$%TNF8;We0GH_1ZMQM+ zYO%cd*0qP^h0Lc9l4mb@`h^!an>;UeuR$qzV)O9lUo+++dSMxnZ38CYfDv3|WEN>h zN&8}Dgv|pPGEzH58QFC^y5?HTm)~47CBDq~GX96wG52=i%Y7zKZoaIEj3!@BT^qo& zchRVO`8&XK3wUV!pbx=co3P^vXJI@NZJ%?9!n@x%eS@=Pdbcxg`bK`e4(G5sYH#$< z$1eJUJp=k_=OT|vnR1>)ahwZGIYoXdtI&TpkGfyu z-RKzZ^z;g7^^T3YCrcTo1CNpo45IYCl8y0SMRJ#Z zC3Na{*bKx=;OCg~vnrXWJ!*FGCGfNIu-U~Qo^8hCmQG@h;pZmMIi04CqLUiHy@|fA z`*_-ouANLj3tu|Pvxfd!jg8@4W%B%AW0$22y9D1yY#H8j=rV|2oN)*C`*mYxZ@`Hr zPwxJodk}S zSIOCh=NTQdig{%rxZf(c&osD29$UcOMEh!=cn@%Fh{^hd*qV)t(T_JEkD@(%n^xx} zT5r`8+K$|;lz4MAe`}I+$i4|&8&_I;j=Ll};%69pu8d=JWvFHdSj+*s_O)Sxu>>|hSxokd8rExW8Z&2v>0D8!f0+3U_TsIV zx;Kz!lr%?T&qmT?3|l?TqZgOulWfr(ys0}~$^>t-#%eq1}|Zs%R(sy!ZOj3cDL z)Y;bD(5)wJA8C)xRrd^D$G;Ux*Pcnx1Fp_5_%>R5oV~xWI$p(pwMji&U>j);Rz0rc zVys4FgQKdvJSb7Yl&vsDg8rv-46;Hr__wuuR|6O;kyW<8r26Qj0(?uD0L|<*HC7!GP zarb_KpsUu&eRx^1=ZOdDLo!WCJHr+vKMaIr(X~VJ<29)zg;HsAva;Jyr zrN!3txymzrMv7+_o{LsmkeFR~rRX@pJ(KxQ(OP@<<{1XJ*oyhz#0L82<8HgO*?WL{ zc0ipsA4lUoq8bmO8ARdTEhrUd3poEyc|XD5&fER+Hh{BZ%j53pVym6zmAA&0cZn_U zGdj0ll1$ut`0T3#hY+V|UHFl1xaA6&b?IHu=$**| zk5!~CzJ1JXO!H$hy&R{05qgpxFU8;*0$2GRL0v(aiTPrQSVFF}&G(b0lKZ`AT9noF z!dC1_(QgHH=?_J~=HUY5{AVhQe&4D3Hv5lXJ?8#I;L>*}Tkxz*Jrw^o zBv4!&W{6e)Qi<@6C!p8f6(|GyjWf@j`yBnCko3+|A5wiLeCdaF=~o#~ihvITkK&8k zUv_-r+|&6*k*%NJSG0^icM7yt3#~hRTC=``4fw+`_l*?7x!I!?rPr;mygx5-s@P!J zdOdA}l~Ml1(9$)a4;Wg-&|P*m{d>U1MJ}6OEMDY=$&2`RuDEJ7R&E7pHj(Bc(u)jo z5*6oIe2}jumTm*z_ws$Ad}q(wxqM%cT&&+=S8NO4XFfdUo-W^)N}K6ce~q&%`Y!Uu zx6AnCZ2zFAYp0u-6FMC&SSDH?V!cBrd>vB<(@q`M=1F5ieXud~XO= z0xTASRRBA0Zmfi7U9qC8Sci7?^(Juu>w(hkn6 z^zEW^C=YpVkUSZ<MReM1D?^ zj69;3hk%KEzfJ&Z2fsf(X6)sg^oO^}H)6~>H(E`XH%2%3|B>L&HK@((0`KmSiE6+;m4=v zVAd{r|FHy@%H0IrpOfGD#^qY> zn`JBs`u&1)qskojjv$j#h8>il=+|TJhk2LzviAG-#F>mi6xRfd&rP1^z3Lo$*=FFX(pO`&ek`vr@q~uUla!)kCd$sIG4D z1Z>E6RQmmXC~6;j4ZKi$`5Nf*NO@LWKs?<)$J{RpAZiZax4GN%=n7Rdh@V>8A?R>$956f;n!t0Tq{#MB%T|^K7*1z)bLgu$GL7D3)=Qhe* z_bBtAV<_|G%dOn!lsV$=4p02xs7RsHa~~q+hhKZQ-!8ij&oMvTbJKvvmN<2kfk)(Y z`F*qx^;9{XZ^|~AoYsM)I@zm_@4xrkz|QL?xj(2Fd_5N-A5V^%^Y^UY zDZ@G_oG&Wal(S$9_Ts;7Ima(OWPg%!R+4AM!7=wiu|*y-d91#Pj>)Qv<7kea5la8; z1YbSnERlZoU4t+Bd{ee&=fzP%+1*O?@8A;rMzqDrygTjS;nDsN{?ez%^v(e1+C$=J zFH0D|S*Msq<)3b|_!k)bBGUyIGk*PNOz+P&{V)`l6`YILCGt?=5zJVuy1eUt{CzJF z4=sB1*-L^l?fd@y`!AOeugckk5&fE(y|mR{@a>@P*E-Tx8Tiy!>F*QJe$85QUVI-2 zqrzi^#~sM%ba=3ccjs>K@d%HXJwSZSAIIFArQCNII>9z1j>_J1p>2qBsf^(?Q#BTW zr}GT_il4ANzTV&$8io8?FJb?xPQsz6c@&8laD)KhwjT^f_ z?MB9ua9jyKcwtl8*06fCjlBEG%dG;3*8OdHxIrSem>?rse{j_wHtzlfRK|YAj~I;M zStL@iAykm+q>N+v%kig78+RYKd9A&ojr<_&@?2lie&wWXxqaM4_2?t8YG4P+A8fw{ zVD+R6!dieW3&BssSiW9StMT~{wnHBr9DoLM_mnF&NBpW18 z5qR!Ib|c48?Z38vG$vBf{daycJ{K-yq=oilX`K_qh87 zOFL^X78`>Szl35)2&_jrEfz9>3BC>B>!AMkMBt-ba(-1=0{8n=@QH5QBjvku+`UZ7 zd?*{Et}ap)$po>H?B$o^FZkxTtGzl3m#%GDKVoCzE8<4X)x3z<4(jmDmF#@#pZA4e8%UQUrZBdRu{_hMJDK7Z`E+54+| z)MF{=#pKN1jK0o!2!M`R7sZCVeI8 zT}v;k?St|+$sXWF@|RtSe-0jeG*gbE>_wSd{D*eR?&6E&3WP|NpmVDA(CDPR?~wA= zq|ZCR5A%Ml9SWb*?C6(#?k8;zY2Pnt)iy?zniy9$H5fOA=^sC@{O*F0=J)2=YxZ2?H1bFGkWpcN) zEG`J0UcN2+F1q*7WtRQi|BUP>*3WSeTq`z>n=_H|)d-)2#*Ak6CjhTax3R8A`ufVZ zgN4TR&;Sxr308MDf-7~`xI5S43i{MEj!{m0x(P`S`eCSRDdp%Q@1F0C>$$msF@T6i z){=@AX$O-|RY}@yq%F96+`V)ff1p32`;?k94`Z`JI@4wqJzsD&=Oz5Ww}EU7GzruG zzsfivco{=DR)Oe-YVvMEe?9JG(5GM6b9~`Y`?MvpjruIwK3o8`Eu;*c;Hi4Wxclc! zerXw$UrXhN&QF9GhKeHZAbAhGa@-xVJP7u4f6l2oeyJRs*eXJp-Eyj@%D^}CgUH8g z$MuY9!+&CK>|Rjm+&>JiirRTf9eGy0Zrt51MxW|&l^uW1N4$Ny;Fy*bTmt z6UN=wr17a=K=1X`I;kvboffknFYe~3lF0U6a2+^l+%4u^o=W-WL#%hbiMfd7w~Z;$ zc2ixf?M4`aSOy~FL9wCLq>NSI*aeQL{^G|NX$;+x>5XyOt<7i~>MtzLOL;oMwfp38 z_Yb@qIgav6Nw;IWN4PxrfJ6>b;OLw+?tUzn3`y)T-kpp-wVuzW$=>4cEMND zf`9hSLstFSHG;F9W%HR1CiC8tU4;@T(Pkz4y?j*hEG~a{78iSFRHg$ws$0 zxq|Hd;99kL+-whc)Pn&!+1UyHP}OOD<7h|VI1otqO^G3foIQ8hy=r*!!Jd-+U}b&PbMO%|IujIv=@?ir4^C;7)6fypta|;alMx) zQjUDNKY`1*`oP_TAgY-5by|&J$;4+36u!suo55bhMnSohCk( zA>v3YKQy5CXE~d}$s_%#^;+z))0q<-#~-RAbILz^eBy~`3MhDX2as?c^-*pGU-$jv z<{WeI+4+y!rLxR{(@y*jW(tqak}}L0q%04NyPu@rIK^Il{c%22U*X^DqE+==ov&55 za=zL7#&yjlur?y=mq=?7iw#(9(4GbDEzoZK(YRS_yRS}T3_Id=GPa1K<*+7ORyZl? z_mDo;XVc#{dHOn%7CpkaLHf#9QMVgOAH`!|HuMaZ>n7RnTno<9k6=HZGwyc0l0QP@ zn(|8V*}jf!$TgJ_ZKiQLf^P-*W?qfoM;1h*%13+K`A!vg!Y3BV-jZw?(6L(Z75tj< zEj8};B4y66&a`z7wWnW3iL1Lcx;+;qXMwK)e6^$F?%PE^erNEhT#0R68GqKaDT4%U z)JFOa(lpZ#6w!ZSp0het=>>oNqc~;7M{tftl+sE~s2I-H% zv(g&901VQ}_*zcd*zf=M@LZE5zd z2JXjZ%9z7;?ffYA@9uH;`Ls;9LL9xH(rLq-Xv4gW88n zM9z*x4`NAlJI+q}p3`AZ+&m}2h?>BN`oB&egtBG}l8;&^3nED;>* z!O>6pMtCewIlqm-3f?$j;vuzvA{TazMdLZ(Ia`)9rsfso+eN-|@-+w|@&)|@%)q=m z<4sObnn)|NCN%S7%omXzy{CaVPMcTno7Z(PbaVT(L|IFztQ9gnK0Y!}-nPMBw}# zaIbazdA^UDkJ-hPr(0w2JLCAXY2P61NG*hJ#E+Vs>w6+U*ql~ky)*a&IPqD z$`{@1r2D+YV3Ws3!eoJ-CYhJ?_Te#2+dmQ$Wm7OcjqPgq6z6)OM-6@d>h}s!==kl zA_FR$i&zOwJR6NKJ$B5ziTLAp?9f=K+%o0k`)4#hjY*O6HG{8c&JMRgw0uq(_3^EU zwtdoLQ6KoKODEhPrF~<5U(DhkbCTldc9#$mZI6OaGCsU}hx@`B04hfze8F)Ie3!f` zQjXI28o*ca@D6uXx_^hpa(~@H1Fhyop97_GDg4_6z7>z|aNn5rZ)*9X^#y*YdC~Pv z#|Rw+U;XF~ccv^fc>T=mMPKBdV$y%S>1fg~TELnWa8t|lSNrT z)G-A7ZG{u=gXwnh+u65?6#qgA)Y%c)%r(h`3Ou!7fi9@V7AF(mm#$DZz3j})FV)Ez z)US0F9rQccJa&8sq;Z!W!1tUs;a(yDyiBrjLfX9tcxCy7-r+2A89WOC2I^XP{66D3 zN-w0V|0*L=SE{djPxcG_w@$dTgbwZAE}=I#CkUs~elX)xtgrEXfz{<%HUm{Ao?qOh ztWTzm2EG4OD5fFwgc3+B7kqDJcrM+9z4QlDhc!B5|SRO3B_0uGXpv_k*^M9dT@g z)rqsF7bh0yrMYKfmj+3@Wx+&xT{Y;Z4(UVjVO$&Kb*+Ntj8D^E3n$!f@E*{EAw6UJ zcx8NL6dtiX8^E&yJl>tV)-T5j0_(+BrM+B>lGVJk3x!+Y)rTv2H;}hs(S+I8s4}BA zh{h?f(YnZU=1HuIEQKvK+xyg3PLX%W%d2|Lv$cuTm7KWZ=IQR?A~4q|)j7~CSi^Wy zJ>lY3uyU$2)sN`Y#9=MVZ)Z&}TqN7gB`uH0XszV0pUBuB(g>^ym}lQgpKAqH3GCJB zHdQ(r8!bDb)0HEw$WssL+bGwm4uAOar0bE|t~fcCyT{A`ek@B zJXi0I>jKA`T|V!G$GyN-g#~b z&pQdZ{lM6G`}ns1cN6YfODkF~S{m8|veApQbcUR_0ZEd$mM>>ok{ zy_OL#{W1Et*?Mqhk~-PGu4{vmx0AfP;pdCZD{VGN-i=R8n0vgm&C>h)Zt~hp((NZ* z@so#6H*+oimqVlz{ZvJ|!rc?$m>6F_WsJj#ifEl;;)XlHRrS<_yC1xwH-dgh#&l!r zbcAGKe)8-PL~UkipIzW9N7t?pd^`f%53C#52Nhyq`Rj<^1a@=)HWSzZV3J23`M(U< z{t&DR*gjw#n#!cB2ey)Wh&;+8|2G3$15Bp1(g#-K3d!eH+IBHB(`+U0F7jS6d0t$Q3}u15G9JWk=6;-~C)_I?LpRHB5cD@T${x-N zN)j^Lnd;sVJy+AYq}VY{(3$zii9?>jEos-2wvx2J;9d3)cd$Fp^FPGz@w$xhi}jOs zH))@iv??#02@{MxmiVE2rUNxf32d8gU@_N8|h@8RL<|CXAh_zcg}}CPZ__>l)3xD3AZY9ykI`D z8vhI--k&uND$_?WFUhlqJbf3@_n7*KoCM|EpYz$dLS^CuqNAAZhXmc6Ia^MUa~b@6 zkENe==XKCe(5NvN*?k6$Fa5p^TqW@T>U3RnpPs+xauE?T-68EpuafrZBX9k4*sE#( zhd=-E&&srQ!tcdNq*1+Bb*1pN;1)UmV#3A6uROBjmfGv$US{K?zmATtu!LllK@=WD!uChxQBh$Fh59<;t zliHT5v$Wn-;^)e7M!Ci*NFR3Aqd%8VxCf;TC|l4b@crDn+#<&(hmF;=|E(508!n%4 zn={K5H4f^1BJpUQskVT$Zx{HsG`)2A_JVKp%9jq`oG&syHotWEI>1--p_dL{@pH^W z{yO1qr#+lEyv6D-jjN7slg7jo(Q;?v3uoPmf2M_gpXtAh(uLx$Pi&5E-?V>lCHO|K zed+KGf^XY(FCD&_Uqb$Fciu>IpE`==Xg8 zef#L&q}5n}hflGxsd`LhQ_HV*v-(-&Uh_%gRSMYjRB&*@#H)-Oe7WLPgm(uh>&*X5 zxKHsO9uGtMSa{bT$vZTt;HtWf`3|`LH8otlxo|a+zY|=oFHE@awc}wh9+kn^?hU|w z#R^t9h`3r#c`9RtCK#hyl_X+03|yS7hgRiZ8JFA%_vO%X?whS`VDBBE4eY%GYIoP- zF1s$9;M^pU>ETis8=?Mo;=y7&UA-;Gw>NxUp*Dh!QA@_%JrG)iO!-VCR+wXj)hd)@c{VtzC`#$2ULHB`2Qnzz9c$e zJ-8Z=-1$Gz0abs;Uz6Nvbim6_K7I`sb6P$1>k0mnmA}ozaSek<`rnX8kP|`$?6(v#l4LJVI+f zv`SV_xEB&P>U3w*(y=Q@i*?$gqFvRxjx6)c75$6@9<7TFEtwBB9w1g1+0e5O-fL)O z`10;d`|nhTNIzXqp7pf(-(^fW-7BBY%T!Prp-D_wwUidq^&&0 z(qN9+E=MP@^rAZzSuqm1D8Tq={v7@@#t7%%Ct4ZAhRbz!403V5$VK+xm6q#9aBk?F zaGw=f`=N(Z+fw)6sI2*8kKTv%nrNK5_A0X0^SxN?*m?F&j+Nl#DdxB4PW+GX<68pI zI?6eh{yCn)vrVK~10UKX%_L`P7V~+Ov^z3*#!l`wR3yo(JOl`k)xhpefNOwFL8=Y zJt<0T&NrFQ?%0_=(@kJ?z33)ssPVqhH>Uoc&_;6n#{u_BUp zGq6tb2Gwmy112EBDO~4AkH&4ofkIn8aRiABx-rr%)HkJCVCv745kC(JOMf^4bn+0sP0Mt)? zf%>q;HrmR!7`WMPWY;Pg3iYfux#owB=&bkGh$(E}=gRhL`(B+~>b;uz&qmgF#{YX< zGbu^~xP)K1&hK2w_a?sANV!%2W}n|Fb?+hFzBvQ#5=qA(x;ie$6`I(9e!lh4c8%6= z5%g<^`UP{W#Ff!=qZ;**7kr!Xp79_yHC%eVHR?X(BVVkz>cQ1KW2f6RHC&fTxpLv^ z0axcMcDlP5hs~Jdj}sw331=fD-W)vy&(*nfc|0#x^4gv5dQds%7OO7t$2}&|Vx_RH zr$%LjM$tnI%or`~5A3x#<9T0i6t5;uNyc}P^_x$(cB=z^o-%$--{CwD=)O|`+SY0( zVYU&4EMX|nSt7>?nsXC0PZxRWUql{}xgqi_Cy(f4iCqOdcy&of{Q>*<-pcnI%)1J(-c zbG)1K#OG7w`N?ZJFk-;OtM!m(7is288rB1ip;)E;zXZ@$z8AiJr+dD9*E(@T97p{N zq6JB!ia;dk_L8pp5a}vOH)9j=B&55M_rUrZYZ};{Rz7sIk8LbwRbCTT4GbDtsRu_N zI8GBBTBdqouwGs>mwJhwTS=Nx(lC7MBd|_jsSvCO*bp#J-;11W0X7Kim6DToCjLor z4%z%i2%JZ7?zZ_QpF9FP0Bi^OcKM7 z3X->)bO%HD8h{-D7F<(r0k%H`TLo+%upl2c0NV>pLb~OV`OLn<@?v{{9U*h8-kI8W z*UB9rc0Q!>0pbUFmjbt8*n$A8 z2Uuq)-4w)zGWBaN;mHfLASRb%I2?RNe=Dj#VAI$dAFrzCc z+g_nRyffgRM?&nr20tuk0q1MmPEH=lSJF#7SdM(ms;p;!aaO(t@@*jBr@&!!zE3Ys z0JNo&kx|Bv_2k<}zPXZbl2`yq+fUjVBZp2aZM~PY6{Njb@P^_B2lRWzPeluO$-kP-l}vaWi_K&h?tWwHFzAJl%Kv^%cMGB))60VceV5 z`z3HS!=F#N#3tGTzLjs<=^n?siBFSL=83K3m*U&L?Z^sxBCtOw)ajDA^LOGPqzzv zTTa^P)(XC7Uzvr^w|&z7_}$T42+X;Un}#r-P8%@iay|?`9?@&n$-G$a&YkAI==*@5 zE4~2M=k3{nW+5K~Wg$$_XaKQx(1+w*Mc&=y{VMOyr^%}^0LZ!6-*s_L{S&dmoPb9{ zYcuJW{m#<5=*7|6N8UB$Ra&PUnpQE+`2!xU!(SY&2J+6@Wod1H#Ubm}LHaJzUoL#O zoAfV&hug?I}B=fb*d>TugD2RJU!t0!C6={p#ywm-pOnlMpH_N?7x%_sO ziL-Laxafr;a4mmJwq6i@wwG@!p4$0ep95A2u8JQrKZZ`3=$J{yqn3CN<55V5;SCfV zE5VU}+D><-@F)rgz8i^=Zi)XW8wXBasZ&2VdZ^P|Z8@Unf8H9hL}z6?JmnAt(O(7k zFmmVZ__!HY9yK5%7L_E^qFz2Dk(OE zKabE|UYHkK{`Q@21$A(a^XU40fK5&-f>dQ8UpIe4;NmgKt^I zPWPa|e-GT(ms9~%R$U4c1<(4sVzJA;a{b8Qk#ZdbPr*BExxNb=`LXj@%a7=CE$>Bd zd*!;x$5(qyUhJTU@558aw;6os0Ui^0t@7 z|2(_wYO}#Cu_RA?J5p!I$&1|w&XeW+IKPbhz*Plq>7GP{ftI40+51!Z6ZI7&GX zx9nZ`&1nT^uE`gaZ5>CMwAwusi}73kB2pHapaYMZ-X2p%o zW&1cM_|2Ao4;-U%B7JAW1I(x1W9R9r8<6$jU2mXlpT_Jy!#HAZirLAHgT&ZR(yvRo zY@@7lr|vU#E`$d7f@h+K`r<-y{Yyo&OTAe4ua!O@i=9WArM;Xtn=;q&-U*%3c{|-w z^tkgHk4{cMtzIoPCVa~#lwO-VK#ayrXq5`BKNKo|gw{c5?On*6QQGuz;993iXtm`f z9*w4@@q5x1vHh%@%o=dTkK){JXbJzCI2^5WvE|=q4@pb;w}I&I@^!dHY$W~4L@eQJ(Kx5 z@A3#QH$%G>*jb_%?j`Ny?KX)ln!8;$L;IH}TV8(B&=xu~*>u)?mZfve)aXpsW;2Ns z==A8!_35mF&hqzKI;Z~kbXGyx=UIFdBJa;nkLH`d*2AzQXNg4&_6#gl z@Mq2|89&b1>F$+r?b-j2xc7mNt0?!lH@n*sNL=EzYP?2`S|nn?sJ-5Jt-5W~Hf_@q zAV7cs3q%PLF+hMQK?997YQQK}A_j?4v})C=Rij4D)fRg-O4N!`Bh78oHqolN8YOD4 z=l%ZX%-M7H>`7bYzVCZJpKVWmGxN+d|DJi~nP=v>^rF|)+zM>W#$hGK?aO7**>{$e zUtfU^&oX|E-@<-!(c{ji1<=NMYe#sEe=v8UD8BYVZ^^}vJJTxXeb5W}7xe&f4C^wVO|gKX^`6obPETF0dck7R|k^)jmL8w~GD*X?fcDwUO7ZH$3kA zNIE8M8}yww^RVf|MqlL{@w)RM)n8CcD_my&?<>FsC$cs0@{7PV&6@sR$zjW~>mGNy zi{YmsP?lQn8=*CPB=iFFJx@NpK3H&0N#cWX3N^Th?}h%&5){Ap;H+=~784DcCnPnXKQ6XRzINUI%Q!OQE!8Tr*cyC61v|KrZf)L%Qnjb0kQcO!nWC~rN~+NIVM-T zG*u4`(A+irxbqCv&np8oX~(ttro3&&jLTl4vf!LdDd~ow*T@`b^pt;b>4`}qt?Nm-;jH^48w)RC8n*c#jLjID9Oh~ z|JdWs2Q&bnmGeF1hqKFe6vjvSg9w;+Ruru`;XB{Dy&W|wBE>4R!0r9rt?Ci>yMLX#FJ1%I1}5``9>r?}YX$S}hEm^Z1?vD?Eoo@lguM%U zr+=b|UCUC0FWca20=5**CEG|hvUq>?yi;(V4xA0-Y+laBgYl(P-As{w?3Dc{yiZ(x zu;jbgdx-l+#>Gjj+5Z+Pb;()zHbJZK)cq&4V{4t>L)=Q@4)N}`pYfenBi!#bUxuKa z#M@0g#ETsxM&~!@_VVnZXL8L^I|f2al@Tz{+)=qHQ!pRA1~ z+RYqYM!He`^I5;4V`j8|ySOk~$7HQ$@8w*ZnvKUdM0S8RQGPbh+2`9{W*54faQT^! zm~Z4IkDOw3hBSO_`!CvqGJctNjT_tl@p0-+`scIo(Z3{r>qiSowdpi8jzQyJMfHTX zsKf`bY(}aRS{p9=9^>is{SGD`(^t8ltedUowR}phQ2oAv@Jz}6z}b1$?(UuZT%T!W z5&oU6#O)&PKH@U+Zyzb_&RrFBc;%oWQQ*@?*0h3EX3zqiNr7+r8dufy3PaOdWpL_zEgXV~w&|80@!2OWg%_&)Z;(*6Ix+;v?sivPy5 z_MhyXqW_=VbzM)LB+n>_)jWH@`@OrLB~ASY$A{TlbMNb6v84WxRTRuO;&WyUyGPRq z%@Jr;OILmfnw~CbbT?Glbw=+`oNM$-4u3mnap#?5_UN~gcK4b4okyjsJ{(9J(t{Jb zy~LHzGKndR=H`8*Z`ios8KpBMdjsj$d3ri(`WZ%*;{v9Pn=$D$#!GOLn{M3iysw6 z2h~y~H3cb`(tI^>k9j zi?I+_{ROclU}}$`pGF0k+82>v9;IClruGeD^Al7W@Nxth*+72+aZ!lgjnA$g3 z-a#<&7mT+HOzj)Q_JXN>JzJylm;h6GgL?TaShC;hq{J!bm;ABY9&^0Q{;|b;7a7yN zT;|bb>wqhWQ%1VaYBV!%=6&0Vc{=fLOl&!>59^CA4I$beGUXS&k4|RSf5Ljf_JVEX zU5~Iqu;j-ko!1z|#dd*Jft@X#fk!&WUa-1{Ce0ezmDfh+(E4XjR_EY`9Lcsh;+AZi zboMH-S%(TzuD*l${*=X$e8 zD0*GcYrlWL^D;}%-uGgBOpJ|;-Im0MRj;s@Rb^0sxPvf0|j)@hLC!Khcz~kVW_M2lhL&RuA)iLN_N9?;?L&E1WuUzpF&0 zC|#O#b07J~@-yvEy3q&sXZx#Y^@8mM8|B@-H;6X0J{USn<6axIqDa0vzx9Y-GuTeBOAX@oqYkiRVAqQoqlcP)6m@Q4jn0cbEm|QrcRtla zVlVW{KFb;*>Cy9bP~WbYc$In*OF;tNj?FAvCX9?Rwz!(J)(mQovd56WU)b-wx|lzR zzJ+C))#D#>n?P=|MY_q(buveP(pYRDUEeqNJBNaO$@rruep29dB;si-b`iH>WWTe{ z#tq(g@KKc5|KxgUqo;;oH(CPgClczHaoqeNEo=!E6e3d_Z@^reO z)BWgv=QAg0_pyRR=keNoFLaKMJvn}w>CYv=DeSOHrDvS@Xg{x z8lEDy{aF+3RtTLxWSx~!PUF6Vd zAE%kW{dpqJ^(OB8?aJOsm4~=N-D?gk4d(gRN`4mp68!<}UnNVpv(DbTV8)+WLmGbr z^Yu66OLh%OK@${IN>A41z}#PxflL!LOUL&+>m@&Z84pc+Pdn9PeJ(cXTNiI!l$wq= zU{$I~&GYL@Gzh&{l0Uy9kwBVTM#v*M<65jPoP~aO#iaAotlnYt1pnIsrcbU-Rc}5;H*c+sq{blb zgx2!M>EGu}I!D+a4fJoL!}vOfp(XdUA4W@r6-93#6VzOWJwqcqwAh9Gu2SURlC8Te zmy_i%`S&cFbUtm%a<^2|VBMQ9J6+BA$*Fxacc)2zjP&!bnsmOzyZHC}Iwx#O(Kd~S zDU+r9zRrBu)zw0Ekwk`M>HW;N|1s&@tA$r+jQjRP>z|Aee|AA?yr7HNTs$*8qgoSlfA^}vcESxxC;S%avSUa@k1bI_|=J?ZrE?&@`N zRYt4UMh~^4x-FfgzY}MN;uv3fbMLa&Q`y5ls~#C;K_Opg-78GtSrbShm27Dwees$} z_Y4t#OD}8%>;KT?N$tb*6L$x3y?b!p8#m+NT*=N*z0$YhzoMf=dQnNHO+hOLFFG5q zHj0>Q(upe&M(QTn{2;NJ{S1xobKq%?8sg{dUZ8fP5 zeY~rlD2GB()^bXqWmT$eU8-XxQTUF6?AO8k*A__!A0+>&x=E+m=5OyhWRxbJL#I*C zq}Dlsm3?1h(v6d@?A%FnSH0V}&3ahrs{M2|TAy1QwdPKsk^}VT^Cq3QCEdER^&T~6 zZQxF(%hhr7E1v1$)>Q4PRQ;M%Bgf#`P1L;83hn+2Cmpu1?091;jo(7QW1w-R^1nh% zuj)e*e*?s?Zk%))dH4FMj-a{S)Lp)c?r!3>5$`owx-69Kx-s!fWJq?>a>wi(@w+aa zbgsAK$?7;Exn=D2>RbO4Swzeu>8PT^kH&1{LF`$VVW%qQ4&4|L>o4-frtOxKHBm zGuk@JBvwbWzp5xr7R}I_eZ!=C#?g#F2HD-5kH}futmo_!_wY+$3=RY2ktshkhVS?L z^9lIb7|qXyq9*v6gI4T;N#{(p|H6BcY=#FJ(Fe7|dy@Bt#_S41zu^$}Vd!J-J|6vl zde3tg^anpO=@>6<)3=OlG-K4rMl*+79X;)SA}h*iSM?e@jC^@L^_r{io3Y@_S<}vP zFaC3My{>h?p$V;u>#(I}$5lxGGkj&o)y=8K^{EUWSk0Fo9Akzx{RW{q2Tkvr@_A2ycPlTe@Ko2Lbd4@vdQ?-<#;MvtZO z*J2D*GfyR-`I~q^#`<#=>FxST_XqwxTK{zC**ZQ@z5|=`yQrV!OI&)vdxbHcCgKo~ zI_24irPHKmm@jXdbkA>^{$kdm2oApSaK0B8yD@ZIi91Bxt97W?AGcBS&L*_N`1ep1 zH-=TkuSb_+Wf8t)#Mg@D0{&kCf!l5RbF-?ZOK)TLZ&c6sNg>sW>Jppkwgpo8RAB8w zo2bzyYCg{2D`CY8S0>)yDw_->E7dD$qA{Hd=5UAdTbduw`_EA^L=8Cs+br1-2VZd!0HDb|LFP z;kDos5qJiCE&}fWFJ`|YNT&okec(&MgL!TPs|v+cp2Og^AzU=}foH&_?&>M$Hx1Ur z-b$YUW6RCe`K50aA4T6H-Rf){&pKB*rO$gf!mIf;{Dc!W=V9P`C zj)GN#d3$6Uzp>vke&Ml8@0h=PWe47pz~069z0mVWbLWjp;_fAGgW{q$Y~d3!%2RVn zEn&s%3tgfxzKyaaejC>vxO!|eVfBQ)OJOMNo8zo0abn>!Doi_63h`LuO~@y^m* z3@<_5+nD;HMerJPk4W|J zDDRmE%-wLJl{cSgvfPY%6ttFEU9O$9lcbfID!_-YzxZRCPMVlqh!6F#5oooab->~J zS3@h04>PaD`$CGEIbWl(68k;=X)igDz2`vfumr3M3~wIGpRdRGb`7h$*VA<)r>dHw zW|C*1HFx#_=da9DZdnbjkbY(D1NE{mMyvvGI^Cp7$Uk3Y{E#%a>?NNa4^KL6o&4dq zZKzI7KDEU~LtTmUo`Qj=>w!e#hEpT+#IpIym#2CjNNh~?5$pE_dS|Js_j~l-Abf4A zU;hm1pY8f*r~VnzKO+w$E>!fZ|B=Xv*+q(ec`ohqOt#masi0K~+MsM#DqA<`b?gdl*1+hUe)t_uvdc?~vOD|Zqy0*>SdJk}PkMkJ)ypWD_zU)p zNigvjtkXG5FNhV+x%>sOGO+4%{PB?{k1DWs=ne7i>JhAl%-*i`oM2AJJK;Rn=)xbX zi||U7%52`vT-gqdS!k@aG&aYv02f|Zqe-sazVB75SGBIjEyed>38 z&}wKukb5Vd@tsBoca6PfJv~IcI`}I;t`22&#hnF-??o$7Mh{XM3g_wXFFoLJl2VFjSGe0a~-rdZp#fuA})5fNUsjMwba1DAd+Hq&fDo15PTUJRzM6 zb)z8B5nUd$$02&-&>Mcm0q3a^^n81RwQKdA*xpd_7xZ1)`1Y($81?-YOAr3OxR6o^ zZ{n{5ddqJ;;3TsA8T)X^c46DEs~~ZDba}O%ZfrAqp?7o{veB-SgL*Law`Gxzi#`|{ zDfBtDee$p9WU2h3+3|OMJ30vhmlLJA3M()qdLn=ge%m7PN(#56ZkI zx?HRj+$ni56QR7@aEI=`=z-K~osNroSO?E;wL()0I!2oYr++ zUv}FhE2*I>J}2O_uHk_5XUd)Y?)ZuLG<7+>D6;HkjUYa2;Is2jmd_q&@)(|v(f*fN zxd@+jUcf%zoRvA^sjY$ggot+~idV9J0iJ^P*je^FPl@Eoj8pM6&gCe@@U({a1gxuc_NzWzq^TBMW1_zfD^5o(=u>Itg-CcwB&!?{`+KUNtbDY))0KXQhae| zYp=3q#oxl8!xwVCnJ~%kt7> zdGCha;71QQyI$tyZFF*|*!w!kSdlf>XNkM)C#(lF&l@|Sjawf7o2RC$y-H(=SlPY< z?wz97q)KjpcgcYEM&#!c2b}MlKC{otr3-TBi=YgcbR8%oWU`Si+}^wK{=#`Er$k$Z(W|6NNN6h23I7vY|6&waud6QQo?f>fcx zmlPyolY|>vo~fgF+9ROwWrUZHTm9Y*UrG2nw)9Q<2EsEr;rLb*Zc?ymgLc9P2|ola zmp^@*iTrE$x2C9pXvY6`fOsuGKM**lo3)?zvZg{Cq+!>aW5MqF3K^z0-bcEUUmS40 z;nUZJup2G?cF|80c8qx2iFdvZ#?TLgI)lHTQ;W*V*-Q2Oob6EeTtGUWi0%IU0cUWL zOCzZJ`u<90E?u7*JT>vG!097hrF$T-OI_Z>|LLI|%E!@1S!a$satz zM!=c}4my8R-{Dbz+6OjEAH7zSuo+*z{m$=CmZz~~;#E>;=(ChCC_l;79W(sL9a=fx zWgkn1?nxIfmHbk_-{ZVk33$XyCXtBkd+9-E`lb9~c=7wTk+1$(->9*^z(%^^MS1ib z;r@c(?aAJSyBuscd1S6U=~JG*DMH;y^3Z?+sxtbv^yF0IYo1yK|_% zd>zZzhrXpY%&#vq-_*iS1M~*3KIrUHMfhcowt2Fdv}OTvs=q;KE$KbznA5OPw9Fjo zwPiu#KFNlB-i*Fc54~CF?Yrlo(-N_tWcsVIJsVlGKGhdbd@%{V{8`tu2jom2tl@~} z!ZZd6 zjg1|oEhX(WF3qYq*Sl+MC~PlbtqX)r5jH~D#a=o*!<0^K6~oA#eDGl4d&Bh4pe;cf zaM*uwbx~)krvP(nL5BYo!|+fCts!W=(aQ&S4v)VU!ionEn)^_wk93>^9bQ;BVQqvN zyM*@R>;14n!gehXHcZ$&VNv;w6IS=p==|mg+euhdx)M5C@?+7wR1#K8*!gbx%FXvz zZ2i{~)*BI~G22Yo7-5>8^$6<#I~Kxv!73igjW-C^5Wv)CyTH1@hD3}p5!{D2wj^^u zkW`NxiCt4_PXA2EGw=HRDw6Qwlx|uKNr12&)9!3-%U+xNDCz z*s_ADCvE#lL$eoJ)6nM%mSDRwC-o-He2g4&?|ouBD?A^g;MJ3C$<^HN1V10q`u1G3 zG+$;yC(rNP_z-_n@;qP0Wo<)CPh)(3?x53Q>pVEehjeGY`zbH+2x@41Dq|b8wk4AMUxC(KjE@cct+mhf=;7~r zPXe1&a2;ai8665*U@_+2F;okG|8ZoG*~3pRfgX?AW+(YHevS53K1nYhUmx)Kwz~gh zBJqLf`Nym~Dn*zlaKGnQ-{;>kF_{SIDC+Oa_zfN2v_Aei_#rjgbD^k?HH>Z$3``RitSr zef@V1Iu9s4bK!NV+Qw9Uy;o+|YU@(9bspmC>f1>(Oq#Q-Y_Ma{$W~vEG+(XygDV_z zRay1(sy7tGw(maZ>_hiSe(bx)88^27jV0t29-&UT_i2w{59*$>ed${0@uc}RaJ5?b z_YXSHJpr$6`XFV5)-4+S&^QK-f400G(3pTme|+j>@5_)6dA^(E$_4K{#Y!3xpM1CS zYviKBfyC6w-k0%>Ncpt7DE`67`X_*n8IimBEJwtLWORL#(f4NIYb&zzd1|eY+%)~j z>{VD@zdk-eF8P)vW(M`A@qh@b9@Qcw$^m{JP2C~mIjkrqeoaZ*zyoo2UZ=zGNQM5D(8H0J6Ju~zZq&Sy&kYm zu(Jc$09b!0-cGRHA#69;80AUXv|4kFgG~_6?44nw;EtZ}P)f57QoZU4t!atl@={LN z?#Vsb@+y7>*9Px6Wy-1MT{er&oY7)>Ft)WC!o~=@-HvywlbQWJBi9*Kt;ljFjI4gg z2?16BIVV6 zGPXRbS(mPtx;7zJoNU*3H*ZH>Ouxv3RXhD-W|ZI z!A8OQB}aIawhpXu^AV?9?ZhK21J>M4z6N0}#Q*JJBTJ^7H>(4MY|cgpVw`9Fc99#G zEsm5M99@4-wC0K>uolu2SI$|=&oh>~^2_F`pSM&Fx^nCjSJ6RnD;BTvqna-PVg z<=ZrTT5_Uhr!9gOEvGtZhgSMkQ|`Gl{_f=019lXwB)6X!o!!()kbXLGN_4?7#4qSA zm$)@LO|9|gE96rMov)k9?w1C?uWWU*Dvhkr@HF~Z4QVqgrkqFZ__TIJmUF?g>$=}Q zXM`Agk;Ys%>1S6?IbX<4Pu=FEe;|||dBOL}D$e&tKQ{-iFUmk~pZKYna%L>Oyz@Pr z$;|ez^a5A7;W7Lf8Zl~1L;aze&uJW8J>@)>Z7(zTh1$#7tTTyS(RJ^ho9KpCSKX9z zmBums)b5uvj;U}BW3APT5~o7L{>9-V=Ce}HDVv};DxVF}xkT0JI5Y>&opKHrx-|37 zO&B|uIY)|}s~~Z0v`yW}8jaZsKIgK$amx91u}9CWTk`Zz&W)Nq(+@}Q!?LV@PLgUp zFIBb5Ecmk$6nUw7?IWMv%~Q@@yocp4<_qm(SG_*e5!MvFAy*ocbUD8XXcfMG%6V^= z7P$q_?m-J#9wuk@)HP1ao`pOky^b^M(L7iM)(o~n0DiukVE@583xX1A$*euwz^A~yb12!qUL`ZIwRzIL$t&CQmB)6{ z9c!7&-VG!@WEiaax~c5<#e?NA1l+k9&S_#hQLAx9Z=SSGZzL`6delb6&t^Ua!^Ez3 zD39-T^+J`Wg0RlkDW^vBU3|b?5-mDu@Le}dnR{>f)78eLyWmImLjQG;k z_OI1N7keXU%_F1IsLVI3RCjaGi@jya`6%z|pV={3u2p3zy{qJ0=&_dcD6BJk5K3G1 z+(fLMv=4|E+AGigD%r8ht&?nzV$ey)X(jFWTc@16?K~H(lRRCx-qmsPUEZ1YqrDK; z6eWtIr&;WL8S+0${?m6%Ij80J=P0}A8jgFtPQ>!(|MhC}h>pI}^?!OE>+828_iX*x zdqhII3?|Z?PQ#7j_qY%v)|KD1eLXx6La)1P%K4ypHab4GH~HIEX=hX-((D1hG5gMd z=0vp(z<(4uUdlcbd*5d5r#W^GnqzlOIp=0|7c-Yf)q||*cSTR-YmC2cx}5#Ao+&2{ zUELF~Ic~O>jXZ54Y(64P^IQjEW$&``*YqRCRIlueCcwKae=9LVj>z~j1@osdvh5Xfw9?uQi zx?iJBCuFov2;I+2v;CBSd}CCd`L^&b@`!z4%6V0`EPnfiY{-~Hb>C-He_dhrL}D+1 z-~K7*=jyNGpWhO&k^85d`|LTzXjz!}D(+@l)6;OY7N42r$jB4)m7JcfO$5!8Ezm69 zKIL2y$fpym@sm@|H%=vjFH3y8fsUwsI@Ne-Dsxq;QAPQXC7+cnxaFJ$*7$m|F>A{) zXzu>Pl*3)iww?XDF*3&Jy=Kj)GojZ-*A1sBUtvf8xw)HAx_hJo($z0SAO6aeb1m=g zyv4SfJ&zmoJI@f@Ox!NwUVk!jFO9ECgmgjwe|j{p>r6ZDC6C^(O*zYL{i@CvQkdSp!&W!9-iqM%@rZzv11pb0MKCl(;cKakWSB8o*kzQ7ql+6CLZ? zlZtf*v9#fJ{2cEj);eP8)}V#?`U|06@z1r)s?O`rz<%;0Ul;BHYX+MH`;zUGdG;}F zS~R8_FEN`I{9$#9Y%k|Vq_I?EZ7Oq3s&OT8pD=Oj%$Q)agLJ&FPSs!G(cs<1*#zM% zef)8TNS(Qpd=_4oJcb=i{e<=p>Ra)@yAua`jj_pQh*JI{;{Oc!o}Dv&zd3(A+5Lig zCY$KKYfdfa&*iI(JnJTp+3_j&d!?pN`eQavp0y?J*2v6PG}jthW6;|Aiz(;cEWd`9 zud|x*Z`b8*iOT3PwbG2KvNO>aCa{T64|=o?r~;bdp%bQXRKqEv!p*tY$K~ z!ToE4u^lGnFeP#hj=QcppducRl5Z(IzKZvle;yYReu>8+7w@}%OilQ7s5~5tk0tN@ zOgwzHCUH)INkd;Teowu`ue_rr5P2k? zP#*;fiA|%TNA%tGW^9h#$^P*FOgY!74$s_?U*{M3b*}a5Fg*4DVJhz%z^dPI!iuS1 z+{kSoGvi=^v%U-8pCmiVQXIqT$!81iS(#zZd|jOt80(94XO?TL(if0R(FMJ>ZYKRC zy!_av59U@LUG&&I!|^pJCNu5ERp_1HV2>wi_z`H^+Wg7B)y0CZ<=+K*b| za$8^;zRJ!)KVN#tS!w;bY}w3OmXd7IJSBgmbksL~F+^)Ev26?A2Ym4%r=32ZeD5dn zd=7ja!tTRhi(V0ZX2i6k=HO!d7|zZ-*vN6~9sVReN}HCaBwJ!Hn5JRxVda zF(#EJ_EP3^(k!z2f=xp!>8x6nx$h@y24Mx=M3QBsDS6o;_ilx#aU2|bl;;`xhTB?B ztzFIkE3md}?>@iNYpG<$<5uXWUw+6rBL1Ln*I%rSd*R8ofqV7>q`JF4UQ={_?&mFB zT9eS4g4XG|>mh#(1$i|m#3E!bGDo(b$|~g7D-XHfMuIP^$D%*jbDZ>t_^i7Cq@F## z%-Cxo?Zm$y%HETlxBfPKnEeRzUDR}RCpUgi&G0e=y~&PeAa)Yj!M!`@JL$oW{VPh-^B)IBos&qp>b`*Pn+aK(%+du-(Y8lt)~sSEnu z^uH}xdx5VD`g5aQk8z%AG+JPqdXX$26Tjyja^9V-mneExU&r_QiAa9APY_y}ck?~g z=0nar^Fs1j+w#V)@#zfKb60%9k8V~%Pfx&C?J3v$zG__yeGjSkKkd1toe!CDF23gB zYxY{p*C2Rk4E`g0NuCVwYw_~A*3C!#z3dhEw_Jb7x!cMHYhy+>_%`Omo8v~NXZt(p zdigbycH&KkoZn^F*`faFkGu7$!Fb}vL~46{6(<-sq=q=oJ;G9YFs^AkUrwO-Ul@$N zn%L_^<5NPp1qnJXjcY*RMRhg}Kg+uhIcpc{$38zb+<}ysk6!nidt^ngdKvpCZ#@#Y zx0-$y{64dp8+864$LNpa((gi0tE`myYJ6Z%_a{1)5xv%O9%nlpvJy)hKm;GBJmf3m8 zX|?^r>a&J^=*)}R3w@gC=QC^e;~HVH8+u(|IOKdj+cqJajL)yJvU~;G!$vF=m-D~w z`-;}?))lEAPC|R?D~Fu#N7Sn?U#vWsjVFq=JDbld1~C?V^W09|#SJVy+`ZD5*u+iM zi8i4Z2T)8W@%SBp=LbMVb^Br4D-*G$BZr(9=iY%9lBed*VD_aG=1$OvF_5)ok3cW^ zgF}IJnW?)FJ*x+|#kWL`D0CIc&fizK{=f}A^wdo^y{kbiEAd)Y1 z6pgiQ&?wZ2qT-Kcr$bHUorGX zv;lIc_cSz`E6{8fm|X*eWFP%&3woomfitJ=j^@|cpgL84=+(y# zJDUT8V#(sA8Vdvcfj4sX` zmwRrj>Y7ybrc}BnRf|cvM#ka{cTQ8*OHx(!VWma=ym&eGBS zq^;G(b*1EqePjSf@a%W{_kjNJyG}??0c1m zour(7LNeAL<93bORgidb^qh`=(}Pcm?D^D48>HLFr(xY;=SWtT`Ez}qO{1?M@zOYw zbrlkJeQG=Z??lq}6~vnX*VqN6L^O^f5dzw??@W>B*v7-o=~+8UXiZ?onKwr+qz32UUi*$z9G#6*Dr`o91Pv?hUFH+e+k4Yt=kw3^c=h!_U-vQP*2WbV zG0w6~x8H%dELyNiY3qPo_3K!FZawTwl;qI%=On*QH?cjGcrcObK{xN?&_F*K4aVz* zeoHeFF+-vPZL5@f@N(j>BR*MW`0XO^zHNt{*A{zu`}1pP?l5Ii4%yN)@@3~?r$qwW9}oDY<((nZx;N3CWLdk$-1NGaemYVRy%j{s zo>xH3^NG32+yyJ$n#Hj6l3wy``TAk!1+0w&GR@x)=1`L7t(TS4HATAriSB@vhJBYh z`8jNKyF)e(13~)TQRr6>A9mJd=iI1zv-3@xYn#k(9QR(w>J_Y~_8fM8VcQ^R_hZKO z?xy-hUdK!vQun1EvokWY`1#sO$G(%r%z2HS@H|L9yMA=oxzL``53V&r@&YBBj|3&y zWM$Ps-|3-c`&b!*zA3r~pc5@y6cIn(2Y9t1;(k;lakoA*qAq(m|LZd&iD!5p9`Q0# z_U_!?e}6$NbNI0HE_D3l=^w~jpZoIE&Y9?}SF?6yt7<2ESwqG=!iu&a@rr1^G0te+ zUf<8T<1g6rN5fmq+^Dg+Zzc1?9}hc!_*ee$`)+7#n!GpZ&LCsZj%w({@?yrv#f*rr78%_n?6hg^LeXM>J_Qa>yeJy;og1QFRzCObD)JK^if1v9av_;{nn)v^u zQ-wGH8vnIks$1h>09xBqN3v(2?$NzWcPG%(tvr;0^3C*|;7Nov`W{=3FkIt}hivb% zbI~#AcbtC2`C#roWYjv=*w-JFFpsRKRq@m~%uOQIrX@z{bz^)1Vq9JU@$31Y>!`V} zj54*=P(SP|-e7gJX*o#Ls%EsTnO6=rt@jRwA;Zd*%6QtQo8sjVAr@1*4 zmHARMRuqY+k`44v&Z~{^jz23|D0)6m?F26;Zs9wyVW=*QpFi`CouAD$65TGwX`U(_kQ9=P-6O=@TQ+tLZ=DeAJGm zpdBr=#xu6~XGbsV%)UlF^co)K{)`VDaehPHCO`M?Jl%D@d>D-V%ZwK~VNv4WHK*qf z7^IWyOT~Xdo6pnh{W*QIXn{K#zWfQR*QVitC&O>YM&^%!BaYE@qx4$Cm({83b>1$5 zt5rqu+Q@)4=3c^*P0Z&H9&v`V_6gIsLbAf{@)Dm;29G%xgA6w_(TD}BhK?!YbHO@ zWA9edf1zdALRjIWKXL{1umV#$&OHL%{C zSsjK(&liq3`|RGi^+zD0^+!Q+ zs}QU(X)4d7zm6YqM(w_0FpUK5M}tZ5=BR~qhBQ-89C6Q$g~n`1KIuHbW04I`pQ?e@ z4ropP<%o0V3F@q?Ao1FWN;6|ZV{8sORmV8HEyvacc`{>+v*_6Oqve~C!5YUk=cB(p zWjg130gYh2VA?{^qdwCL)(@uYlke6;cUI?-Xac5ZZZGs+ zmFEy?j*`aHX)D1-!KT4(6rFnc1_s7@_B51xp_8tZ7_vHuZ^qXdR=TzRbH@|1|#=c9`(XVyiCBR)g=$MSY9!QPRwl|B3vY z)%NqmEq>OtvrhC)d9d*mx^ivFh3LzKU8peA_9k!S%I4(l*+yQgJNjJlKs2>SA0P29 zWIofk;FAaMvhBf%TqFMycNLiU)E9BZLnrxk{L6Hn|E|X91hhH{Tgg0}{4liqaT?N< z%>1zdKk=Q3$j-9DoYUHVb3yFm5AJcU5pVB-o_%`xKY39imMNQd_^hDOV}o;U$bKSc zzmG?$74GrXN4d=?sL;ft53?|E{wI>x)n?Cb{IdnIk>^gE^<46ANIS|Nl~Gn%o>lzy%t*7v+=hbz%z$)CCDgX1h@f2_lwB=H>tW`v%Z)eBpp+560C=V9rE zpEw~+<3n?9bc4vX%*eyp+X`a0(k5rC4X!d$-N=ot7n8|D(>XSp?O?|~mWvI8&4;it zu(=R61vU%j$x6wld9b4)thf>R38rl#J&IQjHWiAu3~UliW^_G@R|_@)_9lb4SQA(^ z@C$iC!^u^oU*ttUZLagSCaQ92Me}Q1y~)J)FnMiyBtjPg6&%m zrt${ui_KsvZ!nJzFqJom^@4@VI|!!o%D$;b`R)Si`e<%H*bCMf!Y05vz)U%9+F7u6 zFz>D;g(WY8zW^pZwiK)t?ESpE^Xdl4an1w8-{Oh!3_)q)O%dGrk$&K*FEyw<+K{Z$hk~Fg|$Gt3GYJQeNo||HIpyv>^I}< zHFZWvI%{t#X~JGgUN6@iZuAMHtNH$jE@#qwN2Yl3-M}Af%^&Nzuv(hEKd&`FqvYXf z=NjtUE(lB&=4-z3HhF(+NovC-@o}WJKb26#e)kG_b|t(x z9@H3s*tgNm4cL3USeLkntM6qlr+-k6gQ`!zfB18FaLt}EcW35XJJA2@NxlPGJ)c1* zwd4O#mB+qV5F6^Z>&1JZ$s;|di~N@^opx%`a{~1f zmCyRrY+<5Fb83DK8C#3WGbVZ!)6R={*R!186j=SP>8#B+SZ{&7TJ5B<=3tHQR-4(A z(LUVLD@p&Q>Hk;07oCBAH}uQDJe~cPVo+BJ*$FW8%2_Sn*n|jqcVqxHqk66!!rzKK z9+FP<%(q0hDfMb?w_1CAH?pQ_(GKWUXIMXfb^2uQR*XK*zW$%PTX7e8SAA{zWbalS zj;t4SP>lEyU5Sy8nIxhwyNCDk{pQSGz1p$vD)zI6r=6bMv2OaZuSc7;9k+WL`&@oc z=Dw7kNWM0hZ*0$fwIJ4h#WmmwLDSke_Ou>itRP0KT1dT0DvQ|%EU3kl@ z{dHmAsX=v~cpA&g_$5EE$9YI=iAT-OguDK`n*AQie~9ofUlD8Dkw~tXhtyyDq1E*2 zX$SM}acEijgZ}<5$)Eh%G`hCvO^e=Zr=2%TDGb$_&zq%(-&K>mC-dnUc_taz`E}N3 ztEQc!2%Y3lZnfg zd?!5cNFUgD4f}l;OgqEbe(3Lq`|^bQZFL7lePVI+(r0y1AGFGwv9Dh^?fg;7ps6>X zmakiwKD`bvJ?y1GO=Y0NO3Bay>#F<*0Ht!_D1|l}_zmCHsE{XHD9^@kazf{Ot z9hp%a5nBNQ3+j8j;A!WT)6S=C`2u@`KhSlh=_`zcWbNrI3F-q!Nmp^zwDVUxf9Aw{|k-pknH7-@`CtZ78u^TN#Y9j5p}jgA1F_JAiBa`DzFo}dXrlhOdYt*4^b6V+H)-5!erK;aLXkjXYRysF!g>l>emU>zE1TnQ zW680)s2=N;@~bAi^_|lWt`vFo9g?T!9+3;9ZOC$`G&*OG=)D`;*-4Jg&f|rlaS=kFf`}?Fzvk7_W7WV%w6()ilqKn#Nku)eEC3$9QGYB2TMqUs0gR`tj4fxADccdX3UFf?<(2>&FVeAok%im1Z>$~nb&!DWtzPo zt6XO<#2X7rDoi|8eFuL*(!ZjZGC#Y`&e_;!gX7khsb>HA>gYM!$W)^XeY+rb;BIdp z?9E1Y?#?tmF+ODIHS);Jn?^_TzcXWHcYWgQX!V5_Yl|K@xNgorCDr(bRA!CY zr*ARuYt4J&mICiD-4^q$z#pxF_Z!T+driKZ+$9yi%LKT8sDCVNWqlHx$*~7jgOz~= zZESU5rC>oDUYP>ISrZJ%Q?2-{PJUWuw^GO`i^+1coX{t&rayiL;G3!S+eM8GRDW`__wwY$@0Vkk_G?8QhmeI+7{AT!n{2rq>vuRoWA&w?!hyFrnR zjKGqOUfX6_S_-`6X8JF2-lsV5i@_Wf;#&S)SHyvKV!PvFIq}Acr(*_+Ckdf8XaJuC zH~JJfXHpD5t>Dw(!8x-FYzizm2DXCDf(6IVcCe!XOm#DC>4jyK!p3dM{!T8n3iQf>4Z^plk@3n=nPOxx&^ns~9ym+e5ZD6WT&u>I)5aGi% zeGsn#-v=&!-^RN;*3b#9&zEL|oO@uH7s`oNcnkXO;u&XU0e|2Ng0wEYDpj(c<~C~- z@mujnz6t-1-0xzfN!M`7jO(W$j};6a(l^DQp3pZo%0)+g_f-Kpnp1k9GYXx}yo={N z*{+@UrjT%t5arT|_d%>)Po!_?I!4_H5uqQfGPaTV z(Sq1B;ZNqL&^ zTUZ;|Qn1r`*Q2r54OR|jWI!Nqg$>&HMqdepiPkV-6(Ra#U^M~yD&G{C$`__jkm>u$ z4mbSO0?1>FzHWxBTfx6FFo=%|;xs`+ebtqloQEoO>!*gWKEl>1%wOvneMt4wO!x@l z%?da4-MZ)mFFoCteafc~Y$;e+4y6g(L0AQ0`*~MB(e174w6Q{kHAUT!kMEFs{-)+M=l*l1yOps=_ zaLe0DoW_#e@^*nWgN4hhdK)0Dg)q-QL8QgsF7P(+odS4-?FB1-syqJ*Q(aDgRfD}; z0QJ7vemqI>lP!FY`_4ViISS)Ze#LKV9UY0c}w}#+4SME zqzP*wtd96bzJ>H$whh_T@|P}kMYo&3T&0M_Jfgn?8vW2XLx4LLxb4pk+e_Gvh%oUw zMc7D)*Lko}urRMx#9wkd>ovk|vH5J_NSo0M)0{`hniSB^DTj8wGsLSUP1(Ot|C&F* z^Xx^&9%%OhGV!}2Mcx{T=LmB-eOK#)9po`j9vAQ)9%n}XM=oC#63hDRZ_a{1X?avH z$DmjEG=E+fR?^A(6-;I-J;IiP#R8bxq8jWNyoBpd{iwmlH##Vj=qBn&GIHm>f>;G~ z&Z3Tz-?`Gv4M7_xRb=MIapI-lzQ>HE*d3Jj=`%S#B1^zxV4nUZnO6aJ4ElG9KJ9C5ZNW8PmA)jC zQ`h?Bcxb65UfZFShW_=IRSZq%&po#}Mn`%}L~qjf>p& zJlUbI`kEr$V4yt0=D`NQ5aH%g`xL*8@dZ{R#K;TWRgiM-DIDV-Y3MB@-U#tL9ZhAb z1sevtNNG*|VDAe0%F{P*3Dv*y>>}M1>CUq0d^@9iykC=y>VG@&juLO$#tZJ<`1XA> zzRdkl_cD;^PkA;6XxeUCGbk)ye4un$j zE2N{H7o3CHE(q^-$!;zBqaQDb{T04c$H~nuUwZHDVt#!7jO%m7-@*pLrognvsYlo@ zu-FS`%z0c7+Y43(X55zxOH5BUkRr9F!{t~LD{6MCTLlA zHuBbfB|oE^du4c(_&nmR{!Z4H(D8g3Ro~m8T@H35@5xUSmveKhVQ`;v({z)ji*WIw zM|AqZ2Eb?v^VIS0EOh$8*y@TU+g%=maUPu|{wb{ym5A%Rz8&d`bBfc@f(XZ!kcUxDW(MI5zJ z6~CqMpz)vlzWMr!TmHM)e|pi3^BVBjjj0mV;|{RxU`Eg6n--h3lH=^3tU;=?QNqUw z_r_~A*wSAX#P)%mtFpWu`u>Hfl7_c&{ylf2U9P5ds~ zjDB$DjPo$>0eV^$ZOV#(+M>65x`KFbAx&Kcd?<~vVM)hcS#&A9r-4S{vy*V#JD7jT zD>Sy4!|a)u#J$*4bBv(U(M?^lA6*N-@_kAkyd>Kvr^tKq#WT(da6K7*%O|ksoHgS- zroQxj;!1N^@iRq6=Ez{PU?-t?sU7zEjAbu);QV@ImEqk2r@t*vI%a3Ge zdpG_4>=|bh?Q?*pw={ zj22v1}pcert>&){2E>c z0=%ehhv20wZRcuiOt;aG#N!xv1NiHCmz?1vW0xBHH-D-QmpzXCo^bhJCZA|Db(A-@ z)sxvw$kAocL6z^(%7WOR;pbYV|1Rlygx79ieul1*d+;M0joM@z`OSgLpEUV7H^1PV z;rDA@!u9j~NdxPP^v!PQ94235zkIu!FL11dU*Ue{vlZCe5a`L<-8}O8vcIp#_Zjg8 zjjVQ@uf7l&VD{-%&Lm^(oAB{jy}!=oLp=1tL+hFu=TOq(^t}`C@bhRM*eQ7b@p;C@9(QbrRt8!%#E+si0<9j8)`w0^YX`LYiJyV4 z-+kSC_y?KfvSX&ljAKpTenF>}rqBPl?Z?a`i%|vf1*pCrl@wpL*z=<e#lUGrUJ5alxS!MSc; zlF>WJtMdjcqlKS9MoZ3ZXP(N|^;s@0)%85IVsEx}{jV;qhbg$m)1f&NiNnOW?u2=yu+mfY;@FdXw^gO zb#R)z+2hOjSfKa&cdy4k7Sd7C(o}DK|6UM#Becz4^ah{DDn1BZedCOC9dVLt!#o;) zvFsvWbmri((c|%jKAjHeWT0cl-qXT#^85PoBe7VyN9Q-^nfjM5J_4Ou=%iH6UxJ(Z zk0@tvsGOX47JnVp=rgz2a(>aJtF|cpHT~meTh7n9bp5u-FXsgOJpvumrtk6TWS~<4 z9aGNR!*ue?xpX=fyT_w*flEhi-TZ#+wa_`AcRO#%Y;NSnZt`d#%*>}}9zPG!+6k@Z z1!$H02KnyMI#O@y(=T@}zje?$2CX6DvlKRK%V;^X&h?jN?7t{lVK@83YR^9Muf4_V zw{HHywUuXwUZ~&Ja`EmJ?OwknJ&)RR8oE`tdHptAuKf1gF+(*&$F%1QeL7V~(Z`|V z_S-O>-1;&7A394sI=?wL+iyFdlZKAlZ^8e;ep~fw&LZ9H^;?&&$~gwz`3|q&x^(^a z%P;5FU&mr!gO1y8eLAJTEr^xB#p}0WI{D?CfX-bWoeNw#qFMF<_D7&&Y}))StO~3F z%WrTTl zOwrl~mIl+iYnM02DGfg(gw+scd`$=zkNd#t!Jeb|JZXNFzr#NqY!C0r+cudtv)`sg zS?lGh7|*_>?{DRqmt48ZKKUDOvc9gbfd-HIeGjz9pgk_yXS=j*zsK$pkk_9H$uVnh zPQQll`FM1n*l6gg90MQ39^8qqA@6$BN9M?@9n9DPenMPh&-47Y@Z!-f2-1bC)(p?X zXT)NMZ`@;iN0Og*X^YPC-*cWHI%X_<7@R)r_qF^!68mi|_D$%BujI`>oo?ueA2X+1 zlf%yjT77kvA@pF4sk#@%Vq2i2{*!#2OGh+&?!(>-9lh%jkCV{u_jr8viFw=xk4+wr zf2_;a;}o>JLbR82S!*ZQRpgU=l(>F9!uK6PwhiCOGwEBKXo}g}LDFsg2h&xM-#F=t-#+7fSbUm#H2aZA+bzj> zhgWs3uSfF3jE|FyTSeTtlZY$1IQU2Q|GH+J_wt_n;yTlJYFUspxT#m1Ed zv86Y-ekjQ<((|PG%|mw(x@TQ{=P&xS3;$FQn}oKh=Z8Gnbid$T$#n3|i}k69XxgpNZuj_Z zb!peZYcsT$+%;pq#g=SxX)3v7VRfdW`Ge1L{Wne712b{V;K^Z=DyfQRyEr|WN z)%KltxcNv{_K{EH<{9TQ<#P+Tzt_f`f-jU6&FX}j3mebIhYdRAioemNBe}TsBltH# z=TzSH2-^ywn6TODe1Umx?@yzZswI?$0Wn;a!RkJ`6qkof^R!8RWND+M#Y zk2c;;u%%#LKTz7;VC4Z!eWUCz>~DhoH}A<-@}Uj$`iAzo!ZrjuPWP>4E%L@aPMgxN zcKKBMwL^CobW53^ldp2=2G@=L*{-ZS_}cO6b=!RqqCVCrpGAH?6XesnWyXmqpFh>u zG3%cp3-uFu2V-zuxc!Rz*^BkcGXy;zm8a&fwBI{t%-tBt9bP{4^?MTjo@H)7*?+uz z%F&k&lg}!Z=O#BF^^+asGeJK03cngWM>fQBI*MdNJI-SVpi{5-uXgFEt#^Km^-)i5 zKiLgd1s3cl<6z6dynZ5@N5QHC7?A0E$%owZ838=XrxdJ{xSI^(VwGS+V8K3`2HO?F z8o+jjuokc#U`+vfonZ4JtPiZ_UF@SK+=$|38(4ksjPvS6J~jl_`Mw#)+sjeBQLuh6 zugtjf1LZW7YM5$Y;=LJ8p6gF2+I(*Z-KB*!p0W}>nChtfv{bKO)d~N zM%YwDnA-9vVbg>mTFfIX_6hv8K4{Nx@TagPU?X4;Dx61H1z6pEe&0|%F9&M`3)i#a zHxkwqir)&>vOxSE!dgS|2f#WOh(APFCt>yErAKR?QLrJf-wH78Zq7o>+2!jfU#^=I zd;j8ZUg`UhWq~nKyq)|$gd7cEX8aCLT_3_QYda5SwUg9o) zka1N}~AY9meeM>!KCKK(KgJyC#p-8TMurtIdfb9kQqySSVob?Z$W6~`cv~NzGU^i0Q(oa*L+h&~6Y+7_8o7SH3 zt=HE?bJE%}Hjs9Vw0A14KNd6&IYelSNIvBRcM*5l!!yqFZGEUOSHByMI?85!s(d?)eCGMaW_|oC`F?KjDH_M1G4zRlo`(8BdI$E9?K95j zc#rA>xB>eIJ21+sZre%I_Q@IdJMAG~eN(q;7nX}Tb;~#^CGAeqF8S1qS+lu)^>S?` znXpm9Di#QvCT#fvVad;+UqyuVLbsf-X2O=Lp5Zn4-Evch`W`zFSK+w z^93=ly*dfozCc*N_=yNppW8*)7-3zaiHx@AbF{@GiM3wW-0P*Lh=IZXD&I6|%06@A zIY)7qe2)Hkl5x|--F*^q)$d!0Tl!hfFWE60u^!FD?HA*+##`pha?)-mZSm)sRgg2cayz>O%1IGy$&%-u6Sf^uPD({o2 z(TiU zxi8%*kkJJ*T3fMA24CgG*AwuqJ!3kNy~i=5Q;gzW^7?A>Qhg=Q_w(9EUJYJeE03R- z=(O>Bxku;uKAke0Gj>4djdts)HQD1PLQ5kj;w$%$W_2Q=+C2ZUa3#FSTtnB-?-Ay`9%-VWMeieN$ zB24|U`Kj^PZo*7^I_KE-#6QCFnkMa%(P+9>aUX)4!CS^L4A4Y z|NauwuTD0->br&XgGbOMG&aZF^tPXy^^|5v?0j%o(VeNe2BCG2DKg`@!3PMUsO3EpDl9IM5 zDXB==-Pv8(Ma9HKB|}9;BSj^pq@v)Q~}=-tov&-tGJ-}61^o{PRbuxhYDo~18z$nu%60x$P^ z|EppdB2$6P^byF&I5Ke@*QhPCCo1Lo#n;+nUWGO0RWRsXWDa;2GxpuOU5#z)$c@Cb z+(*)vi{u9b`G>jtOwzl^lEW|Fd3$L0ZN~0{+xDnowq<*6t0c-XbCq5ra@9}&IewAI zz11P7{PTp^wcB%BL`;Tz)$Rt6YhC?wJOp681Ube>{a%{u9KReTB6yw%c@P>k&xc&W zS)#KZjWU(s8_3yLxd0WAzz>2{nZTSf{4^{-0tq>Di1uOK(8sBF<jgX6!8Zst0>;r%^%H$#VACFK8f?jfEr1ytZrL@kFc?Q;6Z$fL!2Aa0EUN;rQm|tk zd=aoH*h?K)6s+2V)q>T5Wjgqpz-qyqX|#jYfW64U*9%q;mgB&NeDsa`=$rA;x9Fp9 zUF&n&lKn`sePJ-M?-++IC18?Yr)&jS6`0hG{N(?2U=?7_HrWg&<#(bmaeI;Qb%IHL zon_SrCgo?HX$ZqN4AuyCHP7bxO}T%nB1U3!Dhh3UozLO zXCz{snNv2GLdj zPrGc3&^ZQH2Nu!#^?iA~dwRflhxN8AG+Em&YWYgd+s);E<~NPFLBL$Ot9^;=N9m8~ ze1AeGGbzhu(W&<()C~`=!HUsN%Blj{X^)L{U{h(>DDque{w5Sl%1w>&Da&oYn2FtlQOr#b)4m>bHF)eB1glTez8Nj=v~N+% zH*5Qxz7O{4djjPyRM~M#l-nLIQgW57%Ku}mcRlt+!P?WbuUX5NiY@%a59kCNLH;rU zRQap&bGJJ#cX7^ApNft zzAE@`;n}fX5I&}J_uf(iyd&_ACiBvGZC)vh9(bdHL-8izwaWs%jy_o9-Wn7icM7>S zk8dv;dxlleI3O_1-9blv&JtF~>Faxa0mJmXMeyc4VftPIRKC)9zJ zg1N?juo5t&5?$eiz$FMLj7?kIzn3GH}EN72qzG_k7| zS~fH*pHjC?nqQ5tbb5W)mpU&Xr-RLO_h((E%A0s9GNNk`xoUJ-c@%66tP0He9@aEi zE7;qh%TMfI02>5r6@VUP#UrH7H=t#P4#g*gX4koF!0Ye&vU$iFsseJ+6P$m5_wC|^ z(!Oq&J3{Z_aPR%S!CWzUv>om)C->o^6NJ-KF9KbjWvfiD~dXxm z&on&NdtH*3VX!$c9Y$)kNob-h(NN=FHNfujzYceb7Hwxb$a9yB$kxO7~Rs8zAI6~1El)LAUM?6LRa zguPNn1Mo%R+a)$BJ}Y0Bm@S5kiSHxjGYxMeynBULrOlQ&R7r0MT0gW?gr@W;AAmW| zE*~}i{TTlVzSD${GEVGmhu@X3OWH#zeADoC3m@NF(R;aS&a~z{&OW%t?EP|#$c313 znoe}(9(^b-@jLlRUi!caz?9F=XA!&Q+tlFFHb$XEpq7_h!NPDcv)S2Ve&Uu=^Kf6{NAs zkgW1?l{E#5Z6(N*XB~=9C1g~rP>y^_r!-%{WVb`w^I%ahun4b=$;Lx33v!8YF4ttL zK3fXbjxHnnQ2c(;rShqKANPFEDH&IzA`U6}8ArAiS*uqtXvo&=i#`=(CA*Jid!TxH0-mM8o_r#s9Yas{3lGJIM9(KoJ>8kByWmzp=qj@*4%v;sS6~4&dCv1XN z1WnEPm*wONe>+$WSUJx!{&c9j$9H6f`DZ`0R%oh?D4GmQ-0dxuNWMgV0-jlTq#u~| zBiqI1H}B`6twTFYXi8q|cIN$p`4yVe&LXfnFd5TKU3hCeO5Jp^^-~Xx za3^;+65LjG%o^JHZljzOkdL!CM;$$GbfUZb#qROD53CGK%?ZqFGUp6~wSuX++fGA` zZEB6PPmcQ(Q6q_QP4(qP>{diZ+5W*nIG!y&MFFnNzV(GL%Vc(1U-Xh?XLrtdTh z?<&0Bx{~&?3T+LV-H&Pu>U{{+usOHx0S6-3lC4pP^W zk7i_BUgDfP)%UNdx!?_nxs$dnY4^jMf1G=rIRcgkW{u0jHwl&t=3INufn|GSV_=zJ zA|pTb|2gJ6u-6EnPes=*pOSOZwQ2WtiE z2Rm6wnzG$sV_>gyU;|*Y9&8kB)q_oeW#_wP=fR4=oc67PMZuh9W&E1{2j=pB!K%Ps z>ae94tk#2-gEfH3SRg+sgBq}UFjc-9YXqwUbCzElSRZL-<}4W15;Cj3sU7)w1a++}I}mUsr( zR%I!6M&K2B%g3((i)#4~ik`a(*o`~sihrZ4#ADweSR_sRX0*K1 zzC|tHtnG8gMji3*-R6vyD98%m`1gO)|2_6a!K%}=uUX4m^(=lzCs;f34es)Dw>u6Q zI6Z%BWX|k8@__$55>EZ`D9&S zu7R1yrLQ)?+Yaxyl6hH&+PqR0J@Dog{0s4Kt2BcdU{+jMWIMqE9@##y4eW918wOhk zJ5g-pC$bY@dB|GrU3|V-FtKldXC42>^Hx)b+UHw?-UD5&ZBq1C;x?}KChI|&VoQ+ad^hyv0{`` zKQmyHVBQ$H&|=W0d}z||GoNC;3vIL5x9~;aTZB*f9jZL6cr{o%wUb)-V(`5g z8Hv9+WA!c2a$n_H%XDy-JltrltW|unAHE{^RQ=gy4<9jdotBI*lkkz3EX=o*r@>_ygp&{SMm>9JzucI;dDO5huTZ=a+`{NIj|W0x5* zR56}9_~zj27QW{gBOgLn!HKM~MHfH$|2S9?nDXycjQqkxf186=22GX!MP^($2G$7n zR#Bkxr`BEs73};;zsdUp{Q*9;2XLwByB2{BgUNY)`3YYc*cg~)ll19out~5>g-MMq zYA>BSJ78Eba$W*;jV-%)vDgJ5D)rirt|dt`5o316$j>%o}WtN~|lC;J| z2$e&AQg)e3#DkEv>QC}p0M-RI&9hm5c1+8zzoe*^OqFV$twTQlHHXw4HYx4t^mGPl zSnTOTb{<*F9vOFr!RElW^K6!}e=L0#zJ)FLB#jODmKl%0+(CURuHW!LAU%(_YwK&~0Q_);ZRr zmK#BC7P+h3a(vIZ!?5Lcu$P=@X3B;o$^NB@vWAU!TIvhm?_5Ob_!)?z52 z56T?UjlPQQ{;|m+XjRahG3jxzRyqq>oLJM*kb!^T9k=ADA`oNxxeKo3$`Itu15dMJxDo z$hqc!uyruyYoc59SA!XE)O!x-cgAH@`!i$c#H|^Dq02-d_IDvyfSjdEuzs*24>kf; z0;b9m9^sn=EBEltfmJy$nQLNTwP5MQ(DM&7zgp?2wq@3ts()zB(0uD3S_?EQrkO{& z4VvG=fRx>B;EnJeEdW2YcH?Tf1~9SFIQgF$<~vMHs=Ww(4SFZ^QlTGX(XUAOeKY7B zgl^T7*t-Ze0`?M~O~22+4=M08<53D)0kp9cv{Go}&{RE8kKIB0oAfo%CVXfzr#3^I zg!WtQ6Y0GJdw$uMGZPFQcis(pDOzG#{>b?5D%Xuf3 zG^I@&HFlbEU1;RZrk`)@1!u38aGF}si>-qMEZ=I$oodQOz~`~G2)RG=Y|gQq*>Zdb z{JYMQ()S|Nuy|cW<`0M%>s(r=_CA%L!2sG}$SsMwY~}R`RaPZ;Wdwc{)^`6G8Ge$7 zy8nm&bgFwyY6j~BdyO#h6TVKc0S{jvSigsF7;Fs8*|sOZCc(UITiRN9UobEQ?ejbv zZ^te*9+8*s5Z41Z>tbhCusTs0s?A8b7BvO}K`UR+xARr<7xsrr55=2RPR%@2fVI8F z-UBQ_z7DJl>|CDBwAEV3ZtHjmA8>n8syup;8}aBG0vkUxI_tLz6lifi~#TKM6MA!REmF!JKKv!1{djN}8E}rGNR*B+Wu-3(%}_N3c?`mr~qJq4{9T5k$kH?*M?v>|BYJ~Ww=Gx?Oq3^XYx zV~&fq)w}PeKO*!Z=nK%rCpX3|dVRvDs(>CiP0J5hbX&d=dM+wqJc(VT&d;Ii)^vfGS*{+;#I>4)MPgZ+{;_^RL=OM|ZkzJ)aS`r*smv3Y*0 zsgKNaXn*jXCTqwz7Z9stDKBef)f0p&wu0!hn zXwm14BQOJXY^%&^4+g*OnKX7}6(ie+Ju}Fj$Fp&dX^-t|%XKlfb1wW-@CTxY;^zqe zOBrd{%nx866DHOE+1rrzA5r%f znrHr)7lpSJ-izPs8u5yHT(n2PX9~1vU(pPTj4+n_H0{Z}|U6KTX~$c<0jKm3+0rTUKf3OX{W@ ztQIU)-3>z@Ns7zCeOKAEqz0XSz8SkmZf2jB+wYQ7YZZ~> zlu43YJ90z&wcJTAIkmn>mEIU~?NwSXJmDr7g1-7j;<%N(-Yjj&fPpy3WOxsBmd+jE2JmV^CSO|lOm zzCstWxvhudw`L;y3?DJG%SWwc)HeYbMfJB9`0jTw_!r;lr&8S?GKaqEcCGI?SJ@n~ zz6kae@j3aPE@B#zx3@X+w#O`&Gv(HY&~S!XB4#_t4;q20TkW%xGB!4&rx8rn^G2R& zmp!j>PQf!z4ZzY+pNWzqSYG;}UxgihH) zXA?Thex%FuPig2(qdX_j+4pO0=QC;O+`>-j6EoY4K*^GOj#vaM_h9Q_RbbCYMt&lj zeUuTX@L*xEs0S+n6MatE3a~N{RtHwqv z$sr-aG|r&Uj;wmb$I&&md?U%>jmhzxv27RUOmO&ZYTNFd8UE(B?aW!&7Cb8l zxz}YtjzZoExe~I1F9W;`FlvFj;jQ#2A~&|8;2)swLu5B1dqmH9hy?qzj3|=CEtz+M z*T7u~UZIWJjZrs=(0ve-ObTB4eBcYTQD-4@B?pt;S+leUt5I|DzeJQmk=2Kusc2eTqO7@{g*=TybvA=U zSqYU_RcL18&o=_KFIkSCmgxVy^Q^}bk59@w`IR}1=jSv>6W0lITB4NKZafaE1OP;L zeiq}|asPhZ_2_OvcQ}7JuD*C?_mwL5UDW!{z@Cg?C!^K5j8@T{A^QGEMk6DboMG@8 zwxm0%yH)2%Bl~5`@!Q?wit-=raaQkBpzfQbaXx9BK^jrgsFXB#)lLRqPZ7~a@6zL^ zShNFfMk4BZTp)ZI%4ot@A(#wdSfJr?YzgbkinC+M1 zO*#D1GI$!{uZRC(;eXKNKgf9s>uiqm*SJQg3W`e zGh~#L^PIp1YFm^S;o%GOHwf=4ygPZ8GVPXkS#cKSMRsYglExG~1t-~Q2sRIv4<>r# zC)g@j9+=v@=ik-*Rb0+}j$lCw()i<3PTy4R*;;!(@^w{D{@B8GA+v6l{{TXTf@SHa=*j%{f7RZq42=-jyY9shWGoW}Gc>1?PQ=md&{HJ4{)- z9vfWsDDj0D=~uvi8qel=8J4eCn|s${U-mIZpb@%NPhqeySOeH*Wgms7$01jZTtBvT zgZ-Rm;|Kqv%QUIYM3UZ@-YMJSt}<0INzu8A&ivPCogZ=ORBM4mJ?S?&68o7cbV`{7 za`9KtDf-Pa;figuS*iX0F!b3?=;hFj*QS=QhhDM?y&ZbPCiDU5y_?V{pigc>Ux2=r zf}SsVej($-$;;+<-qbl4rXMrO$dfi{tX)(mYe1+5!eQL(=*L(uAc zXcD`bgf{SsN8{@KBgqp#Y0vZEt;oDmA-eyA^?ESlMF!{ZmJ|DS(k}ABhQY!jOLz2s zPoQ)QdhHhU)-C9Lo6se1W14R5HI{=-gAHNxmw7hFN&MU+{GLGWv5b$eTTb~sfe7?M z=vKO7OB5^&X6;)ERtr|_;FB_K0xJRw^Xylq-CNLyx1djNL62=h7n`&5O#7_7m4k)B zB)`w4OdHVAgN_r?C;9e%PoNX}fTOI$mOik4Fsn>u-d=4Y-U@av&&C~`pi*<8-lM2i z_Y|9Z6pWZ*c;x4})Bdwvi979cs-5@nf9Ri+Djnl&bt+4(f0%xK}_BD;LsYl{BgXpQ*zHENmAlIQs-JM#Qh$~*TJ?#3nL1sPs z3YNa#&oeWd>(MpzEu!x}{%!mW++7CF>$}vsh)g8^XG7<2#MV=Gbudu%`epUqCSw3R zDSwx{@>f_N2z&!QDt|YddL)0H=ov-Ne~UfWIP`3hza{e5YUwMt_06I$8d+B7(~Kk37fs)}=nS!T}r!B|%^m)Ko`qkF@+A~6(sT875=DyzJY0u^MIz+G`urdcG^I@PR z7$^n%I*-O@NL!8J%!eHz`No?pmpEu(-jyT#5%{Oz|Az3lCGm5l&*qo1s)xUJ2mT{5 z1mmJK^41*sW~!`-z7er^m)5uQx#^R#54?}{(C+2?i@Baji_r~O9y#~H+t zq#SPhh3#AT-QRy1KFby6{rzLewV$~hKVRgo+Cr`oxdu~i6}h3a=szO2$C7j2->)~R zxrraIMEOTzXHhHrVb#m=my6sh9dg$F{aWlgON=O@w7Yuby3SrU@9*E3)$NWu6uU!q zyn#LbcN4I1He2f*g8aua0>Avrqg99pjrP4w468-r%8VFYUi zoAh9vU^8IZ(B&t5ePGibY#40HBRc^$3+6mGJFE4{c~JR@z9q1jM|J~j-Gk-63jg$7 zZkr3iavhlDyA-Sd%zG|VXf@CxDQL~mqA6(I&}viAhM+a2piM$+PeEIN*6TwPU#0ve z`V%xMC*#qdC!L$Ehdu^foqPR~MYqq*wnLw@AiAuC_lvp1~H zEva*}oLkBWeOny7*1YDleZh* zqRY0{D|r}$w;Nt7UoxIegAIXQ$}@4cq_eLv=<6xyQnuCYobS0@*WKHGqQ|4uXJ6al zZ-rmg-Em(2tHc3E7mP(WS?vVmK<0$SVIT)g8led#>l&$aE_5F z&%Pe^Kl{oXrsjU@7&`0Qwa#0gyUtRvy)zh?|J1U1_Vp%oTJhX8&c0S4JJCzq6@TDN zhpbv3*x%p1mG!}8=Ca~C!ldf#YbUaKcMvCM&&)WmoVYhs z#>OV}6oaX=ujQs)_PmyS_O&1WI{4Mu*8(qp^4ZsE_`Bg(W9j;jwj3MBuyGuIYYY*) zr@`hNn2dJ|U~6FM%xl@(@xQ;E9&ahU<7x29*few?7zjVGY@U5BGV|sgoBYncE+DsL z$r&!W#8^Re)sB~NW>)%a{w>@mGE85PbE;20uKTPyXK0@dw9jMj<1|UA&;R_VI!_}y zo6yeWpgAr{%0l>lQjE&^h<`a{Lz2`Am8`Z}iuh7+dnX=#O*D z@dKjsSxe_8dB@Irf1QcEH=?upkGecVW<5A#rk?deTID&8&Vv8dc4noaa|=6F`;X9m zLoxUHAFu`wwg%P=W}Wj8*~~YvzVu)PV2vIu0w(&LvQe;l4^|6S=fEnY4S|V$zh*4* z`r4lMQ<+mAdaeJ_sLHH*#K+JT+ZI!CY_Bd=A8DO^= z&azgTALQkAh|sH@XJ4z}A9_J7ezB9^5o40Gua`K^zP7=?bZjhstxtKVJUGv#uBJHq zdYx|;)IDKRP79=Ayd)NXVTgbDt#6!Zq&)lTpNN!GBeq9RVZS6l7XMjZavFBMR>&Q| z9IM+FdV_z(?#T*H=k#rRSI)q;vvY}lPHdB-zMPrOfXr$fXo!=j9FnX7 zzF5ob1(aj2J1v=WAaWO}snJWlmJ_1xv=VZXR`6`7yGcsqsw63~Jop*VBmsdcl4Oc-os(w#8(P`FdRT5cBbCJ;PBTT)>aMU~h$30jRMXM#%kS*kAtuK=$E zmolo*W%ND~`lP6L=4z|5Q3CD^vv=aPnhU`Cwl(M?AsJ>SiMbviH^ z$BTN{{{gGz*?9H`9^VoVO1AyD`$vK}68Eo#e*%7W-t|`||B;+`ttmAE;G+G~&oS3OEhVV?Ad;6F|B&U&zeLEL%Xbp!eobjz0s!n8LF^oC97)zEu4p*KUH+=Si(eQgu^2=v0Yr?!6vdQA$t>uthMn7v{JZMaG5=XzL#S+_%#{PEY0+UB%Gyp;>z{9lWwNO9Qkpv}l4R_cT^;x2JjcUnlfx=+0PFA6S(GlY9?@Re-%s^fct;N(x~fC&8P+ zTLoABd|ysDDAxn;QWv@j&k{V-oA5{;vQH!aT%Mf25@? zD~kbG9(HEQd{+x^QyRR|KBn&Edt4qY9F#AC^tgwHot#}uE0*aUJmH*eu}Ko z$2mo4O7^~F*#%@vd}Jm68_-Ij*>!WCYO^w)*P*uz+V>=l3(-rRJMX9G%!>YQ8OMy| zbp@)xd^e*)K4wyMy#tZ57o8*M9I|x2^ttPl`kW}|yx6W-;+|l^X2Ej7oc(DDEZc!e zy>EbJg0=E&)~7l<#oe62i>-=RzRO+2YvHSa&A9$M&U02YfcIIfs*Uu*+X?Ru;oT3f z@?Vr*c&|*{g;Wl`Z3pu+yz4xh<;(qq2jy-){jEaUamP_|74$SWVbijm#Y~~9{G+2G zYUf|-br!51%&%TcKEr++G*z!(`}V)3Uc1maW9fYFbJr>P>f4DwwcE~@V8dXgV9t7- z04s4|Qm?aM5wNZ5wft`UDfm>qJ~p;#y*9x+46mx!d*I!qUTb!-9)S0`)N3C)y7$=m zmoa!4tPAWCp3U^}8})sbw)eMJ?tV7!6^2dZ7Ll7cLOB)xi84PT_n}03>TCk7kb5v$ zi|J(pt4A|lR0V&<-k5pkuqq!1KgWYLzlv4L_}&Kp5d5cz{&(c$%GaX>?**Un;1Td) zu?PGb{B|?{wtjH~r_5NextJjpky${dq%s!2-d`s4F7~q}fNZURYLUs?Z3No)#p3O< z=q4Y|Sf`5T*s--J=PWt?RGPeEWZ`yiMo#VSocet^wL5aEQ6;*Y(LH!pEdIr`bcbr4 zDncW&`JSAVR6rTJr_sG$6N{g%*Vz8!b#T<28WRJygt6538nXA`-<%hVbIp8GzCAHs zxkZ~B6a9mSRfyN3EBpLd{Jpx|b-bSih+};vF!#%;kB-n?@hw;Fu9xw3^ zoa6Lv8K*-prFL9H^zJNzSuSLU@(o%@XSFUCUzV~_euOnYgyu3-Q7mTGYqy4mJJr=IdG<|4bSB31dph%pvqS?@_`E~ z7;oXZUU-yW8GNr8CSyY>w0>xu9@2T>9Ac$;pN!g3J6=VG)z}b$r4jzBOJnA@yj*#< zKQFX1>-~w`%b47cOcye8PSWg$p8Xc*zG8bYIPcpaE3--wUlMy8V)18q7F|C6Yq@;U z#?^ipLyBN8tYkjDO!o_AJMB~5Zy$O+buMEE6}CG|Y_5j4<%(GRFA1CJXa4tF$k@U4 zWIi2Qk6r!fD!MWj|EZ77gsp?;m_wI!ow6G5W{{ag<}}@2oZoHbZTH|0oWmVGU)qPU zkMZ_BG4neu{JWgLx_fy)s41qtgKYfZ>rOv`J+yAa_WvYsz9Kbol1~4}LmS z{?XCU9E*Qj?D&+a!`XJN^$wwVSJPGbCtpj*y7CXU2$yy63mtVGpK(LmJc=#wwrv&Ps+0xY#mI+PWX2ht3D<2CL)QJ3m~_%2d{L z`H;%tn}pAAj;MoH^nvu}h;I1jZc6goSvPUnh5nA!?yL`51FrH}CXgv?`}fQd*=I4| zeehqKBPx*HK(Ydk8%@J$J^n8SNd<1huQ8n*_ce>|@_95(p?9;LTtbH%g*}u_z0m4 zzq%70ljtbCH5R{FbX;rc@XLSbMSeO|{vRMd)2-`&r>P_D`ag&KAN1rOEC6QZRmT1z zunp3%Fd6&Gz}CUMW1pB&|9F7@@(90>P^b4bo>j<){kWSIVj{5>Ln*(UO5_{+B- zUvqmbUL^T?YYKVs#|Dz+%Sm$-`DhROz#^Y-%IiL3`vlw-MxW{TYwx0C9Ub9M#LTm< zPY*fUomWS%pAK11mVJf&Ds)tf9gmtioa=mbXQx`{t8*LsSnqz{*H@=pWsOjAF8d>& z)N48w`$(+mere9x@}(bhyc#3zkzVc7TxF)MeL6daEoJy^cRXF%`Wmt|pVqRc9+_<9 ztMpf7zl$EjaLKAOy9r|O6ndTO~MtIC65yXYK1XHmb_`Oh!teyZAySLdT14SW4`N_niIGy2(B{2gNFW2R1L zxvTh`x@&NkoVSox$uR3v?$OQ5qweeMe|%1_|32Wd-7~kz#fYx;F9(@Dh_1^Kbk3pk z60!a5Y1#hpk=ib0l>ZIB_i&Fcqg7gt@-gkU`R^*DcG8&}&}DR=nT~EV|8g14qO;}m zx{NMQ%l0EKBWasu|4IM7Hx~ap&&EqPwf&=>+VpLkqO%8`MfYi)^NbAkIO#2qv~Mm)AuFLp9;@YCQnB&*zV*pzRCSFUnZU|JU>n7`IwWZ z2p+NjA>nx_q33f>o+^08ABe@jAUyXcc)Bbesf%WKmWK5?s!zeA&J(czNl}HN_dqWq zPilVcu;{_NEFHo#3Qr65bwAI>H3^;$C(j%_<>dPg;knS{A>SNAwd-U99%`KQm;i`KRCyf5nV*8c!tg-|OTTzkBo|)@Shh z`Q3RBvYz;=_PcLKm-5B%f)9i?^}DN(%l?}7yKh9!<99bfFN3bi=?YWtk@(%i$d4nh z>f-I2$fxkTW5^ebXutbKDdactyNk!j-`8XDm&)9;I-tiU70>hf%!8isj#@Hf!p-Pd zLdWw&$77r7@b%rr?;b_R=r`y?qT_R>jxGG|$9?BOtP;O_qMr5pf2QYmN50K|?>F^2 zahI94uiyP(vj5wJZ1K0WZ0?cC4k0^(tQvoQdygu6-x%&^lhYlznDbK){%d~sJhG)@ zZohjKtOP8T-<^92=Lt5UN1*q*=x)Ec8v5iW^k(R5o6vip7k=AmpWE+V`wr_L=tMcV z^X7A^ES>&el0RG?T=8u`Tln2|4+R7L--*SENCk{@taLW@yZ`2!&K7?6Bj=qVF;vq4O%K%a^2O`(wU!xyhPA{O%so8Jo~$^mu<#+x&Nx(Yoa4`?`$U%ygcs z-(B@R_WORI%jop9Y(L^M5+8Y#bZQ>aKJs7gcD7A#{ifw3i_Xl4Y44L-=Qp3bPVx2f zuOQxy&QyMPDfH=0=(W&eDd=JJk7IN8quRGU9-EbK@AhpM;3@i{_HBIk!vH*Uq^o@VI}>x z-6~)27%Lsw>$iRLad@UFW93gidzY1Om*2erPZ9Pizxz+{Y~gq3{s8|5e&u&Rmc*ar zcbCJz4!@i)Fz!#{Px8BquQCExeR(qeCiuk`4n5#qoBMzj&^l7k8ld&3ptVE$rVlMl z5)0V(6KJ(O8^>Xv^5uzRs5^ZF0pT$o2?l}>Xr6eVFHaFXZ?<^;5W+U%op4+Oi7gM8x@ z9_hOWNblAB-?#)_{KEXLlSZ#)$2(k@F$ryHyk zOwRh4>8KbB=OKdx{d7W=S-cFapJ6NKZxXo)Tmd*$=S7A@R32-HG%_9>(dMY z=|{EAoG<*9IoCS!A}>tfc$ndzVzHfcqwq-`8jpWUkKg25eLFbFi!#D&zMLp}N6do4d3*Kn)icaHN-oKgKT&L7oF?@aSS?yY| zaowly$Jz0&3XKT!#K`!stvG@rhcY!%xE`o)@!h&6x zlPmVEgB63RF+kZ6e76ipQm1*>bN(Bel^?OE2&@h4Nc(0za>jqf;+tuUxdHx8TQ}6?&aB7>UPef_$j=vV>>}Ao?ixEIWn1ljKzN>imJ^{EHot z4=^6a+;!fC&K@vT=W8F|vd+ig&s~Yd|4+*B7x1gG%2DTENv`uD(w#@-_J@d6qhFM1J6DeIG`JC662HJzx3xG`4_;}Qt)pGKlq55YfAzTb?#*SLZM>SaCXvni zD`k&=Y@B4tQl!qlDf`-wU^h8W42l(5e4Wzu%1h#1v;1GN-}*P|PRim~bgDMH`vpgt z-)`bM{ao`~OBGq~+l~!VKUK&S|D9N?$eida^C7wVa$i=9h^V=)9ht6w==0B~n6`Jz zxN;yQGe`$Dn)n`-%#9=PPd&@Idr9MTD-HPNdmWLWcJqj;k`v)yfPZPjypPG)Yw2gK zHu(=G^ur&8KX;D!_5a1LM zmv-2T4Qt5$UGjL0X~V(bKKEEDr}BQ`sZYKfDZY6RX^#e1;ve8y+Pu?Wq`zqgq$*3+ z?SWr2|AkiKrIOZf4(L9079j@Kh4OJs?t_$aEP^j=ti)e0d{4ut+Ji3la`}>~UQkkw zvev5={%tGqHwpjWEWOscr^BeCvItCsLCn|$j=aeAAXjwMO8i|S_pBwS;?=6pN+myJ zy1S;FuIDM_s*heV-;+zop}m8<03t#5!E`&~?3Cnt9sc&rmH63`&e2x8kkAT8qa67T z{}=m_&s#CipdSyPJKyvloo_~3DW_`qhn~L@{}0id=mUxNX}_q69g^R6;m=x$?-u@( zt-7|y4C%HXGRw*I8+G}PAvck&M~v8pOWr4vYELn@fY(fKfz|e^1!UP zyI_l8VK6B#`3bfTR^{Q#?&N+-2Nr>^VV?I>!1hY{WLbU39!u-C=U#4(i|49wQFPa! zr~QR?8Ht`@^t6E~Jx8afryo6iFLLWyLr*W5(i2Kg&mww8j&?}z8R_0*v!8%*hWIz2sA=qY-sThAbR3c-|~<@EIQqNn_2Zap#dlz}Nde@ai! zJbG%6ck3zn9sA{AO3zd2=_$O0_c04>J<`TY!J5FFzHJY>yTHB$-FO1sYAkX1wjVS{ zTz%D^>WA&<$$R;V%eUEcM*R>{x#^5pY-L{1>#j;-{9H!OQ+7K!Ogv=VO@{^KnD319ZZw3vjiEWYeK{KG{nak3vUjyL&}d|Aq}<0zTj+3oXg zYf*yI?iM~yyyD~)bFX2Owk~sH^;4V=f%ig5^EZ^6;@_9>UH58VBBb8rrb-iw5%XS; zs#}?_UbhmjM3}a;RQ>xN*`Nhj+J>#{*_s-9hnP@p8q7W0Du2Y!SZ2 z?b^qE*s4n(-&FxIivJ|}9)-X2loj({fhR3~@m)Ex52L5AKzB*6clAGN8ZJ5Y1^0-OeJ>XOnktmbA69+vL7T>RxQY zb{%_USnM$LO94!EyR2P0HSI}Rv>>;NoYQaZ0^0y{`mOz7nWwq^))BB0Fel$6Sh)j} zakJo0oM!}6^ZZGajcPxe`>olxvww2BJy(jJ2J|$5DLp5qr>6!z?d5Jg!{})PQ+iHF zPftI3`gXYWtf8kDOzAm3Jw1!)8QJO9Q?^9=2UB{EOHWU65B~2ix1LV)OoAyr$EK&J z1w9K6=% zMHOy6gXk#)Q+h(_>FGsJd8J!V3_WFFN>BVFTejys+U`7hYWLZCfRxX<23`&R0s;6* zpUnIu>p>4z09FB(t+>n>YXmF`rsBhVV~S1S#9bg&&>DScqQ4PZw?}^)SeFOu0qX>F zra1`K?xRX zTch<^KC{X{*oH^84J>oN+rA#KJP$Sq7WQCcV8tG68m!cVEr3Nm*cw>12g~ebztMq7 z*%p8`g2}vX?lsuw;!2^lrJ&V7>q$XthBlaj)(vee1#Jl0bPC!ew1pJ31!!x~ynES_ zzYS>XDdh4$&3;>zf8I-=g;UU~p_Ta1!lc&(tpeJaJe%_+eh)_m#O)D>UwC@pY1x8j z6rO=Cc;?`l-hyWXp7kww!gsKKJY#d4qwrL3!P5v&+ZH_C@Ci@C+sa}b{Q`UaRSo+wLcKXJ)zPR)#IKlmM3A>nP%XHT?=zg?ng&0I&Kmj{G`lAz$P77F<9nbIHO&| z{sGU%EjQ`5l={1krSR9oe~$29>gCsG>U5Aw^f$uaWAUH9IltIg^BLMJ{IB9!eu6cE z%{#C#SR2?n*p)md#>3zqb3kOhC_Dr3M9G^|!_U*yj9_ z$5rfYf!|p-MnC1}z{2q5gAIdiRySqvtZ%_n4^Omqb3L7s{w6$P_Ygeu@K|TinazKe@wh524#~Jca`tB9;JBc^l;M*Q4DZ5=UQ;1yVI+IqtmOIZS zmuMdxE^x_7emAgb3OQ#T=MLasfju9({DiL%Y{r9?f=zp{DlpOKl&uGw^k6Mu6JXAC zyR<%MOsF4h4!##EOH5lvz!p8&B-pA0llD{acjj|2sW;=i_j%eIyV9QaQw4w7#VhJO zxN(Y?pXu7cUyjZ;_}k!L5&g%&zdvy&mO7K>xF5ILjO{0m2{DS8d1?DR{ppgG#J4D< ztb4#pJlG&uxd$5qs{-4GjQm7)8mz*@w*XcHrp{++zBRBOFzJi(6WPpr8DGJ=6=Grq zV6~UJu?W~KSb!!cKaq`sOl_qlqItwmO?#jY`BH~T(U+ZT905&qM# zQ{vO!?{oD-?}Ki&(J*WyV7*|Q)!QsQ6An44kK+GhJ_Y+Z&&KbXQ?<89aHpre?aaE- zgUA%E>bM=96<2DV_qcSbc_&fFyFD6G=oFu>=ZlOl=oFjHGU2G686FAL0gXU6u3Cw6 zY+SYR+hwWjykFh^o@X?tJtK5QMow484nFMHr2gNYaR#eE94%~fzM`SlTotIyy&wO$ zX(j#>_DG};=`|%|deS>^m8=1;3!;uAbslv)vg#49L)ZE>EAcz&1EwxztJ-U^?xs^9w64Oxc;iZZ#L2(&_RwDWrYi#Sjn+5nZ?sB%golhk!-xFqL+ItudpDui zL!V4RmpYydgaT{OXPC&0Ypz!9GO5h%cWm}ao!+Jkoqc>~9m>|Kd50=#Hg~4Bc@6r& zCiJ{7;qRoNi_NVWG>(t1#IK_ajg!-}dBfjkIm)GM?jp_NTm5a8G-IS$2VJGP>)(^+ z25EME!aq%^+k!7M-fluKgT9o4E`6yEdj2Pgfh5|$`c|pk{#86hw|~Cx`)rDqUXQLJ zbk%)oC4Pgu-`l$EelOpj4zBU7%$+&yM1;EJ)vw2c(BUTBfe_}el9tsw<%8d_fp+7h(s6g1;2 z_;Yvrrx%7+;zN^lR~fWMXv5OJIo_dT2h0`v+-c|?gb3xBuyvx!{LW4batr+{>KzEv zPgDMdJ!eWFfO*R0_wCBM(G8gANG8w~{p^bQKAIZ;_sO~8GjhTgt0fC3a8!J=_$bB? z_->Oi>-MYkIO}QOYMj;g)lOst|B|A+RFT~_^!4AT_1*p4^hMA&jlTIW5htVX8TX^l zGnegGAC>v6n#!#AEXKcz|Nj-vd>}8sR{mzd3c=2B_hEbfR(Hed?+jNrlS4A?nMihI z$u$zC9M|>`_GIxMeksSlJvisT3rV}`YxJ+LaUMgAQTFfGb&+`eRh^$3J317|9a)Lj zvuSAjjvIHayjED>&yiX9>5!bOl}RlZbwlVbdvGN#&+?NpN#6>pZxhs{F^*` z(?r@F9^p2r((y;Moq5M7rkyIx}GDQ5m>iz8UXJy(FtL6-lzFa$}j7 zURrz(%+8TZB)P0^@_x%ccu7{U6ZvFT-um&B#a5<&7=ap}lI0VfJ=?B^=vyGFzE79> z_9fRL%O0Jy(Qk6zNW?vqBs=WZtrV`U}&h38rMAV%a|&#S(#=Ics6 zdmnnStfQzC&kVdHEA+{XoF$T9&De+QzZcj4BKTFDqUe}Vp!%;X@oN~AjR!Avt})g6 z!5(AuT4W+4)NhbZvTLB$n5CH7hR&v=4##&;HpU1#t+CO)mJVLAJ@=;Vf#4Tqh?KcZ zWXF(g&(yLVM<%<5Z13~6>^sw!mH8o(8ww0%AC8x0WAkgQa`5bN+vUAi$~)J;9Mt^| z6lb7(4F5m>aN_*}!D_&Iz+T0({G=~5f(?Up<4>6FRn@a!`7=~z@W$W}>$j1NOZfi| z0A36Bsg8;)$yeqJLxF{Y!|@r)*Z9;W{`Je`qt62D&7pqh{O6EFzVg4#db7x#uVS!T zFe_h@b|-1Kfc-(zK0l4LXEH*+^-o(|H07uCl1^@kJMAH`Y%piqfft1WMPQFh+9xEZ zT_I;6RDG&;y)TEff8(FFYS&VCx!+;FKiy6{3RVa<0Coq@-Z@>x`1a;hH|5kubLy{F zpE~t>nL%_np=TXE&OA(#hrDv#CO^w5ay6Dcw>CLTI)N%X50ds0*v5W$+U3WF0{QT% z^1IYZ+isWtO8HekM1MHXopu9Q512FGfvH1Au|*f7}AO^4&J<=^tl z=WhZm+IBd8ssP-tav*ql;y#3VXhYDP{HtKb2mSf8ze~Ie{c`3@equ)$tQQ>?CTW&{ z<$_sp`vbw2#61Jm&=!4YqN@p7CTYK%XZc0=n|v|tAMA@f8$W!vYX995)3)wIaBm$c zn?}*ogN$`nOLR|x^@B-RSAJsWJXjx?_q{Not!sWSO>D~h9{n9T*-JO;n-GS&d+Q=| zAC#a%1z(`ZXKf|%g+kc637SnKGt0AajFk^o8l3;6IgLI2Lknl0}5@Wgiy`RJCiq*ZcEH zIjxenk`Et_ODs-)lHUqs%fasD**MvfRcG<=1GzhgLm*<6-o8_HQ2H?YZ^&46AZ1zw zHU*~63Xm`E`@2*chS=8#t>7bvCFew-L3043$=HZKgO**h@__|(3`Ge&eQg3y74)@HX_FRT(Q1&^@)IYOCgWEze@riyf?jt>QTka=3#?_<1CiG9wqtoGfT+K;Ob ze#v(gM7x%Ho5hC6?RFayYzeFwO!6f^!8X7uz+SHq#xMR~Q^0r&rfj+Sx!Y1d$$a|> zw=FGT0kCYHuGrEAw$Wqv1+irTTh_spEvIg2OJz=daM-t8m`fy$7-{r<@^Iok4@o2X za@JE|Dvjf|Ok*EuY+Cl!k21gX+WD0tB(#U=V^Do%n zqz(PbO2ZRJ)3J>D;5W_pWm$dEOU=y*Rz6Zjxj!Txa;L3ZutKnQFzfvo8FSl6uMJG4 z_W1J{agr}a0ZD|(l$U&?0+UFBe^eD1Ssx5tkoZvUjQ z@EJP|$-@%ZJebq>DnEgEDcDz`8`qfWx_mDdo!)sr@)-U5Zui(31#1R#`d+nQO<-RC zO2)7j&F`f}p!Gm&LGDO=ugq{LFo2Bmy&gPYwXg1AvhNjvueP6Zkv8%Oe9n14lI(lc z!(aMY?NdEw=}q>%y5I|cPV@cBpHJ$bX$t@M9=Go`iEJL&m!%G#v}7H=m*-wA(KCvk z9%QWfOUiT#tRL)Jo+V%SUhlDduSIBqfy40*O*;_O{$`kMVeM{8*;l_Z6ljD`omo29 zvc=(xKkjZ1#mICb6aD;Q^LqdhOUB`Q-EK7@)At&Me-!?^kTneWoplfnc029qe;ohd z-Xrt9@?ORGjZCxXAF55;_o_m!A35cF-H4pW&q(Eabs{%=-(mCaqMI!_+lP|Zm*vbV zC6X9t#K#y%ZevjUUbiI6ZRLB7{fPPE3%2hiZD1N~9ZcPgr^=PZ69bR5nI&iiU$o~J z!8XA1!7N`(#p6nrl84bk);I86BV$m=%!B`Z> z?Nm3uIT3Wq+?V+ujDHUtj(?hmpAs5|YFau1= zM1F#mfQ7*W_9mIy*SF?Ts zQ#RbRnGKbc!~MzSK!93w=lz87a>SnRMRyarH@>EIU%Hv@&C9qM-3?#2^C!Be&|MFv z?B4&c>z+pU)HiJ1(#Hx<3I!&?l)-=0Tz=7Y9wAaw?54LkrYcaJ3O+|?cG04@Z|n<0N= zhvN%8C+K^H&Nr~N@lv*B@GZf|X$>_-IKM%)NAAc}HU^1U$lh|IAFHuP%CQak{O>Ra z@GL*UdcdM!YJI1%L9lkP{ldgg_{PA-J+jkaOJMI*TxJYx0j%U9VgrJy`su(7FzvevQDYn zRcLF_9?*8`??A~w-DAabB(M2DXTA7C|NN9d3qR&x4%N`ssDn?7e)C@OPRr&dXqi*~ zW!nX9{_*6zip_)27AU{1Y@UX%7X5x{FG1`7{^9to+Ah8y^#RK+<4NY(mqL2F1hM}#K zhmYv=Xq(qsa#PUSXq$etMQ8!)Qu)6sf5Gc4xxg&`4(;8KRse14+oo@;>++x_R|>70 zHt;#ymISQ^S`TG*lTA~X6iZz;LtBGp`{L@m4)TrQ_gcPh6u$Xl&Z|);qTkrdO_#P0 zqI@w9lIW#bWfotL(9ip_I5wA9vdK8HKzgI-k$naE3AP3{1NL@>Fvj!$%wOP7g7Kvg z-S6oVp%>#l<#LyMG)rQ8#qhLFACCWn=Y%~H7mozxg*5Y|5eKu&A=koN_2a|w5#d#R z7Z122goh<3`}CrtLVYyGjCb$Nih`RTkf}1~;eK?Cp~G6UO1~Tdn*>w$Zz#Ryc=rEk zdms3`=JWsmea?MO{xoT%+7g;_m$I;qAlrhlRMR$XgUo^=CWwixEUdO5vs9xpQi7rg zf}$v5f}$u5I)b1Gim;#~B&nJpbN`;#`<(l}&wX-|vd{N-9*;KXeqGo5x~})X>;3w@-Eiof-%(QMWG3uNDL zXld~JXzYBzLS(kR{zc?@iN6{_rk-cf-S4j{K>ou4T9%9**CH#wi}qP$z09q^@iIO; zqat)2VcoKPNSfqnKm1v5Gsd8dT5S%$J3mgHaPRA9kFwc5d^cAi;d67DPa`}l#jF!J z;l2H2);pK5y@~t}8R#BX0K|rc$e6nE3+KMoqZ4eP_q4=9#$sD+cq-V2Ho{x)x;E^Y zV1xb+F?(*7*iIRY1el=bC|``#Ck%OxJ)9_p6C;JL2+#mV#N?0yqi1}W82ri=Fp zsk-;Sh&*|=*7ZH(i<9KKff?+4kxJz4N)EI7 zy9>xycmIPvLs2dJXolBHz5hY#PB}7%K4x4mGRH(^#?!<%CntN1LRX4R%D%6r$7Hr5 zGv(7SB22?7>)Db~d`G-K?q`i`r}*-uod0w76T!!4d6cz&?y&qwte1NS)JtbyQVVjb z2EWkfNvvxfIqo-;SaY#W;{)T>gA%~dMVrJ^_)!cFYu?| ze-UYudYL_fOx>o(>*cOnsh9KLqrYwT#pc$eUbdoVIhd}O>*nZm{a@;38?yV6v$c8| z{w3{Ys4sGdl%EqFeZ%S{y|>U9x3yzh*qNl9uD`ogLHGu%FS16$@9%`a%yY+#PHTOh zz&$fj7@9Ux`I9=@io8MFwZ}@ZN7qqzFI8?RIoO_l!dE5t>2E1n8;ghe&-k99m_0co z*(3F}FrWTWN?&A0Dd%?#mp5$xYhh@=(d0EDZ{?W2NRG%mZ!~!^`;Hq)p7`J9Zt81# zpR<=du^qj|TVGT5r~Mn-7nvhOc@a4dFKJV^$Q;%<@EUbjq2yKbbbXkav z8>^#bLF(9q136C$-=(};f15ST$H?_UZl}5^?a0`>Zbzj*k@+vi-`RZ$e6U8a2k;GI&tmRT4Tq*soMpUNtjCMx@Z{{&7a1cw(Z2l% z@;6B*ZL?l0#D6k7d2B2{b)Qr6vm5>%_}>tI9w}$}2eJQY*S<(4@75JX&X|#}KT3Tq zgJw7$M|hQW;0Q9~W5y}spzvAV)p@ea^n~F^(*^PU`~~I;pz-Lq^u#eUWUDaY=#=iG9=&#;^T^ zA0)iC;kOAk`1+`^79B?$ZP9Tlq+i~z@W=b~MXnGV_I6}!rH}e;V0m==sP)KOxNl#i zU*vs%$guJf&__LFv^KBl6XxUc`keh^iEUnN>?q}Q2)zs7n=EDOj^TRa{Xkt#^<>1k z!EKfHeIm28kokyT_C?MWnG2#aM;S*l=4%a>85{GhKxX9u_-&E7UsUGk?cZY}bChx9 zATnDH>WfT}I|KTr4@+|$cVGMWx8VM&u2GZe2bX#(@qsQC+ zjn6IeGN4X&Fhhv3g<;A16xh7*2-x@nD6H%pMw zQPdYn78&mqdi_IU-5g;I+erB4nXV1@JKzjOLV9Y0DvT8`dM_-rZj-~1}Le8$JY zx^Dh1xE|?q86vY6nT;iVk&{H`6H%F?*Ugq-nX$TASVaHs@V?0JMCOH2nep_A`w;hB z6!rbi;JT^zZHk@CklB7jU&NBSIei3~zPkCxt<=pGy{!Koome-A&{GSh>*l6|bsGPe zx;co<4#ID(ZdT5uy@l^CDML#gJ;Umzj3LLmrP?=!TtWECWBMY`N%&Ko@Wc8OiDSrL zj#LK4Cv8Dq&#|sOxe4~@dKw!;jxeUpn-x-J<$aOI#h#Sm_5_R}M;-IkBd`7VzQ`o$ z-@SIwu(BR7hRho&e&Pc*AaB_TeUW|im~=FGu`y)jNby6iiczgO#aQ%wa*%oBZKcz3STx3p)%JkJ)?|72>^+d4D*m!aaGFMc& z?dgXF!_qr$XS~jCtvxL+rv3SY+n(Myf=pkX-CBD(zn}4Bbz+@uMbAnwU1w)HdVWlu zU4_g+!jD#GGo=i{7d{jJ%ey>+Z34>%lXYErWE?OAmIbEAb&Rx5_v|Il8sNT#0Gi0k zgH{18Sp@M2Rt#3|!^*+tgN@N#4qr7`nGdT6EA?T^!AiiqaaaXb4Cal)TCgIppg3%V zRuw=Kdk3L4Li5HUeKX@n9~K6y2lK`u53J6I6@%6KuyU{(uxxMqU3pPV@}VBA3BEmq zPp?lq_h<{P1=@-Ln)t#s&{jj!>-~CcO55e`kzoxYy8na`zntEH79R@eeK13W&pNBm z*;jI+jN#>^Qc>D$cV0jHOVS(qgJRdn@{buwemUXSC&*`j%E2a)pO45|=!4MTcIA89 z73`P$|8mX49qF-Vg;`sP`)a~A|EVwXmK)aF-}A&h&^8HQz$90}!aluU)_OKy`;klK z+oen8+u!cKsJR)rr}!c#4ZY+`{uc6^@bd1A!+%YzoHAYptq$5QLgSIVt^#WY`-1?~ zH9m!wGB2%+{|`wMdIR*{5$IB0S3)m1J1$eOHDL2SSQxAmY$4c3ysHI#c_}01w;ADT zrere0=V77Py9J)YKlep06dtoIBg|1I*^wnOGrwZL05tDC*qs$i{P?LMp>Y^@LL z0$T%?F2Z;uY%kbqFzOaHfO#t)D%K;O4URw&lM4neiDOj6FhwMYEbmg5YhL~JY z!1}z{QwyF|lPI$ZY=RG40hS3?D&ct~4_AYgfa&zo^FSP$qyXkZ+W@T&TBk5WbL$o5 zTGpp?WvgHOE?n|mmfDM9q%XJ0V7O+Cm!6UXT1Pj+D){_RX zaY-g=S) z=9gCh=9gFM%JbS%>B{r!r~zB$(INGu(Us?~Co8}+|JoPZZz^(DgQfeh4zLukQt0wX zdFuu%0t>1qL(poVbqezjtS7nOQa{fhUQfiP5@`9*qWu%WD!}rwJEZ4`^05;9X z*9?~9!&fN3blg zTCk{Z%LSVTRs(jiFvZs(82!~c6VGf2kZamx;#Kwrh@1+-Z1BhtY#~@Dm^Ymoz&d#p-}(~n^y&gj2lK|M7c2*CCke|V^3)Ld*zuo@p$3bxFLRf4tpuo|!qu-!xfPa*#r!8U;n2{2-ROHPz< z62re0W=@(aCRisjR@C)HPGEd1GKSp`8((i{kChobf5d1jhv3=&rG78!i=4r`liz$R zPoIOATXNl8Mz5q#F}y?YY9G&U!OFo>E=a_x!6tx>hc1us)q{n>=4r&imV=dp9qYkX zfvp6K=Bemf3)T)MG=7QTAHWt8|wKT>DeLXZ~|>Vwuba ztMXyFV3lCedLi}~fmQgh`C#Q>+lp=;;alL!^I~;i3*pPqTn^tduxcN+63lPk8dsjz zmQFCS&#R*+K;ED$&+C`cBTjt1SQt#?Mg54xBM(gC8?8r$V8vi!-;Qp%^NwqA5N3Vi zk_-!>edAH{M%utmXne(LQic-M;|EP2$S>Q*6j26Ead) zdI?)h*r2+SV%Vw#TBmE1&Og1^%RDSrR~UPV-_3)!AKn__rLQQX#+hkcrp1Vcilq;C z2wypT#Y^0^WZhmc_oMyCZ=zf6b$_)UzEb$gL|($0&xwxeA~~DReH*kZyz00N9mD)5 zVRj~nwf9dA?8rO{S^>W|Ag@knQWk0E+_52+Hd>ys;nhwu#m2>Yj!DYO1k+X(7xl&N za~CWJYynsv@A3#%0Ja>=Th2=!2SqHWS zEG&Rdn`2^W(+q9U!!L1Z1+y-3^Fj0Lv!-)1!lcn$r+kS1F8H$H)AK?VPP+Dj75{*+ z6PUcOfNwvs={U#s9I!^PLj?Q1gB5_S2m7U9CuC$w{VWCB0=7UfUANgY*~lJw#$M66 zX+BJw1AyUV2KO#V{gn2$k+3<9ZawgYl}!+EQ~aIplCOrh9NyWY$4%c3u;pOlKh+-{ z8@s_a_^>TtnM?beJCgtG@TJ()e=x7SY_J9}ue@Bab`K_TE&@}Ry6Zx5yY+m${$@1u zC|n^UaZd0_l23JnEh4NwgY?^sOeqt~z^cJ&d6#<4epQ`DLTiP#0h)|Og(l

2b(TTr#b0b0cJJ1b9={UWQy*EU}3OBg)gdmNr*BmCu}(a;VBa>@D}|5Ua_eI z-YSnS!MeffJy;lQ3s?)79%pI&e<2lTq|I}Ew}i_|wpA}Ylf;Me2sRCD@bbiVun=sE z4=V%f_h8~%sze^xt0D}YUf&X5ua5ba%L081lW$^28?x%Ii06l->3XnQuv}NZ`z>o- z?~aI8G-%>Owz%QvxV(*A5srVQoUoP3ImM#e+UykCb7YkbulGZVy zp^o}L!uI-XTmZJ|2iPcaT<(U~`88(U#O8JV={8Ty#BRKzGfnN-*Mh8ipM7m$bwAL)Za2KwzAbL}YS+FI=8k3F+?#H* z(=gtfvO4}_Y_-B?Ul~}-545k&4KIFF9_e2!1M4LGDFW#94=8sm-cw*>{#0z~AY96_ z#JubVv%qp=vf}NLw_Zs2^fV`Yw9H9e34`Sk{%8rW`wpnob%iS`-65cqnPT`Bz*j$< z&%1w5m#>BJcEZ~hf|U4&~U z+_i2vZyr<7qH80}=q2Q_bhEXMS?RVKBK%?rpBQhtyKx&O-HPC?xH{1<&Ic>^V1@9t zRjGSp>ZPi7X=>AHPT?p3W!6faMpThm&VA8%=x6uoa zmtmc6^dNVAH^UDggZ==ew~p z)7XrwC@!*5Q#QSS{Fk zAJzoc?!iRv3b3^Sa)q`QT2BB?BVo&2d3$KY!K%Otd|_+BihWoUSeXx70aoF|R)a0@VI5#KKCBz8-iK`g zYw}?!(8{27jzFu1)-wXF5!&Dgw3X1(uMf7R z9a?wD^RE`H3apTK#oTW6TlZ|*Ns-A|(bED?Jv@5ulHY>0fwg+D60r1Y&i{fv zPfxup7FLs7q?_ZR2HXjAfF)*1V^2(7{-qIJjjQ)x>BPvhd%dWqTF>Ie< zb?8jlhdF%PzS9S+%EG_i(if>iwmcG#TCkK`A)ihg%h?kG;=&x3zMlz-jm{9=pfBCksH|r8mk@45eUfG?vq_&-@ikYS|6s*nv$?MU>`30NNT#nI4F8t> zO8EQX|F!VHD-bn}-D8lNW+tRKwsVb;#{FML=Qn8=IHF-X{HV1qua5NwN2Z<#C4 zTP~}>(tSE=!Tk0$fqCtd^0w(OA#I=JoApkHue_Z&bpx8H`ck7OHUwNbUdHhuYJPq*Y!vCc3FLwBs%J&|A;J57S zv7yzI9@4L04z>zRuZz-`^{&DAzh&PJ?^<{dAzZBQg>!MI#0G{ zs)rKiTpGbvda!(Wd(gcCtciDP&T!pK;JfQZv(n_M?C22(2^+5EymU0|PSLROyP4x* zXC#D`GEh#~l84=K)tB60pvPy?esyI+8Ib-<@l@Iy_z&jYnb%_}`Hc8J$SUZK(4*xx z3|lQ&1K4Q!)&ftPC!FNRAhxxF$y%y)vSV97dHYe{vM>27=OI?RGIvdosoR}c9?$gU z@hIQ2FW-&%J!Fc_PMXBOWnTtV3w;p!BFdG2yr%b?#LryKV!ZZTM$JqTadC=kB-bI`XV>T;8@Qq^9_5x5j97?N$-5aes}#b zSQbcxzYYGn_P$7km*4YEQu%uBNuF=m_rTx!d~iNU*|mN`doc=qD)h`3g2PMRHvEnD z7CP67vzM3?)bX)*e5|bVQm2!?H>NcBoS*&%oY=G+ooikW)+zMW(0fOrcR|nY3=S_g z7t~R|phl{T>ug2mYUo<$C;xABu0`jTjlnv_rf%p{ z-yNB*_F%j)0$u7-7v0fvJf5W~Nn$;6b>wuO&0&Nqt1tZX`pJLAlv@o;{BhU(Smi}?DEj`c*186c%Q`4BQhbDLI$sF)f&S%kY z(`TjhUX{=SruE{xQsnYdsTcW#%ih!%xl?55{uz5168n3(rfl@1^^9%On4Wf0BH*lf z)+4J9Su;eI_WydHEZ?v{MRzsjEJX>V{(8<2!?#L&)(p$_v${Rfel~t~-EEK78fI5u zb(y8_cPqS*`zZTdd58bs7FUrF=&b+^4zuucdUL=0QzIF-oKkzk3UR=q|CDFLXzW*bw@OApz z^WvN&t_zU8DH8AVN}9DJrw6P`oO&%sF0K$cZO92H^(XAfUk{cI_B?cXM0WTh z?ymy-xwOlNk0kqIogGmdGxw(dKc+w7ywX&#Zm?anWJh)_vWLJv!p~Z}jwE}&mMxde z==dXNbw*sa^kwS6+Q8y#VtNgPIg-Uz&^PQ`;oks%)E1H34%QFmjeB+j>o425aesi3 zOJM#a#yve3`^LxZ6WL*~Hn6BZN!J==cY^7-Uo?{J#JE=>r*Zp4*|lH|U{Tqn$lieL z6=2N#P=}DMeT#4Z9Br<;m_hN_k4etNV zOf!2j8rTHY#Nlt}1MD;1Z`qrFB%Jvb)FP;dxSuvRt3gi$EOCR(;alnEDS&f)a^$1; zd{Nnsh43m#ARaMNNNqtO)Iwnr6U`B8f@-2)=54~Quk;vLr%{9^A?*4y{*kG!6VU;M*=z3COd zOE^Bp)ZaMowGcVH2Ous>@9!?Wl=Zy1{gD#V-#Q7IQQzqKJ~OSje8c`PdnrB`(ah-aPyRJ*yjYN zgCwnPm#tso@5>Y0rYx{puwCGhN9w{fu;pO)(4X*>SD#+pFDv@7->~nvjQbTU`Xhgk zcpVUpmoBSu`FgDB#*1&*+rjaQ%`tAo-jvf5+ zyVD4b3D&K@Z9oxK^Ya;Ros+100az87S9d$Q>%k5b-L1oQbFZZP4f}Y0eil3rq06=C z-hiI!3*)+rz&43JU{~`ldFQ=rMbGX1z;D>+?$3M}a=dY9xSacwz`l?;>>7>3=w))x zt)$;_^weGvkAuW(6<7_JSGRSAe81F9zxVhuN!*X>a`V5W--H9G4>u<2&H<|h^Xjfc z_j0iNS?IE^AFexou0{8I4wY}%Pf7{&Nt`rt{M-g)TCJ|kOaAXN#aHEA8B*0x^*i6N zKM$EvKlcN^VP8quWl#4<{w@8{!#rVqKJq-iVP7o{a$cJLhP`wH%MYafeZ4OB4SOK% zZx({*w)ZEjJ2Zez14|Vq9?9=!upA#>E7(*YUprXXhjoEv`>pAg4Kgj^!1a&|7BoxU<(Ct(%L!qn#-*}vUbFP zK04m2gLebG-gqwq>jX>D5*BVo7;FL9&pcQ;Sc4C%23z67>cQ6fu;pMI z!J@VlB6k(oAlRVjpq>Zsi?zP7^b53H^9 z4JDro4krDd>vzu4>3ojekKo?2O-AFDC2gzVO<&*dd=Ho3g4Kd$`LHIisbE5vNA#=! z3xk~?0P%Le(aU_uVvE6;lqhzD6{Y1u?u54t-V20R+k_w1=eRlJ=a#+MX{SbCwH?;4 zSRWuvv|MKqGz+W)OzMN9Gk&i^9<)wq&&Fu1bvkFoISAmI!v;QyLj}AG|Cv~p7lKv$ zum-RNV9|KX-)68XAJz(12^L+S6ux#>o)_x^tA)>d=Asv@#)qlG;r2}c6Z^cj@U1anO!RP zx%uu@kh>9>T#C(`NM}*PrX0flgAu|qw$kU7B&>HjS2|o$SBeN*_(E`9seo1l?K#&b z&%FvQkqOYUhbDlqXufoxKzpu;}=fbxU zzWMN-DDo2gQ?;X-)8xhOy$TXW;!<`!?LA?3=G|JnS?q}2t1xW_{eNie$fC0n^S@Y{ zaIZqMlT7s8ql@vdOIcY!_^KECW9R<`s{>mIwjb~E2(}EY1&rwGN3fM(&7QDg>l(0? zV7gDH{i1y7&5_XsZB0}zXZR)Edcih<{gQVlJ(wrZ_bSBGO>|8tqP={HKBe$+cYt6y zVAH^&`=do(0a!lR&xKE?@7!VXiXm0PR|;RZ$&dKG3VO~yKF)W?O%>*&fdgUJpSB{a zr?WpYQ*@s0*wzjfekGo+k~Upn)4;;Qr_&~Wufi5+xgLIrOUg{vYryhi{MygV_1~+I z3tt(0(X^4UMPPM5AnXG8mcys>+Pzny4y+wa=LvTx===xk2RlS;`Mq;yekItnSNoki zLUi3`j={ZGK@OOb{Q7Q$e1dF%w*uY;65hw_j&*W_?qCqznX}kWMc5Jvd!}R0RIpAk z*~hESbFh4{?AKhZ#=%O!s(e@lSQ}VWp4hh#tRL*JA}?;AyDt!5!)z3LS%%%0Abo*W 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 literal 0 HcmV?d00001 diff --git a/armorcore/tools/bin/macos/amake b/armorcore/tools/bin/macos/amake new file mode 100755 index 0000000000000000000000000000000000000000..76034fa1b0ce96777953228aad43c7aebe512a43 GIT binary patch literal 1072944 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%0Doq0HCnlR7{*irHPl8pUO-nY@WH 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%0DPa|ZwtWp3%)D> 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 I_`eYN2dR%wB>(^b literal 0 HcmV?d00001 diff --git a/armorcore/tools/bin/windows_x64/amake.exe b/armorcore/tools/bin/windows_x64/amake.exe new file mode 100644 index 0000000000000000000000000000000000000000..601fcb2b3cfe9c141d0a8453db11dc36dbb7b4da GIT binary patch literal 1425920 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�KC_A)cOZ2Qm$vvz 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

Dof-7f&4W(_b5AMFRO1a^9`xQ?@Gqj=;Oq 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%0DhtYTaX?@f%vyV%`Rk^E$X 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 ^iBRFWCA(Kj#k>7{G&Aw*DCjJN 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 z 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

  • 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�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�asuQ>YyQvt<|7;kQy)6SD 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

    hSp;{*+@>>hbGI_o+s@S1} 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 literal 0 HcmV?d00001 diff --git a/armorcore/tools/tcc/main.c b/armorcore/tools/tcc/main.c new file mode 100755 index 000000000..b2fb6d734 --- /dev/null +++ b/armorcore/tools/tcc/main.c @@ -0,0 +1,6 @@ +#include + +int main() { + printf("Hello World\n"); + return 0; +} diff --git a/armorcore/tools/tcc/make.sh b/armorcore/tools/tcc/make.sh new file mode 100755 index 000000000..ce8a0b154 --- /dev/null +++ b/armorcore/tools/tcc/make.sh @@ -0,0 +1 @@ +./tcc -o myprog main.c -I'./' -I'./include' -B'./' \ No newline at end of file diff --git a/armorcore/tools/tcc/tcc b/armorcore/tools/tcc/tcc new file mode 100755 index 0000000000000000000000000000000000000000..6b706d476d3620d322cba7b128ecc703e7fc8716 GIT binary patch literal 256888 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%0D6AXEg_Dshnwu-R|Su7X>K= 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

    c^tSbIQtQREdQv7m*3Ij4C@i{*G-vQ?j@1 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 SYWc{e~4n1izx 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 literal 0 HcmV?d00001 diff --git a/armorcore/tools/tcc/tcclib.h b/armorcore/tools/tcc/tcclib.h new file mode 100644 index 000000000..8d59e4c9a --- /dev/null +++ b/armorcore/tools/tcc/tcclib.h @@ -0,0 +1,80 @@ +/* Simple libc header for TCC + * + * Add any function you want from the libc there. This file is here + * only for your convenience so that you do not need to put the whole + * glibc include files on your floppy disk + */ +#ifndef _TCCLIB_H +#define _TCCLIB_H + +#include +#include + +/* stdlib.h */ +void *calloc(size_t nmemb, size_t size); +void *malloc(size_t size); +void free(void *ptr); +void *realloc(void *ptr, size_t size); +int atoi(const char *nptr); +long int strtol(const char *nptr, char **endptr, int base); +unsigned long int strtoul(const char *nptr, char **endptr, int base); +void exit(int); + +/* stdio.h */ +typedef struct __FILE FILE; +#define EOF (-1) +extern FILE *stdin; +extern FILE *stdout; +extern FILE *stderr; +FILE *fopen(const char *path, const char *mode); +FILE *fdopen(int fildes, const char *mode); +FILE *freopen(const char *path, const char *mode, FILE *stream); +int fclose(FILE *stream); +size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); +size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream); +int fgetc(FILE *stream); +char *fgets(char *s, int size, FILE *stream); +int getc(FILE *stream); +int getchar(void); +char *gets(char *s); +int ungetc(int c, FILE *stream); +int fflush(FILE *stream); +int putchar (int c); + +int printf(const char *format, ...); +int fprintf(FILE *stream, const char *format, ...); +int sprintf(char *str, const char *format, ...); +int snprintf(char *str, size_t size, const char *format, ...); +int asprintf(char **strp, const char *format, ...); +int dprintf(int fd, const char *format, ...); +int vprintf(const char *format, va_list ap); +int vfprintf(FILE *stream, const char *format, va_list ap); +int vsprintf(char *str, const char *format, va_list ap); +int vsnprintf(char *str, size_t size, const char *format, va_list ap); +int vasprintf(char **strp, const char *format, va_list ap); +int vdprintf(int fd, const char *format, va_list ap); + +void perror(const char *s); + +/* string.h */ +char *strcat(char *dest, const char *src); +char *strchr(const char *s, int c); +char *strrchr(const char *s, int c); +char *strcpy(char *dest, const char *src); +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); +char *strdup(const char *s); +size_t strlen(const char *s); + +/* dlfcn.h */ +#define RTLD_LAZY 0x001 +#define RTLD_NOW 0x002 +#define RTLD_GLOBAL 0x100 + +void *dlopen(const char *filename, int flag); +const char *dlerror(void); +void *dlsym(void *handle, char *symbol); +int dlclose(void *handle); + +#endif /* _TCCLIB_H */ diff --git a/armorcore/tools/tests/cube/assets/cube.arm b/armorcore/tools/tests/cube/assets/cube.arm new file mode 100644 index 0000000000000000000000000000000000000000..5d9356e42498d54e3cf050e04dd44d0c47709379 GIT binary patch literal 1405 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 +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +-------------------------------------------------------------------------------- + +The preprocessor has the core license stated above, plus an additional licence: + +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ diff --git a/armorcore/tools/to_spirv/glslang/OGLCompilersDLL/InitializeDll.cpp b/armorcore/tools/to_spirv/glslang/OGLCompilersDLL/InitializeDll.cpp new file mode 100644 index 000000000..abea9108b --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/OGLCompilersDLL/InitializeDll.cpp @@ -0,0 +1,165 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#define SH_EXPORTING + +#include + +#include "InitializeDll.h" +#include "../glslang/Include/InitializeGlobals.h" +#include "../glslang/Public/ShaderLang.h" +#include "../glslang/Include/PoolAlloc.h" + +namespace glslang { + +OS_TLSIndex ThreadInitializeIndex = OS_INVALID_TLS_INDEX; + +// Per-process initialization. +// Needs to be called at least once before parsing, etc. is done. +// Will also do thread initialization for the calling thread; other +// threads will need to do that explicitly. +bool InitProcess() +{ + glslang::GetGlobalLock(); + + if (ThreadInitializeIndex != OS_INVALID_TLS_INDEX) { + // + // Function is re-entrant. + // + + glslang::ReleaseGlobalLock(); + return true; + } + + ThreadInitializeIndex = OS_AllocTLSIndex(); + + if (ThreadInitializeIndex == OS_INVALID_TLS_INDEX) { + assert(0 && "InitProcess(): Failed to allocate TLS area for init flag"); + + glslang::ReleaseGlobalLock(); + return false; + } + + if (! InitializePoolIndex()) { + assert(0 && "InitProcess(): Failed to initialize global pool"); + + glslang::ReleaseGlobalLock(); + return false; + } + + if (! InitThread()) { + assert(0 && "InitProcess(): Failed to initialize thread"); + + glslang::ReleaseGlobalLock(); + return false; + } + + glslang::ReleaseGlobalLock(); + return true; +} + +// Per-thread scoped initialization. +// Must be called at least once by each new thread sharing the +// symbol tables, etc., needed to parse. +bool InitThread() +{ + // + // This function is re-entrant + // + if (ThreadInitializeIndex == OS_INVALID_TLS_INDEX) { + assert(0 && "InitThread(): Process hasn't been initalised."); + return false; + } + + if (OS_GetTLSValue(ThreadInitializeIndex) != 0) + return true; + + if (! OS_SetTLSValue(ThreadInitializeIndex, (void *)1)) { + assert(0 && "InitThread(): Unable to set init flag."); + return false; + } + + glslang::SetThreadPoolAllocator(nullptr); + + return true; +} + +// Not necessary to call this: InitThread() is reentrant, and the need +// to do per thread tear down has been removed. +// +// This is kept, with memory management removed, to satisfy any exiting +// calls to it that rely on it. +bool DetachThread() +{ + bool success = true; + + if (ThreadInitializeIndex == OS_INVALID_TLS_INDEX) + return true; + + // + // Function is re-entrant and this thread may not have been initialized. + // + if (OS_GetTLSValue(ThreadInitializeIndex) != 0) { + if (!OS_SetTLSValue(ThreadInitializeIndex, (void *)0)) { + assert(0 && "DetachThread(): Unable to clear init flag."); + success = false; + } + } + + return success; +} + +// Not necessary to call this: InitProcess() is reentrant. +// +// This is kept, with memory management removed, to satisfy any exiting +// calls to it that rely on it. +// +// Users of glslang should call shFinalize() or glslang::FinalizeProcess() for +// process-scoped memory tear down. +bool DetachProcess() +{ + bool success = true; + + if (ThreadInitializeIndex == OS_INVALID_TLS_INDEX) + return true; + + success = DetachThread(); + + OS_FreeTLSIndex(ThreadInitializeIndex); + ThreadInitializeIndex = OS_INVALID_TLS_INDEX; + + return success; +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/OGLCompilersDLL/InitializeDll.h b/armorcore/tools/to_spirv/glslang/OGLCompilersDLL/InitializeDll.h new file mode 100644 index 000000000..661cee4d2 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/OGLCompilersDLL/InitializeDll.h @@ -0,0 +1,49 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef __INITIALIZEDLL_H +#define __INITIALIZEDLL_H + +#include "../glslang/OSDependent/osinclude.h" + +namespace glslang { + +bool InitProcess(); +bool InitThread(); +bool DetachThread(); // not called from standalone, perhaps other tools rely on parts of it +bool DetachProcess(); // not called from standalone, perhaps other tools rely on parts of it + +} // end namespace glslang + +#endif // __INITIALIZEDLL_H + diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.AMD.h b/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.AMD.h new file mode 100644 index 000000000..009d2f1cf --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.AMD.h @@ -0,0 +1,108 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextAMD_H +#define GLSLextAMD_H + +static const int GLSLextAMDVersion = 100; +static const int GLSLextAMDRevision = 7; + +// SPV_AMD_shader_ballot +static const char* const E_SPV_AMD_shader_ballot = "SPV_AMD_shader_ballot"; + +enum ShaderBallotAMD { + ShaderBallotBadAMD = 0, // Don't use + + SwizzleInvocationsAMD = 1, + SwizzleInvocationsMaskedAMD = 2, + WriteInvocationAMD = 3, + MbcntAMD = 4, + + ShaderBallotCountAMD +}; + +// SPV_AMD_shader_trinary_minmax +static const char* const E_SPV_AMD_shader_trinary_minmax = "SPV_AMD_shader_trinary_minmax"; + +enum ShaderTrinaryMinMaxAMD { + ShaderTrinaryMinMaxBadAMD = 0, // Don't use + + FMin3AMD = 1, + UMin3AMD = 2, + SMin3AMD = 3, + FMax3AMD = 4, + UMax3AMD = 5, + SMax3AMD = 6, + FMid3AMD = 7, + UMid3AMD = 8, + SMid3AMD = 9, + + ShaderTrinaryMinMaxCountAMD +}; + +// SPV_AMD_shader_explicit_vertex_parameter +static const char* const E_SPV_AMD_shader_explicit_vertex_parameter = "SPV_AMD_shader_explicit_vertex_parameter"; + +enum ShaderExplicitVertexParameterAMD { + ShaderExplicitVertexParameterBadAMD = 0, // Don't use + + InterpolateAtVertexAMD = 1, + + ShaderExplicitVertexParameterCountAMD +}; + +// SPV_AMD_gcn_shader +static const char* const E_SPV_AMD_gcn_shader = "SPV_AMD_gcn_shader"; + +enum GcnShaderAMD { + GcnShaderBadAMD = 0, // Don't use + + CubeFaceIndexAMD = 1, + CubeFaceCoordAMD = 2, + TimeAMD = 3, + + GcnShaderCountAMD +}; + +// SPV_AMD_gpu_shader_half_float +static const char* const E_SPV_AMD_gpu_shader_half_float = "SPV_AMD_gpu_shader_half_float"; + +// SPV_AMD_texture_gather_bias_lod +static const char* const E_SPV_AMD_texture_gather_bias_lod = "SPV_AMD_texture_gather_bias_lod"; + +// SPV_AMD_gpu_shader_int16 +static const char* const E_SPV_AMD_gpu_shader_int16 = "SPV_AMD_gpu_shader_int16"; + +// SPV_AMD_shader_image_load_store_lod +static const char* const E_SPV_AMD_shader_image_load_store_lod = "SPV_AMD_shader_image_load_store_lod"; + +// SPV_AMD_shader_fragment_mask +static const char* const E_SPV_AMD_shader_fragment_mask = "SPV_AMD_shader_fragment_mask"; + +// SPV_AMD_gpu_shader_half_float_fetch +static const char* const E_SPV_AMD_gpu_shader_half_float_fetch = "SPV_AMD_gpu_shader_half_float_fetch"; + +#endif // #ifndef GLSLextAMD_H diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.EXT.h b/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.EXT.h new file mode 100644 index 000000000..40164b618 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.EXT.h @@ -0,0 +1,39 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextEXT_H +#define GLSLextEXT_H + +static const int GLSLextEXTVersion = 100; +static const int GLSLextEXTRevision = 2; + +static const char* const E_SPV_EXT_shader_stencil_export = "SPV_EXT_shader_stencil_export"; +static const char* const E_SPV_EXT_shader_viewport_index_layer = "SPV_EXT_shader_viewport_index_layer"; +static const char* const E_SPV_EXT_fragment_fully_covered = "SPV_EXT_fragment_fully_covered"; +static const char* const E_SPV_EXT_fragment_invocation_density = "SPV_EXT_fragment_invocation_density"; +static const char* const E_SPV_EXT_demote_to_helper_invocation = "SPV_EXT_demote_to_helper_invocation"; + +#endif // #ifndef GLSLextEXT_H diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.KHR.h b/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.KHR.h new file mode 100644 index 000000000..d783a8f29 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.KHR.h @@ -0,0 +1,51 @@ +/* +** Copyright (c) 2014-2020 The Khronos Group Inc. +** Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextKHR_H +#define GLSLextKHR_H + +static const int GLSLextKHRVersion = 100; +static const int GLSLextKHRRevision = 2; + +static const char* const E_SPV_KHR_shader_ballot = "SPV_KHR_shader_ballot"; +static const char* const E_SPV_KHR_subgroup_vote = "SPV_KHR_subgroup_vote"; +static const char* const E_SPV_KHR_device_group = "SPV_KHR_device_group"; +static const char* const E_SPV_KHR_multiview = "SPV_KHR_multiview"; +static const char* const E_SPV_KHR_shader_draw_parameters = "SPV_KHR_shader_draw_parameters"; +static const char* const E_SPV_KHR_16bit_storage = "SPV_KHR_16bit_storage"; +static const char* const E_SPV_KHR_8bit_storage = "SPV_KHR_8bit_storage"; +static const char* const E_SPV_KHR_storage_buffer_storage_class = "SPV_KHR_storage_buffer_storage_class"; +static const char* const E_SPV_KHR_post_depth_coverage = "SPV_KHR_post_depth_coverage"; +static const char* const E_SPV_KHR_vulkan_memory_model = "SPV_KHR_vulkan_memory_model"; +static const char* const E_SPV_EXT_physical_storage_buffer = "SPV_EXT_physical_storage_buffer"; +static const char* const E_SPV_KHR_physical_storage_buffer = "SPV_KHR_physical_storage_buffer"; +static const char* const E_SPV_EXT_fragment_shader_interlock = "SPV_EXT_fragment_shader_interlock"; +static const char* const E_SPV_KHR_shader_clock = "SPV_KHR_shader_clock"; +static const char* const E_SPV_KHR_non_semantic_info = "SPV_KHR_non_semantic_info"; +static const char* const E_SPV_KHR_ray_tracing = "SPV_KHR_ray_tracing"; +static const char* const E_SPV_KHR_ray_query = "SPV_KHR_ray_query"; +#endif // #ifndef GLSLextKHR_H diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.NV.h b/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.NV.h new file mode 100644 index 000000000..50146da10 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.ext.NV.h @@ -0,0 +1,81 @@ +/* +** Copyright (c) 2014-2017 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextNV_H +#define GLSLextNV_H + +enum BuiltIn; +enum Decoration; +enum Op; +enum Capability; + +static const int GLSLextNVVersion = 100; +static const int GLSLextNVRevision = 11; + +//SPV_NV_sample_mask_override_coverage +const char* const E_SPV_NV_sample_mask_override_coverage = "SPV_NV_sample_mask_override_coverage"; + +//SPV_NV_geometry_shader_passthrough +const char* const E_SPV_NV_geometry_shader_passthrough = "SPV_NV_geometry_shader_passthrough"; + +//SPV_NV_viewport_array2 +const char* const E_SPV_NV_viewport_array2 = "SPV_NV_viewport_array2"; +const char* const E_ARB_shader_viewport_layer_array = "SPV_ARB_shader_viewport_layer_array"; + +//SPV_NV_stereo_view_rendering +const char* const E_SPV_NV_stereo_view_rendering = "SPV_NV_stereo_view_rendering"; + +//SPV_NVX_multiview_per_view_attributes +const char* const E_SPV_NVX_multiview_per_view_attributes = "SPV_NVX_multiview_per_view_attributes"; + +//SPV_NV_shader_subgroup_partitioned +const char* const E_SPV_NV_shader_subgroup_partitioned = "SPV_NV_shader_subgroup_partitioned"; + +//SPV_NV_fragment_shader_barycentric +const char* const E_SPV_NV_fragment_shader_barycentric = "SPV_NV_fragment_shader_barycentric"; + +//SPV_NV_compute_shader_derivatives +const char* const E_SPV_NV_compute_shader_derivatives = "SPV_NV_compute_shader_derivatives"; + +//SPV_NV_shader_image_footprint +const char* const E_SPV_NV_shader_image_footprint = "SPV_NV_shader_image_footprint"; + +//SPV_NV_mesh_shader +const char* const E_SPV_NV_mesh_shader = "SPV_NV_mesh_shader"; + +//SPV_NV_raytracing +const char* const E_SPV_NV_ray_tracing = "SPV_NV_ray_tracing"; + +//SPV_NV_shading_rate +const char* const E_SPV_NV_shading_rate = "SPV_NV_shading_rate"; + +//SPV_NV_cooperative_matrix +const char* const E_SPV_NV_cooperative_matrix = "SPV_NV_cooperative_matrix"; + +//SPV_NV_shader_sm_builtins +const char* const E_SPV_NV_shader_sm_builtins = "SPV_NV_shader_sm_builtins"; + +#endif // #ifndef GLSLextNV_H diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.std.450.h b/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.std.450.h new file mode 100644 index 000000000..df31092be --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/GLSL.std.450.h @@ -0,0 +1,131 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLstd450_H +#define GLSLstd450_H + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 1; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +#endif // #ifndef GLSLstd450_H diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/GlslangToSpv.cpp b/armorcore/tools/to_spirv/glslang/SPIRV/GlslangToSpv.cpp new file mode 100644 index 000000000..bab63a853 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/GlslangToSpv.cpp @@ -0,0 +1,8727 @@ +// +// Copyright (C) 2014-2016 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Visit the nodes in the glslang intermediate tree representation to +// translate them to SPIR-V. +// + +#include "spirv.hpp" +#include "GlslangToSpv.h" +#include "SpvBuilder.h" +namespace spv { + #include "GLSL.std.450.h" + #include "GLSL.ext.KHR.h" + #include "GLSL.ext.EXT.h" + #include "GLSL.ext.AMD.h" + #include "GLSL.ext.NV.h" + #include "NonSemanticDebugPrintf.h" +} + +// Glslang includes +#include "../glslang/MachineIndependent/localintermediate.h" +#include "../glslang/MachineIndependent/SymbolTable.h" +#include "../glslang/Include/Common.h" +#include "../glslang/Include/revision.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +namespace { +class SpecConstantOpModeGuard { +public: + SpecConstantOpModeGuard(spv::Builder* builder) + : builder_(builder) { + previous_flag_ = builder->isInSpecConstCodeGenMode(); + } + ~SpecConstantOpModeGuard() { + previous_flag_ ? builder_->setToSpecConstCodeGenMode() + : builder_->setToNormalCodeGenMode(); + } + void turnOnSpecConstantOpMode() { + builder_->setToSpecConstCodeGenMode(); + } + +private: + spv::Builder* builder_; + bool previous_flag_; +}; + +struct OpDecorations { + public: + OpDecorations(spv::Decoration precision, spv::Decoration noContraction, spv::Decoration nonUniform) : + precision(precision) +#ifndef GLSLANG_WEB + , + noContraction(noContraction), + nonUniform(nonUniform) +#endif + { } + + spv::Decoration precision; + +#ifdef GLSLANG_WEB + void addNoContraction(spv::Builder&, spv::Id) const { } + void addNonUniform(spv::Builder&, spv::Id) const { } +#else + void addNoContraction(spv::Builder& builder, spv::Id t) { builder.addDecoration(t, noContraction); } + void addNonUniform(spv::Builder& builder, spv::Id t) { builder.addDecoration(t, nonUniform); } + protected: + spv::Decoration noContraction; + spv::Decoration nonUniform; +#endif + +}; + +} // namespace + +// +// The main holder of information for translating glslang to SPIR-V. +// +// Derives from the AST walking base class. +// +class TGlslangToSpvTraverser : public glslang::TIntermTraverser { +public: + TGlslangToSpvTraverser(unsigned int spvVersion, const glslang::TIntermediate*, spv::SpvBuildLogger* logger, + glslang::SpvOptions& options); + virtual ~TGlslangToSpvTraverser() { } + + bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*); + bool visitBinary(glslang::TVisit, glslang::TIntermBinary*); + void visitConstantUnion(glslang::TIntermConstantUnion*); + bool visitSelection(glslang::TVisit, glslang::TIntermSelection*); + bool visitSwitch(glslang::TVisit, glslang::TIntermSwitch*); + void visitSymbol(glslang::TIntermSymbol* symbol); + bool visitUnary(glslang::TVisit, glslang::TIntermUnary*); + bool visitLoop(glslang::TVisit, glslang::TIntermLoop*); + bool visitBranch(glslang::TVisit visit, glslang::TIntermBranch*); + + void finishSpv(); + void dumpSpv(std::vector& out); + +protected: + TGlslangToSpvTraverser(TGlslangToSpvTraverser&); + TGlslangToSpvTraverser& operator=(TGlslangToSpvTraverser&); + + spv::Decoration TranslateInterpolationDecoration(const glslang::TQualifier& qualifier); + spv::Decoration TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier); + spv::Decoration TranslateNonUniformDecoration(const glslang::TQualifier& qualifier); + spv::Builder::AccessChain::CoherentFlags TranslateCoherent(const glslang::TType& type); + spv::MemoryAccessMask TranslateMemoryAccess(const spv::Builder::AccessChain::CoherentFlags &coherentFlags); + spv::ImageOperandsMask TranslateImageOperands(const spv::Builder::AccessChain::CoherentFlags &coherentFlags); + spv::Scope TranslateMemoryScope(const spv::Builder::AccessChain::CoherentFlags &coherentFlags); + spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable, bool memberDeclaration); + spv::ImageFormat TranslateImageFormat(const glslang::TType& type); + spv::SelectionControlMask TranslateSelectionControl(const glslang::TIntermSelection&) const; + spv::SelectionControlMask TranslateSwitchControl(const glslang::TIntermSwitch&) const; + spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&, std::vector& operands) const; + spv::StorageClass TranslateStorageClass(const glslang::TType&); + void addIndirectionIndexCapabilities(const glslang::TType& baseType, const glslang::TType& indexType); + spv::Id createSpvVariable(const glslang::TIntermSymbol*, spv::Id forcedType); + spv::Id getSampledType(const glslang::TSampler&); + spv::Id getInvertedSwizzleType(const glslang::TIntermTyped&); + spv::Id createInvertedSwizzle(spv::Decoration precision, const glslang::TIntermTyped&, spv::Id parentResult); + void convertSwizzle(const glslang::TIntermAggregate&, std::vector& swizzle); + spv::Id convertGlslangToSpvType(const glslang::TType& type, bool forwardReferenceOnly = false); + spv::Id convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking, const glslang::TQualifier&, + bool lastBufferBlockMember, bool forwardReferenceOnly = false); + bool filterMember(const glslang::TType& member); + spv::Id convertGlslangStructToSpvType(const glslang::TType&, const glslang::TTypeList* glslangStruct, + glslang::TLayoutPacking, const glslang::TQualifier&); + void decorateStructType(const glslang::TType&, const glslang::TTypeList* glslangStruct, glslang::TLayoutPacking, + const glslang::TQualifier&, spv::Id); + spv::Id makeArraySizeId(const glslang::TArraySizes&, int dim); + spv::Id accessChainLoad(const glslang::TType& type); + void accessChainStore(const glslang::TType& type, spv::Id rvalue); + void multiTypeStore(const glslang::TType&, spv::Id rValue); + glslang::TLayoutPacking getExplicitLayout(const glslang::TType& type) const; + int getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking, glslang::TLayoutMatrix); + int getMatrixStride(const glslang::TType& matrixType, glslang::TLayoutPacking, glslang::TLayoutMatrix); + void updateMemberOffset(const glslang::TType& structType, const glslang::TType& memberType, int& currentOffset, + int& nextOffset, glslang::TLayoutPacking, glslang::TLayoutMatrix); + void declareUseOfStructMember(const glslang::TTypeList& members, int glslangMember); + + bool isShaderEntryPoint(const glslang::TIntermAggregate* node); + bool writableParam(glslang::TStorageQualifier) const; + bool originalParam(glslang::TStorageQualifier, const glslang::TType&, bool implicitThisParam); + void makeFunctions(const glslang::TIntermSequence&); + void makeGlobalInitializers(const glslang::TIntermSequence&); + void visitFunctions(const glslang::TIntermSequence&); + void handleFunctionEntry(const glslang::TIntermAggregate* node); + void translateArguments(const glslang::TIntermAggregate& node, std::vector& arguments, + spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags); + void translateArguments(glslang::TIntermUnary& node, std::vector& arguments); + spv::Id createImageTextureFunctionCall(glslang::TIntermOperator* node); + spv::Id handleUserFunctionCall(const glslang::TIntermAggregate*); + + spv::Id createBinaryOperation(glslang::TOperator op, OpDecorations&, spv::Id typeId, spv::Id left, spv::Id right, + glslang::TBasicType typeProxy, bool reduceComparison = true); + spv::Id createBinaryMatrixOperation(spv::Op, OpDecorations&, spv::Id typeId, spv::Id left, spv::Id right); + spv::Id createUnaryOperation(glslang::TOperator op, OpDecorations&, spv::Id typeId, spv::Id operand, + glslang::TBasicType typeProxy, + const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags); + spv::Id createUnaryMatrixOperation(spv::Op op, OpDecorations&, spv::Id typeId, spv::Id operand, + glslang::TBasicType typeProxy); + spv::Id createConversion(glslang::TOperator op, OpDecorations&, spv::Id destTypeId, spv::Id operand, + glslang::TBasicType typeProxy); + spv::Id createIntWidthConversion(glslang::TOperator op, spv::Id operand, int vectorSize); + spv::Id makeSmearedConstant(spv::Id constant, int vectorSize); + spv::Id createAtomicOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, + std::vector& operands, glslang::TBasicType typeProxy, + const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags); + spv::Id createInvocationsOperation(glslang::TOperator op, spv::Id typeId, std::vector& operands, + glslang::TBasicType typeProxy); + spv::Id CreateInvocationsVectorOperation(spv::Op op, spv::GroupOperation groupOperation, + spv::Id typeId, std::vector& operands); + spv::Id createSubgroupOperation(glslang::TOperator op, spv::Id typeId, std::vector& operands, + glslang::TBasicType typeProxy); + spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, + std::vector& operands, glslang::TBasicType typeProxy); + spv::Id createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId); + spv::Id getSymbolId(const glslang::TIntermSymbol* node); + void addMeshNVDecoration(spv::Id id, int member, const glslang::TQualifier & qualifier); + spv::Id createSpvConstant(const glslang::TIntermTyped&); + spv::Id createSpvConstantFromConstUnionArray(const glslang::TType& type, const glslang::TConstUnionArray&, + int& nextConst, bool specConstant); + bool isTrivialLeaf(const glslang::TIntermTyped* node); + bool isTrivial(const glslang::TIntermTyped* node); + spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right); + spv::Id getExtBuiltins(const char* name); + std::pair getForcedType(glslang::TBuiltInVariable builtIn, const glslang::TType&); + spv::Id translateForcedType(spv::Id object); + spv::Id createCompositeConstruct(spv::Id typeId, std::vector constituents); + + glslang::SpvOptions& options; + spv::Function* shaderEntry; + spv::Function* currentFunction; + spv::Instruction* entryPoint; + int sequenceDepth; + + spv::SpvBuildLogger* logger; + + // There is a 1:1 mapping between a spv builder and a module; this is thread safe + spv::Builder builder; + bool inEntryPoint; + bool entryPointTerminated; + bool linkageOnly; // true when visiting the set of objects in the AST present only for + // establishing interface, whether or not they were statically used + std::set iOSet; // all input/output variables from either static use or declaration of interface + const glslang::TIntermediate* glslangIntermediate; + bool nanMinMaxClamp; // true if use NMin/NMax/NClamp instead of FMin/FMax/FClamp + spv::Id stdBuiltins; + spv::Id nonSemanticDebugPrintf; + std::unordered_map extBuiltinMap; + + std::unordered_map symbolValues; + std::unordered_set rValueParameters; // set of formal function parameters passed as rValues, + // rather than a pointer + std::unordered_map functionMap; + std::unordered_map structMap[glslang::ElpCount][glslang::ElmCount]; + // for mapping glslang block indices to spv indices (e.g., due to hidden members): + std::unordered_map> memberRemapper; + // for mapping glslang symbol struct to symbol Id + std::unordered_map glslangTypeToIdMap; + std::stack breakForLoop; // false means break for switch + std::unordered_map counterOriginator; + // Map pointee types for EbtReference to their forward pointers + std::map forwardPointers; + // Type forcing, for when SPIR-V wants a different type than the AST, + // requiring local translation to and from SPIR-V type on every access. + // Maps AST-required-type-id> + std::unordered_map forceType; +}; + +// +// Helper functions for translating glslang representations to SPIR-V enumerants. +// + +// Translate glslang profile to SPIR-V source language. +spv::SourceLanguage TranslateSourceLanguage(glslang::EShSource source, EProfile profile) +{ +#ifdef GLSLANG_WEB + return spv::SourceLanguageESSL; +#endif + + switch (source) { + case glslang::EShSourceGlsl: + switch (profile) { + case ENoProfile: + case ECoreProfile: + case ECompatibilityProfile: + return spv::SourceLanguageGLSL; + case EEsProfile: + return spv::SourceLanguageESSL; + default: + return spv::SourceLanguageUnknown; + } + case glslang::EShSourceHlsl: + return spv::SourceLanguageHLSL; + default: + return spv::SourceLanguageUnknown; + } +} + +// Translate glslang language (stage) to SPIR-V execution model. +spv::ExecutionModel TranslateExecutionModel(EShLanguage stage) +{ + switch (stage) { + case EShLangVertex: return spv::ExecutionModelVertex; + case EShLangFragment: return spv::ExecutionModelFragment; + case EShLangCompute: return spv::ExecutionModelGLCompute; +#ifndef GLSLANG_WEB + case EShLangTessControl: return spv::ExecutionModelTessellationControl; + case EShLangTessEvaluation: return spv::ExecutionModelTessellationEvaluation; + case EShLangGeometry: return spv::ExecutionModelGeometry; + case EShLangRayGen: return spv::ExecutionModelRayGenerationKHR; + case EShLangIntersect: return spv::ExecutionModelIntersectionKHR; + case EShLangAnyHit: return spv::ExecutionModelAnyHitKHR; + case EShLangClosestHit: return spv::ExecutionModelClosestHitKHR; + case EShLangMiss: return spv::ExecutionModelMissKHR; + case EShLangCallable: return spv::ExecutionModelCallableKHR; + case EShLangTaskNV: return spv::ExecutionModelTaskNV; + case EShLangMeshNV: return spv::ExecutionModelMeshNV; +#endif + default: + assert(0); + return spv::ExecutionModelFragment; + } +} + +// Translate glslang sampler type to SPIR-V dimensionality. +spv::Dim TranslateDimensionality(const glslang::TSampler& sampler) +{ + switch (sampler.dim) { + case glslang::Esd1D: return spv::Dim1D; + case glslang::Esd2D: return spv::Dim2D; + case glslang::Esd3D: return spv::Dim3D; + case glslang::EsdCube: return spv::DimCube; + case glslang::EsdRect: return spv::DimRect; + case glslang::EsdBuffer: return spv::DimBuffer; + case glslang::EsdSubpass: return spv::DimSubpassData; + default: + assert(0); + return spv::Dim2D; + } +} + +// Translate glslang precision to SPIR-V precision decorations. +spv::Decoration TranslatePrecisionDecoration(glslang::TPrecisionQualifier glslangPrecision) +{ + switch (glslangPrecision) { + case glslang::EpqLow: return spv::DecorationRelaxedPrecision; + case glslang::EpqMedium: return spv::DecorationRelaxedPrecision; + default: + return spv::NoPrecision; + } +} + +// Translate glslang type to SPIR-V precision decorations. +spv::Decoration TranslatePrecisionDecoration(const glslang::TType& type) +{ + return TranslatePrecisionDecoration(type.getQualifier().precision); +} + +// Translate glslang type to SPIR-V block decorations. +spv::Decoration TranslateBlockDecoration(const glslang::TType& type, bool useStorageBuffer) +{ + if (type.getBasicType() == glslang::EbtBlock) { + switch (type.getQualifier().storage) { + case glslang::EvqUniform: return spv::DecorationBlock; + case glslang::EvqBuffer: return useStorageBuffer ? spv::DecorationBlock : spv::DecorationBufferBlock; + case glslang::EvqVaryingIn: return spv::DecorationBlock; + case glslang::EvqVaryingOut: return spv::DecorationBlock; +#ifndef GLSLANG_WEB + case glslang::EvqPayload: return spv::DecorationBlock; + case glslang::EvqPayloadIn: return spv::DecorationBlock; + case glslang::EvqHitAttr: return spv::DecorationBlock; + case glslang::EvqCallableData: return spv::DecorationBlock; + case glslang::EvqCallableDataIn: return spv::DecorationBlock; +#endif + default: + assert(0); + break; + } + } + + return spv::DecorationMax; +} + +// Translate glslang type to SPIR-V memory decorations. +void TranslateMemoryDecoration(const glslang::TQualifier& qualifier, std::vector& memory, + bool useVulkanMemoryModel) +{ + if (!useVulkanMemoryModel) { + if (qualifier.isCoherent()) + memory.push_back(spv::DecorationCoherent); + if (qualifier.isVolatile()) { + memory.push_back(spv::DecorationVolatile); + memory.push_back(spv::DecorationCoherent); + } + } + if (qualifier.isRestrict()) + memory.push_back(spv::DecorationRestrict); + if (qualifier.isReadOnly()) + memory.push_back(spv::DecorationNonWritable); + if (qualifier.isWriteOnly()) + memory.push_back(spv::DecorationNonReadable); +} + +// Translate glslang type to SPIR-V layout decorations. +spv::Decoration TranslateLayoutDecoration(const glslang::TType& type, glslang::TLayoutMatrix matrixLayout) +{ + if (type.isMatrix()) { + switch (matrixLayout) { + case glslang::ElmRowMajor: + return spv::DecorationRowMajor; + case glslang::ElmColumnMajor: + return spv::DecorationColMajor; + default: + // opaque layouts don't need a majorness + return spv::DecorationMax; + } + } else { + switch (type.getBasicType()) { + default: + return spv::DecorationMax; + break; + case glslang::EbtBlock: + switch (type.getQualifier().storage) { + case glslang::EvqUniform: + case glslang::EvqBuffer: + switch (type.getQualifier().layoutPacking) { + case glslang::ElpShared: return spv::DecorationGLSLShared; + case glslang::ElpPacked: return spv::DecorationGLSLPacked; + default: + return spv::DecorationMax; + } + case glslang::EvqVaryingIn: + case glslang::EvqVaryingOut: + if (type.getQualifier().isTaskMemory()) { + switch (type.getQualifier().layoutPacking) { + case glslang::ElpShared: return spv::DecorationGLSLShared; + case glslang::ElpPacked: return spv::DecorationGLSLPacked; + default: break; + } + } else { + assert(type.getQualifier().layoutPacking == glslang::ElpNone); + } + return spv::DecorationMax; +#ifndef GLSLANG_WEB + case glslang::EvqPayload: + case glslang::EvqPayloadIn: + case glslang::EvqHitAttr: + case glslang::EvqCallableData: + case glslang::EvqCallableDataIn: + return spv::DecorationMax; +#endif + default: + assert(0); + return spv::DecorationMax; + } + } + } +} + +// Translate glslang type to SPIR-V interpolation decorations. +// Returns spv::DecorationMax when no decoration +// should be applied. +spv::Decoration TGlslangToSpvTraverser::TranslateInterpolationDecoration(const glslang::TQualifier& qualifier) +{ + if (qualifier.smooth) + // Smooth decoration doesn't exist in SPIR-V 1.0 + return spv::DecorationMax; + else if (qualifier.isNonPerspective()) + return spv::DecorationNoPerspective; + else if (qualifier.flat) + return spv::DecorationFlat; + else if (qualifier.isExplicitInterpolation()) { + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::DecorationExplicitInterpAMD; + } + else + return spv::DecorationMax; +} + +// Translate glslang type to SPIR-V auxiliary storage decorations. +// Returns spv::DecorationMax when no decoration +// should be applied. +spv::Decoration TGlslangToSpvTraverser::TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier) +{ + if (qualifier.centroid) + return spv::DecorationCentroid; +#ifndef GLSLANG_WEB + else if (qualifier.patch) + return spv::DecorationPatch; + else if (qualifier.sample) { + builder.addCapability(spv::CapabilitySampleRateShading); + return spv::DecorationSample; + } +#endif + + return spv::DecorationMax; +} + +// If glslang type is invariant, return SPIR-V invariant decoration. +spv::Decoration TranslateInvariantDecoration(const glslang::TQualifier& qualifier) +{ + if (qualifier.invariant) + return spv::DecorationInvariant; + else + return spv::DecorationMax; +} + +// If glslang type is noContraction, return SPIR-V NoContraction decoration. +spv::Decoration TranslateNoContractionDecoration(const glslang::TQualifier& qualifier) +{ +#ifndef GLSLANG_WEB + if (qualifier.isNoContraction()) + return spv::DecorationNoContraction; + else +#endif + return spv::DecorationMax; +} + +// If glslang type is nonUniform, return SPIR-V NonUniform decoration. +spv::Decoration TGlslangToSpvTraverser::TranslateNonUniformDecoration(const glslang::TQualifier& qualifier) +{ +#ifndef GLSLANG_WEB + if (qualifier.isNonUniform()) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityShaderNonUniformEXT); + return spv::DecorationNonUniformEXT; + } else +#endif + return spv::DecorationMax; +} + +spv::MemoryAccessMask TGlslangToSpvTraverser::TranslateMemoryAccess( + const spv::Builder::AccessChain::CoherentFlags &coherentFlags) +{ + spv::MemoryAccessMask mask = spv::MemoryAccessMaskNone; + +#ifndef GLSLANG_WEB + if (!glslangIntermediate->usingVulkanMemoryModel() || coherentFlags.isImage) + return mask; + + if (coherentFlags.isVolatile() || coherentFlags.anyCoherent()) { + mask = mask | spv::MemoryAccessMakePointerAvailableKHRMask | + spv::MemoryAccessMakePointerVisibleKHRMask; + } + + if (coherentFlags.nonprivate) { + mask = mask | spv::MemoryAccessNonPrivatePointerKHRMask; + } + if (coherentFlags.volatil) { + mask = mask | spv::MemoryAccessVolatileMask; + } + if (mask != spv::MemoryAccessMaskNone) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } +#endif + + return mask; +} + +spv::ImageOperandsMask TGlslangToSpvTraverser::TranslateImageOperands( + const spv::Builder::AccessChain::CoherentFlags &coherentFlags) +{ + spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone; + +#ifndef GLSLANG_WEB + if (!glslangIntermediate->usingVulkanMemoryModel()) + return mask; + + if (coherentFlags.volatil || + coherentFlags.anyCoherent()) { + mask = mask | spv::ImageOperandsMakeTexelAvailableKHRMask | + spv::ImageOperandsMakeTexelVisibleKHRMask; + } + if (coherentFlags.nonprivate) { + mask = mask | spv::ImageOperandsNonPrivateTexelKHRMask; + } + if (coherentFlags.volatil) { + mask = mask | spv::ImageOperandsVolatileTexelKHRMask; + } + if (mask != spv::ImageOperandsMaskNone) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } +#endif + + return mask; +} + +spv::Builder::AccessChain::CoherentFlags TGlslangToSpvTraverser::TranslateCoherent(const glslang::TType& type) +{ + spv::Builder::AccessChain::CoherentFlags flags = {}; +#ifndef GLSLANG_WEB + flags.coherent = type.getQualifier().coherent; + flags.devicecoherent = type.getQualifier().devicecoherent; + flags.queuefamilycoherent = type.getQualifier().queuefamilycoherent; + // shared variables are implicitly workgroupcoherent in GLSL. + flags.workgroupcoherent = type.getQualifier().workgroupcoherent || + type.getQualifier().storage == glslang::EvqShared; + flags.subgroupcoherent = type.getQualifier().subgroupcoherent; + flags.shadercallcoherent = type.getQualifier().shadercallcoherent; + flags.volatil = type.getQualifier().volatil; + // *coherent variables are implicitly nonprivate in GLSL + flags.nonprivate = type.getQualifier().nonprivate || + flags.anyCoherent() || + flags.volatil; + flags.isImage = type.getBasicType() == glslang::EbtSampler; +#endif + return flags; +} + +spv::Scope TGlslangToSpvTraverser::TranslateMemoryScope( + const spv::Builder::AccessChain::CoherentFlags &coherentFlags) +{ + spv::Scope scope = spv::ScopeMax; + +#ifndef GLSLANG_WEB + if (coherentFlags.volatil || coherentFlags.coherent) { + // coherent defaults to Device scope in the old model, QueueFamilyKHR scope in the new model + scope = glslangIntermediate->usingVulkanMemoryModel() ? spv::ScopeQueueFamilyKHR : spv::ScopeDevice; + } else if (coherentFlags.devicecoherent) { + scope = spv::ScopeDevice; + } else if (coherentFlags.queuefamilycoherent) { + scope = spv::ScopeQueueFamilyKHR; + } else if (coherentFlags.workgroupcoherent) { + scope = spv::ScopeWorkgroup; + } else if (coherentFlags.subgroupcoherent) { + scope = spv::ScopeSubgroup; + } else if (coherentFlags.shadercallcoherent) { + scope = spv::ScopeShaderCallKHR; + } + if (glslangIntermediate->usingVulkanMemoryModel() && scope == spv::ScopeDevice) { + builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR); + } +#endif + + return scope; +} + +// Translate a glslang built-in variable to a SPIR-V built in decoration. Also generate +// associated capabilities when required. For some built-in variables, a capability +// is generated only when using the variable in an executable instruction, but not when +// just declaring a struct member variable with it. This is true for PointSize, +// ClipDistance, and CullDistance. +spv::BuiltIn TGlslangToSpvTraverser::TranslateBuiltInDecoration(glslang::TBuiltInVariable builtIn, + bool memberDeclaration) +{ + switch (builtIn) { + case glslang::EbvPointSize: +#ifndef GLSLANG_WEB + // Defer adding the capability until the built-in is actually used. + if (! memberDeclaration) { + switch (glslangIntermediate->getStage()) { + case EShLangGeometry: + builder.addCapability(spv::CapabilityGeometryPointSize); + break; + case EShLangTessControl: + case EShLangTessEvaluation: + builder.addCapability(spv::CapabilityTessellationPointSize); + break; + default: + break; + } + } +#endif + return spv::BuiltInPointSize; + + case glslang::EbvPosition: return spv::BuiltInPosition; + case glslang::EbvVertexId: return spv::BuiltInVertexId; + case glslang::EbvInstanceId: return spv::BuiltInInstanceId; + case glslang::EbvVertexIndex: return spv::BuiltInVertexIndex; + case glslang::EbvInstanceIndex: return spv::BuiltInInstanceIndex; + + case glslang::EbvFragCoord: return spv::BuiltInFragCoord; + case glslang::EbvPointCoord: return spv::BuiltInPointCoord; + case glslang::EbvFace: return spv::BuiltInFrontFacing; + case glslang::EbvFragDepth: return spv::BuiltInFragDepth; + + case glslang::EbvNumWorkGroups: return spv::BuiltInNumWorkgroups; + case glslang::EbvWorkGroupSize: return spv::BuiltInWorkgroupSize; + case glslang::EbvWorkGroupId: return spv::BuiltInWorkgroupId; + case glslang::EbvLocalInvocationId: return spv::BuiltInLocalInvocationId; + case glslang::EbvLocalInvocationIndex: return spv::BuiltInLocalInvocationIndex; + case glslang::EbvGlobalInvocationId: return spv::BuiltInGlobalInvocationId; + +#ifndef GLSLANG_WEB + // These *Distance capabilities logically belong here, but if the member is declared and + // then never used, consumers of SPIR-V prefer the capability not be declared. + // They are now generated when used, rather than here when declared. + // Potentially, the specification should be more clear what the minimum + // use needed is to trigger the capability. + // + case glslang::EbvClipDistance: + if (!memberDeclaration) + builder.addCapability(spv::CapabilityClipDistance); + return spv::BuiltInClipDistance; + + case glslang::EbvCullDistance: + if (!memberDeclaration) + builder.addCapability(spv::CapabilityCullDistance); + return spv::BuiltInCullDistance; + + case glslang::EbvViewportIndex: + builder.addCapability(spv::CapabilityMultiViewport); + if (glslangIntermediate->getStage() == EShLangVertex || + glslangIntermediate->getStage() == EShLangTessControl || + glslangIntermediate->getStage() == EShLangTessEvaluation) { + + builder.addIncorporatedExtension(spv::E_SPV_EXT_shader_viewport_index_layer, spv::Spv_1_5); + builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT); + } + return spv::BuiltInViewportIndex; + + case glslang::EbvSampleId: + builder.addCapability(spv::CapabilitySampleRateShading); + return spv::BuiltInSampleId; + + case glslang::EbvSamplePosition: + builder.addCapability(spv::CapabilitySampleRateShading); + return spv::BuiltInSamplePosition; + + case glslang::EbvSampleMask: + return spv::BuiltInSampleMask; + + case glslang::EbvLayer: + if (glslangIntermediate->getStage() == EShLangMeshNV) { + return spv::BuiltInLayer; + } + builder.addCapability(spv::CapabilityGeometry); + if (glslangIntermediate->getStage() == EShLangVertex || + glslangIntermediate->getStage() == EShLangTessControl || + glslangIntermediate->getStage() == EShLangTessEvaluation) { + + builder.addIncorporatedExtension(spv::E_SPV_EXT_shader_viewport_index_layer, spv::Spv_1_5); + builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT); + } + return spv::BuiltInLayer; + + case glslang::EbvBaseVertex: + builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3); + builder.addCapability(spv::CapabilityDrawParameters); + return spv::BuiltInBaseVertex; + + case glslang::EbvBaseInstance: + builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3); + builder.addCapability(spv::CapabilityDrawParameters); + return spv::BuiltInBaseInstance; + + case glslang::EbvDrawId: + builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3); + builder.addCapability(spv::CapabilityDrawParameters); + return spv::BuiltInDrawIndex; + + case glslang::EbvPrimitiveId: + if (glslangIntermediate->getStage() == EShLangFragment) + builder.addCapability(spv::CapabilityGeometry); + return spv::BuiltInPrimitiveId; + + case glslang::EbvFragStencilRef: + builder.addExtension(spv::E_SPV_EXT_shader_stencil_export); + builder.addCapability(spv::CapabilityStencilExportEXT); + return spv::BuiltInFragStencilRefEXT; + + case glslang::EbvInvocationId: return spv::BuiltInInvocationId; + case glslang::EbvTessLevelInner: return spv::BuiltInTessLevelInner; + case glslang::EbvTessLevelOuter: return spv::BuiltInTessLevelOuter; + case glslang::EbvTessCoord: return spv::BuiltInTessCoord; + case glslang::EbvPatchVertices: return spv::BuiltInPatchVertices; + case glslang::EbvHelperInvocation: return spv::BuiltInHelperInvocation; + + case glslang::EbvSubGroupSize: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupSize; + + case glslang::EbvSubGroupInvocation: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupLocalInvocationId; + + case glslang::EbvSubGroupEqMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupEqMask; + + case glslang::EbvSubGroupGeMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupGeMask; + + case glslang::EbvSubGroupGtMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupGtMask; + + case glslang::EbvSubGroupLeMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupLeMask; + + case glslang::EbvSubGroupLtMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupLtMask; + + case glslang::EbvNumSubgroups: + builder.addCapability(spv::CapabilityGroupNonUniform); + return spv::BuiltInNumSubgroups; + + case glslang::EbvSubgroupID: + builder.addCapability(spv::CapabilityGroupNonUniform); + return spv::BuiltInSubgroupId; + + case glslang::EbvSubgroupSize2: + builder.addCapability(spv::CapabilityGroupNonUniform); + return spv::BuiltInSubgroupSize; + + case glslang::EbvSubgroupInvocation2: + builder.addCapability(spv::CapabilityGroupNonUniform); + return spv::BuiltInSubgroupLocalInvocationId; + + case glslang::EbvSubgroupEqMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupEqMask; + + case glslang::EbvSubgroupGeMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupGeMask; + + case glslang::EbvSubgroupGtMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupGtMask; + + case glslang::EbvSubgroupLeMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupLeMask; + + case glslang::EbvSubgroupLtMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupLtMask; + + case glslang::EbvBaryCoordNoPersp: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordNoPerspAMD; + + case glslang::EbvBaryCoordNoPerspCentroid: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordNoPerspCentroidAMD; + + case glslang::EbvBaryCoordNoPerspSample: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordNoPerspSampleAMD; + + case glslang::EbvBaryCoordSmooth: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordSmoothAMD; + + case glslang::EbvBaryCoordSmoothCentroid: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordSmoothCentroidAMD; + + case glslang::EbvBaryCoordSmoothSample: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordSmoothSampleAMD; + + case glslang::EbvBaryCoordPullModel: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordPullModelAMD; + + case glslang::EbvDeviceIndex: + builder.addIncorporatedExtension(spv::E_SPV_KHR_device_group, spv::Spv_1_3); + builder.addCapability(spv::CapabilityDeviceGroup); + return spv::BuiltInDeviceIndex; + + case glslang::EbvViewIndex: + builder.addIncorporatedExtension(spv::E_SPV_KHR_multiview, spv::Spv_1_3); + builder.addCapability(spv::CapabilityMultiView); + return spv::BuiltInViewIndex; + + case glslang::EbvFragSizeEXT: + builder.addExtension(spv::E_SPV_EXT_fragment_invocation_density); + builder.addCapability(spv::CapabilityFragmentDensityEXT); + return spv::BuiltInFragSizeEXT; + + case glslang::EbvFragInvocationCountEXT: + builder.addExtension(spv::E_SPV_EXT_fragment_invocation_density); + builder.addCapability(spv::CapabilityFragmentDensityEXT); + return spv::BuiltInFragInvocationCountEXT; + + case glslang::EbvViewportMaskNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NV_viewport_array2); + builder.addCapability(spv::CapabilityShaderViewportMaskNV); + } + return spv::BuiltInViewportMaskNV; + case glslang::EbvSecondaryPositionNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + } + return spv::BuiltInSecondaryPositionNV; + case glslang::EbvSecondaryViewportMaskNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + } + return spv::BuiltInSecondaryViewportMaskNV; + case glslang::EbvPositionPerViewNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes); + builder.addCapability(spv::CapabilityPerViewAttributesNV); + } + return spv::BuiltInPositionPerViewNV; + case glslang::EbvViewportMaskPerViewNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes); + builder.addCapability(spv::CapabilityPerViewAttributesNV); + } + return spv::BuiltInViewportMaskPerViewNV; + case glslang::EbvFragFullyCoveredNV: + builder.addExtension(spv::E_SPV_EXT_fragment_fully_covered); + builder.addCapability(spv::CapabilityFragmentFullyCoveredEXT); + return spv::BuiltInFullyCoveredEXT; + case glslang::EbvFragmentSizeNV: + builder.addExtension(spv::E_SPV_NV_shading_rate); + builder.addCapability(spv::CapabilityShadingRateNV); + return spv::BuiltInFragmentSizeNV; + case glslang::EbvInvocationsPerPixelNV: + builder.addExtension(spv::E_SPV_NV_shading_rate); + builder.addCapability(spv::CapabilityShadingRateNV); + return spv::BuiltInInvocationsPerPixelNV; + + // ray tracing + case glslang::EbvLaunchId: + return spv::BuiltInLaunchIdKHR; + case glslang::EbvLaunchSize: + return spv::BuiltInLaunchSizeKHR; + case glslang::EbvWorldRayOrigin: + return spv::BuiltInWorldRayOriginKHR; + case glslang::EbvWorldRayDirection: + return spv::BuiltInWorldRayDirectionKHR; + case glslang::EbvObjectRayOrigin: + return spv::BuiltInObjectRayOriginKHR; + case glslang::EbvObjectRayDirection: + return spv::BuiltInObjectRayDirectionKHR; + case glslang::EbvRayTmin: + return spv::BuiltInRayTminKHR; + case glslang::EbvRayTmax: + return spv::BuiltInRayTmaxKHR; + case glslang::EbvInstanceCustomIndex: + return spv::BuiltInInstanceCustomIndexKHR; + case glslang::EbvHitT: + return spv::BuiltInHitTKHR; + case glslang::EbvHitKind: + return spv::BuiltInHitKindKHR; + case glslang::EbvObjectToWorld: + case glslang::EbvObjectToWorld3x4: + return spv::BuiltInObjectToWorldKHR; + case glslang::EbvWorldToObject: + case glslang::EbvWorldToObject3x4: + return spv::BuiltInWorldToObjectKHR; + case glslang::EbvIncomingRayFlags: + return spv::BuiltInIncomingRayFlagsKHR; + case glslang::EbvGeometryIndex: + return spv::BuiltInRayGeometryIndexKHR; + + // barycentrics + case glslang::EbvBaryCoordNV: + builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric); + builder.addCapability(spv::CapabilityFragmentBarycentricNV); + return spv::BuiltInBaryCoordNV; + case glslang::EbvBaryCoordNoPerspNV: + builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric); + builder.addCapability(spv::CapabilityFragmentBarycentricNV); + return spv::BuiltInBaryCoordNoPerspNV; + + // mesh shaders + case glslang::EbvTaskCountNV: + return spv::BuiltInTaskCountNV; + case glslang::EbvPrimitiveCountNV: + return spv::BuiltInPrimitiveCountNV; + case glslang::EbvPrimitiveIndicesNV: + return spv::BuiltInPrimitiveIndicesNV; + case glslang::EbvClipDistancePerViewNV: + return spv::BuiltInClipDistancePerViewNV; + case glslang::EbvCullDistancePerViewNV: + return spv::BuiltInCullDistancePerViewNV; + case glslang::EbvLayerPerViewNV: + return spv::BuiltInLayerPerViewNV; + case glslang::EbvMeshViewCountNV: + return spv::BuiltInMeshViewCountNV; + case glslang::EbvMeshViewIndicesNV: + return spv::BuiltInMeshViewIndicesNV; + + // sm builtins + case glslang::EbvWarpsPerSM: + builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); + builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); + return spv::BuiltInWarpsPerSMNV; + case glslang::EbvSMCount: + builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); + builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); + return spv::BuiltInSMCountNV; + case glslang::EbvWarpID: + builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); + builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); + return spv::BuiltInWarpIDNV; + case glslang::EbvSMID: + builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); + builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); + return spv::BuiltInSMIDNV; +#endif + + default: + return spv::BuiltInMax; + } +} + +// Translate glslang image layout format to SPIR-V image format. +spv::ImageFormat TGlslangToSpvTraverser::TranslateImageFormat(const glslang::TType& type) +{ + assert(type.getBasicType() == glslang::EbtSampler); + +#ifdef GLSLANG_WEB + return spv::ImageFormatUnknown; +#endif + + // Check for capabilities + switch (type.getQualifier().getFormat()) { + case glslang::ElfRg32f: + case glslang::ElfRg16f: + case glslang::ElfR11fG11fB10f: + case glslang::ElfR16f: + case glslang::ElfRgba16: + case glslang::ElfRgb10A2: + case glslang::ElfRg16: + case glslang::ElfRg8: + case glslang::ElfR16: + case glslang::ElfR8: + case glslang::ElfRgba16Snorm: + case glslang::ElfRg16Snorm: + case glslang::ElfRg8Snorm: + case glslang::ElfR16Snorm: + case glslang::ElfR8Snorm: + + case glslang::ElfRg32i: + case glslang::ElfRg16i: + case glslang::ElfRg8i: + case glslang::ElfR16i: + case glslang::ElfR8i: + + case glslang::ElfRgb10a2ui: + case glslang::ElfRg32ui: + case glslang::ElfRg16ui: + case glslang::ElfRg8ui: + case glslang::ElfR16ui: + case glslang::ElfR8ui: + builder.addCapability(spv::CapabilityStorageImageExtendedFormats); + break; + + default: + break; + } + + // do the translation + switch (type.getQualifier().getFormat()) { + case glslang::ElfNone: return spv::ImageFormatUnknown; + case glslang::ElfRgba32f: return spv::ImageFormatRgba32f; + case glslang::ElfRgba16f: return spv::ImageFormatRgba16f; + case glslang::ElfR32f: return spv::ImageFormatR32f; + case glslang::ElfRgba8: return spv::ImageFormatRgba8; + case glslang::ElfRgba8Snorm: return spv::ImageFormatRgba8Snorm; + case glslang::ElfRg32f: return spv::ImageFormatRg32f; + case glslang::ElfRg16f: return spv::ImageFormatRg16f; + case glslang::ElfR11fG11fB10f: return spv::ImageFormatR11fG11fB10f; + case glslang::ElfR16f: return spv::ImageFormatR16f; + case glslang::ElfRgba16: return spv::ImageFormatRgba16; + case glslang::ElfRgb10A2: return spv::ImageFormatRgb10A2; + case glslang::ElfRg16: return spv::ImageFormatRg16; + case glslang::ElfRg8: return spv::ImageFormatRg8; + case glslang::ElfR16: return spv::ImageFormatR16; + case glslang::ElfR8: return spv::ImageFormatR8; + case glslang::ElfRgba16Snorm: return spv::ImageFormatRgba16Snorm; + case glslang::ElfRg16Snorm: return spv::ImageFormatRg16Snorm; + case glslang::ElfRg8Snorm: return spv::ImageFormatRg8Snorm; + case glslang::ElfR16Snorm: return spv::ImageFormatR16Snorm; + case glslang::ElfR8Snorm: return spv::ImageFormatR8Snorm; + case glslang::ElfRgba32i: return spv::ImageFormatRgba32i; + case glslang::ElfRgba16i: return spv::ImageFormatRgba16i; + case glslang::ElfRgba8i: return spv::ImageFormatRgba8i; + case glslang::ElfR32i: return spv::ImageFormatR32i; + case glslang::ElfRg32i: return spv::ImageFormatRg32i; + case glslang::ElfRg16i: return spv::ImageFormatRg16i; + case glslang::ElfRg8i: return spv::ImageFormatRg8i; + case glslang::ElfR16i: return spv::ImageFormatR16i; + case glslang::ElfR8i: return spv::ImageFormatR8i; + case glslang::ElfRgba32ui: return spv::ImageFormatRgba32ui; + case glslang::ElfRgba16ui: return spv::ImageFormatRgba16ui; + case glslang::ElfRgba8ui: return spv::ImageFormatRgba8ui; + case glslang::ElfR32ui: return spv::ImageFormatR32ui; + case glslang::ElfRg32ui: return spv::ImageFormatRg32ui; + case glslang::ElfRg16ui: return spv::ImageFormatRg16ui; + case glslang::ElfRgb10a2ui: return spv::ImageFormatRgb10a2ui; + case glslang::ElfRg8ui: return spv::ImageFormatRg8ui; + case glslang::ElfR16ui: return spv::ImageFormatR16ui; + case glslang::ElfR8ui: return spv::ImageFormatR8ui; + default: return spv::ImageFormatMax; + } +} + +spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSelectionControl( + const glslang::TIntermSelection& selectionNode) const +{ + if (selectionNode.getFlatten()) + return spv::SelectionControlFlattenMask; + if (selectionNode.getDontFlatten()) + return spv::SelectionControlDontFlattenMask; + return spv::SelectionControlMaskNone; +} + +spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSwitchControl(const glslang::TIntermSwitch& switchNode) + const +{ + if (switchNode.getFlatten()) + return spv::SelectionControlFlattenMask; + if (switchNode.getDontFlatten()) + return spv::SelectionControlDontFlattenMask; + return spv::SelectionControlMaskNone; +} + +// return a non-0 dependency if the dependency argument must be set +spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang::TIntermLoop& loopNode, + std::vector& operands) const +{ + spv::LoopControlMask control = spv::LoopControlMaskNone; + + if (loopNode.getDontUnroll()) + control = control | spv::LoopControlDontUnrollMask; + if (loopNode.getUnroll()) + control = control | spv::LoopControlUnrollMask; + if (unsigned(loopNode.getLoopDependency()) == glslang::TIntermLoop::dependencyInfinite) + control = control | spv::LoopControlDependencyInfiniteMask; + else if (loopNode.getLoopDependency() > 0) { + control = control | spv::LoopControlDependencyLengthMask; + operands.push_back((unsigned int)loopNode.getLoopDependency()); + } + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) { + if (loopNode.getMinIterations() > 0) { + control = control | spv::LoopControlMinIterationsMask; + operands.push_back(loopNode.getMinIterations()); + } + if (loopNode.getMaxIterations() < glslang::TIntermLoop::iterationsInfinite) { + control = control | spv::LoopControlMaxIterationsMask; + operands.push_back(loopNode.getMaxIterations()); + } + if (loopNode.getIterationMultiple() > 1) { + control = control | spv::LoopControlIterationMultipleMask; + operands.push_back(loopNode.getIterationMultiple()); + } + if (loopNode.getPeelCount() > 0) { + control = control | spv::LoopControlPeelCountMask; + operands.push_back(loopNode.getPeelCount()); + } + if (loopNode.getPartialCount() > 0) { + control = control | spv::LoopControlPartialCountMask; + operands.push_back(loopNode.getPartialCount()); + } + } + + return control; +} + +// Translate glslang type to SPIR-V storage class. +spv::StorageClass TGlslangToSpvTraverser::TranslateStorageClass(const glslang::TType& type) +{ + if (type.getBasicType() == glslang::EbtRayQuery) + return spv::StorageClassFunction; + if (type.getQualifier().isPipeInput()) + return spv::StorageClassInput; + if (type.getQualifier().isPipeOutput()) + return spv::StorageClassOutput; + + if (glslangIntermediate->getSource() != glslang::EShSourceHlsl || + type.getQualifier().storage == glslang::EvqUniform) { + if (type.isAtomic()) + return spv::StorageClassAtomicCounter; + if (type.containsOpaque()) + return spv::StorageClassUniformConstant; + } + + if (type.getQualifier().isUniformOrBuffer() && + type.getQualifier().isShaderRecord()) { + return spv::StorageClassShaderRecordBufferKHR; + } + + if (glslangIntermediate->usingStorageBuffer() && type.getQualifier().storage == glslang::EvqBuffer) { + builder.addIncorporatedExtension(spv::E_SPV_KHR_storage_buffer_storage_class, spv::Spv_1_3); + return spv::StorageClassStorageBuffer; + } + + if (type.getQualifier().isUniformOrBuffer()) { + if (type.getQualifier().isPushConstant()) + return spv::StorageClassPushConstant; + if (type.getBasicType() == glslang::EbtBlock) + return spv::StorageClassUniform; + return spv::StorageClassUniformConstant; + } + + switch (type.getQualifier().storage) { + case glslang::EvqGlobal: return spv::StorageClassPrivate; + case glslang::EvqConstReadOnly: return spv::StorageClassFunction; + case glslang::EvqTemporary: return spv::StorageClassFunction; + case glslang::EvqShared: return spv::StorageClassWorkgroup; +#ifndef GLSLANG_WEB + case glslang::EvqPayload: return spv::StorageClassRayPayloadKHR; + case glslang::EvqPayloadIn: return spv::StorageClassIncomingRayPayloadKHR; + case glslang::EvqHitAttr: return spv::StorageClassHitAttributeKHR; + case glslang::EvqCallableData: return spv::StorageClassCallableDataKHR; + case glslang::EvqCallableDataIn: return spv::StorageClassIncomingCallableDataKHR; +#endif + default: + assert(0); + break; + } + + return spv::StorageClassFunction; +} + +// Add capabilities pertaining to how an array is indexed. +void TGlslangToSpvTraverser::addIndirectionIndexCapabilities(const glslang::TType& baseType, + const glslang::TType& indexType) +{ +#ifndef GLSLANG_WEB + if (indexType.getQualifier().isNonUniform()) { + // deal with an asserted non-uniform index + // SPV_EXT_descriptor_indexing already added in TranslateNonUniformDecoration + if (baseType.getBasicType() == glslang::EbtSampler) { + if (baseType.getQualifier().hasAttachment()) + builder.addCapability(spv::CapabilityInputAttachmentArrayNonUniformIndexingEXT); + else if (baseType.isImage() && baseType.getSampler().isBuffer()) + builder.addCapability(spv::CapabilityStorageTexelBufferArrayNonUniformIndexingEXT); + else if (baseType.isTexture() && baseType.getSampler().isBuffer()) + builder.addCapability(spv::CapabilityUniformTexelBufferArrayNonUniformIndexingEXT); + else if (baseType.isImage()) + builder.addCapability(spv::CapabilityStorageImageArrayNonUniformIndexingEXT); + else if (baseType.isTexture()) + builder.addCapability(spv::CapabilitySampledImageArrayNonUniformIndexingEXT); + } else if (baseType.getBasicType() == glslang::EbtBlock) { + if (baseType.getQualifier().storage == glslang::EvqBuffer) + builder.addCapability(spv::CapabilityStorageBufferArrayNonUniformIndexingEXT); + else if (baseType.getQualifier().storage == glslang::EvqUniform) + builder.addCapability(spv::CapabilityUniformBufferArrayNonUniformIndexingEXT); + } + } else { + // assume a dynamically uniform index + if (baseType.getBasicType() == glslang::EbtSampler) { + if (baseType.getQualifier().hasAttachment()) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityInputAttachmentArrayDynamicIndexingEXT); + } else if (baseType.isImage() && baseType.getSampler().isBuffer()) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityStorageTexelBufferArrayDynamicIndexingEXT); + } else if (baseType.isTexture() && baseType.getSampler().isBuffer()) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityUniformTexelBufferArrayDynamicIndexingEXT); + } + } + } +#endif +} + +// Return whether or not the given type is something that should be tied to a +// descriptor set. +bool IsDescriptorResource(const glslang::TType& type) +{ + // uniform and buffer blocks are included, unless it is a push_constant + if (type.getBasicType() == glslang::EbtBlock) + return type.getQualifier().isUniformOrBuffer() && + ! type.getQualifier().isShaderRecord() && + ! type.getQualifier().isPushConstant(); + + // non block... + // basically samplerXXX/subpass/sampler/texture are all included + // if they are the global-scope-class, not the function parameter + // (or local, if they ever exist) class. + if (type.getBasicType() == glslang::EbtSampler) + return type.getQualifier().isUniformOrBuffer(); + + // None of the above. + return false; +} + +void InheritQualifiers(glslang::TQualifier& child, const glslang::TQualifier& parent) +{ + if (child.layoutMatrix == glslang::ElmNone) + child.layoutMatrix = parent.layoutMatrix; + + if (parent.invariant) + child.invariant = true; + if (parent.flat) + child.flat = true; + if (parent.centroid) + child.centroid = true; +#ifndef GLSLANG_WEB + if (parent.nopersp) + child.nopersp = true; + if (parent.explicitInterp) + child.explicitInterp = true; + if (parent.perPrimitiveNV) + child.perPrimitiveNV = true; + if (parent.perViewNV) + child.perViewNV = true; + if (parent.perTaskNV) + child.perTaskNV = true; + if (parent.patch) + child.patch = true; + if (parent.sample) + child.sample = true; + if (parent.coherent) + child.coherent = true; + if (parent.devicecoherent) + child.devicecoherent = true; + if (parent.queuefamilycoherent) + child.queuefamilycoherent = true; + if (parent.workgroupcoherent) + child.workgroupcoherent = true; + if (parent.subgroupcoherent) + child.subgroupcoherent = true; + if (parent.shadercallcoherent) + child.shadercallcoherent = true; + if (parent.nonprivate) + child.nonprivate = true; + if (parent.volatil) + child.volatil = true; + if (parent.restrict) + child.restrict = true; + if (parent.readonly) + child.readonly = true; + if (parent.writeonly) + child.writeonly = true; +#endif +} + +bool HasNonLayoutQualifiers(const glslang::TType& type, const glslang::TQualifier& qualifier) +{ + // This should list qualifiers that simultaneous satisfy: + // - struct members might inherit from a struct declaration + // (note that non-block structs don't explicitly inherit, + // only implicitly, meaning no decoration involved) + // - affect decorations on the struct members + // (note smooth does not, and expecting something like volatile + // to effect the whole object) + // - are not part of the offset/st430/etc or row/column-major layout + return qualifier.invariant || (qualifier.hasLocation() && type.getBasicType() == glslang::EbtBlock); +} + +// +// Implement the TGlslangToSpvTraverser class. +// + +TGlslangToSpvTraverser::TGlslangToSpvTraverser(unsigned int spvVersion, + const glslang::TIntermediate* glslangIntermediate, + spv::SpvBuildLogger* buildLogger, glslang::SpvOptions& options) : + TIntermTraverser(true, false, true), + options(options), + shaderEntry(nullptr), currentFunction(nullptr), + sequenceDepth(0), logger(buildLogger), + builder(spvVersion, (glslang::GetKhronosToolId() << 16) | glslang::GetSpirvGeneratorVersion(), logger), + inEntryPoint(false), entryPointTerminated(false), linkageOnly(false), + glslangIntermediate(glslangIntermediate), + nanMinMaxClamp(glslangIntermediate->getNanMinMaxClamp()), + nonSemanticDebugPrintf(0) +{ + spv::ExecutionModel executionModel = TranslateExecutionModel(glslangIntermediate->getStage()); + + builder.clearAccessChain(); + builder.setSource(TranslateSourceLanguage(glslangIntermediate->getSource(), glslangIntermediate->getProfile()), + glslangIntermediate->getVersion()); + + if (options.generateDebugInfo) { + builder.setEmitOpLines(); + builder.setSourceFile(glslangIntermediate->getSourceFile()); + + // Set the source shader's text. If for SPV version 1.0, include + // a preamble in comments stating the OpModuleProcessed instructions. + // Otherwise, emit those as actual instructions. + std::string text; + const std::vector& processes = glslangIntermediate->getProcesses(); + for (int p = 0; p < (int)processes.size(); ++p) { + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_1) { + text.append("// OpModuleProcessed "); + text.append(processes[p]); + text.append("\n"); + } else + builder.addModuleProcessed(processes[p]); + } + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_1 && (int)processes.size() > 0) + text.append("#line 1\n"); + text.append(glslangIntermediate->getSourceText()); + builder.setSourceText(text); + // Pass name and text for all included files + const std::map& include_txt = glslangIntermediate->getIncludeText(); + for (auto iItr = include_txt.begin(); iItr != include_txt.end(); ++iItr) + builder.addInclude(iItr->first, iItr->second); + } + stdBuiltins = builder.import("GLSL.std.450"); + + spv::AddressingModel addressingModel = spv::AddressingModelLogical; + spv::MemoryModel memoryModel = spv::MemoryModelGLSL450; + + if (glslangIntermediate->usingPhysicalStorageBuffer()) { + addressingModel = spv::AddressingModelPhysicalStorageBuffer64EXT; + builder.addIncorporatedExtension(spv::E_SPV_EXT_physical_storage_buffer, spv::Spv_1_5); + builder.addCapability(spv::CapabilityPhysicalStorageBufferAddressesEXT); + } + if (glslangIntermediate->usingVulkanMemoryModel()) { + memoryModel = spv::MemoryModelVulkanKHR; + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + builder.addIncorporatedExtension(spv::E_SPV_KHR_vulkan_memory_model, spv::Spv_1_5); + } + builder.setMemoryModel(addressingModel, memoryModel); + + if (glslangIntermediate->usingVariablePointers()) { + builder.addCapability(spv::CapabilityVariablePointers); + } + + shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str()); + entryPoint = builder.addEntryPoint(executionModel, shaderEntry, glslangIntermediate->getEntryPointName().c_str()); + + // Add the source extensions + const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions(); + for (auto it = sourceExtensions.begin(); it != sourceExtensions.end(); ++it) + builder.addSourceExtension(it->first.c_str()); + + // Add the top-level modes for this shader. + + if (glslangIntermediate->getXfbMode()) { + builder.addCapability(spv::CapabilityTransformFeedback); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeXfb); + } + + if (sourceExtensions.find("GL_EXT_ray_flags_primitive_culling") != sourceExtensions.end()) { + builder.addCapability(spv::CapabilityRayTraversalPrimitiveCullingProvisionalKHR); + } + + unsigned int mode; + switch (glslangIntermediate->getStage()) { + case EShLangVertex: + builder.addCapability(spv::CapabilityShader); + break; + + case EShLangFragment: + builder.addCapability(spv::CapabilityShader); + if (glslangIntermediate->getPixelCenterInteger()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModePixelCenterInteger); + + if (glslangIntermediate->getOriginUpperLeft()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginUpperLeft); + else + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginLowerLeft); + + if (glslangIntermediate->getEarlyFragmentTests()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModeEarlyFragmentTests); + + if (glslangIntermediate->getPostDepthCoverage()) { + builder.addCapability(spv::CapabilitySampleMaskPostDepthCoverage); + builder.addExecutionMode(shaderEntry, spv::ExecutionModePostDepthCoverage); + builder.addExtension(spv::E_SPV_KHR_post_depth_coverage); + } + + if (glslangIntermediate->getDepth() != glslang::EldUnchanged && glslangIntermediate->isDepthReplacing()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModeDepthReplacing); + +#ifndef GLSLANG_WEB + + switch(glslangIntermediate->getDepth()) { + case glslang::EldGreater: mode = spv::ExecutionModeDepthGreater; break; + case glslang::EldLess: mode = spv::ExecutionModeDepthLess; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + switch (glslangIntermediate->getInterlockOrdering()) { + case glslang::EioPixelInterlockOrdered: mode = spv::ExecutionModePixelInterlockOrderedEXT; + break; + case glslang::EioPixelInterlockUnordered: mode = spv::ExecutionModePixelInterlockUnorderedEXT; + break; + case glslang::EioSampleInterlockOrdered: mode = spv::ExecutionModeSampleInterlockOrderedEXT; + break; + case glslang::EioSampleInterlockUnordered: mode = spv::ExecutionModeSampleInterlockUnorderedEXT; + break; + case glslang::EioShadingRateInterlockOrdered: mode = spv::ExecutionModeShadingRateInterlockOrderedEXT; + break; + case glslang::EioShadingRateInterlockUnordered: mode = spv::ExecutionModeShadingRateInterlockUnorderedEXT; + break; + default: mode = spv::ExecutionModeMax; + break; + } + if (mode != spv::ExecutionModeMax) { + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + if (mode == spv::ExecutionModeShadingRateInterlockOrderedEXT || + mode == spv::ExecutionModeShadingRateInterlockUnorderedEXT) { + builder.addCapability(spv::CapabilityFragmentShaderShadingRateInterlockEXT); + } else if (mode == spv::ExecutionModePixelInterlockOrderedEXT || + mode == spv::ExecutionModePixelInterlockUnorderedEXT) { + builder.addCapability(spv::CapabilityFragmentShaderPixelInterlockEXT); + } else { + builder.addCapability(spv::CapabilityFragmentShaderSampleInterlockEXT); + } + builder.addExtension(spv::E_SPV_EXT_fragment_shader_interlock); + } +#endif + break; + + case EShLangCompute: + builder.addCapability(spv::CapabilityShader); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeLocalSize, glslangIntermediate->getLocalSize(0), + glslangIntermediate->getLocalSize(1), + glslangIntermediate->getLocalSize(2)); + if (glslangIntermediate->getLayoutDerivativeModeNone() == glslang::LayoutDerivativeGroupQuads) { + builder.addCapability(spv::CapabilityComputeDerivativeGroupQuadsNV); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeDerivativeGroupQuadsNV); + builder.addExtension(spv::E_SPV_NV_compute_shader_derivatives); + } else if (glslangIntermediate->getLayoutDerivativeModeNone() == glslang::LayoutDerivativeGroupLinear) { + builder.addCapability(spv::CapabilityComputeDerivativeGroupLinearNV); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeDerivativeGroupLinearNV); + builder.addExtension(spv::E_SPV_NV_compute_shader_derivatives); + } + break; +#ifndef GLSLANG_WEB + case EShLangTessEvaluation: + case EShLangTessControl: + builder.addCapability(spv::CapabilityTessellation); + + glslang::TLayoutGeometry primitive; + + if (glslangIntermediate->getStage() == EShLangTessControl) { + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, + glslangIntermediate->getVertices()); + primitive = glslangIntermediate->getOutputPrimitive(); + } else { + primitive = glslangIntermediate->getInputPrimitive(); + } + + switch (primitive) { + case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break; + case glslang::ElgQuads: mode = spv::ExecutionModeQuads; break; + case glslang::ElgIsolines: mode = spv::ExecutionModeIsolines; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + + switch (glslangIntermediate->getVertexSpacing()) { + case glslang::EvsEqual: mode = spv::ExecutionModeSpacingEqual; break; + case glslang::EvsFractionalEven: mode = spv::ExecutionModeSpacingFractionalEven; break; + case glslang::EvsFractionalOdd: mode = spv::ExecutionModeSpacingFractionalOdd; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + + switch (glslangIntermediate->getVertexOrder()) { + case glslang::EvoCw: mode = spv::ExecutionModeVertexOrderCw; break; + case glslang::EvoCcw: mode = spv::ExecutionModeVertexOrderCcw; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + + if (glslangIntermediate->getPointMode()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModePointMode); + break; + + case EShLangGeometry: + builder.addCapability(spv::CapabilityGeometry); + switch (glslangIntermediate->getInputPrimitive()) { + case glslang::ElgPoints: mode = spv::ExecutionModeInputPoints; break; + case glslang::ElgLines: mode = spv::ExecutionModeInputLines; break; + case glslang::ElgLinesAdjacency: mode = spv::ExecutionModeInputLinesAdjacency; break; + case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break; + case glslang::ElgTrianglesAdjacency: mode = spv::ExecutionModeInputTrianglesAdjacency; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + + builder.addExecutionMode(shaderEntry, spv::ExecutionModeInvocations, glslangIntermediate->getInvocations()); + + switch (glslangIntermediate->getOutputPrimitive()) { + case glslang::ElgPoints: mode = spv::ExecutionModeOutputPoints; break; + case glslang::ElgLineStrip: mode = spv::ExecutionModeOutputLineStrip; break; + case glslang::ElgTriangleStrip: mode = spv::ExecutionModeOutputTriangleStrip; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices()); + break; + + case EShLangRayGen: + case EShLangIntersect: + case EShLangAnyHit: + case EShLangClosestHit: + case EShLangMiss: + case EShLangCallable: + { + auto& extensions = glslangIntermediate->getRequestedExtensions(); + if (extensions.find("GL_NV_ray_tracing") == extensions.end()) { + builder.addCapability(spv::CapabilityRayTracingProvisionalKHR); + builder.addExtension("SPV_KHR_ray_tracing"); + } + else { + builder.addCapability(spv::CapabilityRayTracingNV); + builder.addExtension("SPV_NV_ray_tracing"); + } + break; + } + case EShLangTaskNV: + case EShLangMeshNV: + builder.addCapability(spv::CapabilityMeshShadingNV); + builder.addExtension(spv::E_SPV_NV_mesh_shader); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeLocalSize, glslangIntermediate->getLocalSize(0), + glslangIntermediate->getLocalSize(1), + glslangIntermediate->getLocalSize(2)); + if (glslangIntermediate->getStage() == EShLangMeshNV) { + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, + glslangIntermediate->getVertices()); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputPrimitivesNV, + glslangIntermediate->getPrimitives()); + + switch (glslangIntermediate->getOutputPrimitive()) { + case glslang::ElgPoints: mode = spv::ExecutionModeOutputPoints; break; + case glslang::ElgLines: mode = spv::ExecutionModeOutputLinesNV; break; + case glslang::ElgTriangles: mode = spv::ExecutionModeOutputTrianglesNV; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + } + break; +#endif + + default: + break; + } +} + +// Finish creating SPV, after the traversal is complete. +void TGlslangToSpvTraverser::finishSpv() +{ + // Finish the entry point function + if (! entryPointTerminated) { + builder.setBuildPoint(shaderEntry->getLastBlock()); + builder.leaveFunction(); + } + + // finish off the entry-point SPV instruction by adding the Input/Output + for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it) + entryPoint->addIdOperand(*it); + + // Add capabilities, extensions, remove unneeded decorations, etc., + // based on the resulting SPIR-V. + // Note: WebGPU code generation must have the opportunity to aggressively + // prune unreachable merge blocks and continue targets. + builder.postProcess(); +} + +// Write the SPV into 'out'. +void TGlslangToSpvTraverser::dumpSpv(std::vector& out) +{ + builder.dump(out); +} + +// +// Implement the traversal functions. +// +// Return true from interior nodes to have the external traversal +// continue on to children. Return false if children were +// already processed. +// + +// +// Symbols can turn into +// - uniform/input reads +// - output writes +// - complex lvalue base setups: foo.bar[3].... , where we see foo and start up an access chain +// - something simple that degenerates into the last bullet +// +void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol) +{ + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (symbol->getType().isStruct()) + glslangTypeToIdMap[symbol->getType().getStruct()] = symbol->getId(); + + if (symbol->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + + // getSymbolId() will set up all the IO decorations on the first call. + // Formal function parameters were mapped during makeFunctions(). + spv::Id id = getSymbolId(symbol); + + if (builder.isPointer(id)) { + // Include all "static use" and "linkage only" interface variables on the OpEntryPoint instruction + // Consider adding to the OpEntryPoint interface list. + // Only looking at structures if they have at least one member. + if (!symbol->getType().isStruct() || symbol->getType().getStruct()->size() > 0) { + spv::StorageClass sc = builder.getStorageClass(id); + // Before SPIR-V 1.4, we only want to include Input and Output. + // Starting with SPIR-V 1.4, we want all globals. + if ((glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4 && sc != spv::StorageClassFunction) || + (sc == spv::StorageClassInput || sc == spv::StorageClassOutput)) { + iOSet.insert(id); + } + } + + // If the SPIR-V type is required to be different than the AST type + // (for ex SubgroupMasks or 3x4 ObjectToWorld/WorldToObject matrices), + // translate now from the SPIR-V type to the AST type, for the consuming + // operation. + // Note this turns it from an l-value to an r-value. + // Currently, all symbols needing this are inputs; avoid the map lookup when non-input. + if (symbol->getType().getQualifier().storage == glslang::EvqVaryingIn) + id = translateForcedType(id); + } + + // Only process non-linkage-only nodes for generating actual static uses + if (! linkageOnly || symbol->getQualifier().isSpecConstant()) { + // Prepare to generate code for the access + + // L-value chains will be computed left to right. We're on the symbol now, + // which is the left-most part of the access chain, so now is "clear" time, + // followed by setting the base. + builder.clearAccessChain(); + + // For now, we consider all user variables as being in memory, so they are pointers, + // except for + // A) R-Value arguments to a function, which are an intermediate object. + // See comments in handleUserFunctionCall(). + // B) Specialization constants (normal constants don't even come in as a variable), + // These are also pure R-values. + // C) R-Values from type translation, see above call to translateForcedType() + glslang::TQualifier qualifier = symbol->getQualifier(); + if (qualifier.isSpecConstant() || rValueParameters.find(symbol->getId()) != rValueParameters.end() || + !builder.isPointerType(builder.getTypeId(id))) + builder.setAccessChainRValue(id); + else + builder.setAccessChainLValue(id); + } + +#ifdef ENABLE_HLSL + // Process linkage-only nodes for any special additional interface work. + if (linkageOnly) { + if (glslangIntermediate->getHlslFunctionality1()) { + // Map implicit counter buffers to their originating buffers, which should have been + // seen by now, given earlier pruning of unused counters, and preservation of order + // of declaration. + if (symbol->getType().getQualifier().isUniformOrBuffer()) { + if (!glslangIntermediate->hasCounterBufferName(symbol->getName())) { + // Save possible originating buffers for counter buffers, keyed by + // making the potential counter-buffer name. + std::string keyName = symbol->getName().c_str(); + keyName = glslangIntermediate->addCounterBufferName(keyName); + counterOriginator[keyName] = symbol; + } else { + // Handle a counter buffer, by finding the saved originating buffer. + std::string keyName = symbol->getName().c_str(); + auto it = counterOriginator.find(keyName); + if (it != counterOriginator.end()) { + id = getSymbolId(it->second); + if (id != spv::NoResult) { + spv::Id counterId = getSymbolId(symbol); + if (counterId != spv::NoResult) { + builder.addExtension("SPV_GOOGLE_hlsl_functionality1"); + builder.addDecorationId(id, spv::DecorationHlslCounterBufferGOOGLE, counterId); + } + } + } + } + } + } + } +#endif +} + +bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::TIntermBinary* node) +{ + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + if (node->getLeft()->getAsSymbolNode() != nullptr && node->getLeft()->getType().isStruct()) { + glslangTypeToIdMap[node->getLeft()->getType().getStruct()] = node->getLeft()->getAsSymbolNode()->getId(); + } + if (node->getRight()->getAsSymbolNode() != nullptr && node->getRight()->getType().isStruct()) { + glslangTypeToIdMap[node->getRight()->getType().getStruct()] = node->getRight()->getAsSymbolNode()->getId(); + } + + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + + // First, handle special cases + switch (node->getOp()) { + case glslang::EOpAssign: + case glslang::EOpAddAssign: + case glslang::EOpSubAssign: + case glslang::EOpMulAssign: + case glslang::EOpVectorTimesMatrixAssign: + case glslang::EOpVectorTimesScalarAssign: + case glslang::EOpMatrixTimesScalarAssign: + case glslang::EOpMatrixTimesMatrixAssign: + case glslang::EOpDivAssign: + case glslang::EOpModAssign: + case glslang::EOpAndAssign: + case glslang::EOpInclusiveOrAssign: + case glslang::EOpExclusiveOrAssign: + case glslang::EOpLeftShiftAssign: + case glslang::EOpRightShiftAssign: + // A bin-op assign "a += b" means the same thing as "a = a + b" + // where a is evaluated before b. For a simple assignment, GLSL + // says to evaluate the left before the right. So, always, left + // node then right node. + { + // get the left l-value, save it away + builder.clearAccessChain(); + node->getLeft()->traverse(this); + spv::Builder::AccessChain lValue = builder.getAccessChain(); + + // evaluate the right + builder.clearAccessChain(); + node->getRight()->traverse(this); + spv::Id rValue = accessChainLoad(node->getRight()->getType()); + + if (node->getOp() != glslang::EOpAssign) { + // the left is also an r-value + builder.setAccessChain(lValue); + spv::Id leftRValue = accessChainLoad(node->getLeft()->getType()); + + // do the operation + OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()), + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + rValue = createBinaryOperation(node->getOp(), decorations, + convertGlslangToSpvType(node->getType()), leftRValue, rValue, + node->getType().getBasicType()); + + // these all need their counterparts in createBinaryOperation() + assert(rValue != spv::NoResult); + } + + // store the result + builder.setAccessChain(lValue); + multiTypeStore(node->getLeft()->getType(), rValue); + + // assignments are expressions having an rValue after they are evaluated... + builder.clearAccessChain(); + builder.setAccessChainRValue(rValue); + } + return false; + case glslang::EOpIndexDirect: + case glslang::EOpIndexDirectStruct: + { + // Structure, array, matrix, or vector indirection with statically known index. + // Get the left part of the access chain. + node->getLeft()->traverse(this); + + // Add the next element in the chain + + const int glslangIndex = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (! node->getLeft()->getType().isArray() && + node->getLeft()->getType().isVector() && + node->getOp() == glslang::EOpIndexDirect) { + // This is essentially a hard-coded vector swizzle of size 1, + // so short circuit the access-chain stuff with a swizzle. + std::vector swizzle; + swizzle.push_back(glslangIndex); + int dummySize; + builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType()), + TranslateCoherent(node->getLeft()->getType()), + glslangIntermediate->getBaseAlignmentScalar( + node->getLeft()->getType(), dummySize)); + } else { + + // Load through a block reference is performed with a dot operator that + // is mapped to EOpIndexDirectStruct. When we get to the actual reference, + // do a load and reset the access chain. + if (node->getLeft()->isReference() && + !node->getLeft()->getType().isArray() && + node->getOp() == glslang::EOpIndexDirectStruct) + { + spv::Id left = accessChainLoad(node->getLeft()->getType()); + builder.clearAccessChain(); + builder.setAccessChainLValue(left); + } + + int spvIndex = glslangIndex; + if (node->getLeft()->getBasicType() == glslang::EbtBlock && + node->getOp() == glslang::EOpIndexDirectStruct) + { + // This may be, e.g., an anonymous block-member selection, which generally need + // index remapping due to hidden members in anonymous blocks. + int glslangId = glslangTypeToIdMap[node->getLeft()->getType().getStruct()]; + if (memberRemapper.find(glslangId) != memberRemapper.end()) { + std::vector& remapper = memberRemapper[glslangId]; + assert(remapper.size() > 0); + spvIndex = remapper[glslangIndex]; + } + } + + // normal case for indexing array or structure or block + builder.accessChainPush(builder.makeIntConstant(spvIndex), + TranslateCoherent(node->getLeft()->getType()), + node->getLeft()->getType().getBufferReferenceAlignment()); + + // Add capabilities here for accessing PointSize and clip/cull distance. + // We have deferred generation of associated capabilities until now. + if (node->getLeft()->getType().isStruct() && ! node->getLeft()->getType().isArray()) + declareUseOfStructMember(*(node->getLeft()->getType().getStruct()), glslangIndex); + } + } + return false; + case glslang::EOpIndexIndirect: + { + // Array, matrix, or vector indirection with variable index. + // Will use native SPIR-V access-chain for and array indirection; + // matrices are arrays of vectors, so will also work for a matrix. + // Will use the access chain's 'component' for variable index into a vector. + + // This adapter is building access chains left to right. + // Set up the access chain to the left. + node->getLeft()->traverse(this); + + // save it so that computing the right side doesn't trash it + spv::Builder::AccessChain partial = builder.getAccessChain(); + + // compute the next index in the chain + builder.clearAccessChain(); + node->getRight()->traverse(this); + spv::Id index = accessChainLoad(node->getRight()->getType()); + + addIndirectionIndexCapabilities(node->getLeft()->getType(), node->getRight()->getType()); + + // restore the saved access chain + builder.setAccessChain(partial); + + if (! node->getLeft()->getType().isArray() && node->getLeft()->getType().isVector()) { + int dummySize; + builder.accessChainPushComponent(index, convertGlslangToSpvType(node->getLeft()->getType()), + TranslateCoherent(node->getLeft()->getType()), + glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), + dummySize)); + } else + builder.accessChainPush(index, TranslateCoherent(node->getLeft()->getType()), + node->getLeft()->getType().getBufferReferenceAlignment()); + } + return false; + case glslang::EOpVectorSwizzle: + { + node->getLeft()->traverse(this); + std::vector swizzle; + convertSwizzle(*node->getRight()->getAsAggregate(), swizzle); + int dummySize; + builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType()), + TranslateCoherent(node->getLeft()->getType()), + glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), + dummySize)); + } + return false; + case glslang::EOpMatrixSwizzle: + logger->missingFunctionality("matrix swizzle"); + return true; + case glslang::EOpLogicalOr: + case glslang::EOpLogicalAnd: + { + + // These may require short circuiting, but can sometimes be done as straight + // binary operations. The right operand must be short circuited if it has + // side effects, and should probably be if it is complex. + if (isTrivial(node->getRight()->getAsTyped())) + break; // handle below as a normal binary operation + // otherwise, we need to do dynamic short circuiting on the right operand + spv::Id result = createShortCircuit(node->getOp(), *node->getLeft()->getAsTyped(), + *node->getRight()->getAsTyped()); + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + } + return false; + default: + break; + } + + // Assume generic binary op... + + // get right operand + builder.clearAccessChain(); + node->getLeft()->traverse(this); + spv::Id left = accessChainLoad(node->getLeft()->getType()); + + // get left operand + builder.clearAccessChain(); + node->getRight()->traverse(this); + spv::Id right = accessChainLoad(node->getRight()->getType()); + + // get result + OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()), + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + spv::Id result = createBinaryOperation(node->getOp(), decorations, + convertGlslangToSpvType(node->getType()), left, right, + node->getLeft()->getType().getBasicType()); + + builder.clearAccessChain(); + if (! result) { + logger->missingFunctionality("unknown glslang binary operation"); + return true; // pick up a child as the place-holder result + } else { + builder.setAccessChainRValue(result); + return false; + } +} + +// Figure out what, if any, type changes are needed when accessing a specific built-in. +// Returns . +// Also see comment for 'forceType', regarding tracking SPIR-V-required types. +std::pair TGlslangToSpvTraverser::getForcedType(glslang::TBuiltInVariable glslangBuiltIn, + const glslang::TType& glslangType) +{ + switch(glslangBuiltIn) + { + case glslang::EbvSubGroupEqMask: + case glslang::EbvSubGroupGeMask: + case glslang::EbvSubGroupGtMask: + case glslang::EbvSubGroupLeMask: + case glslang::EbvSubGroupLtMask: { + // these require changing a 64-bit scaler -> a vector of 32-bit components + if (glslangType.isVector()) + break; + std::pair ret(builder.makeVectorType(builder.makeUintType(32), 4), + builder.makeUintType(64)); + return ret; + } + // There are no SPIR-V builtins defined for these and map onto original non-transposed + // builtins. During visitBinary we insert a transpose + case glslang::EbvWorldToObject3x4: + case glslang::EbvObjectToWorld3x4: { + std::pair ret(builder.makeMatrixType(builder.makeFloatType(32), 4, 3), + builder.makeMatrixType(builder.makeFloatType(32), 3, 4) + ); + return ret; + } + default: + break; + } + + std::pair ret(spv::NoType, spv::NoType); + return ret; +} + +// For an object previously identified (see getForcedType() and forceType) +// as needing type translations, do the translation needed for a load, turning +// an L-value into in R-value. +spv::Id TGlslangToSpvTraverser::translateForcedType(spv::Id object) +{ + const auto forceIt = forceType.find(object); + if (forceIt == forceType.end()) + return object; + + spv::Id desiredTypeId = forceIt->second; + spv::Id objectTypeId = builder.getTypeId(object); + assert(builder.isPointerType(objectTypeId)); + objectTypeId = builder.getContainedTypeId(objectTypeId); + if (builder.isVectorType(objectTypeId) && + builder.getScalarTypeWidth(builder.getContainedTypeId(objectTypeId)) == 32) { + if (builder.getScalarTypeWidth(desiredTypeId) == 64) { + // handle 32-bit v.xy* -> 64-bit + builder.clearAccessChain(); + builder.setAccessChainLValue(object); + object = builder.accessChainLoad(spv::NoPrecision, spv::DecorationMax, objectTypeId); + std::vector components; + components.push_back(builder.createCompositeExtract(object, builder.getContainedTypeId(objectTypeId), 0)); + components.push_back(builder.createCompositeExtract(object, builder.getContainedTypeId(objectTypeId), 1)); + + spv::Id vecType = builder.makeVectorType(builder.getContainedTypeId(objectTypeId), 2); + return builder.createUnaryOp(spv::OpBitcast, desiredTypeId, + builder.createCompositeConstruct(vecType, components)); + } else { + logger->missingFunctionality("forcing 32-bit vector type to non 64-bit scalar"); + } + } else if (builder.isMatrixType(objectTypeId)) { + // There are no SPIR-V builtins defined for 3x4 variants of ObjectToWorld/WorldToObject + // and we insert a transpose after loading the original non-transposed builtins + builder.clearAccessChain(); + builder.setAccessChainLValue(object); + object = builder.accessChainLoad(spv::NoPrecision, spv::DecorationMax, objectTypeId); + return builder.createUnaryOp(spv::OpTranspose, desiredTypeId, object); + + } else { + logger->missingFunctionality("forcing non 32-bit vector type"); + } + + return object; +} + +bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) +{ + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + + spv::Id result = spv::NoResult; + + // try texturing first + result = createImageTextureFunctionCall(node); + if (result != spv::NoResult) { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; // done with this node + } + + // Non-texturing. + + if (node->getOp() == glslang::EOpArrayLength) { + // Quite special; won't want to evaluate the operand. + + // Currently, the front-end does not allow .length() on an array until it is sized, + // except for the last block membeor of an SSBO. + // TODO: If this changes, link-time sized arrays might show up here, and need their + // size extracted. + + // Normal .length() would have been constant folded by the front-end. + // So, this has to be block.lastMember.length(). + // SPV wants "block" and member number as the operands, go get them. + + spv::Id length; + if (node->getOperand()->getType().isCoopMat()) { + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + + spv::Id typeId = convertGlslangToSpvType(node->getOperand()->getType()); + assert(builder.isCooperativeMatrixType(typeId)); + + length = builder.createCooperativeMatrixLength(typeId); + } else { + glslang::TIntermTyped* block = node->getOperand()->getAsBinaryNode()->getLeft(); + block->traverse(this); + unsigned int member = node->getOperand()->getAsBinaryNode()->getRight()->getAsConstantUnion() + ->getConstArray()[0].getUConst(); + length = builder.createArrayLength(builder.accessChainGetLValue(), member); + } + + // GLSL semantics say the result of .length() is an int, while SPIR-V says + // signedness must be 0. So, convert from SPIR-V unsigned back to GLSL's + // AST expectation of a signed result. + if (glslangIntermediate->getSource() == glslang::EShSourceGlsl) { + if (builder.isInSpecConstCodeGenMode()) { + length = builder.createBinOp(spv::OpIAdd, builder.makeIntType(32), length, builder.makeIntConstant(0)); + } else { + length = builder.createUnaryOp(spv::OpBitcast, builder.makeIntType(32), length); + } + } + + builder.clearAccessChain(); + builder.setAccessChainRValue(length); + + return false; + } + + // Start by evaluating the operand + + // Does it need a swizzle inversion? If so, evaluation is inverted; + // operate first on the swizzle base, then apply the swizzle. + spv::Id invertedType = spv::NoType; + auto resultType = [&invertedType, &node, this](){ return invertedType != spv::NoType ? + invertedType : convertGlslangToSpvType(node->getType()); }; + if (node->getOp() == glslang::EOpInterpolateAtCentroid) + invertedType = getInvertedSwizzleType(*node->getOperand()); + + builder.clearAccessChain(); + TIntermNode *operandNode; + if (invertedType != spv::NoType) + operandNode = node->getOperand()->getAsBinaryNode()->getLeft(); + else + operandNode = node->getOperand(); + + operandNode->traverse(this); + + spv::Id operand = spv::NoResult; + + spv::Builder::AccessChain::CoherentFlags lvalueCoherentFlags; + +#ifndef GLSLANG_WEB + if (node->getOp() == glslang::EOpAtomicCounterIncrement || + node->getOp() == glslang::EOpAtomicCounterDecrement || + node->getOp() == glslang::EOpAtomicCounter || + node->getOp() == glslang::EOpInterpolateAtCentroid || + node->getOp() == glslang::EOpRayQueryProceed || + node->getOp() == glslang::EOpRayQueryGetRayTMin || + node->getOp() == glslang::EOpRayQueryGetRayFlags || + node->getOp() == glslang::EOpRayQueryGetWorldRayOrigin || + node->getOp() == glslang::EOpRayQueryGetWorldRayDirection || + node->getOp() == glslang::EOpRayQueryGetIntersectionCandidateAABBOpaque || + node->getOp() == glslang::EOpRayQueryTerminate || + node->getOp() == glslang::EOpRayQueryConfirmIntersection) { + operand = builder.accessChainGetLValue(); // Special case l-value operands + lvalueCoherentFlags = builder.getAccessChain().coherentFlags; + lvalueCoherentFlags |= TranslateCoherent(operandNode->getAsTyped()->getType()); + } else +#endif + { + operand = accessChainLoad(node->getOperand()->getType()); + } + + OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()), + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + + // it could be a conversion + if (! result) + result = createConversion(node->getOp(), decorations, resultType(), operand, + node->getOperand()->getBasicType()); + + // if not, then possibly an operation + if (! result) + result = createUnaryOperation(node->getOp(), decorations, resultType(), operand, + node->getOperand()->getBasicType(), lvalueCoherentFlags); + + if (result) { + if (invertedType) { + result = createInvertedSwizzle(decorations.precision, *node->getOperand(), result); + decorations.addNonUniform(builder, result); + } + + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; // done with this node + } + + // it must be a special case, check... + switch (node->getOp()) { + case glslang::EOpPostIncrement: + case glslang::EOpPostDecrement: + case glslang::EOpPreIncrement: + case glslang::EOpPreDecrement: + { + // we need the integer value "1" or the floating point "1.0" to add/subtract + spv::Id one = 0; + if (node->getBasicType() == glslang::EbtFloat) + one = builder.makeFloatConstant(1.0F); +#ifndef GLSLANG_WEB + else if (node->getBasicType() == glslang::EbtDouble) + one = builder.makeDoubleConstant(1.0); + else if (node->getBasicType() == glslang::EbtFloat16) + one = builder.makeFloat16Constant(1.0F); + else if (node->getBasicType() == glslang::EbtInt8 || node->getBasicType() == glslang::EbtUint8) + one = builder.makeInt8Constant(1); + else if (node->getBasicType() == glslang::EbtInt16 || node->getBasicType() == glslang::EbtUint16) + one = builder.makeInt16Constant(1); + else if (node->getBasicType() == glslang::EbtInt64 || node->getBasicType() == glslang::EbtUint64) + one = builder.makeInt64Constant(1); +#endif + else + one = builder.makeIntConstant(1); + glslang::TOperator op; + if (node->getOp() == glslang::EOpPreIncrement || + node->getOp() == glslang::EOpPostIncrement) + op = glslang::EOpAdd; + else + op = glslang::EOpSub; + + spv::Id result = createBinaryOperation(op, decorations, + convertGlslangToSpvType(node->getType()), operand, one, + node->getType().getBasicType()); + assert(result != spv::NoResult); + + // The result of operation is always stored, but conditionally the + // consumed result. The consumed result is always an r-value. + builder.accessChainStore(result); + builder.clearAccessChain(); + if (node->getOp() == glslang::EOpPreIncrement || + node->getOp() == glslang::EOpPreDecrement) + builder.setAccessChainRValue(result); + else + builder.setAccessChainRValue(operand); + } + + return false; + +#ifndef GLSLANG_WEB + case glslang::EOpEmitStreamVertex: + builder.createNoResultOp(spv::OpEmitStreamVertex, operand); + return false; + case glslang::EOpEndStreamPrimitive: + builder.createNoResultOp(spv::OpEndStreamPrimitive, operand); + return false; + case glslang::EOpRayQueryTerminate: + builder.createNoResultOp(spv::OpRayQueryTerminateKHR, operand); + return false; + case glslang::EOpRayQueryConfirmIntersection: + builder.createNoResultOp(spv::OpRayQueryConfirmIntersectionKHR, operand); + return false; +#endif + + default: + logger->missingFunctionality("unknown glslang unary"); + return true; // pick up operand as placeholder result + } +} + +// Construct a composite object, recursively copying members if their types don't match +spv::Id TGlslangToSpvTraverser::createCompositeConstruct(spv::Id resultTypeId, std::vector constituents) +{ + for (int c = 0; c < (int)constituents.size(); ++c) { + spv::Id& constituent = constituents[c]; + spv::Id lType = builder.getContainedTypeId(resultTypeId, c); + spv::Id rType = builder.getTypeId(constituent); + if (lType != rType) { + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) { + constituent = builder.createUnaryOp(spv::OpCopyLogical, lType, constituent); + } else if (builder.isStructType(rType)) { + std::vector rTypeConstituents; + int numrTypeConstituents = builder.getNumTypeConstituents(rType); + for (int i = 0; i < numrTypeConstituents; ++i) { + rTypeConstituents.push_back(builder.createCompositeExtract(constituent, + builder.getContainedTypeId(rType, i), i)); + } + constituents[c] = createCompositeConstruct(lType, rTypeConstituents); + } else { + assert(builder.isArrayType(rType)); + std::vector rTypeConstituents; + int numrTypeConstituents = builder.getNumTypeConstituents(rType); + + spv::Id elementRType = builder.getContainedTypeId(rType); + for (int i = 0; i < numrTypeConstituents; ++i) { + rTypeConstituents.push_back(builder.createCompositeExtract(constituent, elementRType, i)); + } + constituents[c] = createCompositeConstruct(lType, rTypeConstituents); + } + } + } + return builder.createCompositeConstruct(resultTypeId, constituents); +} + +bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TIntermAggregate* node) +{ + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + + spv::Id result = spv::NoResult; + spv::Id invertedType = spv::NoType; // to use to override the natural type of the node + std::vector complexLvalues; // for holding swizzling l-values too complex for + // SPIR-V, for an out parameter + std::vector temporaryLvalues; // temporaries to pass, as proxies for complexLValues + + auto resultType = [&invertedType, &node, this](){ return invertedType != spv::NoType ? + invertedType : + convertGlslangToSpvType(node->getType()); }; + + // try texturing + result = createImageTextureFunctionCall(node); + if (result != spv::NoResult) { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; + } +#ifndef GLSLANG_WEB + else if (node->getOp() == glslang::EOpImageStore || + node->getOp() == glslang::EOpImageStoreLod || + node->getOp() == glslang::EOpImageAtomicStore) { + // "imageStore" is a special case, which has no result + return false; + } +#endif + + glslang::TOperator binOp = glslang::EOpNull; + bool reduceComparison = true; + bool isMatrix = false; + bool noReturnValue = false; + bool atomic = false; + + spv::Builder::AccessChain::CoherentFlags lvalueCoherentFlags; + + assert(node->getOp()); + + spv::Decoration precision = TranslatePrecisionDecoration(node->getOperationPrecision()); + + switch (node->getOp()) { + case glslang::EOpSequence: + { + if (preVisit) + ++sequenceDepth; + else + --sequenceDepth; + + if (sequenceDepth == 1) { + // If this is the parent node of all the functions, we want to see them + // early, so all call points have actual SPIR-V functions to reference. + // In all cases, still let the traverser visit the children for us. + makeFunctions(node->getAsAggregate()->getSequence()); + + // Also, we want all globals initializers to go into the beginning of the entry point, before + // anything else gets there, so visit out of order, doing them all now. + makeGlobalInitializers(node->getAsAggregate()->getSequence()); + + // Initializers are done, don't want to visit again, but functions and link objects need to be processed, + // so do them manually. + visitFunctions(node->getAsAggregate()->getSequence()); + + return false; + } + + return true; + } + case glslang::EOpLinkerObjects: + { + if (visit == glslang::EvPreVisit) + linkageOnly = true; + else + linkageOnly = false; + + return true; + } + case glslang::EOpComma: + { + // processing from left to right naturally leaves the right-most + // lying around in the access chain + glslang::TIntermSequence& glslangOperands = node->getSequence(); + for (int i = 0; i < (int)glslangOperands.size(); ++i) + glslangOperands[i]->traverse(this); + + return false; + } + case glslang::EOpFunction: + if (visit == glslang::EvPreVisit) { + if (isShaderEntryPoint(node)) { + inEntryPoint = true; + builder.setBuildPoint(shaderEntry->getLastBlock()); + currentFunction = shaderEntry; + } else { + handleFunctionEntry(node); + } + } else { + if (inEntryPoint) + entryPointTerminated = true; + builder.leaveFunction(); + inEntryPoint = false; + } + + return true; + case glslang::EOpParameters: + // Parameters will have been consumed by EOpFunction processing, but not + // the body, so we still visited the function node's children, making this + // child redundant. + return false; + case glslang::EOpFunctionCall: + { + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + if (node->isUserDefined()) + result = handleUserFunctionCall(node); + if (result) { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + } else + logger->missingFunctionality("missing user function; linker needs to catch that"); + + return false; + } + case glslang::EOpConstructMat2x2: + case glslang::EOpConstructMat2x3: + case glslang::EOpConstructMat2x4: + case glslang::EOpConstructMat3x2: + case glslang::EOpConstructMat3x3: + case glslang::EOpConstructMat3x4: + case glslang::EOpConstructMat4x2: + case glslang::EOpConstructMat4x3: + case glslang::EOpConstructMat4x4: + case glslang::EOpConstructDMat2x2: + case glslang::EOpConstructDMat2x3: + case glslang::EOpConstructDMat2x4: + case glslang::EOpConstructDMat3x2: + case glslang::EOpConstructDMat3x3: + case glslang::EOpConstructDMat3x4: + case glslang::EOpConstructDMat4x2: + case glslang::EOpConstructDMat4x3: + case glslang::EOpConstructDMat4x4: + case glslang::EOpConstructIMat2x2: + case glslang::EOpConstructIMat2x3: + case glslang::EOpConstructIMat2x4: + case glslang::EOpConstructIMat3x2: + case glslang::EOpConstructIMat3x3: + case glslang::EOpConstructIMat3x4: + case glslang::EOpConstructIMat4x2: + case glslang::EOpConstructIMat4x3: + case glslang::EOpConstructIMat4x4: + case glslang::EOpConstructUMat2x2: + case glslang::EOpConstructUMat2x3: + case glslang::EOpConstructUMat2x4: + case glslang::EOpConstructUMat3x2: + case glslang::EOpConstructUMat3x3: + case glslang::EOpConstructUMat3x4: + case glslang::EOpConstructUMat4x2: + case glslang::EOpConstructUMat4x3: + case glslang::EOpConstructUMat4x4: + case glslang::EOpConstructBMat2x2: + case glslang::EOpConstructBMat2x3: + case glslang::EOpConstructBMat2x4: + case glslang::EOpConstructBMat3x2: + case glslang::EOpConstructBMat3x3: + case glslang::EOpConstructBMat3x4: + case glslang::EOpConstructBMat4x2: + case glslang::EOpConstructBMat4x3: + case glslang::EOpConstructBMat4x4: + case glslang::EOpConstructF16Mat2x2: + case glslang::EOpConstructF16Mat2x3: + case glslang::EOpConstructF16Mat2x4: + case glslang::EOpConstructF16Mat3x2: + case glslang::EOpConstructF16Mat3x3: + case glslang::EOpConstructF16Mat3x4: + case glslang::EOpConstructF16Mat4x2: + case glslang::EOpConstructF16Mat4x3: + case glslang::EOpConstructF16Mat4x4: + isMatrix = true; + // fall through + case glslang::EOpConstructFloat: + case glslang::EOpConstructVec2: + case glslang::EOpConstructVec3: + case glslang::EOpConstructVec4: + case glslang::EOpConstructDouble: + case glslang::EOpConstructDVec2: + case glslang::EOpConstructDVec3: + case glslang::EOpConstructDVec4: + case glslang::EOpConstructFloat16: + case glslang::EOpConstructF16Vec2: + case glslang::EOpConstructF16Vec3: + case glslang::EOpConstructF16Vec4: + case glslang::EOpConstructBool: + case glslang::EOpConstructBVec2: + case glslang::EOpConstructBVec3: + case glslang::EOpConstructBVec4: + case glslang::EOpConstructInt8: + case glslang::EOpConstructI8Vec2: + case glslang::EOpConstructI8Vec3: + case glslang::EOpConstructI8Vec4: + case glslang::EOpConstructUint8: + case glslang::EOpConstructU8Vec2: + case glslang::EOpConstructU8Vec3: + case glslang::EOpConstructU8Vec4: + case glslang::EOpConstructInt16: + case glslang::EOpConstructI16Vec2: + case glslang::EOpConstructI16Vec3: + case glslang::EOpConstructI16Vec4: + case glslang::EOpConstructUint16: + case glslang::EOpConstructU16Vec2: + case glslang::EOpConstructU16Vec3: + case glslang::EOpConstructU16Vec4: + case glslang::EOpConstructInt: + case glslang::EOpConstructIVec2: + case glslang::EOpConstructIVec3: + case glslang::EOpConstructIVec4: + case glslang::EOpConstructUint: + case glslang::EOpConstructUVec2: + case glslang::EOpConstructUVec3: + case glslang::EOpConstructUVec4: + case glslang::EOpConstructInt64: + case glslang::EOpConstructI64Vec2: + case glslang::EOpConstructI64Vec3: + case glslang::EOpConstructI64Vec4: + case glslang::EOpConstructUint64: + case glslang::EOpConstructU64Vec2: + case glslang::EOpConstructU64Vec3: + case glslang::EOpConstructU64Vec4: + case glslang::EOpConstructStruct: + case glslang::EOpConstructTextureSampler: + case glslang::EOpConstructReference: + case glslang::EOpConstructCooperativeMatrix: + { + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + std::vector arguments; + translateArguments(*node, arguments, lvalueCoherentFlags); + spv::Id constructed; + if (node->getOp() == glslang::EOpConstructTextureSampler) + constructed = builder.createOp(spv::OpSampledImage, resultType(), arguments); + else if (node->getOp() == glslang::EOpConstructStruct || + node->getOp() == glslang::EOpConstructCooperativeMatrix || + node->getType().isArray()) { + std::vector constituents; + for (int c = 0; c < (int)arguments.size(); ++c) + constituents.push_back(arguments[c]); + constructed = createCompositeConstruct(resultType(), constituents); + } else if (isMatrix) + constructed = builder.createMatrixConstructor(precision, arguments, resultType()); + else + constructed = builder.createConstructor(precision, arguments, resultType()); + + builder.clearAccessChain(); + builder.setAccessChainRValue(constructed); + + return false; + } + + // These six are component-wise compares with component-wise results. + // Forward on to createBinaryOperation(), requesting a vector result. + case glslang::EOpLessThan: + case glslang::EOpGreaterThan: + case glslang::EOpLessThanEqual: + case glslang::EOpGreaterThanEqual: + case glslang::EOpVectorEqual: + case glslang::EOpVectorNotEqual: + { + // Map the operation to a binary + binOp = node->getOp(); + reduceComparison = false; + switch (node->getOp()) { + case glslang::EOpVectorEqual: binOp = glslang::EOpVectorEqual; break; + case glslang::EOpVectorNotEqual: binOp = glslang::EOpVectorNotEqual; break; + default: binOp = node->getOp(); break; + } + + break; + } + case glslang::EOpMul: + // component-wise matrix multiply + binOp = glslang::EOpMul; + break; + case glslang::EOpOuterProduct: + // two vectors multiplied to make a matrix + binOp = glslang::EOpOuterProduct; + break; + case glslang::EOpDot: + { + // for scalar dot product, use multiply + glslang::TIntermSequence& glslangOperands = node->getSequence(); + if (glslangOperands[0]->getAsTyped()->getVectorSize() == 1) + binOp = glslang::EOpMul; + break; + } + case glslang::EOpMod: + // when an aggregate, this is the floating-point mod built-in function, + // which can be emitted by the one in createBinaryOperation() + binOp = glslang::EOpMod; + break; + + case glslang::EOpEmitVertex: + case glslang::EOpEndPrimitive: + case glslang::EOpBarrier: + case glslang::EOpMemoryBarrier: + case glslang::EOpMemoryBarrierAtomicCounter: + case glslang::EOpMemoryBarrierBuffer: + case glslang::EOpMemoryBarrierImage: + case glslang::EOpMemoryBarrierShared: + case glslang::EOpGroupMemoryBarrier: + case glslang::EOpDeviceMemoryBarrier: + case glslang::EOpAllMemoryBarrierWithGroupSync: + case glslang::EOpDeviceMemoryBarrierWithGroupSync: + case glslang::EOpWorkgroupMemoryBarrier: + case glslang::EOpWorkgroupMemoryBarrierWithGroupSync: + case glslang::EOpSubgroupBarrier: + case glslang::EOpSubgroupMemoryBarrier: + case glslang::EOpSubgroupMemoryBarrierBuffer: + case glslang::EOpSubgroupMemoryBarrierImage: + case glslang::EOpSubgroupMemoryBarrierShared: + noReturnValue = true; + // These all have 0 operands and will naturally finish up in the code below for 0 operands + break; + + case glslang::EOpAtomicAdd: + case glslang::EOpAtomicMin: + case glslang::EOpAtomicMax: + case glslang::EOpAtomicAnd: + case glslang::EOpAtomicOr: + case glslang::EOpAtomicXor: + case glslang::EOpAtomicExchange: + case glslang::EOpAtomicCompSwap: + atomic = true; + break; + +#ifndef GLSLANG_WEB + case glslang::EOpAtomicStore: + noReturnValue = true; + // fallthrough + case glslang::EOpAtomicLoad: + atomic = true; + break; + + case glslang::EOpAtomicCounterAdd: + case glslang::EOpAtomicCounterSubtract: + case glslang::EOpAtomicCounterMin: + case glslang::EOpAtomicCounterMax: + case glslang::EOpAtomicCounterAnd: + case glslang::EOpAtomicCounterOr: + case glslang::EOpAtomicCounterXor: + case glslang::EOpAtomicCounterExchange: + case glslang::EOpAtomicCounterCompSwap: + builder.addExtension("SPV_KHR_shader_atomic_counter_ops"); + builder.addCapability(spv::CapabilityAtomicStorageOps); + atomic = true; + break; + + case glslang::EOpAbsDifference: + case glslang::EOpAddSaturate: + case glslang::EOpSubSaturate: + case glslang::EOpAverage: + case glslang::EOpAverageRounded: + case glslang::EOpMul32x16: + builder.addCapability(spv::CapabilityIntegerFunctions2INTEL); + builder.addExtension("SPV_INTEL_shader_integer_functions2"); + binOp = node->getOp(); + break; + + case glslang::EOpIgnoreIntersection: + case glslang::EOpTerminateRay: + case glslang::EOpTrace: + case glslang::EOpExecuteCallable: + case glslang::EOpWritePackedPrimitiveIndices4x8NV: + noReturnValue = true; + break; + case glslang::EOpRayQueryInitialize: + case glslang::EOpRayQueryTerminate: + case glslang::EOpRayQueryGenerateIntersection: + case glslang::EOpRayQueryConfirmIntersection: + builder.addExtension("SPV_KHR_ray_query"); + builder.addCapability(spv::CapabilityRayQueryProvisionalKHR); + noReturnValue = true; + break; + case glslang::EOpRayQueryProceed: + case glslang::EOpRayQueryGetIntersectionType: + case glslang::EOpRayQueryGetRayTMin: + case glslang::EOpRayQueryGetRayFlags: + case glslang::EOpRayQueryGetIntersectionT: + case glslang::EOpRayQueryGetIntersectionInstanceCustomIndex: + case glslang::EOpRayQueryGetIntersectionInstanceId: + case glslang::EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: + case glslang::EOpRayQueryGetIntersectionGeometryIndex: + case glslang::EOpRayQueryGetIntersectionPrimitiveIndex: + case glslang::EOpRayQueryGetIntersectionBarycentrics: + case glslang::EOpRayQueryGetIntersectionFrontFace: + case glslang::EOpRayQueryGetIntersectionCandidateAABBOpaque: + case glslang::EOpRayQueryGetIntersectionObjectRayDirection: + case glslang::EOpRayQueryGetIntersectionObjectRayOrigin: + case glslang::EOpRayQueryGetWorldRayDirection: + case glslang::EOpRayQueryGetWorldRayOrigin: + case glslang::EOpRayQueryGetIntersectionObjectToWorld: + case glslang::EOpRayQueryGetIntersectionWorldToObject: + builder.addExtension("SPV_KHR_ray_query"); + builder.addCapability(spv::CapabilityRayQueryProvisionalKHR); + break; + case glslang::EOpCooperativeMatrixLoad: + case glslang::EOpCooperativeMatrixStore: + noReturnValue = true; + break; + case glslang::EOpBeginInvocationInterlock: + case glslang::EOpEndInvocationInterlock: + builder.addExtension(spv::E_SPV_EXT_fragment_shader_interlock); + noReturnValue = true; + break; +#endif + + case glslang::EOpDebugPrintf: + noReturnValue = true; + break; + + default: + break; + } + + // + // See if it maps to a regular operation. + // + if (binOp != glslang::EOpNull) { + glslang::TIntermTyped* left = node->getSequence()[0]->getAsTyped(); + glslang::TIntermTyped* right = node->getSequence()[1]->getAsTyped(); + assert(left && right); + + builder.clearAccessChain(); + left->traverse(this); + spv::Id leftId = accessChainLoad(left->getType()); + + builder.clearAccessChain(); + right->traverse(this); + spv::Id rightId = accessChainLoad(right->getType()); + + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + OpDecorations decorations = { precision, + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + result = createBinaryOperation(binOp, decorations, + resultType(), leftId, rightId, + left->getType().getBasicType(), reduceComparison); + + // code above should only make binOp that exists in createBinaryOperation + assert(result != spv::NoResult); + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; + } + + // + // Create the list of operands. + // + glslang::TIntermSequence& glslangOperands = node->getSequence(); + std::vector operands; + std::vector memoryAccessOperands; + for (int arg = 0; arg < (int)glslangOperands.size(); ++arg) { + // special case l-value operands; there are just a few + bool lvalue = false; + switch (node->getOp()) { + case glslang::EOpModf: + if (arg == 1) + lvalue = true; + break; + + case glslang::EOpRayQueryInitialize: + case glslang::EOpRayQueryTerminate: + case glslang::EOpRayQueryConfirmIntersection: + case glslang::EOpRayQueryProceed: + case glslang::EOpRayQueryGenerateIntersection: + case glslang::EOpRayQueryGetIntersectionType: + case glslang::EOpRayQueryGetIntersectionT: + case glslang::EOpRayQueryGetIntersectionInstanceCustomIndex: + case glslang::EOpRayQueryGetIntersectionInstanceId: + case glslang::EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: + case glslang::EOpRayQueryGetIntersectionGeometryIndex: + case glslang::EOpRayQueryGetIntersectionPrimitiveIndex: + case glslang::EOpRayQueryGetIntersectionBarycentrics: + case glslang::EOpRayQueryGetIntersectionFrontFace: + case glslang::EOpRayQueryGetIntersectionObjectRayDirection: + case glslang::EOpRayQueryGetIntersectionObjectRayOrigin: + case glslang::EOpRayQueryGetIntersectionObjectToWorld: + case glslang::EOpRayQueryGetIntersectionWorldToObject: + if (arg == 0) + lvalue = true; + break; + + case glslang::EOpAtomicAdd: + case glslang::EOpAtomicMin: + case glslang::EOpAtomicMax: + case glslang::EOpAtomicAnd: + case glslang::EOpAtomicOr: + case glslang::EOpAtomicXor: + case glslang::EOpAtomicExchange: + case glslang::EOpAtomicCompSwap: + if (arg == 0) + lvalue = true; + break; + +#ifndef GLSLANG_WEB + case glslang::EOpFrexp: + if (arg == 1) + lvalue = true; + break; + case glslang::EOpInterpolateAtSample: + case glslang::EOpInterpolateAtOffset: + case glslang::EOpInterpolateAtVertex: + if (arg == 0) { + lvalue = true; + + // Does it need a swizzle inversion? If so, evaluation is inverted; + // operate first on the swizzle base, then apply the swizzle. + // That is, we transform + // + // interpolate(v.zy) -> interpolate(v).zy + // + if (glslangOperands[0]->getAsOperator() && + glslangOperands[0]->getAsOperator()->getOp() == glslang::EOpVectorSwizzle) + invertedType = convertGlslangToSpvType( + glslangOperands[0]->getAsBinaryNode()->getLeft()->getType()); + } + break; + case glslang::EOpAtomicLoad: + case glslang::EOpAtomicStore: + case glslang::EOpAtomicCounterAdd: + case glslang::EOpAtomicCounterSubtract: + case glslang::EOpAtomicCounterMin: + case glslang::EOpAtomicCounterMax: + case glslang::EOpAtomicCounterAnd: + case glslang::EOpAtomicCounterOr: + case glslang::EOpAtomicCounterXor: + case glslang::EOpAtomicCounterExchange: + case glslang::EOpAtomicCounterCompSwap: + if (arg == 0) + lvalue = true; + break; + case glslang::EOpAddCarry: + case glslang::EOpSubBorrow: + if (arg == 2) + lvalue = true; + break; + case glslang::EOpUMulExtended: + case glslang::EOpIMulExtended: + if (arg >= 2) + lvalue = true; + break; + case glslang::EOpCooperativeMatrixLoad: + if (arg == 0 || arg == 1) + lvalue = true; + break; + case glslang::EOpCooperativeMatrixStore: + if (arg == 1) + lvalue = true; + break; +#endif + default: + break; + } + builder.clearAccessChain(); + if (invertedType != spv::NoType && arg == 0) + glslangOperands[0]->getAsBinaryNode()->getLeft()->traverse(this); + else + glslangOperands[arg]->traverse(this); + +#ifndef GLSLANG_WEB + if (node->getOp() == glslang::EOpCooperativeMatrixLoad || + node->getOp() == glslang::EOpCooperativeMatrixStore) { + + if (arg == 1) { + // fold "element" parameter into the access chain + spv::Builder::AccessChain save = builder.getAccessChain(); + builder.clearAccessChain(); + glslangOperands[2]->traverse(this); + + spv::Id elementId = accessChainLoad(glslangOperands[2]->getAsTyped()->getType()); + + builder.setAccessChain(save); + + // Point to the first element of the array. + builder.accessChainPush(elementId, + TranslateCoherent(glslangOperands[arg]->getAsTyped()->getType()), + glslangOperands[arg]->getAsTyped()->getType().getBufferReferenceAlignment()); + + spv::Builder::AccessChain::CoherentFlags coherentFlags = builder.getAccessChain().coherentFlags; + unsigned int alignment = builder.getAccessChain().alignment; + + int memoryAccess = TranslateMemoryAccess(coherentFlags); + if (node->getOp() == glslang::EOpCooperativeMatrixLoad) + memoryAccess &= ~spv::MemoryAccessMakePointerAvailableKHRMask; + if (node->getOp() == glslang::EOpCooperativeMatrixStore) + memoryAccess &= ~spv::MemoryAccessMakePointerVisibleKHRMask; + if (builder.getStorageClass(builder.getAccessChain().base) == + spv::StorageClassPhysicalStorageBufferEXT) { + memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); + } + + memoryAccessOperands.push_back(spv::IdImmediate(false, memoryAccess)); + + if (memoryAccess & spv::MemoryAccessAlignedMask) { + memoryAccessOperands.push_back(spv::IdImmediate(false, alignment)); + } + + if (memoryAccess & + (spv::MemoryAccessMakePointerAvailableKHRMask | spv::MemoryAccessMakePointerVisibleKHRMask)) { + memoryAccessOperands.push_back(spv::IdImmediate(true, + builder.makeUintConstant(TranslateMemoryScope(coherentFlags)))); + } + } else if (arg == 2) { + continue; + } + } +#endif + + // for l-values, pass the address, for r-values, pass the value + if (lvalue) { + if (invertedType == spv::NoType && !builder.isSpvLvalue()) { + // SPIR-V cannot represent an l-value containing a swizzle that doesn't + // reduce to a simple access chain. So, we need a temporary vector to + // receive the result, and must later swizzle that into the original + // l-value. + complexLvalues.push_back(builder.getAccessChain()); + temporaryLvalues.push_back(builder.createVariable(spv::StorageClassFunction, + builder.accessChainGetInferredType(), "swizzleTemp")); + operands.push_back(temporaryLvalues.back()); + } else { + operands.push_back(builder.accessChainGetLValue()); + } + lvalueCoherentFlags = builder.getAccessChain().coherentFlags; + lvalueCoherentFlags |= TranslateCoherent(glslangOperands[arg]->getAsTyped()->getType()); + } else { + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + glslang::TOperator glslangOp = node->getOp(); + if (arg == 1 && + (glslangOp == glslang::EOpRayQueryGetIntersectionType || + glslangOp == glslang::EOpRayQueryGetIntersectionT || + glslangOp == glslang::EOpRayQueryGetIntersectionInstanceCustomIndex || + glslangOp == glslang::EOpRayQueryGetIntersectionInstanceId || + glslangOp == glslang::EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset || + glslangOp == glslang::EOpRayQueryGetIntersectionGeometryIndex || + glslangOp == glslang::EOpRayQueryGetIntersectionPrimitiveIndex || + glslangOp == glslang::EOpRayQueryGetIntersectionBarycentrics || + glslangOp == glslang::EOpRayQueryGetIntersectionFrontFace || + glslangOp == glslang::EOpRayQueryGetIntersectionObjectRayDirection || + glslangOp == glslang::EOpRayQueryGetIntersectionObjectRayOrigin || + glslangOp == glslang::EOpRayQueryGetIntersectionObjectToWorld || + glslangOp == glslang::EOpRayQueryGetIntersectionWorldToObject + )) { + bool cond = glslangOperands[arg]->getAsConstantUnion()->getConstArray()[0].getBConst(); + operands.push_back(builder.makeIntConstant(cond ? 1 : 0)); + } + else { + operands.push_back(accessChainLoad(glslangOperands[arg]->getAsTyped()->getType())); + } + + } + } + + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); +#ifndef GLSLANG_WEB + if (node->getOp() == glslang::EOpCooperativeMatrixLoad) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // buf + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // stride + idImmOps.push_back(spv::IdImmediate(true, operands[3])); // colMajor + idImmOps.insert(idImmOps.end(), memoryAccessOperands.begin(), memoryAccessOperands.end()); + // get the pointee type + spv::Id typeId = builder.getContainedTypeId(builder.getTypeId(operands[0])); + assert(builder.isCooperativeMatrixType(typeId)); + // do the op + spv::Id result = builder.createOp(spv::OpCooperativeMatrixLoadNV, typeId, idImmOps); + // store the result to the pointer (out param 'm') + builder.createStore(result, operands[0]); + result = 0; + } else if (node->getOp() == glslang::EOpCooperativeMatrixStore) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // buf + idImmOps.push_back(spv::IdImmediate(true, operands[0])); // object + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // stride + idImmOps.push_back(spv::IdImmediate(true, operands[3])); // colMajor + idImmOps.insert(idImmOps.end(), memoryAccessOperands.begin(), memoryAccessOperands.end()); + + builder.createNoResultOp(spv::OpCooperativeMatrixStoreNV, idImmOps); + result = 0; + } else +#endif + if (atomic) { + // Handle all atomics + result = createAtomicOperation(node->getOp(), precision, resultType(), operands, node->getBasicType(), + lvalueCoherentFlags); + } else if (node->getOp() == glslang::EOpDebugPrintf) { + if (!nonSemanticDebugPrintf) { + nonSemanticDebugPrintf = builder.import("NonSemantic.DebugPrintf"); + } + result = builder.createBuiltinCall(builder.makeVoidType(), nonSemanticDebugPrintf, spv::NonSemanticDebugPrintfDebugPrintf, operands); + builder.addExtension(spv::E_SPV_KHR_non_semantic_info); + } else { + // Pass through to generic operations. + switch (glslangOperands.size()) { + case 0: + result = createNoArgOperation(node->getOp(), precision, resultType()); + break; + case 1: + { + OpDecorations decorations = { precision, + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + result = createUnaryOperation( + node->getOp(), decorations, + resultType(), operands.front(), + glslangOperands[0]->getAsTyped()->getBasicType(), lvalueCoherentFlags); + } + break; + default: + result = createMiscOperation(node->getOp(), precision, resultType(), operands, node->getBasicType()); + break; + } + + if (invertedType != spv::NoResult) + result = createInvertedSwizzle(precision, *glslangOperands[0]->getAsBinaryNode(), result); + + for (unsigned int i = 0; i < temporaryLvalues.size(); ++i) { + builder.setAccessChain(complexLvalues[i]); + builder.accessChainStore(builder.createLoad(temporaryLvalues[i])); + } + } + + if (noReturnValue) + return false; + + if (! result) { + logger->missingFunctionality("unknown glslang aggregate"); + return true; // pick up a child as a placeholder operand + } else { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + return false; + } +} + +// This path handles both if-then-else and ?: +// The if-then-else has a node type of void, while +// ?: has either a void or a non-void node type +// +// Leaving the result, when not void: +// GLSL only has r-values as the result of a :?, but +// if we have an l-value, that can be more efficient if it will +// become the base of a complex r-value expression, because the +// next layer copies r-values into memory to use the access-chain mechanism +bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang::TIntermSelection* node) +{ + // see if OpSelect can handle it + const auto isOpSelectable = [&]() { + if (node->getBasicType() == glslang::EbtVoid) + return false; + // OpSelect can do all other types starting with SPV 1.4 + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_4) { + // pre-1.4, only scalars and vectors can be handled + if ((!node->getType().isScalar() && !node->getType().isVector())) + return false; + } + return true; + }; + + // See if it simple and safe, or required, to execute both sides. + // Crucially, side effects must be either semantically required or avoided, + // and there are performance trade-offs. + // Return true if required or a good idea (and safe) to execute both sides, + // false otherwise. + const auto bothSidesPolicy = [&]() -> bool { + // do we have both sides? + if (node->getTrueBlock() == nullptr || + node->getFalseBlock() == nullptr) + return false; + + // required? (unless we write additional code to look for side effects + // and make performance trade-offs if none are present) + if (!node->getShortCircuit()) + return true; + + // if not required to execute both, decide based on performance/practicality... + + if (!isOpSelectable()) + return false; + + assert(node->getType() == node->getTrueBlock() ->getAsTyped()->getType() && + node->getType() == node->getFalseBlock()->getAsTyped()->getType()); + + // return true if a single operand to ? : is okay for OpSelect + const auto operandOkay = [](glslang::TIntermTyped* node) { + return node->getAsSymbolNode() || node->getType().getQualifier().isConstant(); + }; + + return operandOkay(node->getTrueBlock() ->getAsTyped()) && + operandOkay(node->getFalseBlock()->getAsTyped()); + }; + + spv::Id result = spv::NoResult; // upcoming result selecting between trueValue and falseValue + // emit the condition before doing anything with selection + node->getCondition()->traverse(this); + spv::Id condition = accessChainLoad(node->getCondition()->getType()); + + // Find a way of executing both sides and selecting the right result. + const auto executeBothSides = [&]() -> void { + // execute both sides + node->getTrueBlock()->traverse(this); + spv::Id trueValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()); + node->getFalseBlock()->traverse(this); + spv::Id falseValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()); + + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + + // done if void + if (node->getBasicType() == glslang::EbtVoid) + return; + + // emit code to select between trueValue and falseValue + + // see if OpSelect can handle it + if (isOpSelectable()) { + // Emit OpSelect for this selection. + + // smear condition to vector, if necessary (AST is always scalar) + // Before 1.4, smear like for mix(), starting with 1.4, keep it scalar + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_4 && builder.isVector(trueValue)) { + condition = builder.smearScalar(spv::NoPrecision, condition, + builder.makeVectorType(builder.makeBoolType(), + builder.getNumComponents(trueValue))); + } + + // OpSelect + result = builder.createTriOp(spv::OpSelect, + convertGlslangToSpvType(node->getType()), condition, + trueValue, falseValue); + + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + } else { + // We need control flow to select the result. + // TODO: Once SPIR-V OpSelect allows arbitrary types, eliminate this path. + result = builder.createVariable(spv::StorageClassFunction, convertGlslangToSpvType(node->getType())); + + // Selection control: + const spv::SelectionControlMask control = TranslateSelectionControl(*node); + + // make an "if" based on the value created by the condition + spv::Builder::If ifBuilder(condition, control, builder); + + // emit the "then" statement + builder.createStore(trueValue, result); + ifBuilder.makeBeginElse(); + // emit the "else" statement + builder.createStore(falseValue, result); + + // finish off the control flow + ifBuilder.makeEndIf(); + + builder.clearAccessChain(); + builder.setAccessChainLValue(result); + } + }; + + // Execute the one side needed, as per the condition + const auto executeOneSide = [&]() { + // Always emit control flow. + if (node->getBasicType() != glslang::EbtVoid) + result = builder.createVariable(spv::StorageClassFunction, convertGlslangToSpvType(node->getType())); + + // Selection control: + const spv::SelectionControlMask control = TranslateSelectionControl(*node); + + // make an "if" based on the value created by the condition + spv::Builder::If ifBuilder(condition, control, builder); + + // emit the "then" statement + if (node->getTrueBlock() != nullptr) { + node->getTrueBlock()->traverse(this); + if (result != spv::NoResult) + builder.createStore(accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()), result); + } + + if (node->getFalseBlock() != nullptr) { + ifBuilder.makeBeginElse(); + // emit the "else" statement + node->getFalseBlock()->traverse(this); + if (result != spv::NoResult) + builder.createStore(accessChainLoad(node->getFalseBlock()->getAsTyped()->getType()), result); + } + + // finish off the control flow + ifBuilder.makeEndIf(); + + if (result != spv::NoResult) { + builder.clearAccessChain(); + builder.setAccessChainLValue(result); + } + }; + + // Try for OpSelect (or a requirement to execute both sides) + if (bothSidesPolicy()) { + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + executeBothSides(); + } else + executeOneSide(); + + return false; +} + +bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::TIntermSwitch* node) +{ + // emit and get the condition before doing anything with switch + node->getCondition()->traverse(this); + spv::Id selector = accessChainLoad(node->getCondition()->getAsTyped()->getType()); + + // Selection control: + const spv::SelectionControlMask control = TranslateSwitchControl(*node); + + // browse the children to sort out code segments + int defaultSegment = -1; + std::vector codeSegments; + glslang::TIntermSequence& sequence = node->getBody()->getSequence(); + std::vector caseValues; + std::vector valueIndexToSegment(sequence.size()); // note: probably not all are used, it is an overestimate + for (glslang::TIntermSequence::iterator c = sequence.begin(); c != sequence.end(); ++c) { + TIntermNode* child = *c; + if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpDefault) + defaultSegment = (int)codeSegments.size(); + else if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpCase) { + valueIndexToSegment[caseValues.size()] = (int)codeSegments.size(); + caseValues.push_back(child->getAsBranchNode()->getExpression()->getAsConstantUnion() + ->getConstArray()[0].getIConst()); + } else + codeSegments.push_back(child); + } + + // handle the case where the last code segment is missing, due to no code + // statements between the last case and the end of the switch statement + if ((caseValues.size() && (int)codeSegments.size() == valueIndexToSegment[caseValues.size() - 1]) || + (int)codeSegments.size() == defaultSegment) + codeSegments.push_back(nullptr); + + // make the switch statement + std::vector segmentBlocks; // returned, as the blocks allocated in the call + builder.makeSwitch(selector, control, (int)codeSegments.size(), caseValues, valueIndexToSegment, defaultSegment, + segmentBlocks); + + // emit all the code in the segments + breakForLoop.push(false); + for (unsigned int s = 0; s < codeSegments.size(); ++s) { + builder.nextSwitchSegment(segmentBlocks, s); + if (codeSegments[s]) + codeSegments[s]->traverse(this); + else + builder.addSwitchBreak(); + } + breakForLoop.pop(); + + builder.endSwitch(segmentBlocks); + + return false; +} + +void TGlslangToSpvTraverser::visitConstantUnion(glslang::TIntermConstantUnion* node) +{ + int nextConst = 0; + spv::Id constant = createSpvConstantFromConstUnionArray(node->getType(), node->getConstArray(), nextConst, false); + + builder.clearAccessChain(); + builder.setAccessChainRValue(constant); +} + +bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIntermLoop* node) +{ + auto blocks = builder.makeNewLoop(); + builder.createBranch(&blocks.head); + + // Loop control: + std::vector operands; + const spv::LoopControlMask control = TranslateLoopControl(*node, operands); + + // Spec requires back edges to target header blocks, and every header block + // must dominate its merge block. Make a header block first to ensure these + // conditions are met. By definition, it will contain OpLoopMerge, followed + // by a block-ending branch. But we don't want to put any other body/test + // instructions in it, since the body/test may have arbitrary instructions, + // including merges of its own. + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + builder.setBuildPoint(&blocks.head); + builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control, operands); + if (node->testFirst() && node->getTest()) { + spv::Block& test = builder.makeNewBlock(); + builder.createBranch(&test); + + builder.setBuildPoint(&test); + node->getTest()->traverse(this); + spv::Id condition = accessChainLoad(node->getTest()->getType()); + builder.createConditionalBranch(condition, &blocks.body, &blocks.merge); + + builder.setBuildPoint(&blocks.body); + breakForLoop.push(true); + if (node->getBody()) + node->getBody()->traverse(this); + builder.createBranch(&blocks.continue_target); + breakForLoop.pop(); + + builder.setBuildPoint(&blocks.continue_target); + if (node->getTerminal()) + node->getTerminal()->traverse(this); + builder.createBranch(&blocks.head); + } else { + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + builder.createBranch(&blocks.body); + + breakForLoop.push(true); + builder.setBuildPoint(&blocks.body); + if (node->getBody()) + node->getBody()->traverse(this); + builder.createBranch(&blocks.continue_target); + breakForLoop.pop(); + + builder.setBuildPoint(&blocks.continue_target); + if (node->getTerminal()) + node->getTerminal()->traverse(this); + if (node->getTest()) { + node->getTest()->traverse(this); + spv::Id condition = + accessChainLoad(node->getTest()->getType()); + builder.createConditionalBranch(condition, &blocks.head, &blocks.merge); + } else { + // TODO: unless there was a break/return/discard instruction + // somewhere in the body, this is an infinite loop, so we should + // issue a warning. + builder.createBranch(&blocks.head); + } + } + builder.setBuildPoint(&blocks.merge); + builder.closeLoop(); + return false; +} + +bool TGlslangToSpvTraverser::visitBranch(glslang::TVisit /* visit */, glslang::TIntermBranch* node) +{ + if (node->getExpression()) + node->getExpression()->traverse(this); + + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + + switch (node->getFlowOp()) { + case glslang::EOpKill: + builder.makeDiscard(); + break; + case glslang::EOpBreak: + if (breakForLoop.top()) + builder.createLoopExit(); + else + builder.addSwitchBreak(); + break; + case glslang::EOpContinue: + builder.createLoopContinue(); + break; + case glslang::EOpReturn: + if (node->getExpression()) { + const glslang::TType& glslangReturnType = node->getExpression()->getType(); + spv::Id returnId = accessChainLoad(glslangReturnType); + if (builder.getTypeId(returnId) != currentFunction->getReturnType()) { + builder.clearAccessChain(); + spv::Id copyId = builder.createVariable(spv::StorageClassFunction, currentFunction->getReturnType()); + builder.setAccessChainLValue(copyId); + multiTypeStore(glslangReturnType, returnId); + returnId = builder.createLoad(copyId); + } + builder.makeReturn(false, returnId); + } else + builder.makeReturn(false); + + builder.clearAccessChain(); + break; + +#ifndef GLSLANG_WEB + case glslang::EOpDemote: + builder.createNoResultOp(spv::OpDemoteToHelperInvocationEXT); + builder.addExtension(spv::E_SPV_EXT_demote_to_helper_invocation); + builder.addCapability(spv::CapabilityDemoteToHelperInvocationEXT); + break; +#endif + + default: + assert(0); + break; + } + + return false; +} + +spv::Id TGlslangToSpvTraverser::createSpvVariable(const glslang::TIntermSymbol* node, spv::Id forcedType) +{ + // First, steer off constants, which are not SPIR-V variables, but + // can still have a mapping to a SPIR-V Id. + // This includes specialization constants. + if (node->getQualifier().isConstant()) { + spv::Id result = createSpvConstant(*node); + if (result != spv::NoResult) + return result; + } + + // Now, handle actual variables + spv::StorageClass storageClass = TranslateStorageClass(node->getType()); + spv::Id spvType = forcedType == spv::NoType ? convertGlslangToSpvType(node->getType()) + : forcedType; + + const bool contains16BitType = node->getType().contains16BitFloat() || + node->getType().contains16BitInt(); + if (contains16BitType) { + switch (storageClass) { + case spv::StorageClassInput: + case spv::StorageClassOutput: + builder.addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + builder.addCapability(spv::CapabilityStorageInputOutput16); + break; + case spv::StorageClassUniform: + builder.addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + if (node->getType().getQualifier().storage == glslang::EvqBuffer) + builder.addCapability(spv::CapabilityStorageUniformBufferBlock16); + else + builder.addCapability(spv::CapabilityStorageUniform16); + break; +#ifndef GLSLANG_WEB + case spv::StorageClassPushConstant: + builder.addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + builder.addCapability(spv::CapabilityStoragePushConstant16); + break; + case spv::StorageClassStorageBuffer: + case spv::StorageClassPhysicalStorageBufferEXT: + builder.addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + builder.addCapability(spv::CapabilityStorageUniformBufferBlock16); + break; +#endif + default: + if (node->getType().contains16BitFloat()) + builder.addCapability(spv::CapabilityFloat16); + if (node->getType().contains16BitInt()) + builder.addCapability(spv::CapabilityInt16); + break; + } + } + + if (node->getType().contains8BitInt()) { + if (storageClass == spv::StorageClassPushConstant) { + builder.addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5); + builder.addCapability(spv::CapabilityStoragePushConstant8); + } else if (storageClass == spv::StorageClassUniform) { + builder.addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5); + builder.addCapability(spv::CapabilityUniformAndStorageBuffer8BitAccess); + } else if (storageClass == spv::StorageClassStorageBuffer) { + builder.addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5); + builder.addCapability(spv::CapabilityStorageBuffer8BitAccess); + } else { + builder.addCapability(spv::CapabilityInt8); + } + } + + const char* name = node->getName().c_str(); + if (glslang::IsAnonymous(name)) + name = ""; + + return builder.createVariable(storageClass, spvType, name); +} + +// Return type Id of the sampled type. +spv::Id TGlslangToSpvTraverser::getSampledType(const glslang::TSampler& sampler) +{ + switch (sampler.type) { + case glslang::EbtInt: return builder.makeIntType(32); + case glslang::EbtUint: return builder.makeUintType(32); + case glslang::EbtFloat: return builder.makeFloatType(32); +#ifndef GLSLANG_WEB + case glslang::EbtFloat16: + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float_fetch); + builder.addCapability(spv::CapabilityFloat16ImageAMD); + return builder.makeFloatType(16); +#endif + default: + assert(0); + return builder.makeFloatType(32); + } +} + +// If node is a swizzle operation, return the type that should be used if +// the swizzle base is first consumed by another operation, before the swizzle +// is applied. +spv::Id TGlslangToSpvTraverser::getInvertedSwizzleType(const glslang::TIntermTyped& node) +{ + if (node.getAsOperator() && + node.getAsOperator()->getOp() == glslang::EOpVectorSwizzle) + return convertGlslangToSpvType(node.getAsBinaryNode()->getLeft()->getType()); + else + return spv::NoType; +} + +// When inverting a swizzle with a parent op, this function +// will apply the swizzle operation to a completed parent operation. +spv::Id TGlslangToSpvTraverser::createInvertedSwizzle(spv::Decoration precision, const glslang::TIntermTyped& node, + spv::Id parentResult) +{ + std::vector swizzle; + convertSwizzle(*node.getAsBinaryNode()->getRight()->getAsAggregate(), swizzle); + return builder.createRvalueSwizzle(precision, convertGlslangToSpvType(node.getType()), parentResult, swizzle); +} + +// Convert a glslang AST swizzle node to a swizzle vector for building SPIR-V. +void TGlslangToSpvTraverser::convertSwizzle(const glslang::TIntermAggregate& node, std::vector& swizzle) +{ + const glslang::TIntermSequence& swizzleSequence = node.getSequence(); + for (int i = 0; i < (int)swizzleSequence.size(); ++i) + swizzle.push_back(swizzleSequence[i]->getAsConstantUnion()->getConstArray()[0].getIConst()); +} + +// Convert from a glslang type to an SPV type, by calling into a +// recursive version of this function. This establishes the inherited +// layout state rooted from the top-level type. +spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type, bool forwardReferenceOnly) +{ + return convertGlslangToSpvType(type, getExplicitLayout(type), type.getQualifier(), false, forwardReferenceOnly); +} + +// Do full recursive conversion of an arbitrary glslang type to a SPIR-V Id. +// explicitLayout can be kept the same throughout the hierarchical recursive walk. +// Mutually recursive with convertGlslangStructToSpvType(). +spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type, + glslang::TLayoutPacking explicitLayout, const glslang::TQualifier& qualifier, + bool lastBufferBlockMember, bool forwardReferenceOnly) +{ + spv::Id spvType = spv::NoResult; + + switch (type.getBasicType()) { + case glslang::EbtVoid: + spvType = builder.makeVoidType(); + assert (! type.isArray()); + break; + case glslang::EbtBool: + // "transparent" bool doesn't exist in SPIR-V. The GLSL convention is + // a 32-bit int where non-0 means true. + if (explicitLayout != glslang::ElpNone) + spvType = builder.makeUintType(32); + else + spvType = builder.makeBoolType(); + break; + case glslang::EbtInt: + spvType = builder.makeIntType(32); + break; + case glslang::EbtUint: + spvType = builder.makeUintType(32); + break; + case glslang::EbtFloat: + spvType = builder.makeFloatType(32); + break; +#ifndef GLSLANG_WEB + case glslang::EbtDouble: + spvType = builder.makeFloatType(64); + break; + case glslang::EbtFloat16: + spvType = builder.makeFloatType(16); + break; + case glslang::EbtInt8: + spvType = builder.makeIntType(8); + break; + case glslang::EbtUint8: + spvType = builder.makeUintType(8); + break; + case glslang::EbtInt16: + spvType = builder.makeIntType(16); + break; + case glslang::EbtUint16: + spvType = builder.makeUintType(16); + break; + case glslang::EbtInt64: + spvType = builder.makeIntType(64); + break; + case glslang::EbtUint64: + spvType = builder.makeUintType(64); + break; + case glslang::EbtAtomicUint: + builder.addCapability(spv::CapabilityAtomicStorage); + spvType = builder.makeUintType(32); + break; + case glslang::EbtAccStruct: + spvType = builder.makeAccelerationStructureType(); + break; + case glslang::EbtRayQuery: + spvType = builder.makeRayQueryType(); + break; + case glslang::EbtReference: + { + // Make the forward pointer, then recurse to convert the structure type, then + // patch up the forward pointer with a real pointer type. + if (forwardPointers.find(type.getReferentType()) == forwardPointers.end()) { + spv::Id forwardId = builder.makeForwardPointer(spv::StorageClassPhysicalStorageBufferEXT); + forwardPointers[type.getReferentType()] = forwardId; + } + spvType = forwardPointers[type.getReferentType()]; + if (!forwardReferenceOnly) { + spv::Id referentType = convertGlslangToSpvType(*type.getReferentType()); + builder.makePointerFromForwardPointer(spv::StorageClassPhysicalStorageBufferEXT, + forwardPointers[type.getReferentType()], + referentType); + } + } + break; +#endif + case glslang::EbtSampler: + { + const glslang::TSampler& sampler = type.getSampler(); + if (sampler.isPureSampler()) { + spvType = builder.makeSamplerType(); + } else { + // an image is present, make its type + spvType = builder.makeImageType(getSampledType(sampler), TranslateDimensionality(sampler), + sampler.isShadow(), sampler.isArrayed(), sampler.isMultiSample(), + sampler.isImageClass() ? 2 : 1, TranslateImageFormat(type), sampler.isVideo()); + if (sampler.isCombined()) { + // already has both image and sampler, make the combined type + spvType = builder.makeSampledImageType(spvType); + } + } + } + break; + case glslang::EbtStruct: + case glslang::EbtBlock: + { + // If we've seen this struct type, return it + const glslang::TTypeList* glslangMembers = type.getStruct(); + + // Try to share structs for different layouts, but not yet for other + // kinds of qualification (primarily not yet including interpolant qualification). + if (! HasNonLayoutQualifiers(type, qualifier)) + spvType = structMap[explicitLayout][qualifier.layoutMatrix][glslangMembers]; + if (spvType != spv::NoResult) + break; + + // else, we haven't seen it... + if (type.getBasicType() == glslang::EbtBlock) + memberRemapper[glslangTypeToIdMap[glslangMembers]].resize(glslangMembers->size()); + spvType = convertGlslangStructToSpvType(type, glslangMembers, explicitLayout, qualifier); + } + break; + case glslang::EbtString: + // no type used for OpString + return 0; + default: + assert(0); + break; + } + + if (type.isMatrix()) + spvType = builder.makeMatrixType(spvType, type.getMatrixCols(), type.getMatrixRows()); + else { + // If this variable has a vector element count greater than 1, create a SPIR-V vector + if (type.getVectorSize() > 1) + spvType = builder.makeVectorType(spvType, type.getVectorSize()); + } + + if (type.isCoopMat()) { + builder.addCapability(spv::CapabilityCooperativeMatrixNV); + builder.addExtension(spv::E_SPV_NV_cooperative_matrix); + if (type.getBasicType() == glslang::EbtFloat16) + builder.addCapability(spv::CapabilityFloat16); + if (type.getBasicType() == glslang::EbtUint8 || + type.getBasicType() == glslang::EbtInt8) { + builder.addCapability(spv::CapabilityInt8); + } + + spv::Id scope = makeArraySizeId(*type.getTypeParameters(), 1); + spv::Id rows = makeArraySizeId(*type.getTypeParameters(), 2); + spv::Id cols = makeArraySizeId(*type.getTypeParameters(), 3); + + spvType = builder.makeCooperativeMatrixType(spvType, scope, rows, cols); + } + + if (type.isArray()) { + int stride = 0; // keep this 0 unless doing an explicit layout; 0 will mean no decoration, no stride + + // Do all but the outer dimension + if (type.getArraySizes()->getNumDims() > 1) { + // We need to decorate array strides for types needing explicit layout, except blocks. + if (explicitLayout != glslang::ElpNone && type.getBasicType() != glslang::EbtBlock) { + // Use a dummy glslang type for querying internal strides of + // arrays of arrays, but using just a one-dimensional array. + glslang::TType simpleArrayType(type, 0); // deference type of the array + while (simpleArrayType.getArraySizes()->getNumDims() > 1) + simpleArrayType.getArraySizes()->dereference(); + + // Will compute the higher-order strides here, rather than making a whole + // pile of types and doing repetitive recursion on their contents. + stride = getArrayStride(simpleArrayType, explicitLayout, qualifier.layoutMatrix); + } + + // make the arrays + for (int dim = type.getArraySizes()->getNumDims() - 1; dim > 0; --dim) { + spvType = builder.makeArrayType(spvType, makeArraySizeId(*type.getArraySizes(), dim), stride); + if (stride > 0) + builder.addDecoration(spvType, spv::DecorationArrayStride, stride); + stride *= type.getArraySizes()->getDimSize(dim); + } + } else { + // single-dimensional array, and don't yet have stride + + // We need to decorate array strides for types needing explicit layout, except blocks. + if (explicitLayout != glslang::ElpNone && type.getBasicType() != glslang::EbtBlock) + stride = getArrayStride(type, explicitLayout, qualifier.layoutMatrix); + } + + // Do the outer dimension, which might not be known for a runtime-sized array. + // (Unsized arrays that survive through linking will be runtime-sized arrays) + if (type.isSizedArray()) + spvType = builder.makeArrayType(spvType, makeArraySizeId(*type.getArraySizes(), 0), stride); + else { +#ifndef GLSLANG_WEB + if (!lastBufferBlockMember) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityRuntimeDescriptorArrayEXT); + } +#endif + spvType = builder.makeRuntimeArray(spvType); + } + if (stride > 0) + builder.addDecoration(spvType, spv::DecorationArrayStride, stride); + } + + return spvType; +} + +// TODO: this functionality should exist at a higher level, in creating the AST +// +// Identify interface members that don't have their required extension turned on. +// +bool TGlslangToSpvTraverser::filterMember(const glslang::TType& member) +{ +#ifndef GLSLANG_WEB + auto& extensions = glslangIntermediate->getRequestedExtensions(); + + if (member.getFieldName() == "gl_SecondaryViewportMaskNV" && + extensions.find("GL_NV_stereo_view_rendering") == extensions.end()) + return true; + if (member.getFieldName() == "gl_SecondaryPositionNV" && + extensions.find("GL_NV_stereo_view_rendering") == extensions.end()) + return true; + + if (glslangIntermediate->getStage() != EShLangMeshNV) { + if (member.getFieldName() == "gl_ViewportMask" && + extensions.find("GL_NV_viewport_array2") == extensions.end()) + return true; + if (member.getFieldName() == "gl_PositionPerViewNV" && + extensions.find("GL_NVX_multiview_per_view_attributes") == extensions.end()) + return true; + if (member.getFieldName() == "gl_ViewportMaskPerViewNV" && + extensions.find("GL_NVX_multiview_per_view_attributes") == extensions.end()) + return true; + } +#endif + + return false; +}; + +// Do full recursive conversion of a glslang structure (or block) type to a SPIR-V Id. +// explicitLayout can be kept the same throughout the hierarchical recursive walk. +// Mutually recursive with convertGlslangToSpvType(). +spv::Id TGlslangToSpvTraverser::convertGlslangStructToSpvType(const glslang::TType& type, + const glslang::TTypeList* glslangMembers, + glslang::TLayoutPacking explicitLayout, + const glslang::TQualifier& qualifier) +{ + // Create a vector of struct types for SPIR-V to consume + std::vector spvMembers; + int memberDelta = 0; // how much the member's index changes from glslang to SPIR-V, normally 0, + // except sometimes for blocks + std::vector > deferredForwardPointers; + for (int i = 0; i < (int)glslangMembers->size(); i++) { + glslang::TType& glslangMember = *(*glslangMembers)[i].type; + if (glslangMember.hiddenMember()) { + ++memberDelta; + if (type.getBasicType() == glslang::EbtBlock) + memberRemapper[glslangTypeToIdMap[glslangMembers]][i] = -1; + } else { + if (type.getBasicType() == glslang::EbtBlock) { + if (filterMember(glslangMember)) { + memberDelta++; + memberRemapper[glslangTypeToIdMap[glslangMembers]][i] = -1; + continue; + } + memberRemapper[glslangTypeToIdMap[glslangMembers]][i] = i - memberDelta; + } + // modify just this child's view of the qualifier + glslang::TQualifier memberQualifier = glslangMember.getQualifier(); + InheritQualifiers(memberQualifier, qualifier); + + // manually inherit location + if (! memberQualifier.hasLocation() && qualifier.hasLocation()) + memberQualifier.layoutLocation = qualifier.layoutLocation; + + // recurse + bool lastBufferBlockMember = qualifier.storage == glslang::EvqBuffer && + i == (int)glslangMembers->size() - 1; + + // Make forward pointers for any pointer members, and create a list of members to + // convert to spirv types after creating the struct. + if (glslangMember.isReference()) { + if (forwardPointers.find(glslangMember.getReferentType()) == forwardPointers.end()) { + deferredForwardPointers.push_back(std::make_pair(&glslangMember, memberQualifier)); + } + spvMembers.push_back( + convertGlslangToSpvType(glslangMember, explicitLayout, memberQualifier, lastBufferBlockMember, + true)); + } else { + spvMembers.push_back( + convertGlslangToSpvType(glslangMember, explicitLayout, memberQualifier, lastBufferBlockMember, + false)); + } + } + } + + // Make the SPIR-V type + spv::Id spvType = builder.makeStructType(spvMembers, type.getTypeName().c_str()); + if (! HasNonLayoutQualifiers(type, qualifier)) + structMap[explicitLayout][qualifier.layoutMatrix][glslangMembers] = spvType; + + // Decorate it + decorateStructType(type, glslangMembers, explicitLayout, qualifier, spvType); + + for (int i = 0; i < (int)deferredForwardPointers.size(); ++i) { + auto it = deferredForwardPointers[i]; + convertGlslangToSpvType(*it.first, explicitLayout, it.second, false); + } + + return spvType; +} + +void TGlslangToSpvTraverser::decorateStructType(const glslang::TType& type, + const glslang::TTypeList* glslangMembers, + glslang::TLayoutPacking explicitLayout, + const glslang::TQualifier& qualifier, + spv::Id spvType) +{ + // Name and decorate the non-hidden members + int offset = -1; + int locationOffset = 0; // for use within the members of this struct + for (int i = 0; i < (int)glslangMembers->size(); i++) { + glslang::TType& glslangMember = *(*glslangMembers)[i].type; + int member = i; + if (type.getBasicType() == glslang::EbtBlock) { + member = memberRemapper[glslangTypeToIdMap[glslangMembers]][i]; + if (filterMember(glslangMember)) + continue; + } + + // modify just this child's view of the qualifier + glslang::TQualifier memberQualifier = glslangMember.getQualifier(); + InheritQualifiers(memberQualifier, qualifier); + + // using -1 above to indicate a hidden member + if (member < 0) + continue; + + builder.addMemberName(spvType, member, glslangMember.getFieldName().c_str()); + builder.addMemberDecoration(spvType, member, + TranslateLayoutDecoration(glslangMember, memberQualifier.layoutMatrix)); + builder.addMemberDecoration(spvType, member, TranslatePrecisionDecoration(glslangMember)); + // Add interpolation and auxiliary storage decorations only to + // top-level members of Input and Output storage classes + if (type.getQualifier().storage == glslang::EvqVaryingIn || + type.getQualifier().storage == glslang::EvqVaryingOut) { + if (type.getBasicType() == glslang::EbtBlock || + glslangIntermediate->getSource() == glslang::EShSourceHlsl) { + builder.addMemberDecoration(spvType, member, TranslateInterpolationDecoration(memberQualifier)); + builder.addMemberDecoration(spvType, member, TranslateAuxiliaryStorageDecoration(memberQualifier)); +#ifndef GLSLANG_WEB + addMeshNVDecoration(spvType, member, memberQualifier); +#endif + } + } + builder.addMemberDecoration(spvType, member, TranslateInvariantDecoration(memberQualifier)); + +#ifndef GLSLANG_WEB + if (type.getBasicType() == glslang::EbtBlock && + qualifier.storage == glslang::EvqBuffer) { + // Add memory decorations only to top-level members of shader storage block + std::vector memory; + TranslateMemoryDecoration(memberQualifier, memory, glslangIntermediate->usingVulkanMemoryModel()); + for (unsigned int i = 0; i < memory.size(); ++i) + builder.addMemberDecoration(spvType, member, memory[i]); + } + +#endif + + // Location assignment was already completed correctly by the front end, + // just track whether a member needs to be decorated. + // Ignore member locations if the container is an array, as that's + // ill-specified and decisions have been made to not allow this. + if (! type.isArray() && memberQualifier.hasLocation()) + builder.addMemberDecoration(spvType, member, spv::DecorationLocation, memberQualifier.layoutLocation); + + if (qualifier.hasLocation()) // track for upcoming inheritance + locationOffset += glslangIntermediate->computeTypeLocationSize( + glslangMember, glslangIntermediate->getStage()); + + // component, XFB, others + if (glslangMember.getQualifier().hasComponent()) + builder.addMemberDecoration(spvType, member, spv::DecorationComponent, + glslangMember.getQualifier().layoutComponent); + if (glslangMember.getQualifier().hasXfbOffset()) + builder.addMemberDecoration(spvType, member, spv::DecorationOffset, + glslangMember.getQualifier().layoutXfbOffset); + else if (explicitLayout != glslang::ElpNone) { + // figure out what to do with offset, which is accumulating + int nextOffset; + updateMemberOffset(type, glslangMember, offset, nextOffset, explicitLayout, memberQualifier.layoutMatrix); + if (offset >= 0) + builder.addMemberDecoration(spvType, member, spv::DecorationOffset, offset); + offset = nextOffset; + } + + if (glslangMember.isMatrix() && explicitLayout != glslang::ElpNone) + builder.addMemberDecoration(spvType, member, spv::DecorationMatrixStride, + getMatrixStride(glslangMember, explicitLayout, memberQualifier.layoutMatrix)); + + // built-in variable decorations + spv::BuiltIn builtIn = TranslateBuiltInDecoration(glslangMember.getQualifier().builtIn, true); + if (builtIn != spv::BuiltInMax) + builder.addMemberDecoration(spvType, member, spv::DecorationBuiltIn, (int)builtIn); + +#ifndef GLSLANG_WEB + // nonuniform + builder.addMemberDecoration(spvType, member, TranslateNonUniformDecoration(glslangMember.getQualifier())); + + if (glslangIntermediate->getHlslFunctionality1() && memberQualifier.semanticName != nullptr) { + builder.addExtension("SPV_GOOGLE_hlsl_functionality1"); + builder.addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationHlslSemanticGOOGLE, + memberQualifier.semanticName); + } + + if (builtIn == spv::BuiltInLayer) { + // SPV_NV_viewport_array2 extension + if (glslangMember.getQualifier().layoutViewportRelative){ + builder.addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationViewportRelativeNV); + builder.addCapability(spv::CapabilityShaderViewportMaskNV); + builder.addExtension(spv::E_SPV_NV_viewport_array2); + } + if (glslangMember.getQualifier().layoutSecondaryViewportRelativeOffset != -2048){ + builder.addMemberDecoration(spvType, member, + (spv::Decoration)spv::DecorationSecondaryViewportRelativeNV, + glslangMember.getQualifier().layoutSecondaryViewportRelativeOffset); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + } + } + if (glslangMember.getQualifier().layoutPassthrough) { + builder.addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationPassthroughNV); + builder.addCapability(spv::CapabilityGeometryShaderPassthroughNV); + builder.addExtension(spv::E_SPV_NV_geometry_shader_passthrough); + } +#endif + } + + // Decorate the structure + builder.addDecoration(spvType, TranslateLayoutDecoration(type, qualifier.layoutMatrix)); + builder.addDecoration(spvType, TranslateBlockDecoration(type, glslangIntermediate->usingStorageBuffer())); +} + +// Turn the expression forming the array size into an id. +// This is not quite trivial, because of specialization constants. +// Sometimes, a raw constant is turned into an Id, and sometimes +// a specialization constant expression is. +spv::Id TGlslangToSpvTraverser::makeArraySizeId(const glslang::TArraySizes& arraySizes, int dim) +{ + // First, see if this is sized with a node, meaning a specialization constant: + glslang::TIntermTyped* specNode = arraySizes.getDimNode(dim); + if (specNode != nullptr) { + builder.clearAccessChain(); + specNode->traverse(this); + return accessChainLoad(specNode->getAsTyped()->getType()); + } + + // Otherwise, need a compile-time (front end) size, get it: + int size = arraySizes.getDimSize(dim); + assert(size > 0); + return builder.makeUintConstant(size); +} + +// Wrap the builder's accessChainLoad to: +// - localize handling of RelaxedPrecision +// - use the SPIR-V inferred type instead of another conversion of the glslang type +// (avoids unnecessary work and possible type punning for structures) +// - do conversion of concrete to abstract type +spv::Id TGlslangToSpvTraverser::accessChainLoad(const glslang::TType& type) +{ + spv::Id nominalTypeId = builder.accessChainGetInferredType(); + + spv::Builder::AccessChain::CoherentFlags coherentFlags = builder.getAccessChain().coherentFlags; + coherentFlags |= TranslateCoherent(type); + + unsigned int alignment = builder.getAccessChain().alignment; + alignment |= type.getBufferReferenceAlignment(); + + spv::Id loadedId = builder.accessChainLoad(TranslatePrecisionDecoration(type), + TranslateNonUniformDecoration(type.getQualifier()), + nominalTypeId, + spv::MemoryAccessMask(TranslateMemoryAccess(coherentFlags) & ~spv::MemoryAccessMakePointerAvailableKHRMask), + TranslateMemoryScope(coherentFlags), + alignment); + + // Need to convert to abstract types when necessary + if (type.getBasicType() == glslang::EbtBool) { + if (builder.isScalarType(nominalTypeId)) { + // Conversion for bool + spv::Id boolType = builder.makeBoolType(); + if (nominalTypeId != boolType) + loadedId = builder.createBinOp(spv::OpINotEqual, boolType, loadedId, builder.makeUintConstant(0)); + } else if (builder.isVectorType(nominalTypeId)) { + // Conversion for bvec + int vecSize = builder.getNumTypeComponents(nominalTypeId); + spv::Id bvecType = builder.makeVectorType(builder.makeBoolType(), vecSize); + if (nominalTypeId != bvecType) + loadedId = builder.createBinOp(spv::OpINotEqual, bvecType, loadedId, + makeSmearedConstant(builder.makeUintConstant(0), vecSize)); + } + } + + return loadedId; +} + +// Wrap the builder's accessChainStore to: +// - do conversion of concrete to abstract type +// +// Implicitly uses the existing builder.accessChain as the storage target. +void TGlslangToSpvTraverser::accessChainStore(const glslang::TType& type, spv::Id rvalue) +{ + // Need to convert to abstract types when necessary + if (type.getBasicType() == glslang::EbtBool) { + spv::Id nominalTypeId = builder.accessChainGetInferredType(); + + if (builder.isScalarType(nominalTypeId)) { + // Conversion for bool + spv::Id boolType = builder.makeBoolType(); + if (nominalTypeId != boolType) { + // keep these outside arguments, for determinant order-of-evaluation + spv::Id one = builder.makeUintConstant(1); + spv::Id zero = builder.makeUintConstant(0); + rvalue = builder.createTriOp(spv::OpSelect, nominalTypeId, rvalue, one, zero); + } else if (builder.getTypeId(rvalue) != boolType) + rvalue = builder.createBinOp(spv::OpINotEqual, boolType, rvalue, builder.makeUintConstant(0)); + } else if (builder.isVectorType(nominalTypeId)) { + // Conversion for bvec + int vecSize = builder.getNumTypeComponents(nominalTypeId); + spv::Id bvecType = builder.makeVectorType(builder.makeBoolType(), vecSize); + if (nominalTypeId != bvecType) { + // keep these outside arguments, for determinant order-of-evaluation + spv::Id one = makeSmearedConstant(builder.makeUintConstant(1), vecSize); + spv::Id zero = makeSmearedConstant(builder.makeUintConstant(0), vecSize); + rvalue = builder.createTriOp(spv::OpSelect, nominalTypeId, rvalue, one, zero); + } else if (builder.getTypeId(rvalue) != bvecType) + rvalue = builder.createBinOp(spv::OpINotEqual, bvecType, rvalue, + makeSmearedConstant(builder.makeUintConstant(0), vecSize)); + } + } + + spv::Builder::AccessChain::CoherentFlags coherentFlags = builder.getAccessChain().coherentFlags; + coherentFlags |= TranslateCoherent(type); + + unsigned int alignment = builder.getAccessChain().alignment; + alignment |= type.getBufferReferenceAlignment(); + + builder.accessChainStore(rvalue, + spv::MemoryAccessMask(TranslateMemoryAccess(coherentFlags) & + ~spv::MemoryAccessMakePointerVisibleKHRMask), + TranslateMemoryScope(coherentFlags), alignment); +} + +// For storing when types match at the glslang level, but not might match at the +// SPIR-V level. +// +// This especially happens when a single glslang type expands to multiple +// SPIR-V types, like a struct that is used in a member-undecorated way as well +// as in a member-decorated way. +// +// NOTE: This function can handle any store request; if it's not special it +// simplifies to a simple OpStore. +// +// Implicitly uses the existing builder.accessChain as the storage target. +void TGlslangToSpvTraverser::multiTypeStore(const glslang::TType& type, spv::Id rValue) +{ + // we only do the complex path here if it's an aggregate + if (! type.isStruct() && ! type.isArray()) { + accessChainStore(type, rValue); + return; + } + + // and, it has to be a case of type aliasing + spv::Id rType = builder.getTypeId(rValue); + spv::Id lValue = builder.accessChainGetLValue(); + spv::Id lType = builder.getContainedTypeId(builder.getTypeId(lValue)); + if (lType == rType) { + accessChainStore(type, rValue); + return; + } + + // Recursively (as needed) copy an aggregate type to a different aggregate type, + // where the two types were the same type in GLSL. This requires member + // by member copy, recursively. + + // SPIR-V 1.4 added an instruction to do help do this. + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) { + // However, bool in uniform space is changed to int, so + // OpCopyLogical does not work for that. + // TODO: It would be more robust to do a full recursive verification of the types satisfying SPIR-V rules. + bool rBool = builder.containsType(builder.getTypeId(rValue), spv::OpTypeBool, 0); + bool lBool = builder.containsType(lType, spv::OpTypeBool, 0); + if (lBool == rBool) { + spv::Id logicalCopy = builder.createUnaryOp(spv::OpCopyLogical, lType, rValue); + accessChainStore(type, logicalCopy); + return; + } + } + + // If an array, copy element by element. + if (type.isArray()) { + glslang::TType glslangElementType(type, 0); + spv::Id elementRType = builder.getContainedTypeId(rType); + for (int index = 0; index < type.getOuterArraySize(); ++index) { + // get the source member + spv::Id elementRValue = builder.createCompositeExtract(rValue, elementRType, index); + + // set up the target storage + builder.clearAccessChain(); + builder.setAccessChainLValue(lValue); + builder.accessChainPush(builder.makeIntConstant(index), TranslateCoherent(type), + type.getBufferReferenceAlignment()); + + // store the member + multiTypeStore(glslangElementType, elementRValue); + } + } else { + assert(type.isStruct()); + + // loop over structure members + const glslang::TTypeList& members = *type.getStruct(); + for (int m = 0; m < (int)members.size(); ++m) { + const glslang::TType& glslangMemberType = *members[m].type; + + // get the source member + spv::Id memberRType = builder.getContainedTypeId(rType, m); + spv::Id memberRValue = builder.createCompositeExtract(rValue, memberRType, m); + + // set up the target storage + builder.clearAccessChain(); + builder.setAccessChainLValue(lValue); + builder.accessChainPush(builder.makeIntConstant(m), TranslateCoherent(type), + type.getBufferReferenceAlignment()); + + // store the member + multiTypeStore(glslangMemberType, memberRValue); + } + } +} + +// Decide whether or not this type should be +// decorated with offsets and strides, and if so +// whether std140 or std430 rules should be applied. +glslang::TLayoutPacking TGlslangToSpvTraverser::getExplicitLayout(const glslang::TType& type) const +{ + // has to be a block + if (type.getBasicType() != glslang::EbtBlock) + return glslang::ElpNone; + + // has to be a uniform or buffer block or task in/out blocks + if (type.getQualifier().storage != glslang::EvqUniform && + type.getQualifier().storage != glslang::EvqBuffer && + !type.getQualifier().isTaskMemory()) + return glslang::ElpNone; + + // return the layout to use + switch (type.getQualifier().layoutPacking) { + case glslang::ElpStd140: + case glslang::ElpStd430: + case glslang::ElpScalar: + return type.getQualifier().layoutPacking; + default: + return glslang::ElpNone; + } +} + +// Given an array type, returns the integer stride required for that array +int TGlslangToSpvTraverser::getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking explicitLayout, + glslang::TLayoutMatrix matrixLayout) +{ + int size; + int stride; + glslangIntermediate->getMemberAlignment(arrayType, size, stride, explicitLayout, + matrixLayout == glslang::ElmRowMajor); + + return stride; +} + +// Given a matrix type, or array (of array) of matrixes type, returns the integer stride required for that matrix +// when used as a member of an interface block +int TGlslangToSpvTraverser::getMatrixStride(const glslang::TType& matrixType, glslang::TLayoutPacking explicitLayout, + glslang::TLayoutMatrix matrixLayout) +{ + glslang::TType elementType; + elementType.shallowCopy(matrixType); + elementType.clearArraySizes(); + + int size; + int stride; + glslangIntermediate->getMemberAlignment(elementType, size, stride, explicitLayout, + matrixLayout == glslang::ElmRowMajor); + + return stride; +} + +// Given a member type of a struct, realign the current offset for it, and compute +// the next (not yet aligned) offset for the next member, which will get aligned +// on the next call. +// 'currentOffset' should be passed in already initialized, ready to modify, and reflecting +// the migration of data from nextOffset -> currentOffset. It should be -1 on the first call. +// -1 means a non-forced member offset (no decoration needed). +void TGlslangToSpvTraverser::updateMemberOffset(const glslang::TType& structType, const glslang::TType& memberType, + int& currentOffset, int& nextOffset, glslang::TLayoutPacking explicitLayout, glslang::TLayoutMatrix matrixLayout) +{ + // this will get a positive value when deemed necessary + nextOffset = -1; + + // override anything in currentOffset with user-set offset + if (memberType.getQualifier().hasOffset()) + currentOffset = memberType.getQualifier().layoutOffset; + + // It could be that current linker usage in glslang updated all the layoutOffset, + // in which case the following code does not matter. But, that's not quite right + // once cross-compilation unit GLSL validation is done, as the original user + // settings are needed in layoutOffset, and then the following will come into play. + + if (explicitLayout == glslang::ElpNone) { + if (! memberType.getQualifier().hasOffset()) + currentOffset = -1; + + return; + } + + // Getting this far means we need explicit offsets + if (currentOffset < 0) + currentOffset = 0; + + // Now, currentOffset is valid (either 0, or from a previous nextOffset), + // but possibly not yet correctly aligned. + + int memberSize; + int dummyStride; + int memberAlignment = glslangIntermediate->getMemberAlignment(memberType, memberSize, dummyStride, explicitLayout, + matrixLayout == glslang::ElmRowMajor); + + // Adjust alignment for HLSL rules + // TODO: make this consistent in early phases of code: + // adjusting this late means inconsistencies with earlier code, which for reflection is an issue + // Until reflection is brought in sync with these adjustments, don't apply to $Global, + // which is the most likely to rely on reflection, and least likely to rely implicit layouts + if (glslangIntermediate->usingHlslOffsets() && + ! memberType.isArray() && memberType.isVector() && structType.getTypeName().compare("$Global") != 0) { + int dummySize; + int componentAlignment = glslangIntermediate->getBaseAlignmentScalar(memberType, dummySize); + if (componentAlignment <= 4) + memberAlignment = componentAlignment; + } + + // Bump up to member alignment + glslang::RoundToPow2(currentOffset, memberAlignment); + + // Bump up to vec4 if there is a bad straddle + if (explicitLayout != glslang::ElpScalar && glslangIntermediate->improperStraddle(memberType, memberSize, + currentOffset)) + glslang::RoundToPow2(currentOffset, 16); + + nextOffset = currentOffset + memberSize; +} + +void TGlslangToSpvTraverser::declareUseOfStructMember(const glslang::TTypeList& members, int glslangMember) +{ + const glslang::TBuiltInVariable glslangBuiltIn = members[glslangMember].type->getQualifier().builtIn; + switch (glslangBuiltIn) + { + case glslang::EbvPointSize: +#ifndef GLSLANG_WEB + case glslang::EbvClipDistance: + case glslang::EbvCullDistance: + case glslang::EbvViewportMaskNV: + case glslang::EbvSecondaryPositionNV: + case glslang::EbvSecondaryViewportMaskNV: + case glslang::EbvPositionPerViewNV: + case glslang::EbvViewportMaskPerViewNV: + case glslang::EbvTaskCountNV: + case glslang::EbvPrimitiveCountNV: + case glslang::EbvPrimitiveIndicesNV: + case glslang::EbvClipDistancePerViewNV: + case glslang::EbvCullDistancePerViewNV: + case glslang::EbvLayerPerViewNV: + case glslang::EbvMeshViewCountNV: + case glslang::EbvMeshViewIndicesNV: +#endif + // Generate the associated capability. Delegate to TranslateBuiltInDecoration. + // Alternately, we could just call this for any glslang built-in, since the + // capability already guards against duplicates. + TranslateBuiltInDecoration(glslangBuiltIn, false); + break; + default: + // Capabilities were already generated when the struct was declared. + break; + } +} + +bool TGlslangToSpvTraverser::isShaderEntryPoint(const glslang::TIntermAggregate* node) +{ + return node->getName().compare(glslangIntermediate->getEntryPointMangledName().c_str()) == 0; +} + +// Does parameter need a place to keep writes, separate from the original? +// Assumes called after originalParam(), which filters out block/buffer/opaque-based +// qualifiers such that we should have only in/out/inout/constreadonly here. +bool TGlslangToSpvTraverser::writableParam(glslang::TStorageQualifier qualifier) const +{ + assert(qualifier == glslang::EvqIn || + qualifier == glslang::EvqOut || + qualifier == glslang::EvqInOut || + qualifier == glslang::EvqConstReadOnly); + return qualifier != glslang::EvqConstReadOnly; +} + +// Is parameter pass-by-original? +bool TGlslangToSpvTraverser::originalParam(glslang::TStorageQualifier qualifier, const glslang::TType& paramType, + bool implicitThisParam) +{ + if (implicitThisParam) // implicit this + return true; + if (glslangIntermediate->getSource() == glslang::EShSourceHlsl) + return paramType.getBasicType() == glslang::EbtBlock; + return paramType.containsOpaque() || // sampler, etc. + (paramType.getBasicType() == glslang::EbtBlock && qualifier == glslang::EvqBuffer); // SSBO +} + +// Make all the functions, skeletally, without actually visiting their bodies. +void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslFunctions) +{ + const auto getParamDecorations = [&](std::vector& decorations, const glslang::TType& type, + bool useVulkanMemoryModel) { + spv::Decoration paramPrecision = TranslatePrecisionDecoration(type); + if (paramPrecision != spv::NoPrecision) + decorations.push_back(paramPrecision); + TranslateMemoryDecoration(type.getQualifier(), decorations, useVulkanMemoryModel); + if (type.isReference()) { + // Original and non-writable params pass the pointer directly and + // use restrict/aliased, others are stored to a pointer in Function + // memory and use RestrictPointer/AliasedPointer. + if (originalParam(type.getQualifier().storage, type, false) || + !writableParam(type.getQualifier().storage)) { + decorations.push_back(type.getQualifier().isRestrict() ? spv::DecorationRestrict : + spv::DecorationAliased); + } else { + decorations.push_back(type.getQualifier().isRestrict() ? spv::DecorationRestrictPointerEXT : + spv::DecorationAliasedPointerEXT); + } + } + }; + + for (int f = 0; f < (int)glslFunctions.size(); ++f) { + glslang::TIntermAggregate* glslFunction = glslFunctions[f]->getAsAggregate(); + if (! glslFunction || glslFunction->getOp() != glslang::EOpFunction || isShaderEntryPoint(glslFunction)) + continue; + + // We're on a user function. Set up the basic interface for the function now, + // so that it's available to call. Translating the body will happen later. + // + // Typically (except for a "const in" parameter), an address will be passed to the + // function. What it is an address of varies: + // + // - "in" parameters not marked as "const" can be written to without modifying the calling + // argument so that write needs to be to a copy, hence the address of a copy works. + // + // - "const in" parameters can just be the r-value, as no writes need occur. + // + // - "out" and "inout" arguments can't be done as pointers to the calling argument, because + // GLSL has copy-in/copy-out semantics. They can be handled though with a pointer to a copy. + + std::vector paramTypes; + std::vector> paramDecorations; // list of decorations per parameter + glslang::TIntermSequence& parameters = glslFunction->getSequence()[0]->getAsAggregate()->getSequence(); + +#ifdef ENABLE_HLSL + bool implicitThis = (int)parameters.size() > 0 && parameters[0]->getAsSymbolNode()->getName() == + glslangIntermediate->implicitThisName; +#else + bool implicitThis = false; +#endif + + paramDecorations.resize(parameters.size()); + for (int p = 0; p < (int)parameters.size(); ++p) { + const glslang::TType& paramType = parameters[p]->getAsTyped()->getType(); + spv::Id typeId = convertGlslangToSpvType(paramType); + if (originalParam(paramType.getQualifier().storage, paramType, implicitThis && p == 0)) + typeId = builder.makePointer(TranslateStorageClass(paramType), typeId); + else if (writableParam(paramType.getQualifier().storage)) + typeId = builder.makePointer(spv::StorageClassFunction, typeId); + else + rValueParameters.insert(parameters[p]->getAsSymbolNode()->getId()); + getParamDecorations(paramDecorations[p], paramType, glslangIntermediate->usingVulkanMemoryModel()); + paramTypes.push_back(typeId); + } + + spv::Block* functionBlock; + spv::Function *function = builder.makeFunctionEntry(TranslatePrecisionDecoration(glslFunction->getType()), + convertGlslangToSpvType(glslFunction->getType()), + glslFunction->getName().c_str(), paramTypes, + paramDecorations, &functionBlock); + if (implicitThis) + function->setImplicitThis(); + + // Track function to emit/call later + functionMap[glslFunction->getName().c_str()] = function; + + // Set the parameter id's + for (int p = 0; p < (int)parameters.size(); ++p) { + symbolValues[parameters[p]->getAsSymbolNode()->getId()] = function->getParamId(p); + // give a name too + builder.addName(function->getParamId(p), parameters[p]->getAsSymbolNode()->getName().c_str()); + + const glslang::TType& paramType = parameters[p]->getAsTyped()->getType(); + if (paramType.contains8BitInt()) + builder.addCapability(spv::CapabilityInt8); + if (paramType.contains16BitInt()) + builder.addCapability(spv::CapabilityInt16); + if (paramType.contains16BitFloat()) + builder.addCapability(spv::CapabilityFloat16); + } + } +} + +// Process all the initializers, while skipping the functions and link objects +void TGlslangToSpvTraverser::makeGlobalInitializers(const glslang::TIntermSequence& initializers) +{ + builder.setBuildPoint(shaderEntry->getLastBlock()); + for (int i = 0; i < (int)initializers.size(); ++i) { + glslang::TIntermAggregate* initializer = initializers[i]->getAsAggregate(); + if (initializer && initializer->getOp() != glslang::EOpFunction && initializer->getOp() != + glslang::EOpLinkerObjects) { + + // We're on a top-level node that's not a function. Treat as an initializer, whose + // code goes into the beginning of the entry point. + initializer->traverse(this); + } + } +} + +// Process all the functions, while skipping initializers. +void TGlslangToSpvTraverser::visitFunctions(const glslang::TIntermSequence& glslFunctions) +{ + for (int f = 0; f < (int)glslFunctions.size(); ++f) { + glslang::TIntermAggregate* node = glslFunctions[f]->getAsAggregate(); + if (node && (node->getOp() == glslang::EOpFunction || node->getOp() == glslang::EOpLinkerObjects)) + node->traverse(this); + } +} + +void TGlslangToSpvTraverser::handleFunctionEntry(const glslang::TIntermAggregate* node) +{ + // SPIR-V functions should already be in the functionMap from the prepass + // that called makeFunctions(). + currentFunction = functionMap[node->getName().c_str()]; + spv::Block* functionBlock = currentFunction->getEntryBlock(); + builder.setBuildPoint(functionBlock); +} + +void TGlslangToSpvTraverser::translateArguments(const glslang::TIntermAggregate& node, std::vector& arguments, + spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags) +{ + const glslang::TIntermSequence& glslangArguments = node.getSequence(); + + glslang::TSampler sampler = {}; + bool cubeCompare = false; +#ifndef GLSLANG_WEB + bool f16ShadowCompare = false; +#endif + if (node.isTexture() || node.isImage()) { + sampler = glslangArguments[0]->getAsTyped()->getType().getSampler(); + cubeCompare = sampler.dim == glslang::EsdCube && sampler.arrayed && sampler.shadow; +#ifndef GLSLANG_WEB + f16ShadowCompare = sampler.shadow && + glslangArguments[1]->getAsTyped()->getType().getBasicType() == glslang::EbtFloat16; +#endif + } + + for (int i = 0; i < (int)glslangArguments.size(); ++i) { + builder.clearAccessChain(); + glslangArguments[i]->traverse(this); + +#ifndef GLSLANG_WEB + // Special case l-value operands + bool lvalue = false; + switch (node.getOp()) { + case glslang::EOpImageAtomicAdd: + case glslang::EOpImageAtomicMin: + case glslang::EOpImageAtomicMax: + case glslang::EOpImageAtomicAnd: + case glslang::EOpImageAtomicOr: + case glslang::EOpImageAtomicXor: + case glslang::EOpImageAtomicExchange: + case glslang::EOpImageAtomicCompSwap: + case glslang::EOpImageAtomicLoad: + case glslang::EOpImageAtomicStore: + if (i == 0) + lvalue = true; + break; + case glslang::EOpSparseImageLoad: + if ((sampler.ms && i == 3) || (! sampler.ms && i == 2)) + lvalue = true; + break; + case glslang::EOpSparseTexture: + if (((cubeCompare || f16ShadowCompare) && i == 3) || (! (cubeCompare || f16ShadowCompare) && i == 2)) + lvalue = true; + break; + case glslang::EOpSparseTextureClamp: + if (((cubeCompare || f16ShadowCompare) && i == 4) || (! (cubeCompare || f16ShadowCompare) && i == 3)) + lvalue = true; + break; + case glslang::EOpSparseTextureLod: + case glslang::EOpSparseTextureOffset: + if ((f16ShadowCompare && i == 4) || (! f16ShadowCompare && i == 3)) + lvalue = true; + break; + case glslang::EOpSparseTextureFetch: + if ((sampler.dim != glslang::EsdRect && i == 3) || (sampler.dim == glslang::EsdRect && i == 2)) + lvalue = true; + break; + case glslang::EOpSparseTextureFetchOffset: + if ((sampler.dim != glslang::EsdRect && i == 4) || (sampler.dim == glslang::EsdRect && i == 3)) + lvalue = true; + break; + case glslang::EOpSparseTextureLodOffset: + case glslang::EOpSparseTextureGrad: + case glslang::EOpSparseTextureOffsetClamp: + if ((f16ShadowCompare && i == 5) || (! f16ShadowCompare && i == 4)) + lvalue = true; + break; + case glslang::EOpSparseTextureGradOffset: + case glslang::EOpSparseTextureGradClamp: + if ((f16ShadowCompare && i == 6) || (! f16ShadowCompare && i == 5)) + lvalue = true; + break; + case glslang::EOpSparseTextureGradOffsetClamp: + if ((f16ShadowCompare && i == 7) || (! f16ShadowCompare && i == 6)) + lvalue = true; + break; + case glslang::EOpSparseTextureGather: + if ((sampler.shadow && i == 3) || (! sampler.shadow && i == 2)) + lvalue = true; + break; + case glslang::EOpSparseTextureGatherOffset: + case glslang::EOpSparseTextureGatherOffsets: + if ((sampler.shadow && i == 4) || (! sampler.shadow && i == 3)) + lvalue = true; + break; + case glslang::EOpSparseTextureGatherLod: + if (i == 3) + lvalue = true; + break; + case glslang::EOpSparseTextureGatherLodOffset: + case glslang::EOpSparseTextureGatherLodOffsets: + if (i == 4) + lvalue = true; + break; + case glslang::EOpSparseImageLoadLod: + if (i == 3) + lvalue = true; + break; + case glslang::EOpImageSampleFootprintNV: + if (i == 4) + lvalue = true; + break; + case glslang::EOpImageSampleFootprintClampNV: + case glslang::EOpImageSampleFootprintLodNV: + if (i == 5) + lvalue = true; + break; + case glslang::EOpImageSampleFootprintGradNV: + if (i == 6) + lvalue = true; + break; + case glslang::EOpImageSampleFootprintGradClampNV: + if (i == 7) + lvalue = true; + break; + default: + break; + } + + if (lvalue) { + arguments.push_back(builder.accessChainGetLValue()); + lvalueCoherentFlags = builder.getAccessChain().coherentFlags; + lvalueCoherentFlags |= TranslateCoherent(glslangArguments[i]->getAsTyped()->getType()); + } else +#endif + arguments.push_back(accessChainLoad(glslangArguments[i]->getAsTyped()->getType())); + } +} + +void TGlslangToSpvTraverser::translateArguments(glslang::TIntermUnary& node, std::vector& arguments) +{ + builder.clearAccessChain(); + node.getOperand()->traverse(this); + arguments.push_back(accessChainLoad(node.getOperand()->getType())); +} + +spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermOperator* node) +{ + if (! node->isImage() && ! node->isTexture()) + return spv::NoResult; + + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + + // Process a GLSL texturing op (will be SPV image) + + const glslang::TType &imageType = node->getAsAggregate() + ? node->getAsAggregate()->getSequence()[0]->getAsTyped()->getType() + : node->getAsUnaryNode()->getOperand()->getAsTyped()->getType(); + const glslang::TSampler sampler = imageType.getSampler(); +#ifdef GLSLANG_WEB + const bool f16ShadowCompare = false; +#else + bool f16ShadowCompare = (sampler.shadow && node->getAsAggregate()) + ? node->getAsAggregate()->getSequence()[1]->getAsTyped()->getType().getBasicType() == glslang::EbtFloat16 + : false; +#endif + + const auto signExtensionMask = [&]() { + if (builder.getSpvVersion() >= spv::Spv_1_4) { + if (sampler.type == glslang::EbtUint) + return spv::ImageOperandsZeroExtendMask; + else if (sampler.type == glslang::EbtInt) + return spv::ImageOperandsSignExtendMask; + } + return spv::ImageOperandsMaskNone; + }; + + spv::Builder::AccessChain::CoherentFlags lvalueCoherentFlags; + + std::vector arguments; + if (node->getAsAggregate()) + translateArguments(*node->getAsAggregate(), arguments, lvalueCoherentFlags); + else + translateArguments(*node->getAsUnaryNode(), arguments); + spv::Decoration precision = TranslatePrecisionDecoration(node->getOperationPrecision()); + + spv::Builder::TextureParameters params = { }; + params.sampler = arguments[0]; + + glslang::TCrackedTextureOp cracked; + node->crackTexture(sampler, cracked); + + const bool isUnsignedResult = node->getType().getBasicType() == glslang::EbtUint; + + // Check for queries + if (cracked.query) { + // OpImageQueryLod works on a sampled image, for other queries the image has to be extracted first + if (node->getOp() != glslang::EOpTextureQueryLod && builder.isSampledImage(params.sampler)) + params.sampler = builder.createUnaryOp(spv::OpImage, builder.getImageType(params.sampler), params.sampler); + + switch (node->getOp()) { + case glslang::EOpImageQuerySize: + case glslang::EOpTextureQuerySize: + if (arguments.size() > 1) { + params.lod = arguments[1]; + return builder.createTextureQueryCall(spv::OpImageQuerySizeLod, params, isUnsignedResult); + } else + return builder.createTextureQueryCall(spv::OpImageQuerySize, params, isUnsignedResult); +#ifndef GLSLANG_WEB + case glslang::EOpImageQuerySamples: + case glslang::EOpTextureQuerySamples: + return builder.createTextureQueryCall(spv::OpImageQuerySamples, params, isUnsignedResult); + case glslang::EOpTextureQueryLod: + params.coords = arguments[1]; + return builder.createTextureQueryCall(spv::OpImageQueryLod, params, isUnsignedResult); + case glslang::EOpTextureQueryLevels: + return builder.createTextureQueryCall(spv::OpImageQueryLevels, params, isUnsignedResult); + case glslang::EOpSparseTexelsResident: + return builder.createUnaryOp(spv::OpImageSparseTexelsResident, builder.makeBoolType(), arguments[0]); +#endif + default: + assert(0); + break; + } + } + + int components = node->getType().getVectorSize(); + + if (node->getOp() == glslang::EOpTextureFetch) { + // These must produce 4 components, per SPIR-V spec. We'll add a conversion constructor if needed. + // This will only happen through the HLSL path for operator[], so we do not have to handle e.g. + // the EOpTexture/Proj/Lod/etc family. It would be harmless to do so, but would need more logic + // here around e.g. which ones return scalars or other types. + components = 4; + } + + glslang::TType returnType(node->getType().getBasicType(), glslang::EvqTemporary, components); + + auto resultType = [&returnType,this]{ return convertGlslangToSpvType(returnType); }; + + // Check for image functions other than queries + if (node->isImage()) { + std::vector operands; + auto opIt = arguments.begin(); + spv::IdImmediate image = { true, *(opIt++) }; + operands.push_back(image); + + // Handle subpass operations + // TODO: GLSL should change to have the "MS" only on the type rather than the + // built-in function. + if (cracked.subpass) { + // add on the (0,0) coordinate + spv::Id zero = builder.makeIntConstant(0); + std::vector comps; + comps.push_back(zero); + comps.push_back(zero); + spv::IdImmediate coord = { true, + builder.makeCompositeConstant(builder.makeVectorType(builder.makeIntType(32), 2), comps) }; + operands.push_back(coord); + spv::IdImmediate imageOperands = { false, spv::ImageOperandsMaskNone }; + imageOperands.word = imageOperands.word | signExtensionMask(); + if (sampler.isMultiSample()) { + imageOperands.word = imageOperands.word | spv::ImageOperandsSampleMask; + } + if (imageOperands.word != spv::ImageOperandsMaskNone) { + operands.push_back(imageOperands); + if (sampler.isMultiSample()) { + spv::IdImmediate imageOperand = { true, *(opIt++) }; + operands.push_back(imageOperand); + } + } + spv::Id result = builder.createOp(spv::OpImageRead, resultType(), operands); + builder.setPrecision(result, precision); + return result; + } + + spv::IdImmediate coord = { true, *(opIt++) }; + operands.push_back(coord); + if (node->getOp() == glslang::EOpImageLoad || node->getOp() == glslang::EOpImageLoadLod) { + spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone; + if (sampler.isMultiSample()) { + mask = mask | spv::ImageOperandsSampleMask; + } + if (cracked.lod) { + builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod); + builder.addCapability(spv::CapabilityImageReadWriteLodAMD); + mask = mask | spv::ImageOperandsLodMask; + } + mask = mask | TranslateImageOperands(TranslateCoherent(imageType)); + mask = (spv::ImageOperandsMask)(mask & ~spv::ImageOperandsMakeTexelAvailableKHRMask); + mask = mask | signExtensionMask(); + if (mask != spv::ImageOperandsMaskNone) { + spv::IdImmediate imageOperands = { false, (unsigned int)mask }; + operands.push_back(imageOperands); + } + if (mask & spv::ImageOperandsSampleMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsLodMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsMakeTexelVisibleKHRMask) { + spv::IdImmediate imageOperand = { true, + builder.makeUintConstant(TranslateMemoryScope(TranslateCoherent(imageType))) }; + operands.push_back(imageOperand); + } + + if (builder.getImageTypeFormat(builder.getImageType(operands.front().word)) == spv::ImageFormatUnknown) + builder.addCapability(spv::CapabilityStorageImageReadWithoutFormat); + + std::vector result(1, builder.createOp(spv::OpImageRead, resultType(), operands)); + builder.setPrecision(result[0], precision); + + // If needed, add a conversion constructor to the proper size. + if (components != node->getType().getVectorSize()) + result[0] = builder.createConstructor(precision, result, convertGlslangToSpvType(node->getType())); + + return result[0]; + } else if (node->getOp() == glslang::EOpImageStore || node->getOp() == glslang::EOpImageStoreLod) { + + // Push the texel value before the operands + if (sampler.isMultiSample() || cracked.lod) { + spv::IdImmediate texel = { true, *(opIt + 1) }; + operands.push_back(texel); + } else { + spv::IdImmediate texel = { true, *opIt }; + operands.push_back(texel); + } + + spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone; + if (sampler.isMultiSample()) { + mask = mask | spv::ImageOperandsSampleMask; + } + if (cracked.lod) { + builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod); + builder.addCapability(spv::CapabilityImageReadWriteLodAMD); + mask = mask | spv::ImageOperandsLodMask; + } + mask = mask | TranslateImageOperands(TranslateCoherent(imageType)); + mask = (spv::ImageOperandsMask)(mask & ~spv::ImageOperandsMakeTexelVisibleKHRMask); + mask = mask | signExtensionMask(); + if (mask != spv::ImageOperandsMaskNone) { + spv::IdImmediate imageOperands = { false, (unsigned int)mask }; + operands.push_back(imageOperands); + } + if (mask & spv::ImageOperandsSampleMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsLodMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsMakeTexelAvailableKHRMask) { + spv::IdImmediate imageOperand = { true, + builder.makeUintConstant(TranslateMemoryScope(TranslateCoherent(imageType))) }; + operands.push_back(imageOperand); + } + + builder.createNoResultOp(spv::OpImageWrite, operands); + if (builder.getImageTypeFormat(builder.getImageType(operands.front().word)) == spv::ImageFormatUnknown) + builder.addCapability(spv::CapabilityStorageImageWriteWithoutFormat); + return spv::NoResult; + } else if (node->getOp() == glslang::EOpSparseImageLoad || + node->getOp() == glslang::EOpSparseImageLoadLod) { + builder.addCapability(spv::CapabilitySparseResidency); + if (builder.getImageTypeFormat(builder.getImageType(operands.front().word)) == spv::ImageFormatUnknown) + builder.addCapability(spv::CapabilityStorageImageReadWithoutFormat); + + spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone; + if (sampler.isMultiSample()) { + mask = mask | spv::ImageOperandsSampleMask; + } + if (cracked.lod) { + builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod); + builder.addCapability(spv::CapabilityImageReadWriteLodAMD); + + mask = mask | spv::ImageOperandsLodMask; + } + mask = mask | TranslateImageOperands(TranslateCoherent(imageType)); + mask = (spv::ImageOperandsMask)(mask & ~spv::ImageOperandsMakeTexelAvailableKHRMask); + mask = mask | signExtensionMask(); + if (mask != spv::ImageOperandsMaskNone) { + spv::IdImmediate imageOperands = { false, (unsigned int)mask }; + operands.push_back(imageOperands); + } + if (mask & spv::ImageOperandsSampleMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsLodMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsMakeTexelVisibleKHRMask) { + spv::IdImmediate imageOperand = { true, builder.makeUintConstant(TranslateMemoryScope( + TranslateCoherent(imageType))) }; + operands.push_back(imageOperand); + } + + // Create the return type that was a special structure + spv::Id texelOut = *opIt; + spv::Id typeId0 = resultType(); + spv::Id typeId1 = builder.getDerefTypeId(texelOut); + spv::Id resultTypeId = builder.makeStructResultType(typeId0, typeId1); + + spv::Id resultId = builder.createOp(spv::OpImageSparseRead, resultTypeId, operands); + + // Decode the return type + builder.createStore(builder.createCompositeExtract(resultId, typeId1, 1), texelOut); + return builder.createCompositeExtract(resultId, typeId0, 0); + } else { + // Process image atomic operations + + // GLSL "IMAGE_PARAMS" will involve in constructing an image texel pointer and this pointer, + // as the first source operand, is required by SPIR-V atomic operations. + // For non-MS, the sample value should be 0 + spv::IdImmediate sample = { true, sampler.isMultiSample() ? *(opIt++) : builder.makeUintConstant(0) }; + operands.push_back(sample); + + spv::Id resultTypeId; + // imageAtomicStore has a void return type so base the pointer type on + // the type of the value operand. + if (node->getOp() == glslang::EOpImageAtomicStore) { + resultTypeId = builder.makePointer(spv::StorageClassImage, builder.getTypeId(*opIt)); + } else { + resultTypeId = builder.makePointer(spv::StorageClassImage, resultType()); + } + spv::Id pointer = builder.createOp(spv::OpImageTexelPointer, resultTypeId, operands); + if (imageType.getQualifier().nonUniform) { + builder.addDecoration(pointer, spv::DecorationNonUniformEXT); + } + + std::vector operands; + operands.push_back(pointer); + for (; opIt != arguments.end(); ++opIt) + operands.push_back(*opIt); + + return createAtomicOperation(node->getOp(), precision, resultType(), operands, node->getBasicType(), + lvalueCoherentFlags); + } + } + +#ifndef GLSLANG_WEB + // Check for fragment mask functions other than queries + if (cracked.fragMask) { + assert(sampler.ms); + + auto opIt = arguments.begin(); + std::vector operands; + + // Extract the image if necessary + if (builder.isSampledImage(params.sampler)) + params.sampler = builder.createUnaryOp(spv::OpImage, builder.getImageType(params.sampler), params.sampler); + + operands.push_back(params.sampler); + ++opIt; + + if (sampler.isSubpass()) { + // add on the (0,0) coordinate + spv::Id zero = builder.makeIntConstant(0); + std::vector comps; + comps.push_back(zero); + comps.push_back(zero); + operands.push_back(builder.makeCompositeConstant( + builder.makeVectorType(builder.makeIntType(32), 2), comps)); + } + + for (; opIt != arguments.end(); ++opIt) + operands.push_back(*opIt); + + spv::Op fragMaskOp = spv::OpNop; + if (node->getOp() == glslang::EOpFragmentMaskFetch) + fragMaskOp = spv::OpFragmentMaskFetchAMD; + else if (node->getOp() == glslang::EOpFragmentFetch) + fragMaskOp = spv::OpFragmentFetchAMD; + + builder.addExtension(spv::E_SPV_AMD_shader_fragment_mask); + builder.addCapability(spv::CapabilityFragmentMaskAMD); + return builder.createOp(fragMaskOp, resultType(), operands); + } +#endif + + // Check for texture functions other than queries + bool sparse = node->isSparseTexture(); + bool imageFootprint = node->isImageFootprint(); + bool cubeCompare = sampler.dim == glslang::EsdCube && sampler.isArrayed() && sampler.isShadow(); + + // check for bias argument + bool bias = false; + if (! cracked.lod && ! cracked.grad && ! cracked.fetch && ! cubeCompare) { + int nonBiasArgCount = 2; + if (cracked.gather) + ++nonBiasArgCount; // comp argument should be present when bias argument is present + + if (f16ShadowCompare) + ++nonBiasArgCount; + if (cracked.offset) + ++nonBiasArgCount; + else if (cracked.offsets) + ++nonBiasArgCount; + if (cracked.grad) + nonBiasArgCount += 2; + if (cracked.lodClamp) + ++nonBiasArgCount; + if (sparse) + ++nonBiasArgCount; + if (imageFootprint) + //Following three extra arguments + // int granularity, bool coarse, out gl_TextureFootprint2DNV footprint + nonBiasArgCount += 3; + if ((int)arguments.size() > nonBiasArgCount) + bias = true; + } + + // See if the sampler param should really be just the SPV image part + if (cracked.fetch) { + // a fetch needs to have the image extracted first + if (builder.isSampledImage(params.sampler)) + params.sampler = builder.createUnaryOp(spv::OpImage, builder.getImageType(params.sampler), params.sampler); + } + +#ifndef GLSLANG_WEB + if (cracked.gather) { + const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions(); + if (bias || cracked.lod || + sourceExtensions.find(glslang::E_GL_AMD_texture_gather_bias_lod) != sourceExtensions.end()) { + builder.addExtension(spv::E_SPV_AMD_texture_gather_bias_lod); + builder.addCapability(spv::CapabilityImageGatherBiasLodAMD); + } + } +#endif + + // set the rest of the arguments + + params.coords = arguments[1]; + int extraArgs = 0; + bool noImplicitLod = false; + + // sort out where Dref is coming from + if (cubeCompare || f16ShadowCompare) { + params.Dref = arguments[2]; + ++extraArgs; + } else if (sampler.shadow && cracked.gather) { + params.Dref = arguments[2]; + ++extraArgs; + } else if (sampler.shadow) { + std::vector indexes; + int dRefComp; + if (cracked.proj) + dRefComp = 2; // "The resulting 3rd component of P in the shadow forms is used as Dref" + else + dRefComp = builder.getNumComponents(params.coords) - 1; + indexes.push_back(dRefComp); + params.Dref = builder.createCompositeExtract(params.coords, + builder.getScalarTypeId(builder.getTypeId(params.coords)), indexes); + } + + // lod + if (cracked.lod) { + params.lod = arguments[2 + extraArgs]; + ++extraArgs; + } else if (glslangIntermediate->getStage() != EShLangFragment && + !(glslangIntermediate->getStage() == EShLangCompute && + glslangIntermediate->hasLayoutDerivativeModeNone())) { + // we need to invent the default lod for an explicit lod instruction for a non-fragment stage + noImplicitLod = true; + } + + // multisample + if (sampler.isMultiSample()) { + params.sample = arguments[2 + extraArgs]; // For MS, "sample" should be specified + ++extraArgs; + } + + // gradient + if (cracked.grad) { + params.gradX = arguments[2 + extraArgs]; + params.gradY = arguments[3 + extraArgs]; + extraArgs += 2; + } + + // offset and offsets + if (cracked.offset) { + params.offset = arguments[2 + extraArgs]; + ++extraArgs; + } else if (cracked.offsets) { + params.offsets = arguments[2 + extraArgs]; + ++extraArgs; + } + +#ifndef GLSLANG_WEB + // lod clamp + if (cracked.lodClamp) { + params.lodClamp = arguments[2 + extraArgs]; + ++extraArgs; + } + // sparse + if (sparse) { + params.texelOut = arguments[2 + extraArgs]; + ++extraArgs; + } + // gather component + if (cracked.gather && ! sampler.shadow) { + // default component is 0, if missing, otherwise an argument + if (2 + extraArgs < (int)arguments.size()) { + params.component = arguments[2 + extraArgs]; + ++extraArgs; + } else + params.component = builder.makeIntConstant(0); + } + spv::Id resultStruct = spv::NoResult; + if (imageFootprint) { + //Following three extra arguments + // int granularity, bool coarse, out gl_TextureFootprint2DNV footprint + params.granularity = arguments[2 + extraArgs]; + params.coarse = arguments[3 + extraArgs]; + resultStruct = arguments[4 + extraArgs]; + extraArgs += 3; + } +#endif + // bias + if (bias) { + params.bias = arguments[2 + extraArgs]; + ++extraArgs; + } + +#ifndef GLSLANG_WEB + if (imageFootprint) { + builder.addExtension(spv::E_SPV_NV_shader_image_footprint); + builder.addCapability(spv::CapabilityImageFootprintNV); + + + //resultStructType(OpenGL type) contains 5 elements: + //struct gl_TextureFootprint2DNV { + // uvec2 anchor; + // uvec2 offset; + // uvec2 mask; + // uint lod; + // uint granularity; + //}; + //or + //struct gl_TextureFootprint3DNV { + // uvec3 anchor; + // uvec3 offset; + // uvec2 mask; + // uint lod; + // uint granularity; + //}; + spv::Id resultStructType = builder.getContainedTypeId(builder.getTypeId(resultStruct)); + assert(builder.isStructType(resultStructType)); + + //resType (SPIR-V type) contains 6 elements: + //Member 0 must be a Boolean type scalar(LOD), + //Member 1 must be a vector of integer type, whose Signedness operand is 0(anchor), + //Member 2 must be a vector of integer type, whose Signedness operand is 0(offset), + //Member 3 must be a vector of integer type, whose Signedness operand is 0(mask), + //Member 4 must be a scalar of integer type, whose Signedness operand is 0(lod), + //Member 5 must be a scalar of integer type, whose Signedness operand is 0(granularity). + std::vector members; + members.push_back(resultType()); + for (int i = 0; i < 5; i++) { + members.push_back(builder.getContainedTypeId(resultStructType, i)); + } + spv::Id resType = builder.makeStructType(members, "ResType"); + + //call ImageFootprintNV + spv::Id res = builder.createTextureCall(precision, resType, sparse, cracked.fetch, cracked.proj, + cracked.gather, noImplicitLod, params, signExtensionMask()); + + //copy resType (SPIR-V type) to resultStructType(OpenGL type) + for (int i = 0; i < 5; i++) { + builder.clearAccessChain(); + builder.setAccessChainLValue(resultStruct); + + //Accessing to a struct we created, no coherent flag is set + spv::Builder::AccessChain::CoherentFlags flags; + flags.clear(); + + builder.accessChainPush(builder.makeIntConstant(i), flags, 0); + builder.accessChainStore(builder.createCompositeExtract(res, builder.getContainedTypeId(resType, i+1), + i+1)); + } + return builder.createCompositeExtract(res, resultType(), 0); + } +#endif + + // projective component (might not to move) + // GLSL: "The texture coordinates consumed from P, not including the last component of P, + // are divided by the last component of P." + // SPIR-V: "... (u [, v] [, w], q)... It may be a vector larger than needed, but all + // unused components will appear after all used components." + if (cracked.proj) { + int projSourceComp = builder.getNumComponents(params.coords) - 1; + int projTargetComp; + switch (sampler.dim) { + case glslang::Esd1D: projTargetComp = 1; break; + case glslang::Esd2D: projTargetComp = 2; break; + case glslang::EsdRect: projTargetComp = 2; break; + default: projTargetComp = projSourceComp; break; + } + // copy the projective coordinate if we have to + if (projTargetComp != projSourceComp) { + spv::Id projComp = builder.createCompositeExtract(params.coords, + builder.getScalarTypeId(builder.getTypeId(params.coords)), projSourceComp); + params.coords = builder.createCompositeInsert(projComp, params.coords, + builder.getTypeId(params.coords), projTargetComp); + } + } + +#ifndef GLSLANG_WEB + // nonprivate + if (imageType.getQualifier().nonprivate) { + params.nonprivate = true; + } + + // volatile + if (imageType.getQualifier().volatil) { + params.volatil = true; + } +#endif + + std::vector result( 1, + builder.createTextureCall(precision, resultType(), sparse, cracked.fetch, cracked.proj, cracked.gather, + noImplicitLod, params, signExtensionMask()) + ); + + if (components != node->getType().getVectorSize()) + result[0] = builder.createConstructor(precision, result, convertGlslangToSpvType(node->getType())); + + return result[0]; +} + +spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAggregate* node) +{ + // Grab the function's pointer from the previously created function + spv::Function* function = functionMap[node->getName().c_str()]; + if (! function) + return 0; + + const glslang::TIntermSequence& glslangArgs = node->getSequence(); + const glslang::TQualifierList& qualifiers = node->getQualifierList(); + + // See comments in makeFunctions() for details about the semantics for parameter passing. + // + // These imply we need a four step process: + // 1. Evaluate the arguments + // 2. Allocate and make copies of in, out, and inout arguments + // 3. Make the call + // 4. Copy back the results + + // 1. Evaluate the arguments and their types + std::vector lValues; + std::vector rValues; + std::vector argTypes; + for (int a = 0; a < (int)glslangArgs.size(); ++a) { + argTypes.push_back(&glslangArgs[a]->getAsTyped()->getType()); + // build l-value + builder.clearAccessChain(); + glslangArgs[a]->traverse(this); + // keep outputs and pass-by-originals as l-values, evaluate others as r-values + if (originalParam(qualifiers[a], *argTypes[a], function->hasImplicitThis() && a == 0) || + writableParam(qualifiers[a])) { + // save l-value + lValues.push_back(builder.getAccessChain()); + } else { + // process r-value + rValues.push_back(accessChainLoad(*argTypes.back())); + } + } + + // 2. Allocate space for anything needing a copy, and if it's "in" or "inout" + // copy the original into that space. + // + // Also, build up the list of actual arguments to pass in for the call + int lValueCount = 0; + int rValueCount = 0; + std::vector spvArgs; + for (int a = 0; a < (int)glslangArgs.size(); ++a) { + spv::Id arg; + if (originalParam(qualifiers[a], *argTypes[a], function->hasImplicitThis() && a == 0)) { + builder.setAccessChain(lValues[lValueCount]); + arg = builder.accessChainGetLValue(); + ++lValueCount; + } else if (writableParam(qualifiers[a])) { + // need space to hold the copy + arg = builder.createVariable(spv::StorageClassFunction, + builder.getContainedTypeId(function->getParamType(a)), "param"); + if (qualifiers[a] == glslang::EvqIn || qualifiers[a] == glslang::EvqInOut) { + // need to copy the input into output space + builder.setAccessChain(lValues[lValueCount]); + spv::Id copy = accessChainLoad(*argTypes[a]); + builder.clearAccessChain(); + builder.setAccessChainLValue(arg); + multiTypeStore(*argTypes[a], copy); + } + ++lValueCount; + } else { + // process r-value, which involves a copy for a type mismatch + if (function->getParamType(a) != convertGlslangToSpvType(*argTypes[a])) { + spv::Id argCopy = builder.createVariable(spv::StorageClassFunction, function->getParamType(a), "arg"); + builder.clearAccessChain(); + builder.setAccessChainLValue(argCopy); + multiTypeStore(*argTypes[a], rValues[rValueCount]); + arg = builder.createLoad(argCopy); + } else + arg = rValues[rValueCount]; + ++rValueCount; + } + spvArgs.push_back(arg); + } + + // 3. Make the call. + spv::Id result = builder.createFunctionCall(function, spvArgs); + builder.setPrecision(result, TranslatePrecisionDecoration(node->getType())); + + // 4. Copy back out an "out" arguments. + lValueCount = 0; + for (int a = 0; a < (int)glslangArgs.size(); ++a) { + if (originalParam(qualifiers[a], *argTypes[a], function->hasImplicitThis() && a == 0)) + ++lValueCount; + else if (writableParam(qualifiers[a])) { + if (qualifiers[a] == glslang::EvqOut || qualifiers[a] == glslang::EvqInOut) { + spv::Id copy = builder.createLoad(spvArgs[a]); + builder.setAccessChain(lValues[lValueCount]); + multiTypeStore(*argTypes[a], copy); + } + ++lValueCount; + } + } + + return result; +} + +// Translate AST operation to SPV operation, already having SPV-based operands/types. +spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, OpDecorations& decorations, + spv::Id typeId, spv::Id left, spv::Id right, + glslang::TBasicType typeProxy, bool reduceComparison) +{ + bool isUnsigned = isTypeUnsignedInt(typeProxy); + bool isFloat = isTypeFloat(typeProxy); + bool isBool = typeProxy == glslang::EbtBool; + + spv::Op binOp = spv::OpNop; + bool needMatchingVectors = true; // for non-matrix ops, would a scalar need to smear to match a vector? + bool comparison = false; + + switch (op) { + case glslang::EOpAdd: + case glslang::EOpAddAssign: + if (isFloat) + binOp = spv::OpFAdd; + else + binOp = spv::OpIAdd; + break; + case glslang::EOpSub: + case glslang::EOpSubAssign: + if (isFloat) + binOp = spv::OpFSub; + else + binOp = spv::OpISub; + break; + case glslang::EOpMul: + case glslang::EOpMulAssign: + if (isFloat) + binOp = spv::OpFMul; + else + binOp = spv::OpIMul; + break; + case glslang::EOpVectorTimesScalar: + case glslang::EOpVectorTimesScalarAssign: + if (isFloat && (builder.isVector(left) || builder.isVector(right))) { + if (builder.isVector(right)) + std::swap(left, right); + assert(builder.isScalar(right)); + needMatchingVectors = false; + binOp = spv::OpVectorTimesScalar; + } else if (isFloat) + binOp = spv::OpFMul; + else + binOp = spv::OpIMul; + break; + case glslang::EOpVectorTimesMatrix: + case glslang::EOpVectorTimesMatrixAssign: + binOp = spv::OpVectorTimesMatrix; + break; + case glslang::EOpMatrixTimesVector: + binOp = spv::OpMatrixTimesVector; + break; + case glslang::EOpMatrixTimesScalar: + case glslang::EOpMatrixTimesScalarAssign: + binOp = spv::OpMatrixTimesScalar; + break; + case glslang::EOpMatrixTimesMatrix: + case glslang::EOpMatrixTimesMatrixAssign: + binOp = spv::OpMatrixTimesMatrix; + break; + case glslang::EOpOuterProduct: + binOp = spv::OpOuterProduct; + needMatchingVectors = false; + break; + + case glslang::EOpDiv: + case glslang::EOpDivAssign: + if (isFloat) + binOp = spv::OpFDiv; + else if (isUnsigned) + binOp = spv::OpUDiv; + else + binOp = spv::OpSDiv; + break; + case glslang::EOpMod: + case glslang::EOpModAssign: + if (isFloat) + binOp = spv::OpFMod; + else if (isUnsigned) + binOp = spv::OpUMod; + else + binOp = spv::OpSMod; + break; + case glslang::EOpRightShift: + case glslang::EOpRightShiftAssign: + if (isUnsigned) + binOp = spv::OpShiftRightLogical; + else + binOp = spv::OpShiftRightArithmetic; + break; + case glslang::EOpLeftShift: + case glslang::EOpLeftShiftAssign: + binOp = spv::OpShiftLeftLogical; + break; + case glslang::EOpAnd: + case glslang::EOpAndAssign: + binOp = spv::OpBitwiseAnd; + break; + case glslang::EOpLogicalAnd: + needMatchingVectors = false; + binOp = spv::OpLogicalAnd; + break; + case glslang::EOpInclusiveOr: + case glslang::EOpInclusiveOrAssign: + binOp = spv::OpBitwiseOr; + break; + case glslang::EOpLogicalOr: + needMatchingVectors = false; + binOp = spv::OpLogicalOr; + break; + case glslang::EOpExclusiveOr: + case glslang::EOpExclusiveOrAssign: + binOp = spv::OpBitwiseXor; + break; + case glslang::EOpLogicalXor: + needMatchingVectors = false; + binOp = spv::OpLogicalNotEqual; + break; + + case glslang::EOpAbsDifference: + binOp = isUnsigned ? spv::OpAbsUSubINTEL : spv::OpAbsISubINTEL; + break; + + case glslang::EOpAddSaturate: + binOp = isUnsigned ? spv::OpUAddSatINTEL : spv::OpIAddSatINTEL; + break; + + case glslang::EOpSubSaturate: + binOp = isUnsigned ? spv::OpUSubSatINTEL : spv::OpISubSatINTEL; + break; + + case glslang::EOpAverage: + binOp = isUnsigned ? spv::OpUAverageINTEL : spv::OpIAverageINTEL; + break; + + case glslang::EOpAverageRounded: + binOp = isUnsigned ? spv::OpUAverageRoundedINTEL : spv::OpIAverageRoundedINTEL; + break; + + case glslang::EOpMul32x16: + binOp = isUnsigned ? spv::OpUMul32x16INTEL : spv::OpIMul32x16INTEL; + break; + + case glslang::EOpLessThan: + case glslang::EOpGreaterThan: + case glslang::EOpLessThanEqual: + case glslang::EOpGreaterThanEqual: + case glslang::EOpEqual: + case glslang::EOpNotEqual: + case glslang::EOpVectorEqual: + case glslang::EOpVectorNotEqual: + comparison = true; + break; + default: + break; + } + + // handle mapped binary operations (should be non-comparison) + if (binOp != spv::OpNop) { + assert(comparison == false); + if (builder.isMatrix(left) || builder.isMatrix(right) || + builder.isCooperativeMatrix(left) || builder.isCooperativeMatrix(right)) + return createBinaryMatrixOperation(binOp, decorations, typeId, left, right); + + // No matrix involved; make both operands be the same number of components, if needed + if (needMatchingVectors) + builder.promoteScalar(decorations.precision, left, right); + + spv::Id result = builder.createBinOp(binOp, typeId, left, right); + decorations.addNoContraction(builder, result); + decorations.addNonUniform(builder, result); + return builder.setPrecision(result, decorations.precision); + } + + if (! comparison) + return 0; + + // Handle comparison instructions + + if (reduceComparison && (op == glslang::EOpEqual || op == glslang::EOpNotEqual) + && (builder.isVector(left) || builder.isMatrix(left) || builder.isAggregate(left))) { + spv::Id result = builder.createCompositeCompare(decorations.precision, left, right, op == glslang::EOpEqual); + decorations.addNonUniform(builder, result); + return result; + } + + switch (op) { + case glslang::EOpLessThan: + if (isFloat) + binOp = spv::OpFOrdLessThan; + else if (isUnsigned) + binOp = spv::OpULessThan; + else + binOp = spv::OpSLessThan; + break; + case glslang::EOpGreaterThan: + if (isFloat) + binOp = spv::OpFOrdGreaterThan; + else if (isUnsigned) + binOp = spv::OpUGreaterThan; + else + binOp = spv::OpSGreaterThan; + break; + case glslang::EOpLessThanEqual: + if (isFloat) + binOp = spv::OpFOrdLessThanEqual; + else if (isUnsigned) + binOp = spv::OpULessThanEqual; + else + binOp = spv::OpSLessThanEqual; + break; + case glslang::EOpGreaterThanEqual: + if (isFloat) + binOp = spv::OpFOrdGreaterThanEqual; + else if (isUnsigned) + binOp = spv::OpUGreaterThanEqual; + else + binOp = spv::OpSGreaterThanEqual; + break; + case glslang::EOpEqual: + case glslang::EOpVectorEqual: + if (isFloat) + binOp = spv::OpFOrdEqual; + else if (isBool) + binOp = spv::OpLogicalEqual; + else + binOp = spv::OpIEqual; + break; + case glslang::EOpNotEqual: + case glslang::EOpVectorNotEqual: + if (isFloat) + binOp = spv::OpFOrdNotEqual; + else if (isBool) + binOp = spv::OpLogicalNotEqual; + else + binOp = spv::OpINotEqual; + break; + default: + break; + } + + if (binOp != spv::OpNop) { + spv::Id result = builder.createBinOp(binOp, typeId, left, right); + decorations.addNoContraction(builder, result); + decorations.addNonUniform(builder, result); + return builder.setPrecision(result, decorations.precision); + } + + return 0; +} + +// +// Translate AST matrix operation to SPV operation, already having SPV-based operands/types. +// These can be any of: +// +// matrix * scalar +// scalar * matrix +// matrix * matrix linear algebraic +// matrix * vector +// vector * matrix +// matrix * matrix componentwise +// matrix op matrix op in {+, -, /} +// matrix op scalar op in {+, -, /} +// scalar op matrix op in {+, -, /} +// +spv::Id TGlslangToSpvTraverser::createBinaryMatrixOperation(spv::Op op, OpDecorations& decorations, spv::Id typeId, + spv::Id left, spv::Id right) +{ + bool firstClass = true; + + // First, handle first-class matrix operations (* and matrix/scalar) + switch (op) { + case spv::OpFDiv: + if (builder.isMatrix(left) && builder.isScalar(right)) { + // turn matrix / scalar into a multiply... + spv::Id resultType = builder.getTypeId(right); + right = builder.createBinOp(spv::OpFDiv, resultType, builder.makeFpConstant(resultType, 1.0), right); + op = spv::OpMatrixTimesScalar; + } else + firstClass = false; + break; + case spv::OpMatrixTimesScalar: + if (builder.isMatrix(right) || builder.isCooperativeMatrix(right)) + std::swap(left, right); + assert(builder.isScalar(right)); + break; + case spv::OpVectorTimesMatrix: + assert(builder.isVector(left)); + assert(builder.isMatrix(right)); + break; + case spv::OpMatrixTimesVector: + assert(builder.isMatrix(left)); + assert(builder.isVector(right)); + break; + case spv::OpMatrixTimesMatrix: + assert(builder.isMatrix(left)); + assert(builder.isMatrix(right)); + break; + default: + firstClass = false; + break; + } + + if (builder.isCooperativeMatrix(left) || builder.isCooperativeMatrix(right)) + firstClass = true; + + if (firstClass) { + spv::Id result = builder.createBinOp(op, typeId, left, right); + decorations.addNoContraction(builder, result); + decorations.addNonUniform(builder, result); + return builder.setPrecision(result, decorations.precision); + } + + // Handle component-wise +, -, *, %, and / for all combinations of type. + // The result type of all of them is the same type as the (a) matrix operand. + // The algorithm is to: + // - break the matrix(es) into vectors + // - smear any scalar to a vector + // - do vector operations + // - make a matrix out the vector results + switch (op) { + case spv::OpFAdd: + case spv::OpFSub: + case spv::OpFDiv: + case spv::OpFMod: + case spv::OpFMul: + { + // one time set up... + bool leftMat = builder.isMatrix(left); + bool rightMat = builder.isMatrix(right); + unsigned int numCols = leftMat ? builder.getNumColumns(left) : builder.getNumColumns(right); + int numRows = leftMat ? builder.getNumRows(left) : builder.getNumRows(right); + spv::Id scalarType = builder.getScalarTypeId(typeId); + spv::Id vecType = builder.makeVectorType(scalarType, numRows); + std::vector results; + spv::Id smearVec = spv::NoResult; + if (builder.isScalar(left)) + smearVec = builder.smearScalar(decorations.precision, left, vecType); + else if (builder.isScalar(right)) + smearVec = builder.smearScalar(decorations.precision, right, vecType); + + // do each vector op + for (unsigned int c = 0; c < numCols; ++c) { + std::vector indexes; + indexes.push_back(c); + spv::Id leftVec = leftMat ? builder.createCompositeExtract( left, vecType, indexes) : smearVec; + spv::Id rightVec = rightMat ? builder.createCompositeExtract(right, vecType, indexes) : smearVec; + spv::Id result = builder.createBinOp(op, vecType, leftVec, rightVec); + decorations.addNoContraction(builder, result); + decorations.addNonUniform(builder, result); + results.push_back(builder.setPrecision(result, decorations.precision)); + } + + // put the pieces together + spv::Id result = builder.setPrecision(builder.createCompositeConstruct(typeId, results), decorations.precision); + decorations.addNonUniform(builder, result); + return result; + } + default: + assert(0); + return spv::NoResult; + } +} + +spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, OpDecorations& decorations, spv::Id typeId, + spv::Id operand, glslang::TBasicType typeProxy, const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags) +{ + spv::Op unaryOp = spv::OpNop; + int extBuiltins = -1; + int libCall = -1; + bool isUnsigned = isTypeUnsignedInt(typeProxy); + bool isFloat = isTypeFloat(typeProxy); + + switch (op) { + case glslang::EOpNegative: + if (isFloat) { + unaryOp = spv::OpFNegate; + if (builder.isMatrixType(typeId)) + return createUnaryMatrixOperation(unaryOp, decorations, typeId, operand, typeProxy); + } else + unaryOp = spv::OpSNegate; + break; + + case glslang::EOpLogicalNot: + case glslang::EOpVectorLogicalNot: + unaryOp = spv::OpLogicalNot; + break; + case glslang::EOpBitwiseNot: + unaryOp = spv::OpNot; + break; + + case glslang::EOpDeterminant: + libCall = spv::GLSLstd450Determinant; + break; + case glslang::EOpMatrixInverse: + libCall = spv::GLSLstd450MatrixInverse; + break; + case glslang::EOpTranspose: + unaryOp = spv::OpTranspose; + break; + + case glslang::EOpRadians: + libCall = spv::GLSLstd450Radians; + break; + case glslang::EOpDegrees: + libCall = spv::GLSLstd450Degrees; + break; + case glslang::EOpSin: + libCall = spv::GLSLstd450Sin; + break; + case glslang::EOpCos: + libCall = spv::GLSLstd450Cos; + break; + case glslang::EOpTan: + libCall = spv::GLSLstd450Tan; + break; + case glslang::EOpAcos: + libCall = spv::GLSLstd450Acos; + break; + case glslang::EOpAsin: + libCall = spv::GLSLstd450Asin; + break; + case glslang::EOpAtan: + libCall = spv::GLSLstd450Atan; + break; + + case glslang::EOpAcosh: + libCall = spv::GLSLstd450Acosh; + break; + case glslang::EOpAsinh: + libCall = spv::GLSLstd450Asinh; + break; + case glslang::EOpAtanh: + libCall = spv::GLSLstd450Atanh; + break; + case glslang::EOpTanh: + libCall = spv::GLSLstd450Tanh; + break; + case glslang::EOpCosh: + libCall = spv::GLSLstd450Cosh; + break; + case glslang::EOpSinh: + libCall = spv::GLSLstd450Sinh; + break; + + case glslang::EOpLength: + libCall = spv::GLSLstd450Length; + break; + case glslang::EOpNormalize: + libCall = spv::GLSLstd450Normalize; + break; + + case glslang::EOpExp: + libCall = spv::GLSLstd450Exp; + break; + case glslang::EOpLog: + libCall = spv::GLSLstd450Log; + break; + case glslang::EOpExp2: + libCall = spv::GLSLstd450Exp2; + break; + case glslang::EOpLog2: + libCall = spv::GLSLstd450Log2; + break; + case glslang::EOpSqrt: + libCall = spv::GLSLstd450Sqrt; + break; + case glslang::EOpInverseSqrt: + libCall = spv::GLSLstd450InverseSqrt; + break; + + case glslang::EOpFloor: + libCall = spv::GLSLstd450Floor; + break; + case glslang::EOpTrunc: + libCall = spv::GLSLstd450Trunc; + break; + case glslang::EOpRound: + libCall = spv::GLSLstd450Round; + break; + case glslang::EOpRoundEven: + libCall = spv::GLSLstd450RoundEven; + break; + case glslang::EOpCeil: + libCall = spv::GLSLstd450Ceil; + break; + case glslang::EOpFract: + libCall = spv::GLSLstd450Fract; + break; + + case glslang::EOpIsNan: + unaryOp = spv::OpIsNan; + break; + case glslang::EOpIsInf: + unaryOp = spv::OpIsInf; + break; + case glslang::EOpIsFinite: + unaryOp = spv::OpIsFinite; + break; + + case glslang::EOpFloatBitsToInt: + case glslang::EOpFloatBitsToUint: + case glslang::EOpIntBitsToFloat: + case glslang::EOpUintBitsToFloat: + case glslang::EOpDoubleBitsToInt64: + case glslang::EOpDoubleBitsToUint64: + case glslang::EOpInt64BitsToDouble: + case glslang::EOpUint64BitsToDouble: + case glslang::EOpFloat16BitsToInt16: + case glslang::EOpFloat16BitsToUint16: + case glslang::EOpInt16BitsToFloat16: + case glslang::EOpUint16BitsToFloat16: + unaryOp = spv::OpBitcast; + break; + + case glslang::EOpPackSnorm2x16: + libCall = spv::GLSLstd450PackSnorm2x16; + break; + case glslang::EOpUnpackSnorm2x16: + libCall = spv::GLSLstd450UnpackSnorm2x16; + break; + case glslang::EOpPackUnorm2x16: + libCall = spv::GLSLstd450PackUnorm2x16; + break; + case glslang::EOpUnpackUnorm2x16: + libCall = spv::GLSLstd450UnpackUnorm2x16; + break; + case glslang::EOpPackHalf2x16: + libCall = spv::GLSLstd450PackHalf2x16; + break; + case glslang::EOpUnpackHalf2x16: + libCall = spv::GLSLstd450UnpackHalf2x16; + break; +#ifndef GLSLANG_WEB + case glslang::EOpPackSnorm4x8: + libCall = spv::GLSLstd450PackSnorm4x8; + break; + case glslang::EOpUnpackSnorm4x8: + libCall = spv::GLSLstd450UnpackSnorm4x8; + break; + case glslang::EOpPackUnorm4x8: + libCall = spv::GLSLstd450PackUnorm4x8; + break; + case glslang::EOpUnpackUnorm4x8: + libCall = spv::GLSLstd450UnpackUnorm4x8; + break; + case glslang::EOpPackDouble2x32: + libCall = spv::GLSLstd450PackDouble2x32; + break; + case glslang::EOpUnpackDouble2x32: + libCall = spv::GLSLstd450UnpackDouble2x32; + break; +#endif + + case glslang::EOpPackInt2x32: + case glslang::EOpUnpackInt2x32: + case glslang::EOpPackUint2x32: + case glslang::EOpUnpackUint2x32: + case glslang::EOpPack16: + case glslang::EOpPack32: + case glslang::EOpPack64: + case glslang::EOpUnpack32: + case glslang::EOpUnpack16: + case glslang::EOpUnpack8: + case glslang::EOpPackInt2x16: + case glslang::EOpUnpackInt2x16: + case glslang::EOpPackUint2x16: + case glslang::EOpUnpackUint2x16: + case glslang::EOpPackInt4x16: + case glslang::EOpUnpackInt4x16: + case glslang::EOpPackUint4x16: + case glslang::EOpUnpackUint4x16: + case glslang::EOpPackFloat2x16: + case glslang::EOpUnpackFloat2x16: + unaryOp = spv::OpBitcast; + break; + + case glslang::EOpDPdx: + unaryOp = spv::OpDPdx; + break; + case glslang::EOpDPdy: + unaryOp = spv::OpDPdy; + break; + case glslang::EOpFwidth: + unaryOp = spv::OpFwidth; + break; + + case glslang::EOpAny: + unaryOp = spv::OpAny; + break; + case glslang::EOpAll: + unaryOp = spv::OpAll; + break; + + case glslang::EOpAbs: + if (isFloat) + libCall = spv::GLSLstd450FAbs; + else + libCall = spv::GLSLstd450SAbs; + break; + case glslang::EOpSign: + if (isFloat) + libCall = spv::GLSLstd450FSign; + else + libCall = spv::GLSLstd450SSign; + break; + +#ifndef GLSLANG_WEB + case glslang::EOpDPdxFine: + unaryOp = spv::OpDPdxFine; + break; + case glslang::EOpDPdyFine: + unaryOp = spv::OpDPdyFine; + break; + case glslang::EOpFwidthFine: + unaryOp = spv::OpFwidthFine; + break; + case glslang::EOpDPdxCoarse: + unaryOp = spv::OpDPdxCoarse; + break; + case glslang::EOpDPdyCoarse: + unaryOp = spv::OpDPdyCoarse; + break; + case glslang::EOpFwidthCoarse: + unaryOp = spv::OpFwidthCoarse; + break; + case glslang::EOpRayQueryProceed: + unaryOp = spv::OpRayQueryProceedKHR; + break; + case glslang::EOpRayQueryGetRayTMin: + unaryOp = spv::OpRayQueryGetRayTMinKHR; + break; + case glslang::EOpRayQueryGetRayFlags: + unaryOp = spv::OpRayQueryGetRayFlagsKHR; + break; + case glslang::EOpRayQueryGetWorldRayOrigin: + unaryOp = spv::OpRayQueryGetWorldRayOriginKHR; + break; + case glslang::EOpRayQueryGetWorldRayDirection: + unaryOp = spv::OpRayQueryGetWorldRayDirectionKHR; + break; + case glslang::EOpRayQueryGetIntersectionCandidateAABBOpaque: + unaryOp = spv::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR; + break; + case glslang::EOpInterpolateAtCentroid: + if (typeProxy == glslang::EbtFloat16) + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + libCall = spv::GLSLstd450InterpolateAtCentroid; + break; + case glslang::EOpAtomicCounterIncrement: + case glslang::EOpAtomicCounterDecrement: + case glslang::EOpAtomicCounter: + { + // Handle all of the atomics in one place, in createAtomicOperation() + std::vector operands; + operands.push_back(operand); + return createAtomicOperation(op, decorations.precision, typeId, operands, typeProxy, lvalueCoherentFlags); + } + + case glslang::EOpBitFieldReverse: + unaryOp = spv::OpBitReverse; + break; + case glslang::EOpBitCount: + unaryOp = spv::OpBitCount; + break; + case glslang::EOpFindLSB: + libCall = spv::GLSLstd450FindILsb; + break; + case glslang::EOpFindMSB: + if (isUnsigned) + libCall = spv::GLSLstd450FindUMsb; + else + libCall = spv::GLSLstd450FindSMsb; + break; + + case glslang::EOpCountLeadingZeros: + builder.addCapability(spv::CapabilityIntegerFunctions2INTEL); + builder.addExtension("SPV_INTEL_shader_integer_functions2"); + unaryOp = spv::OpUCountLeadingZerosINTEL; + break; + + case glslang::EOpCountTrailingZeros: + builder.addCapability(spv::CapabilityIntegerFunctions2INTEL); + builder.addExtension("SPV_INTEL_shader_integer_functions2"); + unaryOp = spv::OpUCountTrailingZerosINTEL; + break; + + case glslang::EOpBallot: + case glslang::EOpReadFirstInvocation: + case glslang::EOpAnyInvocation: + case glslang::EOpAllInvocations: + case glslang::EOpAllInvocationsEqual: + case glslang::EOpMinInvocations: + case glslang::EOpMaxInvocations: + case glslang::EOpAddInvocations: + case glslang::EOpMinInvocationsNonUniform: + case glslang::EOpMaxInvocationsNonUniform: + case glslang::EOpAddInvocationsNonUniform: + case glslang::EOpMinInvocationsInclusiveScan: + case glslang::EOpMaxInvocationsInclusiveScan: + case glslang::EOpAddInvocationsInclusiveScan: + case glslang::EOpMinInvocationsInclusiveScanNonUniform: + case glslang::EOpMaxInvocationsInclusiveScanNonUniform: + case glslang::EOpAddInvocationsInclusiveScanNonUniform: + case glslang::EOpMinInvocationsExclusiveScan: + case glslang::EOpMaxInvocationsExclusiveScan: + case glslang::EOpAddInvocationsExclusiveScan: + case glslang::EOpMinInvocationsExclusiveScanNonUniform: + case glslang::EOpMaxInvocationsExclusiveScanNonUniform: + case glslang::EOpAddInvocationsExclusiveScanNonUniform: + { + std::vector operands; + operands.push_back(operand); + return createInvocationsOperation(op, typeId, operands, typeProxy); + } + case glslang::EOpSubgroupAll: + case glslang::EOpSubgroupAny: + case glslang::EOpSubgroupAllEqual: + case glslang::EOpSubgroupBroadcastFirst: + case glslang::EOpSubgroupBallot: + case glslang::EOpSubgroupInverseBallot: + case glslang::EOpSubgroupBallotBitCount: + case glslang::EOpSubgroupBallotInclusiveBitCount: + case glslang::EOpSubgroupBallotExclusiveBitCount: + case glslang::EOpSubgroupBallotFindLSB: + case glslang::EOpSubgroupBallotFindMSB: + case glslang::EOpSubgroupAdd: + case glslang::EOpSubgroupMul: + case glslang::EOpSubgroupMin: + case glslang::EOpSubgroupMax: + case glslang::EOpSubgroupAnd: + case glslang::EOpSubgroupOr: + case glslang::EOpSubgroupXor: + case glslang::EOpSubgroupInclusiveAdd: + case glslang::EOpSubgroupInclusiveMul: + case glslang::EOpSubgroupInclusiveMin: + case glslang::EOpSubgroupInclusiveMax: + case glslang::EOpSubgroupInclusiveAnd: + case glslang::EOpSubgroupInclusiveOr: + case glslang::EOpSubgroupInclusiveXor: + case glslang::EOpSubgroupExclusiveAdd: + case glslang::EOpSubgroupExclusiveMul: + case glslang::EOpSubgroupExclusiveMin: + case glslang::EOpSubgroupExclusiveMax: + case glslang::EOpSubgroupExclusiveAnd: + case glslang::EOpSubgroupExclusiveOr: + case glslang::EOpSubgroupExclusiveXor: + case glslang::EOpSubgroupQuadSwapHorizontal: + case glslang::EOpSubgroupQuadSwapVertical: + case glslang::EOpSubgroupQuadSwapDiagonal: { + std::vector operands; + operands.push_back(operand); + return createSubgroupOperation(op, typeId, operands, typeProxy); + } + case glslang::EOpMbcnt: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::MbcntAMD; + break; + + case glslang::EOpCubeFaceIndex: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_gcn_shader); + libCall = spv::CubeFaceIndexAMD; + break; + + case glslang::EOpCubeFaceCoord: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_gcn_shader); + libCall = spv::CubeFaceCoordAMD; + break; + case glslang::EOpSubgroupPartition: + unaryOp = spv::OpGroupNonUniformPartitionNV; + break; + case glslang::EOpConstructReference: + unaryOp = spv::OpBitcast; + break; +#endif + + case glslang::EOpCopyObject: + unaryOp = spv::OpCopyObject; + break; + + default: + return 0; + } + + spv::Id id; + if (libCall >= 0) { + std::vector args; + args.push_back(operand); + id = builder.createBuiltinCall(typeId, extBuiltins >= 0 ? extBuiltins : stdBuiltins, libCall, args); + } else { + id = builder.createUnaryOp(unaryOp, typeId, operand); + } + + decorations.addNoContraction(builder, id); + decorations.addNonUniform(builder, id); + return builder.setPrecision(id, decorations.precision); +} + +// Create a unary operation on a matrix +spv::Id TGlslangToSpvTraverser::createUnaryMatrixOperation(spv::Op op, OpDecorations& decorations, spv::Id typeId, + spv::Id operand, glslang::TBasicType /* typeProxy */) +{ + // Handle unary operations vector by vector. + // The result type is the same type as the original type. + // The algorithm is to: + // - break the matrix into vectors + // - apply the operation to each vector + // - make a matrix out the vector results + + // get the types sorted out + int numCols = builder.getNumColumns(operand); + int numRows = builder.getNumRows(operand); + spv::Id srcVecType = builder.makeVectorType(builder.getScalarTypeId(builder.getTypeId(operand)), numRows); + spv::Id destVecType = builder.makeVectorType(builder.getScalarTypeId(typeId), numRows); + std::vector results; + + // do each vector op + for (int c = 0; c < numCols; ++c) { + std::vector indexes; + indexes.push_back(c); + spv::Id srcVec = builder.createCompositeExtract(operand, srcVecType, indexes); + spv::Id destVec = builder.createUnaryOp(op, destVecType, srcVec); + decorations.addNoContraction(builder, destVec); + decorations.addNonUniform(builder, destVec); + results.push_back(builder.setPrecision(destVec, decorations.precision)); + } + + // put the pieces together + spv::Id result = builder.setPrecision(builder.createCompositeConstruct(typeId, results), decorations.precision); + decorations.addNonUniform(builder, result); + return result; +} + +// For converting integers where both the bitwidth and the signedness could +// change, but only do the width change here. The caller is still responsible +// for the signedness conversion. +spv::Id TGlslangToSpvTraverser::createIntWidthConversion(glslang::TOperator op, spv::Id operand, int vectorSize) +{ + // Get the result type width, based on the type to convert to. + int width = 32; + switch(op) { + case glslang::EOpConvInt16ToUint8: + case glslang::EOpConvIntToUint8: + case glslang::EOpConvInt64ToUint8: + case glslang::EOpConvUint16ToInt8: + case glslang::EOpConvUintToInt8: + case glslang::EOpConvUint64ToInt8: + width = 8; + break; + case glslang::EOpConvInt8ToUint16: + case glslang::EOpConvIntToUint16: + case glslang::EOpConvInt64ToUint16: + case glslang::EOpConvUint8ToInt16: + case glslang::EOpConvUintToInt16: + case glslang::EOpConvUint64ToInt16: + width = 16; + break; + case glslang::EOpConvInt8ToUint: + case glslang::EOpConvInt16ToUint: + case glslang::EOpConvInt64ToUint: + case glslang::EOpConvUint8ToInt: + case glslang::EOpConvUint16ToInt: + case glslang::EOpConvUint64ToInt: + width = 32; + break; + case glslang::EOpConvInt8ToUint64: + case glslang::EOpConvInt16ToUint64: + case glslang::EOpConvIntToUint64: + case glslang::EOpConvUint8ToInt64: + case glslang::EOpConvUint16ToInt64: + case glslang::EOpConvUintToInt64: + width = 64; + break; + + default: + assert(false && "Default missing"); + break; + } + + // Get the conversion operation and result type, + // based on the target width, but the source type. + spv::Id type = spv::NoType; + spv::Op convOp = spv::OpNop; + switch(op) { + case glslang::EOpConvInt8ToUint16: + case glslang::EOpConvInt8ToUint: + case glslang::EOpConvInt8ToUint64: + case glslang::EOpConvInt16ToUint8: + case glslang::EOpConvInt16ToUint: + case glslang::EOpConvInt16ToUint64: + case glslang::EOpConvIntToUint8: + case glslang::EOpConvIntToUint16: + case glslang::EOpConvIntToUint64: + case glslang::EOpConvInt64ToUint8: + case glslang::EOpConvInt64ToUint16: + case glslang::EOpConvInt64ToUint: + convOp = spv::OpSConvert; + type = builder.makeIntType(width); + break; + default: + convOp = spv::OpUConvert; + type = builder.makeUintType(width); + break; + } + + if (vectorSize > 0) + type = builder.makeVectorType(type, vectorSize); + + return builder.createUnaryOp(convOp, type, operand); +} + +spv::Id TGlslangToSpvTraverser::createConversion(glslang::TOperator op, OpDecorations& decorations, spv::Id destType, + spv::Id operand, glslang::TBasicType typeProxy) +{ + spv::Op convOp = spv::OpNop; + spv::Id zero = 0; + spv::Id one = 0; + + int vectorSize = builder.isVectorType(destType) ? builder.getNumTypeComponents(destType) : 0; + + switch (op) { + case glslang::EOpConvIntToBool: + case glslang::EOpConvUintToBool: + zero = builder.makeUintConstant(0); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpINotEqual, destType, operand, zero); + case glslang::EOpConvFloatToBool: + zero = builder.makeFloatConstant(0.0F); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero); + case glslang::EOpConvBoolToFloat: + convOp = spv::OpSelect; + zero = builder.makeFloatConstant(0.0F); + one = builder.makeFloatConstant(1.0F); + break; + + case glslang::EOpConvBoolToInt: + case glslang::EOpConvBoolToInt64: +#ifndef GLSLANG_WEB + if (op == glslang::EOpConvBoolToInt64) { + zero = builder.makeInt64Constant(0); + one = builder.makeInt64Constant(1); + } else +#endif + { + zero = builder.makeIntConstant(0); + one = builder.makeIntConstant(1); + } + + convOp = spv::OpSelect; + break; + + case glslang::EOpConvBoolToUint: + case glslang::EOpConvBoolToUint64: +#ifndef GLSLANG_WEB + if (op == glslang::EOpConvBoolToUint64) { + zero = builder.makeUint64Constant(0); + one = builder.makeUint64Constant(1); + } else +#endif + { + zero = builder.makeUintConstant(0); + one = builder.makeUintConstant(1); + } + + convOp = spv::OpSelect; + break; + + case glslang::EOpConvInt8ToFloat16: + case glslang::EOpConvInt8ToFloat: + case glslang::EOpConvInt8ToDouble: + case glslang::EOpConvInt16ToFloat16: + case glslang::EOpConvInt16ToFloat: + case glslang::EOpConvInt16ToDouble: + case glslang::EOpConvIntToFloat16: + case glslang::EOpConvIntToFloat: + case glslang::EOpConvIntToDouble: + case glslang::EOpConvInt64ToFloat: + case glslang::EOpConvInt64ToDouble: + case glslang::EOpConvInt64ToFloat16: + convOp = spv::OpConvertSToF; + break; + + case glslang::EOpConvUint8ToFloat16: + case glslang::EOpConvUint8ToFloat: + case glslang::EOpConvUint8ToDouble: + case glslang::EOpConvUint16ToFloat16: + case glslang::EOpConvUint16ToFloat: + case glslang::EOpConvUint16ToDouble: + case glslang::EOpConvUintToFloat16: + case glslang::EOpConvUintToFloat: + case glslang::EOpConvUintToDouble: + case glslang::EOpConvUint64ToFloat: + case glslang::EOpConvUint64ToDouble: + case glslang::EOpConvUint64ToFloat16: + convOp = spv::OpConvertUToF; + break; + + case glslang::EOpConvFloat16ToInt8: + case glslang::EOpConvFloatToInt8: + case glslang::EOpConvDoubleToInt8: + case glslang::EOpConvFloat16ToInt16: + case glslang::EOpConvFloatToInt16: + case glslang::EOpConvDoubleToInt16: + case glslang::EOpConvFloat16ToInt: + case glslang::EOpConvFloatToInt: + case glslang::EOpConvDoubleToInt: + case glslang::EOpConvFloat16ToInt64: + case glslang::EOpConvFloatToInt64: + case glslang::EOpConvDoubleToInt64: + convOp = spv::OpConvertFToS; + break; + + case glslang::EOpConvUint8ToInt8: + case glslang::EOpConvInt8ToUint8: + case glslang::EOpConvUint16ToInt16: + case glslang::EOpConvInt16ToUint16: + case glslang::EOpConvUintToInt: + case glslang::EOpConvIntToUint: + case glslang::EOpConvUint64ToInt64: + case glslang::EOpConvInt64ToUint64: + if (builder.isInSpecConstCodeGenMode()) { + // Build zero scalar or vector for OpIAdd. +#ifndef GLSLANG_WEB + if(op == glslang::EOpConvUint8ToInt8 || op == glslang::EOpConvInt8ToUint8) { + zero = builder.makeUint8Constant(0); + } else if (op == glslang::EOpConvUint16ToInt16 || op == glslang::EOpConvInt16ToUint16) { + zero = builder.makeUint16Constant(0); + } else if (op == glslang::EOpConvUint64ToInt64 || op == glslang::EOpConvInt64ToUint64) { + zero = builder.makeUint64Constant(0); + } else +#endif + { + zero = builder.makeUintConstant(0); + } + zero = makeSmearedConstant(zero, vectorSize); + // Use OpIAdd, instead of OpBitcast to do the conversion when + // generating for OpSpecConstantOp instruction. + return builder.createBinOp(spv::OpIAdd, destType, operand, zero); + } + // For normal run-time conversion instruction, use OpBitcast. + convOp = spv::OpBitcast; + break; + + case glslang::EOpConvFloat16ToUint8: + case glslang::EOpConvFloatToUint8: + case glslang::EOpConvDoubleToUint8: + case glslang::EOpConvFloat16ToUint16: + case glslang::EOpConvFloatToUint16: + case glslang::EOpConvDoubleToUint16: + case glslang::EOpConvFloat16ToUint: + case glslang::EOpConvFloatToUint: + case glslang::EOpConvDoubleToUint: + case glslang::EOpConvFloatToUint64: + case glslang::EOpConvDoubleToUint64: + case glslang::EOpConvFloat16ToUint64: + convOp = spv::OpConvertFToU; + break; + +#ifndef GLSLANG_WEB + case glslang::EOpConvInt8ToBool: + case glslang::EOpConvUint8ToBool: + zero = builder.makeUint8Constant(0); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpINotEqual, destType, operand, zero); + case glslang::EOpConvInt16ToBool: + case glslang::EOpConvUint16ToBool: + zero = builder.makeUint16Constant(0); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpINotEqual, destType, operand, zero); + case glslang::EOpConvInt64ToBool: + case glslang::EOpConvUint64ToBool: + zero = builder.makeUint64Constant(0); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpINotEqual, destType, operand, zero); + case glslang::EOpConvDoubleToBool: + zero = builder.makeDoubleConstant(0.0); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero); + case glslang::EOpConvFloat16ToBool: + zero = builder.makeFloat16Constant(0.0F); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpFOrdNotEqual, destType, operand, zero); + case glslang::EOpConvBoolToDouble: + convOp = spv::OpSelect; + zero = builder.makeDoubleConstant(0.0); + one = builder.makeDoubleConstant(1.0); + break; + case glslang::EOpConvBoolToFloat16: + convOp = spv::OpSelect; + zero = builder.makeFloat16Constant(0.0F); + one = builder.makeFloat16Constant(1.0F); + break; + case glslang::EOpConvBoolToInt8: + zero = builder.makeInt8Constant(0); + one = builder.makeInt8Constant(1); + convOp = spv::OpSelect; + break; + case glslang::EOpConvBoolToUint8: + zero = builder.makeUint8Constant(0); + one = builder.makeUint8Constant(1); + convOp = spv::OpSelect; + break; + case glslang::EOpConvBoolToInt16: + zero = builder.makeInt16Constant(0); + one = builder.makeInt16Constant(1); + convOp = spv::OpSelect; + break; + case glslang::EOpConvBoolToUint16: + zero = builder.makeUint16Constant(0); + one = builder.makeUint16Constant(1); + convOp = spv::OpSelect; + break; + case glslang::EOpConvDoubleToFloat: + case glslang::EOpConvFloatToDouble: + case glslang::EOpConvDoubleToFloat16: + case glslang::EOpConvFloat16ToDouble: + case glslang::EOpConvFloatToFloat16: + case glslang::EOpConvFloat16ToFloat: + convOp = spv::OpFConvert; + if (builder.isMatrixType(destType)) + return createUnaryMatrixOperation(convOp, decorations, destType, operand, typeProxy); + break; + + case glslang::EOpConvInt8ToInt16: + case glslang::EOpConvInt8ToInt: + case glslang::EOpConvInt8ToInt64: + case glslang::EOpConvInt16ToInt8: + case glslang::EOpConvInt16ToInt: + case glslang::EOpConvInt16ToInt64: + case glslang::EOpConvIntToInt8: + case glslang::EOpConvIntToInt16: + case glslang::EOpConvIntToInt64: + case glslang::EOpConvInt64ToInt8: + case glslang::EOpConvInt64ToInt16: + case glslang::EOpConvInt64ToInt: + convOp = spv::OpSConvert; + break; + + case glslang::EOpConvUint8ToUint16: + case glslang::EOpConvUint8ToUint: + case glslang::EOpConvUint8ToUint64: + case glslang::EOpConvUint16ToUint8: + case glslang::EOpConvUint16ToUint: + case glslang::EOpConvUint16ToUint64: + case glslang::EOpConvUintToUint8: + case glslang::EOpConvUintToUint16: + case glslang::EOpConvUintToUint64: + case glslang::EOpConvUint64ToUint8: + case glslang::EOpConvUint64ToUint16: + case glslang::EOpConvUint64ToUint: + convOp = spv::OpUConvert; + break; + + case glslang::EOpConvInt8ToUint16: + case glslang::EOpConvInt8ToUint: + case glslang::EOpConvInt8ToUint64: + case glslang::EOpConvInt16ToUint8: + case glslang::EOpConvInt16ToUint: + case glslang::EOpConvInt16ToUint64: + case glslang::EOpConvIntToUint8: + case glslang::EOpConvIntToUint16: + case glslang::EOpConvIntToUint64: + case glslang::EOpConvInt64ToUint8: + case glslang::EOpConvInt64ToUint16: + case glslang::EOpConvInt64ToUint: + case glslang::EOpConvUint8ToInt16: + case glslang::EOpConvUint8ToInt: + case glslang::EOpConvUint8ToInt64: + case glslang::EOpConvUint16ToInt8: + case glslang::EOpConvUint16ToInt: + case glslang::EOpConvUint16ToInt64: + case glslang::EOpConvUintToInt8: + case glslang::EOpConvUintToInt16: + case glslang::EOpConvUintToInt64: + case glslang::EOpConvUint64ToInt8: + case glslang::EOpConvUint64ToInt16: + case glslang::EOpConvUint64ToInt: + // OpSConvert/OpUConvert + OpBitCast + operand = createIntWidthConversion(op, operand, vectorSize); + + if (builder.isInSpecConstCodeGenMode()) { + // Build zero scalar or vector for OpIAdd. + switch(op) { + case glslang::EOpConvInt16ToUint8: + case glslang::EOpConvIntToUint8: + case glslang::EOpConvInt64ToUint8: + case glslang::EOpConvUint16ToInt8: + case glslang::EOpConvUintToInt8: + case glslang::EOpConvUint64ToInt8: + zero = builder.makeUint8Constant(0); + break; + case glslang::EOpConvInt8ToUint16: + case glslang::EOpConvIntToUint16: + case glslang::EOpConvInt64ToUint16: + case glslang::EOpConvUint8ToInt16: + case glslang::EOpConvUintToInt16: + case glslang::EOpConvUint64ToInt16: + zero = builder.makeUint16Constant(0); + break; + case glslang::EOpConvInt8ToUint: + case glslang::EOpConvInt16ToUint: + case glslang::EOpConvInt64ToUint: + case glslang::EOpConvUint8ToInt: + case glslang::EOpConvUint16ToInt: + case glslang::EOpConvUint64ToInt: + zero = builder.makeUintConstant(0); + break; + case glslang::EOpConvInt8ToUint64: + case glslang::EOpConvInt16ToUint64: + case glslang::EOpConvIntToUint64: + case glslang::EOpConvUint8ToInt64: + case glslang::EOpConvUint16ToInt64: + case glslang::EOpConvUintToInt64: + zero = builder.makeUint64Constant(0); + break; + default: + assert(false && "Default missing"); + break; + } + zero = makeSmearedConstant(zero, vectorSize); + // Use OpIAdd, instead of OpBitcast to do the conversion when + // generating for OpSpecConstantOp instruction. + return builder.createBinOp(spv::OpIAdd, destType, operand, zero); + } + // For normal run-time conversion instruction, use OpBitcast. + convOp = spv::OpBitcast; + break; + case glslang::EOpConvUint64ToPtr: + convOp = spv::OpConvertUToPtr; + break; + case glslang::EOpConvPtrToUint64: + convOp = spv::OpConvertPtrToU; + break; + case glslang::EOpConvPtrToUvec2: + case glslang::EOpConvUvec2ToPtr: + if (builder.isVector(operand)) + builder.promoteIncorporatedExtension(spv::E_SPV_EXT_physical_storage_buffer, + spv::E_SPV_KHR_physical_storage_buffer, spv::Spv_1_5); + convOp = spv::OpBitcast; + break; +#endif + + default: + break; + } + + spv::Id result = 0; + if (convOp == spv::OpNop) + return result; + + if (convOp == spv::OpSelect) { + zero = makeSmearedConstant(zero, vectorSize); + one = makeSmearedConstant(one, vectorSize); + result = builder.createTriOp(convOp, destType, operand, one, zero); + } else + result = builder.createUnaryOp(convOp, destType, operand); + + result = builder.setPrecision(result, decorations.precision); + decorations.addNonUniform(builder, result); + return result; +} + +spv::Id TGlslangToSpvTraverser::makeSmearedConstant(spv::Id constant, int vectorSize) +{ + if (vectorSize == 0) + return constant; + + spv::Id vectorTypeId = builder.makeVectorType(builder.getTypeId(constant), vectorSize); + std::vector components; + for (int c = 0; c < vectorSize; ++c) + components.push_back(constant); + return builder.makeCompositeConstant(vectorTypeId, components); +} + +// For glslang ops that map to SPV atomic opCodes +spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv::Decoration /*precision*/, + spv::Id typeId, std::vector& operands, glslang::TBasicType typeProxy, + const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags) +{ + spv::Op opCode = spv::OpNop; + + switch (op) { + case glslang::EOpAtomicAdd: + case glslang::EOpImageAtomicAdd: + case glslang::EOpAtomicCounterAdd: + opCode = spv::OpAtomicIAdd; + break; + case glslang::EOpAtomicCounterSubtract: + opCode = spv::OpAtomicISub; + break; + case glslang::EOpAtomicMin: + case glslang::EOpImageAtomicMin: + case glslang::EOpAtomicCounterMin: + opCode = (typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64) ? + spv::OpAtomicUMin : spv::OpAtomicSMin; + break; + case glslang::EOpAtomicMax: + case glslang::EOpImageAtomicMax: + case glslang::EOpAtomicCounterMax: + opCode = (typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64) ? + spv::OpAtomicUMax : spv::OpAtomicSMax; + break; + case glslang::EOpAtomicAnd: + case glslang::EOpImageAtomicAnd: + case glslang::EOpAtomicCounterAnd: + opCode = spv::OpAtomicAnd; + break; + case glslang::EOpAtomicOr: + case glslang::EOpImageAtomicOr: + case glslang::EOpAtomicCounterOr: + opCode = spv::OpAtomicOr; + break; + case glslang::EOpAtomicXor: + case glslang::EOpImageAtomicXor: + case glslang::EOpAtomicCounterXor: + opCode = spv::OpAtomicXor; + break; + case glslang::EOpAtomicExchange: + case glslang::EOpImageAtomicExchange: + case glslang::EOpAtomicCounterExchange: + opCode = spv::OpAtomicExchange; + break; + case glslang::EOpAtomicCompSwap: + case glslang::EOpImageAtomicCompSwap: + case glslang::EOpAtomicCounterCompSwap: + opCode = spv::OpAtomicCompareExchange; + break; + case glslang::EOpAtomicCounterIncrement: + opCode = spv::OpAtomicIIncrement; + break; + case glslang::EOpAtomicCounterDecrement: + opCode = spv::OpAtomicIDecrement; + break; + case glslang::EOpAtomicCounter: + case glslang::EOpImageAtomicLoad: + case glslang::EOpAtomicLoad: + opCode = spv::OpAtomicLoad; + break; + case glslang::EOpAtomicStore: + case glslang::EOpImageAtomicStore: + opCode = spv::OpAtomicStore; + break; + default: + assert(0); + break; + } + + if (typeProxy == glslang::EbtInt64 || typeProxy == glslang::EbtUint64) + builder.addCapability(spv::CapabilityInt64Atomics); + + // Sort out the operands + // - mapping from glslang -> SPV + // - there are extra SPV operands that are optional in glslang + // - compare-exchange swaps the value and comparator + // - compare-exchange has an extra memory semantics + // - EOpAtomicCounterDecrement needs a post decrement + spv::Id pointerId = 0, compareId = 0, valueId = 0; + // scope defaults to Device in the old model, QueueFamilyKHR in the new model + spv::Id scopeId; + if (glslangIntermediate->usingVulkanMemoryModel()) { + scopeId = builder.makeUintConstant(spv::ScopeQueueFamilyKHR); + } else { + scopeId = builder.makeUintConstant(spv::ScopeDevice); + } + // semantics default to relaxed + spv::Id semanticsId = builder.makeUintConstant(lvalueCoherentFlags.isVolatile() && + glslangIntermediate->usingVulkanMemoryModel() ? + spv::MemorySemanticsVolatileMask : + spv::MemorySemanticsMaskNone); + spv::Id semanticsId2 = semanticsId; + + pointerId = operands[0]; + if (opCode == spv::OpAtomicIIncrement || opCode == spv::OpAtomicIDecrement) { + // no additional operands + } else if (opCode == spv::OpAtomicCompareExchange) { + compareId = operands[1]; + valueId = operands[2]; + if (operands.size() > 3) { + scopeId = operands[3]; + semanticsId = builder.makeUintConstant( + builder.getConstantScalar(operands[4]) | builder.getConstantScalar(operands[5])); + semanticsId2 = builder.makeUintConstant( + builder.getConstantScalar(operands[6]) | builder.getConstantScalar(operands[7])); + } + } else if (opCode == spv::OpAtomicLoad) { + if (operands.size() > 1) { + scopeId = operands[1]; + semanticsId = builder.makeUintConstant( + builder.getConstantScalar(operands[2]) | builder.getConstantScalar(operands[3])); + } + } else { + // atomic store or RMW + valueId = operands[1]; + if (operands.size() > 2) { + scopeId = operands[2]; + semanticsId = builder.makeUintConstant + (builder.getConstantScalar(operands[3]) | builder.getConstantScalar(operands[4])); + } + } + + // Check for capabilities + unsigned semanticsImmediate = builder.getConstantScalar(semanticsId) | builder.getConstantScalar(semanticsId2); + if (semanticsImmediate & (spv::MemorySemanticsMakeAvailableKHRMask | + spv::MemorySemanticsMakeVisibleKHRMask | + spv::MemorySemanticsOutputMemoryKHRMask | + spv::MemorySemanticsVolatileMask)) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } + + if (glslangIntermediate->usingVulkanMemoryModel() && builder.getConstantScalar(scopeId) == spv::ScopeDevice) { + builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR); + } + + std::vector spvAtomicOperands; // hold the spv operands + spvAtomicOperands.push_back(pointerId); + spvAtomicOperands.push_back(scopeId); + spvAtomicOperands.push_back(semanticsId); + if (opCode == spv::OpAtomicCompareExchange) { + spvAtomicOperands.push_back(semanticsId2); + spvAtomicOperands.push_back(valueId); + spvAtomicOperands.push_back(compareId); + } else if (opCode != spv::OpAtomicLoad && opCode != spv::OpAtomicIIncrement && opCode != spv::OpAtomicIDecrement) { + spvAtomicOperands.push_back(valueId); + } + + if (opCode == spv::OpAtomicStore) { + builder.createNoResultOp(opCode, spvAtomicOperands); + return 0; + } else { + spv::Id resultId = builder.createOp(opCode, typeId, spvAtomicOperands); + + // GLSL and HLSL atomic-counter decrement return post-decrement value, + // while SPIR-V returns pre-decrement value. Translate between these semantics. + if (op == glslang::EOpAtomicCounterDecrement) + resultId = builder.createBinOp(spv::OpISub, typeId, resultId, builder.makeIntConstant(1)); + + return resultId; + } +} + +// Create group invocation operations. +spv::Id TGlslangToSpvTraverser::createInvocationsOperation(glslang::TOperator op, spv::Id typeId, + std::vector& operands, glslang::TBasicType typeProxy) +{ + bool isUnsigned = isTypeUnsignedInt(typeProxy); + bool isFloat = isTypeFloat(typeProxy); + + spv::Op opCode = spv::OpNop; + std::vector spvGroupOperands; + spv::GroupOperation groupOperation = spv::GroupOperationMax; + + if (op == glslang::EOpBallot || op == glslang::EOpReadFirstInvocation || + op == glslang::EOpReadInvocation) { + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + } else if (op == glslang::EOpAnyInvocation || + op == glslang::EOpAllInvocations || + op == glslang::EOpAllInvocationsEqual) { + builder.addExtension(spv::E_SPV_KHR_subgroup_vote); + builder.addCapability(spv::CapabilitySubgroupVoteKHR); + } else { + builder.addCapability(spv::CapabilityGroups); + if (op == glslang::EOpMinInvocationsNonUniform || + op == glslang::EOpMaxInvocationsNonUniform || + op == glslang::EOpAddInvocationsNonUniform || + op == glslang::EOpMinInvocationsInclusiveScanNonUniform || + op == glslang::EOpMaxInvocationsInclusiveScanNonUniform || + op == glslang::EOpAddInvocationsInclusiveScanNonUniform || + op == glslang::EOpMinInvocationsExclusiveScanNonUniform || + op == glslang::EOpMaxInvocationsExclusiveScanNonUniform || + op == glslang::EOpAddInvocationsExclusiveScanNonUniform) + builder.addExtension(spv::E_SPV_AMD_shader_ballot); + + switch (op) { + case glslang::EOpMinInvocations: + case glslang::EOpMaxInvocations: + case glslang::EOpAddInvocations: + case glslang::EOpMinInvocationsNonUniform: + case glslang::EOpMaxInvocationsNonUniform: + case glslang::EOpAddInvocationsNonUniform: + groupOperation = spv::GroupOperationReduce; + break; + case glslang::EOpMinInvocationsInclusiveScan: + case glslang::EOpMaxInvocationsInclusiveScan: + case glslang::EOpAddInvocationsInclusiveScan: + case glslang::EOpMinInvocationsInclusiveScanNonUniform: + case glslang::EOpMaxInvocationsInclusiveScanNonUniform: + case glslang::EOpAddInvocationsInclusiveScanNonUniform: + groupOperation = spv::GroupOperationInclusiveScan; + break; + case glslang::EOpMinInvocationsExclusiveScan: + case glslang::EOpMaxInvocationsExclusiveScan: + case glslang::EOpAddInvocationsExclusiveScan: + case glslang::EOpMinInvocationsExclusiveScanNonUniform: + case glslang::EOpMaxInvocationsExclusiveScanNonUniform: + case glslang::EOpAddInvocationsExclusiveScanNonUniform: + groupOperation = spv::GroupOperationExclusiveScan; + break; + default: + break; + } + spv::IdImmediate scope = { true, builder.makeUintConstant(spv::ScopeSubgroup) }; + spvGroupOperands.push_back(scope); + if (groupOperation != spv::GroupOperationMax) { + spv::IdImmediate groupOp = { false, (unsigned)groupOperation }; + spvGroupOperands.push_back(groupOp); + } + } + + for (auto opIt = operands.begin(); opIt != operands.end(); ++opIt) { + spv::IdImmediate op = { true, *opIt }; + spvGroupOperands.push_back(op); + } + + switch (op) { + case glslang::EOpAnyInvocation: + opCode = spv::OpSubgroupAnyKHR; + break; + case glslang::EOpAllInvocations: + opCode = spv::OpSubgroupAllKHR; + break; + case glslang::EOpAllInvocationsEqual: + opCode = spv::OpSubgroupAllEqualKHR; + break; + case glslang::EOpReadInvocation: + opCode = spv::OpSubgroupReadInvocationKHR; + if (builder.isVectorType(typeId)) + return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands); + break; + case glslang::EOpReadFirstInvocation: + opCode = spv::OpSubgroupFirstInvocationKHR; + break; + case glslang::EOpBallot: + { + // NOTE: According to the spec, the result type of "OpSubgroupBallotKHR" must be a 4 component vector of 32 + // bit integer types. The GLSL built-in function "ballotARB()" assumes the maximum number of invocations in + // a subgroup is 64. Thus, we have to convert uvec4.xy to uint64_t as follow: + // + // result = Bitcast(SubgroupBallotKHR(Predicate).xy) + // + spv::Id uintType = builder.makeUintType(32); + spv::Id uvec4Type = builder.makeVectorType(uintType, 4); + spv::Id result = builder.createOp(spv::OpSubgroupBallotKHR, uvec4Type, spvGroupOperands); + + std::vector components; + components.push_back(builder.createCompositeExtract(result, uintType, 0)); + components.push_back(builder.createCompositeExtract(result, uintType, 1)); + + spv::Id uvec2Type = builder.makeVectorType(uintType, 2); + return builder.createUnaryOp(spv::OpBitcast, typeId, + builder.createCompositeConstruct(uvec2Type, components)); + } + + case glslang::EOpMinInvocations: + case glslang::EOpMaxInvocations: + case glslang::EOpAddInvocations: + case glslang::EOpMinInvocationsInclusiveScan: + case glslang::EOpMaxInvocationsInclusiveScan: + case glslang::EOpAddInvocationsInclusiveScan: + case glslang::EOpMinInvocationsExclusiveScan: + case glslang::EOpMaxInvocationsExclusiveScan: + case glslang::EOpAddInvocationsExclusiveScan: + if (op == glslang::EOpMinInvocations || + op == glslang::EOpMinInvocationsInclusiveScan || + op == glslang::EOpMinInvocationsExclusiveScan) { + if (isFloat) + opCode = spv::OpGroupFMin; + else { + if (isUnsigned) + opCode = spv::OpGroupUMin; + else + opCode = spv::OpGroupSMin; + } + } else if (op == glslang::EOpMaxInvocations || + op == glslang::EOpMaxInvocationsInclusiveScan || + op == glslang::EOpMaxInvocationsExclusiveScan) { + if (isFloat) + opCode = spv::OpGroupFMax; + else { + if (isUnsigned) + opCode = spv::OpGroupUMax; + else + opCode = spv::OpGroupSMax; + } + } else { + if (isFloat) + opCode = spv::OpGroupFAdd; + else + opCode = spv::OpGroupIAdd; + } + + if (builder.isVectorType(typeId)) + return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands); + + break; + case glslang::EOpMinInvocationsNonUniform: + case glslang::EOpMaxInvocationsNonUniform: + case glslang::EOpAddInvocationsNonUniform: + case glslang::EOpMinInvocationsInclusiveScanNonUniform: + case glslang::EOpMaxInvocationsInclusiveScanNonUniform: + case glslang::EOpAddInvocationsInclusiveScanNonUniform: + case glslang::EOpMinInvocationsExclusiveScanNonUniform: + case glslang::EOpMaxInvocationsExclusiveScanNonUniform: + case glslang::EOpAddInvocationsExclusiveScanNonUniform: + if (op == glslang::EOpMinInvocationsNonUniform || + op == glslang::EOpMinInvocationsInclusiveScanNonUniform || + op == glslang::EOpMinInvocationsExclusiveScanNonUniform) { + if (isFloat) + opCode = spv::OpGroupFMinNonUniformAMD; + else { + if (isUnsigned) + opCode = spv::OpGroupUMinNonUniformAMD; + else + opCode = spv::OpGroupSMinNonUniformAMD; + } + } + else if (op == glslang::EOpMaxInvocationsNonUniform || + op == glslang::EOpMaxInvocationsInclusiveScanNonUniform || + op == glslang::EOpMaxInvocationsExclusiveScanNonUniform) { + if (isFloat) + opCode = spv::OpGroupFMaxNonUniformAMD; + else { + if (isUnsigned) + opCode = spv::OpGroupUMaxNonUniformAMD; + else + opCode = spv::OpGroupSMaxNonUniformAMD; + } + } + else { + if (isFloat) + opCode = spv::OpGroupFAddNonUniformAMD; + else + opCode = spv::OpGroupIAddNonUniformAMD; + } + + if (builder.isVectorType(typeId)) + return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands); + + break; + default: + logger->missingFunctionality("invocation operation"); + return spv::NoResult; + } + + assert(opCode != spv::OpNop); + return builder.createOp(opCode, typeId, spvGroupOperands); +} + +// Create group invocation operations on a vector +spv::Id TGlslangToSpvTraverser::CreateInvocationsVectorOperation(spv::Op op, spv::GroupOperation groupOperation, + spv::Id typeId, std::vector& operands) +{ + assert(op == spv::OpGroupFMin || op == spv::OpGroupUMin || op == spv::OpGroupSMin || + op == spv::OpGroupFMax || op == spv::OpGroupUMax || op == spv::OpGroupSMax || + op == spv::OpGroupFAdd || op == spv::OpGroupIAdd || op == spv::OpGroupBroadcast || + op == spv::OpSubgroupReadInvocationKHR || + op == spv::OpGroupFMinNonUniformAMD || op == spv::OpGroupUMinNonUniformAMD || + op == spv::OpGroupSMinNonUniformAMD || + op == spv::OpGroupFMaxNonUniformAMD || op == spv::OpGroupUMaxNonUniformAMD || + op == spv::OpGroupSMaxNonUniformAMD || + op == spv::OpGroupFAddNonUniformAMD || op == spv::OpGroupIAddNonUniformAMD); + + // Handle group invocation operations scalar by scalar. + // The result type is the same type as the original type. + // The algorithm is to: + // - break the vector into scalars + // - apply the operation to each scalar + // - make a vector out the scalar results + + // get the types sorted out + int numComponents = builder.getNumComponents(operands[0]); + spv::Id scalarType = builder.getScalarTypeId(builder.getTypeId(operands[0])); + std::vector results; + + // do each scalar op + for (int comp = 0; comp < numComponents; ++comp) { + std::vector indexes; + indexes.push_back(comp); + spv::IdImmediate scalar = { true, builder.createCompositeExtract(operands[0], scalarType, indexes) }; + std::vector spvGroupOperands; + if (op == spv::OpSubgroupReadInvocationKHR) { + spvGroupOperands.push_back(scalar); + spv::IdImmediate operand = { true, operands[1] }; + spvGroupOperands.push_back(operand); + } else if (op == spv::OpGroupBroadcast) { + spv::IdImmediate scope = { true, builder.makeUintConstant(spv::ScopeSubgroup) }; + spvGroupOperands.push_back(scope); + spvGroupOperands.push_back(scalar); + spv::IdImmediate operand = { true, operands[1] }; + spvGroupOperands.push_back(operand); + } else { + spv::IdImmediate scope = { true, builder.makeUintConstant(spv::ScopeSubgroup) }; + spvGroupOperands.push_back(scope); + spv::IdImmediate groupOp = { false, (unsigned)groupOperation }; + spvGroupOperands.push_back(groupOp); + spvGroupOperands.push_back(scalar); + } + + results.push_back(builder.createOp(op, scalarType, spvGroupOperands)); + } + + // put the pieces together + return builder.createCompositeConstruct(typeId, results); +} + +// Create subgroup invocation operations. +spv::Id TGlslangToSpvTraverser::createSubgroupOperation(glslang::TOperator op, spv::Id typeId, + std::vector& operands, glslang::TBasicType typeProxy) +{ + // Add the required capabilities. + switch (op) { + case glslang::EOpSubgroupElect: + builder.addCapability(spv::CapabilityGroupNonUniform); + break; + case glslang::EOpSubgroupAll: + case glslang::EOpSubgroupAny: + case glslang::EOpSubgroupAllEqual: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformVote); + break; + case glslang::EOpSubgroupBroadcast: + case glslang::EOpSubgroupBroadcastFirst: + case glslang::EOpSubgroupBallot: + case glslang::EOpSubgroupInverseBallot: + case glslang::EOpSubgroupBallotBitExtract: + case glslang::EOpSubgroupBallotBitCount: + case glslang::EOpSubgroupBallotInclusiveBitCount: + case glslang::EOpSubgroupBallotExclusiveBitCount: + case glslang::EOpSubgroupBallotFindLSB: + case glslang::EOpSubgroupBallotFindMSB: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + break; + case glslang::EOpSubgroupShuffle: + case glslang::EOpSubgroupShuffleXor: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformShuffle); + break; + case glslang::EOpSubgroupShuffleUp: + case glslang::EOpSubgroupShuffleDown: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformShuffleRelative); + break; + case glslang::EOpSubgroupAdd: + case glslang::EOpSubgroupMul: + case glslang::EOpSubgroupMin: + case glslang::EOpSubgroupMax: + case glslang::EOpSubgroupAnd: + case glslang::EOpSubgroupOr: + case glslang::EOpSubgroupXor: + case glslang::EOpSubgroupInclusiveAdd: + case glslang::EOpSubgroupInclusiveMul: + case glslang::EOpSubgroupInclusiveMin: + case glslang::EOpSubgroupInclusiveMax: + case glslang::EOpSubgroupInclusiveAnd: + case glslang::EOpSubgroupInclusiveOr: + case glslang::EOpSubgroupInclusiveXor: + case glslang::EOpSubgroupExclusiveAdd: + case glslang::EOpSubgroupExclusiveMul: + case glslang::EOpSubgroupExclusiveMin: + case glslang::EOpSubgroupExclusiveMax: + case glslang::EOpSubgroupExclusiveAnd: + case glslang::EOpSubgroupExclusiveOr: + case glslang::EOpSubgroupExclusiveXor: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformArithmetic); + break; + case glslang::EOpSubgroupClusteredAdd: + case glslang::EOpSubgroupClusteredMul: + case glslang::EOpSubgroupClusteredMin: + case glslang::EOpSubgroupClusteredMax: + case glslang::EOpSubgroupClusteredAnd: + case glslang::EOpSubgroupClusteredOr: + case glslang::EOpSubgroupClusteredXor: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformClustered); + break; + case glslang::EOpSubgroupQuadBroadcast: + case glslang::EOpSubgroupQuadSwapHorizontal: + case glslang::EOpSubgroupQuadSwapVertical: + case glslang::EOpSubgroupQuadSwapDiagonal: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformQuad); + break; + case glslang::EOpSubgroupPartitionedAdd: + case glslang::EOpSubgroupPartitionedMul: + case glslang::EOpSubgroupPartitionedMin: + case glslang::EOpSubgroupPartitionedMax: + case glslang::EOpSubgroupPartitionedAnd: + case glslang::EOpSubgroupPartitionedOr: + case glslang::EOpSubgroupPartitionedXor: + case glslang::EOpSubgroupPartitionedInclusiveAdd: + case glslang::EOpSubgroupPartitionedInclusiveMul: + case glslang::EOpSubgroupPartitionedInclusiveMin: + case glslang::EOpSubgroupPartitionedInclusiveMax: + case glslang::EOpSubgroupPartitionedInclusiveAnd: + case glslang::EOpSubgroupPartitionedInclusiveOr: + case glslang::EOpSubgroupPartitionedInclusiveXor: + case glslang::EOpSubgroupPartitionedExclusiveAdd: + case glslang::EOpSubgroupPartitionedExclusiveMul: + case glslang::EOpSubgroupPartitionedExclusiveMin: + case glslang::EOpSubgroupPartitionedExclusiveMax: + case glslang::EOpSubgroupPartitionedExclusiveAnd: + case glslang::EOpSubgroupPartitionedExclusiveOr: + case glslang::EOpSubgroupPartitionedExclusiveXor: + builder.addExtension(spv::E_SPV_NV_shader_subgroup_partitioned); + builder.addCapability(spv::CapabilityGroupNonUniformPartitionedNV); + break; + default: assert(0 && "Unhandled subgroup operation!"); + } + + + const bool isUnsigned = isTypeUnsignedInt(typeProxy); + const bool isFloat = isTypeFloat(typeProxy); + const bool isBool = typeProxy == glslang::EbtBool; + + spv::Op opCode = spv::OpNop; + + // Figure out which opcode to use. + switch (op) { + case glslang::EOpSubgroupElect: opCode = spv::OpGroupNonUniformElect; break; + case glslang::EOpSubgroupAll: opCode = spv::OpGroupNonUniformAll; break; + case glslang::EOpSubgroupAny: opCode = spv::OpGroupNonUniformAny; break; + case glslang::EOpSubgroupAllEqual: opCode = spv::OpGroupNonUniformAllEqual; break; + case glslang::EOpSubgroupBroadcast: opCode = spv::OpGroupNonUniformBroadcast; break; + case glslang::EOpSubgroupBroadcastFirst: opCode = spv::OpGroupNonUniformBroadcastFirst; break; + case glslang::EOpSubgroupBallot: opCode = spv::OpGroupNonUniformBallot; break; + case glslang::EOpSubgroupInverseBallot: opCode = spv::OpGroupNonUniformInverseBallot; break; + case glslang::EOpSubgroupBallotBitExtract: opCode = spv::OpGroupNonUniformBallotBitExtract; break; + case glslang::EOpSubgroupBallotBitCount: + case glslang::EOpSubgroupBallotInclusiveBitCount: + case glslang::EOpSubgroupBallotExclusiveBitCount: opCode = spv::OpGroupNonUniformBallotBitCount; break; + case glslang::EOpSubgroupBallotFindLSB: opCode = spv::OpGroupNonUniformBallotFindLSB; break; + case glslang::EOpSubgroupBallotFindMSB: opCode = spv::OpGroupNonUniformBallotFindMSB; break; + case glslang::EOpSubgroupShuffle: opCode = spv::OpGroupNonUniformShuffle; break; + case glslang::EOpSubgroupShuffleXor: opCode = spv::OpGroupNonUniformShuffleXor; break; + case glslang::EOpSubgroupShuffleUp: opCode = spv::OpGroupNonUniformShuffleUp; break; + case glslang::EOpSubgroupShuffleDown: opCode = spv::OpGroupNonUniformShuffleDown; break; + case glslang::EOpSubgroupAdd: + case glslang::EOpSubgroupInclusiveAdd: + case glslang::EOpSubgroupExclusiveAdd: + case glslang::EOpSubgroupClusteredAdd: + case glslang::EOpSubgroupPartitionedAdd: + case glslang::EOpSubgroupPartitionedInclusiveAdd: + case glslang::EOpSubgroupPartitionedExclusiveAdd: + if (isFloat) { + opCode = spv::OpGroupNonUniformFAdd; + } else { + opCode = spv::OpGroupNonUniformIAdd; + } + break; + case glslang::EOpSubgroupMul: + case glslang::EOpSubgroupInclusiveMul: + case glslang::EOpSubgroupExclusiveMul: + case glslang::EOpSubgroupClusteredMul: + case glslang::EOpSubgroupPartitionedMul: + case glslang::EOpSubgroupPartitionedInclusiveMul: + case glslang::EOpSubgroupPartitionedExclusiveMul: + if (isFloat) { + opCode = spv::OpGroupNonUniformFMul; + } else { + opCode = spv::OpGroupNonUniformIMul; + } + break; + case glslang::EOpSubgroupMin: + case glslang::EOpSubgroupInclusiveMin: + case glslang::EOpSubgroupExclusiveMin: + case glslang::EOpSubgroupClusteredMin: + case glslang::EOpSubgroupPartitionedMin: + case glslang::EOpSubgroupPartitionedInclusiveMin: + case glslang::EOpSubgroupPartitionedExclusiveMin: + if (isFloat) { + opCode = spv::OpGroupNonUniformFMin; + } else if (isUnsigned) { + opCode = spv::OpGroupNonUniformUMin; + } else { + opCode = spv::OpGroupNonUniformSMin; + } + break; + case glslang::EOpSubgroupMax: + case glslang::EOpSubgroupInclusiveMax: + case glslang::EOpSubgroupExclusiveMax: + case glslang::EOpSubgroupClusteredMax: + case glslang::EOpSubgroupPartitionedMax: + case glslang::EOpSubgroupPartitionedInclusiveMax: + case glslang::EOpSubgroupPartitionedExclusiveMax: + if (isFloat) { + opCode = spv::OpGroupNonUniformFMax; + } else if (isUnsigned) { + opCode = spv::OpGroupNonUniformUMax; + } else { + opCode = spv::OpGroupNonUniformSMax; + } + break; + case glslang::EOpSubgroupAnd: + case glslang::EOpSubgroupInclusiveAnd: + case glslang::EOpSubgroupExclusiveAnd: + case glslang::EOpSubgroupClusteredAnd: + case glslang::EOpSubgroupPartitionedAnd: + case glslang::EOpSubgroupPartitionedInclusiveAnd: + case glslang::EOpSubgroupPartitionedExclusiveAnd: + if (isBool) { + opCode = spv::OpGroupNonUniformLogicalAnd; + } else { + opCode = spv::OpGroupNonUniformBitwiseAnd; + } + break; + case glslang::EOpSubgroupOr: + case glslang::EOpSubgroupInclusiveOr: + case glslang::EOpSubgroupExclusiveOr: + case glslang::EOpSubgroupClusteredOr: + case glslang::EOpSubgroupPartitionedOr: + case glslang::EOpSubgroupPartitionedInclusiveOr: + case glslang::EOpSubgroupPartitionedExclusiveOr: + if (isBool) { + opCode = spv::OpGroupNonUniformLogicalOr; + } else { + opCode = spv::OpGroupNonUniformBitwiseOr; + } + break; + case glslang::EOpSubgroupXor: + case glslang::EOpSubgroupInclusiveXor: + case glslang::EOpSubgroupExclusiveXor: + case glslang::EOpSubgroupClusteredXor: + case glslang::EOpSubgroupPartitionedXor: + case glslang::EOpSubgroupPartitionedInclusiveXor: + case glslang::EOpSubgroupPartitionedExclusiveXor: + if (isBool) { + opCode = spv::OpGroupNonUniformLogicalXor; + } else { + opCode = spv::OpGroupNonUniformBitwiseXor; + } + break; + case glslang::EOpSubgroupQuadBroadcast: opCode = spv::OpGroupNonUniformQuadBroadcast; break; + case glslang::EOpSubgroupQuadSwapHorizontal: + case glslang::EOpSubgroupQuadSwapVertical: + case glslang::EOpSubgroupQuadSwapDiagonal: opCode = spv::OpGroupNonUniformQuadSwap; break; + default: assert(0 && "Unhandled subgroup operation!"); + } + + // get the right Group Operation + spv::GroupOperation groupOperation = spv::GroupOperationMax; + switch (op) { + default: + break; + case glslang::EOpSubgroupBallotBitCount: + case glslang::EOpSubgroupAdd: + case glslang::EOpSubgroupMul: + case glslang::EOpSubgroupMin: + case glslang::EOpSubgroupMax: + case glslang::EOpSubgroupAnd: + case glslang::EOpSubgroupOr: + case glslang::EOpSubgroupXor: + groupOperation = spv::GroupOperationReduce; + break; + case glslang::EOpSubgroupBallotInclusiveBitCount: + case glslang::EOpSubgroupInclusiveAdd: + case glslang::EOpSubgroupInclusiveMul: + case glslang::EOpSubgroupInclusiveMin: + case glslang::EOpSubgroupInclusiveMax: + case glslang::EOpSubgroupInclusiveAnd: + case glslang::EOpSubgroupInclusiveOr: + case glslang::EOpSubgroupInclusiveXor: + groupOperation = spv::GroupOperationInclusiveScan; + break; + case glslang::EOpSubgroupBallotExclusiveBitCount: + case glslang::EOpSubgroupExclusiveAdd: + case glslang::EOpSubgroupExclusiveMul: + case glslang::EOpSubgroupExclusiveMin: + case glslang::EOpSubgroupExclusiveMax: + case glslang::EOpSubgroupExclusiveAnd: + case glslang::EOpSubgroupExclusiveOr: + case glslang::EOpSubgroupExclusiveXor: + groupOperation = spv::GroupOperationExclusiveScan; + break; + case glslang::EOpSubgroupClusteredAdd: + case glslang::EOpSubgroupClusteredMul: + case glslang::EOpSubgroupClusteredMin: + case glslang::EOpSubgroupClusteredMax: + case glslang::EOpSubgroupClusteredAnd: + case glslang::EOpSubgroupClusteredOr: + case glslang::EOpSubgroupClusteredXor: + groupOperation = spv::GroupOperationClusteredReduce; + break; + case glslang::EOpSubgroupPartitionedAdd: + case glslang::EOpSubgroupPartitionedMul: + case glslang::EOpSubgroupPartitionedMin: + case glslang::EOpSubgroupPartitionedMax: + case glslang::EOpSubgroupPartitionedAnd: + case glslang::EOpSubgroupPartitionedOr: + case glslang::EOpSubgroupPartitionedXor: + groupOperation = spv::GroupOperationPartitionedReduceNV; + break; + case glslang::EOpSubgroupPartitionedInclusiveAdd: + case glslang::EOpSubgroupPartitionedInclusiveMul: + case glslang::EOpSubgroupPartitionedInclusiveMin: + case glslang::EOpSubgroupPartitionedInclusiveMax: + case glslang::EOpSubgroupPartitionedInclusiveAnd: + case glslang::EOpSubgroupPartitionedInclusiveOr: + case glslang::EOpSubgroupPartitionedInclusiveXor: + groupOperation = spv::GroupOperationPartitionedInclusiveScanNV; + break; + case glslang::EOpSubgroupPartitionedExclusiveAdd: + case glslang::EOpSubgroupPartitionedExclusiveMul: + case glslang::EOpSubgroupPartitionedExclusiveMin: + case glslang::EOpSubgroupPartitionedExclusiveMax: + case glslang::EOpSubgroupPartitionedExclusiveAnd: + case glslang::EOpSubgroupPartitionedExclusiveOr: + case glslang::EOpSubgroupPartitionedExclusiveXor: + groupOperation = spv::GroupOperationPartitionedExclusiveScanNV; + break; + } + + // build the instruction + std::vector spvGroupOperands; + + // Every operation begins with the Execution Scope operand. + spv::IdImmediate executionScope = { true, builder.makeUintConstant(spv::ScopeSubgroup) }; + spvGroupOperands.push_back(executionScope); + + // Next, for all operations that use a Group Operation, push that as an operand. + if (groupOperation != spv::GroupOperationMax) { + spv::IdImmediate groupOperand = { false, (unsigned)groupOperation }; + spvGroupOperands.push_back(groupOperand); + } + + // Push back the operands next. + for (auto opIt = operands.cbegin(); opIt != operands.cend(); ++opIt) { + spv::IdImmediate operand = { true, *opIt }; + spvGroupOperands.push_back(operand); + } + + // Some opcodes have additional operands. + spv::Id directionId = spv::NoResult; + switch (op) { + default: break; + case glslang::EOpSubgroupQuadSwapHorizontal: directionId = builder.makeUintConstant(0); break; + case glslang::EOpSubgroupQuadSwapVertical: directionId = builder.makeUintConstant(1); break; + case glslang::EOpSubgroupQuadSwapDiagonal: directionId = builder.makeUintConstant(2); break; + } + if (directionId != spv::NoResult) { + spv::IdImmediate direction = { true, directionId }; + spvGroupOperands.push_back(direction); + } + + return builder.createOp(opCode, typeId, spvGroupOperands); +} + +spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv::Decoration precision, + spv::Id typeId, std::vector& operands, glslang::TBasicType typeProxy) +{ + bool isUnsigned = isTypeUnsignedInt(typeProxy); + bool isFloat = isTypeFloat(typeProxy); + + spv::Op opCode = spv::OpNop; + int extBuiltins = -1; + int libCall = -1; + size_t consumedOperands = operands.size(); + spv::Id typeId0 = 0; + if (consumedOperands > 0) + typeId0 = builder.getTypeId(operands[0]); + spv::Id typeId1 = 0; + if (consumedOperands > 1) + typeId1 = builder.getTypeId(operands[1]); + spv::Id frexpIntType = 0; + + switch (op) { + case glslang::EOpMin: + if (isFloat) + libCall = nanMinMaxClamp ? spv::GLSLstd450NMin : spv::GLSLstd450FMin; + else if (isUnsigned) + libCall = spv::GLSLstd450UMin; + else + libCall = spv::GLSLstd450SMin; + builder.promoteScalar(precision, operands.front(), operands.back()); + break; + case glslang::EOpModf: + libCall = spv::GLSLstd450Modf; + break; + case glslang::EOpMax: + if (isFloat) + libCall = nanMinMaxClamp ? spv::GLSLstd450NMax : spv::GLSLstd450FMax; + else if (isUnsigned) + libCall = spv::GLSLstd450UMax; + else + libCall = spv::GLSLstd450SMax; + builder.promoteScalar(precision, operands.front(), operands.back()); + break; + case glslang::EOpPow: + libCall = spv::GLSLstd450Pow; + break; + case glslang::EOpDot: + opCode = spv::OpDot; + break; + case glslang::EOpAtan: + libCall = spv::GLSLstd450Atan2; + break; + + case glslang::EOpClamp: + if (isFloat) + libCall = nanMinMaxClamp ? spv::GLSLstd450NClamp : spv::GLSLstd450FClamp; + else if (isUnsigned) + libCall = spv::GLSLstd450UClamp; + else + libCall = spv::GLSLstd450SClamp; + builder.promoteScalar(precision, operands.front(), operands[1]); + builder.promoteScalar(precision, operands.front(), operands[2]); + break; + case glslang::EOpMix: + if (! builder.isBoolType(builder.getScalarTypeId(builder.getTypeId(operands.back())))) { + assert(isFloat); + libCall = spv::GLSLstd450FMix; + } else { + opCode = spv::OpSelect; + std::swap(operands.front(), operands.back()); + } + builder.promoteScalar(precision, operands.front(), operands.back()); + break; + case glslang::EOpStep: + libCall = spv::GLSLstd450Step; + builder.promoteScalar(precision, operands.front(), operands.back()); + break; + case glslang::EOpSmoothStep: + libCall = spv::GLSLstd450SmoothStep; + builder.promoteScalar(precision, operands[0], operands[2]); + builder.promoteScalar(precision, operands[1], operands[2]); + break; + + case glslang::EOpDistance: + libCall = spv::GLSLstd450Distance; + break; + case glslang::EOpCross: + libCall = spv::GLSLstd450Cross; + break; + case glslang::EOpFaceForward: + libCall = spv::GLSLstd450FaceForward; + break; + case glslang::EOpReflect: + libCall = spv::GLSLstd450Reflect; + break; + case glslang::EOpRefract: + libCall = spv::GLSLstd450Refract; + break; + case glslang::EOpBarrier: + { + // This is for the extended controlBarrier function, with four operands. + // The unextended barrier() goes through createNoArgOperation. + assert(operands.size() == 4); + unsigned int executionScope = builder.getConstantScalar(operands[0]); + unsigned int memoryScope = builder.getConstantScalar(operands[1]); + unsigned int semantics = builder.getConstantScalar(operands[2]) | builder.getConstantScalar(operands[3]); + builder.createControlBarrier((spv::Scope)executionScope, (spv::Scope)memoryScope, + (spv::MemorySemanticsMask)semantics); + if (semantics & (spv::MemorySemanticsMakeAvailableKHRMask | + spv::MemorySemanticsMakeVisibleKHRMask | + spv::MemorySemanticsOutputMemoryKHRMask | + spv::MemorySemanticsVolatileMask)) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } + if (glslangIntermediate->usingVulkanMemoryModel() && (executionScope == spv::ScopeDevice || + memoryScope == spv::ScopeDevice)) { + builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR); + } + return 0; + } + break; + case glslang::EOpMemoryBarrier: + { + // This is for the extended memoryBarrier function, with three operands. + // The unextended memoryBarrier() goes through createNoArgOperation. + assert(operands.size() == 3); + unsigned int memoryScope = builder.getConstantScalar(operands[0]); + unsigned int semantics = builder.getConstantScalar(operands[1]) | builder.getConstantScalar(operands[2]); + builder.createMemoryBarrier((spv::Scope)memoryScope, (spv::MemorySemanticsMask)semantics); + if (semantics & (spv::MemorySemanticsMakeAvailableKHRMask | + spv::MemorySemanticsMakeVisibleKHRMask | + spv::MemorySemanticsOutputMemoryKHRMask | + spv::MemorySemanticsVolatileMask)) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } + if (glslangIntermediate->usingVulkanMemoryModel() && memoryScope == spv::ScopeDevice) { + builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR); + } + return 0; + } + break; + +#ifndef GLSLANG_WEB + case glslang::EOpInterpolateAtSample: + if (typeProxy == glslang::EbtFloat16) + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + libCall = spv::GLSLstd450InterpolateAtSample; + break; + case glslang::EOpInterpolateAtOffset: + if (typeProxy == glslang::EbtFloat16) + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + libCall = spv::GLSLstd450InterpolateAtOffset; + break; + case glslang::EOpAddCarry: + opCode = spv::OpIAddCarry; + typeId = builder.makeStructResultType(typeId0, typeId0); + consumedOperands = 2; + break; + case glslang::EOpSubBorrow: + opCode = spv::OpISubBorrow; + typeId = builder.makeStructResultType(typeId0, typeId0); + consumedOperands = 2; + break; + case glslang::EOpUMulExtended: + opCode = spv::OpUMulExtended; + typeId = builder.makeStructResultType(typeId0, typeId0); + consumedOperands = 2; + break; + case glslang::EOpIMulExtended: + opCode = spv::OpSMulExtended; + typeId = builder.makeStructResultType(typeId0, typeId0); + consumedOperands = 2; + break; + case glslang::EOpBitfieldExtract: + if (isUnsigned) + opCode = spv::OpBitFieldUExtract; + else + opCode = spv::OpBitFieldSExtract; + break; + case glslang::EOpBitfieldInsert: + opCode = spv::OpBitFieldInsert; + break; + + case glslang::EOpFma: + libCall = spv::GLSLstd450Fma; + break; + case glslang::EOpFrexp: + { + libCall = spv::GLSLstd450FrexpStruct; + assert(builder.isPointerType(typeId1)); + typeId1 = builder.getContainedTypeId(typeId1); + int width = builder.getScalarTypeWidth(typeId1); + if (width == 16) + // Using 16-bit exp operand, enable extension SPV_AMD_gpu_shader_int16 + builder.addExtension(spv::E_SPV_AMD_gpu_shader_int16); + if (builder.getNumComponents(operands[0]) == 1) + frexpIntType = builder.makeIntegerType(width, true); + else + frexpIntType = builder.makeVectorType(builder.makeIntegerType(width, true), + builder.getNumComponents(operands[0])); + typeId = builder.makeStructResultType(typeId0, frexpIntType); + consumedOperands = 1; + } + break; + case glslang::EOpLdexp: + libCall = spv::GLSLstd450Ldexp; + break; + + case glslang::EOpReadInvocation: + return createInvocationsOperation(op, typeId, operands, typeProxy); + + case glslang::EOpSubgroupBroadcast: + case glslang::EOpSubgroupBallotBitExtract: + case glslang::EOpSubgroupShuffle: + case glslang::EOpSubgroupShuffleXor: + case glslang::EOpSubgroupShuffleUp: + case glslang::EOpSubgroupShuffleDown: + case glslang::EOpSubgroupClusteredAdd: + case glslang::EOpSubgroupClusteredMul: + case glslang::EOpSubgroupClusteredMin: + case glslang::EOpSubgroupClusteredMax: + case glslang::EOpSubgroupClusteredAnd: + case glslang::EOpSubgroupClusteredOr: + case glslang::EOpSubgroupClusteredXor: + case glslang::EOpSubgroupQuadBroadcast: + case glslang::EOpSubgroupPartitionedAdd: + case glslang::EOpSubgroupPartitionedMul: + case glslang::EOpSubgroupPartitionedMin: + case glslang::EOpSubgroupPartitionedMax: + case glslang::EOpSubgroupPartitionedAnd: + case glslang::EOpSubgroupPartitionedOr: + case glslang::EOpSubgroupPartitionedXor: + case glslang::EOpSubgroupPartitionedInclusiveAdd: + case glslang::EOpSubgroupPartitionedInclusiveMul: + case glslang::EOpSubgroupPartitionedInclusiveMin: + case glslang::EOpSubgroupPartitionedInclusiveMax: + case glslang::EOpSubgroupPartitionedInclusiveAnd: + case glslang::EOpSubgroupPartitionedInclusiveOr: + case glslang::EOpSubgroupPartitionedInclusiveXor: + case glslang::EOpSubgroupPartitionedExclusiveAdd: + case glslang::EOpSubgroupPartitionedExclusiveMul: + case glslang::EOpSubgroupPartitionedExclusiveMin: + case glslang::EOpSubgroupPartitionedExclusiveMax: + case glslang::EOpSubgroupPartitionedExclusiveAnd: + case glslang::EOpSubgroupPartitionedExclusiveOr: + case glslang::EOpSubgroupPartitionedExclusiveXor: + return createSubgroupOperation(op, typeId, operands, typeProxy); + + case glslang::EOpSwizzleInvocations: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::SwizzleInvocationsAMD; + break; + case glslang::EOpSwizzleInvocationsMasked: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::SwizzleInvocationsMaskedAMD; + break; + case glslang::EOpWriteInvocation: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::WriteInvocationAMD; + break; + + case glslang::EOpMin3: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax); + if (isFloat) + libCall = spv::FMin3AMD; + else { + if (isUnsigned) + libCall = spv::UMin3AMD; + else + libCall = spv::SMin3AMD; + } + break; + case glslang::EOpMax3: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax); + if (isFloat) + libCall = spv::FMax3AMD; + else { + if (isUnsigned) + libCall = spv::UMax3AMD; + else + libCall = spv::SMax3AMD; + } + break; + case glslang::EOpMid3: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax); + if (isFloat) + libCall = spv::FMid3AMD; + else { + if (isUnsigned) + libCall = spv::UMid3AMD; + else + libCall = spv::SMid3AMD; + } + break; + + case glslang::EOpInterpolateAtVertex: + if (typeProxy == glslang::EbtFloat16) + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + libCall = spv::InterpolateAtVertexAMD; + break; + + case glslang::EOpReportIntersection: + typeId = builder.makeBoolType(); + opCode = spv::OpReportIntersectionKHR; + break; + case glslang::EOpTrace: + builder.createNoResultOp(spv::OpTraceRayKHR, operands); + return 0; + case glslang::EOpExecuteCallable: + builder.createNoResultOp(spv::OpExecuteCallableKHR, operands); + return 0; + + case glslang::EOpRayQueryInitialize: + builder.createNoResultOp(spv::OpRayQueryInitializeKHR, operands); + return 0; + case glslang::EOpRayQueryTerminate: + builder.createNoResultOp(spv::OpRayQueryTerminateKHR, operands); + return 0; + case glslang::EOpRayQueryGenerateIntersection: + builder.createNoResultOp(spv::OpRayQueryGenerateIntersectionKHR, operands); + return 0; + case glslang::EOpRayQueryConfirmIntersection: + builder.createNoResultOp(spv::OpRayQueryConfirmIntersectionKHR, operands); + return 0; + case glslang::EOpRayQueryProceed: + typeId = builder.makeBoolType(); + opCode = spv::OpRayQueryProceedKHR; + break; + case glslang::EOpRayQueryGetIntersectionType: + typeId = builder.makeUintType(32); + opCode = spv::OpRayQueryGetIntersectionTypeKHR; + break; + case glslang::EOpRayQueryGetRayTMin: + typeId = builder.makeFloatType(32); + opCode = spv::OpRayQueryGetRayTMinKHR; + break; + case glslang::EOpRayQueryGetRayFlags: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetRayFlagsKHR; + break; + case glslang::EOpRayQueryGetIntersectionT: + typeId = builder.makeFloatType(32); + opCode = spv::OpRayQueryGetIntersectionTKHR; + break; + case glslang::EOpRayQueryGetIntersectionInstanceCustomIndex: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionInstanceCustomIndexKHR; + break; + case glslang::EOpRayQueryGetIntersectionInstanceId: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionInstanceIdKHR; + break; + case glslang::EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR; + break; + case glslang::EOpRayQueryGetIntersectionGeometryIndex: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionGeometryIndexKHR; + break; + case glslang::EOpRayQueryGetIntersectionPrimitiveIndex: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionPrimitiveIndexKHR; + break; + case glslang::EOpRayQueryGetIntersectionBarycentrics: + typeId = builder.makeVectorType(builder.makeFloatType(32), 2); + opCode = spv::OpRayQueryGetIntersectionBarycentricsKHR; + break; + case glslang::EOpRayQueryGetIntersectionFrontFace: + typeId = builder.makeBoolType(); + opCode = spv::OpRayQueryGetIntersectionFrontFaceKHR; + break; + case glslang::EOpRayQueryGetIntersectionCandidateAABBOpaque: + typeId = builder.makeBoolType(); + opCode = spv::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR; + break; + case glslang::EOpRayQueryGetIntersectionObjectRayDirection: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpRayQueryGetIntersectionObjectRayDirectionKHR; + break; + case glslang::EOpRayQueryGetIntersectionObjectRayOrigin: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpRayQueryGetIntersectionObjectRayOriginKHR; + break; + case glslang::EOpRayQueryGetWorldRayDirection: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpRayQueryGetWorldRayDirectionKHR; + break; + case glslang::EOpRayQueryGetWorldRayOrigin: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpRayQueryGetWorldRayOriginKHR; + break; + case glslang::EOpRayQueryGetIntersectionObjectToWorld: + typeId = builder.makeMatrixType(builder.makeFloatType(32), 4, 3); + opCode = spv::OpRayQueryGetIntersectionObjectToWorldKHR; + break; + case glslang::EOpRayQueryGetIntersectionWorldToObject: + typeId = builder.makeMatrixType(builder.makeFloatType(32), 4, 3); + opCode = spv::OpRayQueryGetIntersectionWorldToObjectKHR; + break; + case glslang::EOpWritePackedPrimitiveIndices4x8NV: + builder.createNoResultOp(spv::OpWritePackedPrimitiveIndices4x8NV, operands); + return 0; + case glslang::EOpCooperativeMatrixMulAdd: + opCode = spv::OpCooperativeMatrixMulAddNV; + break; +#endif // GLSLANG_WEB + default: + return 0; + } + + spv::Id id = 0; + if (libCall >= 0) { + // Use an extended instruction from the standard library. + // Construct the call arguments, without modifying the original operands vector. + // We might need the remaining arguments, e.g. in the EOpFrexp case. + std::vector callArguments(operands.begin(), operands.begin() + consumedOperands); + id = builder.createBuiltinCall(typeId, extBuiltins >= 0 ? extBuiltins : stdBuiltins, libCall, callArguments); + } else if (opCode == spv::OpDot && !isFloat) { + // int dot(int, int) + // NOTE: never called for scalar/vector1, this is turned into simple mul before this can be reached + const int componentCount = builder.getNumComponents(operands[0]); + spv::Id mulOp = builder.createBinOp(spv::OpIMul, builder.getTypeId(operands[0]), operands[0], operands[1]); + builder.setPrecision(mulOp, precision); + id = builder.createCompositeExtract(mulOp, typeId, 0); + for (int i = 1; i < componentCount; ++i) { + builder.setPrecision(id, precision); + id = builder.createBinOp(spv::OpIAdd, typeId, id, builder.createCompositeExtract(mulOp, typeId, i)); + } + } else { + switch (consumedOperands) { + case 0: + // should all be handled by visitAggregate and createNoArgOperation + assert(0); + return 0; + case 1: + // should all be handled by createUnaryOperation + assert(0); + return 0; + case 2: + id = builder.createBinOp(opCode, typeId, operands[0], operands[1]); + break; + default: + // anything 3 or over doesn't have l-value operands, so all should be consumed + assert(consumedOperands == operands.size()); + id = builder.createOp(opCode, typeId, operands); + break; + } + } + +#ifndef GLSLANG_WEB + // Decode the return types that were structures + switch (op) { + case glslang::EOpAddCarry: + case glslang::EOpSubBorrow: + builder.createStore(builder.createCompositeExtract(id, typeId0, 1), operands[2]); + id = builder.createCompositeExtract(id, typeId0, 0); + break; + case glslang::EOpUMulExtended: + case glslang::EOpIMulExtended: + builder.createStore(builder.createCompositeExtract(id, typeId0, 0), operands[3]); + builder.createStore(builder.createCompositeExtract(id, typeId0, 1), operands[2]); + break; + case glslang::EOpFrexp: + { + assert(operands.size() == 2); + if (builder.isFloatType(builder.getScalarTypeId(typeId1))) { + // "exp" is floating-point type (from HLSL intrinsic) + spv::Id member1 = builder.createCompositeExtract(id, frexpIntType, 1); + member1 = builder.createUnaryOp(spv::OpConvertSToF, typeId1, member1); + builder.createStore(member1, operands[1]); + } else + // "exp" is integer type (from GLSL built-in function) + builder.createStore(builder.createCompositeExtract(id, frexpIntType, 1), operands[1]); + id = builder.createCompositeExtract(id, typeId0, 0); + } + break; + default: + break; + } +#endif + + return builder.setPrecision(id, precision); +} + +// Intrinsics with no arguments (or no return value, and no precision). +spv::Id TGlslangToSpvTraverser::createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId) +{ + // GLSL memory barriers use queuefamily scope in new model, device scope in old model + spv::Scope memoryBarrierScope = glslangIntermediate->usingVulkanMemoryModel() ? + spv::ScopeQueueFamilyKHR : spv::ScopeDevice; + + switch (op) { + case glslang::EOpBarrier: + if (glslangIntermediate->getStage() == EShLangTessControl) { + if (glslangIntermediate->usingVulkanMemoryModel()) { + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup, + spv::MemorySemanticsOutputMemoryKHRMask | + spv::MemorySemanticsAcquireReleaseMask); + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } else { + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeInvocation, spv::MemorySemanticsMaskNone); + } + } else { + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup, + spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + } + return 0; + case glslang::EOpMemoryBarrier: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpMemoryBarrierBuffer: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpMemoryBarrierShared: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpGroupMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeWorkgroup, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return 0; +#ifndef GLSLANG_WEB + case glslang::EOpMemoryBarrierAtomicCounter: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsAtomicCounterMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpMemoryBarrierImage: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpAllMemoryBarrierWithGroupSync: + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeDevice, + spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpDeviceMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpDeviceMemoryBarrierWithGroupSync: + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeDevice, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpWorkgroupMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeWorkgroup, spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpWorkgroupMemoryBarrierWithGroupSync: + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup, + spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpSubgroupBarrier: + builder.createControlBarrier(spv::ScopeSubgroup, spv::ScopeSubgroup, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + case glslang::EOpSubgroupMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + case glslang::EOpSubgroupMemoryBarrierBuffer: + builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + case glslang::EOpSubgroupMemoryBarrierImage: + builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + case glslang::EOpSubgroupMemoryBarrierShared: + builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + + case glslang::EOpEmitVertex: + builder.createNoResultOp(spv::OpEmitVertex); + return 0; + case glslang::EOpEndPrimitive: + builder.createNoResultOp(spv::OpEndPrimitive); + return 0; + + case glslang::EOpSubgroupElect: { + std::vector operands; + return createSubgroupOperation(op, typeId, operands, glslang::EbtVoid); + } + case glslang::EOpTime: + { + std::vector args; // Dummy arguments + spv::Id id = builder.createBuiltinCall(typeId, getExtBuiltins(spv::E_SPV_AMD_gcn_shader), spv::TimeAMD, args); + return builder.setPrecision(id, precision); + } + case glslang::EOpIgnoreIntersection: + builder.createNoResultOp(spv::OpIgnoreIntersectionKHR); + return 0; + case glslang::EOpTerminateRay: + builder.createNoResultOp(spv::OpTerminateRayKHR); + return 0; + case glslang::EOpRayQueryInitialize: + builder.createNoResultOp(spv::OpRayQueryInitializeKHR); + return 0; + case glslang::EOpRayQueryTerminate: + builder.createNoResultOp(spv::OpRayQueryTerminateKHR); + return 0; + case glslang::EOpRayQueryGenerateIntersection: + builder.createNoResultOp(spv::OpRayQueryGenerateIntersectionKHR); + return 0; + case glslang::EOpRayQueryConfirmIntersection: + builder.createNoResultOp(spv::OpRayQueryConfirmIntersectionKHR); + return 0; + case glslang::EOpBeginInvocationInterlock: + builder.createNoResultOp(spv::OpBeginInvocationInterlockEXT); + return 0; + case glslang::EOpEndInvocationInterlock: + builder.createNoResultOp(spv::OpEndInvocationInterlockEXT); + return 0; + + case glslang::EOpIsHelperInvocation: + { + std::vector args; // Dummy arguments + builder.addExtension(spv::E_SPV_EXT_demote_to_helper_invocation); + builder.addCapability(spv::CapabilityDemoteToHelperInvocationEXT); + return builder.createOp(spv::OpIsHelperInvocationEXT, typeId, args); + } + + case glslang::EOpReadClockSubgroupKHR: { + std::vector args; + args.push_back(builder.makeUintConstant(spv::ScopeSubgroup)); + builder.addExtension(spv::E_SPV_KHR_shader_clock); + builder.addCapability(spv::CapabilityShaderClockKHR); + return builder.createOp(spv::OpReadClockKHR, typeId, args); + } + + case glslang::EOpReadClockDeviceKHR: { + std::vector args; + args.push_back(builder.makeUintConstant(spv::ScopeDevice)); + builder.addExtension(spv::E_SPV_KHR_shader_clock); + builder.addCapability(spv::CapabilityShaderClockKHR); + return builder.createOp(spv::OpReadClockKHR, typeId, args); + } +#endif + default: + break; + } + + logger->missingFunctionality("unknown operation with no arguments"); + + return 0; +} + +spv::Id TGlslangToSpvTraverser::getSymbolId(const glslang::TIntermSymbol* symbol) +{ + auto iter = symbolValues.find(symbol->getId()); + spv::Id id; + if (symbolValues.end() != iter) { + id = iter->second; + return id; + } + + // it was not found, create it + spv::BuiltIn builtIn = TranslateBuiltInDecoration(symbol->getQualifier().builtIn, false); + auto forcedType = getForcedType(symbol->getQualifier().builtIn, symbol->getType()); + id = createSpvVariable(symbol, forcedType.first); + symbolValues[symbol->getId()] = id; + if (forcedType.second != spv::NoType) + forceType[id] = forcedType.second; + + if (symbol->getBasicType() != glslang::EbtBlock) { + builder.addDecoration(id, TranslatePrecisionDecoration(symbol->getType())); + builder.addDecoration(id, TranslateInterpolationDecoration(symbol->getType().getQualifier())); + builder.addDecoration(id, TranslateAuxiliaryStorageDecoration(symbol->getType().getQualifier())); +#ifndef GLSLANG_WEB + addMeshNVDecoration(id, /*member*/ -1, symbol->getType().getQualifier()); + if (symbol->getQualifier().hasComponent()) + builder.addDecoration(id, spv::DecorationComponent, symbol->getQualifier().layoutComponent); + if (symbol->getQualifier().hasIndex()) + builder.addDecoration(id, spv::DecorationIndex, symbol->getQualifier().layoutIndex); +#endif + if (symbol->getType().getQualifier().hasSpecConstantId()) + builder.addDecoration(id, spv::DecorationSpecId, symbol->getType().getQualifier().layoutSpecConstantId); + // atomic counters use this: + if (symbol->getQualifier().hasOffset()) + builder.addDecoration(id, spv::DecorationOffset, symbol->getQualifier().layoutOffset); + } + + if (symbol->getQualifier().hasLocation()) + builder.addDecoration(id, spv::DecorationLocation, symbol->getQualifier().layoutLocation); + builder.addDecoration(id, TranslateInvariantDecoration(symbol->getType().getQualifier())); + if (symbol->getQualifier().hasStream() && glslangIntermediate->isMultiStream()) { + builder.addCapability(spv::CapabilityGeometryStreams); + builder.addDecoration(id, spv::DecorationStream, symbol->getQualifier().layoutStream); + } + if (symbol->getQualifier().hasSet()) + builder.addDecoration(id, spv::DecorationDescriptorSet, symbol->getQualifier().layoutSet); + else if (IsDescriptorResource(symbol->getType())) { + // default to 0 + builder.addDecoration(id, spv::DecorationDescriptorSet, 0); + } + if (symbol->getQualifier().hasBinding()) + builder.addDecoration(id, spv::DecorationBinding, symbol->getQualifier().layoutBinding); + else if (IsDescriptorResource(symbol->getType())) { + // default to 0 + builder.addDecoration(id, spv::DecorationBinding, 0); + } + if (symbol->getQualifier().hasAttachment()) + builder.addDecoration(id, spv::DecorationInputAttachmentIndex, symbol->getQualifier().layoutAttachment); + if (glslangIntermediate->getXfbMode()) { + builder.addCapability(spv::CapabilityTransformFeedback); + if (symbol->getQualifier().hasXfbBuffer()) { + builder.addDecoration(id, spv::DecorationXfbBuffer, symbol->getQualifier().layoutXfbBuffer); + unsigned stride = glslangIntermediate->getXfbStride(symbol->getQualifier().layoutXfbBuffer); + if (stride != glslang::TQualifier::layoutXfbStrideEnd) + builder.addDecoration(id, spv::DecorationXfbStride, stride); + } + if (symbol->getQualifier().hasXfbOffset()) + builder.addDecoration(id, spv::DecorationOffset, symbol->getQualifier().layoutXfbOffset); + } + + // add built-in variable decoration + if (builtIn != spv::BuiltInMax) { + builder.addDecoration(id, spv::DecorationBuiltIn, (int)builtIn); + } + +#ifndef GLSLANG_WEB + if (symbol->getType().isImage()) { + std::vector memory; + TranslateMemoryDecoration(symbol->getType().getQualifier(), memory, + glslangIntermediate->usingVulkanMemoryModel()); + for (unsigned int i = 0; i < memory.size(); ++i) + builder.addDecoration(id, memory[i]); + } + + // nonuniform + builder.addDecoration(id, TranslateNonUniformDecoration(symbol->getType().getQualifier())); + + if (builtIn == spv::BuiltInSampleMask) { + spv::Decoration decoration; + // GL_NV_sample_mask_override_coverage extension + if (glslangIntermediate->getLayoutOverrideCoverage()) + decoration = (spv::Decoration)spv::DecorationOverrideCoverageNV; + else + decoration = (spv::Decoration)spv::DecorationMax; + builder.addDecoration(id, decoration); + if (decoration != spv::DecorationMax) { + builder.addCapability(spv::CapabilitySampleMaskOverrideCoverageNV); + builder.addExtension(spv::E_SPV_NV_sample_mask_override_coverage); + } + } + else if (builtIn == spv::BuiltInLayer) { + // SPV_NV_viewport_array2 extension + if (symbol->getQualifier().layoutViewportRelative) { + builder.addDecoration(id, (spv::Decoration)spv::DecorationViewportRelativeNV); + builder.addCapability(spv::CapabilityShaderViewportMaskNV); + builder.addExtension(spv::E_SPV_NV_viewport_array2); + } + if (symbol->getQualifier().layoutSecondaryViewportRelativeOffset != -2048) { + builder.addDecoration(id, (spv::Decoration)spv::DecorationSecondaryViewportRelativeNV, + symbol->getQualifier().layoutSecondaryViewportRelativeOffset); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + } + } + + if (symbol->getQualifier().layoutPassthrough) { + builder.addDecoration(id, spv::DecorationPassthroughNV); + builder.addCapability(spv::CapabilityGeometryShaderPassthroughNV); + builder.addExtension(spv::E_SPV_NV_geometry_shader_passthrough); + } + if (symbol->getQualifier().pervertexNV) { + builder.addDecoration(id, spv::DecorationPerVertexNV); + builder.addCapability(spv::CapabilityFragmentBarycentricNV); + builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric); + } + + if (glslangIntermediate->getHlslFunctionality1() && symbol->getType().getQualifier().semanticName != nullptr) { + builder.addExtension("SPV_GOOGLE_hlsl_functionality1"); + builder.addDecoration(id, (spv::Decoration)spv::DecorationHlslSemanticGOOGLE, + symbol->getType().getQualifier().semanticName); + } + + if (symbol->isReference()) { + builder.addDecoration(id, symbol->getType().getQualifier().restrict ? + spv::DecorationRestrictPointerEXT : spv::DecorationAliasedPointerEXT); + } +#endif + + return id; +} + +#ifndef GLSLANG_WEB +// add per-primitive, per-view. per-task decorations to a struct member (member >= 0) or an object +void TGlslangToSpvTraverser::addMeshNVDecoration(spv::Id id, int member, const glslang::TQualifier& qualifier) +{ + if (member >= 0) { + if (qualifier.perPrimitiveNV) { + // Need to add capability/extension for fragment shader. + // Mesh shader already adds this by default. + if (glslangIntermediate->getStage() == EShLangFragment) { + builder.addCapability(spv::CapabilityMeshShadingNV); + builder.addExtension(spv::E_SPV_NV_mesh_shader); + } + builder.addMemberDecoration(id, (unsigned)member, spv::DecorationPerPrimitiveNV); + } + if (qualifier.perViewNV) + builder.addMemberDecoration(id, (unsigned)member, spv::DecorationPerViewNV); + if (qualifier.perTaskNV) + builder.addMemberDecoration(id, (unsigned)member, spv::DecorationPerTaskNV); + } else { + if (qualifier.perPrimitiveNV) { + // Need to add capability/extension for fragment shader. + // Mesh shader already adds this by default. + if (glslangIntermediate->getStage() == EShLangFragment) { + builder.addCapability(spv::CapabilityMeshShadingNV); + builder.addExtension(spv::E_SPV_NV_mesh_shader); + } + builder.addDecoration(id, spv::DecorationPerPrimitiveNV); + } + if (qualifier.perViewNV) + builder.addDecoration(id, spv::DecorationPerViewNV); + if (qualifier.perTaskNV) + builder.addDecoration(id, spv::DecorationPerTaskNV); + } +} +#endif + +// Make a full tree of instructions to build a SPIR-V specialization constant, +// or regular constant if possible. +// +// TBD: this is not yet done, nor verified to be the best design, it does do the leaf symbols though +// +// Recursively walk the nodes. The nodes form a tree whose leaves are +// regular constants, which themselves are trees that createSpvConstant() +// recursively walks. So, this function walks the "top" of the tree: +// - emit specialization constant-building instructions for specConstant +// - when running into a non-spec-constant, switch to createSpvConstant() +spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TIntermTyped& node) +{ + assert(node.getQualifier().isConstant()); + + // Handle front-end constants first (non-specialization constants). + if (! node.getQualifier().specConstant) { + // hand off to the non-spec-constant path + assert(node.getAsConstantUnion() != nullptr || node.getAsSymbolNode() != nullptr); + int nextConst = 0; + return createSpvConstantFromConstUnionArray(node.getType(), node.getAsConstantUnion() ? + node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(), + nextConst, false); + } + + // We now know we have a specialization constant to build + + // gl_WorkGroupSize is a special case until the front-end handles hierarchical specialization constants, + // even then, it's specialization ids are handled by special case syntax in GLSL: layout(local_size_x = ... + if (node.getType().getQualifier().builtIn == glslang::EbvWorkGroupSize) { + std::vector dimConstId; + for (int dim = 0; dim < 3; ++dim) { + bool specConst = (glslangIntermediate->getLocalSizeSpecId(dim) != glslang::TQualifier::layoutNotSet); + dimConstId.push_back(builder.makeUintConstant(glslangIntermediate->getLocalSize(dim), specConst)); + if (specConst) { + builder.addDecoration(dimConstId.back(), spv::DecorationSpecId, + glslangIntermediate->getLocalSizeSpecId(dim)); + } + } + return builder.makeCompositeConstant(builder.makeVectorType(builder.makeUintType(32), 3), dimConstId, true); + } + + // An AST node labelled as specialization constant should be a symbol node. + // Its initializer should either be a sub tree with constant nodes, or a constant union array. + if (auto* sn = node.getAsSymbolNode()) { + spv::Id result; + if (auto* sub_tree = sn->getConstSubtree()) { + // Traverse the constant constructor sub tree like generating normal run-time instructions. + // During the AST traversal, if the node is marked as 'specConstant', SpecConstantOpModeGuard + // will set the builder into spec constant op instruction generating mode. + sub_tree->traverse(this); + result = accessChainLoad(sub_tree->getType()); + } else if (auto* const_union_array = &sn->getConstArray()) { + int nextConst = 0; + result = createSpvConstantFromConstUnionArray(sn->getType(), *const_union_array, nextConst, true); + } else { + logger->missingFunctionality("Invalid initializer for spec onstant."); + return spv::NoResult; + } + builder.addName(result, sn->getName().c_str()); + return result; + } + + // Neither a front-end constant node, nor a specialization constant node with constant union array or + // constant sub tree as initializer. + logger->missingFunctionality("Neither a front-end constant nor a spec constant."); + return spv::NoResult; +} + +// Use 'consts' as the flattened glslang source of scalar constants to recursively +// build the aggregate SPIR-V constant. +// +// If there are not enough elements present in 'consts', 0 will be substituted; +// an empty 'consts' can be used to create a fully zeroed SPIR-V constant. +// +spv::Id TGlslangToSpvTraverser::createSpvConstantFromConstUnionArray(const glslang::TType& glslangType, + const glslang::TConstUnionArray& consts, int& nextConst, bool specConstant) +{ + // vector of constants for SPIR-V + std::vector spvConsts; + + // Type is used for struct and array constants + spv::Id typeId = convertGlslangToSpvType(glslangType); + + if (glslangType.isArray()) { + glslang::TType elementType(glslangType, 0); + for (int i = 0; i < glslangType.getOuterArraySize(); ++i) + spvConsts.push_back(createSpvConstantFromConstUnionArray(elementType, consts, nextConst, false)); + } else if (glslangType.isMatrix()) { + glslang::TType vectorType(glslangType, 0); + for (int col = 0; col < glslangType.getMatrixCols(); ++col) + spvConsts.push_back(createSpvConstantFromConstUnionArray(vectorType, consts, nextConst, false)); + } else if (glslangType.isCoopMat()) { + glslang::TType componentType(glslangType.getBasicType()); + spvConsts.push_back(createSpvConstantFromConstUnionArray(componentType, consts, nextConst, false)); + } else if (glslangType.isStruct()) { + glslang::TVector::const_iterator iter; + for (iter = glslangType.getStruct()->begin(); iter != glslangType.getStruct()->end(); ++iter) + spvConsts.push_back(createSpvConstantFromConstUnionArray(*iter->type, consts, nextConst, false)); + } else if (glslangType.getVectorSize() > 1) { + for (unsigned int i = 0; i < (unsigned int)glslangType.getVectorSize(); ++i) { + bool zero = nextConst >= consts.size(); + switch (glslangType.getBasicType()) { + case glslang::EbtInt: + spvConsts.push_back(builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst())); + break; + case glslang::EbtUint: + spvConsts.push_back(builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst())); + break; + case glslang::EbtFloat: + spvConsts.push_back(builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst())); + break; + case glslang::EbtBool: + spvConsts.push_back(builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst())); + break; +#ifndef GLSLANG_WEB + case glslang::EbtInt8: + spvConsts.push_back(builder.makeInt8Constant(zero ? 0 : consts[nextConst].getI8Const())); + break; + case glslang::EbtUint8: + spvConsts.push_back(builder.makeUint8Constant(zero ? 0 : consts[nextConst].getU8Const())); + break; + case glslang::EbtInt16: + spvConsts.push_back(builder.makeInt16Constant(zero ? 0 : consts[nextConst].getI16Const())); + break; + case glslang::EbtUint16: + spvConsts.push_back(builder.makeUint16Constant(zero ? 0 : consts[nextConst].getU16Const())); + break; + case glslang::EbtInt64: + spvConsts.push_back(builder.makeInt64Constant(zero ? 0 : consts[nextConst].getI64Const())); + break; + case glslang::EbtUint64: + spvConsts.push_back(builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const())); + break; + case glslang::EbtDouble: + spvConsts.push_back(builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst())); + break; + case glslang::EbtFloat16: + spvConsts.push_back(builder.makeFloat16Constant(zero ? 0.0F : (float)consts[nextConst].getDConst())); + break; +#endif + default: + assert(0); + break; + } + ++nextConst; + } + } else { + // we have a non-aggregate (scalar) constant + bool zero = nextConst >= consts.size(); + spv::Id scalar = 0; + switch (glslangType.getBasicType()) { + case glslang::EbtInt: + scalar = builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst(), specConstant); + break; + case glslang::EbtUint: + scalar = builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst(), specConstant); + break; + case glslang::EbtFloat: + scalar = builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst(), specConstant); + break; + case glslang::EbtBool: + scalar = builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst(), specConstant); + break; +#ifndef GLSLANG_WEB + case glslang::EbtInt8: + scalar = builder.makeInt8Constant(zero ? 0 : consts[nextConst].getI8Const(), specConstant); + break; + case glslang::EbtUint8: + scalar = builder.makeUint8Constant(zero ? 0 : consts[nextConst].getU8Const(), specConstant); + break; + case glslang::EbtInt16: + scalar = builder.makeInt16Constant(zero ? 0 : consts[nextConst].getI16Const(), specConstant); + break; + case glslang::EbtUint16: + scalar = builder.makeUint16Constant(zero ? 0 : consts[nextConst].getU16Const(), specConstant); + break; + case glslang::EbtInt64: + scalar = builder.makeInt64Constant(zero ? 0 : consts[nextConst].getI64Const(), specConstant); + break; + case glslang::EbtUint64: + scalar = builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const(), specConstant); + break; + case glslang::EbtDouble: + scalar = builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst(), specConstant); + break; + case glslang::EbtFloat16: + scalar = builder.makeFloat16Constant(zero ? 0.0F : (float)consts[nextConst].getDConst(), specConstant); + break; + case glslang::EbtReference: + scalar = builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const(), specConstant); + scalar = builder.createUnaryOp(spv::OpBitcast, typeId, scalar); + break; +#endif + case glslang::EbtString: + scalar = builder.getStringId(consts[nextConst].getSConst()->c_str()); + break; + default: + assert(0); + break; + } + ++nextConst; + return scalar; + } + + return builder.makeCompositeConstant(typeId, spvConsts); +} + +// Return true if the node is a constant or symbol whose reading has no +// non-trivial observable cost or effect. +bool TGlslangToSpvTraverser::isTrivialLeaf(const glslang::TIntermTyped* node) +{ + // don't know what this is + if (node == nullptr) + return false; + + // a constant is safe + if (node->getAsConstantUnion() != nullptr) + return true; + + // not a symbol means non-trivial + if (node->getAsSymbolNode() == nullptr) + return false; + + // a symbol, depends on what's being read + switch (node->getType().getQualifier().storage) { + case glslang::EvqTemporary: + case glslang::EvqGlobal: + case glslang::EvqIn: + case glslang::EvqInOut: + case glslang::EvqConst: + case glslang::EvqConstReadOnly: + case glslang::EvqUniform: + return true; + default: + return false; + } +} + +// A node is trivial if it is a single operation with no side effects. +// HLSL (and/or vectors) are always trivial, as it does not short circuit. +// Otherwise, error on the side of saying non-trivial. +// Return true if trivial. +bool TGlslangToSpvTraverser::isTrivial(const glslang::TIntermTyped* node) +{ + if (node == nullptr) + return false; + + // count non scalars as trivial, as well as anything coming from HLSL + if (! node->getType().isScalarOrVec1() || glslangIntermediate->getSource() == glslang::EShSourceHlsl) + return true; + + // symbols and constants are trivial + if (isTrivialLeaf(node)) + return true; + + // otherwise, it needs to be a simple operation or one or two leaf nodes + + // not a simple operation + const glslang::TIntermBinary* binaryNode = node->getAsBinaryNode(); + const glslang::TIntermUnary* unaryNode = node->getAsUnaryNode(); + if (binaryNode == nullptr && unaryNode == nullptr) + return false; + + // not on leaf nodes + if (binaryNode && (! isTrivialLeaf(binaryNode->getLeft()) || ! isTrivialLeaf(binaryNode->getRight()))) + return false; + + if (unaryNode && ! isTrivialLeaf(unaryNode->getOperand())) { + return false; + } + + switch (node->getAsOperator()->getOp()) { + case glslang::EOpLogicalNot: + case glslang::EOpConvIntToBool: + case glslang::EOpConvUintToBool: + case glslang::EOpConvFloatToBool: + case glslang::EOpConvDoubleToBool: + case glslang::EOpEqual: + case glslang::EOpNotEqual: + case glslang::EOpLessThan: + case glslang::EOpGreaterThan: + case glslang::EOpLessThanEqual: + case glslang::EOpGreaterThanEqual: + case glslang::EOpIndexDirect: + case glslang::EOpIndexDirectStruct: + case glslang::EOpLogicalXor: + case glslang::EOpAny: + case glslang::EOpAll: + return true; + default: + return false; + } +} + +// Emit short-circuiting code, where 'right' is never evaluated unless +// the left side is true (for &&) or false (for ||). +spv::Id TGlslangToSpvTraverser::createShortCircuit(glslang::TOperator op, glslang::TIntermTyped& left, + glslang::TIntermTyped& right) +{ + spv::Id boolTypeId = builder.makeBoolType(); + + // emit left operand + builder.clearAccessChain(); + left.traverse(this); + spv::Id leftId = accessChainLoad(left.getType()); + + // Operands to accumulate OpPhi operands + std::vector phiOperands; + // accumulate left operand's phi information + phiOperands.push_back(leftId); + phiOperands.push_back(builder.getBuildPoint()->getId()); + + // Make the two kinds of operation symmetric with a "!" + // || => emit "if (! left) result = right" + // && => emit "if ( left) result = right" + // + // TODO: this runtime "not" for || could be avoided by adding functionality + // to 'builder' to have an "else" without an "then" + if (op == glslang::EOpLogicalOr) + leftId = builder.createUnaryOp(spv::OpLogicalNot, boolTypeId, leftId); + + // make an "if" based on the left value + spv::Builder::If ifBuilder(leftId, spv::SelectionControlMaskNone, builder); + + // emit right operand as the "then" part of the "if" + builder.clearAccessChain(); + right.traverse(this); + spv::Id rightId = accessChainLoad(right.getType()); + + // accumulate left operand's phi information + phiOperands.push_back(rightId); + phiOperands.push_back(builder.getBuildPoint()->getId()); + + // finish the "if" + ifBuilder.makeEndIf(); + + // phi together the two results + return builder.createOp(spv::OpPhi, boolTypeId, phiOperands); +} + +#ifndef GLSLANG_WEB +// Return type Id of the imported set of extended instructions corresponds to the name. +// Import this set if it has not been imported yet. +spv::Id TGlslangToSpvTraverser::getExtBuiltins(const char* name) +{ + if (extBuiltinMap.find(name) != extBuiltinMap.end()) + return extBuiltinMap[name]; + else { + builder.addExtension(name); + spv::Id extBuiltins = builder.import(name); + extBuiltinMap[name] = extBuiltins; + return extBuiltins; + } +} +#endif + +}; // end anonymous namespace + +namespace glslang { + +void GetSpirvVersion(std::string& version) +{ + const int bufSize = 100; + char buf[bufSize]; + snprintf(buf, bufSize, "0x%08x, Revision %d", spv::Version, spv::Revision); + version = buf; +} + +// For low-order part of the generator's magic number. Bump up +// when there is a change in the style (e.g., if SSA form changes, +// or a different instruction sequence to do something gets used). +int GetSpirvGeneratorVersion() +{ + // return 1; // start + // return 2; // EOpAtomicCounterDecrement gets a post decrement, to map between GLSL -> SPIR-V + // return 3; // change/correct barrier-instruction operands, to match memory model group decisions + // return 4; // some deeper access chains: for dynamic vector component, and local Boolean component + // return 5; // make OpArrayLength result type be an int with signedness of 0 + // return 6; // revert version 5 change, which makes a different (new) kind of incorrect code, + // versions 4 and 6 each generate OpArrayLength as it has long been done + // return 7; // GLSL volatile keyword maps to both SPIR-V decorations Volatile and Coherent + return 8; // switch to new dead block eliminator; use OpUnreachable +} + +// Write SPIR-V out to a binary file +void OutputSpvBin(const std::vector& spirv, const char* baseName) +{ + std::ofstream out; + out.open(baseName, std::ios::binary | std::ios::out); + if (out.fail()) + printf("ERROR: Failed to open file: %s\n", baseName); + for (int i = 0; i < (int)spirv.size(); ++i) { + unsigned int word = spirv[i]; + out.write((const char*)&word, 4); + } + out.close(); +} + +// Write SPIR-V out to a text file with 32-bit hexadecimal words +void OutputSpvHex(const std::vector& spirv, const char* baseName, const char* varName) +{ +#ifndef GLSLANG_WEB + std::ofstream out; + out.open(baseName, std::ios::binary | std::ios::out); + if (out.fail()) + printf("ERROR: Failed to open file: %s\n", baseName); + out << "\t// " << + GetSpirvGeneratorVersion() << "." << GLSLANG_MINOR_VERSION << "." << GLSLANG_PATCH_LEVEL << + std::endl; + if (varName != nullptr) { + out << "\t #pragma once" << std::endl; + out << "const uint32_t " << varName << "[] = {" << std::endl; + } + const int WORDS_PER_LINE = 8; + for (int i = 0; i < (int)spirv.size(); i += WORDS_PER_LINE) { + out << "\t"; + for (int j = 0; j < WORDS_PER_LINE && i + j < (int)spirv.size(); ++j) { + const unsigned int word = spirv[i + j]; + out << "0x" << std::hex << std::setw(8) << std::setfill('0') << word; + if (i + j + 1 < (int)spirv.size()) { + out << ","; + } + } + out << std::endl; + } + if (varName != nullptr) { + out << "};"; + } + out.close(); +#endif +} + +// +// Set up the glslang traversal +// +void GlslangToSpv(const TIntermediate& intermediate, std::vector& spirv, SpvOptions* options) +{ + spv::SpvBuildLogger logger; + GlslangToSpv(intermediate, spirv, &logger, options); +} + +void GlslangToSpv(const TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, SpvOptions* options) +{ + TIntermNode* root = intermediate.getTreeRoot(); + + if (root == 0) + return; + + SpvOptions defaultOptions; + if (options == nullptr) + options = &defaultOptions; + + GetThreadPoolAllocator().push(); + + TGlslangToSpvTraverser it(intermediate.getSpv().spv, &intermediate, logger, *options); + root->traverse(&it); + it.finishSpv(); + it.dumpSpv(spirv); + +#if ENABLE_OPT + // If from HLSL, run spirv-opt to "legalize" the SPIR-V for Vulkan + // eg. forward and remove memory writes of opaque types. + bool prelegalization = intermediate.getSource() == EShSourceHlsl; + if ((intermediate.getSource() == EShSourceHlsl || options->optimizeSize) && !options->disableOptimizer) { + SpirvToolsLegalize(intermediate, spirv, logger, options); + prelegalization = false; + } + + if (options->validate) + SpirvToolsValidate(intermediate, spirv, logger, prelegalization); + + if (options->disassemble) + SpirvToolsDisassemble(std::cout, spirv); + +#endif + + GetThreadPoolAllocator().pop(); +} + +}; // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/GlslangToSpv.h b/armorcore/tools/to_spirv/glslang/SPIRV/GlslangToSpv.h new file mode 100755 index 000000000..3907be43b --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/GlslangToSpv.h @@ -0,0 +1,61 @@ +// +// Copyright (C) 2014 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#if defined(_MSC_VER) && _MSC_VER >= 1900 + #pragma warning(disable : 4464) // relative include path contains '..' +#endif + +#include "SpvTools.h" +#include "glslang/Include/intermediate.h" + +#include +#include + +#include "Logger.h" + +namespace glslang { + +void GetSpirvVersion(std::string&); +int GetSpirvGeneratorVersion(); +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, + SpvOptions* options = nullptr); +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, SpvOptions* options = nullptr); +void OutputSpvBin(const std::vector& spirv, const char* baseName); +void OutputSpvHex(const std::vector& spirv, const char* baseName, const char* varName); + +} diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/InReadableOrder.cpp b/armorcore/tools/to_spirv/glslang/SPIRV/InReadableOrder.cpp new file mode 100644 index 000000000..9d9410be9 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/InReadableOrder.cpp @@ -0,0 +1,131 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// The SPIR-V spec requires code blocks to appear in an order satisfying the +// dominator-tree direction (ie, dominator before the dominated). This is, +// actually, easy to achieve: any pre-order CFG traversal algorithm will do it. +// Because such algorithms visit a block only after traversing some path to it +// from the root, they necessarily visit the block's idom first. +// +// But not every graph-traversal algorithm outputs blocks in an order that +// appears logical to human readers. The problem is that unrelated branches may +// be interspersed with each other, and merge blocks may come before some of the +// branches being merged. +// +// A good, human-readable order of blocks may be achieved by performing +// depth-first search but delaying merge nodes until after all their branches +// have been visited. This is implemented below by the inReadableOrder() +// function. + +#include "spvIR.h" + +#include +#include + +using spv::Block; +using spv::Id; + +namespace { +// Traverses CFG in a readable order, invoking a pre-set callback on each block. +// Use by calling visit() on the root block. +class ReadableOrderTraverser { +public: + ReadableOrderTraverser(std::function callback) + : callback_(callback) {} + // Visits the block if it hasn't been visited already and isn't currently + // being delayed. Invokes callback(block, why, header), then descends into its + // successors. Delays merge-block and continue-block processing until all + // the branches have been completed. If |block| is an unreachable merge block or + // an unreachable continue target, then |header| is the corresponding header block. + void visit(Block* block, spv::ReachReason why, Block* header) + { + assert(block); + if (why == spv::ReachViaControlFlow) { + reachableViaControlFlow_.insert(block); + } + if (visited_.count(block) || delayed_.count(block)) + return; + callback_(block, why, header); + visited_.insert(block); + Block* mergeBlock = nullptr; + Block* continueBlock = nullptr; + auto mergeInst = block->getMergeInstruction(); + if (mergeInst) { + Id mergeId = mergeInst->getIdOperand(0); + mergeBlock = block->getParent().getParent().getInstruction(mergeId)->getBlock(); + delayed_.insert(mergeBlock); + if (mergeInst->getOpCode() == spv::OpLoopMerge) { + Id continueId = mergeInst->getIdOperand(1); + continueBlock = + block->getParent().getParent().getInstruction(continueId)->getBlock(); + delayed_.insert(continueBlock); + } + } + if (why == spv::ReachViaControlFlow) { + const auto& successors = block->getSuccessors(); + for (auto it = successors.cbegin(); it != successors.cend(); ++it) + visit(*it, why, nullptr); + } + if (continueBlock) { + const spv::ReachReason continueWhy = + (reachableViaControlFlow_.count(continueBlock) > 0) + ? spv::ReachViaControlFlow + : spv::ReachDeadContinue; + delayed_.erase(continueBlock); + visit(continueBlock, continueWhy, block); + } + if (mergeBlock) { + const spv::ReachReason mergeWhy = + (reachableViaControlFlow_.count(mergeBlock) > 0) + ? spv::ReachViaControlFlow + : spv::ReachDeadMerge; + delayed_.erase(mergeBlock); + visit(mergeBlock, mergeWhy, block); + } + } + +private: + std::function callback_; + // Whether a block has already been visited or is being delayed. + std::unordered_set visited_, delayed_; + + // The set of blocks that actually are reached via control flow. + std::unordered_set reachableViaControlFlow_; +}; +} + +void spv::inReadableOrder(Block* root, std::function callback) +{ + ReadableOrderTraverser(callback).visit(root, spv::ReachViaControlFlow, nullptr); +} diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/Logger.cpp b/armorcore/tools/to_spirv/glslang/SPIRV/Logger.cpp new file mode 100644 index 000000000..cdc8469c4 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/Logger.cpp @@ -0,0 +1,72 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef GLSLANG_WEB + +#include "Logger.h" + +#include +#include +#include + +namespace spv { + +void SpvBuildLogger::tbdFunctionality(const std::string& f) +{ + if (std::find(std::begin(tbdFeatures), std::end(tbdFeatures), f) == std::end(tbdFeatures)) + tbdFeatures.push_back(f); +} + +void SpvBuildLogger::missingFunctionality(const std::string& f) +{ + if (std::find(std::begin(missingFeatures), std::end(missingFeatures), f) == std::end(missingFeatures)) + missingFeatures.push_back(f); +} + +std::string SpvBuildLogger::getAllMessages() const { + std::ostringstream messages; + for (auto it = tbdFeatures.cbegin(); it != tbdFeatures.cend(); ++it) + messages << "TBD functionality: " << *it << "\n"; + for (auto it = missingFeatures.cbegin(); it != missingFeatures.cend(); ++it) + messages << "Missing functionality: " << *it << "\n"; + for (auto it = warnings.cbegin(); it != warnings.cend(); ++it) + messages << "warning: " << *it << "\n"; + for (auto it = errors.cbegin(); it != errors.cend(); ++it) + messages << "error: " << *it << "\n"; + return messages.str(); +} + +} // end spv namespace + +#endif diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/Logger.h b/armorcore/tools/to_spirv/glslang/SPIRV/Logger.h new file mode 100644 index 000000000..411367c03 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/Logger.h @@ -0,0 +1,83 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef GLSLANG_SPIRV_LOGGER_H +#define GLSLANG_SPIRV_LOGGER_H + +#include +#include + +namespace spv { + +// A class for holding all SPIR-V build status messages, including +// missing/TBD functionalities, warnings, and errors. +class SpvBuildLogger { +public: + SpvBuildLogger() {} + +#ifdef GLSLANG_WEB + void tbdFunctionality(const std::string& f) { } + void missingFunctionality(const std::string& f) { } + void warning(const std::string& w) { } + void error(const std::string& e) { errors.push_back(e); } + std::string getAllMessages() { return ""; } +#else + + // Registers a TBD functionality. + void tbdFunctionality(const std::string& f); + // Registers a missing functionality. + void missingFunctionality(const std::string& f); + + // Logs a warning. + void warning(const std::string& w) { warnings.push_back(w); } + // Logs an error. + void error(const std::string& e) { errors.push_back(e); } + + // Returns all messages accumulated in the order of: + // TBD functionalities, missing functionalities, warnings, errors. + std::string getAllMessages() const; +#endif + +private: + SpvBuildLogger(const SpvBuildLogger&); + + std::vector tbdFeatures; + std::vector missingFeatures; + std::vector warnings; + std::vector errors; +}; + +} // end spv namespace + +#endif // GLSLANG_SPIRV_LOGGER_H diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/NonSemanticDebugPrintf.h b/armorcore/tools/to_spirv/glslang/SPIRV/NonSemanticDebugPrintf.h new file mode 100644 index 000000000..83796d75e --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/NonSemanticDebugPrintf.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ +#define SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NonSemanticDebugPrintfRevision = 1, + NonSemanticDebugPrintfRevision_BitWidthPadding = 0x7fffffff +}; + +enum NonSemanticDebugPrintfInstructions { + NonSemanticDebugPrintfDebugPrintf = 1, + NonSemanticDebugPrintfInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/SPVRemapper.cpp b/armorcore/tools/to_spirv/glslang/SPIRV/SPVRemapper.cpp new file mode 100644 index 000000000..fd0bb8950 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/SPVRemapper.cpp @@ -0,0 +1,1487 @@ +// +// Copyright (C) 2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "SPVRemapper.h" +#include "doc.h" + +#if !defined (use_cpp11) +// ... not supported before C++11 +#else // defined (use_cpp11) + +#include +#include +#include "../glslang/Include/Common.h" + +namespace spv { + + // By default, just abort on error. Can be overridden via RegisterErrorHandler + spirvbin_t::errorfn_t spirvbin_t::errorHandler = [](const std::string&) { exit(5); }; + // By default, eat log messages. Can be overridden via RegisterLogHandler + spirvbin_t::logfn_t spirvbin_t::logHandler = [](const std::string&) { }; + + // This can be overridden to provide other message behavior if needed + void spirvbin_t::msg(int minVerbosity, int indent, const std::string& txt) const + { + if (verbose >= minVerbosity) + logHandler(std::string(indent, ' ') + txt); + } + + // hash opcode, with special handling for OpExtInst + std::uint32_t spirvbin_t::asOpCodeHash(unsigned word) + { + const spv::Op opCode = asOpCode(word); + + std::uint32_t offset = 0; + + switch (opCode) { + case spv::OpExtInst: + offset += asId(word + 4); break; + default: + break; + } + + return opCode * 19 + offset; // 19 = small prime + } + + spirvbin_t::range_t spirvbin_t::literalRange(spv::Op opCode) const + { + static const int maxCount = 1<<30; + + switch (opCode) { + case spv::OpTypeFloat: // fall through... + case spv::OpTypePointer: return range_t(2, 3); + case spv::OpTypeInt: return range_t(2, 4); + // TODO: case spv::OpTypeImage: + // TODO: case spv::OpTypeSampledImage: + case spv::OpTypeSampler: return range_t(3, 8); + case spv::OpTypeVector: // fall through + case spv::OpTypeMatrix: // ... + case spv::OpTypePipe: return range_t(3, 4); + case spv::OpConstant: return range_t(3, maxCount); + default: return range_t(0, 0); + } + } + + spirvbin_t::range_t spirvbin_t::typeRange(spv::Op opCode) const + { + static const int maxCount = 1<<30; + + if (isConstOp(opCode)) + return range_t(1, 2); + + switch (opCode) { + case spv::OpTypeVector: // fall through + case spv::OpTypeMatrix: // ... + case spv::OpTypeSampler: // ... + case spv::OpTypeArray: // ... + case spv::OpTypeRuntimeArray: // ... + case spv::OpTypePipe: return range_t(2, 3); + case spv::OpTypeStruct: // fall through + case spv::OpTypeFunction: return range_t(2, maxCount); + case spv::OpTypePointer: return range_t(3, 4); + default: return range_t(0, 0); + } + } + + spirvbin_t::range_t spirvbin_t::constRange(spv::Op opCode) const + { + static const int maxCount = 1<<30; + + switch (opCode) { + case spv::OpTypeArray: // fall through... + case spv::OpTypeRuntimeArray: return range_t(3, 4); + case spv::OpConstantComposite: return range_t(3, maxCount); + default: return range_t(0, 0); + } + } + + // Return the size of a type in 32-bit words. This currently only + // handles ints and floats, and is only invoked by queries which must be + // integer types. If ever needed, it can be generalized. + unsigned spirvbin_t::typeSizeInWords(spv::Id id) const + { + const unsigned typeStart = idPos(id); + const spv::Op opCode = asOpCode(typeStart); + + if (errorLatch) + return 0; + + switch (opCode) { + case spv::OpTypeInt: // fall through... + case spv::OpTypeFloat: return (spv[typeStart+2]+31)/32; + default: + return 0; + } + } + + // Looks up the type of a given const or variable ID, and + // returns its size in 32-bit words. + unsigned spirvbin_t::idTypeSizeInWords(spv::Id id) const + { + const auto tid_it = idTypeSizeMap.find(id); + if (tid_it == idTypeSizeMap.end()) { + error("type size for ID not found"); + return 0; + } + + return tid_it->second; + } + + // Is this an opcode we should remove when using --strip? + bool spirvbin_t::isStripOp(spv::Op opCode) const + { + switch (opCode) { + case spv::OpSource: + case spv::OpSourceExtension: + case spv::OpName: + case spv::OpMemberName: + case spv::OpLine: return true; + default: return false; + } + } + + // Return true if this opcode is flow control + bool spirvbin_t::isFlowCtrl(spv::Op opCode) const + { + switch (opCode) { + case spv::OpBranchConditional: + case spv::OpBranch: + case spv::OpSwitch: + case spv::OpLoopMerge: + case spv::OpSelectionMerge: + case spv::OpLabel: + case spv::OpFunction: + case spv::OpFunctionEnd: return true; + default: return false; + } + } + + // Return true if this opcode defines a type + bool spirvbin_t::isTypeOp(spv::Op opCode) const + { + switch (opCode) { + case spv::OpTypeVoid: + case spv::OpTypeBool: + case spv::OpTypeInt: + case spv::OpTypeFloat: + case spv::OpTypeVector: + case spv::OpTypeMatrix: + case spv::OpTypeImage: + case spv::OpTypeSampler: + case spv::OpTypeArray: + case spv::OpTypeRuntimeArray: + case spv::OpTypeStruct: + case spv::OpTypeOpaque: + case spv::OpTypePointer: + case spv::OpTypeFunction: + case spv::OpTypeEvent: + case spv::OpTypeDeviceEvent: + case spv::OpTypeReserveId: + case spv::OpTypeQueue: + case spv::OpTypeSampledImage: + case spv::OpTypePipe: return true; + default: return false; + } + } + + // Return true if this opcode defines a constant + bool spirvbin_t::isConstOp(spv::Op opCode) const + { + switch (opCode) { + case spv::OpConstantSampler: + error("unimplemented constant type"); + return true; + + case spv::OpConstantNull: + case spv::OpConstantTrue: + case spv::OpConstantFalse: + case spv::OpConstantComposite: + case spv::OpConstant: + return true; + + default: + return false; + } + } + + const auto inst_fn_nop = [](spv::Op, unsigned) { return false; }; + const auto op_fn_nop = [](spv::Id&) { }; + + // g++ doesn't like these defined in the class proper in an anonymous namespace. + // Dunno why. Also MSVC doesn't like the constexpr keyword. Also dunno why. + // Defining them externally seems to please both compilers, so, here they are. + const spv::Id spirvbin_t::unmapped = spv::Id(-10000); + const spv::Id spirvbin_t::unused = spv::Id(-10001); + const int spirvbin_t::header_size = 5; + + spv::Id spirvbin_t::nextUnusedId(spv::Id id) + { + while (isNewIdMapped(id)) // search for an unused ID + ++id; + + return id; + } + + spv::Id spirvbin_t::localId(spv::Id id, spv::Id newId) + { + //assert(id != spv::NoResult && newId != spv::NoResult); + + if (id > bound()) { + error(std::string("ID out of range: ") + std::to_string(id)); + return spirvbin_t::unused; + } + + if (id >= idMapL.size()) + idMapL.resize(id+1, unused); + + if (newId != unmapped && newId != unused) { + if (isOldIdUnused(id)) { + error(std::string("ID unused in module: ") + std::to_string(id)); + return spirvbin_t::unused; + } + + if (!isOldIdUnmapped(id)) { + error(std::string("ID already mapped: ") + std::to_string(id) + " -> " + + std::to_string(localId(id))); + + return spirvbin_t::unused; + } + + if (isNewIdMapped(newId)) { + error(std::string("ID already used in module: ") + std::to_string(newId)); + return spirvbin_t::unused; + } + + msg(4, 4, std::string("map: ") + std::to_string(id) + " -> " + std::to_string(newId)); + setMapped(newId); + largestNewId = std::max(largestNewId, newId); + } + + return idMapL[id] = newId; + } + + // Parse a literal string from the SPIR binary and return it as an std::string + // Due to C++11 RValue references, this doesn't copy the result string. + std::string spirvbin_t::literalString(unsigned word) const + { + std::string literal; + + literal.reserve(16); + + const char* bytes = reinterpret_cast(spv.data() + word); + + while (bytes && *bytes) + literal += *bytes++; + + return literal; + } + + void spirvbin_t::applyMap() + { + msg(3, 2, std::string("Applying map: ")); + + // Map local IDs through the ID map + process(inst_fn_nop, // ignore instructions + [this](spv::Id& id) { + id = localId(id); + + if (errorLatch) + return; + + assert(id != unused && id != unmapped); + } + ); + } + + // Find free IDs for anything we haven't mapped + void spirvbin_t::mapRemainder() + { + msg(3, 2, std::string("Remapping remainder: ")); + + spv::Id unusedId = 1; // can't use 0: that's NoResult + spirword_t maxBound = 0; + + for (spv::Id id = 0; id < idMapL.size(); ++id) { + if (isOldIdUnused(id)) + continue; + + // Find a new mapping for any used but unmapped IDs + if (isOldIdUnmapped(id)) { + localId(id, unusedId = nextUnusedId(unusedId)); + if (errorLatch) + return; + } + + if (isOldIdUnmapped(id)) { + error(std::string("old ID not mapped: ") + std::to_string(id)); + return; + } + + // Track max bound + maxBound = std::max(maxBound, localId(id) + 1); + + if (errorLatch) + return; + } + + bound(maxBound); // reset header ID bound to as big as it now needs to be + } + + // Mark debug instructions for stripping + void spirvbin_t::stripDebug() + { + // Strip instructions in the stripOp set: debug info. + process( + [&](spv::Op opCode, unsigned start) { + // remember opcodes we want to strip later + if (isStripOp(opCode)) + stripInst(start); + return true; + }, + op_fn_nop); + } + + // Mark instructions that refer to now-removed IDs for stripping + void spirvbin_t::stripDeadRefs() + { + process( + [&](spv::Op opCode, unsigned start) { + // strip opcodes pointing to removed data + switch (opCode) { + case spv::OpName: + case spv::OpMemberName: + case spv::OpDecorate: + case spv::OpMemberDecorate: + if (idPosR.find(asId(start+1)) == idPosR.end()) + stripInst(start); + break; + default: + break; // leave it alone + } + + return true; + }, + op_fn_nop); + + strip(); + } + + // Update local maps of ID, type, etc positions + void spirvbin_t::buildLocalMaps() + { + msg(2, 2, std::string("build local maps: ")); + + mapped.clear(); + idMapL.clear(); +// preserve nameMap, so we don't clear that. + fnPos.clear(); + fnCalls.clear(); + typeConstPos.clear(); + idPosR.clear(); + entryPoint = spv::NoResult; + largestNewId = 0; + + idMapL.resize(bound(), unused); + + int fnStart = 0; + spv::Id fnRes = spv::NoResult; + + // build local Id and name maps + process( + [&](spv::Op opCode, unsigned start) { + unsigned word = start+1; + spv::Id typeId = spv::NoResult; + + if (spv::InstructionDesc[opCode].hasType()) + typeId = asId(word++); + + // If there's a result ID, remember the size of its type + if (spv::InstructionDesc[opCode].hasResult()) { + const spv::Id resultId = asId(word++); + idPosR[resultId] = start; + + if (typeId != spv::NoResult) { + const unsigned idTypeSize = typeSizeInWords(typeId); + + if (errorLatch) + return false; + + if (idTypeSize != 0) + idTypeSizeMap[resultId] = idTypeSize; + } + } + + if (opCode == spv::Op::OpName) { + const spv::Id target = asId(start+1); + const std::string name = literalString(start+2); + nameMap[name] = target; + + } else if (opCode == spv::Op::OpFunctionCall) { + ++fnCalls[asId(start + 3)]; + } else if (opCode == spv::Op::OpEntryPoint) { + entryPoint = asId(start + 2); + } else if (opCode == spv::Op::OpFunction) { + if (fnStart != 0) { + error("nested function found"); + return false; + } + + fnStart = start; + fnRes = asId(start + 2); + } else if (opCode == spv::Op::OpFunctionEnd) { + assert(fnRes != spv::NoResult); + if (fnStart == 0) { + error("function end without function start"); + return false; + } + + fnPos[fnRes] = range_t(fnStart, start + asWordCount(start)); + fnStart = 0; + } else if (isConstOp(opCode)) { + if (errorLatch) + return false; + + assert(asId(start + 2) != spv::NoResult); + typeConstPos.insert(start); + } else if (isTypeOp(opCode)) { + assert(asId(start + 1) != spv::NoResult); + typeConstPos.insert(start); + } + + return false; + }, + + [this](spv::Id& id) { localId(id, unmapped); } + ); + } + + // Validate the SPIR header + void spirvbin_t::validate() const + { + msg(2, 2, std::string("validating: ")); + + if (spv.size() < header_size) { + error("file too short: "); + return; + } + + if (magic() != spv::MagicNumber) { + error("bad magic number"); + return; + } + + // field 1 = version + // field 2 = generator magic + // field 3 = result bound + + if (schemaNum() != 0) { + error("bad schema, must be 0"); + return; + } + } + + int spirvbin_t::processInstruction(unsigned word, instfn_t instFn, idfn_t idFn) + { + const auto instructionStart = word; + const unsigned wordCount = asWordCount(instructionStart); + const int nextInst = word++ + wordCount; + spv::Op opCode = asOpCode(instructionStart); + + if (nextInst > int(spv.size())) { + error("spir instruction terminated too early"); + return -1; + } + + // Base for computing number of operands; will be updated as more is learned + unsigned numOperands = wordCount - 1; + + if (instFn(opCode, instructionStart)) + return nextInst; + + // Read type and result ID from instruction desc table + if (spv::InstructionDesc[opCode].hasType()) { + idFn(asId(word++)); + --numOperands; + } + + if (spv::InstructionDesc[opCode].hasResult()) { + idFn(asId(word++)); + --numOperands; + } + + // Extended instructions: currently, assume everything is an ID. + // TODO: add whatever data we need for exceptions to that + if (opCode == spv::OpExtInst) { + word += 2; // instruction set, and instruction from set + numOperands -= 2; + + for (unsigned op=0; op < numOperands; ++op) + idFn(asId(word++)); // ID + + return nextInst; + } + + // Circular buffer so we can look back at previous unmapped values during the mapping pass. + static const unsigned idBufferSize = 4; + spv::Id idBuffer[idBufferSize]; + unsigned idBufferPos = 0; + + // Store IDs from instruction in our map + for (int op = 0; numOperands > 0; ++op, --numOperands) { + // SpecConstantOp is special: it includes the operands of another opcode which is + // given as a literal in the 3rd word. We will switch over to pretending that the + // opcode being processed is the literal opcode value of the SpecConstantOp. See the + // SPIRV spec for details. This way we will handle IDs and literals as appropriate for + // the embedded op. + if (opCode == spv::OpSpecConstantOp) { + if (op == 0) { + opCode = asOpCode(word++); // this is the opcode embedded in the SpecConstantOp. + --numOperands; + } + } + + switch (spv::InstructionDesc[opCode].operands.getClass(op)) { + case spv::OperandId: + case spv::OperandScope: + case spv::OperandMemorySemantics: + idBuffer[idBufferPos] = asId(word); + idBufferPos = (idBufferPos + 1) % idBufferSize; + idFn(asId(word++)); + break; + + case spv::OperandVariableIds: + for (unsigned i = 0; i < numOperands; ++i) + idFn(asId(word++)); + return nextInst; + + case spv::OperandVariableLiterals: + // for clarity + // if (opCode == spv::OpDecorate && asDecoration(word - 1) == spv::DecorationBuiltIn) { + // ++word; + // --numOperands; + // } + // word += numOperands; + return nextInst; + + case spv::OperandVariableLiteralId: { + if (opCode == OpSwitch) { + // word-2 is the position of the selector ID. OpSwitch Literals match its type. + // In case the IDs are currently being remapped, we get the word[-2] ID from + // the circular idBuffer. + const unsigned literalSizePos = (idBufferPos+idBufferSize-2) % idBufferSize; + const unsigned literalSize = idTypeSizeInWords(idBuffer[literalSizePos]); + const unsigned numLiteralIdPairs = (nextInst-word) / (1+literalSize); + + if (errorLatch) + return -1; + + for (unsigned arg=0; arg instPos; + instPos.reserve(unsigned(spv.size()) / 16); // initial estimate; can grow if needed. + + // Build local table of instruction start positions + process( + [&](spv::Op, unsigned start) { instPos.push_back(start); return true; }, + op_fn_nop); + + if (errorLatch) + return; + + // Window size for context-sensitive canonicalization values + // Empirical best size from a single data set. TODO: Would be a good tunable. + // We essentially perform a little convolution around each instruction, + // to capture the flavor of nearby code, to hopefully match to similar + // code in other modules. + static const unsigned windowSize = 2; + + for (unsigned entry = 0; entry < unsigned(instPos.size()); ++entry) { + const unsigned start = instPos[entry]; + const spv::Op opCode = asOpCode(start); + + if (opCode == spv::OpFunction) + fnId = asId(start + 2); + + if (opCode == spv::OpFunctionEnd) + fnId = spv::NoResult; + + if (fnId != spv::NoResult) { // if inside a function + if (spv::InstructionDesc[opCode].hasResult()) { + const unsigned word = start + (spv::InstructionDesc[opCode].hasType() ? 2 : 1); + const spv::Id resId = asId(word); + std::uint32_t hashval = fnId * 17; // small prime + + for (unsigned i = entry-1; i >= entry-windowSize; --i) { + if (asOpCode(instPos[i]) == spv::OpFunction) + break; + hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime + } + + for (unsigned i = entry; i <= entry + windowSize; ++i) { + if (asOpCode(instPos[i]) == spv::OpFunctionEnd) + break; + hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime + } + + if (isOldIdUnmapped(resId)) { + localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID)); + if (errorLatch) + return; + } + + } + } + } + + spv::Op thisOpCode(spv::OpNop); + std::unordered_map opCounter; + int idCounter(0); + fnId = spv::NoResult; + + process( + [&](spv::Op opCode, unsigned start) { + switch (opCode) { + case spv::OpFunction: + // Reset counters at each function + idCounter = 0; + opCounter.clear(); + fnId = asId(start + 2); + break; + + case spv::OpImageSampleImplicitLod: + case spv::OpImageSampleExplicitLod: + case spv::OpImageSampleDrefImplicitLod: + case spv::OpImageSampleDrefExplicitLod: + case spv::OpImageSampleProjImplicitLod: + case spv::OpImageSampleProjExplicitLod: + case spv::OpImageSampleProjDrefImplicitLod: + case spv::OpImageSampleProjDrefExplicitLod: + case spv::OpDot: + case spv::OpCompositeExtract: + case spv::OpCompositeInsert: + case spv::OpVectorShuffle: + case spv::OpLabel: + case spv::OpVariable: + + case spv::OpAccessChain: + case spv::OpLoad: + case spv::OpStore: + case spv::OpCompositeConstruct: + case spv::OpFunctionCall: + ++opCounter[opCode]; + idCounter = 0; + thisOpCode = opCode; + break; + default: + thisOpCode = spv::OpNop; + } + + return false; + }, + + [&](spv::Id& id) { + if (thisOpCode != spv::OpNop) { + ++idCounter; + const std::uint32_t hashval = opCounter[thisOpCode] * thisOpCode * 50047 + idCounter + fnId * 117; + + if (isOldIdUnmapped(id)) + localId(id, nextUnusedId(hashval % softTypeIdLimit + firstMappedID)); + } + }); + } + + // EXPERIMENTAL: forward IO and uniform load/stores into operands + // This produces invalid Schema-0 SPIRV + void spirvbin_t::forwardLoadStores() + { + idset_t fnLocalVars; // set of function local vars + idmap_t idMap; // Map of load result IDs to what they load + + // EXPERIMENTAL: Forward input and access chain loads into consumptions + process( + [&](spv::Op opCode, unsigned start) { + // Add inputs and uniforms to the map + if ((opCode == spv::OpVariable && asWordCount(start) == 4) && + (spv[start+3] == spv::StorageClassUniform || + spv[start+3] == spv::StorageClassUniformConstant || + spv[start+3] == spv::StorageClassInput)) + fnLocalVars.insert(asId(start+2)); + + if (opCode == spv::OpAccessChain && fnLocalVars.count(asId(start+3)) > 0) + fnLocalVars.insert(asId(start+2)); + + if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) { + idMap[asId(start+2)] = asId(start+3); + stripInst(start); + } + + return false; + }, + + [&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; } + ); + + if (errorLatch) + return; + + // EXPERIMENTAL: Implicit output stores + fnLocalVars.clear(); + idMap.clear(); + + process( + [&](spv::Op opCode, unsigned start) { + // Add inputs and uniforms to the map + if ((opCode == spv::OpVariable && asWordCount(start) == 4) && + (spv[start+3] == spv::StorageClassOutput)) + fnLocalVars.insert(asId(start+2)); + + if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) { + idMap[asId(start+2)] = asId(start+1); + stripInst(start); + } + + return false; + }, + op_fn_nop); + + if (errorLatch) + return; + + process( + inst_fn_nop, + [&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; } + ); + + if (errorLatch) + return; + + strip(); // strip out data we decided to eliminate + } + + // optimize loads and stores + void spirvbin_t::optLoadStore() + { + idset_t fnLocalVars; // candidates for removal (only locals) + idmap_t idMap; // Map of load result IDs to what they load + blockmap_t blockMap; // Map of IDs to blocks they first appear in + int blockNum = 0; // block count, to avoid crossing flow control + + // Find all the function local pointers stored at most once, and not via access chains + process( + [&](spv::Op opCode, unsigned start) { + const int wordCount = asWordCount(start); + + // Count blocks, so we can avoid crossing flow control + if (isFlowCtrl(opCode)) + ++blockNum; + + // Add local variables to the map + if ((opCode == spv::OpVariable && spv[start+3] == spv::StorageClassFunction && asWordCount(start) == 4)) { + fnLocalVars.insert(asId(start+2)); + return true; + } + + // Ignore process vars referenced via access chain + if ((opCode == spv::OpAccessChain || opCode == spv::OpInBoundsAccessChain) && fnLocalVars.count(asId(start+3)) > 0) { + fnLocalVars.erase(asId(start+3)); + idMap.erase(asId(start+3)); + return true; + } + + if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) { + const spv::Id varId = asId(start+3); + + // Avoid loads before stores + if (idMap.find(varId) == idMap.end()) { + fnLocalVars.erase(varId); + idMap.erase(varId); + } + + // don't do for volatile references + if (wordCount > 4 && (spv[start+4] & spv::MemoryAccessVolatileMask)) { + fnLocalVars.erase(varId); + idMap.erase(varId); + } + + // Handle flow control + if (blockMap.find(varId) == blockMap.end()) { + blockMap[varId] = blockNum; // track block we found it in. + } else if (blockMap[varId] != blockNum) { + fnLocalVars.erase(varId); // Ignore if crosses flow control + idMap.erase(varId); + } + + return true; + } + + if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) { + const spv::Id varId = asId(start+1); + + if (idMap.find(varId) == idMap.end()) { + idMap[varId] = asId(start+2); + } else { + // Remove if it has more than one store to the same pointer + fnLocalVars.erase(varId); + idMap.erase(varId); + } + + // don't do for volatile references + if (wordCount > 3 && (spv[start+3] & spv::MemoryAccessVolatileMask)) { + fnLocalVars.erase(asId(start+3)); + idMap.erase(asId(start+3)); + } + + // Handle flow control + if (blockMap.find(varId) == blockMap.end()) { + blockMap[varId] = blockNum; // track block we found it in. + } else if (blockMap[varId] != blockNum) { + fnLocalVars.erase(varId); // Ignore if crosses flow control + idMap.erase(varId); + } + + return true; + } + + return false; + }, + + // If local var id used anywhere else, don't eliminate + [&](spv::Id& id) { + if (fnLocalVars.count(id) > 0) { + fnLocalVars.erase(id); + idMap.erase(id); + } + } + ); + + if (errorLatch) + return; + + process( + [&](spv::Op opCode, unsigned start) { + if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) + idMap[asId(start+2)] = idMap[asId(start+3)]; + return false; + }, + op_fn_nop); + + if (errorLatch) + return; + + // Chase replacements to their origins, in case there is a chain such as: + // 2 = store 1 + // 3 = load 2 + // 4 = store 3 + // 5 = load 4 + // We want to replace uses of 5 with 1. + for (const auto& idPair : idMap) { + spv::Id id = idPair.first; + while (idMap.find(id) != idMap.end()) // Chase to end of chain + id = idMap[id]; + + idMap[idPair.first] = id; // replace with final result + } + + // Remove the load/store/variables for the ones we've discovered + process( + [&](spv::Op opCode, unsigned start) { + if ((opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) || + (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) || + (opCode == spv::OpVariable && fnLocalVars.count(asId(start+2)) > 0)) { + + stripInst(start); + return true; + } + + return false; + }, + + [&](spv::Id& id) { + if (idMap.find(id) != idMap.end()) id = idMap[id]; + } + ); + + if (errorLatch) + return; + + strip(); // strip out data we decided to eliminate + } + + // remove bodies of uncalled functions + void spirvbin_t::dceFuncs() + { + msg(3, 2, std::string("Removing Dead Functions: ")); + + // TODO: There are more efficient ways to do this. + bool changed = true; + + while (changed) { + changed = false; + + for (auto fn = fnPos.begin(); fn != fnPos.end(); ) { + if (fn->first == entryPoint) { // don't DCE away the entry point! + ++fn; + continue; + } + + const auto call_it = fnCalls.find(fn->first); + + if (call_it == fnCalls.end() || call_it->second == 0) { + changed = true; + stripRange.push_back(fn->second); + + // decrease counts of called functions + process( + [&](spv::Op opCode, unsigned start) { + if (opCode == spv::Op::OpFunctionCall) { + const auto call_it = fnCalls.find(asId(start + 3)); + if (call_it != fnCalls.end()) { + if (--call_it->second <= 0) + fnCalls.erase(call_it); + } + } + + return true; + }, + op_fn_nop, + fn->second.first, + fn->second.second); + + if (errorLatch) + return; + + fn = fnPos.erase(fn); + } else ++fn; + } + } + } + + // remove unused function variables + decorations + void spirvbin_t::dceVars() + { + msg(3, 2, std::string("DCE Vars: ")); + + std::unordered_map varUseCount; + + // Count function variable use + process( + [&](spv::Op opCode, unsigned start) { + if (opCode == spv::OpVariable) { + ++varUseCount[asId(start+2)]; + return true; + } else if (opCode == spv::OpEntryPoint) { + const int wordCount = asWordCount(start); + for (int i = 4; i < wordCount; i++) { + ++varUseCount[asId(start+i)]; + } + return true; + } else + return false; + }, + + [&](spv::Id& id) { if (varUseCount[id]) ++varUseCount[id]; } + ); + + if (errorLatch) + return; + + // Remove single-use function variables + associated decorations and names + process( + [&](spv::Op opCode, unsigned start) { + spv::Id id = spv::NoResult; + if (opCode == spv::OpVariable) + id = asId(start+2); + if (opCode == spv::OpDecorate || opCode == spv::OpName) + id = asId(start+1); + + if (id != spv::NoResult && varUseCount[id] == 1) + stripInst(start); + + return true; + }, + op_fn_nop); + } + + // remove unused types + void spirvbin_t::dceTypes() + { + std::vector isType(bound(), false); + + // for speed, make O(1) way to get to type query (map is log(n)) + for (const auto typeStart : typeConstPos) + isType[asTypeConstId(typeStart)] = true; + + std::unordered_map typeUseCount; + + // This is not the most efficient algorithm, but this is an offline tool, and + // it's easy to write this way. Can be improved opportunistically if needed. + bool changed = true; + while (changed) { + changed = false; + strip(); + typeUseCount.clear(); + + // Count total type usage + process(inst_fn_nop, + [&](spv::Id& id) { if (isType[id]) ++typeUseCount[id]; } + ); + + if (errorLatch) + return; + + // Remove single reference types + for (const auto typeStart : typeConstPos) { + const spv::Id typeId = asTypeConstId(typeStart); + if (typeUseCount[typeId] == 1) { + changed = true; + --typeUseCount[typeId]; + stripInst(typeStart); + } + } + + if (errorLatch) + return; + } + } + +#ifdef NOTDEF + bool spirvbin_t::matchType(const spirvbin_t::globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const + { + // Find the local type id "lt" and global type id "gt" + const auto lt_it = typeConstPosR.find(lt); + if (lt_it == typeConstPosR.end()) + return false; + + const auto typeStart = lt_it->second; + + // Search for entry in global table + const auto gtype = globalTypes.find(gt); + if (gtype == globalTypes.end()) + return false; + + const auto& gdata = gtype->second; + + // local wordcount and opcode + const int wordCount = asWordCount(typeStart); + const spv::Op opCode = asOpCode(typeStart); + + // no type match if opcodes don't match, or operand count doesn't match + if (opCode != opOpCode(gdata[0]) || wordCount != opWordCount(gdata[0])) + return false; + + const unsigned numOperands = wordCount - 2; // all types have a result + + const auto cmpIdRange = [&](range_t range) { + for (int x=range.first; xsecond; + } + + // Hash types to canonical values. This can return ID collisions (it's a bit + // inevitable): it's up to the caller to handle that gracefully. + std::uint32_t spirvbin_t::hashType(unsigned typeStart) const + { + const unsigned wordCount = asWordCount(typeStart); + const spv::Op opCode = asOpCode(typeStart); + + switch (opCode) { + case spv::OpTypeVoid: return 0; + case spv::OpTypeBool: return 1; + case spv::OpTypeInt: return 3 + (spv[typeStart+3]); + case spv::OpTypeFloat: return 5; + case spv::OpTypeVector: + return 6 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1); + case spv::OpTypeMatrix: + return 30 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1); + case spv::OpTypeImage: + return 120 + hashType(idPos(spv[typeStart+2])) + + spv[typeStart+3] + // dimensionality + spv[typeStart+4] * 8 * 16 + // depth + spv[typeStart+5] * 4 * 16 + // arrayed + spv[typeStart+6] * 2 * 16 + // multisampled + spv[typeStart+7] * 1 * 16; // format + case spv::OpTypeSampler: + return 500; + case spv::OpTypeSampledImage: + return 502; + case spv::OpTypeArray: + return 501 + hashType(idPos(spv[typeStart+2])) * spv[typeStart+3]; + case spv::OpTypeRuntimeArray: + return 5000 + hashType(idPos(spv[typeStart+2])); + case spv::OpTypeStruct: + { + std::uint32_t hash = 10000; + for (unsigned w=2; w < wordCount; ++w) + hash += w * hashType(idPos(spv[typeStart+w])); + return hash; + } + + case spv::OpTypeOpaque: return 6000 + spv[typeStart+2]; + case spv::OpTypePointer: return 100000 + hashType(idPos(spv[typeStart+3])); + case spv::OpTypeFunction: + { + std::uint32_t hash = 200000; + for (unsigned w=2; w < wordCount; ++w) + hash += w * hashType(idPos(spv[typeStart+w])); + return hash; + } + + case spv::OpTypeEvent: return 300000; + case spv::OpTypeDeviceEvent: return 300001; + case spv::OpTypeReserveId: return 300002; + case spv::OpTypeQueue: return 300003; + case spv::OpTypePipe: return 300004; + case spv::OpConstantTrue: return 300007; + case spv::OpConstantFalse: return 300008; + case spv::OpConstantComposite: + { + std::uint32_t hash = 300011 + hashType(idPos(spv[typeStart+1])); + for (unsigned w=3; w < wordCount; ++w) + hash += w * hashType(idPos(spv[typeStart+w])); + return hash; + } + case spv::OpConstant: + { + std::uint32_t hash = 400011 + hashType(idPos(spv[typeStart+1])); + for (unsigned w=3; w < wordCount; ++w) + hash += w * spv[typeStart+w]; + return hash; + } + case spv::OpConstantNull: + { + std::uint32_t hash = 500009 + hashType(idPos(spv[typeStart+1])); + return hash; + } + case spv::OpConstantSampler: + { + std::uint32_t hash = 600011 + hashType(idPos(spv[typeStart+1])); + for (unsigned w=3; w < wordCount; ++w) + hash += w * spv[typeStart+w]; + return hash; + } + + default: + error("unknown type opcode"); + return 0; + } + } + + void spirvbin_t::mapTypeConst() + { + globaltypes_t globalTypeMap; + + msg(3, 2, std::string("Remapping Consts & Types: ")); + + static const std::uint32_t softTypeIdLimit = 3011; // small prime. TODO: get from options + static const std::uint32_t firstMappedID = 8; // offset into ID space + + for (auto& typeStart : typeConstPos) { + const spv::Id resId = asTypeConstId(typeStart); + const std::uint32_t hashval = hashType(typeStart); + + if (errorLatch) + return; + + if (isOldIdUnmapped(resId)) { + localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID)); + if (errorLatch) + return; + } + } + } + + // Strip a single binary by removing ranges given in stripRange + void spirvbin_t::strip() + { + if (stripRange.empty()) // nothing to do + return; + + // Sort strip ranges in order of traversal + std::sort(stripRange.begin(), stripRange.end()); + + // Allocate a new binary big enough to hold old binary + // We'll step this iterator through the strip ranges as we go through the binary + auto strip_it = stripRange.begin(); + + int strippedPos = 0; + for (unsigned word = 0; word < unsigned(spv.size()); ++word) { + while (strip_it != stripRange.end() && word >= strip_it->second) + ++strip_it; + + if (strip_it == stripRange.end() || word < strip_it->first || word >= strip_it->second) + spv[strippedPos++] = spv[word]; + } + + spv.resize(strippedPos); + stripRange.clear(); + + buildLocalMaps(); + } + + // Strip a single binary by removing ranges given in stripRange + void spirvbin_t::remap(std::uint32_t opts) + { + options = opts; + + // Set up opcode tables from SpvDoc + spv::Parameterize(); + + validate(); // validate header + buildLocalMaps(); // build ID maps + + msg(3, 4, std::string("ID bound: ") + std::to_string(bound())); + + if (options & STRIP) stripDebug(); + if (errorLatch) return; + + strip(); // strip out data we decided to eliminate + if (errorLatch) return; + + if (options & OPT_LOADSTORE) optLoadStore(); + if (errorLatch) return; + + if (options & OPT_FWD_LS) forwardLoadStores(); + if (errorLatch) return; + + if (options & DCE_FUNCS) dceFuncs(); + if (errorLatch) return; + + if (options & DCE_VARS) dceVars(); + if (errorLatch) return; + + if (options & DCE_TYPES) dceTypes(); + if (errorLatch) return; + + strip(); // strip out data we decided to eliminate + if (errorLatch) return; + + stripDeadRefs(); // remove references to things we DCEed + if (errorLatch) return; + + // after the last strip, we must clean any debug info referring to now-deleted data + + if (options & MAP_TYPES) mapTypeConst(); + if (errorLatch) return; + + if (options & MAP_NAMES) mapNames(); + if (errorLatch) return; + + if (options & MAP_FUNCS) mapFnBodies(); + if (errorLatch) return; + + if (options & MAP_ALL) { + mapRemainder(); // map any unmapped IDs + if (errorLatch) return; + + applyMap(); // Now remap each shader to the new IDs we've come up with + if (errorLatch) return; + } + } + + // remap from a memory image + void spirvbin_t::remap(std::vector& in_spv, std::uint32_t opts) + { + spv.swap(in_spv); + remap(opts); + spv.swap(in_spv); + } + +} // namespace SPV + +#endif // defined (use_cpp11) + diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/SPVRemapper.h b/armorcore/tools/to_spirv/glslang/SPIRV/SPVRemapper.h new file mode 100644 index 000000000..d6b9c346d --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/SPVRemapper.h @@ -0,0 +1,304 @@ +// +// Copyright (C) 2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef SPIRVREMAPPER_H +#define SPIRVREMAPPER_H + +#include +#include +#include +#include + +namespace spv { + +// MSVC defines __cplusplus as an older value, even when it supports almost all of 11. +// We handle that here by making our own symbol. +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1700) +# define use_cpp11 1 +#endif + +class spirvbin_base_t +{ +public: + enum Options { + NONE = 0, + STRIP = (1<<0), + MAP_TYPES = (1<<1), + MAP_NAMES = (1<<2), + MAP_FUNCS = (1<<3), + DCE_FUNCS = (1<<4), + DCE_VARS = (1<<5), + DCE_TYPES = (1<<6), + OPT_LOADSTORE = (1<<7), + OPT_FWD_LS = (1<<8), // EXPERIMENTAL: PRODUCES INVALID SCHEMA-0 SPIRV + MAP_ALL = (MAP_TYPES | MAP_NAMES | MAP_FUNCS), + DCE_ALL = (DCE_FUNCS | DCE_VARS | DCE_TYPES), + OPT_ALL = (OPT_LOADSTORE), + + ALL_BUT_STRIP = (MAP_ALL | DCE_ALL | OPT_ALL), + DO_EVERYTHING = (STRIP | ALL_BUT_STRIP) + }; +}; + +} // namespace SPV + +#if !defined (use_cpp11) +#include +#include + +namespace spv { +class spirvbin_t : public spirvbin_base_t +{ +public: + spirvbin_t(int /*verbose = 0*/) { } + + void remap(std::vector& /*spv*/, unsigned int /*opts = 0*/) + { + printf("Tool not compiled for C++11, which is required for SPIR-V remapping.\n"); + exit(5); + } +}; + +} // namespace SPV + +#else // defined (use_cpp11) + +#include +#include +#include +#include +#include +#include +#include + +#include "spirv.hpp" +#include "spvIR.h" + +namespace spv { + +// class to hold SPIR-V binary data for remapping, DCE, and debug stripping +class spirvbin_t : public spirvbin_base_t +{ +public: + spirvbin_t(int verbose = 0) : entryPoint(spv::NoResult), largestNewId(0), verbose(verbose), errorLatch(false) + { } + + virtual ~spirvbin_t() { } + + // remap on an existing binary in memory + void remap(std::vector& spv, std::uint32_t opts = DO_EVERYTHING); + + // Type for error/log handler functions + typedef std::function errorfn_t; + typedef std::function logfn_t; + + // Register error/log handling functions (can be lambda fn / functor / etc) + static void registerErrorHandler(errorfn_t handler) { errorHandler = handler; } + static void registerLogHandler(logfn_t handler) { logHandler = handler; } + +protected: + // This can be overridden to provide other message behavior if needed + virtual void msg(int minVerbosity, int indent, const std::string& txt) const; + +private: + // Local to global, or global to local ID map + typedef std::unordered_map idmap_t; + typedef std::unordered_set idset_t; + typedef std::unordered_map blockmap_t; + + void remap(std::uint32_t opts = DO_EVERYTHING); + + // Map of names to IDs + typedef std::unordered_map namemap_t; + + typedef std::uint32_t spirword_t; + + typedef std::pair range_t; + typedef std::function idfn_t; + typedef std::function instfn_t; + + // Special Values for ID map: + static const spv::Id unmapped; // unchanged from default value + static const spv::Id unused; // unused ID + static const int header_size; // SPIR header = 5 words + + class id_iterator_t; + + // For mapping type entries between different shaders + typedef std::vector typeentry_t; + typedef std::map globaltypes_t; + + // A set that preserves position order, and a reverse map + typedef std::set posmap_t; + typedef std::unordered_map posmap_rev_t; + + // Maps and ID to the size of its base type, if known. + typedef std::unordered_map typesize_map_t; + + // handle error + void error(const std::string& txt) const { errorLatch = true; errorHandler(txt); } + + bool isConstOp(spv::Op opCode) const; + bool isTypeOp(spv::Op opCode) const; + bool isStripOp(spv::Op opCode) const; + bool isFlowCtrl(spv::Op opCode) const; + range_t literalRange(spv::Op opCode) const; + range_t typeRange(spv::Op opCode) const; + range_t constRange(spv::Op opCode) const; + unsigned typeSizeInWords(spv::Id id) const; + unsigned idTypeSizeInWords(spv::Id id) const; + + spv::Id& asId(unsigned word) { return spv[word]; } + const spv::Id& asId(unsigned word) const { return spv[word]; } + spv::Op asOpCode(unsigned word) const { return opOpCode(spv[word]); } + std::uint32_t asOpCodeHash(unsigned word); + spv::Decoration asDecoration(unsigned word) const { return spv::Decoration(spv[word]); } + unsigned asWordCount(unsigned word) const { return opWordCount(spv[word]); } + spv::Id asTypeConstId(unsigned word) const { return asId(word + (isTypeOp(asOpCode(word)) ? 1 : 2)); } + unsigned idPos(spv::Id id) const; + + static unsigned opWordCount(spirword_t data) { return data >> spv::WordCountShift; } + static spv::Op opOpCode(spirword_t data) { return spv::Op(data & spv::OpCodeMask); } + + // Header access & set methods + spirword_t magic() const { return spv[0]; } // return magic number + spirword_t bound() const { return spv[3]; } // return Id bound from header + spirword_t bound(spirword_t b) { return spv[3] = b; } + spirword_t genmagic() const { return spv[2]; } // generator magic + spirword_t genmagic(spirword_t m) { return spv[2] = m; } + spirword_t schemaNum() const { return spv[4]; } // schema number from header + + // Mapping fns: get + spv::Id localId(spv::Id id) const { return idMapL[id]; } + + // Mapping fns: set + inline spv::Id localId(spv::Id id, spv::Id newId); + void countIds(spv::Id id); + + // Return next unused new local ID. + // NOTE: boost::dynamic_bitset would be more efficient due to find_next(), + // which std::vector doens't have. + inline spv::Id nextUnusedId(spv::Id id); + + void buildLocalMaps(); + std::string literalString(unsigned word) const; // Return literal as a std::string + int literalStringWords(const std::string& str) const { return (int(str.size())+4)/4; } + + bool isNewIdMapped(spv::Id newId) const { return isMapped(newId); } + bool isOldIdUnmapped(spv::Id oldId) const { return localId(oldId) == unmapped; } + bool isOldIdUnused(spv::Id oldId) const { return localId(oldId) == unused; } + bool isOldIdMapped(spv::Id oldId) const { return !isOldIdUnused(oldId) && !isOldIdUnmapped(oldId); } + bool isFunction(spv::Id oldId) const { return fnPos.find(oldId) != fnPos.end(); } + + // bool matchType(const globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const; + // spv::Id findType(const globaltypes_t& globalTypes, spv::Id lt) const; + std::uint32_t hashType(unsigned typeStart) const; + + spirvbin_t& process(instfn_t, idfn_t, unsigned begin = 0, unsigned end = 0); + int processInstruction(unsigned word, instfn_t, idfn_t); + + void validate() const; + void mapTypeConst(); + void mapFnBodies(); + void optLoadStore(); + void dceFuncs(); + void dceVars(); + void dceTypes(); + void mapNames(); + void foldIds(); // fold IDs to smallest space + void forwardLoadStores(); // load store forwarding (EXPERIMENTAL) + void offsetIds(); // create relative offset IDs + + void applyMap(); // remap per local name map + void mapRemainder(); // map any IDs we haven't touched yet + void stripDebug(); // strip all debug info + void stripDeadRefs(); // strips debug info for now-dead references after DCE + void strip(); // remove debug symbols + + std::vector spv; // SPIR words + + namemap_t nameMap; // ID names from OpName + + // Since we want to also do binary ops, we can't use std::vector. we could use + // boost::dynamic_bitset, but we're trying to avoid a boost dependency. + typedef std::uint64_t bits_t; + std::vector mapped; // which new IDs have been mapped + static const int mBits = sizeof(bits_t) * 4; + + bool isMapped(spv::Id id) const { return id < maxMappedId() && ((mapped[id/mBits] & (1LL<<(id%mBits))) != 0); } + void setMapped(spv::Id id) { resizeMapped(id); mapped[id/mBits] |= (1LL<<(id%mBits)); } + void resizeMapped(spv::Id id) { if (id >= maxMappedId()) mapped.resize(id/mBits+1, 0); } + size_t maxMappedId() const { return mapped.size() * mBits; } + + // Add a strip range for a given instruction starting at 'start' + // Note: avoiding brace initializers to please older versions os MSVC. + void stripInst(unsigned start) { stripRange.push_back(range_t(start, start + asWordCount(start))); } + + // Function start and end. use unordered_map because we'll have + // many fewer functions than IDs. + std::unordered_map fnPos; + + // Which functions are called, anywhere in the module, with a call count + std::unordered_map fnCalls; + + posmap_t typeConstPos; // word positions that define types & consts (ordered) + posmap_rev_t idPosR; // reverse map from IDs to positions + typesize_map_t idTypeSizeMap; // maps each ID to its type size, if known. + + std::vector idMapL; // ID {M}ap from {L}ocal to {G}lobal IDs + + spv::Id entryPoint; // module entry point + spv::Id largestNewId; // biggest new ID we have mapped anything to + + // Sections of the binary to strip, given as [begin,end) + std::vector stripRange; + + // processing options: + std::uint32_t options; + int verbose; // verbosity level + + // Error latch: this is set if the error handler is ever executed. It would be better to + // use a try/catch block and throw, but that's not desired for certain environments, so + // this is the alternative. + mutable bool errorLatch; + + static errorfn_t errorHandler; + static logfn_t logHandler; +}; + +} // namespace SPV + +#endif // defined (use_cpp11) +#endif // SPIRVREMAPPER_H diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/SpvBuilder.cpp b/armorcore/tools/to_spirv/glslang/SPIRV/SpvBuilder.cpp new file mode 100644 index 000000000..bf15253e9 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/SpvBuilder.cpp @@ -0,0 +1,3130 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Helper for making SPIR-V IR. Generally, this is documented in the header +// SpvBuilder.h. +// + +#include +#include + +#include +#include + +#include "SpvBuilder.h" + +#ifndef GLSLANG_WEB +#include "hex_float.h" +#endif + +#ifndef _WIN32 + #include +#endif + +namespace spv { + +Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) : + spvVersion(spvVersion), + source(SourceLanguageUnknown), + sourceVersion(0), + sourceFileStringId(NoResult), + currentLine(0), + currentFile(nullptr), + emitOpLines(false), + addressModel(AddressingModelLogical), + memoryModel(MemoryModelGLSL450), + builderNumber(magicNumber), + buildPoint(0), + uniqueId(0), + entryPointFunction(0), + generatingOpCodeForSpecConst(false), + logger(buildLogger) +{ + clearAccessChain(); +} + +Builder::~Builder() +{ +} + +Id Builder::import(const char* name) +{ + Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport); + import->addStringOperand(name); + module.mapInstruction(import); + + imports.push_back(std::unique_ptr(import)); + return import->getResultId(); +} + +// Emit instruction for non-filename-based #line directives (ie. no filename +// seen yet): emit an OpLine if we've been asked to emit OpLines and the line +// number has changed since the last time, and is a valid line number. +void Builder::setLine(int lineNum) +{ + if (lineNum != 0 && lineNum != currentLine) { + currentLine = lineNum; + if (emitOpLines) + addLine(sourceFileStringId, currentLine, 0); + } +} + +// If no filename, do non-filename-based #line emit. Else do filename-based emit. +// Emit OpLine if we've been asked to emit OpLines and the line number or filename +// has changed since the last time, and line number is valid. +void Builder::setLine(int lineNum, const char* filename) +{ + if (filename == nullptr) { + setLine(lineNum); + return; + } + if ((lineNum != 0 && lineNum != currentLine) || currentFile == nullptr || + strncmp(filename, currentFile, strlen(currentFile) + 1) != 0) { + currentLine = lineNum; + currentFile = filename; + if (emitOpLines) { + spv::Id strId = getStringId(filename); + addLine(strId, currentLine, 0); + } + } +} + +void Builder::addLine(Id fileName, int lineNum, int column) +{ + Instruction* line = new Instruction(OpLine); + line->addIdOperand(fileName); + line->addImmediateOperand(lineNum); + line->addImmediateOperand(column); + buildPoint->addInstruction(std::unique_ptr(line)); +} + +// For creating new groupedTypes (will return old type if the requested one was already made). +Id Builder::makeVoidType() +{ + Instruction* type; + if (groupedTypes[OpTypeVoid].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeVoid); + groupedTypes[OpTypeVoid].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + } else + type = groupedTypes[OpTypeVoid].back(); + + return type->getResultId(); +} + +Id Builder::makeBoolType() +{ + Instruction* type; + if (groupedTypes[OpTypeBool].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeBool); + groupedTypes[OpTypeBool].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + } else + type = groupedTypes[OpTypeBool].back(); + + return type->getResultId(); +} + +Id Builder::makeSamplerType() +{ + Instruction* type; + if (groupedTypes[OpTypeSampler].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeSampler); + groupedTypes[OpTypeSampler].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + } else + type = groupedTypes[OpTypeSampler].back(); + + return type->getResultId(); +} + +Id Builder::makePointer(StorageClass storageClass, Id pointee) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { + type = groupedTypes[OpTypePointer][t]; + if (type->getImmediateOperand(0) == (unsigned)storageClass && + type->getIdOperand(1) == pointee) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypePointer); + type->addImmediateOperand(storageClass); + type->addIdOperand(pointee); + groupedTypes[OpTypePointer].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeForwardPointer(StorageClass storageClass) +{ + // Caching/uniquifying doesn't work here, because we don't know the + // pointee type and there can be multiple forward pointers of the same + // storage type. Somebody higher up in the stack must keep track. + Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeForwardPointer); + type->addImmediateOperand(storageClass); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { + type = groupedTypes[OpTypePointer][t]; + if (type->getImmediateOperand(0) == (unsigned)storageClass && + type->getIdOperand(1) == pointee) + return type->getResultId(); + } + + type = new Instruction(forwardPointerType, NoType, OpTypePointer); + type->addImmediateOperand(storageClass); + type->addIdOperand(pointee); + groupedTypes[OpTypePointer].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeIntegerType(int width, bool hasSign) +{ +#ifdef GLSLANG_WEB + assert(width == 32); + width = 32; +#endif + + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) { + type = groupedTypes[OpTypeInt][t]; + if (type->getImmediateOperand(0) == (unsigned)width && + type->getImmediateOperand(1) == (hasSign ? 1u : 0u)) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeInt); + type->addImmediateOperand(width); + type->addImmediateOperand(hasSign ? 1 : 0); + groupedTypes[OpTypeInt].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + // deal with capabilities + switch (width) { + case 8: + case 16: + // these are currently handled by storage-type declarations and post processing + break; + case 64: + addCapability(CapabilityInt64); + break; + default: + break; + } + + return type->getResultId(); +} + +Id Builder::makeFloatType(int width) +{ +#ifdef GLSLANG_WEB + assert(width == 32); + width = 32; +#endif + + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) { + type = groupedTypes[OpTypeFloat][t]; + if (type->getImmediateOperand(0) == (unsigned)width) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeFloat); + type->addImmediateOperand(width); + groupedTypes[OpTypeFloat].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + // deal with capabilities + switch (width) { + case 16: + // currently handled by storage-type declarations and post processing + break; + case 64: + addCapability(CapabilityFloat64); + break; + default: + break; + } + + return type->getResultId(); +} + +// Make a struct without checking for duplication. +// See makeStructResultType() for non-decorated structs +// needed as the result of some instructions, which does +// check for duplicates. +Id Builder::makeStructType(const std::vector& members, const char* name) +{ + // Don't look for previous one, because in the general case, + // structs can be duplicated except for decorations. + + // not found, make it + Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct); + for (int op = 0; op < (int)members.size(); ++op) + type->addIdOperand(members[op]); + groupedTypes[OpTypeStruct].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + addName(type->getResultId(), name); + + return type->getResultId(); +} + +// Make a struct for the simple results of several instructions, +// checking for duplication. +Id Builder::makeStructResultType(Id type0, Id type1) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) { + type = groupedTypes[OpTypeStruct][t]; + if (type->getNumOperands() != 2) + continue; + if (type->getIdOperand(0) != type0 || + type->getIdOperand(1) != type1) + continue; + return type->getResultId(); + } + + // not found, make it + std::vector members; + members.push_back(type0); + members.push_back(type1); + + return makeStructType(members, "ResType"); +} + +Id Builder::makeVectorType(Id component, int size) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) { + type = groupedTypes[OpTypeVector][t]; + if (type->getIdOperand(0) == component && + type->getImmediateOperand(1) == (unsigned)size) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeVector); + type->addIdOperand(component); + type->addImmediateOperand(size); + groupedTypes[OpTypeVector].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeMatrixType(Id component, int cols, int rows) +{ + assert(cols <= maxMatrixSize && rows <= maxMatrixSize); + + Id column = makeVectorType(component, rows); + + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) { + type = groupedTypes[OpTypeMatrix][t]; + if (type->getIdOperand(0) == column && + type->getImmediateOperand(1) == (unsigned)cols) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeMatrix); + type->addIdOperand(column); + type->addImmediateOperand(cols); + groupedTypes[OpTypeMatrix].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixNV].size(); ++t) { + type = groupedTypes[OpTypeCooperativeMatrixNV][t]; + if (type->getIdOperand(0) == component && + type->getIdOperand(1) == scope && + type->getIdOperand(2) == rows && + type->getIdOperand(3) == cols) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixNV); + type->addIdOperand(component); + type->addIdOperand(scope); + type->addIdOperand(rows); + type->addIdOperand(cols); + groupedTypes[OpTypeCooperativeMatrixNV].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + + +// TODO: performance: track arrays per stride +// If a stride is supplied (non-zero) make an array. +// If no stride (0), reuse previous array types. +// 'size' is an Id of a constant or specialization constant of the array size +Id Builder::makeArrayType(Id element, Id sizeId, int stride) +{ + Instruction* type; + if (stride == 0) { + // try to find existing type + for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) { + type = groupedTypes[OpTypeArray][t]; + if (type->getIdOperand(0) == element && + type->getIdOperand(1) == sizeId) + return type->getResultId(); + } + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeArray); + type->addIdOperand(element); + type->addIdOperand(sizeId); + groupedTypes[OpTypeArray].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeRuntimeArray(Id element) +{ + Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray); + type->addIdOperand(element); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeFunctionType(Id returnType, const std::vector& paramTypes) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) { + type = groupedTypes[OpTypeFunction][t]; + if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1) + continue; + bool mismatch = false; + for (int p = 0; p < (int)paramTypes.size(); ++p) { + if (paramTypes[p] != type->getIdOperand(p + 1)) { + mismatch = true; + break; + } + } + if (! mismatch) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeFunction); + type->addIdOperand(returnType); + for (int p = 0; p < (int)paramTypes.size(); ++p) + type->addIdOperand(paramTypes[p]); + groupedTypes[OpTypeFunction].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled, + ImageFormat format, bool video) +{ + assert(sampled == 1 || sampled == 2); + + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) { + type = groupedTypes[OpTypeImage][t]; + if (type->getIdOperand(0) == sampledType && + type->getImmediateOperand(1) == (unsigned int)dim && + type->getImmediateOperand(2) == ( depth ? 1u : 0u) && + type->getImmediateOperand(3) == (arrayed ? 1u : 0u) && + type->getImmediateOperand(4) == ( ms ? 1u : 0u) && + type->getImmediateOperand(5) == sampled && + type->getImmediateOperand(6) == (unsigned int)format && + type->getImmediateOperand(8) == (video ? 1u : 0u)) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeImage); + type->addIdOperand(sampledType); + type->addImmediateOperand( dim); + type->addImmediateOperand( depth ? 1 : 0); + type->addImmediateOperand(arrayed ? 1 : 0); + type->addImmediateOperand( ms ? 1 : 0); + type->addImmediateOperand(sampled); + type->addImmediateOperand((unsigned int)format); + type->addImmediateOperand(0); // Access Qualifier + type->addImmediateOperand(video ? 1 : 0); + + groupedTypes[OpTypeImage].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + +#ifndef GLSLANG_WEB + // deal with capabilities + switch (dim) { + case DimBuffer: + if (sampled == 1) + addCapability(CapabilitySampledBuffer); + else + addCapability(CapabilityImageBuffer); + break; + case Dim1D: + if (sampled == 1) + addCapability(CapabilitySampled1D); + else + addCapability(CapabilityImage1D); + break; + case DimCube: + if (arrayed) { + if (sampled == 1) + addCapability(CapabilitySampledCubeArray); + else + addCapability(CapabilityImageCubeArray); + } + break; + case DimRect: + if (sampled == 1) + addCapability(CapabilitySampledRect); + else + addCapability(CapabilityImageRect); + break; + case DimSubpassData: + addCapability(CapabilityInputAttachment); + break; + default: + break; + } + + if (ms) { + if (sampled == 2) { + // Images used with subpass data are not storage + // images, so don't require the capability for them. + if (dim != Dim::DimSubpassData) + addCapability(CapabilityStorageImageMultisample); + if (arrayed) + addCapability(CapabilityImageMSArray); + } + } +#endif + + return type->getResultId(); +} + +Id Builder::makeSampledImageType(Id imageType) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) { + type = groupedTypes[OpTypeSampledImage][t]; + if (type->getIdOperand(0) == imageType) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage); + type->addIdOperand(imageType); + + groupedTypes[OpTypeSampledImage].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +#ifndef GLSLANG_WEB +Id Builder::makeAccelerationStructureType() +{ + Instruction *type; + if (groupedTypes[OpTypeAccelerationStructureKHR].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureKHR); + groupedTypes[OpTypeAccelerationStructureKHR].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + } else { + type = groupedTypes[OpTypeAccelerationStructureKHR].back(); + } + + return type->getResultId(); +} + +Id Builder::makeRayQueryType() +{ + Instruction *type; + if (groupedTypes[OpTypeRayQueryProvisionalKHR].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeRayQueryProvisionalKHR); + groupedTypes[OpTypeRayQueryProvisionalKHR].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + } else { + type = groupedTypes[OpTypeRayQueryProvisionalKHR].back(); + } + + return type->getResultId(); +} +#endif + +Id Builder::getDerefTypeId(Id resultId) const +{ + Id typeId = getTypeId(resultId); + assert(isPointerType(typeId)); + + return module.getInstruction(typeId)->getIdOperand(1); +} + +Op Builder::getMostBasicTypeClass(Id typeId) const +{ + Instruction* instr = module.getInstruction(typeId); + + Op typeClass = instr->getOpCode(); + switch (typeClass) + { + case OpTypeVector: + case OpTypeMatrix: + case OpTypeArray: + case OpTypeRuntimeArray: + return getMostBasicTypeClass(instr->getIdOperand(0)); + case OpTypePointer: + return getMostBasicTypeClass(instr->getIdOperand(1)); + default: + return typeClass; + } +} + +int Builder::getNumTypeConstituents(Id typeId) const +{ + Instruction* instr = module.getInstruction(typeId); + + switch (instr->getOpCode()) + { + case OpTypeBool: + case OpTypeInt: + case OpTypeFloat: + case OpTypePointer: + return 1; + case OpTypeVector: + case OpTypeMatrix: + return instr->getImmediateOperand(1); + case OpTypeArray: + { + Id lengthId = instr->getIdOperand(1); + return module.getInstruction(lengthId)->getImmediateOperand(0); + } + case OpTypeStruct: + return instr->getNumOperands(); + case OpTypeCooperativeMatrixNV: + // has only one constituent when used with OpCompositeConstruct. + return 1; + default: + assert(0); + return 1; + } +} + +// Return the lowest-level type of scalar that an homogeneous composite is made out of. +// Typically, this is just to find out if something is made out of ints or floats. +// However, it includes returning a structure, if say, it is an array of structure. +Id Builder::getScalarTypeId(Id typeId) const +{ + Instruction* instr = module.getInstruction(typeId); + + Op typeClass = instr->getOpCode(); + switch (typeClass) + { + case OpTypeVoid: + case OpTypeBool: + case OpTypeInt: + case OpTypeFloat: + case OpTypeStruct: + return instr->getResultId(); + case OpTypeVector: + case OpTypeMatrix: + case OpTypeArray: + case OpTypeRuntimeArray: + case OpTypePointer: + return getScalarTypeId(getContainedTypeId(typeId)); + default: + assert(0); + return NoResult; + } +} + +// Return the type of 'member' of a composite. +Id Builder::getContainedTypeId(Id typeId, int member) const +{ + Instruction* instr = module.getInstruction(typeId); + + Op typeClass = instr->getOpCode(); + switch (typeClass) + { + case OpTypeVector: + case OpTypeMatrix: + case OpTypeArray: + case OpTypeRuntimeArray: + case OpTypeCooperativeMatrixNV: + return instr->getIdOperand(0); + case OpTypePointer: + return instr->getIdOperand(1); + case OpTypeStruct: + return instr->getIdOperand(member); + default: + assert(0); + return NoResult; + } +} + +// Return the immediately contained type of a given composite type. +Id Builder::getContainedTypeId(Id typeId) const +{ + return getContainedTypeId(typeId, 0); +} + +// Returns true if 'typeId' is or contains a scalar type declared with 'typeOp' +// of width 'width'. The 'width' is only consumed for int and float types. +// Returns false otherwise. +bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const +{ + const Instruction& instr = *module.getInstruction(typeId); + + Op typeClass = instr.getOpCode(); + switch (typeClass) + { + case OpTypeInt: + case OpTypeFloat: + return typeClass == typeOp && instr.getImmediateOperand(0) == width; + case OpTypeStruct: + for (int m = 0; m < instr.getNumOperands(); ++m) { + if (containsType(instr.getIdOperand(m), typeOp, width)) + return true; + } + return false; + case OpTypePointer: + return false; + case OpTypeVector: + case OpTypeMatrix: + case OpTypeArray: + case OpTypeRuntimeArray: + return containsType(getContainedTypeId(typeId), typeOp, width); + default: + return typeClass == typeOp; + } +} + +// return true if the type is a pointer to PhysicalStorageBufferEXT or an +// array of such pointers. These require restrict/aliased decorations. +bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const +{ + const Instruction& instr = *module.getInstruction(typeId); + + Op typeClass = instr.getOpCode(); + switch (typeClass) + { + case OpTypePointer: + return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT; + case OpTypeArray: + return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId)); + default: + return false; + } +} + +// See if a scalar constant of this type has already been created, so it +// can be reused rather than duplicated. (Required by the specification). +Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value) +{ + Instruction* constant; + for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { + constant = groupedConstants[typeClass][i]; + if (constant->getOpCode() == opcode && + constant->getTypeId() == typeId && + constant->getImmediateOperand(0) == value) + return constant->getResultId(); + } + + return 0; +} + +// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64'). +Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2) +{ + Instruction* constant; + for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { + constant = groupedConstants[typeClass][i]; + if (constant->getOpCode() == opcode && + constant->getTypeId() == typeId && + constant->getImmediateOperand(0) == v1 && + constant->getImmediateOperand(1) == v2) + return constant->getResultId(); + } + + return 0; +} + +// Return true if consuming 'opcode' means consuming a constant. +// "constant" here means after final transform to executable code, +// the value consumed will be a constant, so includes specialization. +bool Builder::isConstantOpCode(Op opcode) const +{ + switch (opcode) { + case OpUndef: + case OpConstantTrue: + case OpConstantFalse: + case OpConstant: + case OpConstantComposite: + case OpConstantSampler: + case OpConstantNull: + case OpSpecConstantTrue: + case OpSpecConstantFalse: + case OpSpecConstant: + case OpSpecConstantComposite: + case OpSpecConstantOp: + return true; + default: + return false; + } +} + +// Return true if consuming 'opcode' means consuming a specialization constant. +bool Builder::isSpecConstantOpCode(Op opcode) const +{ + switch (opcode) { + case OpSpecConstantTrue: + case OpSpecConstantFalse: + case OpSpecConstant: + case OpSpecConstantComposite: + case OpSpecConstantOp: + return true; + default: + return false; + } +} + +Id Builder::makeBoolConstant(bool b, bool specConstant) +{ + Id typeId = makeBoolType(); + Instruction* constant; + Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse); + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = 0; + for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) { + constant = groupedConstants[OpTypeBool][i]; + if (constant->getTypeId() == typeId && constant->getOpCode() == opcode) + existing = constant->getResultId(); + } + + if (existing) + return existing; + } + + // Make it + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeBool].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(value); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeInt].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + + unsigned op1 = value & 0xFFFFFFFF; + unsigned op2 = value >> 32; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(op1); + c->addImmediateOperand(op2); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeInt].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeFloatConstant(float f, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + Id typeId = makeFloatType(32); + union { float fl; unsigned int ui; } u; + u.fl = f; + unsigned value = u.ui; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(value); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeFloat].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeDoubleConstant(double d, bool specConstant) +{ +#ifdef GLSLANG_WEB + assert(0); + return NoResult; +#else + Op opcode = specConstant ? OpSpecConstant : OpConstant; + Id typeId = makeFloatType(64); + union { double db; unsigned long long ull; } u; + u.db = d; + unsigned long long value = u.ull; + unsigned op1 = value & 0xFFFFFFFF; + unsigned op2 = value >> 32; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(op1); + c->addImmediateOperand(op2); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeFloat].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +#endif +} + +Id Builder::makeFloat16Constant(float f16, bool specConstant) +{ +#ifdef GLSLANG_WEB + assert(0); + return NoResult; +#else + Op opcode = specConstant ? OpSpecConstant : OpConstant; + Id typeId = makeFloatType(16); + + spvutils::HexFloat> fVal(f16); + spvutils::HexFloat> f16Val(0); + fVal.castTo(f16Val, spvutils::kRoundToZero); + + unsigned value = f16Val.value().getAsFloat().get_value(); + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (!specConstant) { + Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(value); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeFloat].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +#endif +} + +Id Builder::makeFpConstant(Id type, double d, bool specConstant) +{ +#ifdef GLSLANG_WEB + const int width = 32; + assert(width == getScalarTypeWidth(type)); +#else + const int width = getScalarTypeWidth(type); +#endif + + assert(isFloatType(type)); + + switch (width) { + case 16: + return makeFloat16Constant((float)d, specConstant); + case 32: + return makeFloatConstant((float)d, specConstant); + case 64: + return makeDoubleConstant(d, specConstant); + default: + break; + } + + assert(false); + return NoResult; +} + +Id Builder::findCompositeConstant(Op typeClass, Id typeId, const std::vector& comps) +{ + Instruction* constant = 0; + bool found = false; + for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { + constant = groupedConstants[typeClass][i]; + + if (constant->getTypeId() != typeId) + continue; + + // same contents? + bool mismatch = false; + for (int op = 0; op < constant->getNumOperands(); ++op) { + if (constant->getIdOperand(op) != comps[op]) { + mismatch = true; + break; + } + } + if (! mismatch) { + found = true; + break; + } + } + + return found ? constant->getResultId() : NoResult; +} + +Id Builder::findStructConstant(Id typeId, const std::vector& comps) +{ + Instruction* constant = 0; + bool found = false; + for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) { + constant = groupedStructConstants[typeId][i]; + + // same contents? + bool mismatch = false; + for (int op = 0; op < constant->getNumOperands(); ++op) { + if (constant->getIdOperand(op) != comps[op]) { + mismatch = true; + break; + } + } + if (! mismatch) { + found = true; + break; + } + } + + return found ? constant->getResultId() : NoResult; +} + +// Comments in header +Id Builder::makeCompositeConstant(Id typeId, const std::vector& members, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite; + assert(typeId); + Op typeClass = getTypeClass(typeId); + + switch (typeClass) { + case OpTypeVector: + case OpTypeArray: + case OpTypeMatrix: + case OpTypeCooperativeMatrixNV: + if (! specConstant) { + Id existing = findCompositeConstant(typeClass, typeId, members); + if (existing) + return existing; + } + break; + case OpTypeStruct: + if (! specConstant) { + Id existing = findStructConstant(typeId, members); + if (existing) + return existing; + } + break; + default: + assert(0); + return makeFloatConstant(0.0); + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + for (int op = 0; op < (int)members.size(); ++op) + c->addIdOperand(members[op]); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + if (typeClass == OpTypeStruct) + groupedStructConstants[typeId].push_back(c); + else + groupedConstants[typeClass].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name) +{ + Instruction* entryPoint = new Instruction(OpEntryPoint); + entryPoint->addImmediateOperand(model); + entryPoint->addIdOperand(function->getId()); + entryPoint->addStringOperand(name); + + entryPoints.push_back(std::unique_ptr(entryPoint)); + + return entryPoint; +} + +// Currently relying on the fact that all 'value' of interest are small non-negative values. +void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3) +{ + Instruction* instr = new Instruction(OpExecutionMode); + instr->addIdOperand(entryPoint->getId()); + instr->addImmediateOperand(mode); + if (value1 >= 0) + instr->addImmediateOperand(value1); + if (value2 >= 0) + instr->addImmediateOperand(value2); + if (value3 >= 0) + instr->addImmediateOperand(value3); + + executionModes.push_back(std::unique_ptr(instr)); +} + +void Builder::addName(Id id, const char* string) +{ + Instruction* name = new Instruction(OpName); + name->addIdOperand(id); + name->addStringOperand(string); + + names.push_back(std::unique_ptr(name)); +} + +void Builder::addMemberName(Id id, int memberNumber, const char* string) +{ + Instruction* name = new Instruction(OpMemberName); + name->addIdOperand(id); + name->addImmediateOperand(memberNumber); + name->addStringOperand(string); + + names.push_back(std::unique_ptr(name)); +} + +void Builder::addDecoration(Id id, Decoration decoration, int num) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorate); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + if (num >= 0) + dec->addImmediateOperand(num); + + decorations.push_back(std::unique_ptr(dec)); +} + +void Builder::addDecoration(Id id, Decoration decoration, const char* s) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorateStringGOOGLE); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + dec->addStringOperand(s); + + decorations.push_back(std::unique_ptr(dec)); +} + +void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorateId); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + dec->addIdOperand(idDecoration); + + decorations.push_back(std::unique_ptr(dec)); +} + +void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpMemberDecorate); + dec->addIdOperand(id); + dec->addImmediateOperand(member); + dec->addImmediateOperand(decoration); + if (num >= 0) + dec->addImmediateOperand(num); + + decorations.push_back(std::unique_ptr(dec)); +} + +void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE); + dec->addIdOperand(id); + dec->addImmediateOperand(member); + dec->addImmediateOperand(decoration); + dec->addStringOperand(s); + + decorations.push_back(std::unique_ptr(dec)); +} + +// Comments in header +Function* Builder::makeEntryPoint(const char* entryPoint) +{ + assert(! entryPointFunction); + + Block* entry; + std::vector params; + std::vector> decorations; + + entryPointFunction = makeFunctionEntry(NoPrecision, makeVoidType(), entryPoint, params, decorations, &entry); + + return entryPointFunction; +} + +// Comments in header +Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, + const std::vector& paramTypes, + const std::vector>& decorations, Block **entry) +{ + // Make the function and initial instructions in it + Id typeId = makeFunctionType(returnType, paramTypes); + Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size()); + Function* function = new Function(getUniqueId(), returnType, typeId, firstParamId, module); + + // Set up the precisions + setPrecision(function->getId(), precision); + for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) { + for (int d = 0; d < (int)decorations[p].size(); ++d) + addDecoration(firstParamId + p, decorations[p][d]); + } + + // CFG + if (entry) { + *entry = new Block(getUniqueId(), *function); + function->addBlock(*entry); + setBuildPoint(*entry); + } + + if (name) + addName(function->getId(), name); + + functions.push_back(std::unique_ptr(function)); + + return function; +} + +// Comments in header +void Builder::makeReturn(bool implicit, Id retVal) +{ + if (retVal) { + Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue); + inst->addIdOperand(retVal); + buildPoint->addInstruction(std::unique_ptr(inst)); + } else + buildPoint->addInstruction(std::unique_ptr(new Instruction(NoResult, NoType, OpReturn))); + + if (! implicit) + createAndSetNoPredecessorBlock("post-return"); +} + +// Comments in header +void Builder::leaveFunction() +{ + Block* block = buildPoint; + Function& function = buildPoint->getParent(); + assert(block); + + // If our function did not contain a return, add a return void now. + if (! block->isTerminated()) { + if (function.getReturnType() == makeVoidType()) + makeReturn(true); + else { + makeReturn(true, createUndefined(function.getReturnType())); + } + } +} + +// Comments in header +void Builder::makeDiscard() +{ + buildPoint->addInstruction(std::unique_ptr(new Instruction(OpKill))); + createAndSetNoPredecessorBlock("post-discard"); +} + +// Comments in header +Id Builder::createVariable(StorageClass storageClass, Id type, const char* name, Id initializer) +{ + Id pointerType = makePointer(storageClass, type); + Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable); + inst->addImmediateOperand(storageClass); + if (initializer != NoResult) + inst->addIdOperand(initializer); + + switch (storageClass) { + case StorageClassFunction: + // Validation rules require the declaration in the entry block + buildPoint->getParent().addLocalVariable(std::unique_ptr(inst)); + break; + + default: + constantsTypesGlobals.push_back(std::unique_ptr(inst)); + module.mapInstruction(inst); + break; + } + + if (name) + addName(inst->getResultId(), name); + + return inst->getResultId(); +} + +// Comments in header +Id Builder::createUndefined(Id type) +{ + Instruction* inst = new Instruction(getUniqueId(), type, OpUndef); + buildPoint->addInstruction(std::unique_ptr(inst)); + return inst->getResultId(); +} + +// av/vis/nonprivate are unnecessary and illegal for some storage classes. +spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) + const +{ + switch (sc) { + case spv::StorageClassUniform: + case spv::StorageClassWorkgroup: + case spv::StorageClassStorageBuffer: + case spv::StorageClassPhysicalStorageBufferEXT: + break; + default: + memoryAccess = spv::MemoryAccessMask(memoryAccess & + ~(spv::MemoryAccessMakePointerAvailableKHRMask | + spv::MemoryAccessMakePointerVisibleKHRMask | + spv::MemoryAccessNonPrivatePointerKHRMask)); + break; + } + return memoryAccess; +} + +// Comments in header +void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, + unsigned int alignment) +{ + Instruction* store = new Instruction(OpStore); + store->addIdOperand(lValue); + store->addIdOperand(rValue); + + memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); + + if (memoryAccess != MemoryAccessMaskNone) { + store->addImmediateOperand(memoryAccess); + if (memoryAccess & spv::MemoryAccessAlignedMask) { + store->addImmediateOperand(alignment); + } + if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) { + store->addIdOperand(makeUintConstant(scope)); + } + } + + buildPoint->addInstruction(std::unique_ptr(store)); +} + +// Comments in header +Id Builder::createLoad(Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) +{ + Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad); + load->addIdOperand(lValue); + + memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); + + if (memoryAccess != MemoryAccessMaskNone) { + load->addImmediateOperand(memoryAccess); + if (memoryAccess & spv::MemoryAccessAlignedMask) { + load->addImmediateOperand(alignment); + } + if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) { + load->addIdOperand(makeUintConstant(scope)); + } + } + + buildPoint->addInstruction(std::unique_ptr(load)); + + return load->getResultId(); +} + +// Comments in header +Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector& offsets) +{ + // Figure out the final resulting type. + spv::Id typeId = getTypeId(base); + assert(isPointerType(typeId) && offsets.size() > 0); + typeId = getContainedTypeId(typeId); + for (int i = 0; i < (int)offsets.size(); ++i) { + if (isStructType(typeId)) { + assert(isConstantScalar(offsets[i])); + typeId = getContainedTypeId(typeId, getConstantScalar(offsets[i])); + } else + typeId = getContainedTypeId(typeId, offsets[i]); + } + typeId = makePointer(storageClass, typeId); + + // Make the instruction + Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain); + chain->addIdOperand(base); + for (int i = 0; i < (int)offsets.size(); ++i) + chain->addIdOperand(offsets[i]); + buildPoint->addInstruction(std::unique_ptr(chain)); + + return chain->getResultId(); +} + +Id Builder::createArrayLength(Id base, unsigned int member) +{ + spv::Id intType = makeUintType(32); + Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength); + length->addIdOperand(base); + length->addImmediateOperand(member); + buildPoint->addInstruction(std::unique_ptr(length)); + + return length->getResultId(); +} + +Id Builder::createCooperativeMatrixLength(Id type) +{ + spv::Id intType = makeUintType(32); + + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(OpCooperativeMatrixLengthNV, intType, std::vector(1, type), std::vector()); + } + + Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthNV); + length->addIdOperand(type); + buildPoint->addInstruction(std::unique_ptr(length)); + + return length->getResultId(); +} + +Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(OpCompositeExtract, typeId, std::vector(1, composite), + std::vector(1, index)); + } + Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); + extract->addIdOperand(composite); + extract->addImmediateOperand(index); + buildPoint->addInstruction(std::unique_ptr(extract)); + + return extract->getResultId(); +} + +Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector& indexes) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(OpCompositeExtract, typeId, std::vector(1, composite), indexes); + } + Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); + extract->addIdOperand(composite); + for (int i = 0; i < (int)indexes.size(); ++i) + extract->addImmediateOperand(indexes[i]); + buildPoint->addInstruction(std::unique_ptr(extract)); + + return extract->getResultId(); +} + +Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index) +{ + Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); + insert->addIdOperand(object); + insert->addIdOperand(composite); + insert->addImmediateOperand(index); + buildPoint->addInstruction(std::unique_ptr(insert)); + + return insert->getResultId(); +} + +Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector& indexes) +{ + Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); + insert->addIdOperand(object); + insert->addIdOperand(composite); + for (int i = 0; i < (int)indexes.size(); ++i) + insert->addImmediateOperand(indexes[i]); + buildPoint->addInstruction(std::unique_ptr(insert)); + + return insert->getResultId(); +} + +Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex) +{ + Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic); + extract->addIdOperand(vector); + extract->addIdOperand(componentIndex); + buildPoint->addInstruction(std::unique_ptr(extract)); + + return extract->getResultId(); +} + +Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex) +{ + Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic); + insert->addIdOperand(vector); + insert->addIdOperand(component); + insert->addIdOperand(componentIndex); + buildPoint->addInstruction(std::unique_ptr(insert)); + + return insert->getResultId(); +} + +// An opcode that has no operands, no result id, and no type +void Builder::createNoResultOp(Op opCode) +{ + Instruction* op = new Instruction(opCode); + buildPoint->addInstruction(std::unique_ptr(op)); +} + +// An opcode that has one id operand, no result id, and no type +void Builder::createNoResultOp(Op opCode, Id operand) +{ + Instruction* op = new Instruction(opCode); + op->addIdOperand(operand); + buildPoint->addInstruction(std::unique_ptr(op)); +} + +// An opcode that has one or more operands, no result id, and no type +void Builder::createNoResultOp(Op opCode, const std::vector& operands) +{ + Instruction* op = new Instruction(opCode); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) { + op->addIdOperand(*it); + } + buildPoint->addInstruction(std::unique_ptr(op)); +} + +// An opcode that has multiple operands, no result id, and no type +void Builder::createNoResultOp(Op opCode, const std::vector& operands) +{ + Instruction* op = new Instruction(opCode); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) { + if (it->isId) + op->addIdOperand(it->word); + else + op->addImmediateOperand(it->word); + } + buildPoint->addInstruction(std::unique_ptr(op)); +} + +void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics) +{ + Instruction* op = new Instruction(OpControlBarrier); + op->addIdOperand(makeUintConstant(execution)); + op->addIdOperand(makeUintConstant(memory)); + op->addIdOperand(makeUintConstant(semantics)); + buildPoint->addInstruction(std::unique_ptr(op)); +} + +void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics) +{ + Instruction* op = new Instruction(OpMemoryBarrier); + op->addIdOperand(makeUintConstant(executionScope)); + op->addIdOperand(makeUintConstant(memorySemantics)); + buildPoint->addInstruction(std::unique_ptr(op)); +} + +// An opcode that has one operands, a result id, and a type +Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(opCode, typeId, std::vector(1, operand), std::vector()); + } + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + op->addIdOperand(operand); + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + std::vector operands(2); + operands[0] = left; operands[1] = right; + return createSpecConstantOp(opCode, typeId, operands, std::vector()); + } + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + op->addIdOperand(left); + op->addIdOperand(right); + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + std::vector operands(3); + operands[0] = op1; + operands[1] = op2; + operands[2] = op3; + return createSpecConstantOp( + opCode, typeId, operands, std::vector()); + } + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + op->addIdOperand(op1); + op->addIdOperand(op2); + op->addIdOperand(op3); + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createOp(Op opCode, Id typeId, const std::vector& operands) +{ + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) + op->addIdOperand(*it); + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createOp(Op opCode, Id typeId, const std::vector& operands) +{ + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) { + if (it->isId) + op->addIdOperand(it->word); + else + op->addImmediateOperand(it->word); + } + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector& operands, + const std::vector& literals) +{ + Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp); + op->addImmediateOperand((unsigned) opCode); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) + op->addIdOperand(*it); + for (auto it = literals.cbegin(); it != literals.cend(); ++it) + op->addImmediateOperand(*it); + module.mapInstruction(op); + constantsTypesGlobals.push_back(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createFunctionCall(spv::Function* function, const std::vector& args) +{ + Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall); + op->addIdOperand(function->getId()); + for (int a = 0; a < (int)args.size(); ++a) + op->addIdOperand(args[a]); + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +// Comments in header +Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector& channels) +{ + if (channels.size() == 1) + return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision); + + if (generatingOpCodeForSpecConst) { + std::vector operands(2); + operands[0] = operands[1] = source; + return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision); + } + Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); + assert(isVector(source)); + swizzle->addIdOperand(source); + swizzle->addIdOperand(source); + for (int i = 0; i < (int)channels.size(); ++i) + swizzle->addImmediateOperand(channels[i]); + buildPoint->addInstruction(std::unique_ptr(swizzle)); + + return setPrecision(swizzle->getResultId(), precision); +} + +// Comments in header +Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector& channels) +{ + if (channels.size() == 1 && getNumComponents(source) == 1) + return createCompositeInsert(source, target, typeId, channels.front()); + + Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); + + assert(isVector(target)); + swizzle->addIdOperand(target); + + assert(getNumComponents(source) == (int)channels.size()); + assert(isVector(source)); + swizzle->addIdOperand(source); + + // Set up an identity shuffle from the base value to the result value + unsigned int components[4]; + int numTargetComponents = getNumComponents(target); + for (int i = 0; i < numTargetComponents; ++i) + components[i] = i; + + // Punch in the l-value swizzle + for (int i = 0; i < (int)channels.size(); ++i) + components[channels[i]] = numTargetComponents + i; + + // finish the instruction with these components selectors + for (int i = 0; i < numTargetComponents; ++i) + swizzle->addImmediateOperand(components[i]); + buildPoint->addInstruction(std::unique_ptr(swizzle)); + + return swizzle->getResultId(); +} + +// Comments in header +void Builder::promoteScalar(Decoration precision, Id& left, Id& right) +{ + int direction = getNumComponents(right) - getNumComponents(left); + + if (direction > 0) + left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right))); + else if (direction < 0) + right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left))); + + return; +} + +// Comments in header +Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType) +{ + assert(getNumComponents(scalar) == 1); + assert(getTypeId(scalar) == getScalarTypeId(vectorType)); + + int numComponents = getNumTypeComponents(vectorType); + if (numComponents == 1) + return scalar; + + Instruction* smear = nullptr; + if (generatingOpCodeForSpecConst) { + auto members = std::vector(numComponents, scalar); + // Sometime even in spec-constant-op mode, the temporary vector created by + // promoting a scalar might not be a spec constant. This should depend on + // the scalar. + // e.g.: + // const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar; + // In such cases, the temporary vector created from a_front_end_const_scalar + // is not a spec constant vector, even though the binary operation node is marked + // as 'specConstant' and we are in spec-constant-op mode. + auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar)); + smear = module.getInstruction(result_id); + } else { + smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct); + for (int c = 0; c < numComponents; ++c) + smear->addIdOperand(scalar); + buildPoint->addInstruction(std::unique_ptr(smear)); + } + + return setPrecision(smear->getResultId(), precision); +} + +// Comments in header +Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector& args) +{ + Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst); + inst->addIdOperand(builtins); + inst->addImmediateOperand(entryPoint); + for (int arg = 0; arg < (int)args.size(); ++arg) + inst->addIdOperand(args[arg]); + + buildPoint->addInstruction(std::unique_ptr(inst)); + + return inst->getResultId(); +} + +// Accept all parameters needed to create a texture instruction. +// Create the correct instruction based on the inputs, and make the call. +Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, + bool noImplicitLod, const TextureParameters& parameters, ImageOperandsMask signExtensionMask) +{ + static const int maxTextureArgs = 10; + Id texArgs[maxTextureArgs] = {}; + + // + // Set up the fixed arguments + // + int numArgs = 0; + bool explicitLod = false; + texArgs[numArgs++] = parameters.sampler; + texArgs[numArgs++] = parameters.coords; + if (parameters.Dref != NoResult) + texArgs[numArgs++] = parameters.Dref; + if (parameters.component != NoResult) + texArgs[numArgs++] = parameters.component; + +#ifndef GLSLANG_WEB + if (parameters.granularity != NoResult) + texArgs[numArgs++] = parameters.granularity; + if (parameters.coarse != NoResult) + texArgs[numArgs++] = parameters.coarse; +#endif + + // + // Set up the optional arguments + // + int optArgNum = numArgs; // track which operand, if it exists, is the mask of optional arguments + ++numArgs; // speculatively make room for the mask operand + ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand + if (parameters.bias) { + mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask); + texArgs[numArgs++] = parameters.bias; + } + if (parameters.lod) { + mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); + texArgs[numArgs++] = parameters.lod; + explicitLod = true; + } else if (parameters.gradX) { + mask = (ImageOperandsMask)(mask | ImageOperandsGradMask); + texArgs[numArgs++] = parameters.gradX; + texArgs[numArgs++] = parameters.gradY; + explicitLod = true; + } else if (noImplicitLod && ! fetch && ! gather) { + // have to explicitly use lod of 0 if not allowed to have them be implicit, and + // we would otherwise be about to issue an implicit instruction + mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); + texArgs[numArgs++] = makeFloatConstant(0.0); + explicitLod = true; + } + if (parameters.offset) { + if (isConstant(parameters.offset)) + mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask); + else { + addCapability(CapabilityImageGatherExtended); + mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask); + } + texArgs[numArgs++] = parameters.offset; + } + if (parameters.offsets) { + addCapability(CapabilityImageGatherExtended); + mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask); + texArgs[numArgs++] = parameters.offsets; + } +#ifndef GLSLANG_WEB + if (parameters.sample) { + mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask); + texArgs[numArgs++] = parameters.sample; + } + if (parameters.lodClamp) { + // capability if this bit is used + addCapability(CapabilityMinLod); + + mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask); + texArgs[numArgs++] = parameters.lodClamp; + } + if (parameters.nonprivate) { + mask = mask | ImageOperandsNonPrivateTexelKHRMask; + } + if (parameters.volatil) { + mask = mask | ImageOperandsVolatileTexelKHRMask; + } +#endif + mask = mask | signExtensionMask; + if (mask == ImageOperandsMaskNone) + --numArgs; // undo speculative reservation for the mask argument + else + texArgs[optArgNum] = mask; + + // + // Set up the instruction + // + Op opCode = OpNop; // All paths below need to set this + if (fetch) { + if (sparse) + opCode = OpImageSparseFetch; + else + opCode = OpImageFetch; +#ifndef GLSLANG_WEB + } else if (parameters.granularity && parameters.coarse) { + opCode = OpImageSampleFootprintNV; + } else if (gather) { + if (parameters.Dref) + if (sparse) + opCode = OpImageSparseDrefGather; + else + opCode = OpImageDrefGather; + else + if (sparse) + opCode = OpImageSparseGather; + else + opCode = OpImageGather; +#endif + } else if (explicitLod) { + if (parameters.Dref) { + if (proj) + if (sparse) + opCode = OpImageSparseSampleProjDrefExplicitLod; + else + opCode = OpImageSampleProjDrefExplicitLod; + else + if (sparse) + opCode = OpImageSparseSampleDrefExplicitLod; + else + opCode = OpImageSampleDrefExplicitLod; + } else { + if (proj) + if (sparse) + opCode = OpImageSparseSampleProjExplicitLod; + else + opCode = OpImageSampleProjExplicitLod; + else + if (sparse) + opCode = OpImageSparseSampleExplicitLod; + else + opCode = OpImageSampleExplicitLod; + } + } else { + if (parameters.Dref) { + if (proj) + if (sparse) + opCode = OpImageSparseSampleProjDrefImplicitLod; + else + opCode = OpImageSampleProjDrefImplicitLod; + else + if (sparse) + opCode = OpImageSparseSampleDrefImplicitLod; + else + opCode = OpImageSampleDrefImplicitLod; + } else { + if (proj) + if (sparse) + opCode = OpImageSparseSampleProjImplicitLod; + else + opCode = OpImageSampleProjImplicitLod; + else + if (sparse) + opCode = OpImageSparseSampleImplicitLod; + else + opCode = OpImageSampleImplicitLod; + } + } + + // See if the result type is expecting a smeared result. + // This happens when a legacy shadow*() call is made, which + // gets a vec4 back instead of a float. + Id smearedType = resultType; + if (! isScalarType(resultType)) { + switch (opCode) { + case OpImageSampleDrefImplicitLod: + case OpImageSampleDrefExplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageSampleProjDrefExplicitLod: + resultType = getScalarTypeId(resultType); + break; + default: + break; + } + } + + Id typeId0 = 0; + Id typeId1 = 0; + + if (sparse) { + typeId0 = resultType; + typeId1 = getDerefTypeId(parameters.texelOut); + resultType = makeStructResultType(typeId0, typeId1); + } + + // Build the SPIR-V instruction + Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode); + for (int op = 0; op < optArgNum; ++op) + textureInst->addIdOperand(texArgs[op]); + if (optArgNum < numArgs) + textureInst->addImmediateOperand(texArgs[optArgNum]); + for (int op = optArgNum + 1; op < numArgs; ++op) + textureInst->addIdOperand(texArgs[op]); + setPrecision(textureInst->getResultId(), precision); + buildPoint->addInstruction(std::unique_ptr(textureInst)); + + Id resultId = textureInst->getResultId(); + + if (sparse) { + // set capability + addCapability(CapabilitySparseResidency); + + // Decode the return type that was a special structure + createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut); + resultId = createCompositeExtract(resultId, typeId0, 0); + setPrecision(resultId, precision); + } else { + // When a smear is needed, do it, as per what was computed + // above when resultType was changed to a scalar type. + if (resultType != smearedType) + resultId = smearScalar(precision, resultId, smearedType); + } + + return resultId; +} + +// Comments in header +Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult) +{ + // Figure out the result type + Id resultType = 0; + switch (opCode) { + case OpImageQuerySize: + case OpImageQuerySizeLod: + { + int numComponents = 0; + switch (getTypeDimensionality(getImageType(parameters.sampler))) { + case Dim1D: + case DimBuffer: + numComponents = 1; + break; + case Dim2D: + case DimCube: + case DimRect: + case DimSubpassData: + numComponents = 2; + break; + case Dim3D: + numComponents = 3; + break; + + default: + assert(0); + break; + } + if (isArrayedImageType(getImageType(parameters.sampler))) + ++numComponents; + + Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32); + if (numComponents == 1) + resultType = intType; + else + resultType = makeVectorType(intType, numComponents); + + break; + } + case OpImageQueryLod: + resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2); + break; + case OpImageQueryLevels: + case OpImageQuerySamples: + resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32); + break; + default: + assert(0); + break; + } + + Instruction* query = new Instruction(getUniqueId(), resultType, opCode); + query->addIdOperand(parameters.sampler); + if (parameters.coords) + query->addIdOperand(parameters.coords); + if (parameters.lod) + query->addIdOperand(parameters.lod); + buildPoint->addInstruction(std::unique_ptr(query)); + addCapability(CapabilityImageQuery); + + return query->getResultId(); +} + +// External comments in header. +// Operates recursively to visit the composite's hierarchy. +Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal) +{ + Id boolType = makeBoolType(); + Id valueType = getTypeId(value1); + + Id resultId = NoResult; + + int numConstituents = getNumTypeConstituents(valueType); + + // Scalars and Vectors + + if (isScalarType(valueType) || isVectorType(valueType)) { + assert(valueType == getTypeId(value2)); + // These just need a single comparison, just have + // to figure out what it is. + Op op; + switch (getMostBasicTypeClass(valueType)) { + case OpTypeFloat: + op = equal ? OpFOrdEqual : OpFOrdNotEqual; + break; + case OpTypeInt: + default: + op = equal ? OpIEqual : OpINotEqual; + break; + case OpTypeBool: + op = equal ? OpLogicalEqual : OpLogicalNotEqual; + precision = NoPrecision; + break; + } + + if (isScalarType(valueType)) { + // scalar + resultId = createBinOp(op, boolType, value1, value2); + } else { + // vector + resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2); + setPrecision(resultId, precision); + // reduce vector compares... + resultId = createUnaryOp(equal ? OpAll : OpAny, boolType, resultId); + } + + return setPrecision(resultId, precision); + } + + // Only structs, arrays, and matrices should be left. + // They share in common the reduction operation across their constituents. + assert(isAggregateType(valueType) || isMatrixType(valueType)); + + // Compare each pair of constituents + for (int constituent = 0; constituent < numConstituents; ++constituent) { + std::vector indexes(1, constituent); + Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent); + Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent); + Id constituent1 = createCompositeExtract(value1, constituentType1, indexes); + Id constituent2 = createCompositeExtract(value2, constituentType2, indexes); + + Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal); + + if (constituent == 0) + resultId = subResultId; + else + resultId = setPrecision(createBinOp(equal ? OpLogicalAnd : OpLogicalOr, boolType, resultId, subResultId), + precision); + } + + return resultId; +} + +// OpCompositeConstruct +Id Builder::createCompositeConstruct(Id typeId, const std::vector& constituents) +{ + assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && + getNumTypeConstituents(typeId) == (int)constituents.size())); + + if (generatingOpCodeForSpecConst) { + // Sometime, even in spec-constant-op mode, the constant composite to be + // constructed may not be a specialization constant. + // e.g.: + // const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const); + // The first column vector should be a spec constant one, as a_spec_const is a spec constant. + // The second column vector should NOT be spec constant, as it does not contain any spec constants. + // To handle such cases, we check the constituents of the constant vector to determine whether this + // vector should be created as a spec constant. + return makeCompositeConstant(typeId, constituents, + std::any_of(constituents.begin(), constituents.end(), + [&](spv::Id id) { return isSpecConstant(id); })); + } + + Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct); + for (int c = 0; c < (int)constituents.size(); ++c) + op->addIdOperand(constituents[c]); + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +// Vector or scalar constructor +Id Builder::createConstructor(Decoration precision, const std::vector& sources, Id resultTypeId) +{ + Id result = NoResult; + unsigned int numTargetComponents = getNumTypeComponents(resultTypeId); + unsigned int targetComponent = 0; + + // Special case: when calling a vector constructor with a single scalar + // argument, smear the scalar + if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1) + return smearScalar(precision, sources[0], resultTypeId); + + // accumulate the arguments for OpCompositeConstruct + std::vector constituents; + Id scalarTypeId = getScalarTypeId(resultTypeId); + + // lambda to store the result of visiting an argument component + const auto latchResult = [&](Id comp) { + if (numTargetComponents > 1) + constituents.push_back(comp); + else + result = comp; + ++targetComponent; + }; + + // lambda to visit a vector argument's components + const auto accumulateVectorConstituents = [&](Id sourceArg) { + unsigned int sourceSize = getNumComponents(sourceArg); + unsigned int sourcesToUse = sourceSize; + if (sourcesToUse + targetComponent > numTargetComponents) + sourcesToUse = numTargetComponents - targetComponent; + + for (unsigned int s = 0; s < sourcesToUse; ++s) { + std::vector swiz; + swiz.push_back(s); + latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz)); + } + }; + + // lambda to visit a matrix argument's components + const auto accumulateMatrixConstituents = [&](Id sourceArg) { + unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg); + unsigned int sourcesToUse = sourceSize; + if (sourcesToUse + targetComponent > numTargetComponents) + sourcesToUse = numTargetComponents - targetComponent; + + int col = 0; + int row = 0; + for (unsigned int s = 0; s < sourcesToUse; ++s) { + if (row >= getNumRows(sourceArg)) { + row = 0; + col++; + } + std::vector indexes; + indexes.push_back(col); + indexes.push_back(row); + latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes)); + row++; + } + }; + + // Go through the source arguments, each one could have either + // a single or multiple components to contribute. + for (unsigned int i = 0; i < sources.size(); ++i) { + + if (isScalar(sources[i]) || isPointer(sources[i])) + latchResult(sources[i]); + else if (isVector(sources[i])) + accumulateVectorConstituents(sources[i]); + else if (isMatrix(sources[i])) + accumulateMatrixConstituents(sources[i]); + else + assert(0); + + if (targetComponent >= numTargetComponents) + break; + } + + // If the result is a vector, make it from the gathered constituents. + if (constituents.size() > 0) + result = createCompositeConstruct(resultTypeId, constituents); + + return setPrecision(result, precision); +} + +// Comments in header +Id Builder::createMatrixConstructor(Decoration precision, const std::vector& sources, Id resultTypeId) +{ + Id componentTypeId = getScalarTypeId(resultTypeId); + int numCols = getTypeNumColumns(resultTypeId); + int numRows = getTypeNumRows(resultTypeId); + + Instruction* instr = module.getInstruction(componentTypeId); +#ifdef GLSLANG_WEB + const unsigned bitCount = 32; + assert(bitCount == instr->getImmediateOperand(0)); +#else + const unsigned bitCount = instr->getImmediateOperand(0); +#endif + + // Optimize matrix constructed from a bigger matrix + if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) { + // To truncate the matrix to a smaller number of rows/columns, we need to: + // 1. For each column, extract the column and truncate it to the required size using shuffle + // 2. Assemble the resulting matrix from all columns + Id matrix = sources[0]; + Id columnTypeId = getContainedTypeId(resultTypeId); + Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix)); + + std::vector channels; + for (int row = 0; row < numRows; ++row) + channels.push_back(row); + + std::vector matrixColumns; + for (int col = 0; col < numCols; ++col) { + std::vector indexes; + indexes.push_back(col); + Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes); + setPrecision(colv, precision); + + if (numRows != getNumRows(matrix)) { + matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels)); + } else { + matrixColumns.push_back(colv); + } + } + + return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); + } + + // Otherwise, will use a two step process + // 1. make a compile-time 2D array of values + // 2. construct a matrix from that array + + // Step 1. + + // initialize the array to the identity matrix + Id ids[maxMatrixSize][maxMatrixSize]; + Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0)); + Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0)); + for (int col = 0; col < 4; ++col) { + for (int row = 0; row < 4; ++row) { + if (col == row) + ids[col][row] = one; + else + ids[col][row] = zero; + } + } + + // modify components as dictated by the arguments + if (sources.size() == 1 && isScalar(sources[0])) { + // a single scalar; resets the diagonals + for (int col = 0; col < 4; ++col) + ids[col][col] = sources[0]; + } else if (isMatrix(sources[0])) { + // constructing from another matrix; copy over the parts that exist in both the argument and constructee + Id matrix = sources[0]; + int minCols = std::min(numCols, getNumColumns(matrix)); + int minRows = std::min(numRows, getNumRows(matrix)); + for (int col = 0; col < minCols; ++col) { + std::vector indexes; + indexes.push_back(col); + for (int row = 0; row < minRows; ++row) { + indexes.push_back(row); + ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes); + indexes.pop_back(); + setPrecision(ids[col][row], precision); + } + } + } else { + // fill in the matrix in column-major order with whatever argument components are available + int row = 0; + int col = 0; + + for (int arg = 0; arg < (int)sources.size(); ++arg) { + Id argComp = sources[arg]; + for (int comp = 0; comp < getNumComponents(sources[arg]); ++comp) { + if (getNumComponents(sources[arg]) > 1) { + argComp = createCompositeExtract(sources[arg], componentTypeId, comp); + setPrecision(argComp, precision); + } + ids[col][row++] = argComp; + if (row == numRows) { + row = 0; + col++; + } + } + } + } + + // Step 2: Construct a matrix from that array. + // First make the column vectors, then make the matrix. + + // make the column vectors + Id columnTypeId = getContainedTypeId(resultTypeId); + std::vector matrixColumns; + for (int col = 0; col < numCols; ++col) { + std::vector vectorComponents; + for (int row = 0; row < numRows; ++row) + vectorComponents.push_back(ids[col][row]); + Id column = createCompositeConstruct(columnTypeId, vectorComponents); + setPrecision(column, precision); + matrixColumns.push_back(column); + } + + // make the matrix + return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); +} + +// Comments in header +Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) : + builder(gb), + condition(cond), + control(ctrl), + elseBlock(0) +{ + function = &builder.getBuildPoint()->getParent(); + + // make the blocks, but only put the then-block into the function, + // the else-block and merge-block will be added later, in order, after + // earlier code is emitted + thenBlock = new Block(builder.getUniqueId(), *function); + mergeBlock = new Block(builder.getUniqueId(), *function); + + // Save the current block, so that we can add in the flow control split when + // makeEndIf is called. + headerBlock = builder.getBuildPoint(); + + function->addBlock(thenBlock); + builder.setBuildPoint(thenBlock); +} + +// Comments in header +void Builder::If::makeBeginElse() +{ + // Close out the "then" by having it jump to the mergeBlock + builder.createBranch(mergeBlock); + + // Make the first else block and add it to the function + elseBlock = new Block(builder.getUniqueId(), *function); + function->addBlock(elseBlock); + + // Start building the else block + builder.setBuildPoint(elseBlock); +} + +// Comments in header +void Builder::If::makeEndIf() +{ + // jump to the merge block + builder.createBranch(mergeBlock); + + // Go back to the headerBlock and make the flow control split + builder.setBuildPoint(headerBlock); + builder.createSelectionMerge(mergeBlock, control); + if (elseBlock) + builder.createConditionalBranch(condition, thenBlock, elseBlock); + else + builder.createConditionalBranch(condition, thenBlock, mergeBlock); + + // add the merge block to the function + function->addBlock(mergeBlock); + builder.setBuildPoint(mergeBlock); +} + +// Comments in header +void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector& caseValues, + const std::vector& valueIndexToSegment, int defaultSegment, + std::vector& segmentBlocks) +{ + Function& function = buildPoint->getParent(); + + // make all the blocks + for (int s = 0; s < numSegments; ++s) + segmentBlocks.push_back(new Block(getUniqueId(), function)); + + Block* mergeBlock = new Block(getUniqueId(), function); + + // make and insert the switch's selection-merge instruction + createSelectionMerge(mergeBlock, control); + + // make the switch instruction + Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch); + switchInst->addIdOperand(selector); + auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock; + switchInst->addIdOperand(defaultOrMerge->getId()); + defaultOrMerge->addPredecessor(buildPoint); + for (int i = 0; i < (int)caseValues.size(); ++i) { + switchInst->addImmediateOperand(caseValues[i]); + switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId()); + segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint); + } + buildPoint->addInstruction(std::unique_ptr(switchInst)); + + // push the merge block + switchMerges.push(mergeBlock); +} + +// Comments in header +void Builder::addSwitchBreak() +{ + // branch to the top of the merge block stack + createBranch(switchMerges.top()); + createAndSetNoPredecessorBlock("post-switch-break"); +} + +// Comments in header +void Builder::nextSwitchSegment(std::vector& segmentBlock, int nextSegment) +{ + int lastSegment = nextSegment - 1; + if (lastSegment >= 0) { + // Close out previous segment by jumping, if necessary, to next segment + if (! buildPoint->isTerminated()) + createBranch(segmentBlock[nextSegment]); + } + Block* block = segmentBlock[nextSegment]; + block->getParent().addBlock(block); + setBuildPoint(block); +} + +// Comments in header +void Builder::endSwitch(std::vector& /*segmentBlock*/) +{ + // Close out previous segment by jumping, if necessary, to next segment + if (! buildPoint->isTerminated()) + addSwitchBreak(); + + switchMerges.top()->getParent().addBlock(switchMerges.top()); + setBuildPoint(switchMerges.top()); + + switchMerges.pop(); +} + +Block& Builder::makeNewBlock() +{ + Function& function = buildPoint->getParent(); + auto block = new Block(getUniqueId(), function); + function.addBlock(block); + return *block; +} + +Builder::LoopBlocks& Builder::makeNewLoop() +{ + // This verbosity is needed to simultaneously get the same behavior + // everywhere (id's in the same order), have a syntax that works + // across lots of versions of C++, have no warnings from pedantic + // compilation modes, and leave the rest of the code alone. + Block& head = makeNewBlock(); + Block& body = makeNewBlock(); + Block& merge = makeNewBlock(); + Block& continue_target = makeNewBlock(); + LoopBlocks blocks(head, body, merge, continue_target); + loops.push(blocks); + return loops.top(); +} + +void Builder::createLoopContinue() +{ + createBranch(&loops.top().continue_target); + // Set up a block for dead code. + createAndSetNoPredecessorBlock("post-loop-continue"); +} + +void Builder::createLoopExit() +{ + createBranch(&loops.top().merge); + // Set up a block for dead code. + createAndSetNoPredecessorBlock("post-loop-break"); +} + +void Builder::closeLoop() +{ + loops.pop(); +} + +void Builder::clearAccessChain() +{ + accessChain.base = NoResult; + accessChain.indexChain.clear(); + accessChain.instr = NoResult; + accessChain.swizzle.clear(); + accessChain.component = NoResult; + accessChain.preSwizzleBaseType = NoType; + accessChain.isRValue = false; + accessChain.coherentFlags.clear(); + accessChain.alignment = 0; +} + +// Comments in header +void Builder::accessChainPushSwizzle(std::vector& swizzle, Id preSwizzleBaseType, + AccessChain::CoherentFlags coherentFlags, unsigned int alignment) +{ + accessChain.coherentFlags |= coherentFlags; + accessChain.alignment |= alignment; + + // swizzles can be stacked in GLSL, but simplified to a single + // one here; the base type doesn't change + if (accessChain.preSwizzleBaseType == NoType) + accessChain.preSwizzleBaseType = preSwizzleBaseType; + + // if needed, propagate the swizzle for the current access chain + if (accessChain.swizzle.size() > 0) { + std::vector oldSwizzle = accessChain.swizzle; + accessChain.swizzle.resize(0); + for (unsigned int i = 0; i < swizzle.size(); ++i) { + assert(swizzle[i] < oldSwizzle.size()); + accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]); + } + } else + accessChain.swizzle = swizzle; + + // determine if we need to track this swizzle anymore + simplifyAccessChainSwizzle(); +} + +// Comments in header +void Builder::accessChainStore(Id rvalue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) +{ + assert(accessChain.isRValue == false); + + transferAccessChainSwizzle(true); + Id base = collapseAccessChain(); + Id source = rvalue; + + // dynamic component should be gone + assert(accessChain.component == NoResult); + + // If swizzle still exists, it is out-of-order or not full, we must load the target vector, + // extract and insert elements to perform writeMask and/or swizzle. + if (accessChain.swizzle.size() > 0) { + Id tempBaseId = createLoad(base); + source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle); + } + + // take LSB of alignment + alignment = alignment & ~(alignment & (alignment-1)); + if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) { + memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); + } + + createStore(source, base, memoryAccess, scope, alignment); +} + +// Comments in header +Id Builder::accessChainLoad(Decoration precision, Decoration nonUniform, Id resultType, + spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) +{ + Id id; + + if (accessChain.isRValue) { + // transfer access chain, but try to stay in registers + transferAccessChainSwizzle(false); + if (accessChain.indexChain.size() > 0) { + Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType; + + // if all the accesses are constants, we can use OpCompositeExtract + std::vector indexes; + bool constant = true; + for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) { + if (isConstantScalar(accessChain.indexChain[i])) + indexes.push_back(getConstantScalar(accessChain.indexChain[i])); + else { + constant = false; + break; + } + } + + if (constant) { + id = createCompositeExtract(accessChain.base, swizzleBase, indexes); + } else { + Id lValue = NoResult; + if (spvVersion >= Spv_1_4) { + // make a new function variable for this r-value, using an initializer, + // and mark it as NonWritable so that downstream it can be detected as a lookup + // table + lValue = createVariable(StorageClassFunction, getTypeId(accessChain.base), "indexable", + accessChain.base); + addDecoration(lValue, DecorationNonWritable); + } else { + lValue = createVariable(StorageClassFunction, getTypeId(accessChain.base), "indexable"); + // store into it + createStore(accessChain.base, lValue); + } + // move base to the new variable + accessChain.base = lValue; + accessChain.isRValue = false; + + // load through the access chain + id = createLoad(collapseAccessChain()); + } + setPrecision(id, precision); + } else + id = accessChain.base; // no precision, it was set when this was defined + } else { + transferAccessChainSwizzle(true); + + // take LSB of alignment + alignment = alignment & ~(alignment & (alignment-1)); + if (getStorageClass(accessChain.base) == StorageClassPhysicalStorageBufferEXT) { + memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); + } + + // load through the access chain + id = collapseAccessChain(); + // Apply nonuniform both to the access chain and the loaded value. + // Buffer accesses need the access chain decorated, and this is where + // loaded image types get decorated. TODO: This should maybe move to + // createImageTextureFunctionCall. + addDecoration(id, nonUniform); + id = createLoad(id, memoryAccess, scope, alignment); + setPrecision(id, precision); + addDecoration(id, nonUniform); + } + + // Done, unless there are swizzles to do + if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) + return id; + + // Do remaining swizzling + + // Do the basic swizzle + if (accessChain.swizzle.size() > 0) { + Id swizzledType = getScalarTypeId(getTypeId(id)); + if (accessChain.swizzle.size() > 1) + swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size()); + id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle); + } + + // Do the dynamic component + if (accessChain.component != NoResult) + id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision); + + addDecoration(id, nonUniform); + return id; +} + +Id Builder::accessChainGetLValue() +{ + assert(accessChain.isRValue == false); + + transferAccessChainSwizzle(true); + Id lvalue = collapseAccessChain(); + + // If swizzle exists, it is out-of-order or not full, we must load the target vector, + // extract and insert elements to perform writeMask and/or swizzle. This does not + // go with getting a direct l-value pointer. + assert(accessChain.swizzle.size() == 0); + assert(accessChain.component == NoResult); + + return lvalue; +} + +// comment in header +Id Builder::accessChainGetInferredType() +{ + // anything to operate on? + if (accessChain.base == NoResult) + return NoType; + Id type = getTypeId(accessChain.base); + + // do initial dereference + if (! accessChain.isRValue) + type = getContainedTypeId(type); + + // dereference each index + for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) { + if (isStructType(type)) + type = getContainedTypeId(type, getConstantScalar(*it)); + else + type = getContainedTypeId(type); + } + + // dereference swizzle + if (accessChain.swizzle.size() == 1) + type = getContainedTypeId(type); + else if (accessChain.swizzle.size() > 1) + type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size()); + + // dereference component selection + if (accessChain.component) + type = getContainedTypeId(type); + + return type; +} + +void Builder::dump(std::vector& out) const +{ + // Header, before first instructions: + out.push_back(MagicNumber); + out.push_back(spvVersion); + out.push_back(builderNumber); + out.push_back(uniqueId + 1); + out.push_back(0); + + // Capabilities + for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) { + Instruction capInst(0, 0, OpCapability); + capInst.addImmediateOperand(*it); + capInst.dump(out); + } + + for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) { + Instruction extInst(0, 0, OpExtension); + extInst.addStringOperand(it->c_str()); + extInst.dump(out); + } + + dumpInstructions(out, imports); + Instruction memInst(0, 0, OpMemoryModel); + memInst.addImmediateOperand(addressModel); + memInst.addImmediateOperand(memoryModel); + memInst.dump(out); + + // Instructions saved up while building: + dumpInstructions(out, entryPoints); + dumpInstructions(out, executionModes); + + // Debug instructions + dumpInstructions(out, strings); + dumpSourceInstructions(out); + for (int e = 0; e < (int)sourceExtensions.size(); ++e) { + Instruction sourceExtInst(0, 0, OpSourceExtension); + sourceExtInst.addStringOperand(sourceExtensions[e]); + sourceExtInst.dump(out); + } + dumpInstructions(out, names); + dumpModuleProcesses(out); + + // Annotation instructions + dumpInstructions(out, decorations); + + dumpInstructions(out, constantsTypesGlobals); + dumpInstructions(out, externals); + + // The functions + module.dump(out); +} + +// +// Protected methods. +// + +// Turn the described access chain in 'accessChain' into an instruction(s) +// computing its address. This *cannot* include complex swizzles, which must +// be handled after this is called. +// +// Can generate code. +Id Builder::collapseAccessChain() +{ + assert(accessChain.isRValue == false); + + // did we already emit an access chain for this? + if (accessChain.instr != NoResult) + return accessChain.instr; + + // If we have a dynamic component, we can still transfer + // that into a final operand to the access chain. We need to remap the + // dynamic component through the swizzle to get a new dynamic component to + // update. + // + // This was not done in transferAccessChainSwizzle() because it might + // generate code. + remapDynamicSwizzle(); + if (accessChain.component != NoResult) { + // transfer the dynamic component to the access chain + accessChain.indexChain.push_back(accessChain.component); + accessChain.component = NoResult; + } + + // note that non-trivial swizzling is left pending + + // do we have an access chain? + if (accessChain.indexChain.size() == 0) + return accessChain.base; + + // emit the access chain + StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base)); + accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain); + + return accessChain.instr; +} + +// For a dynamic component selection of a swizzle. +// +// Turn the swizzle and dynamic component into just a dynamic component. +// +// Generates code. +void Builder::remapDynamicSwizzle() +{ + // do we have a swizzle to remap a dynamic component through? + if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) { + // build a vector of the swizzle for the component to map into + std::vector components; + for (int c = 0; c < (int)accessChain.swizzle.size(); ++c) + components.push_back(makeUintConstant(accessChain.swizzle[c])); + Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size()); + Id map = makeCompositeConstant(mapType, components); + + // use it + accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component); + accessChain.swizzle.clear(); + } +} + +// clear out swizzle if it is redundant, that is reselecting the same components +// that would be present without the swizzle. +void Builder::simplifyAccessChainSwizzle() +{ + // If the swizzle has fewer components than the vector, it is subsetting, and must stay + // to preserve that fact. + if (getNumTypeComponents(accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size()) + return; + + // if components are out of order, it is a swizzle + for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) { + if (i != accessChain.swizzle[i]) + return; + } + + // otherwise, there is no need to track this swizzle + accessChain.swizzle.clear(); + if (accessChain.component == NoResult) + accessChain.preSwizzleBaseType = NoType; +} + +// To the extent any swizzling can become part of the chain +// of accesses instead of a post operation, make it so. +// If 'dynamic' is true, include transferring the dynamic component, +// otherwise, leave it pending. +// +// Does not generate code. just updates the access chain. +void Builder::transferAccessChainSwizzle(bool dynamic) +{ + // non existent? + if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) + return; + + // too complex? + // (this requires either a swizzle, or generating code for a dynamic component) + if (accessChain.swizzle.size() > 1) + return; + + // single component, either in the swizzle and/or dynamic component + if (accessChain.swizzle.size() == 1) { + assert(accessChain.component == NoResult); + // handle static component selection + accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front())); + accessChain.swizzle.clear(); + accessChain.preSwizzleBaseType = NoType; + } else if (dynamic && accessChain.component != NoResult) { + assert(accessChain.swizzle.size() == 0); + // handle dynamic component + accessChain.indexChain.push_back(accessChain.component); + accessChain.preSwizzleBaseType = NoType; + accessChain.component = NoResult; + } +} + +// Utility method for creating a new block and setting the insert point to +// be in it. This is useful for flow-control operations that need a "dummy" +// block proceeding them (e.g. instructions after a discard, etc). +void Builder::createAndSetNoPredecessorBlock(const char* /*name*/) +{ + Block* block = new Block(getUniqueId(), buildPoint->getParent()); + block->setUnreachable(); + buildPoint->getParent().addBlock(block); + setBuildPoint(block); + + // if (name) + // addName(block->getId(), name); +} + +// Comments in header +void Builder::createBranch(Block* block) +{ + Instruction* branch = new Instruction(OpBranch); + branch->addIdOperand(block->getId()); + buildPoint->addInstruction(std::unique_ptr(branch)); + block->addPredecessor(buildPoint); +} + +void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control) +{ + Instruction* merge = new Instruction(OpSelectionMerge); + merge->addIdOperand(mergeBlock->getId()); + merge->addImmediateOperand(control); + buildPoint->addInstruction(std::unique_ptr(merge)); +} + +void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, + const std::vector& operands) +{ + Instruction* merge = new Instruction(OpLoopMerge); + merge->addIdOperand(mergeBlock->getId()); + merge->addIdOperand(continueBlock->getId()); + merge->addImmediateOperand(control); + for (int op = 0; op < (int)operands.size(); ++op) + merge->addImmediateOperand(operands[op]); + buildPoint->addInstruction(std::unique_ptr(merge)); +} + +void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock) +{ + Instruction* branch = new Instruction(OpBranchConditional); + branch->addIdOperand(condition); + branch->addIdOperand(thenBlock->getId()); + branch->addIdOperand(elseBlock->getId()); + buildPoint->addInstruction(std::unique_ptr(branch)); + thenBlock->addPredecessor(buildPoint); + elseBlock->addPredecessor(buildPoint); +} + +// OpSource +// [OpSourceContinued] +// ... +void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text, + std::vector& out) const +{ + const int maxWordCount = 0xFFFF; + const int opSourceWordCount = 4; + const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1; + + if (source != SourceLanguageUnknown) { + // OpSource Language Version File Source + Instruction sourceInst(NoResult, NoType, OpSource); + sourceInst.addImmediateOperand(source); + sourceInst.addImmediateOperand(sourceVersion); + // File operand + if (fileId != NoResult) { + sourceInst.addIdOperand(fileId); + // Source operand + if (text.size() > 0) { + int nextByte = 0; + std::string subString; + while ((int)text.size() - nextByte > 0) { + subString = text.substr(nextByte, nonNullBytesPerInstruction); + if (nextByte == 0) { + // OpSource + sourceInst.addStringOperand(subString.c_str()); + sourceInst.dump(out); + } else { + // OpSourcContinued + Instruction sourceContinuedInst(OpSourceContinued); + sourceContinuedInst.addStringOperand(subString.c_str()); + sourceContinuedInst.dump(out); + } + nextByte += nonNullBytesPerInstruction; + } + } else + sourceInst.dump(out); + } else + sourceInst.dump(out); + } +} + +// Dump an OpSource[Continued] sequence for the source and every include file +void Builder::dumpSourceInstructions(std::vector& out) const +{ + dumpSourceInstructions(sourceFileStringId, sourceText, out); + for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr) + dumpSourceInstructions(iItr->first, *iItr->second, out); +} + +void Builder::dumpInstructions(std::vector& out, + const std::vector >& instructions) const +{ + for (int i = 0; i < (int)instructions.size(); ++i) { + instructions[i]->dump(out); + } +} + +void Builder::dumpModuleProcesses(std::vector& out) const +{ + for (int i = 0; i < (int)moduleProcesses.size(); ++i) { + Instruction moduleProcessed(OpModuleProcessed); + moduleProcessed.addStringOperand(moduleProcesses[i]); + moduleProcessed.dump(out); + } +} + +}; // end spv namespace diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/SpvBuilder.h b/armorcore/tools/to_spirv/glslang/SPIRV/SpvBuilder.h new file mode 100644 index 000000000..2b6d6c13f --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/SpvBuilder.h @@ -0,0 +1,838 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// "Builder" is an interface to fully build SPIR-V IR. Allocate one of +// these to build (a thread safe) internal SPIR-V representation (IR), +// and then dump it as a binary stream according to the SPIR-V specification. +// +// A Builder has a 1:1 relationship with a SPIR-V module. +// + +#pragma once +#ifndef SpvBuilder_H +#define SpvBuilder_H + +#include "Logger.h" +#include "spirv.hpp" +#include "spvIR.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace spv { + +typedef enum { + Spv_1_0 = (1 << 16), + Spv_1_1 = (1 << 16) | (1 << 8), + Spv_1_2 = (1 << 16) | (2 << 8), + Spv_1_3 = (1 << 16) | (3 << 8), + Spv_1_4 = (1 << 16) | (4 << 8), + Spv_1_5 = (1 << 16) | (5 << 8), +} SpvVersion; + +class Builder { +public: + Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger); + virtual ~Builder(); + + static const int maxMatrixSize = 4; + + unsigned int getSpvVersion() const { return spvVersion; } + + void setSource(spv::SourceLanguage lang, int version) + { + source = lang; + sourceVersion = version; + } + spv::Id getStringId(const std::string& str) + { + auto sItr = stringIds.find(str); + if (sItr != stringIds.end()) + return sItr->second; + spv::Id strId = getUniqueId(); + Instruction* fileString = new Instruction(strId, NoType, OpString); + const char* file_c_str = str.c_str(); + fileString->addStringOperand(file_c_str); + strings.push_back(std::unique_ptr(fileString)); + module.mapInstruction(fileString); + stringIds[file_c_str] = strId; + return strId; + } + void setSourceFile(const std::string& file) + { + sourceFileStringId = getStringId(file); + } + void setSourceText(const std::string& text) { sourceText = text; } + void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); } + void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); } + void setEmitOpLines() { emitOpLines = true; } + void addExtension(const char* ext) { extensions.insert(ext); } + void removeExtension(const char* ext) + { + extensions.erase(ext); + } + void addIncorporatedExtension(const char* ext, SpvVersion incorporatedVersion) + { + if (getSpvVersion() < static_cast(incorporatedVersion)) + addExtension(ext); + } + void promoteIncorporatedExtension(const char* baseExt, const char* promoExt, SpvVersion incorporatedVersion) + { + removeExtension(baseExt); + addIncorporatedExtension(promoExt, incorporatedVersion); + } + void addInclude(const std::string& name, const std::string& text) + { + spv::Id incId = getStringId(name); + includeFiles[incId] = &text; + } + Id import(const char*); + void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem) + { + addressModel = addr; + memoryModel = mem; + } + + void addCapability(spv::Capability cap) { capabilities.insert(cap); } + + // To get a new for anything needing a new one. + Id getUniqueId() { return ++uniqueId; } + + // To get a set of new s, e.g., for a set of function parameters + Id getUniqueIds(int numIds) + { + Id id = uniqueId + 1; + uniqueId += numIds; + return id; + } + + // Generate OpLine for non-filename-based #line directives (ie no filename + // seen yet): Log the current line, and if different than the last one, + // issue a new OpLine using the new line and current source file name. + void setLine(int line); + + // If filename null, generate OpLine for non-filename-based line directives, + // else do filename-based: Log the current line and file, and if different + // than the last one, issue a new OpLine using the new line and file + // name. + void setLine(int line, const char* filename); + // Low-level OpLine. See setLine() for a layered helper. + void addLine(Id fileName, int line, int column); + + // For creating new types (will return old type if the requested one was already made). + Id makeVoidType(); + Id makeBoolType(); + Id makePointer(StorageClass, Id pointee); + Id makeForwardPointer(StorageClass); + Id makePointerFromForwardPointer(StorageClass, Id forwardPointerType, Id pointee); + Id makeIntegerType(int width, bool hasSign); // generic + Id makeIntType(int width) { return makeIntegerType(width, true); } + Id makeUintType(int width) { return makeIntegerType(width, false); } + Id makeFloatType(int width); + Id makeStructType(const std::vector& members, const char*); + Id makeStructResultType(Id type0, Id type1); + Id makeVectorType(Id component, int size); + Id makeMatrixType(Id component, int cols, int rows); + Id makeArrayType(Id element, Id sizeId, int stride); // 0 stride means no stride decoration + Id makeRuntimeArray(Id element); + Id makeFunctionType(Id returnType, const std::vector& paramTypes); + Id makeImageType(Id sampledType, Dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format, bool video); + Id makeSamplerType(); + Id makeSampledImageType(Id imageType); + Id makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols); + + // accelerationStructureNV type + Id makeAccelerationStructureType(); + // rayQueryEXT type + Id makeRayQueryType(); + + // For querying about types. + Id getTypeId(Id resultId) const { return module.getTypeId(resultId); } + Id getDerefTypeId(Id resultId) const; + Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); } + Op getTypeClass(Id typeId) const { return getOpCode(typeId); } + Op getMostBasicTypeClass(Id typeId) const; + int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); } + int getNumTypeConstituents(Id typeId) const; + int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); } + Id getScalarTypeId(Id typeId) const; + Id getContainedTypeId(Id typeId) const; + Id getContainedTypeId(Id typeId, int) const; + StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); } + ImageFormat getImageTypeFormat(Id typeId) const + { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); } + + bool isPointer(Id resultId) const { return isPointerType(getTypeId(resultId)); } + bool isScalar(Id resultId) const { return isScalarType(getTypeId(resultId)); } + bool isVector(Id resultId) const { return isVectorType(getTypeId(resultId)); } + bool isMatrix(Id resultId) const { return isMatrixType(getTypeId(resultId)); } + bool isCooperativeMatrix(Id resultId)const { return isCooperativeMatrixType(getTypeId(resultId)); } + bool isAggregate(Id resultId) const { return isAggregateType(getTypeId(resultId)); } + bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); } + + bool isBoolType(Id typeId) + { return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); } + bool isIntType(Id typeId) const + { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; } + bool isUintType(Id typeId) const + { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; } + bool isFloatType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat; } + bool isPointerType(Id typeId) const { return getTypeClass(typeId) == OpTypePointer; } + bool isScalarType(Id typeId) const + { return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt || + getTypeClass(typeId) == OpTypeBool; } + bool isVectorType(Id typeId) const { return getTypeClass(typeId) == OpTypeVector; } + bool isMatrixType(Id typeId) const { return getTypeClass(typeId) == OpTypeMatrix; } + bool isStructType(Id typeId) const { return getTypeClass(typeId) == OpTypeStruct; } + bool isArrayType(Id typeId) const { return getTypeClass(typeId) == OpTypeArray; } +#ifdef GLSLANG_WEB + bool isCooperativeMatrixType(Id typeId)const { return false; } +#else + bool isCooperativeMatrixType(Id typeId)const { return getTypeClass(typeId) == OpTypeCooperativeMatrixNV; } +#endif + bool isAggregateType(Id typeId) const + { return isArrayType(typeId) || isStructType(typeId) || isCooperativeMatrixType(typeId); } + bool isImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeImage; } + bool isSamplerType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampler; } + bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; } + bool containsType(Id typeId, Op typeOp, unsigned int width) const; + bool containsPhysicalStorageBufferOrArray(Id typeId) const; + + bool isConstantOpCode(Op opcode) const; + bool isSpecConstantOpCode(Op opcode) const; + bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); } + bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; } + bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); } + unsigned int getConstantScalar(Id resultId) const + { return module.getInstruction(resultId)->getImmediateOperand(0); } + StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); } + + int getScalarTypeWidth(Id typeId) const + { + Id scalarTypeId = getScalarTypeId(typeId); + assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat); + return module.getInstruction(scalarTypeId)->getImmediateOperand(0); + } + + int getTypeNumColumns(Id typeId) const + { + assert(isMatrixType(typeId)); + return getNumTypeConstituents(typeId); + } + int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); } + int getTypeNumRows(Id typeId) const + { + assert(isMatrixType(typeId)); + return getNumTypeComponents(getContainedTypeId(typeId)); + } + int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); } + + Dim getTypeDimensionality(Id typeId) const + { + assert(isImageType(typeId)); + return (Dim)module.getInstruction(typeId)->getImmediateOperand(1); + } + Id getImageType(Id resultId) const + { + Id typeId = getTypeId(resultId); + assert(isImageType(typeId) || isSampledImageType(typeId)); + return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId; + } + bool isArrayedImageType(Id typeId) const + { + assert(isImageType(typeId)); + return module.getInstruction(typeId)->getImmediateOperand(3) != 0; + } + + // For making new constants (will return old constant if the requested one was already made). + Id makeBoolConstant(bool b, bool specConstant = false); + Id makeInt8Constant(int i, bool specConstant = false) + { return makeIntConstant(makeIntType(8), (unsigned)i, specConstant); } + Id makeUint8Constant(unsigned u, bool specConstant = false) + { return makeIntConstant(makeUintType(8), u, specConstant); } + Id makeInt16Constant(int i, bool specConstant = false) + { return makeIntConstant(makeIntType(16), (unsigned)i, specConstant); } + Id makeUint16Constant(unsigned u, bool specConstant = false) + { return makeIntConstant(makeUintType(16), u, specConstant); } + Id makeIntConstant(int i, bool specConstant = false) + { return makeIntConstant(makeIntType(32), (unsigned)i, specConstant); } + Id makeUintConstant(unsigned u, bool specConstant = false) + { return makeIntConstant(makeUintType(32), u, specConstant); } + Id makeInt64Constant(long long i, bool specConstant = false) + { return makeInt64Constant(makeIntType(64), (unsigned long long)i, specConstant); } + Id makeUint64Constant(unsigned long long u, bool specConstant = false) + { return makeInt64Constant(makeUintType(64), u, specConstant); } + Id makeFloatConstant(float f, bool specConstant = false); + Id makeDoubleConstant(double d, bool specConstant = false); + Id makeFloat16Constant(float f16, bool specConstant = false); + Id makeFpConstant(Id type, double d, bool specConstant = false); + + // Turn the array of constants into a proper spv constant of the requested type. + Id makeCompositeConstant(Id type, const std::vector& comps, bool specConst = false); + + // Methods for adding information outside the CFG. + Instruction* addEntryPoint(ExecutionModel, Function*, const char* name); + void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1); + void addName(Id, const char* name); + void addMemberName(Id, int member, const char* name); + void addDecoration(Id, Decoration, int num = -1); + void addDecoration(Id, Decoration, const char*); + void addDecorationId(Id id, Decoration, Id idDecoration); + void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1); + void addMemberDecoration(Id, unsigned int member, Decoration, const char*); + + // At the end of what block do the next create*() instructions go? + void setBuildPoint(Block* bp) { buildPoint = bp; } + Block* getBuildPoint() const { return buildPoint; } + + // Make the entry-point function. The returned pointer is only valid + // for the lifetime of this builder. + Function* makeEntryPoint(const char*); + + // Make a shader-style function, and create its entry block if entry is non-zero. + // Return the function, pass back the entry. + // The returned pointer is only valid for the lifetime of this builder. + Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, + const std::vector& paramTypes, const std::vector>& precisions, Block **entry = 0); + + // Create a return. An 'implicit' return is one not appearing in the source + // code. In the case of an implicit return, no post-return block is inserted. + void makeReturn(bool implicit, Id retVal = 0); + + // Generate all the code needed to finish up a function. + void leaveFunction(); + + // Create a discard. + void makeDiscard(); + + // Create a global or function local or IO variable. + Id createVariable(StorageClass, Id type, const char* name = 0, Id initializer = NoResult); + + // Create an intermediate with an undefined value. + Id createUndefined(Id type); + + // Store into an Id and return the l-value + void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, + spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); + + // Load from an Id and return it + Id createLoad(Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, + spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); + + // Create an OpAccessChain instruction + Id createAccessChain(StorageClass, Id base, const std::vector& offsets); + + // Create an OpArrayLength instruction + Id createArrayLength(Id base, unsigned int member); + + // Create an OpCooperativeMatrixLengthNV instruction + Id createCooperativeMatrixLength(Id type); + + // Create an OpCompositeExtract instruction + Id createCompositeExtract(Id composite, Id typeId, unsigned index); + Id createCompositeExtract(Id composite, Id typeId, const std::vector& indexes); + Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index); + Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector& indexes); + + Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex); + Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex); + + void createNoResultOp(Op); + void createNoResultOp(Op, Id operand); + void createNoResultOp(Op, const std::vector& operands); + void createNoResultOp(Op, const std::vector& operands); + void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask); + void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics); + Id createUnaryOp(Op, Id typeId, Id operand); + Id createBinOp(Op, Id typeId, Id operand1, Id operand2); + Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3); + Id createOp(Op, Id typeId, const std::vector& operands); + Id createOp(Op, Id typeId, const std::vector& operands); + Id createFunctionCall(spv::Function*, const std::vector&); + Id createSpecConstantOp(Op, Id typeId, const std::vector& operands, const std::vector& literals); + + // Take an rvalue (source) and a set of channels to extract from it to + // make a new rvalue, which is returned. + Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector& channels); + + // Take a copy of an lvalue (target) and a source of components, and set the + // source components into the lvalue where the 'channels' say to put them. + // An updated version of the target is returned. + // (No true lvalue or stores are used.) + Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector& channels); + + // If both the id and precision are valid, the id + // gets tagged with the requested precision. + // The passed in id is always the returned id, to simplify use patterns. + Id setPrecision(Id id, Decoration precision) + { + if (precision != NoPrecision && id != NoResult) + addDecoration(id, precision); + + return id; + } + + // Can smear a scalar to a vector for the following forms: + // - promoteScalar(scalar, vector) // smear scalar to width of vector + // - promoteScalar(vector, scalar) // smear scalar to width of vector + // - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to + // - promoteScalar(scalar, scalar) // do nothing + // Other forms are not allowed. + // + // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'. + // The type of the created vector is a vector of components of the same type as the scalar. + // + // Note: One of the arguments will change, with the result coming back that way rather than + // through the return value. + void promoteScalar(Decoration precision, Id& left, Id& right); + + // Make a value by smearing the scalar to fill the type. + // vectorType should be the correct type for making a vector of scalarVal. + // (No conversions are done.) + Id smearScalar(Decoration precision, Id scalarVal, Id vectorType); + + // Create a call to a built-in function. + Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector& args); + + // List of parameters used to create a texture operation + struct TextureParameters { + Id sampler; + Id coords; + Id bias; + Id lod; + Id Dref; + Id offset; + Id offsets; + Id gradX; + Id gradY; + Id sample; + Id component; + Id texelOut; + Id lodClamp; + Id granularity; + Id coarse; + bool nonprivate; + bool volatil; + }; + + // Select the correct texture operation based on all inputs, and emit the correct instruction + Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, + bool noImplicit, const TextureParameters&, ImageOperandsMask); + + // Emit the OpTextureQuery* instruction that was passed in. + // Figure out the right return value and type, and return it. + Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult); + + Id createSamplePositionCall(Decoration precision, Id, Id); + + Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned); + Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id); + + // Reduction comparison for composites: For equal and not-equal resulting in a scalar. + Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */); + + // OpCompositeConstruct + Id createCompositeConstruct(Id typeId, const std::vector& constituents); + + // vector or scalar constructor + Id createConstructor(Decoration precision, const std::vector& sources, Id resultTypeId); + + // matrix constructor + Id createMatrixConstructor(Decoration precision, const std::vector& sources, Id constructee); + + // Helper to use for building nested control flow with if-then-else. + class If { + public: + If(Id condition, unsigned int ctrl, Builder& builder); + ~If() {} + + void makeBeginElse(); + void makeEndIf(); + + private: + If(const If&); + If& operator=(If&); + + Builder& builder; + Id condition; + unsigned int control; + Function* function; + Block* headerBlock; + Block* thenBlock; + Block* elseBlock; + Block* mergeBlock; + }; + + // Make a switch statement. A switch has 'numSegments' of pieces of code, not containing + // any case/default labels, all separated by one or more case/default labels. Each possible + // case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this + // number space. How to compute the value is given by 'condition', as in switch(condition). + // + // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches. + // + // Use a defaultSegment < 0 if there is no default segment (to branch to post switch). + // + // Returns the right set of basic blocks to start each code segment with, so that the caller's + // recursion stack can hold the memory for it. + // + void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector& caseValues, + const std::vector& valueToSegment, int defaultSegment, std::vector& segmentBB); + + // Add a branch to the innermost switch's merge block. + void addSwitchBreak(); + + // Move to the next code segment, passing in the return argument in makeSwitch() + void nextSwitchSegment(std::vector& segmentBB, int segment); + + // Finish off the innermost switch. + void endSwitch(std::vector& segmentBB); + + struct LoopBlocks { + LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) : + head(head), body(body), merge(merge), continue_target(continue_target) { } + Block &head, &body, &merge, &continue_target; + private: + LoopBlocks(); + LoopBlocks& operator=(const LoopBlocks&) = delete; + }; + + // Start a new loop and prepare the builder to generate code for it. Until + // closeLoop() is called for this loop, createLoopContinue() and + // createLoopExit() will target its corresponding blocks. + LoopBlocks& makeNewLoop(); + + // Create a new block in the function containing the build point. Memory is + // owned by the function object. + Block& makeNewBlock(); + + // Add a branch to the continue_target of the current (innermost) loop. + void createLoopContinue(); + + // Add an exit (e.g. "break") from the innermost loop that we're currently + // in. + void createLoopExit(); + + // Close the innermost loop that you're in + void closeLoop(); + + // + // Access chain design for an R-Value vs. L-Value: + // + // There is a single access chain the builder is building at + // any particular time. Such a chain can be used to either to a load or + // a store, when desired. + // + // Expressions can be r-values, l-values, or both, or only r-values: + // a[b.c].d = .... // l-value + // ... = a[b.c].d; // r-value, that also looks like an l-value + // ++a[b.c].d; // r-value and l-value + // (x + y)[2]; // r-value only, can't possibly be l-value + // + // Computing an r-value means generating code. Hence, + // r-values should only be computed when they are needed, not speculatively. + // + // Computing an l-value means saving away information for later use in the compiler, + // no code is generated until the l-value is later dereferenced. It is okay + // to speculatively generate an l-value, just not okay to speculatively dereference it. + // + // The base of the access chain (the left-most variable or expression + // from which everything is based) can be set either as an l-value + // or as an r-value. Most efficient would be to set an l-value if one + // is available. If an expression was evaluated, the resulting r-value + // can be set as the chain base. + // + // The users of this single access chain can save and restore if they + // want to nest or manage multiple chains. + // + + struct AccessChain { + Id base; // for l-values, pointer to the base object, for r-values, the base object + std::vector indexChain; + Id instr; // cache the instruction that generates this access chain + std::vector swizzle; // each std::vector element selects the next GLSL component number + Id component; // a dynamic component index, can coexist with a swizzle, + // done after the swizzle, NoResult if not present + Id preSwizzleBaseType; // dereferenced type, before swizzle or component is applied; + // NoType unless a swizzle or component is present + bool isRValue; // true if 'base' is an r-value, otherwise, base is an l-value + unsigned int alignment; // bitwise OR of alignment values passed in. Accumulates worst alignment. + // Only tracks base and (optional) component selection alignment. + + // Accumulate whether anything in the chain of structures has coherent decorations. + struct CoherentFlags { + CoherentFlags() { clear(); } +#ifdef GLSLANG_WEB + void clear() { } + bool isVolatile() const { return false; } + CoherentFlags operator |=(const CoherentFlags &other) { return *this; } +#else + bool isVolatile() const { return volatil; } + bool anyCoherent() const { + return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent || + subgroupcoherent || shadercallcoherent; + } + + unsigned coherent : 1; + unsigned devicecoherent : 1; + unsigned queuefamilycoherent : 1; + unsigned workgroupcoherent : 1; + unsigned subgroupcoherent : 1; + unsigned shadercallcoherent : 1; + unsigned nonprivate : 1; + unsigned volatil : 1; + unsigned isImage : 1; + + void clear() { + coherent = 0; + devicecoherent = 0; + queuefamilycoherent = 0; + workgroupcoherent = 0; + subgroupcoherent = 0; + shadercallcoherent = 0; + nonprivate = 0; + volatil = 0; + isImage = 0; + } + + CoherentFlags operator |=(const CoherentFlags &other) { + coherent |= other.coherent; + devicecoherent |= other.devicecoherent; + queuefamilycoherent |= other.queuefamilycoherent; + workgroupcoherent |= other.workgroupcoherent; + subgroupcoherent |= other.subgroupcoherent; + shadercallcoherent |= other.shadercallcoherent; + nonprivate |= other.nonprivate; + volatil |= other.volatil; + isImage |= other.isImage; + return *this; + } +#endif + }; + CoherentFlags coherentFlags; + }; + + // + // the SPIR-V builder maintains a single active chain that + // the following methods operate on + // + + // for external save and restore + AccessChain getAccessChain() { return accessChain; } + void setAccessChain(AccessChain newChain) { accessChain = newChain; } + + // clear accessChain + void clearAccessChain(); + + // set new base as an l-value base + void setAccessChainLValue(Id lValue) + { + assert(isPointer(lValue)); + accessChain.base = lValue; + } + + // set new base value as an r-value + void setAccessChainRValue(Id rValue) + { + accessChain.isRValue = true; + accessChain.base = rValue; + } + + // push offset onto the end of the chain + void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment) + { + accessChain.indexChain.push_back(offset); + accessChain.coherentFlags |= coherentFlags; + accessChain.alignment |= alignment; + } + + // push new swizzle onto the end of any existing swizzle, merging into a single swizzle + void accessChainPushSwizzle(std::vector& swizzle, Id preSwizzleBaseType, + AccessChain::CoherentFlags coherentFlags, unsigned int alignment); + + // push a dynamic component selection onto the access chain, only applicable with a + // non-trivial swizzle or no swizzle + void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, + unsigned int alignment) + { + if (accessChain.swizzle.size() != 1) { + accessChain.component = component; + if (accessChain.preSwizzleBaseType == NoType) + accessChain.preSwizzleBaseType = preSwizzleBaseType; + } + accessChain.coherentFlags |= coherentFlags; + accessChain.alignment |= alignment; + } + + // use accessChain and swizzle to store value + void accessChainStore(Id rvalue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, + spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); + + // use accessChain and swizzle to load an r-value + Id accessChainLoad(Decoration precision, Decoration nonUniform, Id ResultType, + spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, + unsigned int alignment = 0); + + // Return whether or not the access chain can be represented in SPIR-V + // as an l-value. + // E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be. + bool isSpvLvalue() const { return accessChain.swizzle.size() <= 1; } + + // get the direct pointer for an l-value + Id accessChainGetLValue(); + + // Get the inferred SPIR-V type of the result of the current access chain, + // based on the type of the base and the chain of dereferences. + Id accessChainGetInferredType(); + + // Add capabilities, extensions, remove unneeded decorations, etc., + // based on the resulting SPIR-V. + void postProcess(); + + // Prune unreachable blocks in the CFG and remove unneeded decorations. + void postProcessCFG(); + +#ifndef GLSLANG_WEB + // Add capabilities, extensions based on instructions in the module. + void postProcessFeatures(); + // Hook to visit each instruction in a block in a function + void postProcess(Instruction&); + // Hook to visit each non-32-bit sized float/int operation in a block. + void postProcessType(const Instruction&, spv::Id typeId); +#endif + + void dump(std::vector&) const; + + void createBranch(Block* block); + void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock); + void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, + const std::vector& operands); + + // Sets to generate opcode for specialization constants. + void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; } + // Sets to generate opcode for non-specialization constants (normal mode). + void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; } + // Check if the builder is generating code for spec constants. + bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; } + + protected: + Id makeIntConstant(Id typeId, unsigned value, bool specConstant); + Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant); + Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value); + Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2); + Id findCompositeConstant(Op typeClass, Id typeId, const std::vector& comps); + Id findStructConstant(Id typeId, const std::vector& comps); + Id collapseAccessChain(); + void remapDynamicSwizzle(); + void transferAccessChainSwizzle(bool dynamic); + void simplifyAccessChainSwizzle(); + void createAndSetNoPredecessorBlock(const char*); + void createSelectionMerge(Block* mergeBlock, unsigned int control); + void dumpSourceInstructions(std::vector&) const; + void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector&) const; + void dumpInstructions(std::vector&, const std::vector >&) const; + void dumpModuleProcesses(std::vector&) const; + spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) + const; + + unsigned int spvVersion; // the version of SPIR-V to emit in the header + SourceLanguage source; + int sourceVersion; + spv::Id sourceFileStringId; + std::string sourceText; + int currentLine; + const char* currentFile; + bool emitOpLines; + std::set extensions; + std::vector sourceExtensions; + std::vector moduleProcesses; + AddressingModel addressModel; + MemoryModel memoryModel; + std::set capabilities; + int builderNumber; + Module module; + Block* buildPoint; + Id uniqueId; + Function* entryPointFunction; + bool generatingOpCodeForSpecConst; + AccessChain accessChain; + + // special blocks of instructions for output + std::vector > strings; + std::vector > imports; + std::vector > entryPoints; + std::vector > executionModes; + std::vector > names; + std::vector > decorations; + std::vector > constantsTypesGlobals; + std::vector > externals; + std::vector > functions; + + // not output, internally used for quick & dirty canonical (unique) creation + + // map type opcodes to constant inst. + std::unordered_map> groupedConstants; + // map struct-id to constant instructions + std::unordered_map> groupedStructConstants; + // map type opcodes to type instructions + std::unordered_map> groupedTypes; + + // stack of switches + std::stack switchMerges; + + // Our loop stack. + std::stack loops; + + // map from strings to their string ids + std::unordered_map stringIds; + + // map from include file name ids to their contents + std::map includeFiles; + + // The stream for outputting warnings and errors. + SpvBuildLogger* logger; +}; // end Builder class + +}; // end spv namespace + +#endif // SpvBuilder_H diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/SpvPostProcess.cpp b/armorcore/tools/to_spirv/glslang/SPIRV/SpvPostProcess.cpp new file mode 100644 index 000000000..d40174d17 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/SpvPostProcess.cpp @@ -0,0 +1,450 @@ +// +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Post-processing for SPIR-V IR, in internal form, not standard binary form. +// + +#include +#include + +#include +#include +#include + +#include "SpvBuilder.h" + +#include "spirv.hpp" +#include "GlslangToSpv.h" +#include "SpvBuilder.h" +namespace spv { + #include "GLSL.std.450.h" + #include "GLSL.ext.KHR.h" + #include "GLSL.ext.EXT.h" + #include "GLSL.ext.AMD.h" + #include "GLSL.ext.NV.h" +} + +namespace spv { + +#ifndef GLSLANG_WEB +// Hook to visit each operand type and result type of an instruction. +// Will be called multiple times for one instruction, once for each typed +// operand and the result. +void Builder::postProcessType(const Instruction& inst, Id typeId) +{ + // Characterize the type being questioned + Id basicTypeOp = getMostBasicTypeClass(typeId); + int width = 0; + if (basicTypeOp == OpTypeFloat || basicTypeOp == OpTypeInt) + width = getScalarTypeWidth(typeId); + + // Do opcode-specific checks + switch (inst.getOpCode()) { + case OpLoad: + case OpStore: + if (basicTypeOp == OpTypeStruct) { + if (containsType(typeId, OpTypeInt, 8)) + addCapability(CapabilityInt8); + if (containsType(typeId, OpTypeInt, 16)) + addCapability(CapabilityInt16); + if (containsType(typeId, OpTypeFloat, 16)) + addCapability(CapabilityFloat16); + } else { + StorageClass storageClass = getStorageClass(inst.getIdOperand(0)); + if (width == 8) { + switch (storageClass) { + case StorageClassPhysicalStorageBufferEXT: + case StorageClassUniform: + case StorageClassStorageBuffer: + case StorageClassPushConstant: + break; + default: + addCapability(CapabilityInt8); + break; + } + } else if (width == 16) { + switch (storageClass) { + case StorageClassPhysicalStorageBufferEXT: + case StorageClassUniform: + case StorageClassStorageBuffer: + case StorageClassPushConstant: + case StorageClassInput: + case StorageClassOutput: + break; + default: + if (basicTypeOp == OpTypeInt) + addCapability(CapabilityInt16); + if (basicTypeOp == OpTypeFloat) + addCapability(CapabilityFloat16); + break; + } + } + } + break; + case OpAccessChain: + case OpPtrAccessChain: + case OpCopyObject: + break; + case OpFConvert: + case OpSConvert: + case OpUConvert: + // Look for any 8/16-bit storage capabilities. If there are none, assume that + // the convert instruction requires the Float16/Int8/16 capability. + if (containsType(typeId, OpTypeFloat, 16) || containsType(typeId, OpTypeInt, 16)) { + bool foundStorage = false; + for (auto it = capabilities.begin(); it != capabilities.end(); ++it) { + spv::Capability cap = *it; + if (cap == spv::CapabilityStorageInputOutput16 || + cap == spv::CapabilityStoragePushConstant16 || + cap == spv::CapabilityStorageUniformBufferBlock16 || + cap == spv::CapabilityStorageUniform16) { + foundStorage = true; + break; + } + } + if (!foundStorage) { + if (containsType(typeId, OpTypeFloat, 16)) + addCapability(CapabilityFloat16); + if (containsType(typeId, OpTypeInt, 16)) + addCapability(CapabilityInt16); + } + } + if (containsType(typeId, OpTypeInt, 8)) { + bool foundStorage = false; + for (auto it = capabilities.begin(); it != capabilities.end(); ++it) { + spv::Capability cap = *it; + if (cap == spv::CapabilityStoragePushConstant8 || + cap == spv::CapabilityUniformAndStorageBuffer8BitAccess || + cap == spv::CapabilityStorageBuffer8BitAccess) { + foundStorage = true; + break; + } + } + if (!foundStorage) { + addCapability(CapabilityInt8); + } + } + break; + case OpExtInst: + switch (inst.getImmediateOperand(1)) { + case GLSLstd450Frexp: + case GLSLstd450FrexpStruct: + if (getSpvVersion() < glslang::EShTargetSpv_1_3 && containsType(typeId, OpTypeInt, 16)) + addExtension(spv::E_SPV_AMD_gpu_shader_int16); + break; + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + if (getSpvVersion() < glslang::EShTargetSpv_1_3 && containsType(typeId, OpTypeFloat, 16)) + addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + break; + default: + break; + } + break; + default: + if (basicTypeOp == OpTypeFloat && width == 16) + addCapability(CapabilityFloat16); + if (basicTypeOp == OpTypeInt && width == 16) + addCapability(CapabilityInt16); + if (basicTypeOp == OpTypeInt && width == 8) + addCapability(CapabilityInt8); + break; + } +} + +// Called for each instruction that resides in a block. +void Builder::postProcess(Instruction& inst) +{ + // Add capabilities based simply on the opcode. + switch (inst.getOpCode()) { + case OpExtInst: + switch (inst.getImmediateOperand(1)) { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + addCapability(CapabilityInterpolationFunction); + break; + default: + break; + } + break; + case OpDPdxFine: + case OpDPdyFine: + case OpFwidthFine: + case OpDPdxCoarse: + case OpDPdyCoarse: + case OpFwidthCoarse: + addCapability(CapabilityDerivativeControl); + break; + + case OpImageQueryLod: + case OpImageQuerySize: + case OpImageQuerySizeLod: + case OpImageQuerySamples: + case OpImageQueryLevels: + addCapability(CapabilityImageQuery); + break; + + case OpGroupNonUniformPartitionNV: + addExtension(E_SPV_NV_shader_subgroup_partitioned); + addCapability(CapabilityGroupNonUniformPartitionedNV); + break; + + case OpLoad: + case OpStore: + { + // For any load/store to a PhysicalStorageBufferEXT, walk the accesschain + // index list to compute the misalignment. The pre-existing alignment value + // (set via Builder::AccessChain::alignment) only accounts for the base of + // the reference type and any scalar component selection in the accesschain, + // and this function computes the rest from the SPIR-V Offset decorations. + Instruction *accessChain = module.getInstruction(inst.getIdOperand(0)); + if (accessChain->getOpCode() == OpAccessChain) { + Instruction *base = module.getInstruction(accessChain->getIdOperand(0)); + // Get the type of the base of the access chain. It must be a pointer type. + Id typeId = base->getTypeId(); + Instruction *type = module.getInstruction(typeId); + assert(type->getOpCode() == OpTypePointer); + if (type->getImmediateOperand(0) != StorageClassPhysicalStorageBufferEXT) { + break; + } + // Get the pointee type. + typeId = type->getIdOperand(1); + type = module.getInstruction(typeId); + // Walk the index list for the access chain. For each index, find any + // misalignment that can apply when accessing the member/element via + // Offset/ArrayStride/MatrixStride decorations, and bitwise OR them all + // together. + int alignment = 0; + for (int i = 1; i < accessChain->getNumOperands(); ++i) { + Instruction *idx = module.getInstruction(accessChain->getIdOperand(i)); + if (type->getOpCode() == OpTypeStruct) { + assert(idx->getOpCode() == OpConstant); + unsigned int c = idx->getImmediateOperand(0); + + const auto function = [&](const std::unique_ptr& decoration) { + if (decoration.get()->getOpCode() == OpMemberDecorate && + decoration.get()->getIdOperand(0) == typeId && + decoration.get()->getImmediateOperand(1) == c && + (decoration.get()->getImmediateOperand(2) == DecorationOffset || + decoration.get()->getImmediateOperand(2) == DecorationMatrixStride)) { + alignment |= decoration.get()->getImmediateOperand(3); + } + }; + std::for_each(decorations.begin(), decorations.end(), function); + // get the next member type + typeId = type->getIdOperand(c); + type = module.getInstruction(typeId); + } else if (type->getOpCode() == OpTypeArray || + type->getOpCode() == OpTypeRuntimeArray) { + const auto function = [&](const std::unique_ptr& decoration) { + if (decoration.get()->getOpCode() == OpDecorate && + decoration.get()->getIdOperand(0) == typeId && + decoration.get()->getImmediateOperand(1) == DecorationArrayStride) { + alignment |= decoration.get()->getImmediateOperand(2); + } + }; + std::for_each(decorations.begin(), decorations.end(), function); + // Get the element type + typeId = type->getIdOperand(0); + type = module.getInstruction(typeId); + } else { + // Once we get to any non-aggregate type, we're done. + break; + } + } + assert(inst.getNumOperands() >= 3); + unsigned int memoryAccess = inst.getImmediateOperand((inst.getOpCode() == OpStore) ? 2 : 1); + assert(memoryAccess & MemoryAccessAlignedMask); + static_cast(memoryAccess); + // Compute the index of the alignment operand. + int alignmentIdx = 2; + if (inst.getOpCode() == OpStore) + alignmentIdx++; + // Merge new and old (mis)alignment + alignment |= inst.getImmediateOperand(alignmentIdx); + // Pick the LSB + alignment = alignment & ~(alignment & (alignment-1)); + // update the Aligned operand + inst.setImmediateOperand(alignmentIdx, alignment); + } + break; + } + + default: + break; + } + + // Checks based on type + if (inst.getTypeId() != NoType) + postProcessType(inst, inst.getTypeId()); + for (int op = 0; op < inst.getNumOperands(); ++op) { + if (inst.isIdOperand(op)) { + // In blocks, these are always result ids, but we are relying on + // getTypeId() to return NoType for things like OpLabel. + if (getTypeId(inst.getIdOperand(op)) != NoType) + postProcessType(inst, getTypeId(inst.getIdOperand(op))); + } + } +} +#endif + +// comment in header +void Builder::postProcessCFG() +{ + // reachableBlocks is the set of blockss reached via control flow, or which are + // unreachable continue targert or unreachable merge. + std::unordered_set reachableBlocks; + std::unordered_map headerForUnreachableContinue; + std::unordered_set unreachableMerges; + std::unordered_set unreachableDefinitions; + // Collect IDs defined in unreachable blocks. For each function, label the + // reachable blocks first. Then for each unreachable block, collect the + // result IDs of the instructions in it. + for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) { + Function* f = *fi; + Block* entry = f->getEntryBlock(); + inReadableOrder(entry, + [&reachableBlocks, &unreachableMerges, &headerForUnreachableContinue] + (Block* b, ReachReason why, Block* header) { + reachableBlocks.insert(b); + if (why == ReachDeadContinue) headerForUnreachableContinue[b] = header; + if (why == ReachDeadMerge) unreachableMerges.insert(b); + }); + for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) { + Block* b = *bi; + if (unreachableMerges.count(b) != 0 || headerForUnreachableContinue.count(b) != 0) { + auto ii = b->getInstructions().cbegin(); + ++ii; // Keep potential decorations on the label. + for (; ii != b->getInstructions().cend(); ++ii) + unreachableDefinitions.insert(ii->get()->getResultId()); + } else if (reachableBlocks.count(b) == 0) { + // The normal case for unreachable code. All definitions are considered dead. + for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ++ii) + unreachableDefinitions.insert(ii->get()->getResultId()); + } + } + } + + // Modify unreachable merge blocks and unreachable continue targets. + // Delete their contents. + for (auto mergeIter = unreachableMerges.begin(); mergeIter != unreachableMerges.end(); ++mergeIter) { + (*mergeIter)->rewriteAsCanonicalUnreachableMerge(); + } + for (auto continueIter = headerForUnreachableContinue.begin(); + continueIter != headerForUnreachableContinue.end(); + ++continueIter) { + Block* continue_target = continueIter->first; + Block* header = continueIter->second; + continue_target->rewriteAsCanonicalUnreachableContinue(header); + } + + // Remove unneeded decorations, for unreachable instructions + decorations.erase(std::remove_if(decorations.begin(), decorations.end(), + [&unreachableDefinitions](std::unique_ptr& I) -> bool { + Id decoration_id = I.get()->getIdOperand(0); + return unreachableDefinitions.count(decoration_id) != 0; + }), + decorations.end()); +} + +#ifndef GLSLANG_WEB +// comment in header +void Builder::postProcessFeatures() { + // Add per-instruction capabilities, extensions, etc., + + // Look for any 8/16 bit type in physical storage buffer class, and set the + // appropriate capability. This happens in createSpvVariable for other storage + // classes, but there isn't always a variable for physical storage buffer. + for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { + Instruction* type = groupedTypes[OpTypePointer][t]; + if (type->getImmediateOperand(0) == (unsigned)StorageClassPhysicalStorageBufferEXT) { + if (containsType(type->getIdOperand(1), OpTypeInt, 8)) { + addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5); + addCapability(spv::CapabilityStorageBuffer8BitAccess); + } + if (containsType(type->getIdOperand(1), OpTypeInt, 16) || + containsType(type->getIdOperand(1), OpTypeFloat, 16)) { + addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + addCapability(spv::CapabilityStorageBuffer16BitAccess); + } + } + } + + // process all block-contained instructions + for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) { + Function* f = *fi; + for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) { + Block* b = *bi; + for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++) + postProcess(*ii->get()); + + // For all local variables that contain pointers to PhysicalStorageBufferEXT, check whether + // there is an existing restrict/aliased decoration. If we don't find one, add Aliased as the + // default. + for (auto vi = b->getLocalVariables().cbegin(); vi != b->getLocalVariables().cend(); vi++) { + const Instruction& inst = *vi->get(); + Id resultId = inst.getResultId(); + if (containsPhysicalStorageBufferOrArray(getDerefTypeId(resultId))) { + bool foundDecoration = false; + const auto function = [&](const std::unique_ptr& decoration) { + if (decoration.get()->getIdOperand(0) == resultId && + decoration.get()->getOpCode() == OpDecorate && + (decoration.get()->getImmediateOperand(1) == spv::DecorationAliasedPointerEXT || + decoration.get()->getImmediateOperand(1) == spv::DecorationRestrictPointerEXT)) { + foundDecoration = true; + } + }; + std::for_each(decorations.begin(), decorations.end(), function); + if (!foundDecoration) { + addDecoration(resultId, spv::DecorationAliasedPointerEXT); + } + } + } + } + } +} +#endif + +// comment in header +void Builder::postProcess() { + postProcessCFG(); +#ifndef GLSLANG_WEB + postProcessFeatures(); +#endif +} + +}; // end spv namespace diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/SpvTools.cpp b/armorcore/tools/to_spirv/glslang/SPIRV/SpvTools.cpp new file mode 100644 index 000000000..1e968ba54 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/SpvTools.cpp @@ -0,0 +1,217 @@ +// +// Copyright (C) 2014-2016 LunarG, Inc. +// Copyright (C) 2018-2020 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Call into SPIRV-Tools to disassemble, validate, and optimize. +// + +#if ENABLE_OPT + +#include +#include + +#include "SpvTools.h" +#include "spirv-tools/optimizer.hpp" +#include "spirv-tools/libspirv.h" + +namespace glslang { + +// Translate glslang's view of target versioning to what SPIRV-Tools uses. +spv_target_env MapToSpirvToolsEnv(const SpvVersion& spvVersion, spv::SpvBuildLogger* logger) +{ + switch (spvVersion.vulkan) { + case glslang::EShTargetVulkan_1_0: + return spv_target_env::SPV_ENV_VULKAN_1_0; + case glslang::EShTargetVulkan_1_1: + switch (spvVersion.spv) { + case EShTargetSpv_1_0: + case EShTargetSpv_1_1: + case EShTargetSpv_1_2: + case EShTargetSpv_1_3: + return spv_target_env::SPV_ENV_VULKAN_1_1; + case EShTargetSpv_1_4: + return spv_target_env::SPV_ENV_VULKAN_1_1_SPIRV_1_4; + default: + logger->missingFunctionality("Target version for SPIRV-Tools validator"); + return spv_target_env::SPV_ENV_VULKAN_1_1; + } + case glslang::EShTargetVulkan_1_2: + return spv_target_env::SPV_ENV_VULKAN_1_2; + default: + break; + } + + if (spvVersion.openGl > 0) + return spv_target_env::SPV_ENV_OPENGL_4_5; + + logger->missingFunctionality("Target version for SPIRV-Tools validator"); + return spv_target_env::SPV_ENV_UNIVERSAL_1_0; +} + + +// Use the SPIRV-Tools disassembler to print SPIR-V. +void SpirvToolsDisassemble(std::ostream& out, const std::vector& spirv) +{ + // disassemble + spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_3); + spv_text text; + spv_diagnostic diagnostic = nullptr; + spvBinaryToText(context, spirv.data(), spirv.size(), + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_INDENT, + &text, &diagnostic); + + // dump + if (diagnostic == nullptr) + out << text->str; + else + spvDiagnosticPrint(diagnostic); + + // teardown + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(context); +} + +// Apply the SPIRV-Tools validator to generated SPIR-V. +void SpirvToolsValidate(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, bool prelegalization) +{ + // validate + spv_context context = spvContextCreate(MapToSpirvToolsEnv(intermediate.getSpv(), logger)); + spv_const_binary_t binary = { spirv.data(), spirv.size() }; + spv_diagnostic diagnostic = nullptr; + spv_validator_options options = spvValidatorOptionsCreate(); + spvValidatorOptionsSetRelaxBlockLayout(options, intermediate.usingHlslOffsets()); + spvValidatorOptionsSetBeforeHlslLegalization(options, prelegalization); + spvValidateWithOptions(context, options, &binary, &diagnostic); + + // report + if (diagnostic != nullptr) { + logger->error("SPIRV-Tools Validation Errors"); + logger->error(diagnostic->error); + } + + // tear down + spvValidatorOptionsDestroy(options); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(context); +} + +// Apply the SPIRV-Tools optimizer to generated SPIR-V, for the purpose of +// legalizing HLSL SPIR-V. +void SpirvToolsLegalize(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, const SpvOptions* options) +{ + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2; + + spvtools::Optimizer optimizer(target_env); + optimizer.SetMessageConsumer( + [](spv_message_level_t level, const char *source, const spv_position_t &position, const char *message) { + auto &out = std::cerr; + switch (level) + { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + out << "error: "; + break; + case SPV_MSG_WARNING: + out << "warning: "; + break; + case SPV_MSG_INFO: + case SPV_MSG_DEBUG: + out << "info: "; + break; + default: + break; + } + if (source) + { + out << source << ":"; + } + out << position.line << ":" << position.column << ":" << position.index << ":"; + if (message) + { + out << " " << message; + } + out << std::endl; + }); + + // If debug (specifically source line info) is being generated, propagate + // line information into all SPIR-V instructions. This avoids loss of + // information when instructions are deleted or moved. Later, remove + // redundant information to minimize final SPRIR-V size. + if (options->generateDebugInfo) { + optimizer.RegisterPass(spvtools::CreatePropagateLineInfoPass()); + } + optimizer.RegisterPass(spvtools::CreateWrapOpKillPass()); + optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass()); + optimizer.RegisterPass(spvtools::CreateMergeReturnPass()); + optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass()); + optimizer.RegisterPass(spvtools::CreateEliminateDeadFunctionsPass()); + optimizer.RegisterPass(spvtools::CreateScalarReplacementPass()); + optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass()); + optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass()); + optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass()); + optimizer.RegisterPass(spvtools::CreateSimplificationPass()); + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); + optimizer.RegisterPass(spvtools::CreateVectorDCEPass()); + optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass()); + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); + optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass()); + optimizer.RegisterPass(spvtools::CreateBlockMergePass()); + optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass()); + optimizer.RegisterPass(spvtools::CreateIfConversionPass()); + optimizer.RegisterPass(spvtools::CreateSimplificationPass()); + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); + optimizer.RegisterPass(spvtools::CreateVectorDCEPass()); + optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass()); + if (options->optimizeSize) { + optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass()); + } + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); + optimizer.RegisterPass(spvtools::CreateCFGCleanupPass()); + if (options->generateDebugInfo) { + optimizer.RegisterPass(spvtools::CreateRedundantLineInfoElimPass()); + } + + spvtools::OptimizerOptions spvOptOptions; + optimizer.SetTargetEnv(MapToSpirvToolsEnv(intermediate.getSpv(), logger)); + spvOptOptions.set_run_validator(false); // The validator may run as a separate step later on + optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); +} + +}; // end namespace glslang + +#endif diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/SpvTools.h b/armorcore/tools/to_spirv/glslang/SPIRV/SpvTools.h new file mode 100644 index 000000000..59c914da0 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/SpvTools.h @@ -0,0 +1,82 @@ +// +// Copyright (C) 2014-2016 LunarG, Inc. +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Call into SPIRV-Tools to disassemble, validate, and optimize. +// + +#pragma once +#ifndef GLSLANG_SPV_TOOLS_H +#define GLSLANG_SPV_TOOLS_H + +#ifdef ENABLE_OPT +#include +#include +#endif + +#include "glslang/MachineIndependent/localintermediate.h" +#include "Logger.h" + +namespace glslang { + +struct SpvOptions { + SpvOptions() : generateDebugInfo(false), disableOptimizer(true), + optimizeSize(false), disassemble(false), validate(false) { } + bool generateDebugInfo; + bool disableOptimizer; + bool optimizeSize; + bool disassemble; + bool validate; +}; + +#ifdef ENABLE_OPT + +// Use the SPIRV-Tools disassembler to print SPIR-V. +void SpirvToolsDisassemble(std::ostream& out, const std::vector& spirv); + +// Apply the SPIRV-Tools validator to generated SPIR-V. +void SpirvToolsValidate(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger*, bool prelegalization); + +// Apply the SPIRV-Tools optimizer to generated SPIR-V, for the purpose of +// legalizing HLSL SPIR-V. +void SpirvToolsLegalize(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger*, const SpvOptions*); + +#endif + +} // end namespace glslang + +#endif // GLSLANG_SPV_TOOLS_H diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/bitutils.h b/armorcore/tools/to_spirv/glslang/SPIRV/bitutils.h new file mode 100644 index 000000000..22e44cec2 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/bitutils.h @@ -0,0 +1,81 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_UTIL_BITUTILS_H_ +#define LIBSPIRV_UTIL_BITUTILS_H_ + +#include +#include + +namespace spvutils { + +// Performs a bitwise copy of source to the destination type Dest. +template +Dest BitwiseCast(Src source) { + Dest dest; + static_assert(sizeof(source) == sizeof(dest), + "BitwiseCast: Source and destination must have the same size"); + std::memcpy(static_cast(&dest), &source, sizeof(dest)); + return dest; +} + +// SetBits returns an integer of type with bits set +// for position through , counting from the least +// significant bit. In particular when Num == 0, no positions are set to 1. +// A static assert will be triggered if First + Num > sizeof(T) * 8, that is, +// a bit that will not fit in the underlying type is set. +template +struct SetBits { + static_assert(First < sizeof(T) * 8, + "Tried to set a bit that is shifted too far."); + const static T get = (T(1) << First) | SetBits::get; +}; + +template +struct SetBits { + const static T get = T(0); +}; + +// This is all compile-time so we can put our tests right here. +static_assert(SetBits::get == uint32_t(0x00000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x00000001), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x80000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x00000006), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xc0000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x7FFFFFFF), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xFFFFFFFF), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xFFFF0000), + "SetBits failed"); + +static_assert(SetBits::get == uint64_t(0x0000000000000001LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x8000000000000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0xc000000000000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x0000000080000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x00000000FFFF0000LL), + "SetBits failed"); + +} // namespace spvutils + +#endif // LIBSPIRV_UTIL_BITUTILS_H_ diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/disassemble.cpp b/armorcore/tools/to_spirv/glslang/SPIRV/disassemble.cpp new file mode 100644 index 000000000..4faa89ea3 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/disassemble.cpp @@ -0,0 +1,743 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Disassembler for SPIR-V. +// + +#include +#include +#include +#include +#include +#include +#include + +#include "disassemble.h" +#include "doc.h" +#include "SpvTools.h" + +namespace spv { + extern "C" { + // Include C-based headers that don't have a namespace + #include "GLSL.std.450.h" + #include "GLSL.ext.AMD.h" + #include "GLSL.ext.NV.h" + } +} +const char* GlslStd450DebugNames[spv::GLSLstd450Count]; + +namespace spv { + +static const char* GLSLextAMDGetDebugNames(const char*, unsigned); +static const char* GLSLextNVGetDebugNames(const char*, unsigned); + +static void Kill(std::ostream& out, const char* message) +{ + out << std::endl << "Disassembly failed: " << message << std::endl; + exit(1); +} + +// used to identify the extended instruction library imported when printing +enum ExtInstSet { + GLSL450Inst, + GLSLextAMDInst, + GLSLextNVInst, + OpenCLExtInst, + NonSemanticDebugPrintfExtInst, +}; + +// Container class for a single instance of a SPIR-V stream, with methods for disassembly. +class SpirvStream { +public: + SpirvStream(std::ostream& out, const std::vector& stream) : out(out), stream(stream), word(0), nextNestedControl(0) { } + virtual ~SpirvStream() { } + + void validate(); + void processInstructions(); + +protected: + SpirvStream(const SpirvStream&); + SpirvStream& operator=(const SpirvStream&); + Op getOpCode(int id) const { return idInstruction[id] ? (Op)(stream[idInstruction[id]] & OpCodeMask) : OpNop; } + + // Output methods + void outputIndent(); + void formatId(Id id, std::stringstream&); + void outputResultId(Id id); + void outputTypeId(Id id); + void outputId(Id id); + void outputMask(OperandClass operandClass, unsigned mask); + void disassembleImmediates(int numOperands); + void disassembleIds(int numOperands); + int disassembleString(); + void disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands); + + // Data + std::ostream& out; // where to write the disassembly + const std::vector& stream; // the actual word stream + int size; // the size of the word stream + int word; // the next word of the stream to read + + // map each to the instruction that created it + Id bound; + std::vector idInstruction; // the word offset into the stream where the instruction for result [id] starts; 0 if not yet seen (forward reference or function parameter) + + std::vector idDescriptor; // the best text string known for explaining the + + // schema + unsigned int schema; + + // stack of structured-merge points + std::stack nestedControl; + Id nextNestedControl; // need a slight delay for when we are nested +}; + +void SpirvStream::validate() +{ + size = (int)stream.size(); + if (size < 4) + Kill(out, "stream is too short"); + + // Magic number + if (stream[word++] != MagicNumber) { + out << "Bad magic number"; + return; + } + + // Version + out << "// Module Version " << std::hex << stream[word++] << std::endl; + + // Generator's magic number + out << "// Generated by (magic number): " << std::hex << stream[word++] << std::dec << std::endl; + + // Result bound + bound = stream[word++]; + idInstruction.resize(bound); + idDescriptor.resize(bound); + out << "// Id's are bound by " << bound << std::endl; + out << std::endl; + + // Reserved schema, must be 0 for now + schema = stream[word++]; + if (schema != 0) + Kill(out, "bad schema, must be 0"); +} + +// Loop over all the instructions, in order, processing each. +// Boiler plate for each is handled here directly, the rest is dispatched. +void SpirvStream::processInstructions() +{ + // Instructions + while (word < size) { + int instructionStart = word; + + // Instruction wordCount and opcode + unsigned int firstWord = stream[word]; + unsigned wordCount = firstWord >> WordCountShift; + Op opCode = (Op)(firstWord & OpCodeMask); + int nextInst = word + wordCount; + ++word; + + // Presence of full instruction + if (nextInst > size) + Kill(out, "stream instruction terminated too early"); + + // Base for computing number of operands; will be updated as more is learned + unsigned numOperands = wordCount - 1; + + // Type + Id typeId = 0; + if (InstructionDesc[opCode].hasType()) { + typeId = stream[word++]; + --numOperands; + } + + // Result + Id resultId = 0; + if (InstructionDesc[opCode].hasResult()) { + resultId = stream[word++]; + --numOperands; + + // save instruction for future reference + idInstruction[resultId] = instructionStart; + } + + outputResultId(resultId); + outputTypeId(typeId); + outputIndent(); + + // Hand off the Op and all its operands + disassembleInstruction(resultId, typeId, opCode, numOperands); + if (word != nextInst) { + out << " ERROR, incorrect number of operands consumed. At " << word << " instead of " << nextInst << " instruction start was " << instructionStart; + word = nextInst; + } + out << std::endl; + } +} + +void SpirvStream::outputIndent() +{ + for (int i = 0; i < (int)nestedControl.size(); ++i) + out << " "; +} + +void SpirvStream::formatId(Id id, std::stringstream& idStream) +{ + if (id != 0) { + // On instructions with no IDs, this is called with "0", which does not + // have to be within ID bounds on null shaders. + if (id >= bound) + Kill(out, "Bad "); + + idStream << id; + if (idDescriptor[id].size() > 0) + idStream << "(" << idDescriptor[id] << ")"; + } +} + +void SpirvStream::outputResultId(Id id) +{ + const int width = 16; + std::stringstream idStream; + formatId(id, idStream); + out << std::setw(width) << std::right << idStream.str(); + if (id != 0) + out << ":"; + else + out << " "; + + if (nestedControl.size() && id == nestedControl.top()) + nestedControl.pop(); +} + +void SpirvStream::outputTypeId(Id id) +{ + const int width = 12; + std::stringstream idStream; + formatId(id, idStream); + out << std::setw(width) << std::right << idStream.str() << " "; +} + +void SpirvStream::outputId(Id id) +{ + if (id >= bound) + Kill(out, "Bad "); + + out << id; + if (idDescriptor[id].size() > 0) + out << "(" << idDescriptor[id] << ")"; +} + +void SpirvStream::outputMask(OperandClass operandClass, unsigned mask) +{ + if (mask == 0) + out << "None"; + else { + for (int m = 0; m < OperandClassParams[operandClass].ceiling; ++m) { + if (mask & (1 << m)) + out << OperandClassParams[operandClass].getName(m) << " "; + } + } +} + +void SpirvStream::disassembleImmediates(int numOperands) +{ + for (int i = 0; i < numOperands; ++i) { + out << stream[word++]; + if (i < numOperands - 1) + out << " "; + } +} + +void SpirvStream::disassembleIds(int numOperands) +{ + for (int i = 0; i < numOperands; ++i) { + outputId(stream[word++]); + if (i < numOperands - 1) + out << " "; + } +} + +// return the number of operands consumed by the string +int SpirvStream::disassembleString() +{ + int startWord = word; + + out << " \""; + + const char* wordString; + bool done = false; + do { + unsigned int content = stream[word]; + wordString = (const char*)&content; + for (int charCount = 0; charCount < 4; ++charCount) { + if (*wordString == 0) { + done = true; + break; + } + out << *(wordString++); + } + ++word; + } while (! done); + + out << "\""; + + return word - startWord; +} + +void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands) +{ + // Process the opcode + + out << (OpcodeString(opCode) + 2); // leave out the "Op" + + if (opCode == OpLoopMerge || opCode == OpSelectionMerge) + nextNestedControl = stream[word]; + else if (opCode == OpBranchConditional || opCode == OpSwitch) { + if (nextNestedControl) { + nestedControl.push(nextNestedControl); + nextNestedControl = 0; + } + } else if (opCode == OpExtInstImport) { + idDescriptor[resultId] = (const char*)(&stream[word]); + } + else { + if (resultId != 0 && idDescriptor[resultId].size() == 0) { + switch (opCode) { + case OpTypeInt: + switch (stream[word]) { + case 8: idDescriptor[resultId] = "int8_t"; break; + case 16: idDescriptor[resultId] = "int16_t"; break; + default: assert(0); // fallthrough + case 32: idDescriptor[resultId] = "int"; break; + case 64: idDescriptor[resultId] = "int64_t"; break; + } + break; + case OpTypeFloat: + switch (stream[word]) { + case 16: idDescriptor[resultId] = "float16_t"; break; + default: assert(0); // fallthrough + case 32: idDescriptor[resultId] = "float"; break; + case 64: idDescriptor[resultId] = "float64_t"; break; + } + break; + case OpTypeBool: + idDescriptor[resultId] = "bool"; + break; + case OpTypeStruct: + idDescriptor[resultId] = "struct"; + break; + case OpTypePointer: + idDescriptor[resultId] = "ptr"; + break; + case OpTypeVector: + if (idDescriptor[stream[word]].size() > 0) { + idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 1); + if (strstr(idDescriptor[stream[word]].c_str(), "8")) { + idDescriptor[resultId].append("8"); + } + if (strstr(idDescriptor[stream[word]].c_str(), "16")) { + idDescriptor[resultId].append("16"); + } + if (strstr(idDescriptor[stream[word]].c_str(), "64")) { + idDescriptor[resultId].append("64"); + } + } + idDescriptor[resultId].append("vec"); + switch (stream[word + 1]) { + case 2: idDescriptor[resultId].append("2"); break; + case 3: idDescriptor[resultId].append("3"); break; + case 4: idDescriptor[resultId].append("4"); break; + case 8: idDescriptor[resultId].append("8"); break; + case 16: idDescriptor[resultId].append("16"); break; + case 32: idDescriptor[resultId].append("32"); break; + default: break; + } + break; + default: + break; + } + } + } + + // Process the operands. Note, a new context-dependent set could be + // swapped in mid-traversal. + + // Handle images specially, so can put out helpful strings. + if (opCode == OpTypeImage) { + out << " "; + disassembleIds(1); + out << " " << DimensionString((Dim)stream[word++]); + out << (stream[word++] != 0 ? " depth" : ""); + out << (stream[word++] != 0 ? " array" : ""); + out << (stream[word++] != 0 ? " multi-sampled" : ""); + switch (stream[word++]) { + case 0: out << " runtime"; break; + case 1: out << " sampled"; break; + case 2: out << " nonsampled"; break; + } + out << " format:" << ImageFormatString((ImageFormat)stream[word++]); + + if (numOperands == 8) { + out << " " << AccessQualifierString(stream[word++]); + } + return; + } + + // Handle all the parameterized operands + for (int op = 0; op < InstructionDesc[opCode].operands.getNum() && numOperands > 0; ++op) { + out << " "; + OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op); + switch (operandClass) { + case OperandId: + case OperandScope: + case OperandMemorySemantics: + disassembleIds(1); + --numOperands; + // Get names for printing "(XXX)" for readability, *after* this id + if (opCode == OpName) + idDescriptor[stream[word - 1]] = (const char*)(&stream[word]); + break; + case OperandVariableIds: + disassembleIds(numOperands); + return; + case OperandImageOperands: + outputMask(OperandImageOperands, stream[word++]); + --numOperands; + disassembleIds(numOperands); + return; + case OperandOptionalLiteral: + case OperandVariableLiterals: + if ((opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn) || + (opCode == OpMemberDecorate && stream[word - 1] == DecorationBuiltIn)) { + out << BuiltInString(stream[word++]); + --numOperands; + ++op; + } + disassembleImmediates(numOperands); + return; + case OperandVariableIdLiteral: + while (numOperands > 0) { + out << std::endl; + outputResultId(0); + outputTypeId(0); + outputIndent(); + out << " Type "; + disassembleIds(1); + out << ", member "; + disassembleImmediates(1); + numOperands -= 2; + } + return; + case OperandVariableLiteralId: + while (numOperands > 0) { + out << std::endl; + outputResultId(0); + outputTypeId(0); + outputIndent(); + out << " case "; + disassembleImmediates(1); + out << ": "; + disassembleIds(1); + numOperands -= 2; + } + return; + case OperandLiteralNumber: + disassembleImmediates(1); + --numOperands; + if (opCode == OpExtInst) { + ExtInstSet extInstSet = GLSL450Inst; + const char* name = idDescriptor[stream[word - 2]].c_str(); + if (strcmp("OpenCL.std", name) == 0) { + extInstSet = OpenCLExtInst; + } else if (strcmp("OpenCL.DebugInfo.100", name) == 0) { + extInstSet = OpenCLExtInst; + } else if (strcmp("NonSemantic.DebugPrintf", name) == 0) { + extInstSet = NonSemanticDebugPrintfExtInst; + } else if (strcmp(spv::E_SPV_AMD_shader_ballot, name) == 0 || + strcmp(spv::E_SPV_AMD_shader_trinary_minmax, name) == 0 || + strcmp(spv::E_SPV_AMD_shader_explicit_vertex_parameter, name) == 0 || + strcmp(spv::E_SPV_AMD_gcn_shader, name) == 0) { + extInstSet = GLSLextAMDInst; + } else if (strcmp(spv::E_SPV_NV_sample_mask_override_coverage, name) == 0 || + strcmp(spv::E_SPV_NV_geometry_shader_passthrough, name) == 0 || + strcmp(spv::E_SPV_NV_viewport_array2, name) == 0 || + strcmp(spv::E_SPV_NVX_multiview_per_view_attributes, name) == 0 || + strcmp(spv::E_SPV_NV_fragment_shader_barycentric, name) == 0 || + strcmp(spv::E_SPV_NV_mesh_shader, name) == 0) { + extInstSet = GLSLextNVInst; + } + unsigned entrypoint = stream[word - 1]; + if (extInstSet == GLSL450Inst) { + if (entrypoint < GLSLstd450Count) { + out << "(" << GlslStd450DebugNames[entrypoint] << ")"; + } + } else if (extInstSet == GLSLextAMDInst) { + out << "(" << GLSLextAMDGetDebugNames(name, entrypoint) << ")"; + } + else if (extInstSet == GLSLextNVInst) { + out << "(" << GLSLextNVGetDebugNames(name, entrypoint) << ")"; + } else if (extInstSet == NonSemanticDebugPrintfExtInst) { + out << "(DebugPrintf)"; + } + } + break; + case OperandOptionalLiteralString: + case OperandLiteralString: + numOperands -= disassembleString(); + break; + case OperandMemoryAccess: + outputMask(OperandMemoryAccess, stream[word++]); + --numOperands; + // Aligned is the only memory access operand that uses an immediate + // value, and it is also the first operand that uses a value at all. + if (stream[word-1] & MemoryAccessAlignedMask) { + disassembleImmediates(1); + numOperands--; + if (numOperands) + out << " "; + } + disassembleIds(numOperands); + return; + default: + assert(operandClass >= OperandSource && operandClass < OperandOpcode); + + if (OperandClassParams[operandClass].bitmask) + outputMask(operandClass, stream[word++]); + else + out << OperandClassParams[operandClass].getName(stream[word++]); + --numOperands; + + break; + } + } + + return; +} + +static void GLSLstd450GetDebugNames(const char** names) +{ + for (int i = 0; i < GLSLstd450Count; ++i) + names[i] = "Unknown"; + + names[GLSLstd450Round] = "Round"; + names[GLSLstd450RoundEven] = "RoundEven"; + names[GLSLstd450Trunc] = "Trunc"; + names[GLSLstd450FAbs] = "FAbs"; + names[GLSLstd450SAbs] = "SAbs"; + names[GLSLstd450FSign] = "FSign"; + names[GLSLstd450SSign] = "SSign"; + names[GLSLstd450Floor] = "Floor"; + names[GLSLstd450Ceil] = "Ceil"; + names[GLSLstd450Fract] = "Fract"; + names[GLSLstd450Radians] = "Radians"; + names[GLSLstd450Degrees] = "Degrees"; + names[GLSLstd450Sin] = "Sin"; + names[GLSLstd450Cos] = "Cos"; + names[GLSLstd450Tan] = "Tan"; + names[GLSLstd450Asin] = "Asin"; + names[GLSLstd450Acos] = "Acos"; + names[GLSLstd450Atan] = "Atan"; + names[GLSLstd450Sinh] = "Sinh"; + names[GLSLstd450Cosh] = "Cosh"; + names[GLSLstd450Tanh] = "Tanh"; + names[GLSLstd450Asinh] = "Asinh"; + names[GLSLstd450Acosh] = "Acosh"; + names[GLSLstd450Atanh] = "Atanh"; + names[GLSLstd450Atan2] = "Atan2"; + names[GLSLstd450Pow] = "Pow"; + names[GLSLstd450Exp] = "Exp"; + names[GLSLstd450Log] = "Log"; + names[GLSLstd450Exp2] = "Exp2"; + names[GLSLstd450Log2] = "Log2"; + names[GLSLstd450Sqrt] = "Sqrt"; + names[GLSLstd450InverseSqrt] = "InverseSqrt"; + names[GLSLstd450Determinant] = "Determinant"; + names[GLSLstd450MatrixInverse] = "MatrixInverse"; + names[GLSLstd450Modf] = "Modf"; + names[GLSLstd450ModfStruct] = "ModfStruct"; + names[GLSLstd450FMin] = "FMin"; + names[GLSLstd450SMin] = "SMin"; + names[GLSLstd450UMin] = "UMin"; + names[GLSLstd450FMax] = "FMax"; + names[GLSLstd450SMax] = "SMax"; + names[GLSLstd450UMax] = "UMax"; + names[GLSLstd450FClamp] = "FClamp"; + names[GLSLstd450SClamp] = "SClamp"; + names[GLSLstd450UClamp] = "UClamp"; + names[GLSLstd450FMix] = "FMix"; + names[GLSLstd450Step] = "Step"; + names[GLSLstd450SmoothStep] = "SmoothStep"; + names[GLSLstd450Fma] = "Fma"; + names[GLSLstd450Frexp] = "Frexp"; + names[GLSLstd450FrexpStruct] = "FrexpStruct"; + names[GLSLstd450Ldexp] = "Ldexp"; + names[GLSLstd450PackSnorm4x8] = "PackSnorm4x8"; + names[GLSLstd450PackUnorm4x8] = "PackUnorm4x8"; + names[GLSLstd450PackSnorm2x16] = "PackSnorm2x16"; + names[GLSLstd450PackUnorm2x16] = "PackUnorm2x16"; + names[GLSLstd450PackHalf2x16] = "PackHalf2x16"; + names[GLSLstd450PackDouble2x32] = "PackDouble2x32"; + names[GLSLstd450UnpackSnorm2x16] = "UnpackSnorm2x16"; + names[GLSLstd450UnpackUnorm2x16] = "UnpackUnorm2x16"; + names[GLSLstd450UnpackHalf2x16] = "UnpackHalf2x16"; + names[GLSLstd450UnpackSnorm4x8] = "UnpackSnorm4x8"; + names[GLSLstd450UnpackUnorm4x8] = "UnpackUnorm4x8"; + names[GLSLstd450UnpackDouble2x32] = "UnpackDouble2x32"; + names[GLSLstd450Length] = "Length"; + names[GLSLstd450Distance] = "Distance"; + names[GLSLstd450Cross] = "Cross"; + names[GLSLstd450Normalize] = "Normalize"; + names[GLSLstd450FaceForward] = "FaceForward"; + names[GLSLstd450Reflect] = "Reflect"; + names[GLSLstd450Refract] = "Refract"; + names[GLSLstd450FindILsb] = "FindILsb"; + names[GLSLstd450FindSMsb] = "FindSMsb"; + names[GLSLstd450FindUMsb] = "FindUMsb"; + names[GLSLstd450InterpolateAtCentroid] = "InterpolateAtCentroid"; + names[GLSLstd450InterpolateAtSample] = "InterpolateAtSample"; + names[GLSLstd450InterpolateAtOffset] = "InterpolateAtOffset"; + names[GLSLstd450NMin] = "NMin"; + names[GLSLstd450NMax] = "NMax"; + names[GLSLstd450NClamp] = "NClamp"; +} + +static const char* GLSLextAMDGetDebugNames(const char* name, unsigned entrypoint) +{ + if (strcmp(name, spv::E_SPV_AMD_shader_ballot) == 0) { + switch (entrypoint) { + case SwizzleInvocationsAMD: return "SwizzleInvocationsAMD"; + case SwizzleInvocationsMaskedAMD: return "SwizzleInvocationsMaskedAMD"; + case WriteInvocationAMD: return "WriteInvocationAMD"; + case MbcntAMD: return "MbcntAMD"; + default: return "Bad"; + } + } else if (strcmp(name, spv::E_SPV_AMD_shader_trinary_minmax) == 0) { + switch (entrypoint) { + case FMin3AMD: return "FMin3AMD"; + case UMin3AMD: return "UMin3AMD"; + case SMin3AMD: return "SMin3AMD"; + case FMax3AMD: return "FMax3AMD"; + case UMax3AMD: return "UMax3AMD"; + case SMax3AMD: return "SMax3AMD"; + case FMid3AMD: return "FMid3AMD"; + case UMid3AMD: return "UMid3AMD"; + case SMid3AMD: return "SMid3AMD"; + default: return "Bad"; + } + } else if (strcmp(name, spv::E_SPV_AMD_shader_explicit_vertex_parameter) == 0) { + switch (entrypoint) { + case InterpolateAtVertexAMD: return "InterpolateAtVertexAMD"; + default: return "Bad"; + } + } + else if (strcmp(name, spv::E_SPV_AMD_gcn_shader) == 0) { + switch (entrypoint) { + case CubeFaceIndexAMD: return "CubeFaceIndexAMD"; + case CubeFaceCoordAMD: return "CubeFaceCoordAMD"; + case TimeAMD: return "TimeAMD"; + default: + break; + } + } + + return "Bad"; +} + +static const char* GLSLextNVGetDebugNames(const char* name, unsigned entrypoint) +{ + if (strcmp(name, spv::E_SPV_NV_sample_mask_override_coverage) == 0 || + strcmp(name, spv::E_SPV_NV_geometry_shader_passthrough) == 0 || + strcmp(name, spv::E_ARB_shader_viewport_layer_array) == 0 || + strcmp(name, spv::E_SPV_NV_viewport_array2) == 0 || + strcmp(name, spv::E_SPV_NVX_multiview_per_view_attributes) == 0 || + strcmp(name, spv::E_SPV_NV_fragment_shader_barycentric) == 0 || + strcmp(name, spv::E_SPV_NV_mesh_shader) == 0 || + strcmp(name, spv::E_SPV_NV_shader_image_footprint) == 0) { + switch (entrypoint) { + // NV builtins + case BuiltInViewportMaskNV: return "ViewportMaskNV"; + case BuiltInSecondaryPositionNV: return "SecondaryPositionNV"; + case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case BuiltInPositionPerViewNV: return "PositionPerViewNV"; + case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case BuiltInBaryCoordNV: return "BaryCoordNV"; + case BuiltInBaryCoordNoPerspNV: return "BaryCoordNoPerspNV"; + case BuiltInTaskCountNV: return "TaskCountNV"; + case BuiltInPrimitiveCountNV: return "PrimitiveCountNV"; + case BuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case BuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case BuiltInCullDistancePerViewNV: return "CullDistancePerViewNV"; + case BuiltInLayerPerViewNV: return "LayerPerViewNV"; + case BuiltInMeshViewCountNV: return "MeshViewCountNV"; + case BuiltInMeshViewIndicesNV: return "MeshViewIndicesNV"; + + // NV Capabilities + case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV"; + case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV"; + case CapabilityPerViewAttributesNV: return "PerViewAttributesNV"; + case CapabilityFragmentBarycentricNV: return "FragmentBarycentricNV"; + case CapabilityMeshShadingNV: return "MeshShadingNV"; + case CapabilityImageFootprintNV: return "ImageFootprintNV"; + case CapabilitySampleMaskOverrideCoverageNV:return "SampleMaskOverrideCoverageNV"; + + // NV Decorations + case DecorationOverrideCoverageNV: return "OverrideCoverageNV"; + case DecorationPassthroughNV: return "PassthroughNV"; + case DecorationViewportRelativeNV: return "ViewportRelativeNV"; + case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV"; + case DecorationPerVertexNV: return "PerVertexNV"; + case DecorationPerPrimitiveNV: return "PerPrimitiveNV"; + case DecorationPerViewNV: return "PerViewNV"; + case DecorationPerTaskNV: return "PerTaskNV"; + + default: return "Bad"; + } + } + return "Bad"; +} + +void Disassemble(std::ostream& out, const std::vector& stream) +{ + SpirvStream SpirvStream(out, stream); + spv::Parameterize(); + GLSLstd450GetDebugNames(GlslStd450DebugNames); + SpirvStream.validate(); + SpirvStream.processInstructions(); +} + +}; // end namespace spv diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/disassemble.h b/armorcore/tools/to_spirv/glslang/SPIRV/disassemble.h new file mode 100644 index 000000000..b6a463577 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/disassemble.h @@ -0,0 +1,53 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Disassembler for SPIR-V. +// + +#pragma once +#ifndef disassembler_H +#define disassembler_H + +#include +#include + +namespace spv { + + // disassemble with glslang custom disassembler + void Disassemble(std::ostream& out, const std::vector&); + +} // end namespace spv + +#endif // disassembler_H diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/doc.cpp b/armorcore/tools/to_spirv/glslang/SPIRV/doc.cpp new file mode 100644 index 000000000..b1f2b820d --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/doc.cpp @@ -0,0 +1,2888 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// 1) Programmatically fill in instruction/operand information. +// This can be used for disassembly, printing documentation, etc. +// +// 2) Print documentation from this parameterization. +// + +#include "doc.h" + +#include +#include +#include + +namespace spv { + extern "C" { + // Include C-based headers that don't have a namespace + #include "GLSL.ext.KHR.h" + #include "GLSL.ext.EXT.h" + #include "GLSL.ext.AMD.h" + #include "GLSL.ext.NV.h" + } +} + +namespace spv { + +// +// Whole set of functions that translate enumerants to their text strings for +// the specification (or their sanitized versions for auto-generating the +// spirv headers. +// +// Also, for masks the ceilings are declared next to these, to help keep them in sync. +// Ceilings should be +// - one more than the maximum value an enumerant takes on, for non-mask enumerants +// (for non-sparse enums, this is the number of enumerants) +// - the number of bits consumed by the set of masks +// (for non-sparse mask enums, this is the number of enumerants) +// + +const char* SourceString(int source) +{ + switch (source) { + case 0: return "Unknown"; + case 1: return "ESSL"; + case 2: return "GLSL"; + case 3: return "OpenCL_C"; + case 4: return "OpenCL_CPP"; + case 5: return "HLSL"; + + default: return "Bad"; + } +} + +const char* ExecutionModelString(int model) +{ + switch (model) { + case 0: return "Vertex"; + case 1: return "TessellationControl"; + case 2: return "TessellationEvaluation"; + case 3: return "Geometry"; + case 4: return "Fragment"; + case 5: return "GLCompute"; + case 6: return "Kernel"; + case ExecutionModelTaskNV: return "TaskNV"; + case ExecutionModelMeshNV: return "MeshNV"; + + default: return "Bad"; + + case ExecutionModelRayGenerationKHR: return "RayGenerationKHR"; + case ExecutionModelIntersectionKHR: return "IntersectionKHR"; + case ExecutionModelAnyHitKHR: return "AnyHitKHR"; + case ExecutionModelClosestHitKHR: return "ClosestHitKHR"; + case ExecutionModelMissKHR: return "MissKHR"; + case ExecutionModelCallableKHR: return "CallableKHR"; + } +} + +const char* AddressingString(int addr) +{ + switch (addr) { + case 0: return "Logical"; + case 1: return "Physical32"; + case 2: return "Physical64"; + + case AddressingModelPhysicalStorageBuffer64EXT: return "PhysicalStorageBuffer64EXT"; + + default: return "Bad"; + } +} + +const char* MemoryString(int mem) +{ + switch (mem) { + case MemoryModelSimple: return "Simple"; + case MemoryModelGLSL450: return "GLSL450"; + case MemoryModelOpenCL: return "OpenCL"; + case MemoryModelVulkanKHR: return "VulkanKHR"; + + default: return "Bad"; + } +} + +const int ExecutionModeCeiling = 33; + +const char* ExecutionModeString(int mode) +{ + switch (mode) { + case 0: return "Invocations"; + case 1: return "SpacingEqual"; + case 2: return "SpacingFractionalEven"; + case 3: return "SpacingFractionalOdd"; + case 4: return "VertexOrderCw"; + case 5: return "VertexOrderCcw"; + case 6: return "PixelCenterInteger"; + case 7: return "OriginUpperLeft"; + case 8: return "OriginLowerLeft"; + case 9: return "EarlyFragmentTests"; + case 10: return "PointMode"; + case 11: return "Xfb"; + case 12: return "DepthReplacing"; + case 13: return "Bad"; + case 14: return "DepthGreater"; + case 15: return "DepthLess"; + case 16: return "DepthUnchanged"; + case 17: return "LocalSize"; + case 18: return "LocalSizeHint"; + case 19: return "InputPoints"; + case 20: return "InputLines"; + case 21: return "InputLinesAdjacency"; + case 22: return "Triangles"; + case 23: return "InputTrianglesAdjacency"; + case 24: return "Quads"; + case 25: return "Isolines"; + case 26: return "OutputVertices"; + case 27: return "OutputPoints"; + case 28: return "OutputLineStrip"; + case 29: return "OutputTriangleStrip"; + case 30: return "VecTypeHint"; + case 31: return "ContractionOff"; + case 32: return "Bad"; + + case 4446: return "PostDepthCoverage"; + + case ExecutionModeOutputLinesNV: return "OutputLinesNV"; + case ExecutionModeOutputPrimitivesNV: return "OutputPrimitivesNV"; + case ExecutionModeOutputTrianglesNV: return "OutputTrianglesNV"; + case ExecutionModeDerivativeGroupQuadsNV: return "DerivativeGroupQuadsNV"; + case ExecutionModeDerivativeGroupLinearNV: return "DerivativeGroupLinearNV"; + + case ExecutionModePixelInterlockOrderedEXT: return "PixelInterlockOrderedEXT"; + case ExecutionModePixelInterlockUnorderedEXT: return "PixelInterlockUnorderedEXT"; + case ExecutionModeSampleInterlockOrderedEXT: return "SampleInterlockOrderedEXT"; + case ExecutionModeSampleInterlockUnorderedEXT: return "SampleInterlockUnorderedEXT"; + case ExecutionModeShadingRateInterlockOrderedEXT: return "ShadingRateInterlockOrderedEXT"; + case ExecutionModeShadingRateInterlockUnorderedEXT: return "ShadingRateInterlockUnorderedEXT"; + + case ExecutionModeCeiling: + default: return "Bad"; + } +} + +const char* StorageClassString(int StorageClass) +{ + switch (StorageClass) { + case 0: return "UniformConstant"; + case 1: return "Input"; + case 2: return "Uniform"; + case 3: return "Output"; + case 4: return "Workgroup"; + case 5: return "CrossWorkgroup"; + case 6: return "Private"; + case 7: return "Function"; + case 8: return "Generic"; + case 9: return "PushConstant"; + case 10: return "AtomicCounter"; + case 11: return "Image"; + case 12: return "StorageBuffer"; + + case StorageClassRayPayloadKHR: return "RayPayloadKHR"; + case StorageClassHitAttributeKHR: return "HitAttributeKHR"; + case StorageClassIncomingRayPayloadKHR: return "IncomingRayPayloadKHR"; + case StorageClassShaderRecordBufferKHR: return "ShaderRecordBufferKHR"; + case StorageClassCallableDataKHR: return "CallableDataKHR"; + case StorageClassIncomingCallableDataKHR: return "IncomingCallableDataKHR"; + + case StorageClassPhysicalStorageBufferEXT: return "PhysicalStorageBufferEXT"; + + default: return "Bad"; + } +} + +const int DecorationCeiling = 45; + +const char* DecorationString(int decoration) +{ + switch (decoration) { + case 0: return "RelaxedPrecision"; + case 1: return "SpecId"; + case 2: return "Block"; + case 3: return "BufferBlock"; + case 4: return "RowMajor"; + case 5: return "ColMajor"; + case 6: return "ArrayStride"; + case 7: return "MatrixStride"; + case 8: return "GLSLShared"; + case 9: return "GLSLPacked"; + case 10: return "CPacked"; + case 11: return "BuiltIn"; + case 12: return "Bad"; + case 13: return "NoPerspective"; + case 14: return "Flat"; + case 15: return "Patch"; + case 16: return "Centroid"; + case 17: return "Sample"; + case 18: return "Invariant"; + case 19: return "Restrict"; + case 20: return "Aliased"; + case 21: return "Volatile"; + case 22: return "Constant"; + case 23: return "Coherent"; + case 24: return "NonWritable"; + case 25: return "NonReadable"; + case 26: return "Uniform"; + case 27: return "Bad"; + case 28: return "SaturatedConversion"; + case 29: return "Stream"; + case 30: return "Location"; + case 31: return "Component"; + case 32: return "Index"; + case 33: return "Binding"; + case 34: return "DescriptorSet"; + case 35: return "Offset"; + case 36: return "XfbBuffer"; + case 37: return "XfbStride"; + case 38: return "FuncParamAttr"; + case 39: return "FP Rounding Mode"; + case 40: return "FP Fast Math Mode"; + case 41: return "Linkage Attributes"; + case 42: return "NoContraction"; + case 43: return "InputAttachmentIndex"; + case 44: return "Alignment"; + + case DecorationCeiling: + default: return "Bad"; + + case DecorationExplicitInterpAMD: return "ExplicitInterpAMD"; + case DecorationOverrideCoverageNV: return "OverrideCoverageNV"; + case DecorationPassthroughNV: return "PassthroughNV"; + case DecorationViewportRelativeNV: return "ViewportRelativeNV"; + case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV"; + case DecorationPerPrimitiveNV: return "PerPrimitiveNV"; + case DecorationPerViewNV: return "PerViewNV"; + case DecorationPerTaskNV: return "PerTaskNV"; + case DecorationPerVertexNV: return "PerVertexNV"; + + case DecorationNonUniformEXT: return "DecorationNonUniformEXT"; + case DecorationHlslCounterBufferGOOGLE: return "DecorationHlslCounterBufferGOOGLE"; + case DecorationHlslSemanticGOOGLE: return "DecorationHlslSemanticGOOGLE"; + case DecorationRestrictPointerEXT: return "DecorationRestrictPointerEXT"; + case DecorationAliasedPointerEXT: return "DecorationAliasedPointerEXT"; + } +} + +const char* BuiltInString(int builtIn) +{ + switch (builtIn) { + case 0: return "Position"; + case 1: return "PointSize"; + case 2: return "Bad"; + case 3: return "ClipDistance"; + case 4: return "CullDistance"; + case 5: return "VertexId"; + case 6: return "InstanceId"; + case 7: return "PrimitiveId"; + case 8: return "InvocationId"; + case 9: return "Layer"; + case 10: return "ViewportIndex"; + case 11: return "TessLevelOuter"; + case 12: return "TessLevelInner"; + case 13: return "TessCoord"; + case 14: return "PatchVertices"; + case 15: return "FragCoord"; + case 16: return "PointCoord"; + case 17: return "FrontFacing"; + case 18: return "SampleId"; + case 19: return "SamplePosition"; + case 20: return "SampleMask"; + case 21: return "Bad"; + case 22: return "FragDepth"; + case 23: return "HelperInvocation"; + case 24: return "NumWorkgroups"; + case 25: return "WorkgroupSize"; + case 26: return "WorkgroupId"; + case 27: return "LocalInvocationId"; + case 28: return "GlobalInvocationId"; + case 29: return "LocalInvocationIndex"; + case 30: return "WorkDim"; + case 31: return "GlobalSize"; + case 32: return "EnqueuedWorkgroupSize"; + case 33: return "GlobalOffset"; + case 34: return "GlobalLinearId"; + case 35: return "Bad"; + case 36: return "SubgroupSize"; + case 37: return "SubgroupMaxSize"; + case 38: return "NumSubgroups"; + case 39: return "NumEnqueuedSubgroups"; + case 40: return "SubgroupId"; + case 41: return "SubgroupLocalInvocationId"; + case 42: return "VertexIndex"; // TBD: put next to VertexId? + case 43: return "InstanceIndex"; // TBD: put next to InstanceId? + + case 4416: return "SubgroupEqMaskKHR"; + case 4417: return "SubgroupGeMaskKHR"; + case 4418: return "SubgroupGtMaskKHR"; + case 4419: return "SubgroupLeMaskKHR"; + case 4420: return "SubgroupLtMaskKHR"; + case 4438: return "DeviceIndex"; + case 4440: return "ViewIndex"; + case 4424: return "BaseVertex"; + case 4425: return "BaseInstance"; + case 4426: return "DrawIndex"; + case 5014: return "FragStencilRefEXT"; + + case 4992: return "BaryCoordNoPerspAMD"; + case 4993: return "BaryCoordNoPerspCentroidAMD"; + case 4994: return "BaryCoordNoPerspSampleAMD"; + case 4995: return "BaryCoordSmoothAMD"; + case 4996: return "BaryCoordSmoothCentroidAMD"; + case 4997: return "BaryCoordSmoothSampleAMD"; + case 4998: return "BaryCoordPullModelAMD"; + case BuiltInLaunchIdKHR: return "LaunchIdKHR"; + case BuiltInLaunchSizeKHR: return "LaunchSizeKHR"; + case BuiltInWorldRayOriginKHR: return "WorldRayOriginKHR"; + case BuiltInWorldRayDirectionKHR: return "WorldRayDirectionKHR"; + case BuiltInObjectRayOriginKHR: return "ObjectRayOriginKHR"; + case BuiltInObjectRayDirectionKHR: return "ObjectRayDirectionKHR"; + case BuiltInRayTminKHR: return "RayTminKHR"; + case BuiltInRayTmaxKHR: return "RayTmaxKHR"; + case BuiltInInstanceCustomIndexKHR: return "InstanceCustomIndexKHR"; + case BuiltInRayGeometryIndexKHR: return "RayGeometryIndexKHR"; + case BuiltInObjectToWorldKHR: return "ObjectToWorldKHR"; + case BuiltInWorldToObjectKHR: return "WorldToObjectKHR"; + case BuiltInHitTKHR: return "HitTKHR"; + case BuiltInHitKindKHR: return "HitKindKHR"; + case BuiltInIncomingRayFlagsKHR: return "IncomingRayFlagsKHR"; + case BuiltInViewportMaskNV: return "ViewportMaskNV"; + case BuiltInSecondaryPositionNV: return "SecondaryPositionNV"; + case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case BuiltInPositionPerViewNV: return "PositionPerViewNV"; + case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; +// case BuiltInFragmentSizeNV: return "FragmentSizeNV"; // superseded by BuiltInFragSizeEXT +// case BuiltInInvocationsPerPixelNV: return "InvocationsPerPixelNV"; // superseded by BuiltInFragInvocationCountEXT + case BuiltInBaryCoordNV: return "BaryCoordNV"; + case BuiltInBaryCoordNoPerspNV: return "BaryCoordNoPerspNV"; + + case BuiltInFragSizeEXT: return "FragSizeEXT"; + case BuiltInFragInvocationCountEXT: return "FragInvocationCountEXT"; + + case 5264: return "FullyCoveredEXT"; + + case BuiltInTaskCountNV: return "TaskCountNV"; + case BuiltInPrimitiveCountNV: return "PrimitiveCountNV"; + case BuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case BuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case BuiltInCullDistancePerViewNV: return "CullDistancePerViewNV"; + case BuiltInLayerPerViewNV: return "LayerPerViewNV"; + case BuiltInMeshViewCountNV: return "MeshViewCountNV"; + case BuiltInMeshViewIndicesNV: return "MeshViewIndicesNV"; + case BuiltInWarpsPerSMNV: return "WarpsPerSMNV"; + case BuiltInSMCountNV: return "SMCountNV"; + case BuiltInWarpIDNV: return "WarpIDNV"; + case BuiltInSMIDNV: return "SMIDNV"; + + default: return "Bad"; + } +} + +const char* DimensionString(int dim) +{ + switch (dim) { + case 0: return "1D"; + case 1: return "2D"; + case 2: return "3D"; + case 3: return "Cube"; + case 4: return "Rect"; + case 5: return "Buffer"; + case 6: return "SubpassData"; + + default: return "Bad"; + } +} + +const char* SamplerAddressingModeString(int mode) +{ + switch (mode) { + case 0: return "None"; + case 1: return "ClampToEdge"; + case 2: return "Clamp"; + case 3: return "Repeat"; + case 4: return "RepeatMirrored"; + + default: return "Bad"; + } +} + +const char* SamplerFilterModeString(int mode) +{ + switch (mode) { + case 0: return "Nearest"; + case 1: return "Linear"; + + default: return "Bad"; + } +} + +const char* ImageFormatString(int format) +{ + switch (format) { + case 0: return "Unknown"; + + // ES/Desktop float + case 1: return "Rgba32f"; + case 2: return "Rgba16f"; + case 3: return "R32f"; + case 4: return "Rgba8"; + case 5: return "Rgba8Snorm"; + + // Desktop float + case 6: return "Rg32f"; + case 7: return "Rg16f"; + case 8: return "R11fG11fB10f"; + case 9: return "R16f"; + case 10: return "Rgba16"; + case 11: return "Rgb10A2"; + case 12: return "Rg16"; + case 13: return "Rg8"; + case 14: return "R16"; + case 15: return "R8"; + case 16: return "Rgba16Snorm"; + case 17: return "Rg16Snorm"; + case 18: return "Rg8Snorm"; + case 19: return "R16Snorm"; + case 20: return "R8Snorm"; + + // ES/Desktop int + case 21: return "Rgba32i"; + case 22: return "Rgba16i"; + case 23: return "Rgba8i"; + case 24: return "R32i"; + + // Desktop int + case 25: return "Rg32i"; + case 26: return "Rg16i"; + case 27: return "Rg8i"; + case 28: return "R16i"; + case 29: return "R8i"; + + // ES/Desktop uint + case 30: return "Rgba32ui"; + case 31: return "Rgba16ui"; + case 32: return "Rgba8ui"; + case 33: return "R32ui"; + + // Desktop uint + case 34: return "Rgb10a2ui"; + case 35: return "Rg32ui"; + case 36: return "Rg16ui"; + case 37: return "Rg8ui"; + case 38: return "R16ui"; + case 39: return "R8ui"; + + default: + return "Bad"; + } +} + +const char* ImageChannelOrderString(int format) +{ + switch (format) { + case 0: return "R"; + case 1: return "A"; + case 2: return "RG"; + case 3: return "RA"; + case 4: return "RGB"; + case 5: return "RGBA"; + case 6: return "BGRA"; + case 7: return "ARGB"; + case 8: return "Intensity"; + case 9: return "Luminance"; + case 10: return "Rx"; + case 11: return "RGx"; + case 12: return "RGBx"; + case 13: return "Depth"; + case 14: return "DepthStencil"; + case 15: return "sRGB"; + case 16: return "sRGBx"; + case 17: return "sRGBA"; + case 18: return "sBGRA"; + + default: + return "Bad"; + } +} + +const char* ImageChannelDataTypeString(int type) +{ + switch (type) + { + case 0: return "SnormInt8"; + case 1: return "SnormInt16"; + case 2: return "UnormInt8"; + case 3: return "UnormInt16"; + case 4: return "UnormShort565"; + case 5: return "UnormShort555"; + case 6: return "UnormInt101010"; + case 7: return "SignedInt8"; + case 8: return "SignedInt16"; + case 9: return "SignedInt32"; + case 10: return "UnsignedInt8"; + case 11: return "UnsignedInt16"; + case 12: return "UnsignedInt32"; + case 13: return "HalfFloat"; + case 14: return "Float"; + case 15: return "UnormInt24"; + case 16: return "UnormInt101010_2"; + + default: + return "Bad"; + } +} + +const int ImageOperandsCeiling = 14; + +const char* ImageOperandsString(int format) +{ + switch (format) { + case ImageOperandsBiasShift: return "Bias"; + case ImageOperandsLodShift: return "Lod"; + case ImageOperandsGradShift: return "Grad"; + case ImageOperandsConstOffsetShift: return "ConstOffset"; + case ImageOperandsOffsetShift: return "Offset"; + case ImageOperandsConstOffsetsShift: return "ConstOffsets"; + case ImageOperandsSampleShift: return "Sample"; + case ImageOperandsMinLodShift: return "MinLod"; + case ImageOperandsMakeTexelAvailableKHRShift: return "MakeTexelAvailableKHR"; + case ImageOperandsMakeTexelVisibleKHRShift: return "MakeTexelVisibleKHR"; + case ImageOperandsNonPrivateTexelKHRShift: return "NonPrivateTexelKHR"; + case ImageOperandsVolatileTexelKHRShift: return "VolatileTexelKHR"; + case ImageOperandsSignExtendShift: return "SignExtend"; + case ImageOperandsZeroExtendShift: return "ZeroExtend"; + + case ImageOperandsCeiling: + default: + return "Bad"; + } +} + +const char* FPFastMathString(int mode) +{ + switch (mode) { + case 0: return "NotNaN"; + case 1: return "NotInf"; + case 2: return "NSZ"; + case 3: return "AllowRecip"; + case 4: return "Fast"; + + default: return "Bad"; + } +} + +const char* FPRoundingModeString(int mode) +{ + switch (mode) { + case 0: return "RTE"; + case 1: return "RTZ"; + case 2: return "RTP"; + case 3: return "RTN"; + + default: return "Bad"; + } +} + +const char* LinkageTypeString(int type) +{ + switch (type) { + case 0: return "Export"; + case 1: return "Import"; + + default: return "Bad"; + } +} + +const char* FuncParamAttrString(int attr) +{ + switch (attr) { + case 0: return "Zext"; + case 1: return "Sext"; + case 2: return "ByVal"; + case 3: return "Sret"; + case 4: return "NoAlias"; + case 5: return "NoCapture"; + case 6: return "NoWrite"; + case 7: return "NoReadWrite"; + + default: return "Bad"; + } +} + +const char* AccessQualifierString(int attr) +{ + switch (attr) { + case 0: return "ReadOnly"; + case 1: return "WriteOnly"; + case 2: return "ReadWrite"; + + default: return "Bad"; + } +} + +const int SelectControlCeiling = 2; + +const char* SelectControlString(int cont) +{ + switch (cont) { + case 0: return "Flatten"; + case 1: return "DontFlatten"; + + case SelectControlCeiling: + default: return "Bad"; + } +} + +const int LoopControlCeiling = LoopControlPartialCountShift + 1; + +const char* LoopControlString(int cont) +{ + switch (cont) { + case LoopControlUnrollShift: return "Unroll"; + case LoopControlDontUnrollShift: return "DontUnroll"; + case LoopControlDependencyInfiniteShift: return "DependencyInfinite"; + case LoopControlDependencyLengthShift: return "DependencyLength"; + case LoopControlMinIterationsShift: return "MinIterations"; + case LoopControlMaxIterationsShift: return "MaxIterations"; + case LoopControlIterationMultipleShift: return "IterationMultiple"; + case LoopControlPeelCountShift: return "PeelCount"; + case LoopControlPartialCountShift: return "PartialCount"; + + case LoopControlCeiling: + default: return "Bad"; + } +} + +const int FunctionControlCeiling = 4; + +const char* FunctionControlString(int cont) +{ + switch (cont) { + case 0: return "Inline"; + case 1: return "DontInline"; + case 2: return "Pure"; + case 3: return "Const"; + + case FunctionControlCeiling: + default: return "Bad"; + } +} + +const char* MemorySemanticsString(int mem) +{ + // Note: No bits set (None) means "Relaxed" + switch (mem) { + case 0: return "Bad"; // Note: this is a placeholder for 'Consume' + case 1: return "Acquire"; + case 2: return "Release"; + case 3: return "AcquireRelease"; + case 4: return "SequentiallyConsistent"; + case 5: return "Bad"; // Note: reserved for future expansion + case 6: return "UniformMemory"; + case 7: return "SubgroupMemory"; + case 8: return "WorkgroupMemory"; + case 9: return "CrossWorkgroupMemory"; + case 10: return "AtomicCounterMemory"; + case 11: return "ImageMemory"; + + default: return "Bad"; + } +} + +const int MemoryAccessCeiling = 6; + +const char* MemoryAccessString(int mem) +{ + switch (mem) { + case MemoryAccessVolatileShift: return "Volatile"; + case MemoryAccessAlignedShift: return "Aligned"; + case MemoryAccessNontemporalShift: return "Nontemporal"; + case MemoryAccessMakePointerAvailableKHRShift: return "MakePointerAvailableKHR"; + case MemoryAccessMakePointerVisibleKHRShift: return "MakePointerVisibleKHR"; + case MemoryAccessNonPrivatePointerKHRShift: return "NonPrivatePointerKHR"; + + default: return "Bad"; + } +} + +const char* ScopeString(int mem) +{ + switch (mem) { + case 0: return "CrossDevice"; + case 1: return "Device"; + case 2: return "Workgroup"; + case 3: return "Subgroup"; + case 4: return "Invocation"; + + default: return "Bad"; + } +} + +const char* GroupOperationString(int gop) +{ + + switch (gop) + { + case GroupOperationReduce: return "Reduce"; + case GroupOperationInclusiveScan: return "InclusiveScan"; + case GroupOperationExclusiveScan: return "ExclusiveScan"; + case GroupOperationClusteredReduce: return "ClusteredReduce"; + case GroupOperationPartitionedReduceNV: return "PartitionedReduceNV"; + case GroupOperationPartitionedInclusiveScanNV: return "PartitionedInclusiveScanNV"; + case GroupOperationPartitionedExclusiveScanNV: return "PartitionedExclusiveScanNV"; + + default: return "Bad"; + } +} + +const char* KernelEnqueueFlagsString(int flag) +{ + switch (flag) + { + case 0: return "NoWait"; + case 1: return "WaitKernel"; + case 2: return "WaitWorkGroup"; + + default: return "Bad"; + } +} + +const char* KernelProfilingInfoString(int info) +{ + switch (info) + { + case 0: return "CmdExecTime"; + + default: return "Bad"; + } +} + +const char* CapabilityString(int info) +{ + switch (info) + { + case 0: return "Matrix"; + case 1: return "Shader"; + case 2: return "Geometry"; + case 3: return "Tessellation"; + case 4: return "Addresses"; + case 5: return "Linkage"; + case 6: return "Kernel"; + case 7: return "Vector16"; + case 8: return "Float16Buffer"; + case 9: return "Float16"; + case 10: return "Float64"; + case 11: return "Int64"; + case 12: return "Int64Atomics"; + case 13: return "ImageBasic"; + case 14: return "ImageReadWrite"; + case 15: return "ImageMipmap"; + case 16: return "Bad"; + case 17: return "Pipes"; + case 18: return "Groups"; + case 19: return "DeviceEnqueue"; + case 20: return "LiteralSampler"; + case 21: return "AtomicStorage"; + case 22: return "Int16"; + case 23: return "TessellationPointSize"; + case 24: return "GeometryPointSize"; + case 25: return "ImageGatherExtended"; + case 26: return "Bad"; + case 27: return "StorageImageMultisample"; + case 28: return "UniformBufferArrayDynamicIndexing"; + case 29: return "SampledImageArrayDynamicIndexing"; + case 30: return "StorageBufferArrayDynamicIndexing"; + case 31: return "StorageImageArrayDynamicIndexing"; + case 32: return "ClipDistance"; + case 33: return "CullDistance"; + case 34: return "ImageCubeArray"; + case 35: return "SampleRateShading"; + case 36: return "ImageRect"; + case 37: return "SampledRect"; + case 38: return "GenericPointer"; + case 39: return "Int8"; + case 40: return "InputAttachment"; + case 41: return "SparseResidency"; + case 42: return "MinLod"; + case 43: return "Sampled1D"; + case 44: return "Image1D"; + case 45: return "SampledCubeArray"; + case 46: return "SampledBuffer"; + case 47: return "ImageBuffer"; + case 48: return "ImageMSArray"; + case 49: return "StorageImageExtendedFormats"; + case 50: return "ImageQuery"; + case 51: return "DerivativeControl"; + case 52: return "InterpolationFunction"; + case 53: return "TransformFeedback"; + case 54: return "GeometryStreams"; + case 55: return "StorageImageReadWithoutFormat"; + case 56: return "StorageImageWriteWithoutFormat"; + case 57: return "MultiViewport"; + case 61: return "GroupNonUniform"; + case 62: return "GroupNonUniformVote"; + case 63: return "GroupNonUniformArithmetic"; + case 64: return "GroupNonUniformBallot"; + case 65: return "GroupNonUniformShuffle"; + case 66: return "GroupNonUniformShuffleRelative"; + case 67: return "GroupNonUniformClustered"; + case 68: return "GroupNonUniformQuad"; + + case CapabilitySubgroupBallotKHR: return "SubgroupBallotKHR"; + case CapabilityDrawParameters: return "DrawParameters"; + case CapabilitySubgroupVoteKHR: return "SubgroupVoteKHR"; + + case CapabilityStorageUniformBufferBlock16: return "StorageUniformBufferBlock16"; + case CapabilityStorageUniform16: return "StorageUniform16"; + case CapabilityStoragePushConstant16: return "StoragePushConstant16"; + case CapabilityStorageInputOutput16: return "StorageInputOutput16"; + + case CapabilityStorageBuffer8BitAccess: return "StorageBuffer8BitAccess"; + case CapabilityUniformAndStorageBuffer8BitAccess: return "UniformAndStorageBuffer8BitAccess"; + case CapabilityStoragePushConstant8: return "StoragePushConstant8"; + + case CapabilityDeviceGroup: return "DeviceGroup"; + case CapabilityMultiView: return "MultiView"; + + case CapabilityStencilExportEXT: return "StencilExportEXT"; + + case CapabilityFloat16ImageAMD: return "Float16ImageAMD"; + case CapabilityImageGatherBiasLodAMD: return "ImageGatherBiasLodAMD"; + case CapabilityFragmentMaskAMD: return "FragmentMaskAMD"; + case CapabilityImageReadWriteLodAMD: return "ImageReadWriteLodAMD"; + + case CapabilityAtomicStorageOps: return "AtomicStorageOps"; + + case CapabilitySampleMaskPostDepthCoverage: return "SampleMaskPostDepthCoverage"; + case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + case CapabilityShaderViewportIndexLayerNV: return "ShaderViewportIndexLayerNV"; + case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV"; + case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV"; + case CapabilityPerViewAttributesNV: return "PerViewAttributesNV"; + case CapabilityGroupNonUniformPartitionedNV: return "GroupNonUniformPartitionedNV"; + case CapabilityRayTracingNV: return "RayTracingNV"; + case CapabilityRayTracingProvisionalKHR: return "RayTracingProvisionalKHR"; + case CapabilityRayQueryProvisionalKHR: return "RayQueryProvisionalKHR"; + case CapabilityRayTraversalPrimitiveCullingProvisionalKHR: return "RayTraversalPrimitiveCullingProvisionalKHR"; + case CapabilityComputeDerivativeGroupQuadsNV: return "ComputeDerivativeGroupQuadsNV"; + case CapabilityComputeDerivativeGroupLinearNV: return "ComputeDerivativeGroupLinearNV"; + case CapabilityFragmentBarycentricNV: return "FragmentBarycentricNV"; + case CapabilityMeshShadingNV: return "MeshShadingNV"; + case CapabilityImageFootprintNV: return "ImageFootprintNV"; +// case CapabilityShadingRateNV: return "ShadingRateNV"; // superseded by FragmentDensityEXT + case CapabilitySampleMaskOverrideCoverageNV: return "SampleMaskOverrideCoverageNV"; + case CapabilityFragmentDensityEXT: return "FragmentDensityEXT"; + + case CapabilityFragmentFullyCoveredEXT: return "FragmentFullyCoveredEXT"; + + case CapabilityShaderNonUniformEXT: return "ShaderNonUniformEXT"; + case CapabilityRuntimeDescriptorArrayEXT: return "RuntimeDescriptorArrayEXT"; + case CapabilityInputAttachmentArrayDynamicIndexingEXT: return "InputAttachmentArrayDynamicIndexingEXT"; + case CapabilityUniformTexelBufferArrayDynamicIndexingEXT: return "UniformTexelBufferArrayDynamicIndexingEXT"; + case CapabilityStorageTexelBufferArrayDynamicIndexingEXT: return "StorageTexelBufferArrayDynamicIndexingEXT"; + case CapabilityUniformBufferArrayNonUniformIndexingEXT: return "UniformBufferArrayNonUniformIndexingEXT"; + case CapabilitySampledImageArrayNonUniformIndexingEXT: return "SampledImageArrayNonUniformIndexingEXT"; + case CapabilityStorageBufferArrayNonUniformIndexingEXT: return "StorageBufferArrayNonUniformIndexingEXT"; + case CapabilityStorageImageArrayNonUniformIndexingEXT: return "StorageImageArrayNonUniformIndexingEXT"; + case CapabilityInputAttachmentArrayNonUniformIndexingEXT: return "InputAttachmentArrayNonUniformIndexingEXT"; + case CapabilityUniformTexelBufferArrayNonUniformIndexingEXT: return "UniformTexelBufferArrayNonUniformIndexingEXT"; + case CapabilityStorageTexelBufferArrayNonUniformIndexingEXT: return "StorageTexelBufferArrayNonUniformIndexingEXT"; + + case CapabilityVulkanMemoryModelKHR: return "VulkanMemoryModelKHR"; + case CapabilityVulkanMemoryModelDeviceScopeKHR: return "VulkanMemoryModelDeviceScopeKHR"; + + case CapabilityPhysicalStorageBufferAddressesEXT: return "PhysicalStorageBufferAddressesEXT"; + + case CapabilityVariablePointers: return "VariablePointers"; + + case CapabilityCooperativeMatrixNV: return "CooperativeMatrixNV"; + case CapabilityShaderSMBuiltinsNV: return "ShaderSMBuiltinsNV"; + + case CapabilityFragmentShaderSampleInterlockEXT: return "CapabilityFragmentShaderSampleInterlockEXT"; + case CapabilityFragmentShaderPixelInterlockEXT: return "CapabilityFragmentShaderPixelInterlockEXT"; + case CapabilityFragmentShaderShadingRateInterlockEXT: return "CapabilityFragmentShaderShadingRateInterlockEXT"; + + case CapabilityDemoteToHelperInvocationEXT: return "DemoteToHelperInvocationEXT"; + case CapabilityShaderClockKHR: return "ShaderClockKHR"; + + case CapabilityIntegerFunctions2INTEL: return "CapabilityIntegerFunctions2INTEL"; + + default: return "Bad"; + } +} + +const char* OpcodeString(int op) +{ + switch (op) { + case 0: return "OpNop"; + case 1: return "OpUndef"; + case 2: return "OpSourceContinued"; + case 3: return "OpSource"; + case 4: return "OpSourceExtension"; + case 5: return "OpName"; + case 6: return "OpMemberName"; + case 7: return "OpString"; + case 8: return "OpLine"; + case 9: return "Bad"; + case 10: return "OpExtension"; + case 11: return "OpExtInstImport"; + case 12: return "OpExtInst"; + case 13: return "Bad"; + case 14: return "OpMemoryModel"; + case 15: return "OpEntryPoint"; + case 16: return "OpExecutionMode"; + case 17: return "OpCapability"; + case 18: return "Bad"; + case 19: return "OpTypeVoid"; + case 20: return "OpTypeBool"; + case 21: return "OpTypeInt"; + case 22: return "OpTypeFloat"; + case 23: return "OpTypeVector"; + case 24: return "OpTypeMatrix"; + case 25: return "OpTypeImage"; + case 26: return "OpTypeSampler"; + case 27: return "OpTypeSampledImage"; + case 28: return "OpTypeArray"; + case 29: return "OpTypeRuntimeArray"; + case 30: return "OpTypeStruct"; + case 31: return "OpTypeOpaque"; + case 32: return "OpTypePointer"; + case 33: return "OpTypeFunction"; + case 34: return "OpTypeEvent"; + case 35: return "OpTypeDeviceEvent"; + case 36: return "OpTypeReserveId"; + case 37: return "OpTypeQueue"; + case 38: return "OpTypePipe"; + case 39: return "OpTypeForwardPointer"; + case 40: return "Bad"; + case 41: return "OpConstantTrue"; + case 42: return "OpConstantFalse"; + case 43: return "OpConstant"; + case 44: return "OpConstantComposite"; + case 45: return "OpConstantSampler"; + case 46: return "OpConstantNull"; + case 47: return "Bad"; + case 48: return "OpSpecConstantTrue"; + case 49: return "OpSpecConstantFalse"; + case 50: return "OpSpecConstant"; + case 51: return "OpSpecConstantComposite"; + case 52: return "OpSpecConstantOp"; + case 53: return "Bad"; + case 54: return "OpFunction"; + case 55: return "OpFunctionParameter"; + case 56: return "OpFunctionEnd"; + case 57: return "OpFunctionCall"; + case 58: return "Bad"; + case 59: return "OpVariable"; + case 60: return "OpImageTexelPointer"; + case 61: return "OpLoad"; + case 62: return "OpStore"; + case 63: return "OpCopyMemory"; + case 64: return "OpCopyMemorySized"; + case 65: return "OpAccessChain"; + case 66: return "OpInBoundsAccessChain"; + case 67: return "OpPtrAccessChain"; + case 68: return "OpArrayLength"; + case 69: return "OpGenericPtrMemSemantics"; + case 70: return "OpInBoundsPtrAccessChain"; + case 71: return "OpDecorate"; + case 72: return "OpMemberDecorate"; + case 73: return "OpDecorationGroup"; + case 74: return "OpGroupDecorate"; + case 75: return "OpGroupMemberDecorate"; + case 76: return "Bad"; + case 77: return "OpVectorExtractDynamic"; + case 78: return "OpVectorInsertDynamic"; + case 79: return "OpVectorShuffle"; + case 80: return "OpCompositeConstruct"; + case 81: return "OpCompositeExtract"; + case 82: return "OpCompositeInsert"; + case 83: return "OpCopyObject"; + case 84: return "OpTranspose"; + case OpCopyLogical: return "OpCopyLogical"; + case 85: return "Bad"; + case 86: return "OpSampledImage"; + case 87: return "OpImageSampleImplicitLod"; + case 88: return "OpImageSampleExplicitLod"; + case 89: return "OpImageSampleDrefImplicitLod"; + case 90: return "OpImageSampleDrefExplicitLod"; + case 91: return "OpImageSampleProjImplicitLod"; + case 92: return "OpImageSampleProjExplicitLod"; + case 93: return "OpImageSampleProjDrefImplicitLod"; + case 94: return "OpImageSampleProjDrefExplicitLod"; + case 95: return "OpImageFetch"; + case 96: return "OpImageGather"; + case 97: return "OpImageDrefGather"; + case 98: return "OpImageRead"; + case 99: return "OpImageWrite"; + case 100: return "OpImage"; + case 101: return "OpImageQueryFormat"; + case 102: return "OpImageQueryOrder"; + case 103: return "OpImageQuerySizeLod"; + case 104: return "OpImageQuerySize"; + case 105: return "OpImageQueryLod"; + case 106: return "OpImageQueryLevels"; + case 107: return "OpImageQuerySamples"; + case 108: return "Bad"; + case 109: return "OpConvertFToU"; + case 110: return "OpConvertFToS"; + case 111: return "OpConvertSToF"; + case 112: return "OpConvertUToF"; + case 113: return "OpUConvert"; + case 114: return "OpSConvert"; + case 115: return "OpFConvert"; + case 116: return "OpQuantizeToF16"; + case 117: return "OpConvertPtrToU"; + case 118: return "OpSatConvertSToU"; + case 119: return "OpSatConvertUToS"; + case 120: return "OpConvertUToPtr"; + case 121: return "OpPtrCastToGeneric"; + case 122: return "OpGenericCastToPtr"; + case 123: return "OpGenericCastToPtrExplicit"; + case 124: return "OpBitcast"; + case 125: return "Bad"; + case 126: return "OpSNegate"; + case 127: return "OpFNegate"; + case 128: return "OpIAdd"; + case 129: return "OpFAdd"; + case 130: return "OpISub"; + case 131: return "OpFSub"; + case 132: return "OpIMul"; + case 133: return "OpFMul"; + case 134: return "OpUDiv"; + case 135: return "OpSDiv"; + case 136: return "OpFDiv"; + case 137: return "OpUMod"; + case 138: return "OpSRem"; + case 139: return "OpSMod"; + case 140: return "OpFRem"; + case 141: return "OpFMod"; + case 142: return "OpVectorTimesScalar"; + case 143: return "OpMatrixTimesScalar"; + case 144: return "OpVectorTimesMatrix"; + case 145: return "OpMatrixTimesVector"; + case 146: return "OpMatrixTimesMatrix"; + case 147: return "OpOuterProduct"; + case 148: return "OpDot"; + case 149: return "OpIAddCarry"; + case 150: return "OpISubBorrow"; + case 151: return "OpUMulExtended"; + case 152: return "OpSMulExtended"; + case 153: return "Bad"; + case 154: return "OpAny"; + case 155: return "OpAll"; + case 156: return "OpIsNan"; + case 157: return "OpIsInf"; + case 158: return "OpIsFinite"; + case 159: return "OpIsNormal"; + case 160: return "OpSignBitSet"; + case 161: return "OpLessOrGreater"; + case 162: return "OpOrdered"; + case 163: return "OpUnordered"; + case 164: return "OpLogicalEqual"; + case 165: return "OpLogicalNotEqual"; + case 166: return "OpLogicalOr"; + case 167: return "OpLogicalAnd"; + case 168: return "OpLogicalNot"; + case 169: return "OpSelect"; + case 170: return "OpIEqual"; + case 171: return "OpINotEqual"; + case 172: return "OpUGreaterThan"; + case 173: return "OpSGreaterThan"; + case 174: return "OpUGreaterThanEqual"; + case 175: return "OpSGreaterThanEqual"; + case 176: return "OpULessThan"; + case 177: return "OpSLessThan"; + case 178: return "OpULessThanEqual"; + case 179: return "OpSLessThanEqual"; + case 180: return "OpFOrdEqual"; + case 181: return "OpFUnordEqual"; + case 182: return "OpFOrdNotEqual"; + case 183: return "OpFUnordNotEqual"; + case 184: return "OpFOrdLessThan"; + case 185: return "OpFUnordLessThan"; + case 186: return "OpFOrdGreaterThan"; + case 187: return "OpFUnordGreaterThan"; + case 188: return "OpFOrdLessThanEqual"; + case 189: return "OpFUnordLessThanEqual"; + case 190: return "OpFOrdGreaterThanEqual"; + case 191: return "OpFUnordGreaterThanEqual"; + case 192: return "Bad"; + case 193: return "Bad"; + case 194: return "OpShiftRightLogical"; + case 195: return "OpShiftRightArithmetic"; + case 196: return "OpShiftLeftLogical"; + case 197: return "OpBitwiseOr"; + case 198: return "OpBitwiseXor"; + case 199: return "OpBitwiseAnd"; + case 200: return "OpNot"; + case 201: return "OpBitFieldInsert"; + case 202: return "OpBitFieldSExtract"; + case 203: return "OpBitFieldUExtract"; + case 204: return "OpBitReverse"; + case 205: return "OpBitCount"; + case 206: return "Bad"; + case 207: return "OpDPdx"; + case 208: return "OpDPdy"; + case 209: return "OpFwidth"; + case 210: return "OpDPdxFine"; + case 211: return "OpDPdyFine"; + case 212: return "OpFwidthFine"; + case 213: return "OpDPdxCoarse"; + case 214: return "OpDPdyCoarse"; + case 215: return "OpFwidthCoarse"; + case 216: return "Bad"; + case 217: return "Bad"; + case 218: return "OpEmitVertex"; + case 219: return "OpEndPrimitive"; + case 220: return "OpEmitStreamVertex"; + case 221: return "OpEndStreamPrimitive"; + case 222: return "Bad"; + case 223: return "Bad"; + case 224: return "OpControlBarrier"; + case 225: return "OpMemoryBarrier"; + case 226: return "Bad"; + case 227: return "OpAtomicLoad"; + case 228: return "OpAtomicStore"; + case 229: return "OpAtomicExchange"; + case 230: return "OpAtomicCompareExchange"; + case 231: return "OpAtomicCompareExchangeWeak"; + case 232: return "OpAtomicIIncrement"; + case 233: return "OpAtomicIDecrement"; + case 234: return "OpAtomicIAdd"; + case 235: return "OpAtomicISub"; + case 236: return "OpAtomicSMin"; + case 237: return "OpAtomicUMin"; + case 238: return "OpAtomicSMax"; + case 239: return "OpAtomicUMax"; + case 240: return "OpAtomicAnd"; + case 241: return "OpAtomicOr"; + case 242: return "OpAtomicXor"; + case 243: return "Bad"; + case 244: return "Bad"; + case 245: return "OpPhi"; + case 246: return "OpLoopMerge"; + case 247: return "OpSelectionMerge"; + case 248: return "OpLabel"; + case 249: return "OpBranch"; + case 250: return "OpBranchConditional"; + case 251: return "OpSwitch"; + case 252: return "OpKill"; + case 253: return "OpReturn"; + case 254: return "OpReturnValue"; + case 255: return "OpUnreachable"; + case 256: return "OpLifetimeStart"; + case 257: return "OpLifetimeStop"; + case 258: return "Bad"; + case 259: return "OpGroupAsyncCopy"; + case 260: return "OpGroupWaitEvents"; + case 261: return "OpGroupAll"; + case 262: return "OpGroupAny"; + case 263: return "OpGroupBroadcast"; + case 264: return "OpGroupIAdd"; + case 265: return "OpGroupFAdd"; + case 266: return "OpGroupFMin"; + case 267: return "OpGroupUMin"; + case 268: return "OpGroupSMin"; + case 269: return "OpGroupFMax"; + case 270: return "OpGroupUMax"; + case 271: return "OpGroupSMax"; + case 272: return "Bad"; + case 273: return "Bad"; + case 274: return "OpReadPipe"; + case 275: return "OpWritePipe"; + case 276: return "OpReservedReadPipe"; + case 277: return "OpReservedWritePipe"; + case 278: return "OpReserveReadPipePackets"; + case 279: return "OpReserveWritePipePackets"; + case 280: return "OpCommitReadPipe"; + case 281: return "OpCommitWritePipe"; + case 282: return "OpIsValidReserveId"; + case 283: return "OpGetNumPipePackets"; + case 284: return "OpGetMaxPipePackets"; + case 285: return "OpGroupReserveReadPipePackets"; + case 286: return "OpGroupReserveWritePipePackets"; + case 287: return "OpGroupCommitReadPipe"; + case 288: return "OpGroupCommitWritePipe"; + case 289: return "Bad"; + case 290: return "Bad"; + case 291: return "OpEnqueueMarker"; + case 292: return "OpEnqueueKernel"; + case 293: return "OpGetKernelNDrangeSubGroupCount"; + case 294: return "OpGetKernelNDrangeMaxSubGroupSize"; + case 295: return "OpGetKernelWorkGroupSize"; + case 296: return "OpGetKernelPreferredWorkGroupSizeMultiple"; + case 297: return "OpRetainEvent"; + case 298: return "OpReleaseEvent"; + case 299: return "OpCreateUserEvent"; + case 300: return "OpIsValidEvent"; + case 301: return "OpSetUserEventStatus"; + case 302: return "OpCaptureEventProfilingInfo"; + case 303: return "OpGetDefaultQueue"; + case 304: return "OpBuildNDRange"; + case 305: return "OpImageSparseSampleImplicitLod"; + case 306: return "OpImageSparseSampleExplicitLod"; + case 307: return "OpImageSparseSampleDrefImplicitLod"; + case 308: return "OpImageSparseSampleDrefExplicitLod"; + case 309: return "OpImageSparseSampleProjImplicitLod"; + case 310: return "OpImageSparseSampleProjExplicitLod"; + case 311: return "OpImageSparseSampleProjDrefImplicitLod"; + case 312: return "OpImageSparseSampleProjDrefExplicitLod"; + case 313: return "OpImageSparseFetch"; + case 314: return "OpImageSparseGather"; + case 315: return "OpImageSparseDrefGather"; + case 316: return "OpImageSparseTexelsResident"; + case 317: return "OpNoLine"; + case 318: return "OpAtomicFlagTestAndSet"; + case 319: return "OpAtomicFlagClear"; + case 320: return "OpImageSparseRead"; + + case OpModuleProcessed: return "OpModuleProcessed"; + case OpDecorateId: return "OpDecorateId"; + + case 333: return "OpGroupNonUniformElect"; + case 334: return "OpGroupNonUniformAll"; + case 335: return "OpGroupNonUniformAny"; + case 336: return "OpGroupNonUniformAllEqual"; + case 337: return "OpGroupNonUniformBroadcast"; + case 338: return "OpGroupNonUniformBroadcastFirst"; + case 339: return "OpGroupNonUniformBallot"; + case 340: return "OpGroupNonUniformInverseBallot"; + case 341: return "OpGroupNonUniformBallotBitExtract"; + case 342: return "OpGroupNonUniformBallotBitCount"; + case 343: return "OpGroupNonUniformBallotFindLSB"; + case 344: return "OpGroupNonUniformBallotFindMSB"; + case 345: return "OpGroupNonUniformShuffle"; + case 346: return "OpGroupNonUniformShuffleXor"; + case 347: return "OpGroupNonUniformShuffleUp"; + case 348: return "OpGroupNonUniformShuffleDown"; + case 349: return "OpGroupNonUniformIAdd"; + case 350: return "OpGroupNonUniformFAdd"; + case 351: return "OpGroupNonUniformIMul"; + case 352: return "OpGroupNonUniformFMul"; + case 353: return "OpGroupNonUniformSMin"; + case 354: return "OpGroupNonUniformUMin"; + case 355: return "OpGroupNonUniformFMin"; + case 356: return "OpGroupNonUniformSMax"; + case 357: return "OpGroupNonUniformUMax"; + case 358: return "OpGroupNonUniformFMax"; + case 359: return "OpGroupNonUniformBitwiseAnd"; + case 360: return "OpGroupNonUniformBitwiseOr"; + case 361: return "OpGroupNonUniformBitwiseXor"; + case 362: return "OpGroupNonUniformLogicalAnd"; + case 363: return "OpGroupNonUniformLogicalOr"; + case 364: return "OpGroupNonUniformLogicalXor"; + case 365: return "OpGroupNonUniformQuadBroadcast"; + case 366: return "OpGroupNonUniformQuadSwap"; + + case 4421: return "OpSubgroupBallotKHR"; + case 4422: return "OpSubgroupFirstInvocationKHR"; + case 4428: return "OpSubgroupAllKHR"; + case 4429: return "OpSubgroupAnyKHR"; + case 4430: return "OpSubgroupAllEqualKHR"; + case 4432: return "OpSubgroupReadInvocationKHR"; + + case 5000: return "OpGroupIAddNonUniformAMD"; + case 5001: return "OpGroupFAddNonUniformAMD"; + case 5002: return "OpGroupFMinNonUniformAMD"; + case 5003: return "OpGroupUMinNonUniformAMD"; + case 5004: return "OpGroupSMinNonUniformAMD"; + case 5005: return "OpGroupFMaxNonUniformAMD"; + case 5006: return "OpGroupUMaxNonUniformAMD"; + case 5007: return "OpGroupSMaxNonUniformAMD"; + + case 5011: return "OpFragmentMaskFetchAMD"; + case 5012: return "OpFragmentFetchAMD"; + + case OpReadClockKHR: return "OpReadClockKHR"; + + case OpDecorateStringGOOGLE: return "OpDecorateStringGOOGLE"; + case OpMemberDecorateStringGOOGLE: return "OpMemberDecorateStringGOOGLE"; + + case OpGroupNonUniformPartitionNV: return "OpGroupNonUniformPartitionNV"; + case OpReportIntersectionKHR: return "OpReportIntersectionKHR"; + case OpIgnoreIntersectionKHR: return "OpIgnoreIntersectionKHR"; + case OpTerminateRayKHR: return "OpTerminateRayKHR"; + case OpTraceRayKHR: return "OpTraceRayKHR"; + case OpTypeAccelerationStructureKHR: return "OpTypeAccelerationStructureKHR"; + case OpExecuteCallableKHR: return "OpExecuteCallableKHR"; + case OpImageSampleFootprintNV: return "OpImageSampleFootprintNV"; + case OpWritePackedPrimitiveIndices4x8NV: return "OpWritePackedPrimitiveIndices4x8NV"; + + case OpTypeRayQueryProvisionalKHR: return "OpTypeRayQueryProvisionalKHR"; + case OpRayQueryInitializeKHR: return "OpRayQueryInitializeKHR"; + case OpRayQueryTerminateKHR: return "OpRayQueryTerminateKHR"; + case OpRayQueryGenerateIntersectionKHR: return "OpRayQueryGenerateIntersectionKHR"; + case OpRayQueryConfirmIntersectionKHR: return "OpRayQueryConfirmIntersectionKHR"; + case OpRayQueryProceedKHR: return "OpRayQueryProceedKHR"; + case OpRayQueryGetIntersectionTypeKHR: return "OpRayQueryGetIntersectionTypeKHR"; + case OpRayQueryGetRayTMinKHR: return "OpRayQueryGetRayTMinKHR"; + case OpRayQueryGetRayFlagsKHR: return "OpRayQueryGetRayFlagsKHR"; + case OpRayQueryGetIntersectionTKHR: return "OpRayQueryGetIntersectionTKHR"; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: return "OpRayQueryGetIntersectionInstanceCustomIndexKHR"; + case OpRayQueryGetIntersectionInstanceIdKHR: return "OpRayQueryGetIntersectionInstanceIdKHR"; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: return "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR"; + case OpRayQueryGetIntersectionGeometryIndexKHR: return "OpRayQueryGetIntersectionGeometryIndexKHR"; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: return "OpRayQueryGetIntersectionPrimitiveIndexKHR"; + case OpRayQueryGetIntersectionBarycentricsKHR: return "OpRayQueryGetIntersectionBarycentricsKHR"; + case OpRayQueryGetIntersectionFrontFaceKHR: return "OpRayQueryGetIntersectionFrontFaceKHR"; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: return "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR"; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: return "OpRayQueryGetIntersectionObjectRayDirectionKHR"; + case OpRayQueryGetIntersectionObjectRayOriginKHR: return "OpRayQueryGetIntersectionObjectRayOriginKHR"; + case OpRayQueryGetWorldRayDirectionKHR: return "OpRayQueryGetWorldRayDirectionKHR"; + case OpRayQueryGetWorldRayOriginKHR: return "OpRayQueryGetWorldRayOriginKHR"; + case OpRayQueryGetIntersectionObjectToWorldKHR: return "OpRayQueryGetIntersectionObjectToWorldKHR"; + case OpRayQueryGetIntersectionWorldToObjectKHR: return "OpRayQueryGetIntersectionWorldToObjectKHR"; + + case OpTypeCooperativeMatrixNV: return "OpTypeCooperativeMatrixNV"; + case OpCooperativeMatrixLoadNV: return "OpCooperativeMatrixLoadNV"; + case OpCooperativeMatrixStoreNV: return "OpCooperativeMatrixStoreNV"; + case OpCooperativeMatrixMulAddNV: return "OpCooperativeMatrixMulAddNV"; + case OpCooperativeMatrixLengthNV: return "OpCooperativeMatrixLengthNV"; + case OpDemoteToHelperInvocationEXT: return "OpDemoteToHelperInvocationEXT"; + case OpIsHelperInvocationEXT: return "OpIsHelperInvocationEXT"; + + case OpBeginInvocationInterlockEXT: return "OpBeginInvocationInterlockEXT"; + case OpEndInvocationInterlockEXT: return "OpEndInvocationInterlockEXT"; + + default: + return "Bad"; + } +} + +// The set of objects that hold all the instruction/operand +// parameterization information. +InstructionParameters InstructionDesc[OpCodeMask + 1]; +OperandParameters ExecutionModeOperands[ExecutionModeCeiling]; +OperandParameters DecorationOperands[DecorationCeiling]; + +EnumDefinition OperandClassParams[OperandCount]; +EnumParameters ExecutionModeParams[ExecutionModeCeiling]; +EnumParameters ImageOperandsParams[ImageOperandsCeiling]; +EnumParameters DecorationParams[DecorationCeiling]; +EnumParameters LoopControlParams[FunctionControlCeiling]; +EnumParameters SelectionControlParams[SelectControlCeiling]; +EnumParameters FunctionControlParams[FunctionControlCeiling]; +EnumParameters MemoryAccessParams[MemoryAccessCeiling]; + +// Set up all the parameterizing descriptions of the opcodes, operands, etc. +void Parameterize() +{ + // only do this once. + static bool initialized = false; + if (initialized) + return; + initialized = true; + + // Exceptions to having a result and a resulting type . + // (Everything is initialized to have both). + + InstructionDesc[OpNop].setResultAndType(false, false); + InstructionDesc[OpSource].setResultAndType(false, false); + InstructionDesc[OpSourceContinued].setResultAndType(false, false); + InstructionDesc[OpSourceExtension].setResultAndType(false, false); + InstructionDesc[OpExtension].setResultAndType(false, false); + InstructionDesc[OpExtInstImport].setResultAndType(true, false); + InstructionDesc[OpCapability].setResultAndType(false, false); + InstructionDesc[OpMemoryModel].setResultAndType(false, false); + InstructionDesc[OpEntryPoint].setResultAndType(false, false); + InstructionDesc[OpExecutionMode].setResultAndType(false, false); + InstructionDesc[OpTypeVoid].setResultAndType(true, false); + InstructionDesc[OpTypeBool].setResultAndType(true, false); + InstructionDesc[OpTypeInt].setResultAndType(true, false); + InstructionDesc[OpTypeFloat].setResultAndType(true, false); + InstructionDesc[OpTypeVector].setResultAndType(true, false); + InstructionDesc[OpTypeMatrix].setResultAndType(true, false); + InstructionDesc[OpTypeImage].setResultAndType(true, false); + InstructionDesc[OpTypeSampler].setResultAndType(true, false); + InstructionDesc[OpTypeSampledImage].setResultAndType(true, false); + InstructionDesc[OpTypeArray].setResultAndType(true, false); + InstructionDesc[OpTypeRuntimeArray].setResultAndType(true, false); + InstructionDesc[OpTypeStruct].setResultAndType(true, false); + InstructionDesc[OpTypeOpaque].setResultAndType(true, false); + InstructionDesc[OpTypePointer].setResultAndType(true, false); + InstructionDesc[OpTypeForwardPointer].setResultAndType(false, false); + InstructionDesc[OpTypeFunction].setResultAndType(true, false); + InstructionDesc[OpTypeEvent].setResultAndType(true, false); + InstructionDesc[OpTypeDeviceEvent].setResultAndType(true, false); + InstructionDesc[OpTypeReserveId].setResultAndType(true, false); + InstructionDesc[OpTypeQueue].setResultAndType(true, false); + InstructionDesc[OpTypePipe].setResultAndType(true, false); + InstructionDesc[OpFunctionEnd].setResultAndType(false, false); + InstructionDesc[OpStore].setResultAndType(false, false); + InstructionDesc[OpImageWrite].setResultAndType(false, false); + InstructionDesc[OpDecorationGroup].setResultAndType(true, false); + InstructionDesc[OpDecorate].setResultAndType(false, false); + InstructionDesc[OpDecorateId].setResultAndType(false, false); + InstructionDesc[OpDecorateStringGOOGLE].setResultAndType(false, false); + InstructionDesc[OpMemberDecorate].setResultAndType(false, false); + InstructionDesc[OpMemberDecorateStringGOOGLE].setResultAndType(false, false); + InstructionDesc[OpGroupDecorate].setResultAndType(false, false); + InstructionDesc[OpGroupMemberDecorate].setResultAndType(false, false); + InstructionDesc[OpName].setResultAndType(false, false); + InstructionDesc[OpMemberName].setResultAndType(false, false); + InstructionDesc[OpString].setResultAndType(true, false); + InstructionDesc[OpLine].setResultAndType(false, false); + InstructionDesc[OpNoLine].setResultAndType(false, false); + InstructionDesc[OpCopyMemory].setResultAndType(false, false); + InstructionDesc[OpCopyMemorySized].setResultAndType(false, false); + InstructionDesc[OpEmitVertex].setResultAndType(false, false); + InstructionDesc[OpEndPrimitive].setResultAndType(false, false); + InstructionDesc[OpEmitStreamVertex].setResultAndType(false, false); + InstructionDesc[OpEndStreamPrimitive].setResultAndType(false, false); + InstructionDesc[OpControlBarrier].setResultAndType(false, false); + InstructionDesc[OpMemoryBarrier].setResultAndType(false, false); + InstructionDesc[OpAtomicStore].setResultAndType(false, false); + InstructionDesc[OpLoopMerge].setResultAndType(false, false); + InstructionDesc[OpSelectionMerge].setResultAndType(false, false); + InstructionDesc[OpLabel].setResultAndType(true, false); + InstructionDesc[OpBranch].setResultAndType(false, false); + InstructionDesc[OpBranchConditional].setResultAndType(false, false); + InstructionDesc[OpSwitch].setResultAndType(false, false); + InstructionDesc[OpKill].setResultAndType(false, false); + InstructionDesc[OpReturn].setResultAndType(false, false); + InstructionDesc[OpReturnValue].setResultAndType(false, false); + InstructionDesc[OpUnreachable].setResultAndType(false, false); + InstructionDesc[OpLifetimeStart].setResultAndType(false, false); + InstructionDesc[OpLifetimeStop].setResultAndType(false, false); + InstructionDesc[OpCommitReadPipe].setResultAndType(false, false); + InstructionDesc[OpCommitWritePipe].setResultAndType(false, false); + InstructionDesc[OpGroupCommitWritePipe].setResultAndType(false, false); + InstructionDesc[OpGroupCommitReadPipe].setResultAndType(false, false); + InstructionDesc[OpCaptureEventProfilingInfo].setResultAndType(false, false); + InstructionDesc[OpSetUserEventStatus].setResultAndType(false, false); + InstructionDesc[OpRetainEvent].setResultAndType(false, false); + InstructionDesc[OpReleaseEvent].setResultAndType(false, false); + InstructionDesc[OpGroupWaitEvents].setResultAndType(false, false); + InstructionDesc[OpAtomicFlagClear].setResultAndType(false, false); + InstructionDesc[OpModuleProcessed].setResultAndType(false, false); + InstructionDesc[OpTypeCooperativeMatrixNV].setResultAndType(true, false); + InstructionDesc[OpCooperativeMatrixStoreNV].setResultAndType(false, false); + InstructionDesc[OpBeginInvocationInterlockEXT].setResultAndType(false, false); + InstructionDesc[OpEndInvocationInterlockEXT].setResultAndType(false, false); + + // Specific additional context-dependent operands + + ExecutionModeOperands[ExecutionModeInvocations].push(OperandLiteralNumber, "'Number of <>'"); + + ExecutionModeOperands[ExecutionModeLocalSize].push(OperandLiteralNumber, "'x size'"); + ExecutionModeOperands[ExecutionModeLocalSize].push(OperandLiteralNumber, "'y size'"); + ExecutionModeOperands[ExecutionModeLocalSize].push(OperandLiteralNumber, "'z size'"); + + ExecutionModeOperands[ExecutionModeLocalSizeHint].push(OperandLiteralNumber, "'x size'"); + ExecutionModeOperands[ExecutionModeLocalSizeHint].push(OperandLiteralNumber, "'y size'"); + ExecutionModeOperands[ExecutionModeLocalSizeHint].push(OperandLiteralNumber, "'z size'"); + + ExecutionModeOperands[ExecutionModeOutputVertices].push(OperandLiteralNumber, "'Vertex count'"); + ExecutionModeOperands[ExecutionModeVecTypeHint].push(OperandLiteralNumber, "'Vector type'"); + + DecorationOperands[DecorationStream].push(OperandLiteralNumber, "'Stream Number'"); + DecorationOperands[DecorationLocation].push(OperandLiteralNumber, "'Location'"); + DecorationOperands[DecorationComponent].push(OperandLiteralNumber, "'Component'"); + DecorationOperands[DecorationIndex].push(OperandLiteralNumber, "'Index'"); + DecorationOperands[DecorationBinding].push(OperandLiteralNumber, "'Binding Point'"); + DecorationOperands[DecorationDescriptorSet].push(OperandLiteralNumber, "'Descriptor Set'"); + DecorationOperands[DecorationOffset].push(OperandLiteralNumber, "'Byte Offset'"); + DecorationOperands[DecorationXfbBuffer].push(OperandLiteralNumber, "'XFB Buffer Number'"); + DecorationOperands[DecorationXfbStride].push(OperandLiteralNumber, "'XFB Stride'"); + DecorationOperands[DecorationArrayStride].push(OperandLiteralNumber, "'Array Stride'"); + DecorationOperands[DecorationMatrixStride].push(OperandLiteralNumber, "'Matrix Stride'"); + DecorationOperands[DecorationBuiltIn].push(OperandLiteralNumber, "See <>"); + DecorationOperands[DecorationFPRoundingMode].push(OperandFPRoundingMode, "'Floating-Point Rounding Mode'"); + DecorationOperands[DecorationFPFastMathMode].push(OperandFPFastMath, "'Fast-Math Mode'"); + DecorationOperands[DecorationLinkageAttributes].push(OperandLiteralString, "'Name'"); + DecorationOperands[DecorationLinkageAttributes].push(OperandLinkageType, "'Linkage Type'"); + DecorationOperands[DecorationFuncParamAttr].push(OperandFuncParamAttr, "'Function Parameter Attribute'"); + DecorationOperands[DecorationSpecId].push(OperandLiteralNumber, "'Specialization Constant ID'"); + DecorationOperands[DecorationInputAttachmentIndex].push(OperandLiteralNumber, "'Attachment Index'"); + DecorationOperands[DecorationAlignment].push(OperandLiteralNumber, "'Alignment'"); + + OperandClassParams[OperandSource].set(0, SourceString, 0); + OperandClassParams[OperandExecutionModel].set(0, ExecutionModelString, nullptr); + OperandClassParams[OperandAddressing].set(0, AddressingString, nullptr); + OperandClassParams[OperandMemory].set(0, MemoryString, nullptr); + OperandClassParams[OperandExecutionMode].set(ExecutionModeCeiling, ExecutionModeString, ExecutionModeParams); + OperandClassParams[OperandExecutionMode].setOperands(ExecutionModeOperands); + OperandClassParams[OperandStorage].set(0, StorageClassString, nullptr); + OperandClassParams[OperandDimensionality].set(0, DimensionString, nullptr); + OperandClassParams[OperandSamplerAddressingMode].set(0, SamplerAddressingModeString, nullptr); + OperandClassParams[OperandSamplerFilterMode].set(0, SamplerFilterModeString, nullptr); + OperandClassParams[OperandSamplerImageFormat].set(0, ImageFormatString, nullptr); + OperandClassParams[OperandImageChannelOrder].set(0, ImageChannelOrderString, nullptr); + OperandClassParams[OperandImageChannelDataType].set(0, ImageChannelDataTypeString, nullptr); + OperandClassParams[OperandImageOperands].set(ImageOperandsCeiling, ImageOperandsString, ImageOperandsParams, true); + OperandClassParams[OperandFPFastMath].set(0, FPFastMathString, nullptr, true); + OperandClassParams[OperandFPRoundingMode].set(0, FPRoundingModeString, nullptr); + OperandClassParams[OperandLinkageType].set(0, LinkageTypeString, nullptr); + OperandClassParams[OperandFuncParamAttr].set(0, FuncParamAttrString, nullptr); + OperandClassParams[OperandAccessQualifier].set(0, AccessQualifierString, nullptr); + OperandClassParams[OperandDecoration].set(DecorationCeiling, DecorationString, DecorationParams); + OperandClassParams[OperandDecoration].setOperands(DecorationOperands); + OperandClassParams[OperandBuiltIn].set(0, BuiltInString, nullptr); + OperandClassParams[OperandSelect].set(SelectControlCeiling, SelectControlString, SelectionControlParams, true); + OperandClassParams[OperandLoop].set(LoopControlCeiling, LoopControlString, LoopControlParams, true); + OperandClassParams[OperandFunction].set(FunctionControlCeiling, FunctionControlString, FunctionControlParams, true); + OperandClassParams[OperandMemorySemantics].set(0, MemorySemanticsString, nullptr, true); + OperandClassParams[OperandMemoryAccess].set(MemoryAccessCeiling, MemoryAccessString, MemoryAccessParams, true); + OperandClassParams[OperandScope].set(0, ScopeString, nullptr); + OperandClassParams[OperandGroupOperation].set(0, GroupOperationString, nullptr); + OperandClassParams[OperandKernelEnqueueFlags].set(0, KernelEnqueueFlagsString, nullptr); + OperandClassParams[OperandKernelProfilingInfo].set(0, KernelProfilingInfoString, nullptr, true); + OperandClassParams[OperandCapability].set(0, CapabilityString, nullptr); + OperandClassParams[OperandOpcode].set(OpCodeMask + 1, OpcodeString, 0); + + // set name of operator, an initial set of style operands, and the description + + InstructionDesc[OpSource].operands.push(OperandSource, ""); + InstructionDesc[OpSource].operands.push(OperandLiteralNumber, "'Version'"); + InstructionDesc[OpSource].operands.push(OperandId, "'File'", true); + InstructionDesc[OpSource].operands.push(OperandLiteralString, "'Source'", true); + + InstructionDesc[OpSourceContinued].operands.push(OperandLiteralString, "'Continued Source'"); + + InstructionDesc[OpSourceExtension].operands.push(OperandLiteralString, "'Extension'"); + + InstructionDesc[OpName].operands.push(OperandId, "'Target'"); + InstructionDesc[OpName].operands.push(OperandLiteralString, "'Name'"); + + InstructionDesc[OpMemberName].operands.push(OperandId, "'Type'"); + InstructionDesc[OpMemberName].operands.push(OperandLiteralNumber, "'Member'"); + InstructionDesc[OpMemberName].operands.push(OperandLiteralString, "'Name'"); + + InstructionDesc[OpString].operands.push(OperandLiteralString, "'String'"); + + InstructionDesc[OpLine].operands.push(OperandId, "'File'"); + InstructionDesc[OpLine].operands.push(OperandLiteralNumber, "'Line'"); + InstructionDesc[OpLine].operands.push(OperandLiteralNumber, "'Column'"); + + InstructionDesc[OpExtension].operands.push(OperandLiteralString, "'Name'"); + + InstructionDesc[OpExtInstImport].operands.push(OperandLiteralString, "'Name'"); + + InstructionDesc[OpCapability].operands.push(OperandCapability, "'Capability'"); + + InstructionDesc[OpMemoryModel].operands.push(OperandAddressing, ""); + InstructionDesc[OpMemoryModel].operands.push(OperandMemory, ""); + + InstructionDesc[OpEntryPoint].operands.push(OperandExecutionModel, ""); + InstructionDesc[OpEntryPoint].operands.push(OperandId, "'Entry Point'"); + InstructionDesc[OpEntryPoint].operands.push(OperandLiteralString, "'Name'"); + InstructionDesc[OpEntryPoint].operands.push(OperandVariableIds, "'Interface'"); + + InstructionDesc[OpExecutionMode].operands.push(OperandId, "'Entry Point'"); + InstructionDesc[OpExecutionMode].operands.push(OperandExecutionMode, "'Mode'"); + InstructionDesc[OpExecutionMode].operands.push(OperandOptionalLiteral, "See <>"); + + InstructionDesc[OpTypeInt].operands.push(OperandLiteralNumber, "'Width'"); + InstructionDesc[OpTypeInt].operands.push(OperandLiteralNumber, "'Signedness'"); + + InstructionDesc[OpTypeFloat].operands.push(OperandLiteralNumber, "'Width'"); + + InstructionDesc[OpTypeVector].operands.push(OperandId, "'Component Type'"); + InstructionDesc[OpTypeVector].operands.push(OperandLiteralNumber, "'Component Count'"); + + InstructionDesc[OpTypeMatrix].operands.push(OperandId, "'Column Type'"); + InstructionDesc[OpTypeMatrix].operands.push(OperandLiteralNumber, "'Column Count'"); + + InstructionDesc[OpTypeImage].operands.push(OperandId, "'Sampled Type'"); + InstructionDesc[OpTypeImage].operands.push(OperandDimensionality, ""); + InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'Depth'"); + InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'Arrayed'"); + InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'MS'"); + InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'Sampled'"); + InstructionDesc[OpTypeImage].operands.push(OperandSamplerImageFormat, ""); + InstructionDesc[OpTypeImage].operands.push(OperandAccessQualifier, "", true); + + InstructionDesc[OpTypeSampledImage].operands.push(OperandId, "'Image Type'"); + + InstructionDesc[OpTypeArray].operands.push(OperandId, "'Element Type'"); + InstructionDesc[OpTypeArray].operands.push(OperandId, "'Length'"); + + InstructionDesc[OpTypeRuntimeArray].operands.push(OperandId, "'Element Type'"); + + InstructionDesc[OpTypeStruct].operands.push(OperandVariableIds, "'Member 0 type', +\n'member 1 type', +\n..."); + + InstructionDesc[OpTypeOpaque].operands.push(OperandLiteralString, "The name of the opaque type."); + + InstructionDesc[OpTypePointer].operands.push(OperandStorage, ""); + InstructionDesc[OpTypePointer].operands.push(OperandId, "'Type'"); + + InstructionDesc[OpTypeForwardPointer].operands.push(OperandId, "'Pointer Type'"); + InstructionDesc[OpTypeForwardPointer].operands.push(OperandStorage, ""); + + InstructionDesc[OpTypePipe].operands.push(OperandAccessQualifier, "'Qualifier'"); + + InstructionDesc[OpTypeFunction].operands.push(OperandId, "'Return Type'"); + InstructionDesc[OpTypeFunction].operands.push(OperandVariableIds, "'Parameter 0 Type', +\n'Parameter 1 Type', +\n..."); + + InstructionDesc[OpConstant].operands.push(OperandVariableLiterals, "'Value'"); + + InstructionDesc[OpConstantComposite].operands.push(OperandVariableIds, "'Constituents'"); + + InstructionDesc[OpConstantSampler].operands.push(OperandSamplerAddressingMode, ""); + InstructionDesc[OpConstantSampler].operands.push(OperandLiteralNumber, "'Param'"); + InstructionDesc[OpConstantSampler].operands.push(OperandSamplerFilterMode, ""); + + InstructionDesc[OpSpecConstant].operands.push(OperandVariableLiterals, "'Value'"); + + InstructionDesc[OpSpecConstantComposite].operands.push(OperandVariableIds, "'Constituents'"); + + InstructionDesc[OpSpecConstantOp].operands.push(OperandLiteralNumber, "'Opcode'"); + InstructionDesc[OpSpecConstantOp].operands.push(OperandVariableIds, "'Operands'"); + + InstructionDesc[OpVariable].operands.push(OperandStorage, ""); + InstructionDesc[OpVariable].operands.push(OperandId, "'Initializer'", true); + + InstructionDesc[OpFunction].operands.push(OperandFunction, ""); + InstructionDesc[OpFunction].operands.push(OperandId, "'Function Type'"); + + InstructionDesc[OpFunctionCall].operands.push(OperandId, "'Function'"); + InstructionDesc[OpFunctionCall].operands.push(OperandVariableIds, "'Argument 0', +\n'Argument 1', +\n..."); + + InstructionDesc[OpExtInst].operands.push(OperandId, "'Set'"); + InstructionDesc[OpExtInst].operands.push(OperandLiteralNumber, "'Instruction'"); + InstructionDesc[OpExtInst].operands.push(OperandVariableIds, "'Operand 1', +\n'Operand 2', +\n..."); + + InstructionDesc[OpLoad].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpLoad].operands.push(OperandMemoryAccess, "", true); + InstructionDesc[OpLoad].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpLoad].operands.push(OperandId, "", true); + + InstructionDesc[OpStore].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpStore].operands.push(OperandId, "'Object'"); + InstructionDesc[OpStore].operands.push(OperandMemoryAccess, "", true); + InstructionDesc[OpStore].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpStore].operands.push(OperandId, "", true); + + InstructionDesc[OpPhi].operands.push(OperandVariableIds, "'Variable, Parent, ...'"); + + InstructionDesc[OpDecorate].operands.push(OperandId, "'Target'"); + InstructionDesc[OpDecorate].operands.push(OperandDecoration, ""); + InstructionDesc[OpDecorate].operands.push(OperandVariableLiterals, "See <>."); + + InstructionDesc[OpDecorateId].operands.push(OperandId, "'Target'"); + InstructionDesc[OpDecorateId].operands.push(OperandDecoration, ""); + InstructionDesc[OpDecorateId].operands.push(OperandVariableIds, "See <>."); + + InstructionDesc[OpDecorateStringGOOGLE].operands.push(OperandId, "'Target'"); + InstructionDesc[OpDecorateStringGOOGLE].operands.push(OperandDecoration, ""); + InstructionDesc[OpDecorateStringGOOGLE].operands.push(OperandLiteralString, "'Literal String'"); + + InstructionDesc[OpMemberDecorate].operands.push(OperandId, "'Structure Type'"); + InstructionDesc[OpMemberDecorate].operands.push(OperandLiteralNumber, "'Member'"); + InstructionDesc[OpMemberDecorate].operands.push(OperandDecoration, ""); + InstructionDesc[OpMemberDecorate].operands.push(OperandVariableLiterals, "See <>."); + + InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandId, "'Structure Type'"); + InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandLiteralNumber, "'Member'"); + InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandDecoration, ""); + InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandLiteralString, "'Literal String'"); + + InstructionDesc[OpGroupDecorate].operands.push(OperandId, "'Decoration Group'"); + InstructionDesc[OpGroupDecorate].operands.push(OperandVariableIds, "'Targets'"); + + InstructionDesc[OpGroupMemberDecorate].operands.push(OperandId, "'Decoration Group'"); + InstructionDesc[OpGroupMemberDecorate].operands.push(OperandVariableIdLiteral, "'Targets'"); + + InstructionDesc[OpVectorExtractDynamic].operands.push(OperandId, "'Vector'"); + InstructionDesc[OpVectorExtractDynamic].operands.push(OperandId, "'Index'"); + + InstructionDesc[OpVectorInsertDynamic].operands.push(OperandId, "'Vector'"); + InstructionDesc[OpVectorInsertDynamic].operands.push(OperandId, "'Component'"); + InstructionDesc[OpVectorInsertDynamic].operands.push(OperandId, "'Index'"); + + InstructionDesc[OpVectorShuffle].operands.push(OperandId, "'Vector 1'"); + InstructionDesc[OpVectorShuffle].operands.push(OperandId, "'Vector 2'"); + InstructionDesc[OpVectorShuffle].operands.push(OperandVariableLiterals, "'Components'"); + + InstructionDesc[OpCompositeConstruct].operands.push(OperandVariableIds, "'Constituents'"); + + InstructionDesc[OpCompositeExtract].operands.push(OperandId, "'Composite'"); + InstructionDesc[OpCompositeExtract].operands.push(OperandVariableLiterals, "'Indexes'"); + + InstructionDesc[OpCompositeInsert].operands.push(OperandId, "'Object'"); + InstructionDesc[OpCompositeInsert].operands.push(OperandId, "'Composite'"); + InstructionDesc[OpCompositeInsert].operands.push(OperandVariableLiterals, "'Indexes'"); + + InstructionDesc[OpCopyObject].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpCopyMemory].operands.push(OperandId, "'Target'"); + InstructionDesc[OpCopyMemory].operands.push(OperandId, "'Source'"); + InstructionDesc[OpCopyMemory].operands.push(OperandMemoryAccess, "", true); + + InstructionDesc[OpCopyMemorySized].operands.push(OperandId, "'Target'"); + InstructionDesc[OpCopyMemorySized].operands.push(OperandId, "'Source'"); + InstructionDesc[OpCopyMemorySized].operands.push(OperandId, "'Size'"); + InstructionDesc[OpCopyMemorySized].operands.push(OperandMemoryAccess, "", true); + + InstructionDesc[OpSampledImage].operands.push(OperandId, "'Image'"); + InstructionDesc[OpSampledImage].operands.push(OperandId, "'Sampler'"); + + InstructionDesc[OpImage].operands.push(OperandId, "'Sampled Image'"); + + InstructionDesc[OpImageRead].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageRead].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageRead].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageRead].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageWrite].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageWrite].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageWrite].operands.push(OperandId, "'Texel'"); + InstructionDesc[OpImageWrite].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageWrite].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageFetch].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageFetch].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageFetch].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageFetch].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageGather].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageGather].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageGather].operands.push(OperandId, "'Component'"); + InstructionDesc[OpImageGather].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageGather].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageDrefGather].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageDrefGather].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageDrefGather].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageDrefGather].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageDrefGather].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseFetch].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageSparseFetch].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseFetch].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseFetch].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseGather].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseGather].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseGather].operands.push(OperandId, "'Component'"); + InstructionDesc[OpImageSparseGather].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseGather].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseRead].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageSparseRead].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseRead].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseRead].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseTexelsResident].operands.push(OperandId, "'Resident Code'"); + + InstructionDesc[OpImageQuerySizeLod].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageQuerySizeLod].operands.push(OperandId, "'Level of Detail'"); + + InstructionDesc[OpImageQuerySize].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpImageQueryLod].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageQueryLod].operands.push(OperandId, "'Coordinate'"); + + InstructionDesc[OpImageQueryLevels].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpImageQuerySamples].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpImageQueryFormat].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpImageQueryOrder].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpAccessChain].operands.push(OperandId, "'Base'"); + InstructionDesc[OpAccessChain].operands.push(OperandVariableIds, "'Indexes'"); + + InstructionDesc[OpInBoundsAccessChain].operands.push(OperandId, "'Base'"); + InstructionDesc[OpInBoundsAccessChain].operands.push(OperandVariableIds, "'Indexes'"); + + InstructionDesc[OpPtrAccessChain].operands.push(OperandId, "'Base'"); + InstructionDesc[OpPtrAccessChain].operands.push(OperandId, "'Element'"); + InstructionDesc[OpPtrAccessChain].operands.push(OperandVariableIds, "'Indexes'"); + + InstructionDesc[OpInBoundsPtrAccessChain].operands.push(OperandId, "'Base'"); + InstructionDesc[OpInBoundsPtrAccessChain].operands.push(OperandId, "'Element'"); + InstructionDesc[OpInBoundsPtrAccessChain].operands.push(OperandVariableIds, "'Indexes'"); + + InstructionDesc[OpSNegate].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpFNegate].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpNot].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpAny].operands.push(OperandId, "'Vector'"); + + InstructionDesc[OpAll].operands.push(OperandId, "'Vector'"); + + InstructionDesc[OpConvertFToU].operands.push(OperandId, "'Float Value'"); + + InstructionDesc[OpConvertFToS].operands.push(OperandId, "'Float Value'"); + + InstructionDesc[OpConvertSToF].operands.push(OperandId, "'Signed Value'"); + + InstructionDesc[OpConvertUToF].operands.push(OperandId, "'Unsigned Value'"); + + InstructionDesc[OpUConvert].operands.push(OperandId, "'Unsigned Value'"); + + InstructionDesc[OpSConvert].operands.push(OperandId, "'Signed Value'"); + + InstructionDesc[OpFConvert].operands.push(OperandId, "'Float Value'"); + + InstructionDesc[OpSatConvertSToU].operands.push(OperandId, "'Signed Value'"); + + InstructionDesc[OpSatConvertUToS].operands.push(OperandId, "'Unsigned Value'"); + + InstructionDesc[OpConvertPtrToU].operands.push(OperandId, "'Pointer'"); + + InstructionDesc[OpConvertUToPtr].operands.push(OperandId, "'Integer Value'"); + + InstructionDesc[OpPtrCastToGeneric].operands.push(OperandId, "'Pointer'"); + + InstructionDesc[OpGenericCastToPtr].operands.push(OperandId, "'Pointer'"); + + InstructionDesc[OpGenericCastToPtrExplicit].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpGenericCastToPtrExplicit].operands.push(OperandStorage, "'Storage'"); + + InstructionDesc[OpGenericPtrMemSemantics].operands.push(OperandId, "'Pointer'"); + + InstructionDesc[OpBitcast].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpQuantizeToF16].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpTranspose].operands.push(OperandId, "'Matrix'"); + + InstructionDesc[OpCopyLogical].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpIsNan].operands.push(OperandId, "'x'"); + + InstructionDesc[OpIsInf].operands.push(OperandId, "'x'"); + + InstructionDesc[OpIsFinite].operands.push(OperandId, "'x'"); + + InstructionDesc[OpIsNormal].operands.push(OperandId, "'x'"); + + InstructionDesc[OpSignBitSet].operands.push(OperandId, "'x'"); + + InstructionDesc[OpLessOrGreater].operands.push(OperandId, "'x'"); + InstructionDesc[OpLessOrGreater].operands.push(OperandId, "'y'"); + + InstructionDesc[OpOrdered].operands.push(OperandId, "'x'"); + InstructionDesc[OpOrdered].operands.push(OperandId, "'y'"); + + InstructionDesc[OpUnordered].operands.push(OperandId, "'x'"); + InstructionDesc[OpUnordered].operands.push(OperandId, "'y'"); + + InstructionDesc[OpArrayLength].operands.push(OperandId, "'Structure'"); + InstructionDesc[OpArrayLength].operands.push(OperandLiteralNumber, "'Array member'"); + + InstructionDesc[OpIAdd].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpIAdd].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFAdd].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFAdd].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpISub].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpISub].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFSub].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFSub].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpIMul].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpIMul].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFMul].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFMul].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUDiv].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUDiv].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSDiv].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSDiv].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFDiv].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFDiv].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUMod].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUMod].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSRem].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSRem].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSMod].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSMod].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFRem].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFRem].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFMod].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFMod].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpVectorTimesScalar].operands.push(OperandId, "'Vector'"); + InstructionDesc[OpVectorTimesScalar].operands.push(OperandId, "'Scalar'"); + + InstructionDesc[OpMatrixTimesScalar].operands.push(OperandId, "'Matrix'"); + InstructionDesc[OpMatrixTimesScalar].operands.push(OperandId, "'Scalar'"); + + InstructionDesc[OpVectorTimesMatrix].operands.push(OperandId, "'Vector'"); + InstructionDesc[OpVectorTimesMatrix].operands.push(OperandId, "'Matrix'"); + + InstructionDesc[OpMatrixTimesVector].operands.push(OperandId, "'Matrix'"); + InstructionDesc[OpMatrixTimesVector].operands.push(OperandId, "'Vector'"); + + InstructionDesc[OpMatrixTimesMatrix].operands.push(OperandId, "'LeftMatrix'"); + InstructionDesc[OpMatrixTimesMatrix].operands.push(OperandId, "'RightMatrix'"); + + InstructionDesc[OpOuterProduct].operands.push(OperandId, "'Vector 1'"); + InstructionDesc[OpOuterProduct].operands.push(OperandId, "'Vector 2'"); + + InstructionDesc[OpDot].operands.push(OperandId, "'Vector 1'"); + InstructionDesc[OpDot].operands.push(OperandId, "'Vector 2'"); + + InstructionDesc[OpIAddCarry].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpIAddCarry].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpISubBorrow].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpISubBorrow].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUMulExtended].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUMulExtended].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSMulExtended].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSMulExtended].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpShiftRightLogical].operands.push(OperandId, "'Base'"); + InstructionDesc[OpShiftRightLogical].operands.push(OperandId, "'Shift'"); + + InstructionDesc[OpShiftRightArithmetic].operands.push(OperandId, "'Base'"); + InstructionDesc[OpShiftRightArithmetic].operands.push(OperandId, "'Shift'"); + + InstructionDesc[OpShiftLeftLogical].operands.push(OperandId, "'Base'"); + InstructionDesc[OpShiftLeftLogical].operands.push(OperandId, "'Shift'"); + + InstructionDesc[OpLogicalOr].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpLogicalOr].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpLogicalAnd].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpLogicalAnd].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpLogicalEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpLogicalEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpLogicalNotEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpLogicalNotEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpLogicalNot].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpBitwiseOr].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpBitwiseOr].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpBitwiseXor].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpBitwiseXor].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpBitwiseAnd].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpBitwiseAnd].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Base'"); + InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Insert'"); + InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Offset'"); + InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Count'"); + + InstructionDesc[OpBitFieldSExtract].operands.push(OperandId, "'Base'"); + InstructionDesc[OpBitFieldSExtract].operands.push(OperandId, "'Offset'"); + InstructionDesc[OpBitFieldSExtract].operands.push(OperandId, "'Count'"); + + InstructionDesc[OpBitFieldUExtract].operands.push(OperandId, "'Base'"); + InstructionDesc[OpBitFieldUExtract].operands.push(OperandId, "'Offset'"); + InstructionDesc[OpBitFieldUExtract].operands.push(OperandId, "'Count'"); + + InstructionDesc[OpBitReverse].operands.push(OperandId, "'Base'"); + + InstructionDesc[OpBitCount].operands.push(OperandId, "'Base'"); + + InstructionDesc[OpSelect].operands.push(OperandId, "'Condition'"); + InstructionDesc[OpSelect].operands.push(OperandId, "'Object 1'"); + InstructionDesc[OpSelect].operands.push(OperandId, "'Object 2'"); + + InstructionDesc[OpIEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpIEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpINotEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpINotEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdNotEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdNotEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordNotEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordNotEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpULessThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpULessThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSLessThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSLessThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdLessThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdLessThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordLessThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordLessThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUGreaterThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUGreaterThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSGreaterThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSGreaterThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdGreaterThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdGreaterThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordGreaterThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordGreaterThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpULessThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpULessThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSLessThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSLessThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdLessThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdLessThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordLessThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordLessThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUGreaterThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUGreaterThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSGreaterThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSGreaterThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdGreaterThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdGreaterThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordGreaterThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordGreaterThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpDPdx].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdy].operands.push(OperandId, "'P'"); + + InstructionDesc[OpFwidth].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdxFine].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdyFine].operands.push(OperandId, "'P'"); + + InstructionDesc[OpFwidthFine].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdxCoarse].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdyCoarse].operands.push(OperandId, "'P'"); + + InstructionDesc[OpFwidthCoarse].operands.push(OperandId, "'P'"); + + InstructionDesc[OpEmitStreamVertex].operands.push(OperandId, "'Stream'"); + + InstructionDesc[OpEndStreamPrimitive].operands.push(OperandId, "'Stream'"); + + InstructionDesc[OpControlBarrier].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpControlBarrier].operands.push(OperandScope, "'Memory'"); + InstructionDesc[OpControlBarrier].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpMemoryBarrier].operands.push(OperandScope, "'Memory'"); + InstructionDesc[OpMemoryBarrier].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpImageTexelPointer].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageTexelPointer].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageTexelPointer].operands.push(OperandId, "'Sample'"); + + InstructionDesc[OpAtomicLoad].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicLoad].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicLoad].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpAtomicStore].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicStore].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicStore].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicStore].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicExchange].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicExchange].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicExchange].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicExchange].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandMemorySemantics, "'Equal'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandMemorySemantics, "'Unequal'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandId, "'Value'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandId, "'Comparator'"); + + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandMemorySemantics, "'Equal'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandMemorySemantics, "'Unequal'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandId, "'Value'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandId, "'Comparator'"); + + InstructionDesc[OpAtomicIIncrement].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicIIncrement].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicIIncrement].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpAtomicIDecrement].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicIDecrement].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicIDecrement].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpAtomicIAdd].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicIAdd].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicIAdd].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicIAdd].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicISub].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicISub].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicISub].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicISub].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicUMin].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicUMin].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicUMin].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicUMin].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicUMax].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicUMax].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicUMax].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicUMax].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicSMin].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicSMin].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicSMin].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicSMin].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicSMax].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicSMax].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicSMax].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicSMax].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicAnd].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicAnd].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicAnd].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicAnd].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicOr].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicOr].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicOr].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicOr].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicXor].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicXor].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicXor].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicXor].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicFlagTestAndSet].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicFlagTestAndSet].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicFlagTestAndSet].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpAtomicFlagClear].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicFlagClear].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicFlagClear].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpLoopMerge].operands.push(OperandId, "'Merge Block'"); + InstructionDesc[OpLoopMerge].operands.push(OperandId, "'Continue Target'"); + InstructionDesc[OpLoopMerge].operands.push(OperandLoop, ""); + InstructionDesc[OpLoopMerge].operands.push(OperandOptionalLiteral, ""); + + InstructionDesc[OpSelectionMerge].operands.push(OperandId, "'Merge Block'"); + InstructionDesc[OpSelectionMerge].operands.push(OperandSelect, ""); + + InstructionDesc[OpBranch].operands.push(OperandId, "'Target Label'"); + + InstructionDesc[OpBranchConditional].operands.push(OperandId, "'Condition'"); + InstructionDesc[OpBranchConditional].operands.push(OperandId, "'True Label'"); + InstructionDesc[OpBranchConditional].operands.push(OperandId, "'False Label'"); + InstructionDesc[OpBranchConditional].operands.push(OperandVariableLiterals, "'Branch weights'"); + + InstructionDesc[OpSwitch].operands.push(OperandId, "'Selector'"); + InstructionDesc[OpSwitch].operands.push(OperandId, "'Default'"); + InstructionDesc[OpSwitch].operands.push(OperandVariableLiteralId, "'Target'"); + + + InstructionDesc[OpReturnValue].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpLifetimeStart].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpLifetimeStart].operands.push(OperandLiteralNumber, "'Size'"); + + InstructionDesc[OpLifetimeStop].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpLifetimeStop].operands.push(OperandLiteralNumber, "'Size'"); + + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Destination'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Source'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Num Elements'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Stride'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Event'"); + + InstructionDesc[OpGroupWaitEvents].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupWaitEvents].operands.push(OperandId, "'Num Events'"); + InstructionDesc[OpGroupWaitEvents].operands.push(OperandId, "'Events List'"); + + InstructionDesc[OpGroupAll].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupAll].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpGroupAny].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupAny].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpGroupBroadcast].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupBroadcast].operands.push(OperandId, "'Value'"); + InstructionDesc[OpGroupBroadcast].operands.push(OperandId, "'LocalId'"); + + InstructionDesc[OpGroupIAdd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupIAdd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupIAdd].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupFAdd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFAdd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFAdd].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupUMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMin].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupSMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMin].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMin].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupUMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMax].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupSMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMax].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMax].operands.push(OperandId, "X"); + + InstructionDesc[OpReadPipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReadPipe].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpReadPipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReadPipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpWritePipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpWritePipe].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpWritePipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpWritePipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Index'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Index'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Num Packets'"); + InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Num Packets'"); + InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpIsValidReserveId].operands.push(OperandId, "'Reserve Id'"); + + InstructionDesc[OpGetNumPipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGetNumPipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGetNumPipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGetMaxPipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGetMaxPipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGetMaxPipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Num Packets'"); + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Num Packets'"); + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpBuildNDRange].operands.push(OperandId, "'GlobalWorkSize'"); + InstructionDesc[OpBuildNDRange].operands.push(OperandId, "'LocalWorkSize'"); + InstructionDesc[OpBuildNDRange].operands.push(OperandId, "'GlobalWorkOffset'"); + + InstructionDesc[OpCaptureEventProfilingInfo].operands.push(OperandId, "'Event'"); + InstructionDesc[OpCaptureEventProfilingInfo].operands.push(OperandId, "'Profiling Info'"); + InstructionDesc[OpCaptureEventProfilingInfo].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpSetUserEventStatus].operands.push(OperandId, "'Event'"); + InstructionDesc[OpSetUserEventStatus].operands.push(OperandId, "'Status'"); + + InstructionDesc[OpIsValidEvent].operands.push(OperandId, "'Event'"); + + InstructionDesc[OpRetainEvent].operands.push(OperandId, "'Event'"); + + InstructionDesc[OpReleaseEvent].operands.push(OperandId, "'Event'"); + + InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Param'"); + InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Param Align'"); + + InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Param'"); + InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Param Align'"); + + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'ND Range'"); + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Param'"); + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Param Align'"); + + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'ND Range'"); + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Param'"); + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Param Align'"); + + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Queue'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Flags'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'ND Range'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Num Events'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Wait Events'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Ret Event'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Param'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Param Align'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandVariableIds, "'Local Size'"); + + InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Queue'"); + InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Num Events'"); + InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Wait Events'"); + InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Ret Event'"); + + InstructionDesc[OpGroupNonUniformElect].operands.push(OperandScope, "'Execution'"); + + InstructionDesc[OpGroupNonUniformAll].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformAll].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformAny].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformAny].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformAllEqual].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformAllEqual].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBroadcast].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBroadcast].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBroadcast].operands.push(OperandId, "ID"); + + InstructionDesc[OpGroupNonUniformBroadcastFirst].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBroadcastFirst].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBallot].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallot].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformInverseBallot].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformInverseBallot].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBallotBitExtract].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallotBitExtract].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBallotBitExtract].operands.push(OperandId, "Bit"); + + InstructionDesc[OpGroupNonUniformBallotBitCount].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallotBitCount].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformBallotBitCount].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBallotFindLSB].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallotFindLSB].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBallotFindMSB].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallotFindMSB].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformShuffle].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformShuffle].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformShuffle].operands.push(OperandId, "'Id'"); + + InstructionDesc[OpGroupNonUniformShuffleXor].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformShuffleXor].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformShuffleXor].operands.push(OperandId, "Mask"); + + InstructionDesc[OpGroupNonUniformShuffleUp].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformShuffleUp].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformShuffleUp].operands.push(OperandId, "Offset"); + + InstructionDesc[OpGroupNonUniformShuffleDown].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformShuffleDown].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformShuffleDown].operands.push(OperandId, "Offset"); + + InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformQuadBroadcast].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformQuadBroadcast].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformQuadBroadcast].operands.push(OperandId, "'Id'"); + + InstructionDesc[OpGroupNonUniformQuadSwap].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformQuadSwap].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformQuadSwap].operands.push(OperandLiteralNumber, "'Direction'"); + + InstructionDesc[OpSubgroupBallotKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupFirstInvocationKHR].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpSubgroupAnyKHR].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpSubgroupAnyKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupAllKHR].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpSubgroupAllKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupAllEqualKHR].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpSubgroupAllEqualKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupReadInvocationKHR].operands.push(OperandId, "'Value'"); + InstructionDesc[OpSubgroupReadInvocationKHR].operands.push(OperandId, "'Index'"); + + InstructionDesc[OpModuleProcessed].operands.push(OperandLiteralString, "'process'"); + + InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpFragmentMaskFetchAMD].operands.push(OperandId, "'Image'"); + InstructionDesc[OpFragmentMaskFetchAMD].operands.push(OperandId, "'Coordinate'"); + + InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Image'"); + InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Fragment Index'"); + + InstructionDesc[OpGroupNonUniformPartitionNV].operands.push(OperandId, "X"); + + InstructionDesc[OpTypeAccelerationStructureKHR].setResultAndType(true, false); + + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'NV Acceleration Structure'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Ray Flags'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Cull Mask'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'SBT Record Offset'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'SBT Record Stride'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Miss Index'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Ray Origin'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'TMin'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Ray Direction'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'TMax'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Payload'"); + InstructionDesc[OpTraceRayKHR].setResultAndType(false, false); + + InstructionDesc[OpReportIntersectionKHR].operands.push(OperandId, "'Hit Parameter'"); + InstructionDesc[OpReportIntersectionKHR].operands.push(OperandId, "'Hit Kind'"); + + InstructionDesc[OpIgnoreIntersectionKHR].setResultAndType(false, false); + + InstructionDesc[OpTerminateRayKHR].setResultAndType(false, false); + + InstructionDesc[OpExecuteCallableKHR].operands.push(OperandId, "SBT Record Index"); + InstructionDesc[OpExecuteCallableKHR].operands.push(OperandId, "CallableData ID"); + InstructionDesc[OpExecuteCallableKHR].setResultAndType(false, false); + + // Ray Query + InstructionDesc[OpTypeAccelerationStructureKHR].setResultAndType(true, false); + InstructionDesc[OpTypeRayQueryProvisionalKHR].setResultAndType(true, false); + + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'AccelerationS'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'RayFlags'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'CullMask'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'Origin'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'Tmin'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'Direction'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'Tmax'"); + InstructionDesc[OpRayQueryInitializeKHR].setResultAndType(false, false); + + InstructionDesc[OpRayQueryTerminateKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryTerminateKHR].setResultAndType(false, false); + + InstructionDesc[OpRayQueryGenerateIntersectionKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGenerateIntersectionKHR].operands.push(OperandId, "'THit'"); + InstructionDesc[OpRayQueryGenerateIntersectionKHR].setResultAndType(false, false); + + InstructionDesc[OpRayQueryConfirmIntersectionKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryConfirmIntersectionKHR].setResultAndType(false, false); + + InstructionDesc[OpRayQueryProceedKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryProceedKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionTypeKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionTypeKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionTypeKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetRayTMinKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetRayTMinKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetRayFlagsKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetRayFlagsKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionTKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionTKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionTKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionInstanceCustomIndexKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceCustomIndexKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceCustomIndexKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionInstanceIdKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceIdKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceIdKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionGeometryIndexKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionGeometryIndexKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionGeometryIndexKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionPrimitiveIndexKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionPrimitiveIndexKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionPrimitiveIndexKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionBarycentricsKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionBarycentricsKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionBarycentricsKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionFrontFaceKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionFrontFaceKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionFrontFaceKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionCandidateAABBOpaqueKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionCandidateAABBOpaqueKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionObjectRayDirectionKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionObjectRayDirectionKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionObjectRayDirectionKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionObjectRayOriginKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionObjectRayOriginKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionObjectRayOriginKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetWorldRayDirectionKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetWorldRayDirectionKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetWorldRayOriginKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetWorldRayOriginKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionObjectToWorldKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionObjectToWorldKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionObjectToWorldKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionWorldToObjectKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionWorldToObjectKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionWorldToObjectKHR].setResultAndType(true, true); + + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Granularity'"); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Coarse'"); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpWritePackedPrimitiveIndices4x8NV].operands.push(OperandId, "'Index Offset'"); + InstructionDesc[OpWritePackedPrimitiveIndices4x8NV].operands.push(OperandId, "'Packed Indices'"); + + InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Component Type'"); + InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Scope'"); + InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Rows'"); + InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Columns'"); + + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "'Stride'"); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "'Column Major'"); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandMemoryAccess, "'Memory Access'"); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "", true); + + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Object'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Stride'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Column Major'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandMemoryAccess, "'Memory Access'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "", true); + + InstructionDesc[OpCooperativeMatrixMulAddNV].operands.push(OperandId, "'A'"); + InstructionDesc[OpCooperativeMatrixMulAddNV].operands.push(OperandId, "'B'"); + InstructionDesc[OpCooperativeMatrixMulAddNV].operands.push(OperandId, "'C'"); + + InstructionDesc[OpCooperativeMatrixLengthNV].operands.push(OperandId, "'Type'"); + + InstructionDesc[OpDemoteToHelperInvocationEXT].setResultAndType(false, false); + + InstructionDesc[OpReadClockKHR].operands.push(OperandScope, "'Scope'"); +} + +}; // end spv namespace diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/doc.h b/armorcore/tools/to_spirv/glslang/SPIRV/doc.h new file mode 100644 index 000000000..293256a2c --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/doc.h @@ -0,0 +1,258 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Parameterize the SPIR-V enumerants. +// + +#pragma once + +#include "spirv.hpp" + +#include + +namespace spv { + +// Fill in all the parameters +void Parameterize(); + +// Return the English names of all the enums. +const char* SourceString(int); +const char* AddressingString(int); +const char* MemoryString(int); +const char* ExecutionModelString(int); +const char* ExecutionModeString(int); +const char* StorageClassString(int); +const char* DecorationString(int); +const char* BuiltInString(int); +const char* DimensionString(int); +const char* SelectControlString(int); +const char* LoopControlString(int); +const char* FunctionControlString(int); +const char* SamplerAddressingModeString(int); +const char* SamplerFilterModeString(int); +const char* ImageFormatString(int); +const char* ImageChannelOrderString(int); +const char* ImageChannelTypeString(int); +const char* ImageChannelDataTypeString(int type); +const char* ImageOperandsString(int format); +const char* ImageOperands(int); +const char* FPFastMathString(int); +const char* FPRoundingModeString(int); +const char* LinkageTypeString(int); +const char* FuncParamAttrString(int); +const char* AccessQualifierString(int); +const char* MemorySemanticsString(int); +const char* MemoryAccessString(int); +const char* ExecutionScopeString(int); +const char* GroupOperationString(int); +const char* KernelEnqueueFlagsString(int); +const char* KernelProfilingInfoString(int); +const char* CapabilityString(int); +const char* OpcodeString(int); +const char* ScopeString(int mem); + +// For grouping opcodes into subsections +enum OpcodeClass { + OpClassMisc, + OpClassDebug, + OpClassAnnotate, + OpClassExtension, + OpClassMode, + OpClassType, + OpClassConstant, + OpClassMemory, + OpClassFunction, + OpClassImage, + OpClassConvert, + OpClassComposite, + OpClassArithmetic, + OpClassBit, + OpClassRelationalLogical, + OpClassDerivative, + OpClassFlowControl, + OpClassAtomic, + OpClassPrimitive, + OpClassBarrier, + OpClassGroup, + OpClassDeviceSideEnqueue, + OpClassPipe, + + OpClassCount, + OpClassMissing // all instructions start out as missing +}; + +// For parameterizing operands. +enum OperandClass { + OperandNone, + OperandId, + OperandVariableIds, + OperandOptionalLiteral, + OperandOptionalLiteralString, + OperandVariableLiterals, + OperandVariableIdLiteral, + OperandVariableLiteralId, + OperandLiteralNumber, + OperandLiteralString, + OperandSource, + OperandExecutionModel, + OperandAddressing, + OperandMemory, + OperandExecutionMode, + OperandStorage, + OperandDimensionality, + OperandSamplerAddressingMode, + OperandSamplerFilterMode, + OperandSamplerImageFormat, + OperandImageChannelOrder, + OperandImageChannelDataType, + OperandImageOperands, + OperandFPFastMath, + OperandFPRoundingMode, + OperandLinkageType, + OperandAccessQualifier, + OperandFuncParamAttr, + OperandDecoration, + OperandBuiltIn, + OperandSelect, + OperandLoop, + OperandFunction, + OperandMemorySemantics, + OperandMemoryAccess, + OperandScope, + OperandGroupOperation, + OperandKernelEnqueueFlags, + OperandKernelProfilingInfo, + OperandCapability, + + OperandOpcode, + + OperandCount +}; + +// Any specific enum can have a set of capabilities that allow it: +typedef std::vector EnumCaps; + +// Parameterize a set of operands with their OperandClass(es) and descriptions. +class OperandParameters { +public: + OperandParameters() { } + void push(OperandClass oc, const char* d, bool opt = false) + { + opClass.push_back(oc); + desc.push_back(d); + optional.push_back(opt); + } + void setOptional(); + OperandClass getClass(int op) const { return opClass[op]; } + const char* getDesc(int op) const { return desc[op]; } + bool isOptional(int op) const { return optional[op]; } + int getNum() const { return (int)opClass.size(); } + +protected: + std::vector opClass; + std::vector desc; + std::vector optional; +}; + +// Parameterize an enumerant +class EnumParameters { +public: + EnumParameters() : desc(0) { } + const char* desc; +}; + +// Parameterize a set of enumerants that form an enum +class EnumDefinition : public EnumParameters { +public: + EnumDefinition() : + ceiling(0), bitmask(false), getName(0), enumParams(0), operandParams(0) { } + void set(int ceil, const char* (*name)(int), EnumParameters* ep, bool mask = false) + { + ceiling = ceil; + getName = name; + bitmask = mask; + enumParams = ep; + } + void setOperands(OperandParameters* op) { operandParams = op; } + int ceiling; // ceiling of enumerants + bool bitmask; // true if these enumerants combine into a bitmask + const char* (*getName)(int); // a function that returns the name for each enumerant value (or shift) + EnumParameters* enumParams; // parameters for each individual enumerant + OperandParameters* operandParams; // sets of operands +}; + +// Parameterize an instruction's logical format, including its known set of operands, +// per OperandParameters above. +class InstructionParameters { +public: + InstructionParameters() : + opDesc("TBD"), + opClass(OpClassMissing), + typePresent(true), // most normal, only exceptions have to be spelled out + resultPresent(true) // most normal, only exceptions have to be spelled out + { } + + void setResultAndType(bool r, bool t) + { + resultPresent = r; + typePresent = t; + } + + bool hasResult() const { return resultPresent != 0; } + bool hasType() const { return typePresent != 0; } + + const char* opDesc; + OpcodeClass opClass; + OperandParameters operands; + +protected: + int typePresent : 1; + int resultPresent : 1; +}; + +// The set of objects that hold all the instruction/operand +// parameterization information. +extern InstructionParameters InstructionDesc[]; + +// These hold definitions of the enumerants used for operands +extern EnumDefinition OperandClassParams[]; + +const char* GetOperandDesc(OperandClass operand); +void PrintImmediateRow(int imm, const char* name, const EnumParameters* enumParams, bool caps, bool hex = false); +const char* AccessQualifierString(int attr); + +void PrintOperands(const OperandParameters& operands, int reservedOperands); + +} // end namespace spv diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/hex_float.h b/armorcore/tools/to_spirv/glslang/SPIRV/hex_float.h new file mode 100644 index 000000000..8be8e9f7e --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/hex_float.h @@ -0,0 +1,1078 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_UTIL_HEX_FLOAT_H_ +#define LIBSPIRV_UTIL_HEX_FLOAT_H_ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1800 +namespace std { +bool isnan(double f) +{ + return ::_isnan(f) != 0; +} +bool isinf(double f) +{ + return ::_finite(f) == 0; +} +} +#endif + +#include "bitutils.h" + +namespace spvutils { + +class Float16 { + public: + Float16(uint16_t v) : val(v) {} + Float16() {} + static bool isNan(const Float16& val) { + return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) != 0); + } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(const Float16& val) { + return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) == 0); + } + Float16(const Float16& other) { val = other.val; } + uint16_t get_value() const { return val; } + + // Returns the maximum normal value. + static Float16 max() { return Float16(0x7bff); } + // Returns the lowest normal value. + static Float16 lowest() { return Float16(0xfbff); } + + private: + uint16_t val; +}; + +// To specialize this type, you must override uint_type to define +// an unsigned integer that can fit your floating point type. +// You must also add a isNan function that returns true if +// a value is Nan. +template +struct FloatProxyTraits { + typedef void uint_type; +}; + +template <> +struct FloatProxyTraits { + typedef uint32_t uint_type; + static bool isNan(float f) { return std::isnan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(float f) { return std::isinf(f); } + // Returns the maximum normal value. + static float max() { return std::numeric_limits::max(); } + // Returns the lowest normal value. + static float lowest() { return std::numeric_limits::lowest(); } +}; + +template <> +struct FloatProxyTraits { + typedef uint64_t uint_type; + static bool isNan(double f) { return std::isnan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(double f) { return std::isinf(f); } + // Returns the maximum normal value. + static double max() { return std::numeric_limits::max(); } + // Returns the lowest normal value. + static double lowest() { return std::numeric_limits::lowest(); } +}; + +template <> +struct FloatProxyTraits { + typedef uint16_t uint_type; + static bool isNan(Float16 f) { return Float16::isNan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(Float16 f) { return Float16::isInfinity(f); } + // Returns the maximum normal value. + static Float16 max() { return Float16::max(); } + // Returns the lowest normal value. + static Float16 lowest() { return Float16::lowest(); } +}; + +// Since copying a floating point number (especially if it is NaN) +// does not guarantee that bits are preserved, this class lets us +// store the type and use it as a float when necessary. +template +class FloatProxy { + public: + typedef typename FloatProxyTraits::uint_type uint_type; + + // Since this is to act similar to the normal floats, + // do not initialize the data by default. + FloatProxy() {} + + // Intentionally non-explicit. This is a proxy type so + // implicit conversions allow us to use it more transparently. + FloatProxy(T val) { data_ = BitwiseCast(val); } + + // Intentionally non-explicit. This is a proxy type so + // implicit conversions allow us to use it more transparently. + FloatProxy(uint_type val) { data_ = val; } + + // This is helpful to have and is guaranteed not to stomp bits. + FloatProxy operator-() const { + return static_cast(data_ ^ + (uint_type(0x1) << (sizeof(T) * 8 - 1))); + } + + // Returns the data as a floating point value. + T getAsFloat() const { return BitwiseCast(data_); } + + // Returns the raw data. + uint_type data() const { return data_; } + + // Returns true if the value represents any type of NaN. + bool isNan() { return FloatProxyTraits::isNan(getAsFloat()); } + // Returns true if the value represents any type of infinity. + bool isInfinity() { return FloatProxyTraits::isInfinity(getAsFloat()); } + + // Returns the maximum normal value. + static FloatProxy max() { + return FloatProxy(FloatProxyTraits::max()); + } + // Returns the lowest normal value. + static FloatProxy lowest() { + return FloatProxy(FloatProxyTraits::lowest()); + } + + private: + uint_type data_; +}; + +template +bool operator==(const FloatProxy& first, const FloatProxy& second) { + return first.data() == second.data(); +} + +// Reads a FloatProxy value as a normal float from a stream. +template +std::istream& operator>>(std::istream& is, FloatProxy& value) { + T float_val; + is >> float_val; + value = FloatProxy(float_val); + return is; +} + +// This is an example traits. It is not meant to be used in practice, but will +// be the default for any non-specialized type. +template +struct HexFloatTraits { + // Integer type that can store this hex-float. + typedef void uint_type; + // Signed integer type that can store this hex-float. + typedef void int_type; + // The numerical type that this HexFloat represents. + typedef void underlying_type; + // The type needed to construct the underlying type. + typedef void native_type; + // The number of bits that are actually relevant in the uint_type. + // This allows us to deal with, for example, 24-bit values in a 32-bit + // integer. + static const uint32_t num_used_bits = 0; + // Number of bits that represent the exponent. + static const uint32_t num_exponent_bits = 0; + // Number of bits that represent the fractional part. + static const uint32_t num_fraction_bits = 0; + // The bias of the exponent. (How much we need to subtract from the stored + // value to get the correct value.) + static const uint32_t exponent_bias = 0; +}; + +// Traits for IEEE float. +// 1 sign bit, 8 exponent bits, 23 fractional bits. +template <> +struct HexFloatTraits> { + typedef uint32_t uint_type; + typedef int32_t int_type; + typedef FloatProxy underlying_type; + typedef float native_type; + static const uint_type num_used_bits = 32; + static const uint_type num_exponent_bits = 8; + static const uint_type num_fraction_bits = 23; + static const uint_type exponent_bias = 127; +}; + +// Traits for IEEE double. +// 1 sign bit, 11 exponent bits, 52 fractional bits. +template <> +struct HexFloatTraits> { + typedef uint64_t uint_type; + typedef int64_t int_type; + typedef FloatProxy underlying_type; + typedef double native_type; + static const uint_type num_used_bits = 64; + static const uint_type num_exponent_bits = 11; + static const uint_type num_fraction_bits = 52; + static const uint_type exponent_bias = 1023; +}; + +// Traits for IEEE half. +// 1 sign bit, 5 exponent bits, 10 fractional bits. +template <> +struct HexFloatTraits> { + typedef uint16_t uint_type; + typedef int16_t int_type; + typedef uint16_t underlying_type; + typedef uint16_t native_type; + static const uint_type num_used_bits = 16; + static const uint_type num_exponent_bits = 5; + static const uint_type num_fraction_bits = 10; + static const uint_type exponent_bias = 15; +}; + +enum round_direction { + kRoundToZero, + kRoundToNearestEven, + kRoundToPositiveInfinity, + kRoundToNegativeInfinity +}; + +// Template class that houses a floating pointer number. +// It exposes a number of constants based on the provided traits to +// assist in interpreting the bits of the value. +template > +class HexFloat { + public: + typedef typename Traits::uint_type uint_type; + typedef typename Traits::int_type int_type; + typedef typename Traits::underlying_type underlying_type; + typedef typename Traits::native_type native_type; + + explicit HexFloat(T f) : value_(f) {} + + T value() const { return value_; } + void set_value(T f) { value_ = f; } + + // These are all written like this because it is convenient to have + // compile-time constants for all of these values. + + // Pass-through values to save typing. + static const uint32_t num_used_bits = Traits::num_used_bits; + static const uint32_t exponent_bias = Traits::exponent_bias; + static const uint32_t num_exponent_bits = Traits::num_exponent_bits; + static const uint32_t num_fraction_bits = Traits::num_fraction_bits; + + // Number of bits to shift left to set the highest relevant bit. + static const uint32_t top_bit_left_shift = num_used_bits - 1; + // How many nibbles (hex characters) the fractional part takes up. + static const uint32_t fraction_nibbles = (num_fraction_bits + 3) / 4; + // If the fractional part does not fit evenly into a hex character (4-bits) + // then we have to left-shift to get rid of leading 0s. This is the amount + // we have to shift (might be 0). + static const uint32_t num_overflow_bits = + fraction_nibbles * 4 - num_fraction_bits; + + // The representation of the fraction, not the actual bits. This + // includes the leading bit that is usually implicit. + static const uint_type fraction_represent_mask = + spvutils::SetBits::get; + + // The topmost bit in the nibble-aligned fraction. + static const uint_type fraction_top_bit = + uint_type(1) << (num_fraction_bits + num_overflow_bits - 1); + + // The least significant bit in the exponent, which is also the bit + // immediately to the left of the significand. + static const uint_type first_exponent_bit = uint_type(1) + << (num_fraction_bits); + + // The mask for the encoded fraction. It does not include the + // implicit bit. + static const uint_type fraction_encode_mask = + spvutils::SetBits::get; + + // The bit that is used as a sign. + static const uint_type sign_mask = uint_type(1) << top_bit_left_shift; + + // The bits that represent the exponent. + static const uint_type exponent_mask = + spvutils::SetBits::get; + + // How far left the exponent is shifted. + static const uint32_t exponent_left_shift = num_fraction_bits; + + // How far from the right edge the fraction is shifted. + static const uint32_t fraction_right_shift = + static_cast(sizeof(uint_type) * 8) - num_fraction_bits; + + // The maximum representable unbiased exponent. + static const int_type max_exponent = + (exponent_mask >> num_fraction_bits) - exponent_bias; + // The minimum representable exponent for normalized numbers. + static const int_type min_exponent = -static_cast(exponent_bias); + + // Returns the bits associated with the value. + uint_type getBits() const { return spvutils::BitwiseCast(value_); } + + // Returns the bits associated with the value, without the leading sign bit. + uint_type getUnsignedBits() const { + return static_cast(spvutils::BitwiseCast(value_) & + ~sign_mask); + } + + // Returns the bits associated with the exponent, shifted to start at the + // lsb of the type. + const uint_type getExponentBits() const { + return static_cast((getBits() & exponent_mask) >> + num_fraction_bits); + } + + // Returns the exponent in unbiased form. This is the exponent in the + // human-friendly form. + const int_type getUnbiasedExponent() const { + return static_cast(getExponentBits() - exponent_bias); + } + + // Returns just the significand bits from the value. + const uint_type getSignificandBits() const { + return getBits() & fraction_encode_mask; + } + + // If the number was normalized, returns the unbiased exponent. + // If the number was denormal, normalize the exponent first. + const int_type getUnbiasedNormalizedExponent() const { + if ((getBits() & ~sign_mask) == 0) { // special case if everything is 0 + return 0; + } + int_type exp = getUnbiasedExponent(); + if (exp == min_exponent) { // We are in denorm land. + uint_type significand_bits = getSignificandBits(); + while ((significand_bits & (first_exponent_bit >> 1)) == 0) { + significand_bits = static_cast(significand_bits << 1); + exp = static_cast(exp - 1); + } + significand_bits &= fraction_encode_mask; + } + return exp; + } + + // Returns the signficand after it has been normalized. + const uint_type getNormalizedSignificand() const { + int_type unbiased_exponent = getUnbiasedNormalizedExponent(); + uint_type significand = getSignificandBits(); + for (int_type i = unbiased_exponent; i <= min_exponent; ++i) { + significand = static_cast(significand << 1); + } + significand &= fraction_encode_mask; + return significand; + } + + // Returns true if this number represents a negative value. + bool isNegative() const { return (getBits() & sign_mask) != 0; } + + // Sets this HexFloat from the individual components. + // Note this assumes EVERY significand is normalized, and has an implicit + // leading one. This means that the only way that this method will set 0, + // is if you set a number so denormalized that it underflows. + // Do not use this method with raw bits extracted from a subnormal number, + // since subnormals do not have an implicit leading 1 in the significand. + // The significand is also expected to be in the + // lowest-most num_fraction_bits of the uint_type. + // The exponent is expected to be unbiased, meaning an exponent of + // 0 actually means 0. + // If underflow_round_up is set, then on underflow, if a number is non-0 + // and would underflow, we round up to the smallest denorm. + void setFromSignUnbiasedExponentAndNormalizedSignificand( + bool negative, int_type exponent, uint_type significand, + bool round_denorm_up) { + bool significand_is_zero = significand == 0; + + if (exponent <= min_exponent) { + // If this was denormalized, then we have to shift the bit on, meaning + // the significand is not zero. + significand_is_zero = false; + significand |= first_exponent_bit; + significand = static_cast(significand >> 1); + } + + while (exponent < min_exponent) { + significand = static_cast(significand >> 1); + ++exponent; + } + + if (exponent == min_exponent) { + if (significand == 0 && !significand_is_zero && round_denorm_up) { + significand = static_cast(0x1); + } + } + + uint_type new_value = 0; + if (negative) { + new_value = static_cast(new_value | sign_mask); + } + exponent = static_cast(exponent + exponent_bias); + assert(exponent >= 0); + + // put it all together + exponent = static_cast((exponent << exponent_left_shift) & + exponent_mask); + significand = static_cast(significand & fraction_encode_mask); + new_value = static_cast(new_value | (exponent | significand)); + value_ = BitwiseCast(new_value); + } + + // Increments the significand of this number by the given amount. + // If this would spill the significand into the implicit bit, + // carry is set to true and the significand is shifted to fit into + // the correct location, otherwise carry is set to false. + // All significands and to_increment are assumed to be within the bounds + // for a valid significand. + static uint_type incrementSignificand(uint_type significand, + uint_type to_increment, bool* carry) { + significand = static_cast(significand + to_increment); + *carry = false; + if (significand & first_exponent_bit) { + *carry = true; + // The implicit 1-bit will have carried, so we should zero-out the + // top bit and shift back. + significand = static_cast(significand & ~first_exponent_bit); + significand = static_cast(significand >> 1); + } + return significand; + } + + // These exist because MSVC throws warnings on negative right-shifts + // even if they are not going to be executed. Eg: + // constant_number < 0? 0: constant_number + // These convert the negative left-shifts into right shifts. + + template + uint_type negatable_left_shift(int_type N, uint_type val) + { + if(N >= 0) + return val << N; + + return val >> -N; + } + + template + uint_type negatable_right_shift(int_type N, uint_type val) + { + if(N >= 0) + return val >> N; + + return val << -N; + } + + // Returns the significand, rounded to fit in a significand in + // other_T. This is shifted so that the most significant + // bit of the rounded number lines up with the most significant bit + // of the returned significand. + template + typename other_T::uint_type getRoundedNormalizedSignificand( + round_direction dir, bool* carry_bit) { + typedef typename other_T::uint_type other_uint_type; + static const int_type num_throwaway_bits = + static_cast(num_fraction_bits) - + static_cast(other_T::num_fraction_bits); + + static const uint_type last_significant_bit = + (num_throwaway_bits < 0) + ? 0 + : negatable_left_shift(num_throwaway_bits, 1u); + static const uint_type first_rounded_bit = + (num_throwaway_bits < 1) + ? 0 + : negatable_left_shift(num_throwaway_bits - 1, 1u); + + static const uint_type throwaway_mask_bits = + num_throwaway_bits > 0 ? num_throwaway_bits : 0; + static const uint_type throwaway_mask = + spvutils::SetBits::get; + + *carry_bit = false; + other_uint_type out_val = 0; + uint_type significand = getNormalizedSignificand(); + // If we are up-casting, then we just have to shift to the right location. + if (num_throwaway_bits <= 0) { + out_val = static_cast(significand); + uint_type shift_amount = static_cast(-num_throwaway_bits); + out_val = static_cast(out_val << shift_amount); + return out_val; + } + + // If every non-representable bit is 0, then we don't have any casting to + // do. + if ((significand & throwaway_mask) == 0) { + return static_cast( + negatable_right_shift(num_throwaway_bits, significand)); + } + + bool round_away_from_zero = false; + // We actually have to narrow the significand here, so we have to follow the + // rounding rules. + switch (dir) { + case kRoundToZero: + break; + case kRoundToPositiveInfinity: + round_away_from_zero = !isNegative(); + break; + case kRoundToNegativeInfinity: + round_away_from_zero = isNegative(); + break; + case kRoundToNearestEven: + // Have to round down, round bit is 0 + if ((first_rounded_bit & significand) == 0) { + break; + } + if (((significand & throwaway_mask) & ~first_rounded_bit) != 0) { + // If any subsequent bit of the rounded portion is non-0 then we round + // up. + round_away_from_zero = true; + break; + } + // We are exactly half-way between 2 numbers, pick even. + if ((significand & last_significant_bit) != 0) { + // 1 for our last bit, round up. + round_away_from_zero = true; + break; + } + break; + } + + if (round_away_from_zero) { + return static_cast( + negatable_right_shift(num_throwaway_bits, incrementSignificand( + significand, last_significant_bit, carry_bit))); + } else { + return static_cast( + negatable_right_shift(num_throwaway_bits, significand)); + } + } + + // Casts this value to another HexFloat. If the cast is widening, + // then round_dir is ignored. If the cast is narrowing, then + // the result is rounded in the direction specified. + // This number will retain Nan and Inf values. + // It will also saturate to Inf if the number overflows, and + // underflow to (0 or min depending on rounding) if the number underflows. + template + void castTo(other_T& other, round_direction round_dir) { + other = other_T(static_cast(0)); + bool negate = isNegative(); + if (getUnsignedBits() == 0) { + if (negate) { + other.set_value(-other.value()); + } + return; + } + uint_type significand = getSignificandBits(); + bool carried = false; + typename other_T::uint_type rounded_significand = + getRoundedNormalizedSignificand(round_dir, &carried); + + int_type exponent = getUnbiasedExponent(); + if (exponent == min_exponent) { + // If we are denormal, normalize the exponent, so that we can encode + // easily. + exponent = static_cast(exponent + 1); + for (uint_type check_bit = first_exponent_bit >> 1; check_bit != 0; + check_bit = static_cast(check_bit >> 1)) { + exponent = static_cast(exponent - 1); + if (check_bit & significand) break; + } + } + + bool is_nan = + (getBits() & exponent_mask) == exponent_mask && significand != 0; + bool is_inf = + !is_nan && + ((exponent + carried) > static_cast(other_T::exponent_bias) || + (significand == 0 && (getBits() & exponent_mask) == exponent_mask)); + + // If we are Nan or Inf we should pass that through. + if (is_inf) { + other.set_value(BitwiseCast( + static_cast( + (negate ? other_T::sign_mask : 0) | other_T::exponent_mask))); + return; + } + if (is_nan) { + typename other_T::uint_type shifted_significand; + shifted_significand = static_cast( + negatable_left_shift( + static_cast(other_T::num_fraction_bits) - + static_cast(num_fraction_bits), significand)); + + // We are some sort of Nan. We try to keep the bit-pattern of the Nan + // as close as possible. If we had to shift off bits so we are 0, then we + // just set the last bit. + other.set_value(BitwiseCast( + static_cast( + (negate ? other_T::sign_mask : 0) | other_T::exponent_mask | + (shifted_significand == 0 ? 0x1 : shifted_significand)))); + return; + } + + bool round_underflow_up = + isNegative() ? round_dir == kRoundToNegativeInfinity + : round_dir == kRoundToPositiveInfinity; + typedef typename other_T::int_type other_int_type; + // setFromSignUnbiasedExponentAndNormalizedSignificand will + // zero out any underflowing value (but retain the sign). + other.setFromSignUnbiasedExponentAndNormalizedSignificand( + negate, static_cast(exponent), rounded_significand, + round_underflow_up); + return; + } + + private: + T value_; + + static_assert(num_used_bits == + Traits::num_exponent_bits + Traits::num_fraction_bits + 1, + "The number of bits do not fit"); + static_assert(sizeof(T) == sizeof(uint_type), "The type sizes do not match"); +}; + +// Returns 4 bits represented by the hex character. +inline uint8_t get_nibble_from_character(int character) { + const char* dec = "0123456789"; + const char* lower = "abcdef"; + const char* upper = "ABCDEF"; + const char* p = nullptr; + if ((p = strchr(dec, character))) { + return static_cast(p - dec); + } else if ((p = strchr(lower, character))) { + return static_cast(p - lower + 0xa); + } else if ((p = strchr(upper, character))) { + return static_cast(p - upper + 0xa); + } + + assert(false && "This was called with a non-hex character"); + return 0; +} + +// Outputs the given HexFloat to the stream. +template +std::ostream& operator<<(std::ostream& os, const HexFloat& value) { + typedef HexFloat HF; + typedef typename HF::uint_type uint_type; + typedef typename HF::int_type int_type; + + static_assert(HF::num_used_bits != 0, + "num_used_bits must be non-zero for a valid float"); + static_assert(HF::num_exponent_bits != 0, + "num_exponent_bits must be non-zero for a valid float"); + static_assert(HF::num_fraction_bits != 0, + "num_fractin_bits must be non-zero for a valid float"); + + const uint_type bits = spvutils::BitwiseCast(value.value()); + const char* const sign = (bits & HF::sign_mask) ? "-" : ""; + const uint_type exponent = static_cast( + (bits & HF::exponent_mask) >> HF::num_fraction_bits); + + uint_type fraction = static_cast((bits & HF::fraction_encode_mask) + << HF::num_overflow_bits); + + const bool is_zero = exponent == 0 && fraction == 0; + const bool is_denorm = exponent == 0 && !is_zero; + + // exponent contains the biased exponent we have to convert it back into + // the normal range. + int_type int_exponent = static_cast(exponent - HF::exponent_bias); + // If the number is all zeros, then we actually have to NOT shift the + // exponent. + int_exponent = is_zero ? 0 : int_exponent; + + // If we are denorm, then start shifting, and decreasing the exponent until + // our leading bit is 1. + + if (is_denorm) { + while ((fraction & HF::fraction_top_bit) == 0) { + fraction = static_cast(fraction << 1); + int_exponent = static_cast(int_exponent - 1); + } + // Since this is denormalized, we have to consume the leading 1 since it + // will end up being implicit. + fraction = static_cast(fraction << 1); // eat the leading 1 + fraction &= HF::fraction_represent_mask; + } + + uint_type fraction_nibbles = HF::fraction_nibbles; + // We do not have to display any trailing 0s, since this represents the + // fractional part. + while (fraction_nibbles > 0 && (fraction & 0xF) == 0) { + // Shift off any trailing values; + fraction = static_cast(fraction >> 4); + --fraction_nibbles; + } + + const auto saved_flags = os.flags(); + const auto saved_fill = os.fill(); + + os << sign << "0x" << (is_zero ? '0' : '1'); + if (fraction_nibbles) { + // Make sure to keep the leading 0s in place, since this is the fractional + // part. + os << "." << std::setw(static_cast(fraction_nibbles)) + << std::setfill('0') << std::hex << fraction; + } + os << "p" << std::dec << (int_exponent >= 0 ? "+" : "") << int_exponent; + + os.flags(saved_flags); + os.fill(saved_fill); + + return os; +} + +// Returns true if negate_value is true and the next character on the +// input stream is a plus or minus sign. In that case we also set the fail bit +// on the stream and set the value to the zero value for its type. +template +inline bool RejectParseDueToLeadingSign(std::istream& is, bool negate_value, + HexFloat& value) { + if (negate_value) { + auto next_char = is.peek(); + if (next_char == '-' || next_char == '+') { + // Fail the parse. Emulate standard behaviour by setting the value to + // the zero value, and set the fail bit on the stream. + value = HexFloat(typename HexFloat::uint_type(0)); + is.setstate(std::ios_base::failbit); + return true; + } + } + return false; +} + +// Parses a floating point number from the given stream and stores it into the +// value parameter. +// If negate_value is true then the number may not have a leading minus or +// plus, and if it successfully parses, then the number is negated before +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template +inline std::istream& ParseNormalFloat(std::istream& is, bool negate_value, + HexFloat& value) { + if (RejectParseDueToLeadingSign(is, negate_value, value)) { + return is; + } + T val; + is >> val; + if (negate_value) { + val = -val; + } + value.set_value(val); + // In the failure case, map -0.0 to 0.0. + if (is.fail() && value.getUnsignedBits() == 0u) { + value = HexFloat(typename HexFloat::uint_type(0)); + } + if (val.isInfinity()) { + // Fail the parse. Emulate standard behaviour by setting the value to + // the closest normal value, and set the fail bit on the stream. + value.set_value((value.isNegative() || negate_value) ? T::lowest() + : T::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + +// Specialization of ParseNormalFloat for FloatProxy values. +// This will parse the float as it were a 32-bit floating point number, +// and then round it down to fit into a Float16 value. +// The number is rounded towards zero. +// If negate_value is true then the number may not have a leading minus or +// plus, and if it successfully parses, then the number is negated before +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template <> +inline std::istream& +ParseNormalFloat, HexFloatTraits>>( + std::istream& is, bool negate_value, + HexFloat, HexFloatTraits>>& value) { + // First parse as a 32-bit float. + HexFloat> float_val(0.0f); + ParseNormalFloat(is, negate_value, float_val); + + // Then convert to 16-bit float, saturating at infinities, and + // rounding toward zero. + float_val.castTo(value, kRoundToZero); + + // Overflow on 16-bit behaves the same as for 32- and 64-bit: set the + // fail bit and set the lowest or highest value. + if (Float16::isInfinity(value.value().getAsFloat())) { + value.set_value(value.isNegative() ? Float16::lowest() : Float16::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + +// Reads a HexFloat from the given stream. +// If the float is not encoded as a hex-float then it will be parsed +// as a regular float. +// This may fail if your stream does not support at least one unget. +// Nan values can be encoded with "0x1.p+exponent_bias". +// This would normally overflow a float and round to +// infinity but this special pattern is the exact representation for a NaN, +// and therefore is actually encoded as the correct NaN. To encode inf, +// either 0x0p+exponent_bias can be specified or any exponent greater than +// exponent_bias. +// Examples using IEEE 32-bit float encoding. +// 0x1.0p+128 (+inf) +// -0x1.0p-128 (-inf) +// +// 0x1.1p+128 (+Nan) +// -0x1.1p+128 (-Nan) +// +// 0x1p+129 (+inf) +// -0x1p+129 (-inf) +template +std::istream& operator>>(std::istream& is, HexFloat& value) { + using HF = HexFloat; + using uint_type = typename HF::uint_type; + using int_type = typename HF::int_type; + + value.set_value(static_cast(0.f)); + + if (is.flags() & std::ios::skipws) { + // If the user wants to skip whitespace , then we should obey that. + while (std::isspace(is.peek())) { + is.get(); + } + } + + auto next_char = is.peek(); + bool negate_value = false; + + if (next_char != '-' && next_char != '0') { + return ParseNormalFloat(is, negate_value, value); + } + + if (next_char == '-') { + negate_value = true; + is.get(); + next_char = is.peek(); + } + + if (next_char == '0') { + is.get(); // We may have to unget this. + auto maybe_hex_start = is.peek(); + if (maybe_hex_start != 'x' && maybe_hex_start != 'X') { + is.unget(); + return ParseNormalFloat(is, negate_value, value); + } else { + is.get(); // Throw away the 'x'; + } + } else { + return ParseNormalFloat(is, negate_value, value); + } + + // This "looks" like a hex-float so treat it as one. + bool seen_p = false; + bool seen_dot = false; + uint_type fraction_index = 0; + + uint_type fraction = 0; + int_type exponent = HF::exponent_bias; + + // Strip off leading zeros so we don't have to special-case them later. + while ((next_char = is.peek()) == '0') { + is.get(); + } + + bool is_denorm = + true; // Assume denorm "representation" until we hear otherwise. + // NB: This does not mean the value is actually denorm, + // it just means that it was written 0. + bool bits_written = false; // Stays false until we write a bit. + while (!seen_p && !seen_dot) { + // Handle characters that are left of the fractional part. + if (next_char == '.') { + seen_dot = true; + } else if (next_char == 'p') { + seen_p = true; + } else if (::isxdigit(next_char)) { + // We know this is not denormalized since we have stripped all leading + // zeroes and we are not a ".". + is_denorm = false; + int number = get_nibble_from_character(next_char); + for (int i = 0; i < 4; ++i, number <<= 1) { + uint_type write_bit = (number & 0x8) ? 0x1 : 0x0; + if (bits_written) { + // If we are here the bits represented belong in the fractional + // part of the float, and we have to adjust the exponent accordingly. + fraction = static_cast( + fraction | + static_cast( + write_bit << (HF::top_bit_left_shift - fraction_index++))); + exponent = static_cast(exponent + 1); + } + bits_written |= write_bit != 0; + } + } else { + // We have not found our exponent yet, so we have to fail. + is.setstate(std::ios::failbit); + return is; + } + is.get(); + next_char = is.peek(); + } + bits_written = false; + while (seen_dot && !seen_p) { + // Handle only fractional parts now. + if (next_char == 'p') { + seen_p = true; + } else if (::isxdigit(next_char)) { + int number = get_nibble_from_character(next_char); + for (int i = 0; i < 4; ++i, number <<= 1) { + uint_type write_bit = (number & 0x8) ? 0x01 : 0x00; + bits_written |= write_bit != 0; + if (is_denorm && !bits_written) { + // Handle modifying the exponent here this way we can handle + // an arbitrary number of hex values without overflowing our + // integer. + exponent = static_cast(exponent - 1); + } else { + fraction = static_cast( + fraction | + static_cast( + write_bit << (HF::top_bit_left_shift - fraction_index++))); + } + } + } else { + // We still have not found our 'p' exponent yet, so this is not a valid + // hex-float. + is.setstate(std::ios::failbit); + return is; + } + is.get(); + next_char = is.peek(); + } + + bool seen_sign = false; + int8_t exponent_sign = 1; + int_type written_exponent = 0; + while (true) { + if ((next_char == '-' || next_char == '+')) { + if (seen_sign) { + is.setstate(std::ios::failbit); + return is; + } + seen_sign = true; + exponent_sign = (next_char == '-') ? -1 : 1; + } else if (::isdigit(next_char)) { + // Hex-floats express their exponent as decimal. + written_exponent = static_cast(written_exponent * 10); + written_exponent = + static_cast(written_exponent + (next_char - '0')); + } else { + break; + } + is.get(); + next_char = is.peek(); + } + + written_exponent = static_cast(written_exponent * exponent_sign); + exponent = static_cast(exponent + written_exponent); + + bool is_zero = is_denorm && (fraction == 0); + if (is_denorm && !is_zero) { + fraction = static_cast(fraction << 1); + exponent = static_cast(exponent - 1); + } else if (is_zero) { + exponent = 0; + } + + if (exponent <= 0 && !is_zero) { + fraction = static_cast(fraction >> 1); + fraction |= static_cast(1) << HF::top_bit_left_shift; + } + + fraction = (fraction >> HF::fraction_right_shift) & HF::fraction_encode_mask; + + const int_type max_exponent = + SetBits::get; + + // Handle actual denorm numbers + while (exponent < 0 && !is_zero) { + fraction = static_cast(fraction >> 1); + exponent = static_cast(exponent + 1); + + fraction &= HF::fraction_encode_mask; + if (fraction == 0) { + // We have underflowed our fraction. We should clamp to zero. + is_zero = true; + exponent = 0; + } + } + + // We have overflowed so we should be inf/-inf. + if (exponent > max_exponent) { + exponent = max_exponent; + fraction = 0; + } + + uint_type output_bits = static_cast( + static_cast(negate_value ? 1 : 0) << HF::top_bit_left_shift); + output_bits |= fraction; + + uint_type shifted_exponent = static_cast( + static_cast(exponent << HF::exponent_left_shift) & + HF::exponent_mask); + output_bits |= shifted_exponent; + + T output_float = spvutils::BitwiseCast(output_bits); + value.set_value(output_float); + + return is; +} + +// Writes a FloatProxy value to a stream. +// Zero and normal numbers are printed in the usual notation, but with +// enough digits to fully reproduce the value. Other values (subnormal, +// NaN, and infinity) are printed as a hex float. +template +std::ostream& operator<<(std::ostream& os, const FloatProxy& value) { + auto float_val = value.getAsFloat(); + switch (std::fpclassify(float_val)) { + case FP_ZERO: + case FP_NORMAL: { + auto saved_precision = os.precision(); + os.precision(std::numeric_limits::digits10); + os << float_val; + os.precision(saved_precision); + } break; + default: + os << HexFloat>(value); + break; + } + return os; +} + +template <> +inline std::ostream& operator<<(std::ostream& os, + const FloatProxy& value) { + os << HexFloat>(value); + return os; +} +} + +#endif // LIBSPIRV_UTIL_HEX_FLOAT_H_ diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/spirv.hpp b/armorcore/tools/to_spirv/glslang/SPIRV/spirv.hpp new file mode 100644 index 000000000..dae36cf20 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/spirv.hpp @@ -0,0 +1,2114 @@ +// Copyright (c) 2014-2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C#, D +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10500 +#define SPV_REVISION 3 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010500; +static const unsigned int Revision = 3; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum SourceLanguage { + SourceLanguageUnknown = 0, + SourceLanguageESSL = 1, + SourceLanguageGLSL = 2, + SourceLanguageOpenCL_C = 3, + SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageMax = 0x7fffffff, +}; + +enum ExecutionModel { + ExecutionModelVertex = 0, + ExecutionModelTessellationControl = 1, + ExecutionModelTessellationEvaluation = 2, + ExecutionModelGeometry = 3, + ExecutionModelFragment = 4, + ExecutionModelGLCompute = 5, + ExecutionModelKernel = 6, + ExecutionModelTaskNV = 5267, + ExecutionModelMeshNV = 5268, + ExecutionModelRayGenerationKHR = 5313, + ExecutionModelRayGenerationNV = 5313, + ExecutionModelIntersectionKHR = 5314, + ExecutionModelIntersectionNV = 5314, + ExecutionModelAnyHitKHR = 5315, + ExecutionModelAnyHitNV = 5315, + ExecutionModelClosestHitKHR = 5316, + ExecutionModelClosestHitNV = 5316, + ExecutionModelMissKHR = 5317, + ExecutionModelMissNV = 5317, + ExecutionModelCallableKHR = 5318, + ExecutionModelCallableNV = 5318, + ExecutionModelMax = 0x7fffffff, +}; + +enum AddressingModel { + AddressingModelLogical = 0, + AddressingModelPhysical32 = 1, + AddressingModelPhysical64 = 2, + AddressingModelPhysicalStorageBuffer64 = 5348, + AddressingModelPhysicalStorageBuffer64EXT = 5348, + AddressingModelMax = 0x7fffffff, +}; + +enum MemoryModel { + MemoryModelSimple = 0, + MemoryModelGLSL450 = 1, + MemoryModelOpenCL = 2, + MemoryModelVulkan = 3, + MemoryModelVulkanKHR = 3, + MemoryModelMax = 0x7fffffff, +}; + +enum ExecutionMode { + ExecutionModeInvocations = 0, + ExecutionModeSpacingEqual = 1, + ExecutionModeSpacingFractionalEven = 2, + ExecutionModeSpacingFractionalOdd = 3, + ExecutionModeVertexOrderCw = 4, + ExecutionModeVertexOrderCcw = 5, + ExecutionModePixelCenterInteger = 6, + ExecutionModeOriginUpperLeft = 7, + ExecutionModeOriginLowerLeft = 8, + ExecutionModeEarlyFragmentTests = 9, + ExecutionModePointMode = 10, + ExecutionModeXfb = 11, + ExecutionModeDepthReplacing = 12, + ExecutionModeDepthGreater = 14, + ExecutionModeDepthLess = 15, + ExecutionModeDepthUnchanged = 16, + ExecutionModeLocalSize = 17, + ExecutionModeLocalSizeHint = 18, + ExecutionModeInputPoints = 19, + ExecutionModeInputLines = 20, + ExecutionModeInputLinesAdjacency = 21, + ExecutionModeTriangles = 22, + ExecutionModeInputTrianglesAdjacency = 23, + ExecutionModeQuads = 24, + ExecutionModeIsolines = 25, + ExecutionModeOutputVertices = 26, + ExecutionModeOutputPoints = 27, + ExecutionModeOutputLineStrip = 28, + ExecutionModeOutputTriangleStrip = 29, + ExecutionModeVecTypeHint = 30, + ExecutionModeContractionOff = 31, + ExecutionModeInitializer = 33, + ExecutionModeFinalizer = 34, + ExecutionModeSubgroupSize = 35, + ExecutionModeSubgroupsPerWorkgroup = 36, + ExecutionModeSubgroupsPerWorkgroupId = 37, + ExecutionModeLocalSizeId = 38, + ExecutionModeLocalSizeHintId = 39, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeDenormPreserve = 4459, + ExecutionModeDenormFlushToZero = 4460, + ExecutionModeSignedZeroInfNanPreserve = 4461, + ExecutionModeRoundingModeRTE = 4462, + ExecutionModeRoundingModeRTZ = 4463, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeOutputLinesNV = 5269, + ExecutionModeOutputPrimitivesNV = 5270, + ExecutionModeDerivativeGroupQuadsNV = 5289, + ExecutionModeDerivativeGroupLinearNV = 5290, + ExecutionModeOutputTrianglesNV = 5298, + ExecutionModePixelInterlockOrderedEXT = 5366, + ExecutionModePixelInterlockUnorderedEXT = 5367, + ExecutionModeSampleInterlockOrderedEXT = 5368, + ExecutionModeSampleInterlockUnorderedEXT = 5369, + ExecutionModeShadingRateInterlockOrderedEXT = 5370, + ExecutionModeShadingRateInterlockUnorderedEXT = 5371, + ExecutionModeMax = 0x7fffffff, +}; + +enum StorageClass { + StorageClassUniformConstant = 0, + StorageClassInput = 1, + StorageClassUniform = 2, + StorageClassOutput = 3, + StorageClassWorkgroup = 4, + StorageClassCrossWorkgroup = 5, + StorageClassPrivate = 6, + StorageClassFunction = 7, + StorageClassGeneric = 8, + StorageClassPushConstant = 9, + StorageClassAtomicCounter = 10, + StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassCallableDataKHR = 5328, + StorageClassCallableDataNV = 5328, + StorageClassIncomingCallableDataKHR = 5329, + StorageClassIncomingCallableDataNV = 5329, + StorageClassRayPayloadKHR = 5338, + StorageClassRayPayloadNV = 5338, + StorageClassHitAttributeKHR = 5339, + StorageClassHitAttributeNV = 5339, + StorageClassIncomingRayPayloadKHR = 5342, + StorageClassIncomingRayPayloadNV = 5342, + StorageClassShaderRecordBufferKHR = 5343, + StorageClassShaderRecordBufferNV = 5343, + StorageClassPhysicalStorageBuffer = 5349, + StorageClassPhysicalStorageBufferEXT = 5349, + StorageClassMax = 0x7fffffff, +}; + +enum Dim { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + DimCube = 3, + DimRect = 4, + DimBuffer = 5, + DimSubpassData = 6, + DimMax = 0x7fffffff, +}; + +enum SamplerAddressingMode { + SamplerAddressingModeNone = 0, + SamplerAddressingModeClampToEdge = 1, + SamplerAddressingModeClamp = 2, + SamplerAddressingModeRepeat = 3, + SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, +}; + +enum SamplerFilterMode { + SamplerFilterModeNearest = 0, + SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, +}; + +enum ImageFormat { + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39, + ImageFormatMax = 0x7fffffff, +}; + +enum ImageChannelOrder { + ImageChannelOrderR = 0, + ImageChannelOrderA = 1, + ImageChannelOrderRG = 2, + ImageChannelOrderRA = 3, + ImageChannelOrderRGB = 4, + ImageChannelOrderRGBA = 5, + ImageChannelOrderBGRA = 6, + ImageChannelOrderARGB = 7, + ImageChannelOrderIntensity = 8, + ImageChannelOrderLuminance = 9, + ImageChannelOrderRx = 10, + ImageChannelOrderRGx = 11, + ImageChannelOrderRGBx = 12, + ImageChannelOrderDepth = 13, + ImageChannelOrderDepthStencil = 14, + ImageChannelOrdersRGB = 15, + ImageChannelOrdersRGBx = 16, + ImageChannelOrdersRGBA = 17, + ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, +}; + +enum ImageChannelDataType { + ImageChannelDataTypeSnormInt8 = 0, + ImageChannelDataTypeSnormInt16 = 1, + ImageChannelDataTypeUnormInt8 = 2, + ImageChannelDataTypeUnormInt16 = 3, + ImageChannelDataTypeUnormShort565 = 4, + ImageChannelDataTypeUnormShort555 = 5, + ImageChannelDataTypeUnormInt101010 = 6, + ImageChannelDataTypeSignedInt8 = 7, + ImageChannelDataTypeSignedInt16 = 8, + ImageChannelDataTypeSignedInt32 = 9, + ImageChannelDataTypeUnsignedInt8 = 10, + ImageChannelDataTypeUnsignedInt16 = 11, + ImageChannelDataTypeUnsignedInt32 = 12, + ImageChannelDataTypeHalfFloat = 13, + ImageChannelDataTypeFloat = 14, + ImageChannelDataTypeUnormInt24 = 15, + ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeMax = 0x7fffffff, +}; + +enum ImageOperandsShift { + ImageOperandsBiasShift = 0, + ImageOperandsLodShift = 1, + ImageOperandsGradShift = 2, + ImageOperandsConstOffsetShift = 3, + ImageOperandsOffsetShift = 4, + ImageOperandsConstOffsetsShift = 5, + ImageOperandsSampleShift = 6, + ImageOperandsMinLodShift = 7, + ImageOperandsMakeTexelAvailableShift = 8, + ImageOperandsMakeTexelAvailableKHRShift = 8, + ImageOperandsMakeTexelVisibleShift = 9, + ImageOperandsMakeTexelVisibleKHRShift = 9, + ImageOperandsNonPrivateTexelShift = 10, + ImageOperandsNonPrivateTexelKHRShift = 10, + ImageOperandsVolatileTexelShift = 11, + ImageOperandsVolatileTexelKHRShift = 11, + ImageOperandsSignExtendShift = 12, + ImageOperandsZeroExtendShift = 13, + ImageOperandsMax = 0x7fffffff, +}; + +enum ImageOperandsMask { + ImageOperandsMaskNone = 0, + ImageOperandsBiasMask = 0x00000001, + ImageOperandsLodMask = 0x00000002, + ImageOperandsGradMask = 0x00000004, + ImageOperandsConstOffsetMask = 0x00000008, + ImageOperandsOffsetMask = 0x00000010, + ImageOperandsConstOffsetsMask = 0x00000020, + ImageOperandsSampleMask = 0x00000040, + ImageOperandsMinLodMask = 0x00000080, + ImageOperandsMakeTexelAvailableMask = 0x00000100, + ImageOperandsMakeTexelAvailableKHRMask = 0x00000100, + ImageOperandsMakeTexelVisibleMask = 0x00000200, + ImageOperandsMakeTexelVisibleKHRMask = 0x00000200, + ImageOperandsNonPrivateTexelMask = 0x00000400, + ImageOperandsNonPrivateTexelKHRMask = 0x00000400, + ImageOperandsVolatileTexelMask = 0x00000800, + ImageOperandsVolatileTexelKHRMask = 0x00000800, + ImageOperandsSignExtendMask = 0x00001000, + ImageOperandsZeroExtendMask = 0x00002000, +}; + +enum FPFastMathModeShift { + FPFastMathModeNotNaNShift = 0, + FPFastMathModeNotInfShift = 1, + FPFastMathModeNSZShift = 2, + FPFastMathModeAllowRecipShift = 3, + FPFastMathModeFastShift = 4, + FPFastMathModeMax = 0x7fffffff, +}; + +enum FPFastMathModeMask { + FPFastMathModeMaskNone = 0, + FPFastMathModeNotNaNMask = 0x00000001, + FPFastMathModeNotInfMask = 0x00000002, + FPFastMathModeNSZMask = 0x00000004, + FPFastMathModeAllowRecipMask = 0x00000008, + FPFastMathModeFastMask = 0x00000010, +}; + +enum FPRoundingMode { + FPRoundingModeRTE = 0, + FPRoundingModeRTZ = 1, + FPRoundingModeRTP = 2, + FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, +}; + +enum LinkageType { + LinkageTypeExport = 0, + LinkageTypeImport = 1, + LinkageTypeMax = 0x7fffffff, +}; + +enum AccessQualifier { + AccessQualifierReadOnly = 0, + AccessQualifierWriteOnly = 1, + AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, +}; + +enum FunctionParameterAttribute { + FunctionParameterAttributeZext = 0, + FunctionParameterAttributeSext = 1, + FunctionParameterAttributeByVal = 2, + FunctionParameterAttributeSret = 3, + FunctionParameterAttributeNoAlias = 4, + FunctionParameterAttributeNoCapture = 5, + FunctionParameterAttributeNoWrite = 6, + FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeMax = 0x7fffffff, +}; + +enum Decoration { + DecorationRelaxedPrecision = 0, + DecorationSpecId = 1, + DecorationBlock = 2, + DecorationBufferBlock = 3, + DecorationRowMajor = 4, + DecorationColMajor = 5, + DecorationArrayStride = 6, + DecorationMatrixStride = 7, + DecorationGLSLShared = 8, + DecorationGLSLPacked = 9, + DecorationCPacked = 10, + DecorationBuiltIn = 11, + DecorationNoPerspective = 13, + DecorationFlat = 14, + DecorationPatch = 15, + DecorationCentroid = 16, + DecorationSample = 17, + DecorationInvariant = 18, + DecorationRestrict = 19, + DecorationAliased = 20, + DecorationVolatile = 21, + DecorationConstant = 22, + DecorationCoherent = 23, + DecorationNonWritable = 24, + DecorationNonReadable = 25, + DecorationUniform = 26, + DecorationUniformId = 27, + DecorationSaturatedConversion = 28, + DecorationStream = 29, + DecorationLocation = 30, + DecorationComponent = 31, + DecorationIndex = 32, + DecorationBinding = 33, + DecorationDescriptorSet = 34, + DecorationOffset = 35, + DecorationXfbBuffer = 36, + DecorationXfbStride = 37, + DecorationFuncParamAttr = 38, + DecorationFPRoundingMode = 39, + DecorationFPFastMathMode = 40, + DecorationLinkageAttributes = 41, + DecorationNoContraction = 42, + DecorationInputAttachmentIndex = 43, + DecorationAlignment = 44, + DecorationMaxByteOffset = 45, + DecorationAlignmentId = 46, + DecorationMaxByteOffsetId = 47, + DecorationNoSignedWrap = 4469, + DecorationNoUnsignedWrap = 4470, + DecorationExplicitInterpAMD = 4999, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationPerPrimitiveNV = 5271, + DecorationPerViewNV = 5272, + DecorationPerTaskNV = 5273, + DecorationPerVertexNV = 5285, + DecorationNonUniform = 5300, + DecorationNonUniformEXT = 5300, + DecorationRestrictPointer = 5355, + DecorationRestrictPointerEXT = 5355, + DecorationAliasedPointer = 5356, + DecorationAliasedPointerEXT = 5356, + DecorationCounterBuffer = 5634, + DecorationHlslCounterBufferGOOGLE = 5634, + DecorationHlslSemanticGOOGLE = 5635, + DecorationUserSemantic = 5635, + DecorationUserTypeGOOGLE = 5636, + DecorationMax = 0x7fffffff, +}; + +enum BuiltIn { + BuiltInPosition = 0, + BuiltInPointSize = 1, + BuiltInClipDistance = 3, + BuiltInCullDistance = 4, + BuiltInVertexId = 5, + BuiltInInstanceId = 6, + BuiltInPrimitiveId = 7, + BuiltInInvocationId = 8, + BuiltInLayer = 9, + BuiltInViewportIndex = 10, + BuiltInTessLevelOuter = 11, + BuiltInTessLevelInner = 12, + BuiltInTessCoord = 13, + BuiltInPatchVertices = 14, + BuiltInFragCoord = 15, + BuiltInPointCoord = 16, + BuiltInFrontFacing = 17, + BuiltInSampleId = 18, + BuiltInSamplePosition = 19, + BuiltInSampleMask = 20, + BuiltInFragDepth = 22, + BuiltInHelperInvocation = 23, + BuiltInNumWorkgroups = 24, + BuiltInWorkgroupSize = 25, + BuiltInWorkgroupId = 26, + BuiltInLocalInvocationId = 27, + BuiltInGlobalInvocationId = 28, + BuiltInLocalInvocationIndex = 29, + BuiltInWorkDim = 30, + BuiltInGlobalSize = 31, + BuiltInEnqueuedWorkgroupSize = 32, + BuiltInGlobalOffset = 33, + BuiltInGlobalLinearId = 34, + BuiltInSubgroupSize = 36, + BuiltInSubgroupMaxSize = 37, + BuiltInNumSubgroups = 38, + BuiltInNumEnqueuedSubgroups = 39, + BuiltInSubgroupId = 40, + BuiltInSubgroupLocalInvocationId = 41, + BuiltInVertexIndex = 42, + BuiltInInstanceIndex = 43, + BuiltInSubgroupEqMask = 4416, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMask = 4417, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMask = 4418, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMask = 4419, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMask = 4420, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInFullyCoveredEXT = 5264, + BuiltInTaskCountNV = 5274, + BuiltInPrimitiveCountNV = 5275, + BuiltInPrimitiveIndicesNV = 5276, + BuiltInClipDistancePerViewNV = 5277, + BuiltInCullDistancePerViewNV = 5278, + BuiltInLayerPerViewNV = 5279, + BuiltInMeshViewCountNV = 5280, + BuiltInMeshViewIndicesNV = 5281, + BuiltInBaryCoordNV = 5286, + BuiltInBaryCoordNoPerspNV = 5287, + BuiltInFragSizeEXT = 5292, + BuiltInFragmentSizeNV = 5292, + BuiltInFragInvocationCountEXT = 5293, + BuiltInInvocationsPerPixelNV = 5293, + BuiltInLaunchIdKHR = 5319, + BuiltInLaunchIdNV = 5319, + BuiltInLaunchSizeKHR = 5320, + BuiltInLaunchSizeNV = 5320, + BuiltInWorldRayOriginKHR = 5321, + BuiltInWorldRayOriginNV = 5321, + BuiltInWorldRayDirectionKHR = 5322, + BuiltInWorldRayDirectionNV = 5322, + BuiltInObjectRayOriginKHR = 5323, + BuiltInObjectRayOriginNV = 5323, + BuiltInObjectRayDirectionKHR = 5324, + BuiltInObjectRayDirectionNV = 5324, + BuiltInRayTminKHR = 5325, + BuiltInRayTminNV = 5325, + BuiltInRayTmaxKHR = 5326, + BuiltInRayTmaxNV = 5326, + BuiltInInstanceCustomIndexKHR = 5327, + BuiltInInstanceCustomIndexNV = 5327, + BuiltInObjectToWorldKHR = 5330, + BuiltInObjectToWorldNV = 5330, + BuiltInWorldToObjectKHR = 5331, + BuiltInWorldToObjectNV = 5331, + BuiltInHitTKHR = 5332, + BuiltInHitTNV = 5332, + BuiltInHitKindKHR = 5333, + BuiltInHitKindNV = 5333, + BuiltInIncomingRayFlagsKHR = 5351, + BuiltInIncomingRayFlagsNV = 5351, + BuiltInRayGeometryIndexKHR = 5352, + BuiltInWarpsPerSMNV = 5374, + BuiltInSMCountNV = 5375, + BuiltInWarpIDNV = 5376, + BuiltInSMIDNV = 5377, + BuiltInMax = 0x7fffffff, +}; + +enum SelectionControlShift { + SelectionControlFlattenShift = 0, + SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, +}; + +enum SelectionControlMask { + SelectionControlMaskNone = 0, + SelectionControlFlattenMask = 0x00000001, + SelectionControlDontFlattenMask = 0x00000002, +}; + +enum LoopControlShift { + LoopControlUnrollShift = 0, + LoopControlDontUnrollShift = 1, + LoopControlDependencyInfiniteShift = 2, + LoopControlDependencyLengthShift = 3, + LoopControlMinIterationsShift = 4, + LoopControlMaxIterationsShift = 5, + LoopControlIterationMultipleShift = 6, + LoopControlPeelCountShift = 7, + LoopControlPartialCountShift = 8, + LoopControlMax = 0x7fffffff, +}; + +enum LoopControlMask { + LoopControlMaskNone = 0, + LoopControlUnrollMask = 0x00000001, + LoopControlDontUnrollMask = 0x00000002, + LoopControlDependencyInfiniteMask = 0x00000004, + LoopControlDependencyLengthMask = 0x00000008, + LoopControlMinIterationsMask = 0x00000010, + LoopControlMaxIterationsMask = 0x00000020, + LoopControlIterationMultipleMask = 0x00000040, + LoopControlPeelCountMask = 0x00000080, + LoopControlPartialCountMask = 0x00000100, +}; + +enum FunctionControlShift { + FunctionControlInlineShift = 0, + FunctionControlDontInlineShift = 1, + FunctionControlPureShift = 2, + FunctionControlConstShift = 3, + FunctionControlMax = 0x7fffffff, +}; + +enum FunctionControlMask { + FunctionControlMaskNone = 0, + FunctionControlInlineMask = 0x00000001, + FunctionControlDontInlineMask = 0x00000002, + FunctionControlPureMask = 0x00000004, + FunctionControlConstMask = 0x00000008, +}; + +enum MemorySemanticsShift { + MemorySemanticsAcquireShift = 1, + MemorySemanticsReleaseShift = 2, + MemorySemanticsAcquireReleaseShift = 3, + MemorySemanticsSequentiallyConsistentShift = 4, + MemorySemanticsUniformMemoryShift = 6, + MemorySemanticsSubgroupMemoryShift = 7, + MemorySemanticsWorkgroupMemoryShift = 8, + MemorySemanticsCrossWorkgroupMemoryShift = 9, + MemorySemanticsAtomicCounterMemoryShift = 10, + MemorySemanticsImageMemoryShift = 11, + MemorySemanticsOutputMemoryShift = 12, + MemorySemanticsOutputMemoryKHRShift = 12, + MemorySemanticsMakeAvailableShift = 13, + MemorySemanticsMakeAvailableKHRShift = 13, + MemorySemanticsMakeVisibleShift = 14, + MemorySemanticsMakeVisibleKHRShift = 14, + MemorySemanticsVolatileShift = 15, + MemorySemanticsMax = 0x7fffffff, +}; + +enum MemorySemanticsMask { + MemorySemanticsMaskNone = 0, + MemorySemanticsAcquireMask = 0x00000002, + MemorySemanticsReleaseMask = 0x00000004, + MemorySemanticsAcquireReleaseMask = 0x00000008, + MemorySemanticsSequentiallyConsistentMask = 0x00000010, + MemorySemanticsUniformMemoryMask = 0x00000040, + MemorySemanticsSubgroupMemoryMask = 0x00000080, + MemorySemanticsWorkgroupMemoryMask = 0x00000100, + MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + MemorySemanticsAtomicCounterMemoryMask = 0x00000400, + MemorySemanticsImageMemoryMask = 0x00000800, + MemorySemanticsOutputMemoryMask = 0x00001000, + MemorySemanticsOutputMemoryKHRMask = 0x00001000, + MemorySemanticsMakeAvailableMask = 0x00002000, + MemorySemanticsMakeAvailableKHRMask = 0x00002000, + MemorySemanticsMakeVisibleMask = 0x00004000, + MemorySemanticsMakeVisibleKHRMask = 0x00004000, + MemorySemanticsVolatileMask = 0x00008000, +}; + +enum MemoryAccessShift { + MemoryAccessVolatileShift = 0, + MemoryAccessAlignedShift = 1, + MemoryAccessNontemporalShift = 2, + MemoryAccessMakePointerAvailableShift = 3, + MemoryAccessMakePointerAvailableKHRShift = 3, + MemoryAccessMakePointerVisibleShift = 4, + MemoryAccessMakePointerVisibleKHRShift = 4, + MemoryAccessNonPrivatePointerShift = 5, + MemoryAccessNonPrivatePointerKHRShift = 5, + MemoryAccessMax = 0x7fffffff, +}; + +enum MemoryAccessMask { + MemoryAccessMaskNone = 0, + MemoryAccessVolatileMask = 0x00000001, + MemoryAccessAlignedMask = 0x00000002, + MemoryAccessNontemporalMask = 0x00000004, + MemoryAccessMakePointerAvailableMask = 0x00000008, + MemoryAccessMakePointerAvailableKHRMask = 0x00000008, + MemoryAccessMakePointerVisibleMask = 0x00000010, + MemoryAccessMakePointerVisibleKHRMask = 0x00000010, + MemoryAccessNonPrivatePointerMask = 0x00000020, + MemoryAccessNonPrivatePointerKHRMask = 0x00000020, +}; + +enum Scope { + ScopeCrossDevice = 0, + ScopeDevice = 1, + ScopeWorkgroup = 2, + ScopeSubgroup = 3, + ScopeInvocation = 4, + ScopeQueueFamily = 5, + ScopeQueueFamilyKHR = 5, + ScopeShaderCallKHR = 6, + ScopeMax = 0x7fffffff, +}; + +enum GroupOperation { + GroupOperationReduce = 0, + GroupOperationInclusiveScan = 1, + GroupOperationExclusiveScan = 2, + GroupOperationClusteredReduce = 3, + GroupOperationPartitionedReduceNV = 6, + GroupOperationPartitionedInclusiveScanNV = 7, + GroupOperationPartitionedExclusiveScanNV = 8, + GroupOperationMax = 0x7fffffff, +}; + +enum KernelEnqueueFlags { + KernelEnqueueFlagsNoWait = 0, + KernelEnqueueFlagsWaitKernel = 1, + KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, +}; + +enum KernelProfilingInfoShift { + KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, +}; + +enum KernelProfilingInfoMask { + KernelProfilingInfoMaskNone = 0, + KernelProfilingInfoCmdExecTimeMask = 0x00000001, +}; + +enum Capability { + CapabilityMatrix = 0, + CapabilityShader = 1, + CapabilityGeometry = 2, + CapabilityTessellation = 3, + CapabilityAddresses = 4, + CapabilityLinkage = 5, + CapabilityKernel = 6, + CapabilityVector16 = 7, + CapabilityFloat16Buffer = 8, + CapabilityFloat16 = 9, + CapabilityFloat64 = 10, + CapabilityInt64 = 11, + CapabilityInt64Atomics = 12, + CapabilityImageBasic = 13, + CapabilityImageReadWrite = 14, + CapabilityImageMipmap = 15, + CapabilityPipes = 17, + CapabilityGroups = 18, + CapabilityDeviceEnqueue = 19, + CapabilityLiteralSampler = 20, + CapabilityAtomicStorage = 21, + CapabilityInt16 = 22, + CapabilityTessellationPointSize = 23, + CapabilityGeometryPointSize = 24, + CapabilityImageGatherExtended = 25, + CapabilityStorageImageMultisample = 27, + CapabilityUniformBufferArrayDynamicIndexing = 28, + CapabilitySampledImageArrayDynamicIndexing = 29, + CapabilityStorageBufferArrayDynamicIndexing = 30, + CapabilityStorageImageArrayDynamicIndexing = 31, + CapabilityClipDistance = 32, + CapabilityCullDistance = 33, + CapabilityImageCubeArray = 34, + CapabilitySampleRateShading = 35, + CapabilityImageRect = 36, + CapabilitySampledRect = 37, + CapabilityGenericPointer = 38, + CapabilityInt8 = 39, + CapabilityInputAttachment = 40, + CapabilitySparseResidency = 41, + CapabilityMinLod = 42, + CapabilitySampled1D = 43, + CapabilityImage1D = 44, + CapabilitySampledCubeArray = 45, + CapabilitySampledBuffer = 46, + CapabilityImageBuffer = 47, + CapabilityImageMSArray = 48, + CapabilityStorageImageExtendedFormats = 49, + CapabilityImageQuery = 50, + CapabilityDerivativeControl = 51, + CapabilityInterpolationFunction = 52, + CapabilityTransformFeedback = 53, + CapabilityGeometryStreams = 54, + CapabilityStorageImageReadWithoutFormat = 55, + CapabilityStorageImageWriteWithoutFormat = 56, + CapabilityMultiViewport = 57, + CapabilitySubgroupDispatch = 58, + CapabilityNamedBarrier = 59, + CapabilityPipeStorage = 60, + CapabilityGroupNonUniform = 61, + CapabilityGroupNonUniformVote = 62, + CapabilityGroupNonUniformArithmetic = 63, + CapabilityGroupNonUniformBallot = 64, + CapabilityGroupNonUniformShuffle = 65, + CapabilityGroupNonUniformShuffleRelative = 66, + CapabilityGroupNonUniformClustered = 67, + CapabilityGroupNonUniformQuad = 68, + CapabilityShaderLayer = 69, + CapabilityShaderViewportIndex = 70, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityStorageBuffer8BitAccess = 4448, + CapabilityUniformAndStorageBuffer8BitAccess = 4449, + CapabilityStoragePushConstant8 = 4450, + CapabilityDenormPreserve = 4464, + CapabilityDenormFlushToZero = 4465, + CapabilitySignedZeroInfNanPreserve = 4466, + CapabilityRoundingModeRTE = 4467, + CapabilityRoundingModeRTZ = 4468, + CapabilityRayQueryProvisionalKHR = 4471, + CapabilityRayTraversalPrimitiveCullingProvisionalKHR = 4478, + CapabilityFloat16ImageAMD = 5008, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilityShaderClockKHR = 5055, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilityFragmentFullyCoveredEXT = 5265, + CapabilityMeshShadingNV = 5266, + CapabilityImageFootprintNV = 5282, + CapabilityFragmentBarycentricNV = 5284, + CapabilityComputeDerivativeGroupQuadsNV = 5288, + CapabilityFragmentDensityEXT = 5291, + CapabilityShadingRateNV = 5291, + CapabilityGroupNonUniformPartitionedNV = 5297, + CapabilityShaderNonUniform = 5301, + CapabilityShaderNonUniformEXT = 5301, + CapabilityRuntimeDescriptorArray = 5302, + CapabilityRuntimeDescriptorArrayEXT = 5302, + CapabilityInputAttachmentArrayDynamicIndexing = 5303, + CapabilityInputAttachmentArrayDynamicIndexingEXT = 5303, + CapabilityUniformTexelBufferArrayDynamicIndexing = 5304, + CapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304, + CapabilityStorageTexelBufferArrayDynamicIndexing = 5305, + CapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305, + CapabilityUniformBufferArrayNonUniformIndexing = 5306, + CapabilityUniformBufferArrayNonUniformIndexingEXT = 5306, + CapabilitySampledImageArrayNonUniformIndexing = 5307, + CapabilitySampledImageArrayNonUniformIndexingEXT = 5307, + CapabilityStorageBufferArrayNonUniformIndexing = 5308, + CapabilityStorageBufferArrayNonUniformIndexingEXT = 5308, + CapabilityStorageImageArrayNonUniformIndexing = 5309, + CapabilityStorageImageArrayNonUniformIndexingEXT = 5309, + CapabilityInputAttachmentArrayNonUniformIndexing = 5310, + CapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310, + CapabilityUniformTexelBufferArrayNonUniformIndexing = 5311, + CapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311, + CapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, + CapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, + CapabilityRayTracingNV = 5340, + CapabilityVulkanMemoryModel = 5345, + CapabilityVulkanMemoryModelKHR = 5345, + CapabilityVulkanMemoryModelDeviceScope = 5346, + CapabilityVulkanMemoryModelDeviceScopeKHR = 5346, + CapabilityPhysicalStorageBufferAddresses = 5347, + CapabilityPhysicalStorageBufferAddressesEXT = 5347, + CapabilityComputeDerivativeGroupLinearNV = 5350, + CapabilityRayTracingProvisionalKHR = 5353, + CapabilityCooperativeMatrixNV = 5357, + CapabilityFragmentShaderSampleInterlockEXT = 5363, + CapabilityFragmentShaderShadingRateInterlockEXT = 5372, + CapabilityShaderSMBuiltinsNV = 5373, + CapabilityFragmentShaderPixelInterlockEXT = 5378, + CapabilityDemoteToHelperInvocationEXT = 5379, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilitySubgroupImageMediaBlockIOINTEL = 5579, + CapabilityIntegerFunctions2INTEL = 5584, + CapabilitySubgroupAvcMotionEstimationINTEL = 5696, + CapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697, + CapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698, + CapabilityMax = 0x7fffffff, +}; + +enum RayFlagsShift { + RayFlagsOpaqueKHRShift = 0, + RayFlagsNoOpaqueKHRShift = 1, + RayFlagsTerminateOnFirstHitKHRShift = 2, + RayFlagsSkipClosestHitShaderKHRShift = 3, + RayFlagsCullBackFacingTrianglesKHRShift = 4, + RayFlagsCullFrontFacingTrianglesKHRShift = 5, + RayFlagsCullOpaqueKHRShift = 6, + RayFlagsCullNoOpaqueKHRShift = 7, + RayFlagsSkipTrianglesKHRShift = 8, + RayFlagsSkipAABBsKHRShift = 9, + RayFlagsMax = 0x7fffffff, +}; + +enum RayFlagsMask { + RayFlagsMaskNone = 0, + RayFlagsOpaqueKHRMask = 0x00000001, + RayFlagsNoOpaqueKHRMask = 0x00000002, + RayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + RayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + RayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + RayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + RayFlagsCullOpaqueKHRMask = 0x00000040, + RayFlagsCullNoOpaqueKHRMask = 0x00000080, + RayFlagsSkipTrianglesKHRMask = 0x00000100, + RayFlagsSkipAABBsKHRMask = 0x00000200, +}; + +enum RayQueryIntersection { + RayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + RayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + RayQueryIntersectionMax = 0x7fffffff, +}; + +enum RayQueryCommittedIntersectionType { + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + RayQueryCommittedIntersectionTypeMax = 0x7fffffff, +}; + +enum RayQueryCandidateIntersectionType { + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + RayQueryCandidateIntersectionTypeMax = 0x7fffffff, +}; + +enum Op { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpTypeRayQueryProvisionalKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpImageSampleFootprintNV = 5283, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionKHR = 5335, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayKHR = 5336, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceRayKHR = 5337, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableKHR = 5344, + OpExecuteCallableNV = 5344, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpMax = 0x7fffffff, +}; + +#ifdef SPV_ENABLE_UTILITY_CODE +inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case OpNop: *hasResult = false; *hasResultType = false; break; + case OpUndef: *hasResult = true; *hasResultType = true; break; + case OpSourceContinued: *hasResult = false; *hasResultType = false; break; + case OpSource: *hasResult = false; *hasResultType = false; break; + case OpSourceExtension: *hasResult = false; *hasResultType = false; break; + case OpName: *hasResult = false; *hasResultType = false; break; + case OpMemberName: *hasResult = false; *hasResultType = false; break; + case OpString: *hasResult = true; *hasResultType = false; break; + case OpLine: *hasResult = false; *hasResultType = false; break; + case OpExtension: *hasResult = false; *hasResultType = false; break; + case OpExtInstImport: *hasResult = true; *hasResultType = false; break; + case OpExtInst: *hasResult = true; *hasResultType = true; break; + case OpMemoryModel: *hasResult = false; *hasResultType = false; break; + case OpEntryPoint: *hasResult = false; *hasResultType = false; break; + case OpExecutionMode: *hasResult = false; *hasResultType = false; break; + case OpCapability: *hasResult = false; *hasResultType = false; break; + case OpTypeVoid: *hasResult = true; *hasResultType = false; break; + case OpTypeBool: *hasResult = true; *hasResultType = false; break; + case OpTypeInt: *hasResult = true; *hasResultType = false; break; + case OpTypeFloat: *hasResult = true; *hasResultType = false; break; + case OpTypeVector: *hasResult = true; *hasResultType = false; break; + case OpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case OpTypeImage: *hasResult = true; *hasResultType = false; break; + case OpTypeSampler: *hasResult = true; *hasResultType = false; break; + case OpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case OpTypeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeStruct: *hasResult = true; *hasResultType = false; break; + case OpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case OpTypePointer: *hasResult = true; *hasResultType = false; break; + case OpTypeFunction: *hasResult = true; *hasResultType = false; break; + case OpTypeEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case OpTypeQueue: *hasResult = true; *hasResultType = false; break; + case OpTypePipe: *hasResult = true; *hasResultType = false; break; + case OpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case OpConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpConstant: *hasResult = true; *hasResultType = true; break; + case OpConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpConstantSampler: *hasResult = true; *hasResultType = true; break; + case OpConstantNull: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpSpecConstant: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case OpFunction: *hasResult = true; *hasResultType = true; break; + case OpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case OpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case OpFunctionCall: *hasResult = true; *hasResultType = true; break; + case OpVariable: *hasResult = true; *hasResultType = true; break; + case OpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case OpLoad: *hasResult = true; *hasResultType = true; break; + case OpStore: *hasResult = false; *hasResultType = false; break; + case OpCopyMemory: *hasResult = false; *hasResultType = false; break; + case OpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case OpAccessChain: *hasResult = true; *hasResultType = true; break; + case OpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case OpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpArrayLength: *hasResult = true; *hasResultType = true; break; + case OpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case OpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpDecorate: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case OpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case OpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case OpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case OpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case OpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case OpCopyObject: *hasResult = true; *hasResultType = true; break; + case OpTranspose: *hasResult = true; *hasResultType = true; break; + case OpSampledImage: *hasResult = true; *hasResultType = true; break; + case OpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageFetch: *hasResult = true; *hasResultType = true; break; + case OpImageGather: *hasResult = true; *hasResultType = true; break; + case OpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageRead: *hasResult = true; *hasResultType = true; break; + case OpImageWrite: *hasResult = false; *hasResultType = false; break; + case OpImage: *hasResult = true; *hasResultType = true; break; + case OpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case OpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case OpConvertFToU: *hasResult = true; *hasResultType = true; break; + case OpConvertFToS: *hasResult = true; *hasResultType = true; break; + case OpConvertSToF: *hasResult = true; *hasResultType = true; break; + case OpConvertUToF: *hasResult = true; *hasResultType = true; break; + case OpUConvert: *hasResult = true; *hasResultType = true; break; + case OpSConvert: *hasResult = true; *hasResultType = true; break; + case OpFConvert: *hasResult = true; *hasResultType = true; break; + case OpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case OpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case OpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case OpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case OpBitcast: *hasResult = true; *hasResultType = true; break; + case OpSNegate: *hasResult = true; *hasResultType = true; break; + case OpFNegate: *hasResult = true; *hasResultType = true; break; + case OpIAdd: *hasResult = true; *hasResultType = true; break; + case OpFAdd: *hasResult = true; *hasResultType = true; break; + case OpISub: *hasResult = true; *hasResultType = true; break; + case OpFSub: *hasResult = true; *hasResultType = true; break; + case OpIMul: *hasResult = true; *hasResultType = true; break; + case OpFMul: *hasResult = true; *hasResultType = true; break; + case OpUDiv: *hasResult = true; *hasResultType = true; break; + case OpSDiv: *hasResult = true; *hasResultType = true; break; + case OpFDiv: *hasResult = true; *hasResultType = true; break; + case OpUMod: *hasResult = true; *hasResultType = true; break; + case OpSRem: *hasResult = true; *hasResultType = true; break; + case OpSMod: *hasResult = true; *hasResultType = true; break; + case OpFRem: *hasResult = true; *hasResultType = true; break; + case OpFMod: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpOuterProduct: *hasResult = true; *hasResultType = true; break; + case OpDot: *hasResult = true; *hasResultType = true; break; + case OpIAddCarry: *hasResult = true; *hasResultType = true; break; + case OpISubBorrow: *hasResult = true; *hasResultType = true; break; + case OpUMulExtended: *hasResult = true; *hasResultType = true; break; + case OpSMulExtended: *hasResult = true; *hasResultType = true; break; + case OpAny: *hasResult = true; *hasResultType = true; break; + case OpAll: *hasResult = true; *hasResultType = true; break; + case OpIsNan: *hasResult = true; *hasResultType = true; break; + case OpIsInf: *hasResult = true; *hasResultType = true; break; + case OpIsFinite: *hasResult = true; *hasResultType = true; break; + case OpIsNormal: *hasResult = true; *hasResultType = true; break; + case OpSignBitSet: *hasResult = true; *hasResultType = true; break; + case OpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case OpOrdered: *hasResult = true; *hasResultType = true; break; + case OpUnordered: *hasResult = true; *hasResultType = true; break; + case OpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpLogicalNot: *hasResult = true; *hasResultType = true; break; + case OpSelect: *hasResult = true; *hasResultType = true; break; + case OpIEqual: *hasResult = true; *hasResultType = true; break; + case OpINotEqual: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpULessThan: *hasResult = true; *hasResultType = true; break; + case OpSLessThan: *hasResult = true; *hasResultType = true; break; + case OpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case OpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case OpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case OpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpNot: *hasResult = true; *hasResultType = true; break; + case OpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case OpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case OpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case OpBitReverse: *hasResult = true; *hasResultType = true; break; + case OpBitCount: *hasResult = true; *hasResultType = true; break; + case OpDPdx: *hasResult = true; *hasResultType = true; break; + case OpDPdy: *hasResult = true; *hasResultType = true; break; + case OpFwidth: *hasResult = true; *hasResultType = true; break; + case OpDPdxFine: *hasResult = true; *hasResultType = true; break; + case OpDPdyFine: *hasResult = true; *hasResultType = true; break; + case OpFwidthFine: *hasResult = true; *hasResultType = true; break; + case OpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case OpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case OpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case OpEmitVertex: *hasResult = false; *hasResultType = false; break; + case OpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case OpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case OpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case OpControlBarrier: *hasResult = false; *hasResultType = false; break; + case OpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case OpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case OpAtomicStore: *hasResult = false; *hasResultType = false; break; + case OpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case OpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case OpAtomicISub: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case OpAtomicOr: *hasResult = true; *hasResultType = true; break; + case OpAtomicXor: *hasResult = true; *hasResultType = true; break; + case OpPhi: *hasResult = true; *hasResultType = true; break; + case OpLoopMerge: *hasResult = false; *hasResultType = false; break; + case OpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case OpLabel: *hasResult = true; *hasResultType = false; break; + case OpBranch: *hasResult = false; *hasResultType = false; break; + case OpBranchConditional: *hasResult = false; *hasResultType = false; break; + case OpSwitch: *hasResult = false; *hasResultType = false; break; + case OpKill: *hasResult = false; *hasResultType = false; break; + case OpReturn: *hasResult = false; *hasResultType = false; break; + case OpReturnValue: *hasResult = false; *hasResultType = false; break; + case OpUnreachable: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case OpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case OpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case OpGroupAll: *hasResult = true; *hasResultType = true; break; + case OpGroupAny: *hasResult = true; *hasResultType = true; break; + case OpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupSMax: *hasResult = true; *hasResultType = true; break; + case OpReadPipe: *hasResult = true; *hasResultType = true; break; + case OpWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case OpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case OpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case OpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case OpRetainEvent: *hasResult = false; *hasResultType = false; break; + case OpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case OpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case OpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case OpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case OpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case OpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case OpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case OpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case OpNoLine: *hasResult = false; *hasResultType = false; break; + case OpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case OpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case OpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case OpSizeOf: *hasResult = true; *hasResultType = true; break; + case OpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case OpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case OpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case OpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case OpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case OpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case OpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case OpDecorateId: *hasResult = false; *hasResultType = false; break; + case OpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case OpCopyLogical: *hasResult = true; *hasResultType = true; break; + case OpPtrEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrDiff: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpTypeRayQueryProvisionalKHR: *hasResult = true; *hasResultType = false; break; + case OpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case OpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break; + case OpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case OpReportIntersectionNV: *hasResult = true; *hasResultType = true; break; + case OpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case OpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case OpTraceNV: *hasResult = false; *hasResultType = false; break; + case OpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; + case OpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case OpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case OpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case OpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpDemoteToHelperInvocationEXT: *hasResult = false; *hasResultType = false; break; + case OpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpDecorateString: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case OpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case OpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + } +} +#endif /* SPV_ENABLE_UTILITY_CODE */ + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } +inline RayFlagsMask operator|(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/armorcore/tools/to_spirv/glslang/SPIRV/spvIR.h b/armorcore/tools/to_spirv/glslang/SPIRV/spvIR.h new file mode 100755 index 000000000..6523035e7 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/SPIRV/spvIR.h @@ -0,0 +1,485 @@ +// +// Copyright (C) 2014 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// SPIRV-IR +// +// Simple in-memory representation (IR) of SPIRV. Just for holding +// Each function's CFG of blocks. Has this hierarchy: +// - Module, which is a list of +// - Function, which is a list of +// - Block, which is a list of +// - Instruction +// + +#pragma once +#ifndef spvIR_H +#define spvIR_H + +#include "spirv.hpp" + +#include +#include +#include +#include +#include +#include + +namespace spv { + +class Block; +class Function; +class Module; + +const Id NoResult = 0; +const Id NoType = 0; + +const Decoration NoPrecision = DecorationMax; + +#ifdef __GNUC__ +# define POTENTIALLY_UNUSED __attribute__((unused)) +#else +# define POTENTIALLY_UNUSED +#endif + +POTENTIALLY_UNUSED +const MemorySemanticsMask MemorySemanticsAllMemory = + (MemorySemanticsMask)(MemorySemanticsUniformMemoryMask | + MemorySemanticsWorkgroupMemoryMask | + MemorySemanticsAtomicCounterMemoryMask | + MemorySemanticsImageMemoryMask); + +struct IdImmediate { + bool isId; // true if word is an Id, false if word is an immediate + unsigned word; + IdImmediate(bool i, unsigned w) : isId(i), word(w) {} +}; + +// +// SPIR-V IR instruction. +// + +class Instruction { +public: + Instruction(Id resultId, Id typeId, Op opCode) : resultId(resultId), typeId(typeId), opCode(opCode), block(nullptr) { } + explicit Instruction(Op opCode) : resultId(NoResult), typeId(NoType), opCode(opCode), block(nullptr) { } + virtual ~Instruction() {} + void addIdOperand(Id id) { + operands.push_back(id); + idOperand.push_back(true); + } + void addImmediateOperand(unsigned int immediate) { + operands.push_back(immediate); + idOperand.push_back(false); + } + void setImmediateOperand(unsigned idx, unsigned int immediate) { + assert(!idOperand[idx]); + operands[idx] = immediate; + } + + void addStringOperand(const char* str) + { + unsigned int word; + char* wordString = (char*)&word; + char* wordPtr = wordString; + int charCount = 0; + char c; + do { + c = *(str++); + *(wordPtr++) = c; + ++charCount; + if (charCount == 4) { + addImmediateOperand(word); + wordPtr = wordString; + charCount = 0; + } + } while (c != 0); + + // deal with partial last word + if (charCount > 0) { + // pad with 0s + for (; charCount < 4; ++charCount) + *(wordPtr++) = 0; + addImmediateOperand(word); + } + } + bool isIdOperand(int op) const { return idOperand[op]; } + void setBlock(Block* b) { block = b; } + Block* getBlock() const { return block; } + Op getOpCode() const { return opCode; } + int getNumOperands() const + { + assert(operands.size() == idOperand.size()); + return (int)operands.size(); + } + Id getResultId() const { return resultId; } + Id getTypeId() const { return typeId; } + Id getIdOperand(int op) const { + assert(idOperand[op]); + return operands[op]; + } + unsigned int getImmediateOperand(int op) const { + assert(!idOperand[op]); + return operands[op]; + } + + // Write out the binary form. + void dump(std::vector& out) const + { + // Compute the wordCount + unsigned int wordCount = 1; + if (typeId) + ++wordCount; + if (resultId) + ++wordCount; + wordCount += (unsigned int)operands.size(); + + // Write out the beginning of the instruction + out.push_back(((wordCount) << WordCountShift) | opCode); + if (typeId) + out.push_back(typeId); + if (resultId) + out.push_back(resultId); + + // Write out the operands + for (int op = 0; op < (int)operands.size(); ++op) + out.push_back(operands[op]); + } + +protected: + Instruction(const Instruction&); + Id resultId; + Id typeId; + Op opCode; + std::vector operands; // operands, both and immediates (both are unsigned int) + std::vector idOperand; // true for operands that are , false for immediates + Block* block; +}; + +// +// SPIR-V IR block. +// + +class Block { +public: + Block(Id id, Function& parent); + virtual ~Block() + { + } + + Id getId() { return instructions.front()->getResultId(); } + + Function& getParent() const { return parent; } + void addInstruction(std::unique_ptr inst); + void addPredecessor(Block* pred) { predecessors.push_back(pred); pred->successors.push_back(this);} + void addLocalVariable(std::unique_ptr inst) { localVariables.push_back(std::move(inst)); } + const std::vector& getPredecessors() const { return predecessors; } + const std::vector& getSuccessors() const { return successors; } + const std::vector >& getInstructions() const { + return instructions; + } + const std::vector >& getLocalVariables() const { return localVariables; } + void setUnreachable() { unreachable = true; } + bool isUnreachable() const { return unreachable; } + // Returns the block's merge instruction, if one exists (otherwise null). + const Instruction* getMergeInstruction() const { + if (instructions.size() < 2) return nullptr; + const Instruction* nextToLast = (instructions.cend() - 2)->get(); + switch (nextToLast->getOpCode()) { + case OpSelectionMerge: + case OpLoopMerge: + return nextToLast; + default: + return nullptr; + } + return nullptr; + } + + // Change this block into a canonical dead merge block. Delete instructions + // as necessary. A canonical dead merge block has only an OpLabel and an + // OpUnreachable. + void rewriteAsCanonicalUnreachableMerge() { + assert(localVariables.empty()); + // Delete all instructions except for the label. + assert(instructions.size() > 0); + instructions.resize(1); + successors.clear(); + addInstruction(std::unique_ptr(new Instruction(OpUnreachable))); + } + // Change this block into a canonical dead continue target branching to the + // given header ID. Delete instructions as necessary. A canonical dead continue + // target has only an OpLabel and an unconditional branch back to the corresponding + // header. + void rewriteAsCanonicalUnreachableContinue(Block* header) { + assert(localVariables.empty()); + // Delete all instructions except for the label. + assert(instructions.size() > 0); + instructions.resize(1); + successors.clear(); + // Add OpBranch back to the header. + assert(header != nullptr); + Instruction* branch = new Instruction(OpBranch); + branch->addIdOperand(header->getId()); + addInstruction(std::unique_ptr(branch)); + successors.push_back(header); + } + + bool isTerminated() const + { + switch (instructions.back()->getOpCode()) { + case OpBranch: + case OpBranchConditional: + case OpSwitch: + case OpKill: + case OpReturn: + case OpReturnValue: + case OpUnreachable: + return true; + default: + return false; + } + } + + void dump(std::vector& out) const + { + instructions[0]->dump(out); + for (int i = 0; i < (int)localVariables.size(); ++i) + localVariables[i]->dump(out); + for (int i = 1; i < (int)instructions.size(); ++i) + instructions[i]->dump(out); + } + +protected: + Block(const Block&); + Block& operator=(Block&); + + // To enforce keeping parent and ownership in sync: + friend Function; + + std::vector > instructions; + std::vector predecessors, successors; + std::vector > localVariables; + Function& parent; + + // track whether this block is known to be uncreachable (not necessarily + // true for all unreachable blocks, but should be set at least + // for the extraneous ones introduced by the builder). + bool unreachable; +}; + +// The different reasons for reaching a block in the inReadableOrder traversal. +enum ReachReason { + // Reachable from the entry block via transfers of control, i.e. branches. + ReachViaControlFlow = 0, + // A continue target that is not reachable via control flow. + ReachDeadContinue, + // A merge block that is not reachable via control flow. + ReachDeadMerge +}; + +// Traverses the control-flow graph rooted at root in an order suited for +// readable code generation. Invokes callback at every node in the traversal +// order. The callback arguments are: +// - the block, +// - the reason we reached the block, +// - if the reason was that block is an unreachable continue or unreachable merge block +// then the last parameter is the corresponding header block. +void inReadableOrder(Block* root, std::function callback); + +// +// SPIR-V IR Function. +// + +class Function { +public: + Function(Id id, Id resultType, Id functionType, Id firstParam, Module& parent); + virtual ~Function() + { + for (int i = 0; i < (int)parameterInstructions.size(); ++i) + delete parameterInstructions[i]; + + for (int i = 0; i < (int)blocks.size(); ++i) + delete blocks[i]; + } + Id getId() const { return functionInstruction.getResultId(); } + Id getParamId(int p) const { return parameterInstructions[p]->getResultId(); } + Id getParamType(int p) const { return parameterInstructions[p]->getTypeId(); } + + void addBlock(Block* block) { blocks.push_back(block); } + void removeBlock(Block* block) + { + auto found = find(blocks.begin(), blocks.end(), block); + assert(found != blocks.end()); + blocks.erase(found); + delete block; + } + + Module& getParent() const { return parent; } + Block* getEntryBlock() const { return blocks.front(); } + Block* getLastBlock() const { return blocks.back(); } + const std::vector& getBlocks() const { return blocks; } + void addLocalVariable(std::unique_ptr inst); + Id getReturnType() const { return functionInstruction.getTypeId(); } + + void setImplicitThis() { implicitThis = true; } + bool hasImplicitThis() const { return implicitThis; } + + void dump(std::vector& out) const + { + // OpFunction + functionInstruction.dump(out); + + // OpFunctionParameter + for (int p = 0; p < (int)parameterInstructions.size(); ++p) + parameterInstructions[p]->dump(out); + + // Blocks + inReadableOrder(blocks[0], [&out](const Block* b, ReachReason, Block*) { b->dump(out); }); + Instruction end(0, 0, OpFunctionEnd); + end.dump(out); + } + +protected: + Function(const Function&); + Function& operator=(Function&); + + Module& parent; + Instruction functionInstruction; + std::vector parameterInstructions; + std::vector blocks; + bool implicitThis; // true if this is a member function expecting to be passed a 'this' as the first argument +}; + +// +// SPIR-V IR Module. +// + +class Module { +public: + Module() {} + virtual ~Module() + { + // TODO delete things + } + + void addFunction(Function *fun) { functions.push_back(fun); } + + void mapInstruction(Instruction *instruction) + { + spv::Id resultId = instruction->getResultId(); + // map the instruction's result id + if (resultId >= idToInstruction.size()) + idToInstruction.resize(resultId + 16); + idToInstruction[resultId] = instruction; + } + + Instruction* getInstruction(Id id) const { return idToInstruction[id]; } + const std::vector& getFunctions() const { return functions; } + spv::Id getTypeId(Id resultId) const { + return idToInstruction[resultId] == nullptr ? NoType : idToInstruction[resultId]->getTypeId(); + } + StorageClass getStorageClass(Id typeId) const + { + assert(idToInstruction[typeId]->getOpCode() == spv::OpTypePointer); + return (StorageClass)idToInstruction[typeId]->getImmediateOperand(0); + } + + void dump(std::vector& out) const + { + for (int f = 0; f < (int)functions.size(); ++f) + functions[f]->dump(out); + } + +protected: + Module(const Module&); + std::vector functions; + + // map from result id to instruction having that result id + std::vector idToInstruction; + + // map from a result id to its type id +}; + +// +// Implementation (it's here due to circular type definitions). +// + +// Add both +// - the OpFunction instruction +// - all the OpFunctionParameter instructions +__inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, Module& parent) + : parent(parent), functionInstruction(id, resultType, OpFunction), implicitThis(false) +{ + // OpFunction + functionInstruction.addImmediateOperand(FunctionControlMaskNone); + functionInstruction.addIdOperand(functionType); + parent.mapInstruction(&functionInstruction); + parent.addFunction(this); + + // OpFunctionParameter + Instruction* typeInst = parent.getInstruction(functionType); + int numParams = typeInst->getNumOperands() - 1; + for (int p = 0; p < numParams; ++p) { + Instruction* param = new Instruction(firstParamId + p, typeInst->getIdOperand(p + 1), OpFunctionParameter); + parent.mapInstruction(param); + parameterInstructions.push_back(param); + } +} + +__inline void Function::addLocalVariable(std::unique_ptr inst) +{ + Instruction* raw_instruction = inst.get(); + blocks[0]->addLocalVariable(std::move(inst)); + parent.mapInstruction(raw_instruction); +} + +__inline Block::Block(Id id, Function& parent) : parent(parent), unreachable(false) +{ + instructions.push_back(std::unique_ptr(new Instruction(id, NoType, OpLabel))); + instructions.back()->setBlock(this); + parent.getParent().mapInstruction(instructions.back().get()); +} + +__inline void Block::addInstruction(std::unique_ptr inst) +{ + Instruction* raw_instruction = inst.get(); + instructions.push_back(std::move(inst)); + raw_instruction->setBlock(this); + if (raw_instruction->getResultId()) + parent.getParent().mapInstruction(raw_instruction); +} + +} // end spv namespace + +#endif // spvIR_H diff --git a/armorcore/tools/to_spirv/glslang/StandAlone/ResourceLimits.cpp b/armorcore/tools/to_spirv/glslang/StandAlone/ResourceLimits.cpp new file mode 100644 index 000000000..7c7f4c4e4 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/StandAlone/ResourceLimits.cpp @@ -0,0 +1,496 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include + +#include "ResourceLimits.h" + +namespace glslang { + +const TBuiltInResource DefaultTBuiltInResource = { + /* .MaxLights = */ 32, + /* .MaxClipPlanes = */ 6, + /* .MaxTextureUnits = */ 32, + /* .MaxTextureCoords = */ 32, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 4096, + /* .MaxVaryingFloats = */ 64, + /* .MaxVertexTextureImageUnits = */ 32, + /* .MaxCombinedTextureImageUnits = */ 80, + /* .MaxTextureImageUnits = */ 32, + /* .MaxFragmentUniformComponents = */ 4096, + /* .MaxDrawBuffers = */ 32, + /* .MaxVertexUniformVectors = */ 128, + /* .MaxVaryingVectors = */ 8, + /* .MaxFragmentUniformVectors = */ 16, + /* .MaxVertexOutputVectors = */ 16, + /* .MaxFragmentInputVectors = */ 15, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 16, + /* .MaxComputeImageUniforms = */ 8, + /* .MaxComputeAtomicCounters = */ 8, + /* .MaxComputeAtomicCounterBuffers = */ 1, + /* .MaxVaryingComponents = */ 60, + /* .MaxVertexOutputComponents = */ 64, + /* .MaxGeometryInputComponents = */ 64, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 128, + /* .MaxImageUnits = */ 8, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8, + /* .MaxCombinedShaderOutputResources = */ 8, + /* .MaxImageSamples = */ 0, + /* .MaxVertexImageUniforms = */ 0, + /* .MaxTessControlImageUniforms = */ 0, + /* .MaxTessEvaluationImageUniforms = */ 0, + /* .MaxGeometryImageUniforms = */ 0, + /* .MaxFragmentImageUniforms = */ 8, + /* .MaxCombinedImageUniforms = */ 8, + /* .MaxGeometryTextureImageUnits = */ 16, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 16, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 0, + /* .MaxTessControlAtomicCounters = */ 0, + /* .MaxTessEvaluationAtomicCounters = */ 0, + /* .MaxGeometryAtomicCounters = */ 0, + /* .MaxFragmentAtomicCounters = */ 8, + /* .MaxCombinedAtomicCounters = */ 8, + /* .MaxAtomicCounterBindings = */ 1, + /* .MaxVertexAtomicCounterBuffers = */ 0, + /* .MaxTessControlAtomicCounterBuffers = */ 0, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, + /* .MaxGeometryAtomicCounterBuffers = */ 0, + /* .MaxFragmentAtomicCounterBuffers = */ 1, + /* .MaxCombinedAtomicCounterBuffers = */ 1, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 4, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + /* .maxDualSourceDrawBuffersEXT = */ 1, + + /* .limits = */ { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + }}; + +std::string GetDefaultTBuiltInResourceString() +{ + std::ostringstream ostream; + + ostream << "MaxLights " << DefaultTBuiltInResource.maxLights << "\n" + << "MaxClipPlanes " << DefaultTBuiltInResource.maxClipPlanes << "\n" + << "MaxTextureUnits " << DefaultTBuiltInResource.maxTextureUnits << "\n" + << "MaxTextureCoords " << DefaultTBuiltInResource.maxTextureCoords << "\n" + << "MaxVertexAttribs " << DefaultTBuiltInResource.maxVertexAttribs << "\n" + << "MaxVertexUniformComponents " << DefaultTBuiltInResource.maxVertexUniformComponents << "\n" + << "MaxVaryingFloats " << DefaultTBuiltInResource.maxVaryingFloats << "\n" + << "MaxVertexTextureImageUnits " << DefaultTBuiltInResource.maxVertexTextureImageUnits << "\n" + << "MaxCombinedTextureImageUnits " << DefaultTBuiltInResource.maxCombinedTextureImageUnits << "\n" + << "MaxTextureImageUnits " << DefaultTBuiltInResource.maxTextureImageUnits << "\n" + << "MaxFragmentUniformComponents " << DefaultTBuiltInResource.maxFragmentUniformComponents << "\n" + << "MaxDrawBuffers " << DefaultTBuiltInResource.maxDrawBuffers << "\n" + << "MaxVertexUniformVectors " << DefaultTBuiltInResource.maxVertexUniformVectors << "\n" + << "MaxVaryingVectors " << DefaultTBuiltInResource.maxVaryingVectors << "\n" + << "MaxFragmentUniformVectors " << DefaultTBuiltInResource.maxFragmentUniformVectors << "\n" + << "MaxVertexOutputVectors " << DefaultTBuiltInResource.maxVertexOutputVectors << "\n" + << "MaxFragmentInputVectors " << DefaultTBuiltInResource.maxFragmentInputVectors << "\n" + << "MinProgramTexelOffset " << DefaultTBuiltInResource.minProgramTexelOffset << "\n" + << "MaxProgramTexelOffset " << DefaultTBuiltInResource.maxProgramTexelOffset << "\n" + << "MaxClipDistances " << DefaultTBuiltInResource.maxClipDistances << "\n" + << "MaxComputeWorkGroupCountX " << DefaultTBuiltInResource.maxComputeWorkGroupCountX << "\n" + << "MaxComputeWorkGroupCountY " << DefaultTBuiltInResource.maxComputeWorkGroupCountY << "\n" + << "MaxComputeWorkGroupCountZ " << DefaultTBuiltInResource.maxComputeWorkGroupCountZ << "\n" + << "MaxComputeWorkGroupSizeX " << DefaultTBuiltInResource.maxComputeWorkGroupSizeX << "\n" + << "MaxComputeWorkGroupSizeY " << DefaultTBuiltInResource.maxComputeWorkGroupSizeY << "\n" + << "MaxComputeWorkGroupSizeZ " << DefaultTBuiltInResource.maxComputeWorkGroupSizeZ << "\n" + << "MaxComputeUniformComponents " << DefaultTBuiltInResource.maxComputeUniformComponents << "\n" + << "MaxComputeTextureImageUnits " << DefaultTBuiltInResource.maxComputeTextureImageUnits << "\n" + << "MaxComputeImageUniforms " << DefaultTBuiltInResource.maxComputeImageUniforms << "\n" + << "MaxComputeAtomicCounters " << DefaultTBuiltInResource.maxComputeAtomicCounters << "\n" + << "MaxComputeAtomicCounterBuffers " << DefaultTBuiltInResource.maxComputeAtomicCounterBuffers << "\n" + << "MaxVaryingComponents " << DefaultTBuiltInResource.maxVaryingComponents << "\n" + << "MaxVertexOutputComponents " << DefaultTBuiltInResource.maxVertexOutputComponents << "\n" + << "MaxGeometryInputComponents " << DefaultTBuiltInResource.maxGeometryInputComponents << "\n" + << "MaxGeometryOutputComponents " << DefaultTBuiltInResource.maxGeometryOutputComponents << "\n" + << "MaxFragmentInputComponents " << DefaultTBuiltInResource.maxFragmentInputComponents << "\n" + << "MaxImageUnits " << DefaultTBuiltInResource.maxImageUnits << "\n" + << "MaxCombinedImageUnitsAndFragmentOutputs " << DefaultTBuiltInResource.maxCombinedImageUnitsAndFragmentOutputs << "\n" + << "MaxCombinedShaderOutputResources " << DefaultTBuiltInResource.maxCombinedShaderOutputResources << "\n" + << "MaxImageSamples " << DefaultTBuiltInResource.maxImageSamples << "\n" + << "MaxVertexImageUniforms " << DefaultTBuiltInResource.maxVertexImageUniforms << "\n" + << "MaxTessControlImageUniforms " << DefaultTBuiltInResource.maxTessControlImageUniforms << "\n" + << "MaxTessEvaluationImageUniforms " << DefaultTBuiltInResource.maxTessEvaluationImageUniforms << "\n" + << "MaxGeometryImageUniforms " << DefaultTBuiltInResource.maxGeometryImageUniforms << "\n" + << "MaxFragmentImageUniforms " << DefaultTBuiltInResource.maxFragmentImageUniforms << "\n" + << "MaxCombinedImageUniforms " << DefaultTBuiltInResource.maxCombinedImageUniforms << "\n" + << "MaxGeometryTextureImageUnits " << DefaultTBuiltInResource.maxGeometryTextureImageUnits << "\n" + << "MaxGeometryOutputVertices " << DefaultTBuiltInResource.maxGeometryOutputVertices << "\n" + << "MaxGeometryTotalOutputComponents " << DefaultTBuiltInResource.maxGeometryTotalOutputComponents << "\n" + << "MaxGeometryUniformComponents " << DefaultTBuiltInResource.maxGeometryUniformComponents << "\n" + << "MaxGeometryVaryingComponents " << DefaultTBuiltInResource.maxGeometryVaryingComponents << "\n" + << "MaxTessControlInputComponents " << DefaultTBuiltInResource.maxTessControlInputComponents << "\n" + << "MaxTessControlOutputComponents " << DefaultTBuiltInResource.maxTessControlOutputComponents << "\n" + << "MaxTessControlTextureImageUnits " << DefaultTBuiltInResource.maxTessControlTextureImageUnits << "\n" + << "MaxTessControlUniformComponents " << DefaultTBuiltInResource.maxTessControlUniformComponents << "\n" + << "MaxTessControlTotalOutputComponents " << DefaultTBuiltInResource.maxTessControlTotalOutputComponents << "\n" + << "MaxTessEvaluationInputComponents " << DefaultTBuiltInResource.maxTessEvaluationInputComponents << "\n" + << "MaxTessEvaluationOutputComponents " << DefaultTBuiltInResource.maxTessEvaluationOutputComponents << "\n" + << "MaxTessEvaluationTextureImageUnits " << DefaultTBuiltInResource.maxTessEvaluationTextureImageUnits << "\n" + << "MaxTessEvaluationUniformComponents " << DefaultTBuiltInResource.maxTessEvaluationUniformComponents << "\n" + << "MaxTessPatchComponents " << DefaultTBuiltInResource.maxTessPatchComponents << "\n" + << "MaxPatchVertices " << DefaultTBuiltInResource.maxPatchVertices << "\n" + << "MaxTessGenLevel " << DefaultTBuiltInResource.maxTessGenLevel << "\n" + << "MaxViewports " << DefaultTBuiltInResource.maxViewports << "\n" + << "MaxVertexAtomicCounters " << DefaultTBuiltInResource.maxVertexAtomicCounters << "\n" + << "MaxTessControlAtomicCounters " << DefaultTBuiltInResource.maxTessControlAtomicCounters << "\n" + << "MaxTessEvaluationAtomicCounters " << DefaultTBuiltInResource.maxTessEvaluationAtomicCounters << "\n" + << "MaxGeometryAtomicCounters " << DefaultTBuiltInResource.maxGeometryAtomicCounters << "\n" + << "MaxFragmentAtomicCounters " << DefaultTBuiltInResource.maxFragmentAtomicCounters << "\n" + << "MaxCombinedAtomicCounters " << DefaultTBuiltInResource.maxCombinedAtomicCounters << "\n" + << "MaxAtomicCounterBindings " << DefaultTBuiltInResource.maxAtomicCounterBindings << "\n" + << "MaxVertexAtomicCounterBuffers " << DefaultTBuiltInResource.maxVertexAtomicCounterBuffers << "\n" + << "MaxTessControlAtomicCounterBuffers " << DefaultTBuiltInResource.maxTessControlAtomicCounterBuffers << "\n" + << "MaxTessEvaluationAtomicCounterBuffers " << DefaultTBuiltInResource.maxTessEvaluationAtomicCounterBuffers << "\n" + << "MaxGeometryAtomicCounterBuffers " << DefaultTBuiltInResource.maxGeometryAtomicCounterBuffers << "\n" + << "MaxFragmentAtomicCounterBuffers " << DefaultTBuiltInResource.maxFragmentAtomicCounterBuffers << "\n" + << "MaxCombinedAtomicCounterBuffers " << DefaultTBuiltInResource.maxCombinedAtomicCounterBuffers << "\n" + << "MaxAtomicCounterBufferSize " << DefaultTBuiltInResource.maxAtomicCounterBufferSize << "\n" + << "MaxTransformFeedbackBuffers " << DefaultTBuiltInResource.maxTransformFeedbackBuffers << "\n" + << "MaxTransformFeedbackInterleavedComponents " << DefaultTBuiltInResource.maxTransformFeedbackInterleavedComponents << "\n" + << "MaxCullDistances " << DefaultTBuiltInResource.maxCullDistances << "\n" + << "MaxCombinedClipAndCullDistances " << DefaultTBuiltInResource.maxCombinedClipAndCullDistances << "\n" + << "MaxSamples " << DefaultTBuiltInResource.maxSamples << "\n" + << "MaxMeshOutputVerticesNV " << DefaultTBuiltInResource.maxMeshOutputVerticesNV << "\n" + << "MaxMeshOutputPrimitivesNV " << DefaultTBuiltInResource.maxMeshOutputPrimitivesNV << "\n" + << "MaxMeshWorkGroupSizeX_NV " << DefaultTBuiltInResource.maxMeshWorkGroupSizeX_NV << "\n" + << "MaxMeshWorkGroupSizeY_NV " << DefaultTBuiltInResource.maxMeshWorkGroupSizeY_NV << "\n" + << "MaxMeshWorkGroupSizeZ_NV " << DefaultTBuiltInResource.maxMeshWorkGroupSizeZ_NV << "\n" + << "MaxTaskWorkGroupSizeX_NV " << DefaultTBuiltInResource.maxTaskWorkGroupSizeX_NV << "\n" + << "MaxTaskWorkGroupSizeY_NV " << DefaultTBuiltInResource.maxTaskWorkGroupSizeY_NV << "\n" + << "MaxTaskWorkGroupSizeZ_NV " << DefaultTBuiltInResource.maxTaskWorkGroupSizeZ_NV << "\n" + << "MaxMeshViewCountNV " << DefaultTBuiltInResource.maxMeshViewCountNV << "\n" + << "MaxDualSourceDrawBuffersEXT " << DefaultTBuiltInResource.maxDualSourceDrawBuffersEXT << "\n" + << "nonInductiveForLoops " << DefaultTBuiltInResource.limits.nonInductiveForLoops << "\n" + << "whileLoops " << DefaultTBuiltInResource.limits.whileLoops << "\n" + << "doWhileLoops " << DefaultTBuiltInResource.limits.doWhileLoops << "\n" + << "generalUniformIndexing " << DefaultTBuiltInResource.limits.generalUniformIndexing << "\n" + << "generalAttributeMatrixVectorIndexing " << DefaultTBuiltInResource.limits.generalAttributeMatrixVectorIndexing << "\n" + << "generalVaryingIndexing " << DefaultTBuiltInResource.limits.generalVaryingIndexing << "\n" + << "generalSamplerIndexing " << DefaultTBuiltInResource.limits.generalSamplerIndexing << "\n" + << "generalVariableIndexing " << DefaultTBuiltInResource.limits.generalVariableIndexing << "\n" + << "generalConstantMatrixVectorIndexing " << DefaultTBuiltInResource.limits.generalConstantMatrixVectorIndexing << "\n" + ; + + return ostream.str(); +} + +void DecodeResourceLimits(TBuiltInResource* resources, char* config) +{ + static const char* delims = " \t\n\r"; + + size_t pos = 0; + std::string configStr(config); + + while ((pos = configStr.find_first_not_of(delims, pos)) != std::string::npos) { + const size_t token_s = pos; + const size_t token_e = configStr.find_first_of(delims, token_s); + const size_t value_s = configStr.find_first_not_of(delims, token_e); + const size_t value_e = configStr.find_first_of(delims, value_s); + pos = value_e; + + // Faster to use compare(), but prefering readability. + const std::string tokenStr = configStr.substr(token_s, token_e-token_s); + const std::string valueStr = configStr.substr(value_s, value_e-value_s); + + if (value_s == std::string::npos || ! (valueStr[0] == '-' || isdigit(valueStr[0]))) { + printf("Error: '%s' bad .conf file. Each name must be followed by one number.\n", + valueStr.c_str()); + return; + } + + const int value = std::atoi(valueStr.c_str()); + + if (tokenStr == "MaxLights") + resources->maxLights = value; + else if (tokenStr == "MaxClipPlanes") + resources->maxClipPlanes = value; + else if (tokenStr == "MaxTextureUnits") + resources->maxTextureUnits = value; + else if (tokenStr == "MaxTextureCoords") + resources->maxTextureCoords = value; + else if (tokenStr == "MaxVertexAttribs") + resources->maxVertexAttribs = value; + else if (tokenStr == "MaxVertexUniformComponents") + resources->maxVertexUniformComponents = value; + else if (tokenStr == "MaxVaryingFloats") + resources->maxVaryingFloats = value; + else if (tokenStr == "MaxVertexTextureImageUnits") + resources->maxVertexTextureImageUnits = value; + else if (tokenStr == "MaxCombinedTextureImageUnits") + resources->maxCombinedTextureImageUnits = value; + else if (tokenStr == "MaxTextureImageUnits") + resources->maxTextureImageUnits = value; + else if (tokenStr == "MaxFragmentUniformComponents") + resources->maxFragmentUniformComponents = value; + else if (tokenStr == "MaxDrawBuffers") + resources->maxDrawBuffers = value; + else if (tokenStr == "MaxVertexUniformVectors") + resources->maxVertexUniformVectors = value; + else if (tokenStr == "MaxVaryingVectors") + resources->maxVaryingVectors = value; + else if (tokenStr == "MaxFragmentUniformVectors") + resources->maxFragmentUniformVectors = value; + else if (tokenStr == "MaxVertexOutputVectors") + resources->maxVertexOutputVectors = value; + else if (tokenStr == "MaxFragmentInputVectors") + resources->maxFragmentInputVectors = value; + else if (tokenStr == "MinProgramTexelOffset") + resources->minProgramTexelOffset = value; + else if (tokenStr == "MaxProgramTexelOffset") + resources->maxProgramTexelOffset = value; + else if (tokenStr == "MaxClipDistances") + resources->maxClipDistances = value; + else if (tokenStr == "MaxComputeWorkGroupCountX") + resources->maxComputeWorkGroupCountX = value; + else if (tokenStr == "MaxComputeWorkGroupCountY") + resources->maxComputeWorkGroupCountY = value; + else if (tokenStr == "MaxComputeWorkGroupCountZ") + resources->maxComputeWorkGroupCountZ = value; + else if (tokenStr == "MaxComputeWorkGroupSizeX") + resources->maxComputeWorkGroupSizeX = value; + else if (tokenStr == "MaxComputeWorkGroupSizeY") + resources->maxComputeWorkGroupSizeY = value; + else if (tokenStr == "MaxComputeWorkGroupSizeZ") + resources->maxComputeWorkGroupSizeZ = value; + else if (tokenStr == "MaxComputeUniformComponents") + resources->maxComputeUniformComponents = value; + else if (tokenStr == "MaxComputeTextureImageUnits") + resources->maxComputeTextureImageUnits = value; + else if (tokenStr == "MaxComputeImageUniforms") + resources->maxComputeImageUniforms = value; + else if (tokenStr == "MaxComputeAtomicCounters") + resources->maxComputeAtomicCounters = value; + else if (tokenStr == "MaxComputeAtomicCounterBuffers") + resources->maxComputeAtomicCounterBuffers = value; + else if (tokenStr == "MaxVaryingComponents") + resources->maxVaryingComponents = value; + else if (tokenStr == "MaxVertexOutputComponents") + resources->maxVertexOutputComponents = value; + else if (tokenStr == "MaxGeometryInputComponents") + resources->maxGeometryInputComponents = value; + else if (tokenStr == "MaxGeometryOutputComponents") + resources->maxGeometryOutputComponents = value; + else if (tokenStr == "MaxFragmentInputComponents") + resources->maxFragmentInputComponents = value; + else if (tokenStr == "MaxImageUnits") + resources->maxImageUnits = value; + else if (tokenStr == "MaxCombinedImageUnitsAndFragmentOutputs") + resources->maxCombinedImageUnitsAndFragmentOutputs = value; + else if (tokenStr == "MaxCombinedShaderOutputResources") + resources->maxCombinedShaderOutputResources = value; + else if (tokenStr == "MaxImageSamples") + resources->maxImageSamples = value; + else if (tokenStr == "MaxVertexImageUniforms") + resources->maxVertexImageUniforms = value; + else if (tokenStr == "MaxTessControlImageUniforms") + resources->maxTessControlImageUniforms = value; + else if (tokenStr == "MaxTessEvaluationImageUniforms") + resources->maxTessEvaluationImageUniforms = value; + else if (tokenStr == "MaxGeometryImageUniforms") + resources->maxGeometryImageUniforms = value; + else if (tokenStr == "MaxFragmentImageUniforms") + resources->maxFragmentImageUniforms = value; + else if (tokenStr == "MaxCombinedImageUniforms") + resources->maxCombinedImageUniforms = value; + else if (tokenStr == "MaxGeometryTextureImageUnits") + resources->maxGeometryTextureImageUnits = value; + else if (tokenStr == "MaxGeometryOutputVertices") + resources->maxGeometryOutputVertices = value; + else if (tokenStr == "MaxGeometryTotalOutputComponents") + resources->maxGeometryTotalOutputComponents = value; + else if (tokenStr == "MaxGeometryUniformComponents") + resources->maxGeometryUniformComponents = value; + else if (tokenStr == "MaxGeometryVaryingComponents") + resources->maxGeometryVaryingComponents = value; + else if (tokenStr == "MaxTessControlInputComponents") + resources->maxTessControlInputComponents = value; + else if (tokenStr == "MaxTessControlOutputComponents") + resources->maxTessControlOutputComponents = value; + else if (tokenStr == "MaxTessControlTextureImageUnits") + resources->maxTessControlTextureImageUnits = value; + else if (tokenStr == "MaxTessControlUniformComponents") + resources->maxTessControlUniformComponents = value; + else if (tokenStr == "MaxTessControlTotalOutputComponents") + resources->maxTessControlTotalOutputComponents = value; + else if (tokenStr == "MaxTessEvaluationInputComponents") + resources->maxTessEvaluationInputComponents = value; + else if (tokenStr == "MaxTessEvaluationOutputComponents") + resources->maxTessEvaluationOutputComponents = value; + else if (tokenStr == "MaxTessEvaluationTextureImageUnits") + resources->maxTessEvaluationTextureImageUnits = value; + else if (tokenStr == "MaxTessEvaluationUniformComponents") + resources->maxTessEvaluationUniformComponents = value; + else if (tokenStr == "MaxTessPatchComponents") + resources->maxTessPatchComponents = value; + else if (tokenStr == "MaxPatchVertices") + resources->maxPatchVertices = value; + else if (tokenStr == "MaxTessGenLevel") + resources->maxTessGenLevel = value; + else if (tokenStr == "MaxViewports") + resources->maxViewports = value; + else if (tokenStr == "MaxVertexAtomicCounters") + resources->maxVertexAtomicCounters = value; + else if (tokenStr == "MaxTessControlAtomicCounters") + resources->maxTessControlAtomicCounters = value; + else if (tokenStr == "MaxTessEvaluationAtomicCounters") + resources->maxTessEvaluationAtomicCounters = value; + else if (tokenStr == "MaxGeometryAtomicCounters") + resources->maxGeometryAtomicCounters = value; + else if (tokenStr == "MaxFragmentAtomicCounters") + resources->maxFragmentAtomicCounters = value; + else if (tokenStr == "MaxCombinedAtomicCounters") + resources->maxCombinedAtomicCounters = value; + else if (tokenStr == "MaxAtomicCounterBindings") + resources->maxAtomicCounterBindings = value; + else if (tokenStr == "MaxVertexAtomicCounterBuffers") + resources->maxVertexAtomicCounterBuffers = value; + else if (tokenStr == "MaxTessControlAtomicCounterBuffers") + resources->maxTessControlAtomicCounterBuffers = value; + else if (tokenStr == "MaxTessEvaluationAtomicCounterBuffers") + resources->maxTessEvaluationAtomicCounterBuffers = value; + else if (tokenStr == "MaxGeometryAtomicCounterBuffers") + resources->maxGeometryAtomicCounterBuffers = value; + else if (tokenStr == "MaxFragmentAtomicCounterBuffers") + resources->maxFragmentAtomicCounterBuffers = value; + else if (tokenStr == "MaxCombinedAtomicCounterBuffers") + resources->maxCombinedAtomicCounterBuffers = value; + else if (tokenStr == "MaxAtomicCounterBufferSize") + resources->maxAtomicCounterBufferSize = value; + else if (tokenStr == "MaxTransformFeedbackBuffers") + resources->maxTransformFeedbackBuffers = value; + else if (tokenStr == "MaxTransformFeedbackInterleavedComponents") + resources->maxTransformFeedbackInterleavedComponents = value; + else if (tokenStr == "MaxCullDistances") + resources->maxCullDistances = value; + else if (tokenStr == "MaxCombinedClipAndCullDistances") + resources->maxCombinedClipAndCullDistances = value; + else if (tokenStr == "MaxSamples") + resources->maxSamples = value; + else if (tokenStr == "MaxMeshOutputVerticesNV") + resources->maxMeshOutputVerticesNV = value; + else if (tokenStr == "MaxMeshOutputPrimitivesNV") + resources->maxMeshOutputPrimitivesNV = value; + else if (tokenStr == "MaxMeshWorkGroupSizeX_NV") + resources->maxMeshWorkGroupSizeX_NV = value; + else if (tokenStr == "MaxMeshWorkGroupSizeY_NV") + resources->maxMeshWorkGroupSizeY_NV = value; + else if (tokenStr == "MaxMeshWorkGroupSizeZ_NV") + resources->maxMeshWorkGroupSizeZ_NV = value; + else if (tokenStr == "MaxTaskWorkGroupSizeX_NV") + resources->maxTaskWorkGroupSizeX_NV = value; + else if (tokenStr == "MaxTaskWorkGroupSizeY_NV") + resources->maxTaskWorkGroupSizeY_NV = value; + else if (tokenStr == "MaxTaskWorkGroupSizeZ_NV") + resources->maxTaskWorkGroupSizeZ_NV = value; + else if (tokenStr == "MaxMeshViewCountNV") + resources->maxMeshViewCountNV = value; + else if (tokenStr == "nonInductiveForLoops") + resources->limits.nonInductiveForLoops = (value != 0); + else if (tokenStr == "whileLoops") + resources->limits.whileLoops = (value != 0); + else if (tokenStr == "doWhileLoops") + resources->limits.doWhileLoops = (value != 0); + else if (tokenStr == "generalUniformIndexing") + resources->limits.generalUniformIndexing = (value != 0); + else if (tokenStr == "generalAttributeMatrixVectorIndexing") + resources->limits.generalAttributeMatrixVectorIndexing = (value != 0); + else if (tokenStr == "generalVaryingIndexing") + resources->limits.generalVaryingIndexing = (value != 0); + else if (tokenStr == "generalSamplerIndexing") + resources->limits.generalSamplerIndexing = (value != 0); + else if (tokenStr == "generalVariableIndexing") + resources->limits.generalVariableIndexing = (value != 0); + else if (tokenStr == "generalConstantMatrixVectorIndexing") + resources->limits.generalConstantMatrixVectorIndexing = (value != 0); + else + printf("Warning: unrecognized limit (%s) in configuration file.\n", tokenStr.c_str()); + + } +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/StandAlone/ResourceLimits.h b/armorcore/tools/to_spirv/glslang/StandAlone/ResourceLimits.h new file mode 100644 index 000000000..736248eb3 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/StandAlone/ResourceLimits.h @@ -0,0 +1,57 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef _STAND_ALONE_RESOURCE_LIMITS_INCLUDED_ +#define _STAND_ALONE_RESOURCE_LIMITS_INCLUDED_ + +#include + +#include "../glslang/Include/ResourceLimits.h" + +namespace glslang { + +// These are the default resources for TBuiltInResources, used for both +// - parsing this string for the case where the user didn't supply one, +// - dumping out a template for user construction of a config file. +extern const TBuiltInResource DefaultTBuiltInResource; + +// Returns the DefaultTBuiltInResource as a human-readable string. +std::string GetDefaultTBuiltInResourceString(); + +// Decodes the resource limits from |config| to |resources|. +void DecodeResourceLimits(TBuiltInResource* resources, char* config); + +} // end namespace glslang + +#endif // _STAND_ALONE_RESOURCE_LIMITS_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/StandAlone/Worklist.h b/armorcore/tools/to_spirv/glslang/StandAlone/Worklist.h new file mode 100644 index 000000000..91b6f516b --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/StandAlone/Worklist.h @@ -0,0 +1,95 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef WORKLIST_H_INCLUDED +#define WORKLIST_H_INCLUDED + +#include "../glslang/OSDependent/osinclude.h" +#include +#include +#include + +namespace glslang { + + class TWorkItem { + public: + TWorkItem() { } + explicit TWorkItem(const std::string& s) : + name(s) { } + std::string name; + std::string results; + std::string resultsIndex; + }; + + class TWorklist { + public: + TWorklist() { } + virtual ~TWorklist() { } + + void add(TWorkItem* item) + { + std::lock_guard guard(mutex); + worklist.push_back(item); + } + + bool remove(TWorkItem*& item) + { + std::lock_guard guard(mutex); + + if (worklist.empty()) + return false; + item = worklist.front(); + worklist.pop_front(); + + return true; + } + + int size() + { + return (int)worklist.size(); + } + + bool empty() + { + return worklist.empty(); + } + + protected: + std::mutex mutex; + std::list worklist; + }; + +} // end namespace glslang + +#endif // WORKLIST_H_INCLUDED diff --git a/armorcore/tools/to_spirv/glslang/glslang/GenericCodeGen/CodeGen.cpp b/armorcore/tools/to_spirv/glslang/glslang/GenericCodeGen/CodeGen.cpp new file mode 100644 index 000000000..b3c7226df --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/GenericCodeGen/CodeGen.cpp @@ -0,0 +1,76 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/Common.h" +#include "../Include/ShHandle.h" +#include "../MachineIndependent/Versions.h" + +// +// Here is where real machine specific high-level data would be defined. +// +class TGenericCompiler : public TCompiler { +public: + TGenericCompiler(EShLanguage l, int dOptions) : TCompiler(l, infoSink), debugOptions(dOptions) { } + virtual bool compile(TIntermNode* root, int version = 0, EProfile profile = ENoProfile); + TInfoSink infoSink; + int debugOptions; +}; + +// +// This function must be provided to create the actual +// compile object used by higher level code. It returns +// a subclass of TCompiler. +// +TCompiler* ConstructCompiler(EShLanguage language, int debugOptions) +{ + return new TGenericCompiler(language, debugOptions); +} + +// +// Delete the compiler made by ConstructCompiler +// +void DeleteCompiler(TCompiler* compiler) +{ + delete compiler; +} + +// +// Generate code from the given parse tree +// +bool TGenericCompiler::compile(TIntermNode* /*root*/, int /*version*/, EProfile /*profile*/) +{ + haveValidObjectCode = true; + + return haveValidObjectCode; +} diff --git a/armorcore/tools/to_spirv/glslang/glslang/GenericCodeGen/Link.cpp b/armorcore/tools/to_spirv/glslang/glslang/GenericCodeGen/Link.cpp new file mode 100644 index 000000000..c38db0f69 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/GenericCodeGen/Link.cpp @@ -0,0 +1,91 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// The top level algorithms for linking multiple +// shaders together. +// +#include "../Include/Common.h" +#include "../Include/ShHandle.h" + +// +// Actual link object, derived from the shader handle base classes. +// +class TGenericLinker : public TLinker { +public: + TGenericLinker(EShExecutable e, int dOptions) : TLinker(e, infoSink), debugOptions(dOptions) { } + bool link(TCompilerList&, TUniformMap*) { return true; } + void getAttributeBindings(ShBindingTable const **) const { } + TInfoSink infoSink; + int debugOptions; +}; + +// +// The internal view of a uniform/float object exchanged with the driver. +// +class TUniformLinkedMap : public TUniformMap { +public: + TUniformLinkedMap() { } + virtual int getLocation(const char*) { return 0; } +}; + +TShHandleBase* ConstructLinker(EShExecutable executable, int debugOptions) +{ + return new TGenericLinker(executable, debugOptions); +} + +void DeleteLinker(TShHandleBase* linker) +{ + delete linker; +} + +TUniformMap* ConstructUniformMap() +{ + return new TUniformLinkedMap(); +} + +void DeleteUniformMap(TUniformMap* map) +{ + delete map; +} + +TShHandleBase* ConstructBindings() +{ + return 0; +} + +void DeleteBindingList(TShHandleBase* bindingList) +{ + delete bindingList; +} diff --git a/armorcore/tools/to_spirv/glslang/glslang/Include/BaseTypes.h b/armorcore/tools/to_spirv/glslang/glslang/Include/BaseTypes.h new file mode 100644 index 000000000..b69eaebf2 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Include/BaseTypes.h @@ -0,0 +1,571 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _BASICTYPES_INCLUDED_ +#define _BASICTYPES_INCLUDED_ + +namespace glslang { + +// +// Basic type. Arrays, vectors, sampler details, etc., are orthogonal to this. +// +enum TBasicType { + EbtVoid, + EbtFloat, + EbtDouble, + EbtFloat16, + EbtInt8, + EbtUint8, + EbtInt16, + EbtUint16, + EbtInt, + EbtUint, + EbtInt64, + EbtUint64, + EbtBool, + EbtAtomicUint, + EbtSampler, + EbtStruct, + EbtBlock, + EbtAccStruct, + EbtReference, + EbtRayQuery, + + // HLSL types that live only temporarily. + EbtString, + + EbtNumTypes +}; + +// +// Storage qualifiers. Should align with different kinds of storage or +// resource or GLSL storage qualifier. Expansion is deprecated. +// +// N.B.: You probably DON'T want to add anything here, but rather just add it +// to the built-in variables. See the comment above TBuiltInVariable. +// +// A new built-in variable will normally be an existing qualifier, like 'in', 'out', etc. +// DO NOT follow the design pattern of, say EvqInstanceId, etc. +// +enum TStorageQualifier { + EvqTemporary, // For temporaries (within a function), read/write + EvqGlobal, // For globals read/write + EvqConst, // User-defined constant values, will be semantically constant and constant folded + EvqVaryingIn, // pipeline input, read only, also supercategory for all built-ins not included in this enum (see TBuiltInVariable) + EvqVaryingOut, // pipeline output, read/write, also supercategory for all built-ins not included in this enum (see TBuiltInVariable) + EvqUniform, // read only, shared with app + EvqBuffer, // read/write, shared with app + EvqShared, // compute shader's read/write 'shared' qualifier + + EvqPayload, + EvqPayloadIn, + EvqHitAttr, + EvqCallableData, + EvqCallableDataIn, + + // parameters + EvqIn, // also, for 'in' in the grammar before we know if it's a pipeline input or an 'in' parameter + EvqOut, // also, for 'out' in the grammar before we know if it's a pipeline output or an 'out' parameter + EvqInOut, + EvqConstReadOnly, // input; also other read-only types having neither a constant value nor constant-value semantics + + // built-ins read by vertex shader + EvqVertexId, + EvqInstanceId, + + // built-ins written by vertex shader + EvqPosition, + EvqPointSize, + EvqClipVertex, + + // built-ins read by fragment shader + EvqFace, + EvqFragCoord, + EvqPointCoord, + + // built-ins written by fragment shader + EvqFragColor, + EvqFragDepth, + + // end of list + EvqLast +}; + +// +// Subcategories of the TStorageQualifier, simply to give a direct mapping +// between built-in variable names and an numerical value (the enum). +// +// For backward compatibility, there is some redundancy between the +// TStorageQualifier and these. Existing members should both be maintained accurately. +// However, any new built-in variable (and any existing non-redundant one) +// must follow the pattern that the specific built-in is here, and only its +// general qualifier is in TStorageQualifier. +// +// Something like gl_Position, which is sometimes 'in' and sometimes 'out' +// shows up as two different built-in variables in a single stage, but +// only has a single enum in TBuiltInVariable, so both the +// TStorageQualifier and the TBuitinVariable are needed to distinguish +// between them. +// +enum TBuiltInVariable { + EbvNone, + EbvNumWorkGroups, + EbvWorkGroupSize, + EbvWorkGroupId, + EbvLocalInvocationId, + EbvGlobalInvocationId, + EbvLocalInvocationIndex, + EbvNumSubgroups, + EbvSubgroupID, + EbvSubGroupSize, + EbvSubGroupInvocation, + EbvSubGroupEqMask, + EbvSubGroupGeMask, + EbvSubGroupGtMask, + EbvSubGroupLeMask, + EbvSubGroupLtMask, + EbvSubgroupSize2, + EbvSubgroupInvocation2, + EbvSubgroupEqMask2, + EbvSubgroupGeMask2, + EbvSubgroupGtMask2, + EbvSubgroupLeMask2, + EbvSubgroupLtMask2, + EbvVertexId, + EbvInstanceId, + EbvVertexIndex, + EbvInstanceIndex, + EbvBaseVertex, + EbvBaseInstance, + EbvDrawId, + EbvPosition, + EbvPointSize, + EbvClipVertex, + EbvClipDistance, + EbvCullDistance, + EbvNormal, + EbvVertex, + EbvMultiTexCoord0, + EbvMultiTexCoord1, + EbvMultiTexCoord2, + EbvMultiTexCoord3, + EbvMultiTexCoord4, + EbvMultiTexCoord5, + EbvMultiTexCoord6, + EbvMultiTexCoord7, + EbvFrontColor, + EbvBackColor, + EbvFrontSecondaryColor, + EbvBackSecondaryColor, + EbvTexCoord, + EbvFogFragCoord, + EbvInvocationId, + EbvPrimitiveId, + EbvLayer, + EbvViewportIndex, + EbvPatchVertices, + EbvTessLevelOuter, + EbvTessLevelInner, + EbvBoundingBox, + EbvTessCoord, + EbvColor, + EbvSecondaryColor, + EbvFace, + EbvFragCoord, + EbvPointCoord, + EbvFragColor, + EbvFragData, + EbvFragDepth, + EbvFragStencilRef, + EbvSampleId, + EbvSamplePosition, + EbvSampleMask, + EbvHelperInvocation, + + EbvBaryCoordNoPersp, + EbvBaryCoordNoPerspCentroid, + EbvBaryCoordNoPerspSample, + EbvBaryCoordSmooth, + EbvBaryCoordSmoothCentroid, + EbvBaryCoordSmoothSample, + EbvBaryCoordPullModel, + + EbvViewIndex, + EbvDeviceIndex, + + EbvFragSizeEXT, + EbvFragInvocationCountEXT, + + EbvSecondaryFragDataEXT, + EbvSecondaryFragColorEXT, + + EbvViewportMaskNV, + EbvSecondaryPositionNV, + EbvSecondaryViewportMaskNV, + EbvPositionPerViewNV, + EbvViewportMaskPerViewNV, + EbvFragFullyCoveredNV, + EbvFragmentSizeNV, + EbvInvocationsPerPixelNV, + // ray tracing + EbvLaunchId, + EbvLaunchSize, + EbvInstanceCustomIndex, + EbvGeometryIndex, + EbvWorldRayOrigin, + EbvWorldRayDirection, + EbvObjectRayOrigin, + EbvObjectRayDirection, + EbvRayTmin, + EbvRayTmax, + EbvHitT, + EbvHitKind, + EbvObjectToWorld, + EbvObjectToWorld3x4, + EbvWorldToObject, + EbvWorldToObject3x4, + EbvIncomingRayFlags, + // barycentrics + EbvBaryCoordNV, + EbvBaryCoordNoPerspNV, + // mesh shaders + EbvTaskCountNV, + EbvPrimitiveCountNV, + EbvPrimitiveIndicesNV, + EbvClipDistancePerViewNV, + EbvCullDistancePerViewNV, + EbvLayerPerViewNV, + EbvMeshViewCountNV, + EbvMeshViewIndicesNV, + + // sm builtins + EbvWarpsPerSM, + EbvSMCount, + EbvWarpID, + EbvSMID, + + // HLSL built-ins that live only temporarily, until they get remapped + // to one of the above. + EbvFragDepthGreater, + EbvFragDepthLesser, + EbvGsOutputStream, + EbvOutputPatch, + EbvInputPatch, + + // structbuffer types + EbvAppendConsume, // no need to differentiate append and consume + EbvRWStructuredBuffer, + EbvStructuredBuffer, + EbvByteAddressBuffer, + EbvRWByteAddressBuffer, + + EbvLast +}; + +// In this enum, order matters; users can assume higher precision is a bigger value +// and EpqNone is 0. +enum TPrecisionQualifier { + EpqNone = 0, + EpqLow, + EpqMedium, + EpqHigh +}; + +#ifdef GLSLANG_WEB +__inline const char* GetStorageQualifierString(TStorageQualifier q) { return ""; } +__inline const char* GetPrecisionQualifierString(TPrecisionQualifier p) { return ""; } +#else +// These will show up in error messages +__inline const char* GetStorageQualifierString(TStorageQualifier q) +{ + switch (q) { + case EvqTemporary: return "temp"; break; + case EvqGlobal: return "global"; break; + case EvqConst: return "const"; break; + case EvqConstReadOnly: return "const (read only)"; break; + case EvqVaryingIn: return "in"; break; + case EvqVaryingOut: return "out"; break; + case EvqUniform: return "uniform"; break; + case EvqBuffer: return "buffer"; break; + case EvqShared: return "shared"; break; + case EvqIn: return "in"; break; + case EvqOut: return "out"; break; + case EvqInOut: return "inout"; break; + case EvqVertexId: return "gl_VertexId"; break; + case EvqInstanceId: return "gl_InstanceId"; break; + case EvqPosition: return "gl_Position"; break; + case EvqPointSize: return "gl_PointSize"; break; + case EvqClipVertex: return "gl_ClipVertex"; break; + case EvqFace: return "gl_FrontFacing"; break; + case EvqFragCoord: return "gl_FragCoord"; break; + case EvqPointCoord: return "gl_PointCoord"; break; + case EvqFragColor: return "fragColor"; break; + case EvqFragDepth: return "gl_FragDepth"; break; + case EvqPayload: return "rayPayloadNV"; break; + case EvqPayloadIn: return "rayPayloadInNV"; break; + case EvqHitAttr: return "hitAttributeNV"; break; + case EvqCallableData: return "callableDataNV"; break; + case EvqCallableDataIn: return "callableDataInNV"; break; + default: return "unknown qualifier"; + } +} + +__inline const char* GetBuiltInVariableString(TBuiltInVariable v) +{ + switch (v) { + case EbvNone: return ""; + case EbvNumWorkGroups: return "NumWorkGroups"; + case EbvWorkGroupSize: return "WorkGroupSize"; + case EbvWorkGroupId: return "WorkGroupID"; + case EbvLocalInvocationId: return "LocalInvocationID"; + case EbvGlobalInvocationId: return "GlobalInvocationID"; + case EbvLocalInvocationIndex: return "LocalInvocationIndex"; + case EbvNumSubgroups: return "NumSubgroups"; + case EbvSubgroupID: return "SubgroupID"; + case EbvSubGroupSize: return "SubGroupSize"; + case EbvSubGroupInvocation: return "SubGroupInvocation"; + case EbvSubGroupEqMask: return "SubGroupEqMask"; + case EbvSubGroupGeMask: return "SubGroupGeMask"; + case EbvSubGroupGtMask: return "SubGroupGtMask"; + case EbvSubGroupLeMask: return "SubGroupLeMask"; + case EbvSubGroupLtMask: return "SubGroupLtMask"; + case EbvSubgroupSize2: return "SubgroupSize"; + case EbvSubgroupInvocation2: return "SubgroupInvocationID"; + case EbvSubgroupEqMask2: return "SubgroupEqMask"; + case EbvSubgroupGeMask2: return "SubgroupGeMask"; + case EbvSubgroupGtMask2: return "SubgroupGtMask"; + case EbvSubgroupLeMask2: return "SubgroupLeMask"; + case EbvSubgroupLtMask2: return "SubgroupLtMask"; + case EbvVertexId: return "VertexId"; + case EbvInstanceId: return "InstanceId"; + case EbvVertexIndex: return "VertexIndex"; + case EbvInstanceIndex: return "InstanceIndex"; + case EbvBaseVertex: return "BaseVertex"; + case EbvBaseInstance: return "BaseInstance"; + case EbvDrawId: return "DrawId"; + case EbvPosition: return "Position"; + case EbvPointSize: return "PointSize"; + case EbvClipVertex: return "ClipVertex"; + case EbvClipDistance: return "ClipDistance"; + case EbvCullDistance: return "CullDistance"; + case EbvNormal: return "Normal"; + case EbvVertex: return "Vertex"; + case EbvMultiTexCoord0: return "MultiTexCoord0"; + case EbvMultiTexCoord1: return "MultiTexCoord1"; + case EbvMultiTexCoord2: return "MultiTexCoord2"; + case EbvMultiTexCoord3: return "MultiTexCoord3"; + case EbvMultiTexCoord4: return "MultiTexCoord4"; + case EbvMultiTexCoord5: return "MultiTexCoord5"; + case EbvMultiTexCoord6: return "MultiTexCoord6"; + case EbvMultiTexCoord7: return "MultiTexCoord7"; + case EbvFrontColor: return "FrontColor"; + case EbvBackColor: return "BackColor"; + case EbvFrontSecondaryColor: return "FrontSecondaryColor"; + case EbvBackSecondaryColor: return "BackSecondaryColor"; + case EbvTexCoord: return "TexCoord"; + case EbvFogFragCoord: return "FogFragCoord"; + case EbvInvocationId: return "InvocationID"; + case EbvPrimitiveId: return "PrimitiveID"; + case EbvLayer: return "Layer"; + case EbvViewportIndex: return "ViewportIndex"; + case EbvPatchVertices: return "PatchVertices"; + case EbvTessLevelOuter: return "TessLevelOuter"; + case EbvTessLevelInner: return "TessLevelInner"; + case EbvBoundingBox: return "BoundingBox"; + case EbvTessCoord: return "TessCoord"; + case EbvColor: return "Color"; + case EbvSecondaryColor: return "SecondaryColor"; + case EbvFace: return "Face"; + case EbvFragCoord: return "FragCoord"; + case EbvPointCoord: return "PointCoord"; + case EbvFragColor: return "FragColor"; + case EbvFragData: return "FragData"; + case EbvFragDepth: return "FragDepth"; + case EbvFragStencilRef: return "FragStencilRef"; + case EbvSampleId: return "SampleId"; + case EbvSamplePosition: return "SamplePosition"; + case EbvSampleMask: return "SampleMaskIn"; + case EbvHelperInvocation: return "HelperInvocation"; + + case EbvBaryCoordNoPersp: return "BaryCoordNoPersp"; + case EbvBaryCoordNoPerspCentroid: return "BaryCoordNoPerspCentroid"; + case EbvBaryCoordNoPerspSample: return "BaryCoordNoPerspSample"; + case EbvBaryCoordSmooth: return "BaryCoordSmooth"; + case EbvBaryCoordSmoothCentroid: return "BaryCoordSmoothCentroid"; + case EbvBaryCoordSmoothSample: return "BaryCoordSmoothSample"; + case EbvBaryCoordPullModel: return "BaryCoordPullModel"; + + case EbvViewIndex: return "ViewIndex"; + case EbvDeviceIndex: return "DeviceIndex"; + + case EbvFragSizeEXT: return "FragSizeEXT"; + case EbvFragInvocationCountEXT: return "FragInvocationCountEXT"; + + case EbvSecondaryFragDataEXT: return "SecondaryFragDataEXT"; + case EbvSecondaryFragColorEXT: return "SecondaryFragColorEXT"; + + case EbvViewportMaskNV: return "ViewportMaskNV"; + case EbvSecondaryPositionNV: return "SecondaryPositionNV"; + case EbvSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case EbvPositionPerViewNV: return "PositionPerViewNV"; + case EbvViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case EbvFragFullyCoveredNV: return "FragFullyCoveredNV"; + case EbvFragmentSizeNV: return "FragmentSizeNV"; + case EbvInvocationsPerPixelNV: return "InvocationsPerPixelNV"; + case EbvLaunchId: return "LaunchIdNV"; + case EbvLaunchSize: return "LaunchSizeNV"; + case EbvInstanceCustomIndex: return "InstanceCustomIndexNV"; + case EbvGeometryIndex: return "GeometryIndexEXT"; + case EbvWorldRayOrigin: return "WorldRayOriginNV"; + case EbvWorldRayDirection: return "WorldRayDirectionNV"; + case EbvObjectRayOrigin: return "ObjectRayOriginNV"; + case EbvObjectRayDirection: return "ObjectRayDirectionNV"; + case EbvRayTmin: return "ObjectRayTminNV"; + case EbvRayTmax: return "ObjectRayTmaxNV"; + case EbvHitT: return "HitTNV"; + case EbvHitKind: return "HitKindNV"; + case EbvIncomingRayFlags: return "IncomingRayFlagsNV"; + case EbvObjectToWorld: return "ObjectToWorldNV"; + case EbvWorldToObject: return "WorldToObjectNV"; + + case EbvBaryCoordNV: return "BaryCoordNV"; + case EbvBaryCoordNoPerspNV: return "BaryCoordNoPerspNV"; + + case EbvTaskCountNV: return "TaskCountNV"; + case EbvPrimitiveCountNV: return "PrimitiveCountNV"; + case EbvPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case EbvClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case EbvCullDistancePerViewNV: return "CullDistancePerViewNV"; + case EbvLayerPerViewNV: return "LayerPerViewNV"; + case EbvMeshViewCountNV: return "MeshViewCountNV"; + case EbvMeshViewIndicesNV: return "MeshViewIndicesNV"; + + case EbvWarpsPerSM: return "WarpsPerSMNV"; + case EbvSMCount: return "SMCountNV"; + case EbvWarpID: return "WarpIDNV"; + case EbvSMID: return "SMIDNV"; + + default: return "unknown built-in variable"; + } +} + +__inline const char* GetPrecisionQualifierString(TPrecisionQualifier p) +{ + switch (p) { + case EpqNone: return ""; break; + case EpqLow: return "lowp"; break; + case EpqMedium: return "mediump"; break; + case EpqHigh: return "highp"; break; + default: return "unknown precision qualifier"; + } +} +#endif + +__inline bool isTypeSignedInt(TBasicType type) +{ + switch (type) { + case EbtInt8: + case EbtInt16: + case EbtInt: + case EbtInt64: + return true; + default: + return false; + } +} + +__inline bool isTypeUnsignedInt(TBasicType type) +{ + switch (type) { + case EbtUint8: + case EbtUint16: + case EbtUint: + case EbtUint64: + return true; + default: + return false; + } +} + +__inline bool isTypeInt(TBasicType type) +{ + return isTypeSignedInt(type) || isTypeUnsignedInt(type); +} + +__inline bool isTypeFloat(TBasicType type) +{ + switch (type) { + case EbtFloat: + case EbtDouble: + case EbtFloat16: + return true; + default: + return false; + } +} + +__inline int getTypeRank(TBasicType type) +{ + int res = -1; + switch(type) { + case EbtInt8: + case EbtUint8: + res = 0; + break; + case EbtInt16: + case EbtUint16: + res = 1; + break; + case EbtInt: + case EbtUint: + res = 2; + break; + case EbtInt64: + case EbtUint64: + res = 3; + break; + default: + assert(false); + break; + } + return res; +} + +} // end namespace glslang + +#endif // _BASICTYPES_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/Include/Common.h b/armorcore/tools/to_spirv/glslang/glslang/Include/Common.h new file mode 100644 index 000000000..733a790cf --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Include/Common.h @@ -0,0 +1,292 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _COMMON_INCLUDED_ +#define _COMMON_INCLUDED_ + + +#if defined(__ANDROID__) || (defined(_MSC_VER) && _MSC_VER < 1700) +#include +namespace std { +template +std::string to_string(const T& val) { + std::ostringstream os; + os << val; + return os.str(); +} +} +#endif + +#if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/) || defined MINGW_HAS_SECURE_API + #include + #ifndef snprintf + #define snprintf sprintf_s + #endif + #define safe_vsprintf(buf,max,format,args) vsnprintf_s((buf), (max), (max), (format), (args)) +#elif defined (solaris) + #define safe_vsprintf(buf,max,format,args) vsnprintf((buf), (max), (format), (args)) + #include + #define UINT_PTR uintptr_t +#else + #define safe_vsprintf(buf,max,format,args) vsnprintf((buf), (max), (format), (args)) + #include + #define UINT_PTR uintptr_t +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1800 + #include + inline long long int strtoll (const char* str, char** endptr, int base) + { + return _strtoi64(str, endptr, base); + } + inline unsigned long long int strtoull (const char* str, char** endptr, int base) + { + return _strtoui64(str, endptr, base); + } + inline long long int atoll (const char* str) + { + return strtoll(str, NULL, 10); + } +#endif + +#if defined(_MSC_VER) +#define strdup _strdup +#endif + +/* windows only pragma */ +#ifdef _MSC_VER + #pragma warning(disable : 4786) // Don't warn about too long identifiers + #pragma warning(disable : 4514) // unused inline method + #pragma warning(disable : 4201) // nameless union +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PoolAlloc.h" + +// +// Put POOL_ALLOCATOR_NEW_DELETE in base classes to make them use this scheme. +// +#define POOL_ALLOCATOR_NEW_DELETE(A) \ + void* operator new(size_t s) { return (A).allocate(s); } \ + void* operator new(size_t, void *_Where) { return (_Where); } \ + void operator delete(void*) { } \ + void operator delete(void *, void *) { } \ + void* operator new[](size_t s) { return (A).allocate(s); } \ + void* operator new[](size_t, void *_Where) { return (_Where); } \ + void operator delete[](void*) { } \ + void operator delete[](void *, void *) { } + +namespace glslang { + + // + // Pool version of string. + // + typedef pool_allocator TStringAllocator; + typedef std::basic_string , TStringAllocator> TString; + +} // end namespace glslang + +// Repackage the std::hash for use by unordered map/set with a TString key. +namespace std { + + template<> struct hash { + std::size_t operator()(const glslang::TString& s) const + { + const unsigned _FNV_offset_basis = 2166136261U; + const unsigned _FNV_prime = 16777619U; + unsigned _Val = _FNV_offset_basis; + size_t _Count = s.size(); + const char* _First = s.c_str(); + for (size_t _Next = 0; _Next < _Count; ++_Next) + { + _Val ^= (unsigned)_First[_Next]; + _Val *= _FNV_prime; + } + + return _Val; + } + }; +} + +namespace glslang { + +inline TString* NewPoolTString(const char* s) +{ + void* memory = GetThreadPoolAllocator().allocate(sizeof(TString)); + return new(memory) TString(s); +} + +template inline T* NewPoolObject(T*) +{ + return new(GetThreadPoolAllocator().allocate(sizeof(T))) T; +} + +template inline T* NewPoolObject(T, int instances) +{ + return new(GetThreadPoolAllocator().allocate(instances * sizeof(T))) T[instances]; +} + +// +// Pool allocator versions of vectors, lists, and maps +// +template class TVector : public std::vector > { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + typedef typename std::vector >::size_type size_type; + TVector() : std::vector >() {} + TVector(const pool_allocator& a) : std::vector >(a) {} + TVector(size_type i) : std::vector >(i) {} + TVector(size_type i, const T& val) : std::vector >(i, val) {} +}; + +template class TList : public std::list > { +}; + +template > +class TMap : public std::map > > { +}; + +template , class PRED = std::equal_to > +class TUnorderedMap : public std::unordered_map > > { +}; + +// +// Persistent string memory. Should only be used for strings that survive +// across compiles/links. +// +typedef std::basic_string TPersistString; + +// +// templatized min and max functions. +// +template T Min(const T a, const T b) { return a < b ? a : b; } +template T Max(const T a, const T b) { return a > b ? a : b; } + +// +// Create a TString object from an integer. +// +#if defined _MSC_VER || defined MINGW_HAS_SECURE_API +inline const TString String(const int i, const int base = 10) +{ + char text[16]; // 32 bit ints are at most 10 digits in base 10 + _itoa_s(i, text, sizeof(text), base); + return text; +} +#else +inline const TString String(const int i, const int /*base*/ = 10) +{ + char text[16]; // 32 bit ints are at most 10 digits in base 10 + + // we assume base 10 for all cases + snprintf(text, sizeof(text), "%d", i); + + return text; +} +#endif + +struct TSourceLoc { + void init() + { + name = nullptr; string = 0; line = 0; column = 0; + } + void init(int stringNum) { init(); string = stringNum; } + // Returns the name if it exists. Otherwise, returns the string number. + std::string getStringNameOrNum(bool quoteStringName = true) const + { + if (name != nullptr) { + TString qstr = quoteStringName ? ("\"" + *name + "\"") : *name; + std::string ret_str(qstr.c_str()); + return ret_str; + } + return std::to_string((long long)string); + } + const char* getFilename() const + { + if (name == nullptr) + return nullptr; + return name->c_str(); + } + const char* getFilenameStr() const { return name == nullptr ? "" : name->c_str(); } + TString* name; // descriptive name for this string, when a textual name is available, otherwise nullptr + int string; + int line; + int column; +}; + +class TPragmaTable : public TMap { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) +}; + +const int MaxTokenLength = 1024; + +template bool IsPow2(T powerOf2) +{ + if (powerOf2 <= 0) + return false; + + return (powerOf2 & (powerOf2 - 1)) == 0; +} + +// Round number up to a multiple of the given powerOf2, which is not +// a power, just a number that must be a power of 2. +template void RoundToPow2(T& number, int powerOf2) +{ + assert(IsPow2(powerOf2)); + number = (number + powerOf2 - 1) & ~(powerOf2 - 1); +} + +template bool IsMultipleOfPow2(T number, int powerOf2) +{ + assert(IsPow2(powerOf2)); + return ! (number & (powerOf2 - 1)); +} + +} // end namespace glslang + +#endif // _COMMON_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/Include/ConstantUnion.h b/armorcore/tools/to_spirv/glslang/glslang/Include/ConstantUnion.h new file mode 100644 index 000000000..c4ffb8577 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Include/ConstantUnion.h @@ -0,0 +1,974 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _CONSTANT_UNION_INCLUDED_ +#define _CONSTANT_UNION_INCLUDED_ + +#include "../Include/Common.h" +#include "../Include/BaseTypes.h" + +namespace glslang { + +class TConstUnion { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TConstUnion() : iConst(0), type(EbtInt) { } + + void setI8Const(signed char i) + { + i8Const = i; + type = EbtInt8; + } + + void setU8Const(unsigned char u) + { + u8Const = u; + type = EbtUint8; + } + + void setI16Const(signed short i) + { + i16Const = i; + type = EbtInt16; + } + + void setU16Const(unsigned short u) + { + u16Const = u; + type = EbtUint16; + } + + void setIConst(int i) + { + iConst = i; + type = EbtInt; + } + + void setUConst(unsigned int u) + { + uConst = u; + type = EbtUint; + } + + void setI64Const(long long i64) + { + i64Const = i64; + type = EbtInt64; + } + + void setU64Const(unsigned long long u64) + { + u64Const = u64; + type = EbtUint64; + } + + void setDConst(double d) + { + dConst = d; + type = EbtDouble; + } + + void setBConst(bool b) + { + bConst = b; + type = EbtBool; + } + + void setSConst(const TString* s) + { + sConst = s; + type = EbtString; + } + + signed char getI8Const() const { return i8Const; } + unsigned char getU8Const() const { return u8Const; } + signed short getI16Const() const { return i16Const; } + unsigned short getU16Const() const { return u16Const; } + int getIConst() const { return iConst; } + unsigned int getUConst() const { return uConst; } + long long getI64Const() const { return i64Const; } + unsigned long long getU64Const() const { return u64Const; } + double getDConst() const { return dConst; } + bool getBConst() const { return bConst; } + const TString* getSConst() const { return sConst; } + + bool operator==(const signed char i) const + { + if (i == i8Const) + return true; + + return false; + } + + bool operator==(const unsigned char u) const + { + if (u == u8Const) + return true; + + return false; + } + + bool operator==(const signed short i) const + { + if (i == i16Const) + return true; + + return false; + } + + bool operator==(const unsigned short u) const + { + if (u == u16Const) + return true; + + return false; + } + + bool operator==(const int i) const + { + if (i == iConst) + return true; + + return false; + } + + bool operator==(const unsigned int u) const + { + if (u == uConst) + return true; + + return false; + } + + bool operator==(const long long i64) const + { + if (i64 == i64Const) + return true; + + return false; + } + + bool operator==(const unsigned long long u64) const + { + if (u64 == u64Const) + return true; + + return false; + } + + bool operator==(const double d) const + { + if (d == dConst) + return true; + + return false; + } + + bool operator==(const bool b) const + { + if (b == bConst) + return true; + + return false; + } + + bool operator==(const TConstUnion& constant) const + { + if (constant.type != type) + return false; + + switch (type) { + case EbtInt: + if (constant.iConst == iConst) + return true; + + break; + case EbtUint: + if (constant.uConst == uConst) + return true; + + break; + case EbtBool: + if (constant.bConst == bConst) + return true; + + break; + case EbtDouble: + if (constant.dConst == dConst) + return true; + + break; + +#ifndef GLSLANG_WEB + case EbtInt16: + if (constant.i16Const == i16Const) + return true; + + break; + case EbtUint16: + if (constant.u16Const == u16Const) + return true; + + break; + case EbtInt8: + if (constant.i8Const == i8Const) + return true; + + break; + case EbtUint8: + if (constant.u8Const == u8Const) + return true; + + break; + case EbtInt64: + if (constant.i64Const == i64Const) + return true; + + break; + case EbtUint64: + if (constant.u64Const == u64Const) + return true; + + break; +#endif + default: + assert(false && "Default missing"); + } + + return false; + } + + bool operator!=(const signed char i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned char u) const + { + return !operator==(u); + } + + bool operator!=(const signed short i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned short u) const + { + return !operator==(u); + } + + bool operator!=(const int i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned int u) const + { + return !operator==(u); + } + + bool operator!=(const long long i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned long long u) const + { + return !operator==(u); + } + + bool operator!=(const float f) const + { + return !operator==(f); + } + + bool operator!=(const bool b) const + { + return !operator==(b); + } + + bool operator!=(const TConstUnion& constant) const + { + return !operator==(constant); + } + + bool operator>(const TConstUnion& constant) const + { + assert(type == constant.type); + switch (type) { + case EbtInt: + if (iConst > constant.iConst) + return true; + + return false; + case EbtUint: + if (uConst > constant.uConst) + return true; + + return false; + case EbtDouble: + if (dConst > constant.dConst) + return true; + + return false; +#ifndef GLSLANG_WEB + case EbtInt8: + if (i8Const > constant.i8Const) + return true; + + return false; + case EbtUint8: + if (u8Const > constant.u8Const) + return true; + + return false; + case EbtInt16: + if (i16Const > constant.i16Const) + return true; + + return false; + case EbtUint16: + if (u16Const > constant.u16Const) + return true; + + return false; + case EbtInt64: + if (i64Const > constant.i64Const) + return true; + + return false; + case EbtUint64: + if (u64Const > constant.u64Const) + return true; + + return false; +#endif + default: + assert(false && "Default missing"); + return false; + } + } + + bool operator<(const TConstUnion& constant) const + { + assert(type == constant.type); + switch (type) { +#ifndef GLSLANG_WEB + case EbtInt8: + if (i8Const < constant.i8Const) + return true; + + return false; + case EbtUint8: + if (u8Const < constant.u8Const) + return true; + + return false; + case EbtInt16: + if (i16Const < constant.i16Const) + return true; + + return false; + case EbtUint16: + if (u16Const < constant.u16Const) + return true; + return false; + case EbtInt64: + if (i64Const < constant.i64Const) + return true; + + return false; + case EbtUint64: + if (u64Const < constant.u64Const) + return true; + + return false; +#endif + case EbtDouble: + if (dConst < constant.dConst) + return true; + + return false; + case EbtInt: + if (iConst < constant.iConst) + return true; + + return false; + case EbtUint: + if (uConst < constant.uConst) + return true; + + return false; + default: + assert(false && "Default missing"); + return false; + } + } + + TConstUnion operator+(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst + constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst + constant.uConst); break; + case EbtDouble: returnValue.setDConst(dConst + constant.dConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const + constant.i8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const + constant.i16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const + constant.i64Const); break; + case EbtUint8: returnValue.setU8Const(u8Const + constant.u8Const); break; + case EbtUint16: returnValue.setU16Const(u16Const + constant.u16Const); break; + case EbtUint64: returnValue.setU64Const(u64Const + constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator-(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst - constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst - constant.uConst); break; + case EbtDouble: returnValue.setDConst(dConst - constant.dConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const - constant.i8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const - constant.i16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const - constant.i64Const); break; + case EbtUint8: returnValue.setU8Const(u8Const - constant.u8Const); break; + case EbtUint16: returnValue.setU16Const(u16Const - constant.u16Const); break; + case EbtUint64: returnValue.setU64Const(u64Const - constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator*(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst * constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst * constant.uConst); break; + case EbtDouble: returnValue.setDConst(dConst * constant.dConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const * constant.i8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const * constant.i16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const * constant.i64Const); break; + case EbtUint8: returnValue.setU8Const(u8Const * constant.u8Const); break; + case EbtUint16: returnValue.setU16Const(u16Const * constant.u16Const); break; + case EbtUint64: returnValue.setU64Const(u64Const * constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator%(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst % constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst % constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const % constant.i8Const); break; + case EbtInt16: returnValue.setI8Const(i8Const % constant.i16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const % constant.i64Const); break; + case EbtUint8: returnValue.setU8Const(u8Const % constant.u8Const); break; + case EbtUint16: returnValue.setU16Const(u16Const % constant.u16Const); break; + case EbtUint64: returnValue.setU64Const(u64Const % constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator>>(const TConstUnion& constant) const + { + TConstUnion returnValue; + switch (type) { +#ifndef GLSLANG_WEB + case EbtInt8: + switch (constant.type) { + case EbtInt8: returnValue.setI8Const(i8Const >> constant.i8Const); break; + case EbtUint8: returnValue.setI8Const(i8Const >> constant.u8Const); break; + case EbtInt16: returnValue.setI8Const(i8Const >> constant.i16Const); break; + case EbtUint16: returnValue.setI8Const(i8Const >> constant.u16Const); break; + case EbtInt: returnValue.setI8Const(i8Const >> constant.iConst); break; + case EbtUint: returnValue.setI8Const(i8Const >> constant.uConst); break; + case EbtInt64: returnValue.setI8Const(i8Const >> constant.i64Const); break; + case EbtUint64: returnValue.setI8Const(i8Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint8: + switch (constant.type) { + case EbtInt8: returnValue.setU8Const(u8Const >> constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const >> constant.u8Const); break; + case EbtInt16: returnValue.setU8Const(u8Const >> constant.i16Const); break; + case EbtUint16: returnValue.setU8Const(u8Const >> constant.u16Const); break; + case EbtInt: returnValue.setU8Const(u8Const >> constant.iConst); break; + case EbtUint: returnValue.setU8Const(u8Const >> constant.uConst); break; + case EbtInt64: returnValue.setU8Const(u8Const >> constant.i64Const); break; + case EbtUint64: returnValue.setU8Const(u8Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt16: + switch (constant.type) { + case EbtInt8: returnValue.setI16Const(i16Const >> constant.i8Const); break; + case EbtUint8: returnValue.setI16Const(i16Const >> constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const >> constant.i16Const); break; + case EbtUint16: returnValue.setI16Const(i16Const >> constant.u16Const); break; + case EbtInt: returnValue.setI16Const(i16Const >> constant.iConst); break; + case EbtUint: returnValue.setI16Const(i16Const >> constant.uConst); break; + case EbtInt64: returnValue.setI16Const(i16Const >> constant.i64Const); break; + case EbtUint64: returnValue.setI16Const(i16Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint16: + switch (constant.type) { + case EbtInt8: returnValue.setU16Const(u16Const >> constant.i8Const); break; + case EbtUint8: returnValue.setU16Const(u16Const >> constant.u8Const); break; + case EbtInt16: returnValue.setU16Const(u16Const >> constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const >> constant.u16Const); break; + case EbtInt: returnValue.setU16Const(u16Const >> constant.iConst); break; + case EbtUint: returnValue.setU16Const(u16Const >> constant.uConst); break; + case EbtInt64: returnValue.setU16Const(u16Const >> constant.i64Const); break; + case EbtUint64: returnValue.setU16Const(u16Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; +#endif + case EbtInt: + switch (constant.type) { + case EbtInt: returnValue.setIConst(iConst >> constant.iConst); break; + case EbtUint: returnValue.setIConst(iConst >> constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setIConst(iConst >> constant.i8Const); break; + case EbtUint8: returnValue.setIConst(iConst >> constant.u8Const); break; + case EbtInt16: returnValue.setIConst(iConst >> constant.i16Const); break; + case EbtUint16: returnValue.setIConst(iConst >> constant.u16Const); break; + case EbtInt64: returnValue.setIConst(iConst >> constant.i64Const); break; + case EbtUint64: returnValue.setIConst(iConst >> constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + break; + case EbtUint: + switch (constant.type) { + case EbtInt: returnValue.setUConst(uConst >> constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst >> constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setUConst(uConst >> constant.i8Const); break; + case EbtUint8: returnValue.setUConst(uConst >> constant.u8Const); break; + case EbtInt16: returnValue.setUConst(uConst >> constant.i16Const); break; + case EbtUint16: returnValue.setUConst(uConst >> constant.u16Const); break; + case EbtInt64: returnValue.setUConst(uConst >> constant.i64Const); break; + case EbtUint64: returnValue.setUConst(uConst >> constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + break; +#ifndef GLSLANG_WEB + case EbtInt64: + switch (constant.type) { + case EbtInt8: returnValue.setI64Const(i64Const >> constant.i8Const); break; + case EbtUint8: returnValue.setI64Const(i64Const >> constant.u8Const); break; + case EbtInt16: returnValue.setI64Const(i64Const >> constant.i16Const); break; + case EbtUint16: returnValue.setI64Const(i64Const >> constant.u16Const); break; + case EbtInt: returnValue.setI64Const(i64Const >> constant.iConst); break; + case EbtUint: returnValue.setI64Const(i64Const >> constant.uConst); break; + case EbtInt64: returnValue.setI64Const(i64Const >> constant.i64Const); break; + case EbtUint64: returnValue.setI64Const(i64Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint64: + switch (constant.type) { + case EbtInt8: returnValue.setU64Const(u64Const >> constant.i8Const); break; + case EbtUint8: returnValue.setU64Const(u64Const >> constant.u8Const); break; + case EbtInt16: returnValue.setU64Const(u64Const >> constant.i16Const); break; + case EbtUint16: returnValue.setU64Const(u64Const >> constant.u16Const); break; + case EbtInt: returnValue.setU64Const(u64Const >> constant.iConst); break; + case EbtUint: returnValue.setU64Const(u64Const >> constant.uConst); break; + case EbtInt64: returnValue.setU64Const(u64Const >> constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator<<(const TConstUnion& constant) const + { + TConstUnion returnValue; + switch (type) { +#ifndef GLSLANG_WEB + case EbtInt8: + switch (constant.type) { + case EbtInt8: returnValue.setI8Const(i8Const << constant.i8Const); break; + case EbtUint8: returnValue.setI8Const(i8Const << constant.u8Const); break; + case EbtInt16: returnValue.setI8Const(i8Const << constant.i16Const); break; + case EbtUint16: returnValue.setI8Const(i8Const << constant.u16Const); break; + case EbtInt: returnValue.setI8Const(i8Const << constant.iConst); break; + case EbtUint: returnValue.setI8Const(i8Const << constant.uConst); break; + case EbtInt64: returnValue.setI8Const(i8Const << constant.i64Const); break; + case EbtUint64: returnValue.setI8Const(i8Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint8: + switch (constant.type) { + case EbtInt8: returnValue.setU8Const(u8Const << constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const << constant.u8Const); break; + case EbtInt16: returnValue.setU8Const(u8Const << constant.i16Const); break; + case EbtUint16: returnValue.setU8Const(u8Const << constant.u16Const); break; + case EbtInt: returnValue.setU8Const(u8Const << constant.iConst); break; + case EbtUint: returnValue.setU8Const(u8Const << constant.uConst); break; + case EbtInt64: returnValue.setU8Const(u8Const << constant.i64Const); break; + case EbtUint64: returnValue.setU8Const(u8Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt16: + switch (constant.type) { + case EbtInt8: returnValue.setI16Const(i16Const << constant.i8Const); break; + case EbtUint8: returnValue.setI16Const(i16Const << constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const << constant.i16Const); break; + case EbtUint16: returnValue.setI16Const(i16Const << constant.u16Const); break; + case EbtInt: returnValue.setI16Const(i16Const << constant.iConst); break; + case EbtUint: returnValue.setI16Const(i16Const << constant.uConst); break; + case EbtInt64: returnValue.setI16Const(i16Const << constant.i64Const); break; + case EbtUint64: returnValue.setI16Const(i16Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint16: + switch (constant.type) { + case EbtInt8: returnValue.setU16Const(u16Const << constant.i8Const); break; + case EbtUint8: returnValue.setU16Const(u16Const << constant.u8Const); break; + case EbtInt16: returnValue.setU16Const(u16Const << constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const << constant.u16Const); break; + case EbtInt: returnValue.setU16Const(u16Const << constant.iConst); break; + case EbtUint: returnValue.setU16Const(u16Const << constant.uConst); break; + case EbtInt64: returnValue.setU16Const(u16Const << constant.i64Const); break; + case EbtUint64: returnValue.setU16Const(u16Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt64: + switch (constant.type) { + case EbtInt8: returnValue.setI64Const(i64Const << constant.i8Const); break; + case EbtUint8: returnValue.setI64Const(i64Const << constant.u8Const); break; + case EbtInt16: returnValue.setI64Const(i64Const << constant.i16Const); break; + case EbtUint16: returnValue.setI64Const(i64Const << constant.u16Const); break; + case EbtInt: returnValue.setI64Const(i64Const << constant.iConst); break; + case EbtUint: returnValue.setI64Const(i64Const << constant.uConst); break; + case EbtInt64: returnValue.setI64Const(i64Const << constant.i64Const); break; + case EbtUint64: returnValue.setI64Const(i64Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint64: + switch (constant.type) { + case EbtInt8: returnValue.setU64Const(u64Const << constant.i8Const); break; + case EbtUint8: returnValue.setU64Const(u64Const << constant.u8Const); break; + case EbtInt16: returnValue.setU64Const(u64Const << constant.i16Const); break; + case EbtUint16: returnValue.setU64Const(u64Const << constant.u16Const); break; + case EbtInt: returnValue.setU64Const(u64Const << constant.iConst); break; + case EbtUint: returnValue.setU64Const(u64Const << constant.uConst); break; + case EbtInt64: returnValue.setU64Const(u64Const << constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; +#endif + case EbtInt: + switch (constant.type) { + case EbtInt: returnValue.setIConst(iConst << constant.iConst); break; + case EbtUint: returnValue.setIConst(iConst << constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setIConst(iConst << constant.i8Const); break; + case EbtUint8: returnValue.setIConst(iConst << constant.u8Const); break; + case EbtInt16: returnValue.setIConst(iConst << constant.i16Const); break; + case EbtUint16: returnValue.setIConst(iConst << constant.u16Const); break; + case EbtInt64: returnValue.setIConst(iConst << constant.i64Const); break; + case EbtUint64: returnValue.setIConst(iConst << constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + break; + case EbtUint: + switch (constant.type) { + case EbtInt: returnValue.setUConst(uConst << constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst << constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setUConst(uConst << constant.i8Const); break; + case EbtUint8: returnValue.setUConst(uConst << constant.u8Const); break; + case EbtInt16: returnValue.setUConst(uConst << constant.i16Const); break; + case EbtUint16: returnValue.setUConst(uConst << constant.u16Const); break; + case EbtInt64: returnValue.setUConst(uConst << constant.i64Const); break; + case EbtUint64: returnValue.setUConst(uConst << constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator&(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst & constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst & constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const & constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const & constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const & constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const & constant.u16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const & constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const & constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator|(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst | constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst | constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const | constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const | constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const | constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const | constant.u16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const | constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const | constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator^(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst ^ constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst ^ constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const ^ constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const ^ constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const ^ constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const ^ constant.u16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const ^ constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const ^ constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator~() const + { + TConstUnion returnValue; + switch (type) { + case EbtInt: returnValue.setIConst(~iConst); break; + case EbtUint: returnValue.setUConst(~uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(~i8Const); break; + case EbtUint8: returnValue.setU8Const(~u8Const); break; + case EbtInt16: returnValue.setI16Const(~i16Const); break; + case EbtUint16: returnValue.setU16Const(~u16Const); break; + case EbtInt64: returnValue.setI64Const(~i64Const); break; + case EbtUint64: returnValue.setU64Const(~u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator&&(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtBool: returnValue.setBConst(bConst && constant.bConst); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator||(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtBool: returnValue.setBConst(bConst || constant.bConst); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TBasicType getType() const { return type; } + +private: + union { + signed char i8Const; // used for i8vec, scalar int8s + unsigned char u8Const; // used for u8vec, scalar uint8s + signed short i16Const; // used for i16vec, scalar int16s + unsigned short u16Const; // used for u16vec, scalar uint16s + int iConst; // used for ivec, scalar ints + unsigned int uConst; // used for uvec, scalar uints + long long i64Const; // used for i64vec, scalar int64s + unsigned long long u64Const; // used for u64vec, scalar uint64s + bool bConst; // used for bvec, scalar bools + double dConst; // used for vec, dvec, mat, dmat, scalar floats and doubles + const TString* sConst; // string constant + }; + + TBasicType type; +}; + +// Encapsulate having a pointer to an array of TConstUnion, +// which only needs to be allocated if its size is going to be +// bigger than 0. +// +// One convenience is being able to use [] to go inside the array, instead +// of C++ assuming it as an array of pointers to vectors. +// +// General usage is that the size is known up front, and it is +// created once with the proper size. +// +class TConstUnionArray { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TConstUnionArray() : unionArray(nullptr) { } + virtual ~TConstUnionArray() { } + + explicit TConstUnionArray(int size) + { + if (size == 0) + unionArray = nullptr; + else + unionArray = new TConstUnionVector(size); + } + TConstUnionArray(const TConstUnionArray& a) = default; + TConstUnionArray(const TConstUnionArray& a, int start, int size) + { + unionArray = new TConstUnionVector(size); + for (int i = 0; i < size; ++i) + (*unionArray)[i] = a[start + i]; + } + + // Use this constructor for a smear operation + TConstUnionArray(int size, const TConstUnion& val) + { + unionArray = new TConstUnionVector(size, val); + } + + int size() const { return unionArray ? (int)unionArray->size() : 0; } + TConstUnion& operator[](size_t index) { return (*unionArray)[index]; } + const TConstUnion& operator[](size_t index) const { return (*unionArray)[index]; } + bool operator==(const TConstUnionArray& rhs) const + { + // this includes the case that both are unallocated + if (unionArray == rhs.unionArray) + return true; + + if (! unionArray || ! rhs.unionArray) + return false; + + return *unionArray == *rhs.unionArray; + } + bool operator!=(const TConstUnionArray& rhs) const { return ! operator==(rhs); } + + double dot(const TConstUnionArray& rhs) + { + assert(rhs.unionArray->size() == unionArray->size()); + double sum = 0.0; + + for (size_t comp = 0; comp < unionArray->size(); ++comp) + sum += (*this)[comp].getDConst() * rhs[comp].getDConst(); + + return sum; + } + + bool empty() const { return unionArray == nullptr; } + +protected: + typedef TVector TConstUnionVector; + TConstUnionVector* unionArray; +}; + +} // end namespace glslang + +#endif // _CONSTANT_UNION_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/Include/InfoSink.h b/armorcore/tools/to_spirv/glslang/glslang/Include/InfoSink.h new file mode 100644 index 000000000..dceb603cf --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Include/InfoSink.h @@ -0,0 +1,144 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _INFOSINK_INCLUDED_ +#define _INFOSINK_INCLUDED_ + +#include "../Include/Common.h" +#include + +namespace glslang { + +// +// TPrefixType is used to centralize how info log messages start. +// See below. +// +enum TPrefixType { + EPrefixNone, + EPrefixWarning, + EPrefixError, + EPrefixInternalError, + EPrefixUnimplemented, + EPrefixNote +}; + +enum TOutputStream { + ENull = 0, + EDebugger = 0x01, + EStdOut = 0x02, + EString = 0x04, +}; +// +// Encapsulate info logs for all objects that have them. +// +// The methods are a general set of tools for getting a variety of +// messages and types inserted into the log. +// +class TInfoSinkBase { +public: + TInfoSinkBase() : outputStream(4) {} + void erase() { sink.erase(); } + TInfoSinkBase& operator<<(const TPersistString& t) { append(t); return *this; } + TInfoSinkBase& operator<<(char c) { append(1, c); return *this; } + TInfoSinkBase& operator<<(const char* s) { append(s); return *this; } + TInfoSinkBase& operator<<(int n) { append(String(n)); return *this; } + TInfoSinkBase& operator<<(unsigned int n) { append(String(n)); return *this; } + TInfoSinkBase& operator<<(float n) { const int size = 40; char buf[size]; + snprintf(buf, size, (fabs(n) > 1e-8 && fabs(n) < 1e8) || n == 0.0f ? "%f" : "%g", n); + append(buf); + return *this; } + TInfoSinkBase& operator+(const TPersistString& t) { append(t); return *this; } + TInfoSinkBase& operator+(const TString& t) { append(t); return *this; } + TInfoSinkBase& operator<<(const TString& t) { append(t); return *this; } + TInfoSinkBase& operator+(const char* s) { append(s); return *this; } + const char* c_str() const { return sink.c_str(); } + void prefix(TPrefixType message) { + switch(message) { + case EPrefixNone: break; + case EPrefixWarning: append("WARNING: "); break; + case EPrefixError: append("ERROR: "); break; + case EPrefixInternalError: append("INTERNAL ERROR: "); break; + case EPrefixUnimplemented: append("UNIMPLEMENTED: "); break; + case EPrefixNote: append("NOTE: "); break; + default: append("UNKNOWN ERROR: "); break; + } + } + void location(const TSourceLoc& loc) { + const int maxSize = 24; + char locText[maxSize]; + snprintf(locText, maxSize, ":%d", loc.line); + append(loc.getStringNameOrNum(false).c_str()); + append(locText); + append(": "); + } + void message(TPrefixType message, const char* s) { + prefix(message); + append(s); + append("\n"); + } + void message(TPrefixType message, const char* s, const TSourceLoc& loc) { + prefix(message); + location(loc); + append(s); + append("\n"); + } + + void setOutputStream(int output = 4) + { + outputStream = output; + } + +protected: + void append(const char* s); + + void append(int count, char c); + void append(const TPersistString& t); + void append(const TString& t); + + void checkMem(size_t growth) { if (sink.capacity() < sink.size() + growth + 2) + sink.reserve(sink.capacity() + sink.capacity() / 2); } + void appendToStream(const char* s); + TPersistString sink; + int outputStream; +}; + +} // end namespace glslang + +class TInfoSink { +public: + glslang::TInfoSinkBase info; + glslang::TInfoSinkBase debug; +}; + +#endif // _INFOSINK_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/Include/InitializeGlobals.h b/armorcore/tools/to_spirv/glslang/glslang/Include/InitializeGlobals.h new file mode 100644 index 000000000..95d0a40e9 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Include/InitializeGlobals.h @@ -0,0 +1,44 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef __INITIALIZE_GLOBALS_INCLUDED_ +#define __INITIALIZE_GLOBALS_INCLUDED_ + +namespace glslang { + +bool InitializePoolIndex(); + +} // end namespace glslang + +#endif // __INITIALIZE_GLOBALS_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/Include/PoolAlloc.h b/armorcore/tools/to_spirv/glslang/glslang/Include/PoolAlloc.h new file mode 100644 index 000000000..b8eccb883 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Include/PoolAlloc.h @@ -0,0 +1,316 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _POOLALLOC_INCLUDED_ +#define _POOLALLOC_INCLUDED_ + +#ifdef _DEBUG +# define GUARD_BLOCKS // define to enable guard block sanity checking +#endif + +// +// This header defines an allocator that can be used to efficiently +// allocate a large number of small requests for heap memory, with the +// intention that they are not individually deallocated, but rather +// collectively deallocated at one time. +// +// This simultaneously +// +// * Makes each individual allocation much more efficient; the +// typical allocation is trivial. +// * Completely avoids the cost of doing individual deallocation. +// * Saves the trouble of tracking down and plugging a large class of leaks. +// +// Individual classes can use this allocator by supplying their own +// new and delete methods. +// +// STL containers can use this allocator by using the pool_allocator +// class as the allocator (second) template argument. +// + +#include +#include +#include + +namespace glslang { + +// If we are using guard blocks, we must track each individual +// allocation. If we aren't using guard blocks, these +// never get instantiated, so won't have any impact. +// + +class TAllocation { +public: + TAllocation(size_t size, unsigned char* mem, TAllocation* prev = 0) : + size(size), mem(mem), prevAlloc(prev) { + // Allocations are bracketed: + // [allocationHeader][initialGuardBlock][userData][finalGuardBlock] + // This would be cleaner with if (guardBlockSize)..., but that + // makes the compiler print warnings about 0 length memsets, + // even with the if() protecting them. +# ifdef GUARD_BLOCKS + memset(preGuard(), guardBlockBeginVal, guardBlockSize); + memset(data(), userDataFill, size); + memset(postGuard(), guardBlockEndVal, guardBlockSize); +# endif + } + + void check() const { + checkGuardBlock(preGuard(), guardBlockBeginVal, "before"); + checkGuardBlock(postGuard(), guardBlockEndVal, "after"); + } + + void checkAllocList() const; + + // Return total size needed to accommodate user buffer of 'size', + // plus our tracking data. + inline static size_t allocationSize(size_t size) { + return size + 2 * guardBlockSize + headerSize(); + } + + // Offset from surrounding buffer to get to user data buffer. + inline static unsigned char* offsetAllocation(unsigned char* m) { + return m + guardBlockSize + headerSize(); + } + +private: + void checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const; + + // Find offsets to pre and post guard blocks, and user data buffer + unsigned char* preGuard() const { return mem + headerSize(); } + unsigned char* data() const { return preGuard() + guardBlockSize; } + unsigned char* postGuard() const { return data() + size; } + + size_t size; // size of the user data area + unsigned char* mem; // beginning of our allocation (pts to header) + TAllocation* prevAlloc; // prior allocation in the chain + + const static unsigned char guardBlockBeginVal; + const static unsigned char guardBlockEndVal; + const static unsigned char userDataFill; + + const static size_t guardBlockSize; +# ifdef GUARD_BLOCKS + inline static size_t headerSize() { return sizeof(TAllocation); } +# else + inline static size_t headerSize() { return 0; } +# endif +}; + +// +// There are several stacks. One is to track the pushing and popping +// of the user, and not yet implemented. The others are simply a +// repositories of free pages or used pages. +// +// Page stacks are linked together with a simple header at the beginning +// of each allocation obtained from the underlying OS. Multi-page allocations +// are returned to the OS. Individual page allocations are kept for future +// re-use. +// +// The "page size" used is not, nor must it match, the underlying OS +// page size. But, having it be about that size or equal to a set of +// pages is likely most optimal. +// +class TPoolAllocator { +public: + TPoolAllocator(int growthIncrement = 8*1024, int allocationAlignment = 16); + + // + // Don't call the destructor just to free up the memory, call pop() + // + ~TPoolAllocator(); + + // + // Call push() to establish a new place to pop memory too. Does not + // have to be called to get things started. + // + void push(); + + // + // Call pop() to free all memory allocated since the last call to push(), + // or if no last call to push, frees all memory since first allocation. + // + void pop(); + + // + // Call popAll() to free all memory allocated. + // + void popAll(); + + // + // Call allocate() to actually acquire memory. Returns 0 if no memory + // available, otherwise a properly aligned pointer to 'numBytes' of memory. + // + void* allocate(size_t numBytes); + + // + // There is no deallocate. The point of this class is that + // deallocation can be skipped by the user of it, as the model + // of use is to simultaneously deallocate everything at once + // by calling pop(), and to not have to solve memory leak problems. + // + +protected: + friend struct tHeader; + + struct tHeader { + tHeader(tHeader* nextPage, size_t pageCount) : +#ifdef GUARD_BLOCKS + lastAllocation(0), +#endif + nextPage(nextPage), pageCount(pageCount) { } + + ~tHeader() { +#ifdef GUARD_BLOCKS + if (lastAllocation) + lastAllocation->checkAllocList(); +#endif + } + +#ifdef GUARD_BLOCKS + TAllocation* lastAllocation; +#endif + tHeader* nextPage; + size_t pageCount; + }; + + struct tAllocState { + size_t offset; + tHeader* page; + }; + typedef std::vector tAllocStack; + + // Track allocations if and only if we're using guard blocks +#ifndef GUARD_BLOCKS + void* initializeAllocation(tHeader*, unsigned char* memory, size_t) { +#else + void* initializeAllocation(tHeader* block, unsigned char* memory, size_t numBytes) { + new(memory) TAllocation(numBytes, memory, block->lastAllocation); + block->lastAllocation = reinterpret_cast(memory); +#endif + + // This is optimized entirely away if GUARD_BLOCKS is not defined. + return TAllocation::offsetAllocation(memory); + } + + size_t pageSize; // granularity of allocation from the OS + size_t alignment; // all returned allocations will be aligned at + // this granularity, which will be a power of 2 + size_t alignmentMask; + size_t headerSkip; // amount of memory to skip to make room for the + // header (basically, size of header, rounded + // up to make it aligned + size_t currentPageOffset; // next offset in top of inUseList to allocate from + tHeader* freeList; // list of popped memory + tHeader* inUseList; // list of all memory currently being used + tAllocStack stack; // stack of where to allocate from, to partition pool + + int numCalls; // just an interesting statistic + size_t totalBytes; // just an interesting statistic +private: + TPoolAllocator& operator=(const TPoolAllocator&); // don't allow assignment operator + TPoolAllocator(const TPoolAllocator&); // don't allow default copy constructor +}; + +// +// There could potentially be many pools with pops happening at +// different times. But a simple use is to have a global pop +// with everyone using the same global allocator. +// +extern TPoolAllocator& GetThreadPoolAllocator(); +void SetThreadPoolAllocator(TPoolAllocator* poolAllocator); + +// +// This STL compatible allocator is intended to be used as the allocator +// parameter to templatized STL containers, like vector and map. +// +// It will use the pools for allocation, and not +// do any deallocation, but will still do destruction. +// +template +class pool_allocator { +public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef T *pointer; + typedef const T *const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T value_type; + template + struct rebind { + typedef pool_allocator other; + }; + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } + + pool_allocator() : allocator(GetThreadPoolAllocator()) { } + pool_allocator(TPoolAllocator& a) : allocator(a) { } + pool_allocator(const pool_allocator& p) : allocator(p.allocator) { } + + template + pool_allocator(const pool_allocator& p) : allocator(p.getAllocator()) { } + + pointer allocate(size_type n) { + return reinterpret_cast(getAllocator().allocate(n * sizeof(T))); } + pointer allocate(size_type n, const void*) { + return reinterpret_cast(getAllocator().allocate(n * sizeof(T))); } + + void deallocate(void*, size_type) { } + void deallocate(pointer, size_type) { } + + pointer _Charalloc(size_t n) { + return reinterpret_cast(getAllocator().allocate(n)); } + + void construct(pointer p, const T& val) { new ((void *)p) T(val); } + void destroy(pointer p) { p->T::~T(); } + + bool operator==(const pool_allocator& rhs) const { return &getAllocator() == &rhs.getAllocator(); } + bool operator!=(const pool_allocator& rhs) const { return &getAllocator() != &rhs.getAllocator(); } + + size_type max_size() const { return static_cast(-1) / sizeof(T); } + size_type max_size(int size) const { return static_cast(-1) / size; } + + TPoolAllocator& getAllocator() const { return allocator; } + +protected: + pool_allocator& operator=(const pool_allocator&) { return *this; } + TPoolAllocator& allocator; +}; + +} // end namespace glslang + +#endif // _POOLALLOC_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/Include/ResourceLimits.h b/armorcore/tools/to_spirv/glslang/glslang/Include/ResourceLimits.h new file mode 100644 index 000000000..b670cf163 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Include/ResourceLimits.h @@ -0,0 +1,150 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _RESOURCE_LIMITS_INCLUDED_ +#define _RESOURCE_LIMITS_INCLUDED_ + +struct TLimits { + bool nonInductiveForLoops; + bool whileLoops; + bool doWhileLoops; + bool generalUniformIndexing; + bool generalAttributeMatrixVectorIndexing; + bool generalVaryingIndexing; + bool generalSamplerIndexing; + bool generalVariableIndexing; + bool generalConstantMatrixVectorIndexing; +}; + +struct TBuiltInResource { + int maxLights; + int maxClipPlanes; + int maxTextureUnits; + int maxTextureCoords; + int maxVertexAttribs; + int maxVertexUniformComponents; + int maxVaryingFloats; + int maxVertexTextureImageUnits; + int maxCombinedTextureImageUnits; + int maxTextureImageUnits; + int maxFragmentUniformComponents; + int maxDrawBuffers; + int maxVertexUniformVectors; + int maxVaryingVectors; + int maxFragmentUniformVectors; + int maxVertexOutputVectors; + int maxFragmentInputVectors; + int minProgramTexelOffset; + int maxProgramTexelOffset; + int maxClipDistances; + int maxComputeWorkGroupCountX; + int maxComputeWorkGroupCountY; + int maxComputeWorkGroupCountZ; + int maxComputeWorkGroupSizeX; + int maxComputeWorkGroupSizeY; + int maxComputeWorkGroupSizeZ; + int maxComputeUniformComponents; + int maxComputeTextureImageUnits; + int maxComputeImageUniforms; + int maxComputeAtomicCounters; + int maxComputeAtomicCounterBuffers; + int maxVaryingComponents; + int maxVertexOutputComponents; + int maxGeometryInputComponents; + int maxGeometryOutputComponents; + int maxFragmentInputComponents; + int maxImageUnits; + int maxCombinedImageUnitsAndFragmentOutputs; + int maxCombinedShaderOutputResources; + int maxImageSamples; + int maxVertexImageUniforms; + int maxTessControlImageUniforms; + int maxTessEvaluationImageUniforms; + int maxGeometryImageUniforms; + int maxFragmentImageUniforms; + int maxCombinedImageUniforms; + int maxGeometryTextureImageUnits; + int maxGeometryOutputVertices; + int maxGeometryTotalOutputComponents; + int maxGeometryUniformComponents; + int maxGeometryVaryingComponents; + int maxTessControlInputComponents; + int maxTessControlOutputComponents; + int maxTessControlTextureImageUnits; + int maxTessControlUniformComponents; + int maxTessControlTotalOutputComponents; + int maxTessEvaluationInputComponents; + int maxTessEvaluationOutputComponents; + int maxTessEvaluationTextureImageUnits; + int maxTessEvaluationUniformComponents; + int maxTessPatchComponents; + int maxPatchVertices; + int maxTessGenLevel; + int maxViewports; + int maxVertexAtomicCounters; + int maxTessControlAtomicCounters; + int maxTessEvaluationAtomicCounters; + int maxGeometryAtomicCounters; + int maxFragmentAtomicCounters; + int maxCombinedAtomicCounters; + int maxAtomicCounterBindings; + int maxVertexAtomicCounterBuffers; + int maxTessControlAtomicCounterBuffers; + int maxTessEvaluationAtomicCounterBuffers; + int maxGeometryAtomicCounterBuffers; + int maxFragmentAtomicCounterBuffers; + int maxCombinedAtomicCounterBuffers; + int maxAtomicCounterBufferSize; + int maxTransformFeedbackBuffers; + int maxTransformFeedbackInterleavedComponents; + int maxCullDistances; + int maxCombinedClipAndCullDistances; + int maxSamples; + int maxMeshOutputVerticesNV; + int maxMeshOutputPrimitivesNV; + int maxMeshWorkGroupSizeX_NV; + int maxMeshWorkGroupSizeY_NV; + int maxMeshWorkGroupSizeZ_NV; + int maxTaskWorkGroupSizeX_NV; + int maxTaskWorkGroupSizeY_NV; + int maxTaskWorkGroupSizeZ_NV; + int maxMeshViewCountNV; + int maxDualSourceDrawBuffersEXT; + + TLimits limits; +}; + +#endif // _RESOURCE_LIMITS_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/Include/ShHandle.h b/armorcore/tools/to_spirv/glslang/glslang/Include/ShHandle.h new file mode 100644 index 000000000..df07bd8ed --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Include/ShHandle.h @@ -0,0 +1,176 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _SHHANDLE_INCLUDED_ +#define _SHHANDLE_INCLUDED_ + +// +// Machine independent part of the compiler private objects +// sent as ShHandle to the driver. +// +// This should not be included by driver code. +// + +#define SH_EXPORTING +#include "../Public/ShaderLang.h" +#include "../MachineIndependent/Versions.h" +#include "InfoSink.h" + +class TCompiler; +class TLinker; +class TUniformMap; + +// +// The base class used to back handles returned to the driver. +// +class TShHandleBase { +public: + TShHandleBase() { pool = new glslang::TPoolAllocator; } + virtual ~TShHandleBase() { delete pool; } + virtual TCompiler* getAsCompiler() { return 0; } + virtual TLinker* getAsLinker() { return 0; } + virtual TUniformMap* getAsUniformMap() { return 0; } + virtual glslang::TPoolAllocator* getPool() const { return pool; } +private: + glslang::TPoolAllocator* pool; +}; + +// +// The base class for the machine dependent linker to derive from +// for managing where uniforms live. +// +class TUniformMap : public TShHandleBase { +public: + TUniformMap() { } + virtual ~TUniformMap() { } + virtual TUniformMap* getAsUniformMap() { return this; } + virtual int getLocation(const char* name) = 0; + virtual TInfoSink& getInfoSink() { return infoSink; } + TInfoSink infoSink; +}; + +class TIntermNode; + +// +// The base class for the machine dependent compiler to derive from +// for managing object code from the compile. +// +class TCompiler : public TShHandleBase { +public: + TCompiler(EShLanguage l, TInfoSink& sink) : infoSink(sink) , language(l), haveValidObjectCode(false) { } + virtual ~TCompiler() { } + EShLanguage getLanguage() { return language; } + virtual TInfoSink& getInfoSink() { return infoSink; } + + virtual bool compile(TIntermNode* root, int version = 0, EProfile profile = ENoProfile) = 0; + + virtual TCompiler* getAsCompiler() { return this; } + virtual bool linkable() { return haveValidObjectCode; } + + TInfoSink& infoSink; +protected: + TCompiler& operator=(TCompiler&); + + EShLanguage language; + bool haveValidObjectCode; +}; + +// +// Link operations are based on a list of compile results... +// +typedef glslang::TVector TCompilerList; +typedef glslang::TVector THandleList; + +// +// The base class for the machine dependent linker to derive from +// to manage the resulting executable. +// + +class TLinker : public TShHandleBase { +public: + TLinker(EShExecutable e, TInfoSink& iSink) : + infoSink(iSink), + executable(e), + haveReturnableObjectCode(false), + appAttributeBindings(0), + fixedAttributeBindings(0), + excludedAttributes(0), + excludedCount(0), + uniformBindings(0) { } + virtual TLinker* getAsLinker() { return this; } + virtual ~TLinker() { } + virtual bool link(TCompilerList&, TUniformMap*) = 0; + virtual bool link(THandleList&) { return false; } + virtual void setAppAttributeBindings(const ShBindingTable* t) { appAttributeBindings = t; } + virtual void setFixedAttributeBindings(const ShBindingTable* t) { fixedAttributeBindings = t; } + virtual void getAttributeBindings(ShBindingTable const **t) const = 0; + virtual void setExcludedAttributes(const int* attributes, int count) { excludedAttributes = attributes; excludedCount = count; } + virtual ShBindingTable* getUniformBindings() const { return uniformBindings; } + virtual const void* getObjectCode() const { return 0; } // a real compiler would be returning object code here + virtual TInfoSink& getInfoSink() { return infoSink; } + TInfoSink& infoSink; +protected: + TLinker& operator=(TLinker&); + EShExecutable executable; + bool haveReturnableObjectCode; // true when objectCode is acceptable to send to driver + + const ShBindingTable* appAttributeBindings; + const ShBindingTable* fixedAttributeBindings; + const int* excludedAttributes; + int excludedCount; + ShBindingTable* uniformBindings; // created by the linker +}; + +// +// This is the interface between the machine independent code +// and the machine dependent code. +// +// The machine dependent code should derive from the classes +// above. Then Construct*() and Delete*() will create and +// destroy the machine dependent objects, which contain the +// above machine independent information. +// +TCompiler* ConstructCompiler(EShLanguage, int); + +TShHandleBase* ConstructLinker(EShExecutable, int); +TShHandleBase* ConstructBindings(); +void DeleteLinker(TShHandleBase*); +void DeleteBindingList(TShHandleBase* bindingList); + +TUniformMap* ConstructUniformMap(); +void DeleteCompiler(TCompiler*); + +void DeleteUniformMap(TUniformMap*); + +#endif // _SHHANDLE_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/Include/Types.h b/armorcore/tools/to_spirv/glslang/glslang/Include/Types.h new file mode 100644 index 000000000..a71a8a1ab --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Include/Types.h @@ -0,0 +1,2492 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// Copyright (C) 2015-2016 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _TYPES_INCLUDED +#define _TYPES_INCLUDED + +#include "../Include/Common.h" +#include "../Include/BaseTypes.h" +#include "../Public/ShaderLang.h" +#include "arrays.h" + +#include + +namespace glslang { + +const int GlslangMaxTypeLength = 200; // TODO: need to print block/struct one member per line, so this can stay bounded + +const char* const AnonymousPrefix = "anon@"; // for something like a block whose members can be directly accessed +inline bool IsAnonymous(const TString& name) +{ + return name.compare(0, 5, AnonymousPrefix) == 0; +} + +// +// Details within a sampler type +// +enum TSamplerDim { + EsdNone, + Esd1D, + Esd2D, + Esd3D, + EsdCube, + EsdRect, + EsdBuffer, + EsdSubpass, // goes only with non-sampled image (image is true) + EsdNumDims +}; + +struct TSampler { // misnomer now; includes images, textures without sampler, and textures with sampler + TBasicType type : 8; // type returned by sampler + TSamplerDim dim : 8; + bool arrayed : 1; + bool shadow : 1; + bool ms : 1; + bool image : 1; // image, combined should be false + bool combined : 1; // true means texture is combined with a sampler, false means texture with no sampler + bool sampler : 1; // true means a pure sampler, other fields should be clear() + +#ifdef GLSLANG_WEB + bool is1D() const { return false; } + bool isBuffer() const { return false; } + bool isRect() const { return false; } + bool isSubpass() const { return false; } + bool isCombined() const { return true; } + bool isImage() const { return false; } + bool isImageClass() const { return false; } + bool isMultiSample() const { return false; } + bool isExternal() const { return false; } + void setExternal(bool e) { } + bool isYuv() const { return false; } + bool isVideo() const { return false; } +#else + bool video : 1; // K extension + unsigned int vectorSize : 3; // vector return type size. + // Some languages support structures as sample results. Storing the whole structure in the + // TSampler is too large, so there is an index to a separate table. + static const unsigned structReturnIndexBits = 4; // number of index bits to use. + static const unsigned structReturnSlots = (1< TTypeList; + +typedef TVector TIdentifierList; + +// +// Following are a series of helper enums for managing layouts and qualifiers, +// used for TPublicType, TType, others. +// + +enum TLayoutPacking { + ElpNone, + ElpShared, // default, but different than saying nothing + ElpStd140, + ElpStd430, + ElpPacked, + ElpScalar, + ElpCount // If expanding, see bitfield width below +}; + +enum TLayoutMatrix { + ElmNone, + ElmRowMajor, + ElmColumnMajor, // default, but different than saying nothing + ElmCount // If expanding, see bitfield width below +}; + +// Union of geometry shader and tessellation shader geometry types. +// They don't go into TType, but rather have current state per shader or +// active parser type (TPublicType). +enum TLayoutGeometry { + ElgNone, + ElgPoints, + ElgLines, + ElgLinesAdjacency, + ElgLineStrip, + ElgTriangles, + ElgTrianglesAdjacency, + ElgTriangleStrip, + ElgQuads, + ElgIsolines, +}; + +enum TVertexSpacing { + EvsNone, + EvsEqual, + EvsFractionalEven, + EvsFractionalOdd +}; + +enum TVertexOrder { + EvoNone, + EvoCw, + EvoCcw +}; + +// Note: order matters, as type of format is done by comparison. +enum TLayoutFormat { + ElfNone, + + // Float image + ElfRgba32f, + ElfRgba16f, + ElfR32f, + ElfRgba8, + ElfRgba8Snorm, + + ElfEsFloatGuard, // to help with comparisons + + ElfRg32f, + ElfRg16f, + ElfR11fG11fB10f, + ElfR16f, + ElfRgba16, + ElfRgb10A2, + ElfRg16, + ElfRg8, + ElfR16, + ElfR8, + ElfRgba16Snorm, + ElfRg16Snorm, + ElfRg8Snorm, + ElfR16Snorm, + ElfR8Snorm, + + ElfFloatGuard, // to help with comparisons + + // Int image + ElfRgba32i, + ElfRgba16i, + ElfRgba8i, + ElfR32i, + + ElfEsIntGuard, // to help with comparisons + + ElfRg32i, + ElfRg16i, + ElfRg8i, + ElfR16i, + ElfR8i, + + ElfIntGuard, // to help with comparisons + + // Uint image + ElfRgba32ui, + ElfRgba16ui, + ElfRgba8ui, + ElfR32ui, + + ElfEsUintGuard, // to help with comparisons + + ElfRg32ui, + ElfRg16ui, + ElfRgb10a2ui, + ElfRg8ui, + ElfR16ui, + ElfR8ui, + + ElfCount +}; + +enum TLayoutDepth { + EldNone, + EldAny, + EldGreater, + EldLess, + EldUnchanged, + + EldCount +}; + +enum TBlendEquationShift { + // No 'EBlendNone': + // These are used as bit-shift amounts. A mask of such shifts will have type 'int', + // and in that space, 0 means no bits set, or none. In this enum, 0 means (1 << 0), a bit is set. + EBlendMultiply, + EBlendScreen, + EBlendOverlay, + EBlendDarken, + EBlendLighten, + EBlendColordodge, + EBlendColorburn, + EBlendHardlight, + EBlendSoftlight, + EBlendDifference, + EBlendExclusion, + EBlendHslHue, + EBlendHslSaturation, + EBlendHslColor, + EBlendHslLuminosity, + EBlendAllEquations, + + EBlendCount +}; + +enum TInterlockOrdering { + EioNone, + EioPixelInterlockOrdered, + EioPixelInterlockUnordered, + EioSampleInterlockOrdered, + EioSampleInterlockUnordered, + EioShadingRateInterlockOrdered, + EioShadingRateInterlockUnordered, + + EioCount, +}; + +enum TShaderInterface +{ + // Includes both uniform blocks and buffer blocks + EsiUniform = 0, + EsiInput, + EsiOutput, + EsiNone, + + EsiCount +}; + + +class TQualifier { +public: + static const int layoutNotSet = -1; + + void clear() + { + precision = EpqNone; + invariant = false; + makeTemporary(); + declaredBuiltIn = EbvNone; +#ifndef GLSLANG_WEB + noContraction = false; +#endif + } + + // drop qualifiers that don't belong in a temporary variable + void makeTemporary() + { + semanticName = nullptr; + storage = EvqTemporary; + builtIn = EbvNone; + clearInterstage(); + clearMemory(); + specConstant = false; + nonUniform = false; + clearLayout(); + } + + void clearInterstage() + { + clearInterpolation(); +#ifndef GLSLANG_WEB + patch = false; + sample = false; +#endif + } + + void clearInterpolation() + { + centroid = false; + smooth = false; + flat = false; +#ifndef GLSLANG_WEB + nopersp = false; + explicitInterp = false; + pervertexNV = false; + perPrimitiveNV = false; + perViewNV = false; + perTaskNV = false; +#endif + } + + void clearMemory() + { +#ifndef GLSLANG_WEB + coherent = false; + devicecoherent = false; + queuefamilycoherent = false; + workgroupcoherent = false; + subgroupcoherent = false; + shadercallcoherent = false; + nonprivate = false; + volatil = false; + restrict = false; + readonly = false; + writeonly = false; +#endif + } + + const char* semanticName; + TStorageQualifier storage : 6; + TBuiltInVariable builtIn : 9; + TBuiltInVariable declaredBuiltIn : 9; + static_assert(EbvLast < 256, "need to increase size of TBuiltInVariable bitfields!"); + TPrecisionQualifier precision : 3; + bool invariant : 1; // require canonical treatment for cross-shader invariance + bool centroid : 1; + bool smooth : 1; + bool flat : 1; + // having a constant_id is not sufficient: expressions have no id, but are still specConstant + bool specConstant : 1; + bool nonUniform : 1; + bool explicitOffset : 1; + +#ifdef GLSLANG_WEB + bool isWriteOnly() const { return false; } + bool isReadOnly() const { return false; } + bool isRestrict() const { return false; } + bool isCoherent() const { return false; } + bool isVolatile() const { return false; } + bool isSample() const { return false; } + bool isMemory() const { return false; } + bool isMemoryQualifierImageAndSSBOOnly() const { return false; } + bool bufferReferenceNeedsVulkanMemoryModel() const { return false; } + bool isInterpolation() const { return flat || smooth; } + bool isExplicitInterpolation() const { return false; } + bool isAuxiliary() const { return centroid; } + bool isPatch() const { return false; } + bool isNoContraction() const { return false; } + void setNoContraction() { } + bool isPervertexNV() const { return false; } +#else + bool noContraction: 1; // prevent contraction and reassociation, e.g., for 'precise' keyword, and expressions it affects + bool nopersp : 1; + bool explicitInterp : 1; + bool pervertexNV : 1; + bool perPrimitiveNV : 1; + bool perViewNV : 1; + bool perTaskNV : 1; + bool patch : 1; + bool sample : 1; + bool restrict : 1; + bool readonly : 1; + bool writeonly : 1; + bool coherent : 1; + bool volatil : 1; + bool devicecoherent : 1; + bool queuefamilycoherent : 1; + bool workgroupcoherent : 1; + bool subgroupcoherent : 1; + bool shadercallcoherent : 1; + bool nonprivate : 1; + bool isWriteOnly() const { return writeonly; } + bool isReadOnly() const { return readonly; } + bool isRestrict() const { return restrict; } + bool isCoherent() const { return coherent; } + bool isVolatile() const { return volatil; } + bool isSample() const { return sample; } + bool isMemory() const + { + return shadercallcoherent || subgroupcoherent || workgroupcoherent || queuefamilycoherent || devicecoherent || coherent || volatil || restrict || readonly || writeonly || nonprivate; + } + bool isMemoryQualifierImageAndSSBOOnly() const + { + return shadercallcoherent || subgroupcoherent || workgroupcoherent || queuefamilycoherent || devicecoherent || coherent || volatil || restrict || readonly || writeonly; + } + bool bufferReferenceNeedsVulkanMemoryModel() const + { + // include qualifiers that map to load/store availability/visibility/nonprivate memory access operands + return subgroupcoherent || workgroupcoherent || queuefamilycoherent || devicecoherent || coherent || nonprivate; + } + bool isInterpolation() const + { + return flat || smooth || nopersp || explicitInterp; + } + bool isExplicitInterpolation() const + { + return explicitInterp; + } + bool isAuxiliary() const + { + return centroid || patch || sample || pervertexNV; + } + bool isPatch() const { return patch; } + bool isNoContraction() const { return noContraction; } + void setNoContraction() { noContraction = true; } + bool isPervertexNV() const { return pervertexNV; } +#endif + + bool isPipeInput() const + { + switch (storage) { + case EvqVaryingIn: + case EvqFragCoord: + case EvqPointCoord: + case EvqFace: + case EvqVertexId: + case EvqInstanceId: + return true; + default: + return false; + } + } + + bool isPipeOutput() const + { + switch (storage) { + case EvqPosition: + case EvqPointSize: + case EvqClipVertex: + case EvqVaryingOut: + case EvqFragColor: + case EvqFragDepth: + return true; + default: + return false; + } + } + + bool isParamInput() const + { + switch (storage) { + case EvqIn: + case EvqInOut: + case EvqConstReadOnly: + return true; + default: + return false; + } + } + + bool isParamOutput() const + { + switch (storage) { + case EvqOut: + case EvqInOut: + return true; + default: + return false; + } + } + + bool isUniformOrBuffer() const + { + switch (storage) { + case EvqUniform: + case EvqBuffer: + return true; + default: + return false; + } + } + + bool isIo() const + { + switch (storage) { + case EvqUniform: + case EvqBuffer: + case EvqVaryingIn: + case EvqFragCoord: + case EvqPointCoord: + case EvqFace: + case EvqVertexId: + case EvqInstanceId: + case EvqPosition: + case EvqPointSize: + case EvqClipVertex: + case EvqVaryingOut: + case EvqFragColor: + case EvqFragDepth: + return true; + default: + return false; + } + } + + // non-built-in symbols that might link between compilation units + bool isLinkable() const + { + switch (storage) { + case EvqGlobal: + case EvqVaryingIn: + case EvqVaryingOut: + case EvqUniform: + case EvqBuffer: + case EvqShared: + return true; + default: + return false; + } + } + +#ifdef GLSLANG_WEB + bool isPerView() const { return false; } + bool isTaskMemory() const { return false; } + bool isArrayedIo(EShLanguage language) const { return false; } +#else + bool isPerPrimitive() const { return perPrimitiveNV; } + bool isPerView() const { return perViewNV; } + bool isTaskMemory() const { return perTaskNV; } + + // True if this type of IO is supposed to be arrayed with extra level for per-vertex data + bool isArrayedIo(EShLanguage language) const + { + switch (language) { + case EShLangGeometry: + return isPipeInput(); + case EShLangTessControl: + return ! patch && (isPipeInput() || isPipeOutput()); + case EShLangTessEvaluation: + return ! patch && isPipeInput(); + case EShLangFragment: + return pervertexNV && isPipeInput(); + case EShLangMeshNV: + return ! perTaskNV && isPipeOutput(); + + default: + return false; + } + } +#endif + + // Implementing an embedded layout-qualifier class here, since C++ can't have a real class bitfield + void clearLayout() // all layout + { + clearUniformLayout(); + +#ifndef GLSLANG_WEB + layoutPushConstant = false; + layoutBufferReference = false; + layoutPassthrough = false; + layoutViewportRelative = false; + // -2048 as the default value indicating layoutSecondaryViewportRelative is not set + layoutSecondaryViewportRelativeOffset = -2048; + layoutShaderRecord = false; + layoutBufferReferenceAlign = layoutBufferReferenceAlignEnd; + layoutFormat = ElfNone; +#endif + + clearInterstageLayout(); + + layoutSpecConstantId = layoutSpecConstantIdEnd; + } + void clearInterstageLayout() + { + layoutLocation = layoutLocationEnd; + layoutComponent = layoutComponentEnd; +#ifndef GLSLANG_WEB + layoutIndex = layoutIndexEnd; + clearStreamLayout(); + clearXfbLayout(); +#endif + } + +#ifndef GLSLANG_WEB + void clearStreamLayout() + { + layoutStream = layoutStreamEnd; + } + void clearXfbLayout() + { + layoutXfbBuffer = layoutXfbBufferEnd; + layoutXfbStride = layoutXfbStrideEnd; + layoutXfbOffset = layoutXfbOffsetEnd; + } +#endif + + bool hasNonXfbLayout() const + { + return hasUniformLayout() || + hasAnyLocation() || + hasStream() || + hasFormat() || + isShaderRecord() || + isPushConstant() || + hasBufferReference(); + } + bool hasLayout() const + { + return hasNonXfbLayout() || + hasXfb(); + } + TLayoutMatrix layoutMatrix : 3; + TLayoutPacking layoutPacking : 4; + int layoutOffset; + int layoutAlign; + + unsigned int layoutLocation : 12; + static const unsigned int layoutLocationEnd = 0xFFF; + + unsigned int layoutComponent : 3; + static const unsigned int layoutComponentEnd = 4; + + unsigned int layoutSet : 7; + static const unsigned int layoutSetEnd = 0x3F; + + unsigned int layoutBinding : 16; + static const unsigned int layoutBindingEnd = 0xFFFF; + + unsigned int layoutIndex : 8; + static const unsigned int layoutIndexEnd = 0xFF; + + unsigned int layoutStream : 8; + static const unsigned int layoutStreamEnd = 0xFF; + + unsigned int layoutXfbBuffer : 4; + static const unsigned int layoutXfbBufferEnd = 0xF; + + unsigned int layoutXfbStride : 14; + static const unsigned int layoutXfbStrideEnd = 0x3FFF; + + unsigned int layoutXfbOffset : 13; + static const unsigned int layoutXfbOffsetEnd = 0x1FFF; + + unsigned int layoutAttachment : 8; // for input_attachment_index + static const unsigned int layoutAttachmentEnd = 0XFF; + + unsigned int layoutSpecConstantId : 11; + static const unsigned int layoutSpecConstantIdEnd = 0x7FF; + +#ifndef GLSLANG_WEB + // stored as log2 of the actual alignment value + unsigned int layoutBufferReferenceAlign : 6; + static const unsigned int layoutBufferReferenceAlignEnd = 0x3F; + + TLayoutFormat layoutFormat : 8; + + bool layoutPushConstant; + bool layoutBufferReference; + bool layoutPassthrough; + bool layoutViewportRelative; + int layoutSecondaryViewportRelativeOffset; + bool layoutShaderRecord; +#endif + + bool hasUniformLayout() const + { + return hasMatrix() || + hasPacking() || + hasOffset() || + hasBinding() || + hasSet() || + hasAlign(); + } + void clearUniformLayout() // only uniform specific + { + layoutMatrix = ElmNone; + layoutPacking = ElpNone; + layoutOffset = layoutNotSet; + layoutAlign = layoutNotSet; + + layoutSet = layoutSetEnd; + layoutBinding = layoutBindingEnd; +#ifndef GLSLANG_WEB + layoutAttachment = layoutAttachmentEnd; +#endif + } + + bool hasMatrix() const + { + return layoutMatrix != ElmNone; + } + bool hasPacking() const + { + return layoutPacking != ElpNone; + } + bool hasAlign() const + { + return layoutAlign != layoutNotSet; + } + bool hasAnyLocation() const + { + return hasLocation() || + hasComponent() || + hasIndex(); + } + bool hasLocation() const + { + return layoutLocation != layoutLocationEnd; + } + bool hasSet() const + { + return layoutSet != layoutSetEnd; + } + bool hasBinding() const + { + return layoutBinding != layoutBindingEnd; + } +#ifdef GLSLANG_WEB + bool hasOffset() const { return false; } + bool isNonPerspective() const { return false; } + bool hasIndex() const { return false; } + unsigned getIndex() const { return 0; } + bool hasComponent() const { return false; } + bool hasStream() const { return false; } + bool hasFormat() const { return false; } + bool hasXfb() const { return false; } + bool hasXfbBuffer() const { return false; } + bool hasXfbStride() const { return false; } + bool hasXfbOffset() const { return false; } + bool hasAttachment() const { return false; } + TLayoutFormat getFormat() const { return ElfNone; } + bool isPushConstant() const { return false; } + bool isShaderRecord() const { return false; } + bool hasBufferReference() const { return false; } + bool hasBufferReferenceAlign() const { return false; } + bool isNonUniform() const { return false; } +#else + bool hasOffset() const + { + return layoutOffset != layoutNotSet; + } + bool isNonPerspective() const { return nopersp; } + bool hasIndex() const + { + return layoutIndex != layoutIndexEnd; + } + unsigned getIndex() const { return layoutIndex; } + bool hasComponent() const + { + return layoutComponent != layoutComponentEnd; + } + bool hasStream() const + { + return layoutStream != layoutStreamEnd; + } + bool hasFormat() const + { + return layoutFormat != ElfNone; + } + bool hasXfb() const + { + return hasXfbBuffer() || + hasXfbStride() || + hasXfbOffset(); + } + bool hasXfbBuffer() const + { + return layoutXfbBuffer != layoutXfbBufferEnd; + } + bool hasXfbStride() const + { + return layoutXfbStride != layoutXfbStrideEnd; + } + bool hasXfbOffset() const + { + return layoutXfbOffset != layoutXfbOffsetEnd; + } + bool hasAttachment() const + { + return layoutAttachment != layoutAttachmentEnd; + } + TLayoutFormat getFormat() const { return layoutFormat; } + bool isPushConstant() const { return layoutPushConstant; } + bool isShaderRecord() const { return layoutShaderRecord; } + bool hasBufferReference() const { return layoutBufferReference; } + bool hasBufferReferenceAlign() const + { + return layoutBufferReferenceAlign != layoutBufferReferenceAlignEnd; + } + bool isNonUniform() const + { + return nonUniform; + } +#endif + bool hasSpecConstantId() const + { + // Not the same thing as being a specialization constant, this + // is just whether or not it was declared with an ID. + return layoutSpecConstantId != layoutSpecConstantIdEnd; + } + bool isSpecConstant() const + { + // True if type is a specialization constant, whether or not it + // had a specialization-constant ID, and false if it is not a + // true front-end constant. + return specConstant; + } + bool isFrontEndConstant() const + { + // True if the front-end knows the final constant value. + // This allows front-end constant folding. + return storage == EvqConst && ! specConstant; + } + bool isConstant() const + { + // True if is either kind of constant; specialization or regular. + return isFrontEndConstant() || isSpecConstant(); + } + void makeSpecConstant() + { + storage = EvqConst; + specConstant = true; + } + static const char* getLayoutPackingString(TLayoutPacking packing) + { + switch (packing) { + case ElpStd140: return "std140"; +#ifndef GLSLANG_WEB + case ElpPacked: return "packed"; + case ElpShared: return "shared"; + case ElpStd430: return "std430"; + case ElpScalar: return "scalar"; +#endif + default: return "none"; + } + } + static const char* getLayoutMatrixString(TLayoutMatrix m) + { + switch (m) { + case ElmColumnMajor: return "column_major"; + case ElmRowMajor: return "row_major"; + default: return "none"; + } + } +#ifdef GLSLANG_WEB + static const char* getLayoutFormatString(TLayoutFormat f) { return "none"; } +#else + static const char* getLayoutFormatString(TLayoutFormat f) + { + switch (f) { + case ElfRgba32f: return "rgba32f"; + case ElfRgba16f: return "rgba16f"; + case ElfRg32f: return "rg32f"; + case ElfRg16f: return "rg16f"; + case ElfR11fG11fB10f: return "r11f_g11f_b10f"; + case ElfR32f: return "r32f"; + case ElfR16f: return "r16f"; + case ElfRgba16: return "rgba16"; + case ElfRgb10A2: return "rgb10_a2"; + case ElfRgba8: return "rgba8"; + case ElfRg16: return "rg16"; + case ElfRg8: return "rg8"; + case ElfR16: return "r16"; + case ElfR8: return "r8"; + case ElfRgba16Snorm: return "rgba16_snorm"; + case ElfRgba8Snorm: return "rgba8_snorm"; + case ElfRg16Snorm: return "rg16_snorm"; + case ElfRg8Snorm: return "rg8_snorm"; + case ElfR16Snorm: return "r16_snorm"; + case ElfR8Snorm: return "r8_snorm"; + + case ElfRgba32i: return "rgba32i"; + case ElfRgba16i: return "rgba16i"; + case ElfRgba8i: return "rgba8i"; + case ElfRg32i: return "rg32i"; + case ElfRg16i: return "rg16i"; + case ElfRg8i: return "rg8i"; + case ElfR32i: return "r32i"; + case ElfR16i: return "r16i"; + case ElfR8i: return "r8i"; + + case ElfRgba32ui: return "rgba32ui"; + case ElfRgba16ui: return "rgba16ui"; + case ElfRgba8ui: return "rgba8ui"; + case ElfRg32ui: return "rg32ui"; + case ElfRg16ui: return "rg16ui"; + case ElfRgb10a2ui: return "rgb10_a2ui"; + case ElfRg8ui: return "rg8ui"; + case ElfR32ui: return "r32ui"; + case ElfR16ui: return "r16ui"; + case ElfR8ui: return "r8ui"; + default: return "none"; + } + } + static const char* getLayoutDepthString(TLayoutDepth d) + { + switch (d) { + case EldAny: return "depth_any"; + case EldGreater: return "depth_greater"; + case EldLess: return "depth_less"; + case EldUnchanged: return "depth_unchanged"; + default: return "none"; + } + } + static const char* getBlendEquationString(TBlendEquationShift e) + { + switch (e) { + case EBlendMultiply: return "blend_support_multiply"; + case EBlendScreen: return "blend_support_screen"; + case EBlendOverlay: return "blend_support_overlay"; + case EBlendDarken: return "blend_support_darken"; + case EBlendLighten: return "blend_support_lighten"; + case EBlendColordodge: return "blend_support_colordodge"; + case EBlendColorburn: return "blend_support_colorburn"; + case EBlendHardlight: return "blend_support_hardlight"; + case EBlendSoftlight: return "blend_support_softlight"; + case EBlendDifference: return "blend_support_difference"; + case EBlendExclusion: return "blend_support_exclusion"; + case EBlendHslHue: return "blend_support_hsl_hue"; + case EBlendHslSaturation: return "blend_support_hsl_saturation"; + case EBlendHslColor: return "blend_support_hsl_color"; + case EBlendHslLuminosity: return "blend_support_hsl_luminosity"; + case EBlendAllEquations: return "blend_support_all_equations"; + default: return "unknown"; + } + } + static const char* getGeometryString(TLayoutGeometry geometry) + { + switch (geometry) { + case ElgPoints: return "points"; + case ElgLines: return "lines"; + case ElgLinesAdjacency: return "lines_adjacency"; + case ElgLineStrip: return "line_strip"; + case ElgTriangles: return "triangles"; + case ElgTrianglesAdjacency: return "triangles_adjacency"; + case ElgTriangleStrip: return "triangle_strip"; + case ElgQuads: return "quads"; + case ElgIsolines: return "isolines"; + default: return "none"; + } + } + static const char* getVertexSpacingString(TVertexSpacing spacing) + { + switch (spacing) { + case EvsEqual: return "equal_spacing"; + case EvsFractionalEven: return "fractional_even_spacing"; + case EvsFractionalOdd: return "fractional_odd_spacing"; + default: return "none"; + } + } + static const char* getVertexOrderString(TVertexOrder order) + { + switch (order) { + case EvoCw: return "cw"; + case EvoCcw: return "ccw"; + default: return "none"; + } + } + static int mapGeometryToSize(TLayoutGeometry geometry) + { + switch (geometry) { + case ElgPoints: return 1; + case ElgLines: return 2; + case ElgLinesAdjacency: return 4; + case ElgTriangles: return 3; + case ElgTrianglesAdjacency: return 6; + default: return 0; + } + } + static const char* getInterlockOrderingString(TInterlockOrdering order) + { + switch (order) { + case EioPixelInterlockOrdered: return "pixel_interlock_ordered"; + case EioPixelInterlockUnordered: return "pixel_interlock_unordered"; + case EioSampleInterlockOrdered: return "sample_interlock_ordered"; + case EioSampleInterlockUnordered: return "sample_interlock_unordered"; + case EioShadingRateInterlockOrdered: return "shading_rate_interlock_ordered"; + case EioShadingRateInterlockUnordered: return "shading_rate_interlock_unordered"; + default: return "none"; + } + } +#endif +}; + +// Qualifiers that don't need to be keep per object. They have shader scope, not object scope. +// So, they will not be part of TType, TQualifier, etc. +struct TShaderQualifiers { + TLayoutGeometry geometry; // geometry/tessellation shader in/out primitives + bool pixelCenterInteger; // fragment shader + bool originUpperLeft; // fragment shader + int invocations; + int vertices; // for tessellation "vertices", geometry & mesh "max_vertices" + TVertexSpacing spacing; + TVertexOrder order; + bool pointMode; + int localSize[3]; // compute shader + bool localSizeNotDefault[3]; // compute shader + int localSizeSpecId[3]; // compute shader specialization id for gl_WorkGroupSize +#ifndef GLSLANG_WEB + bool earlyFragmentTests; // fragment input + bool postDepthCoverage; // fragment input + TLayoutDepth layoutDepth; + bool blendEquation; // true if any blend equation was specified + int numViews; // multiview extenstions + TInterlockOrdering interlockOrdering; + bool layoutOverrideCoverage; // true if layout override_coverage set + bool layoutDerivativeGroupQuads; // true if layout derivative_group_quadsNV set + bool layoutDerivativeGroupLinear; // true if layout derivative_group_linearNV set + int primitives; // mesh shader "max_primitives"DerivativeGroupLinear; // true if layout derivative_group_linearNV set + TLayoutDepth getDepth() const { return layoutDepth; } +#else + TLayoutDepth getDepth() const { return EldNone; } +#endif + + void init() + { + geometry = ElgNone; + originUpperLeft = false; + pixelCenterInteger = false; + invocations = TQualifier::layoutNotSet; + vertices = TQualifier::layoutNotSet; + spacing = EvsNone; + order = EvoNone; + pointMode = false; + localSize[0] = 1; + localSize[1] = 1; + localSize[2] = 1; + localSizeNotDefault[0] = false; + localSizeNotDefault[1] = false; + localSizeNotDefault[2] = false; + localSizeSpecId[0] = TQualifier::layoutNotSet; + localSizeSpecId[1] = TQualifier::layoutNotSet; + localSizeSpecId[2] = TQualifier::layoutNotSet; +#ifndef GLSLANG_WEB + earlyFragmentTests = false; + postDepthCoverage = false; + layoutDepth = EldNone; + blendEquation = false; + numViews = TQualifier::layoutNotSet; + layoutOverrideCoverage = false; + layoutDerivativeGroupQuads = false; + layoutDerivativeGroupLinear = false; + primitives = TQualifier::layoutNotSet; + interlockOrdering = EioNone; +#endif + } + +#ifdef GLSLANG_WEB + bool hasBlendEquation() const { return false; } +#else + bool hasBlendEquation() const { return blendEquation; } +#endif + + // Merge in characteristics from the 'src' qualifier. They can override when + // set, but never erase when not set. + void merge(const TShaderQualifiers& src) + { + if (src.geometry != ElgNone) + geometry = src.geometry; + if (src.pixelCenterInteger) + pixelCenterInteger = src.pixelCenterInteger; + if (src.originUpperLeft) + originUpperLeft = src.originUpperLeft; + if (src.invocations != TQualifier::layoutNotSet) + invocations = src.invocations; + if (src.vertices != TQualifier::layoutNotSet) + vertices = src.vertices; + if (src.spacing != EvsNone) + spacing = src.spacing; + if (src.order != EvoNone) + order = src.order; + if (src.pointMode) + pointMode = true; + for (int i = 0; i < 3; ++i) { + if (src.localSize[i] > 1) + localSize[i] = src.localSize[i]; + } + for (int i = 0; i < 3; ++i) { + localSizeNotDefault[i] = src.localSizeNotDefault[i] || localSizeNotDefault[i]; + } + for (int i = 0; i < 3; ++i) { + if (src.localSizeSpecId[i] != TQualifier::layoutNotSet) + localSizeSpecId[i] = src.localSizeSpecId[i]; + } +#ifndef GLSLANG_WEB + if (src.earlyFragmentTests) + earlyFragmentTests = true; + if (src.postDepthCoverage) + postDepthCoverage = true; + if (src.layoutDepth) + layoutDepth = src.layoutDepth; + if (src.blendEquation) + blendEquation = src.blendEquation; + if (src.numViews != TQualifier::layoutNotSet) + numViews = src.numViews; + if (src.layoutOverrideCoverage) + layoutOverrideCoverage = src.layoutOverrideCoverage; + if (src.layoutDerivativeGroupQuads) + layoutDerivativeGroupQuads = src.layoutDerivativeGroupQuads; + if (src.layoutDerivativeGroupLinear) + layoutDerivativeGroupLinear = src.layoutDerivativeGroupLinear; + if (src.primitives != TQualifier::layoutNotSet) + primitives = src.primitives; + if (src.interlockOrdering != EioNone) + interlockOrdering = src.interlockOrdering; +#endif + } +}; + +// +// TPublicType is just temporarily used while parsing and not quite the same +// information kept per node in TType. Due to the bison stack, it can't have +// types that it thinks have non-trivial constructors. It should +// just be used while recognizing the grammar, not anything else. +// Once enough is known about the situation, the proper information +// moved into a TType, or the parse context, etc. +// +class TPublicType { +public: + TBasicType basicType; + TSampler sampler; + TQualifier qualifier; + TShaderQualifiers shaderQualifiers; + int vectorSize : 4; + int matrixCols : 4; + int matrixRows : 4; + bool coopmat : 1; + TArraySizes* arraySizes; + const TType* userDef; + TSourceLoc loc; + TArraySizes* typeParameters; + +#ifdef GLSLANG_WEB + bool isCoopmat() const { return false; } +#else + bool isCoopmat() const { return coopmat; } +#endif + + void initType(const TSourceLoc& l) + { + basicType = EbtVoid; + vectorSize = 1; + matrixRows = 0; + matrixCols = 0; + arraySizes = nullptr; + userDef = nullptr; + loc = l; + typeParameters = nullptr; + coopmat = false; + } + + void initQualifiers(bool global = false) + { + qualifier.clear(); + if (global) + qualifier.storage = EvqGlobal; + } + + void init(const TSourceLoc& l, bool global = false) + { + initType(l); + sampler.clear(); + initQualifiers(global); + shaderQualifiers.init(); + } + + void setVector(int s) + { + matrixRows = 0; + matrixCols = 0; + vectorSize = s; + } + + void setMatrix(int c, int r) + { + matrixRows = r; + matrixCols = c; + vectorSize = 0; + } + + bool isScalar() const + { + return matrixCols == 0 && vectorSize == 1 && arraySizes == nullptr && userDef == nullptr; + } + + // "Image" is a superset of "Subpass" + bool isImage() const { return basicType == EbtSampler && sampler.isImage(); } + bool isSubpass() const { return basicType == EbtSampler && sampler.isSubpass(); } +}; + +// +// Base class for things that have a type. +// +class TType { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + // for "empty" type (no args) or simple scalar/vector/matrix + explicit TType(TBasicType t = EbtVoid, TStorageQualifier q = EvqTemporary, int vs = 1, int mc = 0, int mr = 0, + bool isVector = false) : + basicType(t), vectorSize(vs), matrixCols(mc), matrixRows(mr), vector1(isVector && vs == 1), coopmat(false), + arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr), typeParameters(nullptr) + { + sampler.clear(); + qualifier.clear(); + qualifier.storage = q; + assert(!(isMatrix() && vectorSize != 0)); // prevent vectorSize != 0 on matrices + } + // for explicit precision qualifier + TType(TBasicType t, TStorageQualifier q, TPrecisionQualifier p, int vs = 1, int mc = 0, int mr = 0, + bool isVector = false) : + basicType(t), vectorSize(vs), matrixCols(mc), matrixRows(mr), vector1(isVector && vs == 1), coopmat(false), + arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr), typeParameters(nullptr) + { + sampler.clear(); + qualifier.clear(); + qualifier.storage = q; + qualifier.precision = p; + assert(p >= EpqNone && p <= EpqHigh); + assert(!(isMatrix() && vectorSize != 0)); // prevent vectorSize != 0 on matrices + } + // for turning a TPublicType into a TType, using a shallow copy + explicit TType(const TPublicType& p) : + basicType(p.basicType), + vectorSize(p.vectorSize), matrixCols(p.matrixCols), matrixRows(p.matrixRows), vector1(false), coopmat(p.coopmat), + arraySizes(p.arraySizes), structure(nullptr), fieldName(nullptr), typeName(nullptr), typeParameters(p.typeParameters) + { + if (basicType == EbtSampler) + sampler = p.sampler; + else + sampler.clear(); + qualifier = p.qualifier; + if (p.userDef) { + if (p.userDef->basicType == EbtReference) { + basicType = EbtReference; + referentType = p.userDef->referentType; + } else { + structure = p.userDef->getWritableStruct(); // public type is short-lived; there are no sharing issues + } + typeName = NewPoolTString(p.userDef->getTypeName().c_str()); + } + if (p.isCoopmat() && p.typeParameters && p.typeParameters->getNumDims() > 0) { + int numBits = p.typeParameters->getDimSize(0); + if (p.basicType == EbtFloat && numBits == 16) { + basicType = EbtFloat16; + qualifier.precision = EpqNone; + } else if (p.basicType == EbtUint && numBits == 8) { + basicType = EbtUint8; + qualifier.precision = EpqNone; + } else if (p.basicType == EbtInt && numBits == 8) { + basicType = EbtInt8; + qualifier.precision = EpqNone; + } + } + } + // for construction of sampler types + TType(const TSampler& sampler, TStorageQualifier q = EvqUniform, TArraySizes* as = nullptr) : + basicType(EbtSampler), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), coopmat(false), + arraySizes(as), structure(nullptr), fieldName(nullptr), typeName(nullptr), + sampler(sampler), typeParameters(nullptr) + { + qualifier.clear(); + qualifier.storage = q; + } + // to efficiently make a dereferenced type + // without ever duplicating the outer structure that will be thrown away + // and using only shallow copy + TType(const TType& type, int derefIndex, bool rowMajor = false) + { + if (type.isArray()) { + shallowCopy(type); + if (type.getArraySizes()->getNumDims() == 1) { + arraySizes = nullptr; + } else { + // want our own copy of the array, so we can edit it + arraySizes = new TArraySizes; + arraySizes->copyDereferenced(*type.arraySizes); + } + } else if (type.basicType == EbtStruct || type.basicType == EbtBlock) { + // do a structure dereference + const TTypeList& memberList = *type.getStruct(); + shallowCopy(*memberList[derefIndex].type); + return; + } else { + // do a vector/matrix dereference + shallowCopy(type); + if (matrixCols > 0) { + // dereference from matrix to vector + if (rowMajor) + vectorSize = matrixCols; + else + vectorSize = matrixRows; + matrixCols = 0; + matrixRows = 0; + if (vectorSize == 1) + vector1 = true; + } else if (isVector()) { + // dereference from vector to scalar + vectorSize = 1; + vector1 = false; + } else if (isCoopMat()) { + coopmat = false; + typeParameters = nullptr; + } + } + } + // for making structures, ... + TType(TTypeList* userDef, const TString& n) : + basicType(EbtStruct), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), coopmat(false), + arraySizes(nullptr), structure(userDef), fieldName(nullptr), typeParameters(nullptr) + { + sampler.clear(); + qualifier.clear(); + typeName = NewPoolTString(n.c_str()); + } + // For interface blocks + TType(TTypeList* userDef, const TString& n, const TQualifier& q) : + basicType(EbtBlock), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), coopmat(false), + qualifier(q), arraySizes(nullptr), structure(userDef), fieldName(nullptr), typeParameters(nullptr) + { + sampler.clear(); + typeName = NewPoolTString(n.c_str()); + } + // for block reference (first parameter must be EbtReference) + explicit TType(TBasicType t, const TType &p, const TString& n) : + basicType(t), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), + arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr) + { + assert(t == EbtReference); + typeName = NewPoolTString(n.c_str()); + qualifier.clear(); + qualifier.storage = p.qualifier.storage; + referentType = p.clone(); + } + virtual ~TType() {} + + // Not for use across pool pops; it will cause multiple instances of TType to point to the same information. + // This only works if that information (like a structure's list of types) does not change and + // the instances are sharing the same pool. + void shallowCopy(const TType& copyOf) + { + basicType = copyOf.basicType; + sampler = copyOf.sampler; + qualifier = copyOf.qualifier; + vectorSize = copyOf.vectorSize; + matrixCols = copyOf.matrixCols; + matrixRows = copyOf.matrixRows; + vector1 = copyOf.vector1; + arraySizes = copyOf.arraySizes; // copying the pointer only, not the contents + fieldName = copyOf.fieldName; + typeName = copyOf.typeName; + if (isStruct()) { + structure = copyOf.structure; + } else { + referentType = copyOf.referentType; + } + typeParameters = copyOf.typeParameters; + coopmat = copyOf.isCoopMat(); + } + + // Make complete copy of the whole type graph rooted at 'copyOf'. + void deepCopy(const TType& copyOf) + { + TMap copied; // to enable copying a type graph as a graph, not a tree + deepCopy(copyOf, copied); + } + + // Recursively make temporary + void makeTemporary() + { + getQualifier().makeTemporary(); + + if (isStruct()) + for (unsigned int i = 0; i < structure->size(); ++i) + (*structure)[i].type->makeTemporary(); + } + + TType* clone() const + { + TType *newType = new TType(); + newType->deepCopy(*this); + + return newType; + } + + void makeVector() { vector1 = true; } + + virtual void hideMember() { basicType = EbtVoid; vectorSize = 1; } + virtual bool hiddenMember() const { return basicType == EbtVoid; } + + virtual void setFieldName(const TString& n) { fieldName = NewPoolTString(n.c_str()); } + virtual const TString& getTypeName() const + { + assert(typeName); + return *typeName; + } + + virtual const TString& getFieldName() const + { + assert(fieldName); + return *fieldName; + } + TShaderInterface getShaderInterface() const + { + if (basicType != EbtBlock) + return EsiNone; + + switch (qualifier.storage) { + default: + return EsiNone; + case EvqVaryingIn: + return EsiInput; + case EvqVaryingOut: + return EsiOutput; + case EvqUniform: + case EvqBuffer: + return EsiUniform; + } + } + + virtual TBasicType getBasicType() const { return basicType; } + virtual const TSampler& getSampler() const { return sampler; } + virtual TSampler& getSampler() { return sampler; } + + virtual TQualifier& getQualifier() { return qualifier; } + virtual const TQualifier& getQualifier() const { return qualifier; } + + virtual int getVectorSize() const { return vectorSize; } // returns 1 for either scalar or vector of size 1, valid for both + virtual int getMatrixCols() const { return matrixCols; } + virtual int getMatrixRows() const { return matrixRows; } + virtual int getOuterArraySize() const { return arraySizes->getOuterSize(); } + virtual TIntermTyped* getOuterArrayNode() const { return arraySizes->getOuterNode(); } + virtual int getCumulativeArraySize() const { return arraySizes->getCumulativeSize(); } +#ifdef GLSLANG_WEB + bool isArrayOfArrays() const { return false; } +#else + bool isArrayOfArrays() const { return arraySizes != nullptr && arraySizes->getNumDims() > 1; } +#endif + virtual int getImplicitArraySize() const { return arraySizes->getImplicitSize(); } + virtual const TArraySizes* getArraySizes() const { return arraySizes; } + virtual TArraySizes* getArraySizes() { return arraySizes; } + virtual TType* getReferentType() const { return referentType; } + virtual const TArraySizes* getTypeParameters() const { return typeParameters; } + virtual TArraySizes* getTypeParameters() { return typeParameters; } + + virtual bool isScalar() const { return ! isVector() && ! isMatrix() && ! isStruct() && ! isArray(); } + virtual bool isScalarOrVec1() const { return isScalar() || vector1; } + virtual bool isVector() const { return vectorSize > 1 || vector1; } + virtual bool isMatrix() const { return matrixCols ? true : false; } + virtual bool isArray() const { return arraySizes != nullptr; } + virtual bool isSizedArray() const { return isArray() && arraySizes->isSized(); } + virtual bool isUnsizedArray() const { return isArray() && !arraySizes->isSized(); } + virtual bool isArrayVariablyIndexed() const { assert(isArray()); return arraySizes->isVariablyIndexed(); } + virtual void setArrayVariablyIndexed() { assert(isArray()); arraySizes->setVariablyIndexed(); } + virtual void updateImplicitArraySize(int size) { assert(isArray()); arraySizes->updateImplicitSize(size); } + virtual bool isStruct() const { return basicType == EbtStruct || basicType == EbtBlock; } + virtual bool isFloatingDomain() const { return basicType == EbtFloat || basicType == EbtDouble || basicType == EbtFloat16; } + virtual bool isIntegerDomain() const + { + switch (basicType) { + case EbtInt8: + case EbtUint8: + case EbtInt16: + case EbtUint16: + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: + case EbtAtomicUint: + return true; + default: + break; + } + return false; + } + virtual bool isOpaque() const { return basicType == EbtSampler +#ifndef GLSLANG_WEB + || basicType == EbtAtomicUint || basicType == EbtAccStruct || basicType == EbtRayQuery +#endif + ; } + virtual bool isBuiltIn() const { return getQualifier().builtIn != EbvNone; } + + // "Image" is a superset of "Subpass" + virtual bool isImage() const { return basicType == EbtSampler && getSampler().isImage(); } + virtual bool isSubpass() const { return basicType == EbtSampler && getSampler().isSubpass(); } + virtual bool isTexture() const { return basicType == EbtSampler && getSampler().isTexture(); } + // Check the block-name convention of creating a block without populating it's members: + virtual bool isUnusableName() const { return isStruct() && structure == nullptr; } + virtual bool isParameterized() const { return typeParameters != nullptr; } +#ifdef GLSLANG_WEB + bool isAtomic() const { return false; } + bool isCoopMat() const { return false; } + bool isReference() const { return false; } +#else + bool isAtomic() const { return basicType == EbtAtomicUint; } + bool isCoopMat() const { return coopmat; } + bool isReference() const { return getBasicType() == EbtReference; } +#endif + + // return true if this type contains any subtype which satisfies the given predicate. + template + bool contains(P predicate) const + { + if (predicate(this)) + return true; + + const auto hasa = [predicate](const TTypeLoc& tl) { return tl.type->contains(predicate); }; + + return isStruct() && std::any_of(structure->begin(), structure->end(), hasa); + } + + // Recursively checks if the type contains the given basic type + virtual bool containsBasicType(TBasicType checkType) const + { + return contains([checkType](const TType* t) { return t->basicType == checkType; } ); + } + + // Recursively check the structure for any arrays, needed for some error checks + virtual bool containsArray() const + { + return contains([](const TType* t) { return t->isArray(); } ); + } + + // Check the structure for any structures, needed for some error checks + virtual bool containsStructure() const + { + return contains([this](const TType* t) { return t != this && t->isStruct(); } ); + } + + // Recursively check the structure for any unsized arrays, needed for triggering a copyUp(). + virtual bool containsUnsizedArray() const + { + return contains([](const TType* t) { return t->isUnsizedArray(); } ); + } + + virtual bool containsOpaque() const + { + return contains([](const TType* t) { return t->isOpaque(); } ); + } + + // Recursively checks if the type contains a built-in variable + virtual bool containsBuiltIn() const + { + return contains([](const TType* t) { return t->isBuiltIn(); } ); + } + + virtual bool containsNonOpaque() const + { + const auto nonOpaque = [](const TType* t) { + switch (t->basicType) { + case EbtVoid: + case EbtFloat: + case EbtDouble: + case EbtFloat16: + case EbtInt8: + case EbtUint8: + case EbtInt16: + case EbtUint16: + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: + case EbtBool: + case EbtReference: + return true; + default: + return false; + } + }; + + return contains(nonOpaque); + } + + virtual bool containsSpecializationSize() const + { + return contains([](const TType* t) { return t->isArray() && t->arraySizes->isOuterSpecialization(); } ); + } + +#ifdef GLSLANG_WEB + bool containsDouble() const { return false; } + bool contains16BitFloat() const { return false; } + bool contains64BitInt() const { return false; } + bool contains16BitInt() const { return false; } + bool contains8BitInt() const { return false; } + bool containsCoopMat() const { return false; } + bool containsReference() const { return false; } +#else + bool containsDouble() const + { + return containsBasicType(EbtDouble); + } + bool contains16BitFloat() const + { + return containsBasicType(EbtFloat16); + } + bool contains64BitInt() const + { + return containsBasicType(EbtInt64) || containsBasicType(EbtUint64); + } + bool contains16BitInt() const + { + return containsBasicType(EbtInt16) || containsBasicType(EbtUint16); + } + bool contains8BitInt() const + { + return containsBasicType(EbtInt8) || containsBasicType(EbtUint8); + } + bool containsCoopMat() const + { + return contains([](const TType* t) { return t->coopmat; } ); + } + bool containsReference() const + { + return containsBasicType(EbtReference); + } +#endif + + // Array editing methods. Array descriptors can be shared across + // type instances. This allows all uses of the same array + // to be updated at once. E.g., all nodes can be explicitly sized + // by tracking and correcting one implicit size. Or, all nodes + // can get the explicit size on a redeclaration that gives size. + // + // N.B.: Don't share with the shared symbol tables (symbols are + // marked as isReadOnly(). Such symbols with arrays that will be + // edited need to copyUp() on first use, so that + // A) the edits don't effect the shared symbol table, and + // B) the edits are shared across all users. + void updateArraySizes(const TType& type) + { + // For when we may already be sharing existing array descriptors, + // keeping the pointers the same, just updating the contents. + assert(arraySizes != nullptr); + assert(type.arraySizes != nullptr); + *arraySizes = *type.arraySizes; + } + void copyArraySizes(const TArraySizes& s) + { + // For setting a fresh new set of array sizes, not yet worrying about sharing. + arraySizes = new TArraySizes; + *arraySizes = s; + } + void transferArraySizes(TArraySizes* s) + { + // For setting an already allocated set of sizes that this type can use + // (no copy made). + arraySizes = s; + } + void clearArraySizes() + { + arraySizes = nullptr; + } + + // Add inner array sizes, to any existing sizes, via copy; the + // sizes passed in can still be reused for other purposes. + void copyArrayInnerSizes(const TArraySizes* s) + { + if (s != nullptr) { + if (arraySizes == nullptr) + copyArraySizes(*s); + else + arraySizes->addInnerSizes(*s); + } + } + void changeOuterArraySize(int s) { arraySizes->changeOuterSize(s); } + + // Recursively make the implicit array size the explicit array size. + // Expicit arrays are compile-time or link-time sized, never run-time sized. + // Sometimes, policy calls for an array to be run-time sized even if it was + // never variably indexed: Don't turn a 'skipNonvariablyIndexed' array into + // an explicit array. + void adoptImplicitArraySizes(bool skipNonvariablyIndexed) + { + if (isUnsizedArray() && !(skipNonvariablyIndexed || isArrayVariablyIndexed())) + changeOuterArraySize(getImplicitArraySize()); + // For multi-dim per-view arrays, set unsized inner dimension size to 1 + if (qualifier.isPerView() && arraySizes && arraySizes->isInnerUnsized()) + arraySizes->clearInnerUnsized(); + if (isStruct() && structure->size() > 0) { + int lastMember = (int)structure->size() - 1; + for (int i = 0; i < lastMember; ++i) + (*structure)[i].type->adoptImplicitArraySizes(false); + // implement the "last member of an SSBO" policy + (*structure)[lastMember].type->adoptImplicitArraySizes(getQualifier().storage == EvqBuffer); + } + } + + + void updateTypeParameters(const TType& type) + { + // For when we may already be sharing existing array descriptors, + // keeping the pointers the same, just updating the contents. + assert(typeParameters != nullptr); + assert(type.typeParameters != nullptr); + *typeParameters = *type.typeParameters; + } + void copyTypeParameters(const TArraySizes& s) + { + // For setting a fresh new set of type parameters, not yet worrying about sharing. + typeParameters = new TArraySizes; + *typeParameters = s; + } + void transferTypeParameters(TArraySizes* s) + { + // For setting an already allocated set of sizes that this type can use + // (no copy made). + typeParameters = s; + } + void clearTypeParameters() + { + typeParameters = nullptr; + } + + // Add inner array sizes, to any existing sizes, via copy; the + // sizes passed in can still be reused for other purposes. + void copyTypeParametersInnerSizes(const TArraySizes* s) + { + if (s != nullptr) { + if (typeParameters == nullptr) + copyTypeParameters(*s); + else + typeParameters->addInnerSizes(*s); + } + } + + + + const char* getBasicString() const + { + return TType::getBasicString(basicType); + } + + static const char* getBasicString(TBasicType t) + { + switch (t) { + case EbtFloat: return "float"; + case EbtInt: return "int"; + case EbtUint: return "uint"; + case EbtSampler: return "sampler/image"; +#ifndef GLSLANG_WEB + case EbtVoid: return "void"; + case EbtDouble: return "double"; + case EbtFloat16: return "float16_t"; + case EbtInt8: return "int8_t"; + case EbtUint8: return "uint8_t"; + case EbtInt16: return "int16_t"; + case EbtUint16: return "uint16_t"; + case EbtInt64: return "int64_t"; + case EbtUint64: return "uint64_t"; + case EbtBool: return "bool"; + case EbtAtomicUint: return "atomic_uint"; + case EbtStruct: return "structure"; + case EbtBlock: return "block"; + case EbtAccStruct: return "accelerationStructureNV"; + case EbtRayQuery: return "rayQueryEXT"; + case EbtReference: return "reference"; +#endif + default: return "unknown type"; + } + } + +#ifdef GLSLANG_WEB + TString getCompleteString() const { return ""; } + const char* getStorageQualifierString() const { return ""; } + const char* getBuiltInVariableString() const { return ""; } + const char* getPrecisionQualifierString() const { return ""; } + TString getBasicTypeString() const { return ""; } +#else + TString getCompleteString() const + { + TString typeString; + + const auto appendStr = [&](const char* s) { typeString.append(s); }; + const auto appendUint = [&](unsigned int u) { typeString.append(std::to_string(u).c_str()); }; + const auto appendInt = [&](int i) { typeString.append(std::to_string(i).c_str()); }; + + if (qualifier.hasLayout()) { + // To reduce noise, skip this if the only layout is an xfb_buffer + // with no triggering xfb_offset. + TQualifier noXfbBuffer = qualifier; + noXfbBuffer.layoutXfbBuffer = TQualifier::layoutXfbBufferEnd; + if (noXfbBuffer.hasLayout()) { + appendStr("layout("); + if (qualifier.hasAnyLocation()) { + appendStr(" location="); + appendUint(qualifier.layoutLocation); + if (qualifier.hasComponent()) { + appendStr(" component="); + appendUint(qualifier.layoutComponent); + } + if (qualifier.hasIndex()) { + appendStr(" index="); + appendUint(qualifier.layoutIndex); + } + } + if (qualifier.hasSet()) { + appendStr(" set="); + appendUint(qualifier.layoutSet); + } + if (qualifier.hasBinding()) { + appendStr(" binding="); + appendUint(qualifier.layoutBinding); + } + if (qualifier.hasStream()) { + appendStr(" stream="); + appendUint(qualifier.layoutStream); + } + if (qualifier.hasMatrix()) { + appendStr(" "); + appendStr(TQualifier::getLayoutMatrixString(qualifier.layoutMatrix)); + } + if (qualifier.hasPacking()) { + appendStr(" "); + appendStr(TQualifier::getLayoutPackingString(qualifier.layoutPacking)); + } + if (qualifier.hasOffset()) { + appendStr(" offset="); + appendInt(qualifier.layoutOffset); + } + if (qualifier.hasAlign()) { + appendStr(" align="); + appendInt(qualifier.layoutAlign); + } + if (qualifier.hasFormat()) { + appendStr(" "); + appendStr(TQualifier::getLayoutFormatString(qualifier.layoutFormat)); + } + if (qualifier.hasXfbBuffer() && qualifier.hasXfbOffset()) { + appendStr(" xfb_buffer="); + appendUint(qualifier.layoutXfbBuffer); + } + if (qualifier.hasXfbOffset()) { + appendStr(" xfb_offset="); + appendUint(qualifier.layoutXfbOffset); + } + if (qualifier.hasXfbStride()) { + appendStr(" xfb_stride="); + appendUint(qualifier.layoutXfbStride); + } + if (qualifier.hasAttachment()) { + appendStr(" input_attachment_index="); + appendUint(qualifier.layoutAttachment); + } + if (qualifier.hasSpecConstantId()) { + appendStr(" constant_id="); + appendUint(qualifier.layoutSpecConstantId); + } + if (qualifier.layoutPushConstant) + appendStr(" push_constant"); + if (qualifier.layoutBufferReference) + appendStr(" buffer_reference"); + if (qualifier.hasBufferReferenceAlign()) { + appendStr(" buffer_reference_align="); + appendUint(1u << qualifier.layoutBufferReferenceAlign); + } + + if (qualifier.layoutPassthrough) + appendStr(" passthrough"); + if (qualifier.layoutViewportRelative) + appendStr(" layoutViewportRelative"); + if (qualifier.layoutSecondaryViewportRelativeOffset != -2048) { + appendStr(" layoutSecondaryViewportRelativeOffset="); + appendInt(qualifier.layoutSecondaryViewportRelativeOffset); + } + if (qualifier.layoutShaderRecord) + appendStr(" shaderRecordNV"); + + appendStr(")"); + } + } + + if (qualifier.invariant) + appendStr(" invariant"); + if (qualifier.noContraction) + appendStr(" noContraction"); + if (qualifier.centroid) + appendStr(" centroid"); + if (qualifier.smooth) + appendStr(" smooth"); + if (qualifier.flat) + appendStr(" flat"); + if (qualifier.nopersp) + appendStr(" noperspective"); + if (qualifier.explicitInterp) + appendStr(" __explicitInterpAMD"); + if (qualifier.pervertexNV) + appendStr(" pervertexNV"); + if (qualifier.perPrimitiveNV) + appendStr(" perprimitiveNV"); + if (qualifier.perViewNV) + appendStr(" perviewNV"); + if (qualifier.perTaskNV) + appendStr(" taskNV"); + if (qualifier.patch) + appendStr(" patch"); + if (qualifier.sample) + appendStr(" sample"); + if (qualifier.coherent) + appendStr(" coherent"); + if (qualifier.devicecoherent) + appendStr(" devicecoherent"); + if (qualifier.queuefamilycoherent) + appendStr(" queuefamilycoherent"); + if (qualifier.workgroupcoherent) + appendStr(" workgroupcoherent"); + if (qualifier.subgroupcoherent) + appendStr(" subgroupcoherent"); + if (qualifier.shadercallcoherent) + appendStr(" shadercallcoherent"); + if (qualifier.nonprivate) + appendStr(" nonprivate"); + if (qualifier.volatil) + appendStr(" volatile"); + if (qualifier.restrict) + appendStr(" restrict"); + if (qualifier.readonly) + appendStr(" readonly"); + if (qualifier.writeonly) + appendStr(" writeonly"); + if (qualifier.specConstant) + appendStr(" specialization-constant"); + if (qualifier.nonUniform) + appendStr(" nonuniform"); + appendStr(" "); + appendStr(getStorageQualifierString()); + if (isArray()) { + for(int i = 0; i < (int)arraySizes->getNumDims(); ++i) { + int size = arraySizes->getDimSize(i); + if (size == UnsizedArraySize && i == 0 && arraySizes->isVariablyIndexed()) + appendStr(" runtime-sized array of"); + else { + if (size == UnsizedArraySize) { + appendStr(" unsized"); + if (i == 0) { + appendStr(" "); + appendInt(arraySizes->getImplicitSize()); + } + } else { + appendStr(" "); + appendInt(arraySizes->getDimSize(i)); + } + appendStr("-element array of"); + } + } + } + if (isParameterized()) { + appendStr("<"); + for(int i = 0; i < (int)typeParameters->getNumDims(); ++i) { + appendInt(typeParameters->getDimSize(i)); + if (i != (int)typeParameters->getNumDims() - 1) + appendStr(", "); + } + appendStr(">"); + } + if (qualifier.precision != EpqNone) { + appendStr(" "); + appendStr(getPrecisionQualifierString()); + } + if (isMatrix()) { + appendStr(" "); + appendInt(matrixCols); + appendStr("X"); + appendInt(matrixRows); + appendStr(" matrix of"); + } else if (isVector()) { + appendStr(" "); + appendInt(vectorSize); + appendStr("-component vector of"); + } + + appendStr(" "); + typeString.append(getBasicTypeString()); + + if (qualifier.builtIn != EbvNone) { + appendStr(" "); + appendStr(getBuiltInVariableString()); + } + + // Add struct/block members + if (isStruct() && structure) { + appendStr("{"); + bool hasHiddenMember = true; + for (size_t i = 0; i < structure->size(); ++i) { + if (! (*structure)[i].type->hiddenMember()) { + if (!hasHiddenMember) + appendStr(", "); + typeString.append((*structure)[i].type->getCompleteString()); + typeString.append(" "); + typeString.append((*structure)[i].type->getFieldName()); + hasHiddenMember = false; + } + } + appendStr("}"); + } + + return typeString; + } + + TString getBasicTypeString() const + { + if (basicType == EbtSampler) + return sampler.getString(); + else + return getBasicString(); + } + + const char* getStorageQualifierString() const { return GetStorageQualifierString(qualifier.storage); } + const char* getBuiltInVariableString() const { return GetBuiltInVariableString(qualifier.builtIn); } + const char* getPrecisionQualifierString() const { return GetPrecisionQualifierString(qualifier.precision); } +#endif + + const TTypeList* getStruct() const { assert(isStruct()); return structure; } + void setStruct(TTypeList* s) { assert(isStruct()); structure = s; } + TTypeList* getWritableStruct() const { assert(isStruct()); return structure; } // This should only be used when known to not be sharing with other threads + void setBasicType(const TBasicType& t) { basicType = t; } + + int computeNumComponents() const + { + int components = 0; + + if (getBasicType() == EbtStruct || getBasicType() == EbtBlock) { + for (TTypeList::const_iterator tl = getStruct()->begin(); tl != getStruct()->end(); tl++) + components += ((*tl).type)->computeNumComponents(); + } else if (matrixCols) + components = matrixCols * matrixRows; + else + components = vectorSize; + + if (arraySizes != nullptr) { + components *= arraySizes->getCumulativeSize(); + } + + return components; + } + + // append this type's mangled name to the passed in 'name' + void appendMangledName(TString& name) const + { + buildMangledName(name); + name += ';' ; + } + + // Do two structure types match? They could be declared independently, + // in different places, but still might satisfy the definition of matching. + // From the spec: + // + // "Structures must have the same name, sequence of type names, and + // type definitions, and member names to be considered the same type. + // This rule applies recursively for nested or embedded types." + // + bool sameStructType(const TType& right) const + { + // Most commonly, they are both nullptr, or the same pointer to the same actual structure + if ((!isStruct() && !right.isStruct()) || + (isStruct() && right.isStruct() && structure == right.structure)) + return true; + + // Both being nullptr was caught above, now they both have to be structures of the same number of elements + if (!isStruct() || !right.isStruct() || + structure->size() != right.structure->size()) + return false; + + // Structure names have to match + if (*typeName != *right.typeName) + return false; + + // Compare the names and types of all the members, which have to match + for (unsigned int i = 0; i < structure->size(); ++i) { + if ((*structure)[i].type->getFieldName() != (*right.structure)[i].type->getFieldName()) + return false; + + if (*(*structure)[i].type != *(*right.structure)[i].type) + return false; + } + + return true; + } + + bool sameReferenceType(const TType& right) const + { + if (isReference() != right.isReference()) + return false; + + if (!isReference() && !right.isReference()) + return true; + + assert(referentType != nullptr); + assert(right.referentType != nullptr); + + if (referentType == right.referentType) + return true; + + return *referentType == *right.referentType; + } + + // See if two types match, in all aspects except arrayness + bool sameElementType(const TType& right) const + { + return basicType == right.basicType && sameElementShape(right); + } + + // See if two type's arrayness match + bool sameArrayness(const TType& right) const + { + return ((arraySizes == nullptr && right.arraySizes == nullptr) || + (arraySizes != nullptr && right.arraySizes != nullptr && *arraySizes == *right.arraySizes)); + } + + // See if two type's arrayness match in everything except their outer dimension + bool sameInnerArrayness(const TType& right) const + { + assert(arraySizes != nullptr && right.arraySizes != nullptr); + return arraySizes->sameInnerArrayness(*right.arraySizes); + } + + // See if two type's parameters match + bool sameTypeParameters(const TType& right) const + { + return ((typeParameters == nullptr && right.typeParameters == nullptr) || + (typeParameters != nullptr && right.typeParameters != nullptr && *typeParameters == *right.typeParameters)); + } + + // See if two type's elements match in all ways except basic type + bool sameElementShape(const TType& right) const + { + return sampler == right.sampler && + vectorSize == right.vectorSize && + matrixCols == right.matrixCols && + matrixRows == right.matrixRows && + vector1 == right.vector1 && + isCoopMat() == right.isCoopMat() && + sameStructType(right) && + sameReferenceType(right); + } + + // See if a cooperative matrix type parameter with unspecified parameters is + // an OK function parameter + bool coopMatParameterOK(const TType& right) const + { + return isCoopMat() && right.isCoopMat() && (getBasicType() == right.getBasicType()) && + typeParameters == nullptr && right.typeParameters != nullptr; + } + + bool sameCoopMatBaseType(const TType &right) const { + bool rv = coopmat && right.coopmat; + if (getBasicType() == EbtFloat || getBasicType() == EbtFloat16) + rv = right.getBasicType() == EbtFloat || right.getBasicType() == EbtFloat16; + else if (getBasicType() == EbtUint || getBasicType() == EbtUint8) + rv = right.getBasicType() == EbtUint || right.getBasicType() == EbtUint8; + else if (getBasicType() == EbtInt || getBasicType() == EbtInt8) + rv = right.getBasicType() == EbtInt || right.getBasicType() == EbtInt8; + else + rv = false; + return rv; + } + + + // See if two types match in all ways (just the actual type, not qualification) + bool operator==(const TType& right) const + { + return sameElementType(right) && sameArrayness(right) && sameTypeParameters(right); + } + + bool operator!=(const TType& right) const + { + return ! operator==(right); + } + + unsigned int getBufferReferenceAlignment() const + { +#ifndef GLSLANG_WEB + if (getBasicType() == glslang::EbtReference) { + return getReferentType()->getQualifier().hasBufferReferenceAlign() ? + (1u << getReferentType()->getQualifier().layoutBufferReferenceAlign) : 16u; + } +#endif + return 0; + } + +protected: + // Require consumer to pick between deep copy and shallow copy. + TType(const TType& type); + TType& operator=(const TType& type); + + // Recursively copy a type graph, while preserving the graph-like + // quality. That is, don't make more than one copy of a structure that + // gets reused multiple times in the type graph. + void deepCopy(const TType& copyOf, TMap& copiedMap) + { + shallowCopy(copyOf); + + if (copyOf.arraySizes) { + arraySizes = new TArraySizes; + *arraySizes = *copyOf.arraySizes; + } + + if (copyOf.typeParameters) { + typeParameters = new TArraySizes; + *typeParameters = *copyOf.typeParameters; + } + + if (copyOf.isStruct() && copyOf.structure) { + auto prevCopy = copiedMap.find(copyOf.structure); + if (prevCopy != copiedMap.end()) + structure = prevCopy->second; + else { + structure = new TTypeList; + copiedMap[copyOf.structure] = structure; + for (unsigned int i = 0; i < copyOf.structure->size(); ++i) { + TTypeLoc typeLoc; + typeLoc.loc = (*copyOf.structure)[i].loc; + typeLoc.type = new TType(); + typeLoc.type->deepCopy(*(*copyOf.structure)[i].type, copiedMap); + structure->push_back(typeLoc); + } + } + } + + if (copyOf.fieldName) + fieldName = NewPoolTString(copyOf.fieldName->c_str()); + if (copyOf.typeName) + typeName = NewPoolTString(copyOf.typeName->c_str()); + } + + + void buildMangledName(TString&) const; + + TBasicType basicType : 8; + int vectorSize : 4; // 1 means either scalar or 1-component vector; see vector1 to disambiguate. + int matrixCols : 4; + int matrixRows : 4; + bool vector1 : 1; // Backward-compatible tracking of a 1-component vector distinguished from a scalar. + // GLSL 4.5 never has a 1-component vector; so this will always be false until such + // functionality is added. + // HLSL does have a 1-component vectors, so this will be true to disambiguate + // from a scalar. + bool coopmat : 1; + TQualifier qualifier; + + TArraySizes* arraySizes; // nullptr unless an array; can be shared across types + // A type can't be both a structure (EbtStruct/EbtBlock) and a reference (EbtReference), so + // conserve space by making these a union + union { + TTypeList* structure; // invalid unless this is a struct; can be shared across types + TType *referentType; // invalid unless this is an EbtReference + }; + TString *fieldName; // for structure field names + TString *typeName; // for structure type name + TSampler sampler; + TArraySizes* typeParameters;// nullptr unless a parameterized type; can be shared across types +}; + +} // end namespace glslang + +#endif // _TYPES_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/Include/arrays.h b/armorcore/tools/to_spirv/glslang/glslang/Include/arrays.h new file mode 100644 index 000000000..7f047d9fb --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Include/arrays.h @@ -0,0 +1,341 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Implement types for tracking GLSL arrays, arrays of arrays, etc. +// + +#ifndef _ARRAYS_INCLUDED +#define _ARRAYS_INCLUDED + +#include + +namespace glslang { + +// This is used to mean there is no size yet (unsized), it is waiting to get a size from somewhere else. +const int UnsizedArraySize = 0; + +class TIntermTyped; +extern bool SameSpecializationConstants(TIntermTyped*, TIntermTyped*); + +// Specialization constants need both a nominal size and a node that defines +// the specialization constant being used. Array types are the same when their +// size and specialization constant nodes are the same. +struct TArraySize { + unsigned int size; + TIntermTyped* node; // nullptr means no specialization constant node + bool operator==(const TArraySize& rhs) const + { + if (size != rhs.size) + return false; + if (node == nullptr || rhs.node == nullptr) + return node == rhs.node; + + return SameSpecializationConstants(node, rhs.node); + } +}; + +// +// TSmallArrayVector is used as the container for the set of sizes in TArraySizes. +// It has generic-container semantics, while TArraySizes has array-of-array semantics. +// That is, TSmallArrayVector should be more focused on mechanism and TArraySizes on policy. +// +struct TSmallArrayVector { + // + // TODO: memory: TSmallArrayVector is intended to be smaller. + // Almost all arrays could be handled by two sizes each fitting + // in 16 bits, needing a real vector only in the cases where there + // are more than 3 sizes or a size needing more than 16 bits. + // + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TSmallArrayVector() : sizes(nullptr) { } + virtual ~TSmallArrayVector() { dealloc(); } + + // For breaking into two non-shared copies, independently modifiable. + TSmallArrayVector& operator=(const TSmallArrayVector& from) + { + if (from.sizes == nullptr) + sizes = nullptr; + else { + alloc(); + *sizes = *from.sizes; + } + + return *this; + } + + int size() const + { + if (sizes == nullptr) + return 0; + return (int)sizes->size(); + } + + unsigned int frontSize() const + { + assert(sizes != nullptr && sizes->size() > 0); + return sizes->front().size; + } + + TIntermTyped* frontNode() const + { + assert(sizes != nullptr && sizes->size() > 0); + return sizes->front().node; + } + + void changeFront(unsigned int s) + { + assert(sizes != nullptr); + // this should only happen for implicitly sized arrays, not specialization constants + assert(sizes->front().node == nullptr); + sizes->front().size = s; + } + + void push_back(unsigned int e, TIntermTyped* n) + { + alloc(); + TArraySize pair = { e, n }; + sizes->push_back(pair); + } + + void push_back(const TSmallArrayVector& newDims) + { + alloc(); + sizes->insert(sizes->end(), newDims.sizes->begin(), newDims.sizes->end()); + } + + void pop_front() + { + assert(sizes != nullptr && sizes->size() > 0); + if (sizes->size() == 1) + dealloc(); + else + sizes->erase(sizes->begin()); + } + + // 'this' should currently not be holding anything, and copyNonFront + // will make it hold a copy of all but the first element of rhs. + // (This would be useful for making a type that is dereferenced by + // one dimension.) + void copyNonFront(const TSmallArrayVector& rhs) + { + assert(sizes == nullptr); + if (rhs.size() > 1) { + alloc(); + sizes->insert(sizes->begin(), rhs.sizes->begin() + 1, rhs.sizes->end()); + } + } + + unsigned int getDimSize(int i) const + { + assert(sizes != nullptr && (int)sizes->size() > i); + return (*sizes)[i].size; + } + + void setDimSize(int i, unsigned int size) const + { + assert(sizes != nullptr && (int)sizes->size() > i); + assert((*sizes)[i].node == nullptr); + (*sizes)[i].size = size; + } + + TIntermTyped* getDimNode(int i) const + { + assert(sizes != nullptr && (int)sizes->size() > i); + return (*sizes)[i].node; + } + + bool operator==(const TSmallArrayVector& rhs) const + { + if (sizes == nullptr && rhs.sizes == nullptr) + return true; + if (sizes == nullptr || rhs.sizes == nullptr) + return false; + return *sizes == *rhs.sizes; + } + bool operator!=(const TSmallArrayVector& rhs) const { return ! operator==(rhs); } + +protected: + TSmallArrayVector(const TSmallArrayVector&); + + void alloc() + { + if (sizes == nullptr) + sizes = new TVector; + } + void dealloc() + { + delete sizes; + sizes = nullptr; + } + + TVector* sizes; // will either hold such a pointer, or in the future, hold the two array sizes +}; + +// +// Represent an array, or array of arrays, to arbitrary depth. This is not +// done through a hierarchy of types in a type tree, rather all contiguous arrayness +// in the type hierarchy is localized into this single cumulative object. +// +// The arrayness in TTtype is a pointer, so that it can be non-allocated and zero +// for the vast majority of types that are non-array types. +// +// Order Policy: these are all identical: +// - left to right order within a contiguous set of ...[..][..][..]... in the source language +// - index order 0, 1, 2, ... within the 'sizes' member below +// - outer-most to inner-most +// +struct TArraySizes { + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TArraySizes() : implicitArraySize(1), variablyIndexed(false) { } + + // For breaking into two non-shared copies, independently modifiable. + TArraySizes& operator=(const TArraySizes& from) + { + implicitArraySize = from.implicitArraySize; + variablyIndexed = from.variablyIndexed; + sizes = from.sizes; + + return *this; + } + + // translate from array-of-array semantics to container semantics + int getNumDims() const { return sizes.size(); } + int getDimSize(int dim) const { return sizes.getDimSize(dim); } + TIntermTyped* getDimNode(int dim) const { return sizes.getDimNode(dim); } + void setDimSize(int dim, int size) { sizes.setDimSize(dim, size); } + int getOuterSize() const { return sizes.frontSize(); } + TIntermTyped* getOuterNode() const { return sizes.frontNode(); } + int getCumulativeSize() const + { + int size = 1; + for (int d = 0; d < sizes.size(); ++d) { + // this only makes sense in paths that have a known array size + assert(sizes.getDimSize(d) != UnsizedArraySize); + size *= sizes.getDimSize(d); + } + return size; + } + void addInnerSize() { addInnerSize((unsigned)UnsizedArraySize); } + void addInnerSize(int s) { addInnerSize((unsigned)s, nullptr); } + void addInnerSize(int s, TIntermTyped* n) { sizes.push_back((unsigned)s, n); } + void addInnerSize(TArraySize pair) { + sizes.push_back(pair.size, pair.node); + } + void addInnerSizes(const TArraySizes& s) { sizes.push_back(s.sizes); } + void changeOuterSize(int s) { sizes.changeFront((unsigned)s); } + int getImplicitSize() const { return implicitArraySize; } + void updateImplicitSize(int s) { implicitArraySize = std::max(implicitArraySize, s); } + bool isInnerUnsized() const + { + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimSize(d) == (unsigned)UnsizedArraySize) + return true; + } + + return false; + } + bool clearInnerUnsized() + { + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimSize(d) == (unsigned)UnsizedArraySize) + setDimSize(d, 1); + } + + return false; + } + bool isInnerSpecialization() const + { + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimNode(d) != nullptr) + return true; + } + + return false; + } + bool isOuterSpecialization() + { + return sizes.getDimNode(0) != nullptr; + } + + bool hasUnsized() const { return getOuterSize() == UnsizedArraySize || isInnerUnsized(); } + bool isSized() const { return getOuterSize() != UnsizedArraySize; } + void dereference() { sizes.pop_front(); } + void copyDereferenced(const TArraySizes& rhs) + { + assert(sizes.size() == 0); + if (rhs.sizes.size() > 1) + sizes.copyNonFront(rhs.sizes); + } + + bool sameInnerArrayness(const TArraySizes& rhs) const + { + if (sizes.size() != rhs.sizes.size()) + return false; + + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimSize(d) != rhs.sizes.getDimSize(d) || + sizes.getDimNode(d) != rhs.sizes.getDimNode(d)) + return false; + } + + return true; + } + + void setVariablyIndexed() { variablyIndexed = true; } + bool isVariablyIndexed() const { return variablyIndexed; } + + bool operator==(const TArraySizes& rhs) const { return sizes == rhs.sizes; } + bool operator!=(const TArraySizes& rhs) const { return sizes != rhs.sizes; } + +protected: + TSmallArrayVector sizes; + + TArraySizes(const TArraySizes&); + + // For tracking maximum referenced compile-time constant index. + // Applies only to the outer-most dimension. Potentially becomes + // the implicit size of the array, if not variably indexed and + // otherwise legal. + int implicitArraySize; + bool variablyIndexed; // true if array is indexed with a non compile-time constant +}; + +} // end namespace glslang + +#endif // _ARRAYS_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/Include/glslang_c_shader_types.h b/armorcore/tools/to_spirv/glslang/glslang/Include/glslang_c_shader_types.h new file mode 100644 index 000000000..769f4c4a8 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Include/glslang_c_shader_types.h @@ -0,0 +1,182 @@ +/** + This code is based on the glslang_c_interface implementation by Viktor Latypov +**/ + +/** +BSD 2-Clause License + +Copyright (c) 2019, Viktor Latypov +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#ifndef C_SHADER_TYPES_H_INCLUDED +#define C_SHADER_TYPES_H_INCLUDED + +#define LAST_ELEMENT_MARKER(x) x + +/* EShLanguage counterpart */ +typedef enum { + GLSLANG_STAGE_VERTEX, + GLSLANG_STAGE_TESSCONTROL, + GLSLANG_STAGE_TESSEVALUATION, + GLSLANG_STAGE_GEOMETRY, + GLSLANG_STAGE_FRAGMENT, + GLSLANG_STAGE_COMPUTE, + GLSLANG_STAGE_RAYGEN_NV, + GLSLANG_STAGE_INTERSECT_NV, + GLSLANG_STAGE_ANYHIT_NV, + GLSLANG_STAGE_CLOSESTHIT_NV, + GLSLANG_STAGE_MISS_NV, + GLSLANG_STAGE_CALLABLE_NV, + GLSLANG_STAGE_TASK_NV, + GLSLANG_STAGE_MESH_NV, + LAST_ELEMENT_MARKER(GLSLANG_STAGE_COUNT), +} glslang_stage_t; // would be better as stage, but this is ancient now + +/* EShLanguageMask counterpart */ +typedef enum { + GLSLANG_STAGE_VERTEX_MASK = (1 << GLSLANG_STAGE_VERTEX), + GLSLANG_STAGE_TESSCONTROL_MASK = (1 << GLSLANG_STAGE_TESSCONTROL), + GLSLANG_STAGE_TESSEVALUATION_MASK = (1 << GLSLANG_STAGE_TESSEVALUATION), + GLSLANG_STAGE_GEOMETRY_MASK = (1 << GLSLANG_STAGE_GEOMETRY), + GLSLANG_STAGE_FRAGMENT_MASK = (1 << GLSLANG_STAGE_FRAGMENT), + GLSLANG_STAGE_COMPUTE_MASK = (1 << GLSLANG_STAGE_COMPUTE), + GLSLANG_STAGE_RAYGEN_NV_MASK = (1 << GLSLANG_STAGE_RAYGEN_NV), + GLSLANG_STAGE_INTERSECT_NV_MASK = (1 << GLSLANG_STAGE_INTERSECT_NV), + GLSLANG_STAGE_ANYHIT_NV_MASK = (1 << GLSLANG_STAGE_ANYHIT_NV), + GLSLANG_STAGE_CLOSESTHIT_NV_MASK = (1 << GLSLANG_STAGE_CLOSESTHIT_NV), + GLSLANG_STAGE_MISS_NV_MASK = (1 << GLSLANG_STAGE_MISS_NV), + GLSLANG_STAGE_CALLABLE_NV_MASK = (1 << GLSLANG_STAGE_CALLABLE_NV), + GLSLANG_STAGE_TASK_NV_MASK = (1 << GLSLANG_STAGE_TASK_NV), + GLSLANG_STAGE_MESH_NV_MASK = (1 << GLSLANG_STAGE_MESH_NV), + LAST_ELEMENT_MARKER(GLSLANG_STAGE_MASK_COUNT), +} glslang_stage_mask_t; + +/* EShSource counterpart */ +typedef enum { + GLSLANG_SOURCE_NONE, + GLSLANG_SOURCE_GLSL, + GLSLANG_SOURCE_HLSL, + LAST_ELEMENT_MARKER(GLSLANG_SOURCE_COUNT), +} glslang_source_t; + +/* EShClient counterpart */ +typedef enum { + GLSLANG_CLIENT_NONE, + GLSLANG_CLIENT_VULKAN, + GLSLANG_CLIENT_OPENGL, + LAST_ELEMENT_MARKER(GLSLANG_CLIENT_COUNT), +} glslang_client_t; + +/* EShTargetLanguage counterpart */ +typedef enum { + GLSLANG_TARGET_NONE, + GLSLANG_TARGET_SPV, + LAST_ELEMENT_MARKER(GLSLANG_TARGET_COUNT), +} glslang_target_language_t; + +/* SH_TARGET_ClientVersion counterpart */ +typedef enum { + GLSLANG_TARGET_VULKAN_1_0 = (1 << 22), + GLSLANG_TARGET_VULKAN_1_1 = (1 << 22) | (1 << 12), + GLSLANG_TARGET_OPENGL_450 = 450, + LAST_ELEMENT_MARKER(GLSLANG_TARGET_CLIENT_VERSION_COUNT), +} glslang_target_client_version_t; + +/* SH_TARGET_LanguageVersion counterpart */ +typedef enum { + GLSLANG_TARGET_SPV_1_0 = (1 << 16), + GLSLANG_TARGET_SPV_1_1 = (1 << 16) | (1 << 8), + GLSLANG_TARGET_SPV_1_2 = (1 << 16) | (2 << 8), + GLSLANG_TARGET_SPV_1_3 = (1 << 16) | (3 << 8), + GLSLANG_TARGET_SPV_1_4 = (1 << 16) | (4 << 8), + GLSLANG_TARGET_SPV_1_5 = (1 << 16) | (5 << 8), + LAST_ELEMENT_MARKER(GLSLANG_TARGET_LANGUAGE_VERSION_COUNT), +} glslang_target_language_version_t; + +/* EShExecutable counterpart */ +typedef enum { GLSLANG_EX_VERTEX_FRAGMENT, GLSLANG_EX_FRAGMENT } glslang_executable_t; + +/* EShOptimizationLevel counterpart */ +typedef enum { + GLSLANG_OPT_NO_GENERATION, + GLSLANG_OPT_NONE, + GLSLANG_OPT_SIMPLE, + GLSLANG_OPT_FULL, + LAST_ELEMENT_MARKER(GLSLANG_OPT_LEVEL_COUNT), +} glslang_optimization_level_t; + +/* EShTextureSamplerTransformMode counterpart */ +typedef enum { + GLSLANG_TEX_SAMP_TRANS_KEEP, + GLSLANG_TEX_SAMP_TRANS_UPGRADE_TEXTURE_REMOVE_SAMPLER, + LAST_ELEMENT_MARKER(GLSLANG_TEX_SAMP_TRANS_COUNT), +} glslang_texture_sampler_transform_mode_t; + +/* EShMessages counterpart */ +typedef enum { + GLSLANG_MSG_DEFAULT_BIT = 0, + GLSLANG_MSG_RELAXED_ERRORS_BIT = (1 << 0), + GLSLANG_MSG_SUPPRESS_WARNINGS_BIT = (1 << 1), + GLSLANG_MSG_AST_BIT = (1 << 2), + GLSLANG_MSG_SPV_RULES_BIT = (1 << 3), + GLSLANG_MSG_VULKAN_RULES_BIT = (1 << 4), + GLSLANG_MSG_ONLY_PREPROCESSOR_BIT = (1 << 5), + GLSLANG_MSG_READ_HLSL_BIT = (1 << 6), + GLSLANG_MSG_CASCADING_ERRORS_BIT = (1 << 7), + GLSLANG_MSG_KEEP_UNCALLED_BIT = (1 << 8), + GLSLANG_MSG_HLSL_OFFSETS_BIT = (1 << 9), + GLSLANG_MSG_DEBUG_INFO_BIT = (1 << 10), + GLSLANG_MSG_HLSL_ENABLE_16BIT_TYPES_BIT = (1 << 11), + GLSLANG_MSG_HLSL_LEGALIZATION_BIT = (1 << 12), + GLSLANG_MSG_HLSL_DX9_COMPATIBLE_BIT = (1 << 13), + GLSLANG_MSG_BUILTIN_SYMBOL_TABLE_BIT = (1 << 14), + LAST_ELEMENT_MARKER(GLSLANG_MSG_COUNT), +} glslang_messages_t; + +/* EShReflectionOptions counterpart */ +typedef enum { + GLSLANG_REFLECTION_DEFAULT_BIT = 0, + GLSLANG_REFLECTION_STRICT_ARRAY_SUFFIX_BIT = (1 << 0), + GLSLANG_REFLECTION_BASIC_ARRAY_SUFFIX_BIT = (1 << 1), + GLSLANG_REFLECTION_INTERMEDIATE_IOO_BIT = (1 << 2), + GLSLANG_REFLECTION_SEPARATE_BUFFERS_BIT = (1 << 3), + GLSLANG_REFLECTION_ALL_BLOCK_VARIABLES_BIT = (1 << 4), + GLSLANG_REFLECTION_UNWRAP_IO_BLOCKS_BIT = (1 << 5), + LAST_ELEMENT_MARKER(GLSLANG_REFLECTION_COUNT), +} glslang_reflection_options_t; + +/* EProfile counterpart (from Versions.h) */ +typedef enum { + GLSLANG_BAD_PROFILE = 0, + GLSLANG_NO_PROFILE = (1 << 0), + GLSLANG_CORE_PROFILE = (1 << 1), + GLSLANG_COMPATIBILITY_PROFILE = (1 << 2), + GLSLANG_ES_PROFILE = (1 << 3), + LAST_ELEMENT_MARKER(GLSLANG_PROFILE_COUNT), +} glslang_profile_t; + +#undef LAST_ELEMENT_MARKER + +#endif diff --git a/armorcore/tools/to_spirv/glslang/glslang/Include/intermediate.h b/armorcore/tools/to_spirv/glslang/glslang/Include/intermediate.h new file mode 100644 index 000000000..bf12fcf8f --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Include/intermediate.h @@ -0,0 +1,1805 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Definition of the in-memory high-level intermediate representation +// of shaders. This is a tree that parser creates. +// +// Nodes in the tree are defined as a hierarchy of classes derived from +// TIntermNode. Each is a node in a tree. There is no preset branching factor; +// each node can have it's own type of list of children. +// + +#ifndef __INTERMEDIATE_H +#define __INTERMEDIATE_H + +#if defined(_MSC_VER) && _MSC_VER >= 1900 + #pragma warning(disable : 4464) // relative include path contains '..' + #pragma warning(disable : 5026) // 'glslang::TIntermUnary': move constructor was implicitly defined as deleted +#endif + +#include "../Include/Common.h" +#include "../Include/Types.h" +#include "../Include/ConstantUnion.h" + +namespace glslang { + +class TIntermediate; + +// +// Operators used by the high-level (parse tree) representation. +// +enum TOperator { + EOpNull, // if in a node, should only mean a node is still being built + EOpSequence, // denotes a list of statements, or parameters, etc. + EOpLinkerObjects, // for aggregate node of objects the linker may need, if not reference by the rest of the AST + EOpFunctionCall, + EOpFunction, // For function definition + EOpParameters, // an aggregate listing the parameters to a function + + // + // Unary operators + // + + EOpNegative, + EOpLogicalNot, + EOpVectorLogicalNot, + EOpBitwiseNot, + + EOpPostIncrement, + EOpPostDecrement, + EOpPreIncrement, + EOpPreDecrement, + + EOpCopyObject, + + // (u)int* -> bool + EOpConvInt8ToBool, + EOpConvUint8ToBool, + EOpConvInt16ToBool, + EOpConvUint16ToBool, + EOpConvIntToBool, + EOpConvUintToBool, + EOpConvInt64ToBool, + EOpConvUint64ToBool, + + // float* -> bool + EOpConvFloat16ToBool, + EOpConvFloatToBool, + EOpConvDoubleToBool, + + // bool -> (u)int* + EOpConvBoolToInt8, + EOpConvBoolToUint8, + EOpConvBoolToInt16, + EOpConvBoolToUint16, + EOpConvBoolToInt, + EOpConvBoolToUint, + EOpConvBoolToInt64, + EOpConvBoolToUint64, + + // bool -> float* + EOpConvBoolToFloat16, + EOpConvBoolToFloat, + EOpConvBoolToDouble, + + // int8_t -> (u)int* + EOpConvInt8ToInt16, + EOpConvInt8ToInt, + EOpConvInt8ToInt64, + EOpConvInt8ToUint8, + EOpConvInt8ToUint16, + EOpConvInt8ToUint, + EOpConvInt8ToUint64, + + // uint8_t -> (u)int* + EOpConvUint8ToInt8, + EOpConvUint8ToInt16, + EOpConvUint8ToInt, + EOpConvUint8ToInt64, + EOpConvUint8ToUint16, + EOpConvUint8ToUint, + EOpConvUint8ToUint64, + + // int8_t -> float* + EOpConvInt8ToFloat16, + EOpConvInt8ToFloat, + EOpConvInt8ToDouble, + + // uint8_t -> float* + EOpConvUint8ToFloat16, + EOpConvUint8ToFloat, + EOpConvUint8ToDouble, + + // int16_t -> (u)int* + EOpConvInt16ToInt8, + EOpConvInt16ToInt, + EOpConvInt16ToInt64, + EOpConvInt16ToUint8, + EOpConvInt16ToUint16, + EOpConvInt16ToUint, + EOpConvInt16ToUint64, + + // uint16_t -> (u)int* + EOpConvUint16ToInt8, + EOpConvUint16ToInt16, + EOpConvUint16ToInt, + EOpConvUint16ToInt64, + EOpConvUint16ToUint8, + EOpConvUint16ToUint, + EOpConvUint16ToUint64, + + // int16_t -> float* + EOpConvInt16ToFloat16, + EOpConvInt16ToFloat, + EOpConvInt16ToDouble, + + // uint16_t -> float* + EOpConvUint16ToFloat16, + EOpConvUint16ToFloat, + EOpConvUint16ToDouble, + + // int32_t -> (u)int* + EOpConvIntToInt8, + EOpConvIntToInt16, + EOpConvIntToInt64, + EOpConvIntToUint8, + EOpConvIntToUint16, + EOpConvIntToUint, + EOpConvIntToUint64, + + // uint32_t -> (u)int* + EOpConvUintToInt8, + EOpConvUintToInt16, + EOpConvUintToInt, + EOpConvUintToInt64, + EOpConvUintToUint8, + EOpConvUintToUint16, + EOpConvUintToUint64, + + // int32_t -> float* + EOpConvIntToFloat16, + EOpConvIntToFloat, + EOpConvIntToDouble, + + // uint32_t -> float* + EOpConvUintToFloat16, + EOpConvUintToFloat, + EOpConvUintToDouble, + + // int64_t -> (u)int* + EOpConvInt64ToInt8, + EOpConvInt64ToInt16, + EOpConvInt64ToInt, + EOpConvInt64ToUint8, + EOpConvInt64ToUint16, + EOpConvInt64ToUint, + EOpConvInt64ToUint64, + + // uint64_t -> (u)int* + EOpConvUint64ToInt8, + EOpConvUint64ToInt16, + EOpConvUint64ToInt, + EOpConvUint64ToInt64, + EOpConvUint64ToUint8, + EOpConvUint64ToUint16, + EOpConvUint64ToUint, + + // int64_t -> float* + EOpConvInt64ToFloat16, + EOpConvInt64ToFloat, + EOpConvInt64ToDouble, + + // uint64_t -> float* + EOpConvUint64ToFloat16, + EOpConvUint64ToFloat, + EOpConvUint64ToDouble, + + // float16_t -> (u)int* + EOpConvFloat16ToInt8, + EOpConvFloat16ToInt16, + EOpConvFloat16ToInt, + EOpConvFloat16ToInt64, + EOpConvFloat16ToUint8, + EOpConvFloat16ToUint16, + EOpConvFloat16ToUint, + EOpConvFloat16ToUint64, + + // float16_t -> float* + EOpConvFloat16ToFloat, + EOpConvFloat16ToDouble, + + // float -> (u)int* + EOpConvFloatToInt8, + EOpConvFloatToInt16, + EOpConvFloatToInt, + EOpConvFloatToInt64, + EOpConvFloatToUint8, + EOpConvFloatToUint16, + EOpConvFloatToUint, + EOpConvFloatToUint64, + + // float -> float* + EOpConvFloatToFloat16, + EOpConvFloatToDouble, + + // float64 _t-> (u)int* + EOpConvDoubleToInt8, + EOpConvDoubleToInt16, + EOpConvDoubleToInt, + EOpConvDoubleToInt64, + EOpConvDoubleToUint8, + EOpConvDoubleToUint16, + EOpConvDoubleToUint, + EOpConvDoubleToUint64, + + // float64_t -> float* + EOpConvDoubleToFloat16, + EOpConvDoubleToFloat, + + // uint64_t <-> pointer + EOpConvUint64ToPtr, + EOpConvPtrToUint64, + + // uvec2 <-> pointer + EOpConvUvec2ToPtr, + EOpConvPtrToUvec2, + + // + // binary operations + // + + EOpAdd, + EOpSub, + EOpMul, + EOpDiv, + EOpMod, + EOpRightShift, + EOpLeftShift, + EOpAnd, + EOpInclusiveOr, + EOpExclusiveOr, + EOpEqual, + EOpNotEqual, + EOpVectorEqual, + EOpVectorNotEqual, + EOpLessThan, + EOpGreaterThan, + EOpLessThanEqual, + EOpGreaterThanEqual, + EOpComma, + + EOpVectorTimesScalar, + EOpVectorTimesMatrix, + EOpMatrixTimesVector, + EOpMatrixTimesScalar, + + EOpLogicalOr, + EOpLogicalXor, + EOpLogicalAnd, + + EOpIndexDirect, + EOpIndexIndirect, + EOpIndexDirectStruct, + + EOpVectorSwizzle, + + EOpMethod, + EOpScoping, + + // + // Built-in functions mapped to operators + // + + EOpRadians, + EOpDegrees, + EOpSin, + EOpCos, + EOpTan, + EOpAsin, + EOpAcos, + EOpAtan, + EOpSinh, + EOpCosh, + EOpTanh, + EOpAsinh, + EOpAcosh, + EOpAtanh, + + EOpPow, + EOpExp, + EOpLog, + EOpExp2, + EOpLog2, + EOpSqrt, + EOpInverseSqrt, + + EOpAbs, + EOpSign, + EOpFloor, + EOpTrunc, + EOpRound, + EOpRoundEven, + EOpCeil, + EOpFract, + EOpModf, + EOpMin, + EOpMax, + EOpClamp, + EOpMix, + EOpStep, + EOpSmoothStep, + + EOpIsNan, + EOpIsInf, + + EOpFma, + + EOpFrexp, + EOpLdexp, + + EOpFloatBitsToInt, + EOpFloatBitsToUint, + EOpIntBitsToFloat, + EOpUintBitsToFloat, + EOpDoubleBitsToInt64, + EOpDoubleBitsToUint64, + EOpInt64BitsToDouble, + EOpUint64BitsToDouble, + EOpFloat16BitsToInt16, + EOpFloat16BitsToUint16, + EOpInt16BitsToFloat16, + EOpUint16BitsToFloat16, + EOpPackSnorm2x16, + EOpUnpackSnorm2x16, + EOpPackUnorm2x16, + EOpUnpackUnorm2x16, + EOpPackSnorm4x8, + EOpUnpackSnorm4x8, + EOpPackUnorm4x8, + EOpUnpackUnorm4x8, + EOpPackHalf2x16, + EOpUnpackHalf2x16, + EOpPackDouble2x32, + EOpUnpackDouble2x32, + EOpPackInt2x32, + EOpUnpackInt2x32, + EOpPackUint2x32, + EOpUnpackUint2x32, + EOpPackFloat2x16, + EOpUnpackFloat2x16, + EOpPackInt2x16, + EOpUnpackInt2x16, + EOpPackUint2x16, + EOpUnpackUint2x16, + EOpPackInt4x16, + EOpUnpackInt4x16, + EOpPackUint4x16, + EOpUnpackUint4x16, + EOpPack16, + EOpPack32, + EOpPack64, + EOpUnpack32, + EOpUnpack16, + EOpUnpack8, + + EOpLength, + EOpDistance, + EOpDot, + EOpCross, + EOpNormalize, + EOpFaceForward, + EOpReflect, + EOpRefract, + + EOpMin3, + EOpMax3, + EOpMid3, + + EOpDPdx, // Fragment only + EOpDPdy, // Fragment only + EOpFwidth, // Fragment only + EOpDPdxFine, // Fragment only + EOpDPdyFine, // Fragment only + EOpFwidthFine, // Fragment only + EOpDPdxCoarse, // Fragment only + EOpDPdyCoarse, // Fragment only + EOpFwidthCoarse, // Fragment only + + EOpInterpolateAtCentroid, // Fragment only + EOpInterpolateAtSample, // Fragment only + EOpInterpolateAtOffset, // Fragment only + EOpInterpolateAtVertex, + + EOpMatrixTimesMatrix, + EOpOuterProduct, + EOpDeterminant, + EOpMatrixInverse, + EOpTranspose, + + EOpFtransform, + + EOpNoise, + + EOpEmitVertex, // geometry only + EOpEndPrimitive, // geometry only + EOpEmitStreamVertex, // geometry only + EOpEndStreamPrimitive, // geometry only + + EOpBarrier, + EOpMemoryBarrier, + EOpMemoryBarrierAtomicCounter, + EOpMemoryBarrierBuffer, + EOpMemoryBarrierImage, + EOpMemoryBarrierShared, // compute only + EOpGroupMemoryBarrier, // compute only + + EOpBallot, + EOpReadInvocation, + EOpReadFirstInvocation, + + EOpAnyInvocation, + EOpAllInvocations, + EOpAllInvocationsEqual, + + EOpSubgroupGuardStart, + EOpSubgroupBarrier, + EOpSubgroupMemoryBarrier, + EOpSubgroupMemoryBarrierBuffer, + EOpSubgroupMemoryBarrierImage, + EOpSubgroupMemoryBarrierShared, // compute only + EOpSubgroupElect, + EOpSubgroupAll, + EOpSubgroupAny, + EOpSubgroupAllEqual, + EOpSubgroupBroadcast, + EOpSubgroupBroadcastFirst, + EOpSubgroupBallot, + EOpSubgroupInverseBallot, + EOpSubgroupBallotBitExtract, + EOpSubgroupBallotBitCount, + EOpSubgroupBallotInclusiveBitCount, + EOpSubgroupBallotExclusiveBitCount, + EOpSubgroupBallotFindLSB, + EOpSubgroupBallotFindMSB, + EOpSubgroupShuffle, + EOpSubgroupShuffleXor, + EOpSubgroupShuffleUp, + EOpSubgroupShuffleDown, + EOpSubgroupAdd, + EOpSubgroupMul, + EOpSubgroupMin, + EOpSubgroupMax, + EOpSubgroupAnd, + EOpSubgroupOr, + EOpSubgroupXor, + EOpSubgroupInclusiveAdd, + EOpSubgroupInclusiveMul, + EOpSubgroupInclusiveMin, + EOpSubgroupInclusiveMax, + EOpSubgroupInclusiveAnd, + EOpSubgroupInclusiveOr, + EOpSubgroupInclusiveXor, + EOpSubgroupExclusiveAdd, + EOpSubgroupExclusiveMul, + EOpSubgroupExclusiveMin, + EOpSubgroupExclusiveMax, + EOpSubgroupExclusiveAnd, + EOpSubgroupExclusiveOr, + EOpSubgroupExclusiveXor, + EOpSubgroupClusteredAdd, + EOpSubgroupClusteredMul, + EOpSubgroupClusteredMin, + EOpSubgroupClusteredMax, + EOpSubgroupClusteredAnd, + EOpSubgroupClusteredOr, + EOpSubgroupClusteredXor, + EOpSubgroupQuadBroadcast, + EOpSubgroupQuadSwapHorizontal, + EOpSubgroupQuadSwapVertical, + EOpSubgroupQuadSwapDiagonal, + + EOpSubgroupPartition, + EOpSubgroupPartitionedAdd, + EOpSubgroupPartitionedMul, + EOpSubgroupPartitionedMin, + EOpSubgroupPartitionedMax, + EOpSubgroupPartitionedAnd, + EOpSubgroupPartitionedOr, + EOpSubgroupPartitionedXor, + EOpSubgroupPartitionedInclusiveAdd, + EOpSubgroupPartitionedInclusiveMul, + EOpSubgroupPartitionedInclusiveMin, + EOpSubgroupPartitionedInclusiveMax, + EOpSubgroupPartitionedInclusiveAnd, + EOpSubgroupPartitionedInclusiveOr, + EOpSubgroupPartitionedInclusiveXor, + EOpSubgroupPartitionedExclusiveAdd, + EOpSubgroupPartitionedExclusiveMul, + EOpSubgroupPartitionedExclusiveMin, + EOpSubgroupPartitionedExclusiveMax, + EOpSubgroupPartitionedExclusiveAnd, + EOpSubgroupPartitionedExclusiveOr, + EOpSubgroupPartitionedExclusiveXor, + + EOpSubgroupGuardStop, + + EOpMinInvocations, + EOpMaxInvocations, + EOpAddInvocations, + EOpMinInvocationsNonUniform, + EOpMaxInvocationsNonUniform, + EOpAddInvocationsNonUniform, + EOpMinInvocationsInclusiveScan, + EOpMaxInvocationsInclusiveScan, + EOpAddInvocationsInclusiveScan, + EOpMinInvocationsInclusiveScanNonUniform, + EOpMaxInvocationsInclusiveScanNonUniform, + EOpAddInvocationsInclusiveScanNonUniform, + EOpMinInvocationsExclusiveScan, + EOpMaxInvocationsExclusiveScan, + EOpAddInvocationsExclusiveScan, + EOpMinInvocationsExclusiveScanNonUniform, + EOpMaxInvocationsExclusiveScanNonUniform, + EOpAddInvocationsExclusiveScanNonUniform, + EOpSwizzleInvocations, + EOpSwizzleInvocationsMasked, + EOpWriteInvocation, + EOpMbcnt, + + EOpCubeFaceIndex, + EOpCubeFaceCoord, + EOpTime, + + EOpAtomicAdd, + EOpAtomicMin, + EOpAtomicMax, + EOpAtomicAnd, + EOpAtomicOr, + EOpAtomicXor, + EOpAtomicExchange, + EOpAtomicCompSwap, + EOpAtomicLoad, + EOpAtomicStore, + + EOpAtomicCounterIncrement, // results in pre-increment value + EOpAtomicCounterDecrement, // results in post-decrement value + EOpAtomicCounter, + EOpAtomicCounterAdd, + EOpAtomicCounterSubtract, + EOpAtomicCounterMin, + EOpAtomicCounterMax, + EOpAtomicCounterAnd, + EOpAtomicCounterOr, + EOpAtomicCounterXor, + EOpAtomicCounterExchange, + EOpAtomicCounterCompSwap, + + EOpAny, + EOpAll, + + EOpCooperativeMatrixLoad, + EOpCooperativeMatrixStore, + EOpCooperativeMatrixMulAdd, + + EOpBeginInvocationInterlock, // Fragment only + EOpEndInvocationInterlock, // Fragment only + + EOpIsHelperInvocation, + + EOpDebugPrintf, + + // + // Branch + // + + EOpKill, // Fragment only + EOpReturn, + EOpBreak, + EOpContinue, + EOpCase, + EOpDefault, + EOpDemote, // Fragment only + + // + // Constructors + // + + EOpConstructGuardStart, + EOpConstructInt, // these first scalar forms also identify what implicit conversion is needed + EOpConstructUint, + EOpConstructInt8, + EOpConstructUint8, + EOpConstructInt16, + EOpConstructUint16, + EOpConstructInt64, + EOpConstructUint64, + EOpConstructBool, + EOpConstructFloat, + EOpConstructDouble, + // Keep vector and matrix constructors in a consistent relative order for + // TParseContext::constructBuiltIn, which converts between 8/16/32 bit + // vector constructors + EOpConstructVec2, + EOpConstructVec3, + EOpConstructVec4, + EOpConstructMat2x2, + EOpConstructMat2x3, + EOpConstructMat2x4, + EOpConstructMat3x2, + EOpConstructMat3x3, + EOpConstructMat3x4, + EOpConstructMat4x2, + EOpConstructMat4x3, + EOpConstructMat4x4, + EOpConstructDVec2, + EOpConstructDVec3, + EOpConstructDVec4, + EOpConstructBVec2, + EOpConstructBVec3, + EOpConstructBVec4, + EOpConstructI8Vec2, + EOpConstructI8Vec3, + EOpConstructI8Vec4, + EOpConstructU8Vec2, + EOpConstructU8Vec3, + EOpConstructU8Vec4, + EOpConstructI16Vec2, + EOpConstructI16Vec3, + EOpConstructI16Vec4, + EOpConstructU16Vec2, + EOpConstructU16Vec3, + EOpConstructU16Vec4, + EOpConstructIVec2, + EOpConstructIVec3, + EOpConstructIVec4, + EOpConstructUVec2, + EOpConstructUVec3, + EOpConstructUVec4, + EOpConstructI64Vec2, + EOpConstructI64Vec3, + EOpConstructI64Vec4, + EOpConstructU64Vec2, + EOpConstructU64Vec3, + EOpConstructU64Vec4, + EOpConstructDMat2x2, + EOpConstructDMat2x3, + EOpConstructDMat2x4, + EOpConstructDMat3x2, + EOpConstructDMat3x3, + EOpConstructDMat3x4, + EOpConstructDMat4x2, + EOpConstructDMat4x3, + EOpConstructDMat4x4, + EOpConstructIMat2x2, + EOpConstructIMat2x3, + EOpConstructIMat2x4, + EOpConstructIMat3x2, + EOpConstructIMat3x3, + EOpConstructIMat3x4, + EOpConstructIMat4x2, + EOpConstructIMat4x3, + EOpConstructIMat4x4, + EOpConstructUMat2x2, + EOpConstructUMat2x3, + EOpConstructUMat2x4, + EOpConstructUMat3x2, + EOpConstructUMat3x3, + EOpConstructUMat3x4, + EOpConstructUMat4x2, + EOpConstructUMat4x3, + EOpConstructUMat4x4, + EOpConstructBMat2x2, + EOpConstructBMat2x3, + EOpConstructBMat2x4, + EOpConstructBMat3x2, + EOpConstructBMat3x3, + EOpConstructBMat3x4, + EOpConstructBMat4x2, + EOpConstructBMat4x3, + EOpConstructBMat4x4, + EOpConstructFloat16, + EOpConstructF16Vec2, + EOpConstructF16Vec3, + EOpConstructF16Vec4, + EOpConstructF16Mat2x2, + EOpConstructF16Mat2x3, + EOpConstructF16Mat2x4, + EOpConstructF16Mat3x2, + EOpConstructF16Mat3x3, + EOpConstructF16Mat3x4, + EOpConstructF16Mat4x2, + EOpConstructF16Mat4x3, + EOpConstructF16Mat4x4, + EOpConstructStruct, + EOpConstructTextureSampler, + EOpConstructNonuniform, // expected to be transformed away, not present in final AST + EOpConstructReference, + EOpConstructCooperativeMatrix, + EOpConstructGuardEnd, + + // + // moves + // + + EOpAssign, + EOpAddAssign, + EOpSubAssign, + EOpMulAssign, + EOpVectorTimesMatrixAssign, + EOpVectorTimesScalarAssign, + EOpMatrixTimesScalarAssign, + EOpMatrixTimesMatrixAssign, + EOpDivAssign, + EOpModAssign, + EOpAndAssign, + EOpInclusiveOrAssign, + EOpExclusiveOrAssign, + EOpLeftShiftAssign, + EOpRightShiftAssign, + + // + // Array operators + // + + // Can apply to arrays, vectors, or matrices. + // Can be decomposed to a constant at compile time, but this does not always happen, + // due to link-time effects. So, consumer can expect either a link-time sized or + // run-time sized array. + EOpArrayLength, + + // + // Image operations + // + + EOpImageGuardBegin, + + EOpImageQuerySize, + EOpImageQuerySamples, + EOpImageLoad, + EOpImageStore, + EOpImageLoadLod, + EOpImageStoreLod, + EOpImageAtomicAdd, + EOpImageAtomicMin, + EOpImageAtomicMax, + EOpImageAtomicAnd, + EOpImageAtomicOr, + EOpImageAtomicXor, + EOpImageAtomicExchange, + EOpImageAtomicCompSwap, + EOpImageAtomicLoad, + EOpImageAtomicStore, + + EOpSubpassLoad, + EOpSubpassLoadMS, + EOpSparseImageLoad, + EOpSparseImageLoadLod, + + EOpImageGuardEnd, + + // + // Texture operations + // + + EOpTextureGuardBegin, + + EOpTextureQuerySize, + EOpTextureQueryLod, + EOpTextureQueryLevels, + EOpTextureQuerySamples, + + EOpSamplingGuardBegin, + + EOpTexture, + EOpTextureProj, + EOpTextureLod, + EOpTextureOffset, + EOpTextureFetch, + EOpTextureFetchOffset, + EOpTextureProjOffset, + EOpTextureLodOffset, + EOpTextureProjLod, + EOpTextureProjLodOffset, + EOpTextureGrad, + EOpTextureGradOffset, + EOpTextureProjGrad, + EOpTextureProjGradOffset, + EOpTextureGather, + EOpTextureGatherOffset, + EOpTextureGatherOffsets, + EOpTextureClamp, + EOpTextureOffsetClamp, + EOpTextureGradClamp, + EOpTextureGradOffsetClamp, + EOpTextureGatherLod, + EOpTextureGatherLodOffset, + EOpTextureGatherLodOffsets, + EOpFragmentMaskFetch, + EOpFragmentFetch, + + EOpSparseTextureGuardBegin, + + EOpSparseTexture, + EOpSparseTextureLod, + EOpSparseTextureOffset, + EOpSparseTextureFetch, + EOpSparseTextureFetchOffset, + EOpSparseTextureLodOffset, + EOpSparseTextureGrad, + EOpSparseTextureGradOffset, + EOpSparseTextureGather, + EOpSparseTextureGatherOffset, + EOpSparseTextureGatherOffsets, + EOpSparseTexelsResident, + EOpSparseTextureClamp, + EOpSparseTextureOffsetClamp, + EOpSparseTextureGradClamp, + EOpSparseTextureGradOffsetClamp, + EOpSparseTextureGatherLod, + EOpSparseTextureGatherLodOffset, + EOpSparseTextureGatherLodOffsets, + + EOpSparseTextureGuardEnd, + + EOpImageFootprintGuardBegin, + EOpImageSampleFootprintNV, + EOpImageSampleFootprintClampNV, + EOpImageSampleFootprintLodNV, + EOpImageSampleFootprintGradNV, + EOpImageSampleFootprintGradClampNV, + EOpImageFootprintGuardEnd, + EOpSamplingGuardEnd, + EOpTextureGuardEnd, + + // + // Integer operations + // + + EOpAddCarry, + EOpSubBorrow, + EOpUMulExtended, + EOpIMulExtended, + EOpBitfieldExtract, + EOpBitfieldInsert, + EOpBitFieldReverse, + EOpBitCount, + EOpFindLSB, + EOpFindMSB, + + EOpCountLeadingZeros, + EOpCountTrailingZeros, + EOpAbsDifference, + EOpAddSaturate, + EOpSubSaturate, + EOpAverage, + EOpAverageRounded, + EOpMul32x16, + + EOpTrace, + EOpReportIntersection, + EOpIgnoreIntersection, + EOpTerminateRay, + EOpExecuteCallable, + EOpWritePackedPrimitiveIndices4x8NV, + + // + // GL_EXT_ray_query operations + // + + EOpRayQueryInitialize, + EOpRayQueryTerminate, + EOpRayQueryGenerateIntersection, + EOpRayQueryConfirmIntersection, + EOpRayQueryProceed, + EOpRayQueryGetIntersectionType, + EOpRayQueryGetRayTMin, + EOpRayQueryGetRayFlags, + EOpRayQueryGetIntersectionT, + EOpRayQueryGetIntersectionInstanceCustomIndex, + EOpRayQueryGetIntersectionInstanceId, + EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset, + EOpRayQueryGetIntersectionGeometryIndex, + EOpRayQueryGetIntersectionPrimitiveIndex, + EOpRayQueryGetIntersectionBarycentrics, + EOpRayQueryGetIntersectionFrontFace, + EOpRayQueryGetIntersectionCandidateAABBOpaque, + EOpRayQueryGetIntersectionObjectRayDirection, + EOpRayQueryGetIntersectionObjectRayOrigin, + EOpRayQueryGetWorldRayDirection, + EOpRayQueryGetWorldRayOrigin, + EOpRayQueryGetIntersectionObjectToWorld, + EOpRayQueryGetIntersectionWorldToObject, + + // + // HLSL operations + // + + EOpClip, // discard if input value < 0 + EOpIsFinite, + EOpLog10, // base 10 log + EOpRcp, // 1/x + EOpSaturate, // clamp from 0 to 1 + EOpSinCos, // sin and cos in out parameters + EOpGenMul, // mul(x,y) on any of mat/vec/scalars + EOpDst, // x = 1, y=src0.y * src1.y, z=src0.z, w=src1.w + EOpInterlockedAdd, // atomic ops, but uses [optional] out arg instead of return + EOpInterlockedAnd, // ... + EOpInterlockedCompareExchange, // ... + EOpInterlockedCompareStore, // ... + EOpInterlockedExchange, // ... + EOpInterlockedMax, // ... + EOpInterlockedMin, // ... + EOpInterlockedOr, // ... + EOpInterlockedXor, // ... + EOpAllMemoryBarrierWithGroupSync, // memory barriers without non-hlsl AST equivalents + EOpDeviceMemoryBarrier, // ... + EOpDeviceMemoryBarrierWithGroupSync, // ... + EOpWorkgroupMemoryBarrier, // ... + EOpWorkgroupMemoryBarrierWithGroupSync, // ... + EOpEvaluateAttributeSnapped, // InterpolateAtOffset with int position on 16x16 grid + EOpF32tof16, // HLSL conversion: half of a PackHalf2x16 + EOpF16tof32, // HLSL conversion: half of an UnpackHalf2x16 + EOpLit, // HLSL lighting coefficient vector + EOpTextureBias, // HLSL texture bias: will be lowered to EOpTexture + EOpAsDouble, // slightly different from EOpUint64BitsToDouble + EOpD3DCOLORtoUBYTE4, // convert and swizzle 4-component color to UBYTE4 range + + EOpMethodSample, // Texture object methods. These are translated to existing + EOpMethodSampleBias, // AST methods, and exist to represent HLSL semantics until that + EOpMethodSampleCmp, // translation is performed. See HlslParseContext::decomposeSampleMethods(). + EOpMethodSampleCmpLevelZero, // ... + EOpMethodSampleGrad, // ... + EOpMethodSampleLevel, // ... + EOpMethodLoad, // ... + EOpMethodGetDimensions, // ... + EOpMethodGetSamplePosition, // ... + EOpMethodGather, // ... + EOpMethodCalculateLevelOfDetail, // ... + EOpMethodCalculateLevelOfDetailUnclamped, // ... + + // Load already defined above for textures + EOpMethodLoad2, // Structure buffer object methods. These are translated to existing + EOpMethodLoad3, // AST methods, and exist to represent HLSL semantics until that + EOpMethodLoad4, // translation is performed. See HlslParseContext::decomposeSampleMethods(). + EOpMethodStore, // ... + EOpMethodStore2, // ... + EOpMethodStore3, // ... + EOpMethodStore4, // ... + EOpMethodIncrementCounter, // ... + EOpMethodDecrementCounter, // ... + // EOpMethodAppend is defined for geo shaders below + EOpMethodConsume, + + // SM5 texture methods + EOpMethodGatherRed, // These are covered under the above EOpMethodSample comment about + EOpMethodGatherGreen, // translation to existing AST opcodes. They exist temporarily + EOpMethodGatherBlue, // because HLSL arguments are slightly different. + EOpMethodGatherAlpha, // ... + EOpMethodGatherCmp, // ... + EOpMethodGatherCmpRed, // ... + EOpMethodGatherCmpGreen, // ... + EOpMethodGatherCmpBlue, // ... + EOpMethodGatherCmpAlpha, // ... + + // geometry methods + EOpMethodAppend, // Geometry shader methods + EOpMethodRestartStrip, // ... + + // matrix + EOpMatrixSwizzle, // select multiple matrix components (non-column) + + // SM6 wave ops + EOpWaveGetLaneCount, // Will decompose to gl_SubgroupSize. + EOpWaveGetLaneIndex, // Will decompose to gl_SubgroupInvocationID. + EOpWaveActiveCountBits, // Will decompose to subgroupBallotBitCount(subgroupBallot()). + EOpWavePrefixCountBits, // Will decompose to subgroupBallotInclusiveBitCount(subgroupBallot()). + + // Shader Clock Ops + EOpReadClockSubgroupKHR, + EOpReadClockDeviceKHR, +}; + +class TIntermTraverser; +class TIntermOperator; +class TIntermAggregate; +class TIntermUnary; +class TIntermBinary; +class TIntermConstantUnion; +class TIntermSelection; +class TIntermSwitch; +class TIntermBranch; +class TIntermTyped; +class TIntermMethod; +class TIntermSymbol; +class TIntermLoop; + +} // end namespace glslang + +// +// Base class for the tree nodes +// +// (Put outside the glslang namespace, as it's used as part of the external interface.) +// +class TIntermNode { +public: + POOL_ALLOCATOR_NEW_DELETE(glslang::GetThreadPoolAllocator()) + + TIntermNode() { loc.init(); } + virtual const glslang::TSourceLoc& getLoc() const { return loc; } + virtual void setLoc(const glslang::TSourceLoc& l) { loc = l; } + virtual void traverse(glslang::TIntermTraverser*) = 0; + virtual glslang::TIntermTyped* getAsTyped() { return 0; } + virtual glslang::TIntermOperator* getAsOperator() { return 0; } + virtual glslang::TIntermConstantUnion* getAsConstantUnion() { return 0; } + virtual glslang::TIntermAggregate* getAsAggregate() { return 0; } + virtual glslang::TIntermUnary* getAsUnaryNode() { return 0; } + virtual glslang::TIntermBinary* getAsBinaryNode() { return 0; } + virtual glslang::TIntermSelection* getAsSelectionNode() { return 0; } + virtual glslang::TIntermSwitch* getAsSwitchNode() { return 0; } + virtual glslang::TIntermMethod* getAsMethodNode() { return 0; } + virtual glslang::TIntermSymbol* getAsSymbolNode() { return 0; } + virtual glslang::TIntermBranch* getAsBranchNode() { return 0; } + virtual glslang::TIntermLoop* getAsLoopNode() { return 0; } + + virtual const glslang::TIntermTyped* getAsTyped() const { return 0; } + virtual const glslang::TIntermOperator* getAsOperator() const { return 0; } + virtual const glslang::TIntermConstantUnion* getAsConstantUnion() const { return 0; } + virtual const glslang::TIntermAggregate* getAsAggregate() const { return 0; } + virtual const glslang::TIntermUnary* getAsUnaryNode() const { return 0; } + virtual const glslang::TIntermBinary* getAsBinaryNode() const { return 0; } + virtual const glslang::TIntermSelection* getAsSelectionNode() const { return 0; } + virtual const glslang::TIntermSwitch* getAsSwitchNode() const { return 0; } + virtual const glslang::TIntermMethod* getAsMethodNode() const { return 0; } + virtual const glslang::TIntermSymbol* getAsSymbolNode() const { return 0; } + virtual const glslang::TIntermBranch* getAsBranchNode() const { return 0; } + virtual const glslang::TIntermLoop* getAsLoopNode() const { return 0; } + virtual ~TIntermNode() { } + +protected: + TIntermNode(const TIntermNode&); + TIntermNode& operator=(const TIntermNode&); + glslang::TSourceLoc loc; +}; + +namespace glslang { + +// +// This is just to help yacc. +// +struct TIntermNodePair { + TIntermNode* node1; + TIntermNode* node2; +}; + +// +// Intermediate class for nodes that have a type. +// +class TIntermTyped : public TIntermNode { +public: + TIntermTyped(const TType& t) { type.shallowCopy(t); } + TIntermTyped(TBasicType basicType) { TType bt(basicType); type.shallowCopy(bt); } + virtual TIntermTyped* getAsTyped() { return this; } + virtual const TIntermTyped* getAsTyped() const { return this; } + virtual void setType(const TType& t) { type.shallowCopy(t); } + virtual const TType& getType() const { return type; } + virtual TType& getWritableType() { return type; } + + virtual TBasicType getBasicType() const { return type.getBasicType(); } + virtual TQualifier& getQualifier() { return type.getQualifier(); } + virtual const TQualifier& getQualifier() const { return type.getQualifier(); } + virtual void propagatePrecision(TPrecisionQualifier); + virtual int getVectorSize() const { return type.getVectorSize(); } + virtual int getMatrixCols() const { return type.getMatrixCols(); } + virtual int getMatrixRows() const { return type.getMatrixRows(); } + virtual bool isMatrix() const { return type.isMatrix(); } + virtual bool isArray() const { return type.isArray(); } + virtual bool isVector() const { return type.isVector(); } + virtual bool isScalar() const { return type.isScalar(); } + virtual bool isStruct() const { return type.isStruct(); } + virtual bool isFloatingDomain() const { return type.isFloatingDomain(); } + virtual bool isIntegerDomain() const { return type.isIntegerDomain(); } + bool isAtomic() const { return type.isAtomic(); } + bool isReference() const { return type.isReference(); } + TString getCompleteString() const { return type.getCompleteString(); } + +protected: + TIntermTyped& operator=(const TIntermTyped&); + TType type; +}; + +// +// Handle for, do-while, and while loops. +// +class TIntermLoop : public TIntermNode { +public: + TIntermLoop(TIntermNode* aBody, TIntermTyped* aTest, TIntermTyped* aTerminal, bool testFirst) : + body(aBody), + test(aTest), + terminal(aTerminal), + first(testFirst), + unroll(false), + dontUnroll(false), + dependency(0), + minIterations(0), + maxIterations(iterationsInfinite), + iterationMultiple(1), + peelCount(0), + partialCount(0) + { } + + virtual TIntermLoop* getAsLoopNode() { return this; } + virtual const TIntermLoop* getAsLoopNode() const { return this; } + virtual void traverse(TIntermTraverser*); + TIntermNode* getBody() const { return body; } + TIntermTyped* getTest() const { return test; } + TIntermTyped* getTerminal() const { return terminal; } + bool testFirst() const { return first; } + + void setUnroll() { unroll = true; } + void setDontUnroll() { + dontUnroll = true; + peelCount = 0; + partialCount = 0; + } + bool getUnroll() const { return unroll; } + bool getDontUnroll() const { return dontUnroll; } + + static const unsigned int dependencyInfinite = 0xFFFFFFFF; + static const unsigned int iterationsInfinite = 0xFFFFFFFF; + void setLoopDependency(int d) { dependency = d; } + int getLoopDependency() const { return dependency; } + + void setMinIterations(unsigned int v) { minIterations = v; } + unsigned int getMinIterations() const { return minIterations; } + void setMaxIterations(unsigned int v) { maxIterations = v; } + unsigned int getMaxIterations() const { return maxIterations; } + void setIterationMultiple(unsigned int v) { iterationMultiple = v; } + unsigned int getIterationMultiple() const { return iterationMultiple; } + void setPeelCount(unsigned int v) { + peelCount = v; + dontUnroll = false; + } + unsigned int getPeelCount() const { return peelCount; } + void setPartialCount(unsigned int v) { + partialCount = v; + dontUnroll = false; + } + unsigned int getPartialCount() const { return partialCount; } + +protected: + TIntermNode* body; // code to loop over + TIntermTyped* test; // exit condition associated with loop, could be 0 for 'for' loops + TIntermTyped* terminal; // exists for for-loops + bool first; // true for while and for, not for do-while + bool unroll; // true if unroll requested + bool dontUnroll; // true if request to not unroll + unsigned int dependency; // loop dependency hint; 0 means not set or unknown + unsigned int minIterations; // as per the SPIR-V specification + unsigned int maxIterations; // as per the SPIR-V specification + unsigned int iterationMultiple; // as per the SPIR-V specification + unsigned int peelCount; // as per the SPIR-V specification + unsigned int partialCount; // as per the SPIR-V specification +}; + +// +// Handle case, break, continue, return, and kill. +// +class TIntermBranch : public TIntermNode { +public: + TIntermBranch(TOperator op, TIntermTyped* e) : + flowOp(op), + expression(e) { } + virtual TIntermBranch* getAsBranchNode() { return this; } + virtual const TIntermBranch* getAsBranchNode() const { return this; } + virtual void traverse(TIntermTraverser*); + TOperator getFlowOp() const { return flowOp; } + TIntermTyped* getExpression() const { return expression; } + void setExpression(TIntermTyped* pExpression) { expression = pExpression; } +protected: + TOperator flowOp; + TIntermTyped* expression; +}; + +// +// Represent method names before seeing their calling signature +// or resolving them to operations. Just an expression as the base object +// and a textural name. +// +class TIntermMethod : public TIntermTyped { +public: + TIntermMethod(TIntermTyped* o, const TType& t, const TString& m) : TIntermTyped(t), object(o), method(m) { } + virtual TIntermMethod* getAsMethodNode() { return this; } + virtual const TIntermMethod* getAsMethodNode() const { return this; } + virtual const TString& getMethodName() const { return method; } + virtual TIntermTyped* getObject() const { return object; } + virtual void traverse(TIntermTraverser*); +protected: + TIntermTyped* object; + TString method; +}; + +// +// Nodes that correspond to symbols or constants in the source code. +// +class TIntermSymbol : public TIntermTyped { +public: + // if symbol is initialized as symbol(sym), the memory comes from the pool allocator of sym. If sym comes from + // per process threadPoolAllocator, then it causes increased memory usage per compile + // it is essential to use "symbol = sym" to assign to symbol + TIntermSymbol(int i, const TString& n, const TType& t) + : TIntermTyped(t), id(i), +#ifndef GLSLANG_WEB + flattenSubset(-1), +#endif + constSubtree(nullptr) + { name = n; } + virtual int getId() const { return id; } + virtual void changeId(int i) { id = i; } + virtual const TString& getName() const { return name; } + virtual void traverse(TIntermTraverser*); + virtual TIntermSymbol* getAsSymbolNode() { return this; } + virtual const TIntermSymbol* getAsSymbolNode() const { return this; } + void setConstArray(const TConstUnionArray& c) { constArray = c; } + const TConstUnionArray& getConstArray() const { return constArray; } + void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; } + TIntermTyped* getConstSubtree() const { return constSubtree; } +#ifndef GLSLANG_WEB + void setFlattenSubset(int subset) { flattenSubset = subset; } + int getFlattenSubset() const { return flattenSubset; } // -1 means full object +#endif + + // This is meant for cases where a node has already been constructed, and + // later on, it becomes necessary to switch to a different symbol. + virtual void switchId(int newId) { id = newId; } + +protected: + int id; // the unique id of the symbol this node represents +#ifndef GLSLANG_WEB + int flattenSubset; // how deeply the flattened object rooted at id has been dereferenced +#endif + TString name; // the name of the symbol this node represents + TConstUnionArray constArray; // if the symbol is a front-end compile-time constant, this is its value + TIntermTyped* constSubtree; +}; + +class TIntermConstantUnion : public TIntermTyped { +public: + TIntermConstantUnion(const TConstUnionArray& ua, const TType& t) : TIntermTyped(t), constArray(ua), literal(false) { } + const TConstUnionArray& getConstArray() const { return constArray; } + virtual TIntermConstantUnion* getAsConstantUnion() { return this; } + virtual const TIntermConstantUnion* getAsConstantUnion() const { return this; } + virtual void traverse(TIntermTraverser*); + virtual TIntermTyped* fold(TOperator, const TIntermTyped*) const; + virtual TIntermTyped* fold(TOperator, const TType&) const; + void setLiteral() { literal = true; } + void setExpression() { literal = false; } + bool isLiteral() const { return literal; } + +protected: + TIntermConstantUnion& operator=(const TIntermConstantUnion&); + + const TConstUnionArray constArray; + bool literal; // true if node represents a literal in the source code +}; + +// Represent the independent aspects of a texturing TOperator +struct TCrackedTextureOp { + bool query; + bool proj; + bool lod; + bool fetch; + bool offset; + bool offsets; + bool gather; + bool grad; + bool subpass; + bool lodClamp; + bool fragMask; +}; + +// +// Intermediate class for node types that hold operators. +// +class TIntermOperator : public TIntermTyped { +public: + virtual TIntermOperator* getAsOperator() { return this; } + virtual const TIntermOperator* getAsOperator() const { return this; } + TOperator getOp() const { return op; } + void setOp(TOperator newOp) { op = newOp; } + bool modifiesState() const; + bool isConstructor() const; + bool isTexture() const { return op > EOpTextureGuardBegin && op < EOpTextureGuardEnd; } + bool isSampling() const { return op > EOpSamplingGuardBegin && op < EOpSamplingGuardEnd; } +#ifdef GLSLANG_WEB + bool isImage() const { return false; } + bool isSparseTexture() const { return false; } + bool isImageFootprint() const { return false; } + bool isSparseImage() const { return false; } + bool isSubgroup() const { return false; } +#else + bool isImage() const { return op > EOpImageGuardBegin && op < EOpImageGuardEnd; } + bool isSparseTexture() const { return op > EOpSparseTextureGuardBegin && op < EOpSparseTextureGuardEnd; } + bool isImageFootprint() const { return op > EOpImageFootprintGuardBegin && op < EOpImageFootprintGuardEnd; } + bool isSparseImage() const { return op == EOpSparseImageLoad; } + bool isSubgroup() const { return op > EOpSubgroupGuardStart && op < EOpSubgroupGuardStop; } +#endif + + void setOperationPrecision(TPrecisionQualifier p) { operationPrecision = p; } + TPrecisionQualifier getOperationPrecision() const { return operationPrecision != EpqNone ? + operationPrecision : + type.getQualifier().precision; } + TString getCompleteString() const + { + TString cs = type.getCompleteString(); + if (getOperationPrecision() != type.getQualifier().precision) { + cs += ", operation at "; + cs += GetPrecisionQualifierString(getOperationPrecision()); + } + + return cs; + } + + // Crack the op into the individual dimensions of texturing operation. + void crackTexture(TSampler sampler, TCrackedTextureOp& cracked) const + { + cracked.query = false; + cracked.proj = false; + cracked.lod = false; + cracked.fetch = false; + cracked.offset = false; + cracked.offsets = false; + cracked.gather = false; + cracked.grad = false; + cracked.subpass = false; + cracked.lodClamp = false; + cracked.fragMask = false; + + switch (op) { + case EOpImageQuerySize: + case EOpImageQuerySamples: + case EOpTextureQuerySize: + case EOpTextureQueryLod: + case EOpTextureQueryLevels: + case EOpTextureQuerySamples: + case EOpSparseTexelsResident: + cracked.query = true; + break; + case EOpTexture: + case EOpSparseTexture: + break; + case EOpTextureProj: + cracked.proj = true; + break; + case EOpTextureLod: + case EOpSparseTextureLod: + cracked.lod = true; + break; + case EOpTextureOffset: + case EOpSparseTextureOffset: + cracked.offset = true; + break; + case EOpTextureFetch: + case EOpSparseTextureFetch: + cracked.fetch = true; + if (sampler.is1D() || (sampler.dim == Esd2D && ! sampler.isMultiSample()) || sampler.dim == Esd3D) + cracked.lod = true; + break; + case EOpTextureFetchOffset: + case EOpSparseTextureFetchOffset: + cracked.fetch = true; + cracked.offset = true; + if (sampler.is1D() || (sampler.dim == Esd2D && ! sampler.isMultiSample()) || sampler.dim == Esd3D) + cracked.lod = true; + break; + case EOpTextureProjOffset: + cracked.offset = true; + cracked.proj = true; + break; + case EOpTextureLodOffset: + case EOpSparseTextureLodOffset: + cracked.offset = true; + cracked.lod = true; + break; + case EOpTextureProjLod: + cracked.lod = true; + cracked.proj = true; + break; + case EOpTextureProjLodOffset: + cracked.offset = true; + cracked.lod = true; + cracked.proj = true; + break; + case EOpTextureGrad: + case EOpSparseTextureGrad: + cracked.grad = true; + break; + case EOpTextureGradOffset: + case EOpSparseTextureGradOffset: + cracked.grad = true; + cracked.offset = true; + break; + case EOpTextureProjGrad: + cracked.grad = true; + cracked.proj = true; + break; + case EOpTextureProjGradOffset: + cracked.grad = true; + cracked.offset = true; + cracked.proj = true; + break; +#ifndef GLSLANG_WEB + case EOpTextureClamp: + case EOpSparseTextureClamp: + cracked.lodClamp = true; + break; + case EOpTextureOffsetClamp: + case EOpSparseTextureOffsetClamp: + cracked.offset = true; + cracked.lodClamp = true; + break; + case EOpTextureGradClamp: + case EOpSparseTextureGradClamp: + cracked.grad = true; + cracked.lodClamp = true; + break; + case EOpTextureGradOffsetClamp: + case EOpSparseTextureGradOffsetClamp: + cracked.grad = true; + cracked.offset = true; + cracked.lodClamp = true; + break; + case EOpTextureGather: + case EOpSparseTextureGather: + cracked.gather = true; + break; + case EOpTextureGatherOffset: + case EOpSparseTextureGatherOffset: + cracked.gather = true; + cracked.offset = true; + break; + case EOpTextureGatherOffsets: + case EOpSparseTextureGatherOffsets: + cracked.gather = true; + cracked.offsets = true; + break; + case EOpTextureGatherLod: + case EOpSparseTextureGatherLod: + cracked.gather = true; + cracked.lod = true; + break; + case EOpTextureGatherLodOffset: + case EOpSparseTextureGatherLodOffset: + cracked.gather = true; + cracked.offset = true; + cracked.lod = true; + break; + case EOpTextureGatherLodOffsets: + case EOpSparseTextureGatherLodOffsets: + cracked.gather = true; + cracked.offsets = true; + cracked.lod = true; + break; + case EOpImageLoadLod: + case EOpImageStoreLod: + case EOpSparseImageLoadLod: + cracked.lod = true; + break; + case EOpFragmentMaskFetch: + cracked.subpass = sampler.dim == EsdSubpass; + cracked.fragMask = true; + break; + case EOpFragmentFetch: + cracked.subpass = sampler.dim == EsdSubpass; + cracked.fragMask = true; + break; + case EOpImageSampleFootprintNV: + break; + case EOpImageSampleFootprintClampNV: + cracked.lodClamp = true; + break; + case EOpImageSampleFootprintLodNV: + cracked.lod = true; + break; + case EOpImageSampleFootprintGradNV: + cracked.grad = true; + break; + case EOpImageSampleFootprintGradClampNV: + cracked.lodClamp = true; + cracked.grad = true; + break; + case EOpSubpassLoad: + case EOpSubpassLoadMS: + cracked.subpass = true; + break; +#endif + default: + break; + } + } + +protected: + TIntermOperator(TOperator o) : TIntermTyped(EbtFloat), op(o), operationPrecision(EpqNone) {} + TIntermOperator(TOperator o, TType& t) : TIntermTyped(t), op(o), operationPrecision(EpqNone) {} + TOperator op; + // The result precision is in the inherited TType, and is usually meant to be both + // the operation precision and the result precision. However, some more complex things, + // like built-in function calls, distinguish between the two, in which case non-EqpNone + // 'operationPrecision' overrides the result precision as far as operation precision + // is concerned. + TPrecisionQualifier operationPrecision; +}; + +// +// Nodes for all the basic binary math operators. +// +class TIntermBinary : public TIntermOperator { +public: + TIntermBinary(TOperator o) : TIntermOperator(o) {} + virtual void traverse(TIntermTraverser*); + virtual void setLeft(TIntermTyped* n) { left = n; } + virtual void setRight(TIntermTyped* n) { right = n; } + virtual TIntermTyped* getLeft() const { return left; } + virtual TIntermTyped* getRight() const { return right; } + virtual TIntermBinary* getAsBinaryNode() { return this; } + virtual const TIntermBinary* getAsBinaryNode() const { return this; } + virtual void updatePrecision(); +protected: + TIntermTyped* left; + TIntermTyped* right; +}; + +// +// Nodes for unary math operators. +// +class TIntermUnary : public TIntermOperator { +public: + TIntermUnary(TOperator o, TType& t) : TIntermOperator(o, t), operand(0) {} + TIntermUnary(TOperator o) : TIntermOperator(o), operand(0) {} + virtual void traverse(TIntermTraverser*); + virtual void setOperand(TIntermTyped* o) { operand = o; } + virtual TIntermTyped* getOperand() { return operand; } + virtual const TIntermTyped* getOperand() const { return operand; } + virtual TIntermUnary* getAsUnaryNode() { return this; } + virtual const TIntermUnary* getAsUnaryNode() const { return this; } + virtual void updatePrecision(); +protected: + TIntermTyped* operand; +}; + +typedef TVector TIntermSequence; +typedef TVector TQualifierList; +// +// Nodes that operate on an arbitrary sized set of children. +// +class TIntermAggregate : public TIntermOperator { +public: + TIntermAggregate() : TIntermOperator(EOpNull), userDefined(false), pragmaTable(nullptr) { } + TIntermAggregate(TOperator o) : TIntermOperator(o), pragmaTable(nullptr) { } + ~TIntermAggregate() { delete pragmaTable; } + virtual TIntermAggregate* getAsAggregate() { return this; } + virtual const TIntermAggregate* getAsAggregate() const { return this; } + virtual void setOperator(TOperator o) { op = o; } + virtual TIntermSequence& getSequence() { return sequence; } + virtual const TIntermSequence& getSequence() const { return sequence; } + virtual void setName(const TString& n) { name = n; } + virtual const TString& getName() const { return name; } + virtual void traverse(TIntermTraverser*); + virtual void setUserDefined() { userDefined = true; } + virtual bool isUserDefined() { return userDefined; } + virtual TQualifierList& getQualifierList() { return qualifier; } + virtual const TQualifierList& getQualifierList() const { return qualifier; } + void setOptimize(bool o) { optimize = o; } + void setDebug(bool d) { debug = d; } + bool getOptimize() const { return optimize; } + bool getDebug() const { return debug; } + void setPragmaTable(const TPragmaTable& pTable); + const TPragmaTable& getPragmaTable() const { return *pragmaTable; } +protected: + TIntermAggregate(const TIntermAggregate&); // disallow copy constructor + TIntermAggregate& operator=(const TIntermAggregate&); // disallow assignment operator + TIntermSequence sequence; + TQualifierList qualifier; + TString name; + bool userDefined; // used for user defined function names + bool optimize; + bool debug; + TPragmaTable* pragmaTable; +}; + +// +// For if tests. +// +class TIntermSelection : public TIntermTyped { +public: + TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB) : + TIntermTyped(EbtVoid), condition(cond), trueBlock(trueB), falseBlock(falseB), + shortCircuit(true), + flatten(false), dontFlatten(false) {} + TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB, const TType& type) : + TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB), + shortCircuit(true), + flatten(false), dontFlatten(false) {} + virtual void traverse(TIntermTraverser*); + virtual TIntermTyped* getCondition() const { return condition; } + virtual TIntermNode* getTrueBlock() const { return trueBlock; } + virtual TIntermNode* getFalseBlock() const { return falseBlock; } + virtual TIntermSelection* getAsSelectionNode() { return this; } + virtual const TIntermSelection* getAsSelectionNode() const { return this; } + + void setNoShortCircuit() { shortCircuit = false; } + bool getShortCircuit() const { return shortCircuit; } + + void setFlatten() { flatten = true; } + void setDontFlatten() { dontFlatten = true; } + bool getFlatten() const { return flatten; } + bool getDontFlatten() const { return dontFlatten; } + +protected: + TIntermTyped* condition; + TIntermNode* trueBlock; + TIntermNode* falseBlock; + bool shortCircuit; // normally all if-then-else and all GLSL ?: short-circuit, but HLSL ?: does not + bool flatten; // true if flatten requested + bool dontFlatten; // true if requested to not flatten +}; + +// +// For switch statements. Designed use is that a switch will have sequence of nodes +// that are either case/default nodes or a *single* node that represents all the code +// in between (if any) consecutive case/defaults. So, a traversal need only deal with +// 0 or 1 nodes per case/default statement. +// +class TIntermSwitch : public TIntermNode { +public: + TIntermSwitch(TIntermTyped* cond, TIntermAggregate* b) : condition(cond), body(b), + flatten(false), dontFlatten(false) {} + virtual void traverse(TIntermTraverser*); + virtual TIntermNode* getCondition() const { return condition; } + virtual TIntermAggregate* getBody() const { return body; } + virtual TIntermSwitch* getAsSwitchNode() { return this; } + virtual const TIntermSwitch* getAsSwitchNode() const { return this; } + + void setFlatten() { flatten = true; } + void setDontFlatten() { dontFlatten = true; } + bool getFlatten() const { return flatten; } + bool getDontFlatten() const { return dontFlatten; } + +protected: + TIntermTyped* condition; + TIntermAggregate* body; + bool flatten; // true if flatten requested + bool dontFlatten; // true if requested to not flatten +}; + +enum TVisit +{ + EvPreVisit, + EvInVisit, + EvPostVisit +}; + +// +// For traversing the tree. User should derive from this, +// put their traversal specific data in it, and then pass +// it to a Traverse method. +// +// When using this, just fill in the methods for nodes you want visited. +// Return false from a pre-visit to skip visiting that node's subtree. +// +// Explicitly set postVisit to true if you want post visiting, otherwise, +// filled in methods will only be called at pre-visit time (before processing +// the subtree). Similarly for inVisit for in-order visiting of nodes with +// multiple children. +// +// If you only want post-visits, explicitly turn off preVisit (and inVisit) +// and turn on postVisit. +// +// In general, for the visit*() methods, return true from interior nodes +// to have the traversal continue on to children. +// +// If you process children yourself, or don't want them processed, return false. +// +class TIntermTraverser { +public: + POOL_ALLOCATOR_NEW_DELETE(glslang::GetThreadPoolAllocator()) + TIntermTraverser(bool preVisit = true, bool inVisit = false, bool postVisit = false, bool rightToLeft = false) : + preVisit(preVisit), + inVisit(inVisit), + postVisit(postVisit), + rightToLeft(rightToLeft), + depth(0), + maxDepth(0) { } + virtual ~TIntermTraverser() { } + + virtual void visitSymbol(TIntermSymbol*) { } + virtual void visitConstantUnion(TIntermConstantUnion*) { } + virtual bool visitBinary(TVisit, TIntermBinary*) { return true; } + virtual bool visitUnary(TVisit, TIntermUnary*) { return true; } + virtual bool visitSelection(TVisit, TIntermSelection*) { return true; } + virtual bool visitAggregate(TVisit, TIntermAggregate*) { return true; } + virtual bool visitLoop(TVisit, TIntermLoop*) { return true; } + virtual bool visitBranch(TVisit, TIntermBranch*) { return true; } + virtual bool visitSwitch(TVisit, TIntermSwitch*) { return true; } + + int getMaxDepth() const { return maxDepth; } + + void incrementDepth(TIntermNode *current) + { + depth++; + maxDepth = (std::max)(maxDepth, depth); + path.push_back(current); + } + + void decrementDepth() + { + depth--; + path.pop_back(); + } + + TIntermNode *getParentNode() + { + return path.size() == 0 ? NULL : path.back(); + } + + const bool preVisit; + const bool inVisit; + const bool postVisit; + const bool rightToLeft; + +protected: + TIntermTraverser& operator=(TIntermTraverser&); + + int depth; + int maxDepth; + + // All the nodes from root to the current node's parent during traversing. + TVector path; +}; + +// KHR_vulkan_glsl says "Two arrays sized with specialization constants are the same type only if +// sized with the same symbol, involving no operations" +inline bool SameSpecializationConstants(TIntermTyped* node1, TIntermTyped* node2) +{ + return node1->getAsSymbolNode() && node2->getAsSymbolNode() && + node1->getAsSymbolNode()->getId() == node2->getAsSymbolNode()->getId(); +} + +} // end namespace glslang + +#endif // __INTERMEDIATE_H diff --git a/armorcore/tools/to_spirv/glslang/glslang/Include/revision.h b/armorcore/tools/to_spirv/glslang/glslang/Include/revision.h new file mode 100644 index 000000000..744c2fb42 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Include/revision.h @@ -0,0 +1,3 @@ +// This header is generated by the make-revision script. + +#define GLSLANG_PATCH_LEVEL 3743 diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Constant.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Constant.cpp new file mode 100644 index 000000000..e21cf427f --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Constant.cpp @@ -0,0 +1,1428 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2018-2020 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "localintermediate.h" +#include +#include +#include +#include + +namespace { + +using namespace glslang; + +typedef union { + double d; + int i[2]; +} DoubleIntUnion; + +// Some helper functions + +bool isNan(double x) +{ + DoubleIntUnion u; + // tough to find a platform independent library function, do it directly + u.d = x; + int bitPatternL = u.i[0]; + int bitPatternH = u.i[1]; + return (bitPatternH & 0x7ff80000) == 0x7ff80000 && + ((bitPatternH & 0xFFFFF) != 0 || bitPatternL != 0); +} + +bool isInf(double x) +{ + DoubleIntUnion u; + // tough to find a platform independent library function, do it directly + u.d = x; + int bitPatternL = u.i[0]; + int bitPatternH = u.i[1]; + return (bitPatternH & 0x7ff00000) == 0x7ff00000 && + (bitPatternH & 0xFFFFF) == 0 && bitPatternL == 0; +} + +const double pi = 3.1415926535897932384626433832795; + +} // end anonymous namespace + + +namespace glslang { + +// +// The fold functions see if an operation on a constant can be done in place, +// without generating run-time code. +// +// Returns the node to keep using, which may or may not be the node passed in. +// +// Note: As of version 1.2, all constant operations must be folded. It is +// not opportunistic, but rather a semantic requirement. +// + +// +// Do folding between a pair of nodes. +// 'this' is the left-hand operand and 'rightConstantNode' is the right-hand operand. +// +// Returns a new node representing the result. +// +TIntermTyped* TIntermConstantUnion::fold(TOperator op, const TIntermTyped* rightConstantNode) const +{ + // For most cases, the return type matches the argument type, so set that + // up and just code to exceptions below. + TType returnType; + returnType.shallowCopy(getType()); + + // + // A pair of nodes is to be folded together + // + + const TIntermConstantUnion *rightNode = rightConstantNode->getAsConstantUnion(); + TConstUnionArray leftUnionArray = getConstArray(); + TConstUnionArray rightUnionArray = rightNode->getConstArray(); + + // Figure out the size of the result + int newComps; + int constComps; + switch(op) { + case EOpMatrixTimesMatrix: + newComps = rightNode->getMatrixCols() * getMatrixRows(); + break; + case EOpMatrixTimesVector: + newComps = getMatrixRows(); + break; + case EOpVectorTimesMatrix: + newComps = rightNode->getMatrixCols(); + break; + default: + newComps = getType().computeNumComponents(); + constComps = rightConstantNode->getType().computeNumComponents(); + if (constComps == 1 && newComps > 1) { + // for a case like vec4 f = vec4(2,3,4,5) + 1.2; + TConstUnionArray smearedArray(newComps, rightNode->getConstArray()[0]); + rightUnionArray = smearedArray; + } else if (constComps > 1 && newComps == 1) { + // for a case like vec4 f = 1.2 + vec4(2,3,4,5); + newComps = constComps; + rightUnionArray = rightNode->getConstArray(); + TConstUnionArray smearedArray(newComps, getConstArray()[0]); + leftUnionArray = smearedArray; + returnType.shallowCopy(rightNode->getType()); + } + break; + } + + TConstUnionArray newConstArray(newComps); + TType constBool(EbtBool, EvqConst); + + switch(op) { + case EOpAdd: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] + rightUnionArray[i]; + break; + case EOpSub: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] - rightUnionArray[i]; + break; + + case EOpMul: + case EOpVectorTimesScalar: + case EOpMatrixTimesScalar: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] * rightUnionArray[i]; + break; + case EOpMatrixTimesMatrix: + for (int row = 0; row < getMatrixRows(); row++) { + for (int column = 0; column < rightNode->getMatrixCols(); column++) { + double sum = 0.0f; + for (int i = 0; i < rightNode->getMatrixRows(); i++) + sum += leftUnionArray[i * getMatrixRows() + row].getDConst() * rightUnionArray[column * rightNode->getMatrixRows() + i].getDConst(); + newConstArray[column * getMatrixRows() + row].setDConst(sum); + } + } + returnType.shallowCopy(TType(getType().getBasicType(), EvqConst, 0, rightNode->getMatrixCols(), getMatrixRows())); + break; + case EOpDiv: + for (int i = 0; i < newComps; i++) { + switch (getType().getBasicType()) { + case EbtDouble: + case EbtFloat: + case EbtFloat16: + if (rightUnionArray[i].getDConst() != 0.0) + newConstArray[i].setDConst(leftUnionArray[i].getDConst() / rightUnionArray[i].getDConst()); + else if (leftUnionArray[i].getDConst() > 0.0) + newConstArray[i].setDConst((double)INFINITY); + else if (leftUnionArray[i].getDConst() < 0.0) + newConstArray[i].setDConst(-(double)INFINITY); + else + newConstArray[i].setDConst((double)NAN); + break; + + case EbtInt: + if (rightUnionArray[i] == 0) + newConstArray[i].setIConst(0x7FFFFFFF); + else if (rightUnionArray[i].getIConst() == -1 && leftUnionArray[i].getIConst() == (int)-0x80000000ll) + newConstArray[i].setIConst((int)-0x80000000ll); + else + newConstArray[i].setIConst(leftUnionArray[i].getIConst() / rightUnionArray[i].getIConst()); + break; + + case EbtUint: + if (rightUnionArray[i] == 0u) + newConstArray[i].setUConst(0xFFFFFFFFu); + else + newConstArray[i].setUConst(leftUnionArray[i].getUConst() / rightUnionArray[i].getUConst()); + break; + +#ifndef GLSLANG_WEB + case EbtInt8: + if (rightUnionArray[i] == (signed char)0) + newConstArray[i].setI8Const((signed char)0x7F); + else if (rightUnionArray[i].getI8Const() == (signed char)-1 && leftUnionArray[i].getI8Const() == (signed char)-0x80) + newConstArray[i].setI8Const((signed char)-0x80); + else + newConstArray[i].setI8Const(leftUnionArray[i].getI8Const() / rightUnionArray[i].getI8Const()); + break; + + case EbtUint8: + if (rightUnionArray[i] == (unsigned char)0u) + newConstArray[i].setU8Const((unsigned char)0xFFu); + else + newConstArray[i].setU8Const(leftUnionArray[i].getU8Const() / rightUnionArray[i].getU8Const()); + break; + + case EbtInt16: + if (rightUnionArray[i] == (signed short)0) + newConstArray[i].setI16Const((signed short)0x7FFF); + else if (rightUnionArray[i].getI16Const() == (signed short)-1 && leftUnionArray[i].getI16Const() == (signed short)-0x8000) + newConstArray[i].setI16Const((signed short)-0x8000); + else + newConstArray[i].setI16Const(leftUnionArray[i].getI16Const() / rightUnionArray[i].getI16Const()); + break; + + case EbtUint16: + if (rightUnionArray[i] == (unsigned short)0u) + newConstArray[i].setU16Const((unsigned short)0xFFFFu); + else + newConstArray[i].setU16Const(leftUnionArray[i].getU16Const() / rightUnionArray[i].getU16Const()); + break; + + case EbtInt64: + if (rightUnionArray[i] == 0ll) + newConstArray[i].setI64Const(0x7FFFFFFFFFFFFFFFll); + else if (rightUnionArray[i].getI64Const() == -1 && leftUnionArray[i].getI64Const() == (long long)-0x8000000000000000ll) + newConstArray[i].setI64Const((long long)-0x8000000000000000ll); + else + newConstArray[i].setI64Const(leftUnionArray[i].getI64Const() / rightUnionArray[i].getI64Const()); + break; + + case EbtUint64: + if (rightUnionArray[i] == 0ull) + newConstArray[i].setU64Const(0xFFFFFFFFFFFFFFFFull); + else + newConstArray[i].setU64Const(leftUnionArray[i].getU64Const() / rightUnionArray[i].getU64Const()); + break; + default: + return 0; +#endif + } + } + break; + + case EOpMatrixTimesVector: + for (int i = 0; i < getMatrixRows(); i++) { + double sum = 0.0f; + for (int j = 0; j < rightNode->getVectorSize(); j++) { + sum += leftUnionArray[j*getMatrixRows() + i].getDConst() * rightUnionArray[j].getDConst(); + } + newConstArray[i].setDConst(sum); + } + + returnType.shallowCopy(TType(getBasicType(), EvqConst, getMatrixRows())); + break; + + case EOpVectorTimesMatrix: + for (int i = 0; i < rightNode->getMatrixCols(); i++) { + double sum = 0.0f; + for (int j = 0; j < getVectorSize(); j++) + sum += leftUnionArray[j].getDConst() * rightUnionArray[i*rightNode->getMatrixRows() + j].getDConst(); + newConstArray[i].setDConst(sum); + } + + returnType.shallowCopy(TType(getBasicType(), EvqConst, rightNode->getMatrixCols())); + break; + + case EOpMod: + for (int i = 0; i < newComps; i++) { + if (rightUnionArray[i] == 0) + newConstArray[i] = leftUnionArray[i]; + else { + switch (getType().getBasicType()) { + case EbtInt: + if (rightUnionArray[i].getIConst() == -1 && leftUnionArray[i].getIConst() == INT_MIN) { + newConstArray[i].setIConst(0); + break; + } else goto modulo_default; +#ifndef GLSLANG_WEB + case EbtInt64: + if (rightUnionArray[i].getI64Const() == -1 && leftUnionArray[i].getI64Const() == LLONG_MIN) { + newConstArray[i].setI64Const(0); + break; + } else goto modulo_default; + case EbtInt16: + if (rightUnionArray[i].getIConst() == -1 && leftUnionArray[i].getIConst() == SHRT_MIN) { + newConstArray[i].setIConst(0); + break; + } else goto modulo_default; +#endif + default: + modulo_default: + newConstArray[i] = leftUnionArray[i] % rightUnionArray[i]; + } + } + } + break; + + case EOpRightShift: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] >> rightUnionArray[i]; + break; + + case EOpLeftShift: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] << rightUnionArray[i]; + break; + + case EOpAnd: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] & rightUnionArray[i]; + break; + case EOpInclusiveOr: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] | rightUnionArray[i]; + break; + case EOpExclusiveOr: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] ^ rightUnionArray[i]; + break; + + case EOpLogicalAnd: // this code is written for possible future use, will not get executed currently + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] && rightUnionArray[i]; + break; + + case EOpLogicalOr: // this code is written for possible future use, will not get executed currently + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] || rightUnionArray[i]; + break; + + case EOpLogicalXor: + for (int i = 0; i < newComps; i++) { + switch (getType().getBasicType()) { + case EbtBool: newConstArray[i].setBConst((leftUnionArray[i] == rightUnionArray[i]) ? false : true); break; + default: assert(false && "Default missing"); + } + } + break; + + case EOpLessThan: + newConstArray[0].setBConst(leftUnionArray[0] < rightUnionArray[0]); + returnType.shallowCopy(constBool); + break; + case EOpGreaterThan: + newConstArray[0].setBConst(leftUnionArray[0] > rightUnionArray[0]); + returnType.shallowCopy(constBool); + break; + case EOpLessThanEqual: + newConstArray[0].setBConst(! (leftUnionArray[0] > rightUnionArray[0])); + returnType.shallowCopy(constBool); + break; + case EOpGreaterThanEqual: + newConstArray[0].setBConst(! (leftUnionArray[0] < rightUnionArray[0])); + returnType.shallowCopy(constBool); + break; + case EOpEqual: + newConstArray[0].setBConst(rightNode->getConstArray() == leftUnionArray); + returnType.shallowCopy(constBool); + break; + case EOpNotEqual: + newConstArray[0].setBConst(rightNode->getConstArray() != leftUnionArray); + returnType.shallowCopy(constBool); + break; + + default: + return 0; + } + + TIntermConstantUnion *newNode = new TIntermConstantUnion(newConstArray, returnType); + newNode->setLoc(getLoc()); + + return newNode; +} + +// +// Do single unary node folding +// +// Returns a new node representing the result. +// +TIntermTyped* TIntermConstantUnion::fold(TOperator op, const TType& returnType) const +{ + // First, size the result, which is mostly the same as the argument's size, + // but not always, and classify what is componentwise. + // Also, eliminate cases that can't be compile-time constant. + int resultSize; + bool componentWise = true; + + int objectSize = getType().computeNumComponents(); + switch (op) { + case EOpDeterminant: + case EOpAny: + case EOpAll: + case EOpLength: + componentWise = false; + resultSize = 1; + break; + + case EOpEmitStreamVertex: + case EOpEndStreamPrimitive: + // These don't fold + return nullptr; + + case EOpPackSnorm2x16: + case EOpPackUnorm2x16: + case EOpPackHalf2x16: + componentWise = false; + resultSize = 1; + break; + + case EOpUnpackSnorm2x16: + case EOpUnpackUnorm2x16: + case EOpUnpackHalf2x16: + componentWise = false; + resultSize = 2; + break; + + case EOpPack16: + case EOpPack32: + case EOpPack64: + case EOpUnpack32: + case EOpUnpack16: + case EOpUnpack8: + case EOpNormalize: + componentWise = false; + resultSize = objectSize; + break; + + default: + resultSize = objectSize; + break; + } + + // Set up for processing + TConstUnionArray newConstArray(resultSize); + const TConstUnionArray& unionArray = getConstArray(); + + // Process non-component-wise operations + switch (op) { + case EOpLength: + case EOpNormalize: + { + double sum = 0; + for (int i = 0; i < objectSize; i++) + sum += unionArray[i].getDConst() * unionArray[i].getDConst(); + double length = sqrt(sum); + if (op == EOpLength) + newConstArray[0].setDConst(length); + else { + for (int i = 0; i < objectSize; i++) + newConstArray[i].setDConst(unionArray[i].getDConst() / length); + } + break; + } + + case EOpAny: + { + bool result = false; + for (int i = 0; i < objectSize; i++) { + if (unionArray[i].getBConst()) + result = true; + } + newConstArray[0].setBConst(result); + break; + } + case EOpAll: + { + bool result = true; + for (int i = 0; i < objectSize; i++) { + if (! unionArray[i].getBConst()) + result = false; + } + newConstArray[0].setBConst(result); + break; + } + + case EOpPackSnorm2x16: + case EOpPackUnorm2x16: + case EOpPackHalf2x16: + case EOpPack16: + case EOpPack32: + case EOpPack64: + case EOpUnpack32: + case EOpUnpack16: + case EOpUnpack8: + + case EOpUnpackSnorm2x16: + case EOpUnpackUnorm2x16: + case EOpUnpackHalf2x16: + + case EOpDeterminant: + case EOpMatrixInverse: + case EOpTranspose: + return nullptr; + + default: + assert(componentWise); + break; + } + + // Turn off the componentwise loop + if (! componentWise) + objectSize = 0; + + // Process component-wise operations + for (int i = 0; i < objectSize; i++) { + switch (op) { + case EOpNegative: + switch (getType().getBasicType()) { + case EbtDouble: + case EbtFloat16: + case EbtFloat: newConstArray[i].setDConst(-unionArray[i].getDConst()); break; + case EbtInt: newConstArray[i].setIConst(-unionArray[i].getIConst()); break; + case EbtUint: newConstArray[i].setUConst(static_cast(-static_cast(unionArray[i].getUConst()))); break; +#ifndef GLSLANG_WEB + case EbtInt8: newConstArray[i].setI8Const(-unionArray[i].getI8Const()); break; + case EbtUint8: newConstArray[i].setU8Const(static_cast(-static_cast(unionArray[i].getU8Const()))); break; + case EbtInt16: newConstArray[i].setI16Const(-unionArray[i].getI16Const()); break; + case EbtUint16:newConstArray[i].setU16Const(static_cast(-static_cast(unionArray[i].getU16Const()))); break; + case EbtInt64: newConstArray[i].setI64Const(-unionArray[i].getI64Const()); break; + case EbtUint64: newConstArray[i].setU64Const(static_cast(-static_cast(unionArray[i].getU64Const()))); break; +#endif + default: + return nullptr; + } + break; + case EOpLogicalNot: + case EOpVectorLogicalNot: + switch (getType().getBasicType()) { + case EbtBool: newConstArray[i].setBConst(!unionArray[i].getBConst()); break; + default: + return nullptr; + } + break; + case EOpBitwiseNot: + newConstArray[i] = ~unionArray[i]; + break; + case EOpRadians: + newConstArray[i].setDConst(unionArray[i].getDConst() * pi / 180.0); + break; + case EOpDegrees: + newConstArray[i].setDConst(unionArray[i].getDConst() * 180.0 / pi); + break; + case EOpSin: + newConstArray[i].setDConst(sin(unionArray[i].getDConst())); + break; + case EOpCos: + newConstArray[i].setDConst(cos(unionArray[i].getDConst())); + break; + case EOpTan: + newConstArray[i].setDConst(tan(unionArray[i].getDConst())); + break; + case EOpAsin: + newConstArray[i].setDConst(asin(unionArray[i].getDConst())); + break; + case EOpAcos: + newConstArray[i].setDConst(acos(unionArray[i].getDConst())); + break; + case EOpAtan: + newConstArray[i].setDConst(atan(unionArray[i].getDConst())); + break; + + case EOpDPdx: + case EOpDPdy: + case EOpFwidth: + case EOpDPdxFine: + case EOpDPdyFine: + case EOpFwidthFine: + case EOpDPdxCoarse: + case EOpDPdyCoarse: + case EOpFwidthCoarse: + // The derivatives are all mandated to create a constant 0. + newConstArray[i].setDConst(0.0); + break; + + case EOpExp: + newConstArray[i].setDConst(exp(unionArray[i].getDConst())); + break; + case EOpLog: + newConstArray[i].setDConst(log(unionArray[i].getDConst())); + break; + case EOpExp2: + { + const double inv_log2_e = 0.69314718055994530941723212145818; + newConstArray[i].setDConst(exp(unionArray[i].getDConst() * inv_log2_e)); + break; + } + case EOpLog2: + { + const double log2_e = 1.4426950408889634073599246810019; + newConstArray[i].setDConst(log2_e * log(unionArray[i].getDConst())); + break; + } + case EOpSqrt: + newConstArray[i].setDConst(sqrt(unionArray[i].getDConst())); + break; + case EOpInverseSqrt: + newConstArray[i].setDConst(1.0 / sqrt(unionArray[i].getDConst())); + break; + + case EOpAbs: + if (unionArray[i].getType() == EbtDouble) + newConstArray[i].setDConst(fabs(unionArray[i].getDConst())); + else if (unionArray[i].getType() == EbtInt) + newConstArray[i].setIConst(abs(unionArray[i].getIConst())); + else + newConstArray[i] = unionArray[i]; + break; + case EOpSign: + #define SIGN(X) (X == 0 ? 0 : (X < 0 ? -1 : 1)) + if (unionArray[i].getType() == EbtDouble) + newConstArray[i].setDConst(SIGN(unionArray[i].getDConst())); + else + newConstArray[i].setIConst(SIGN(unionArray[i].getIConst())); + break; + case EOpFloor: + newConstArray[i].setDConst(floor(unionArray[i].getDConst())); + break; + case EOpTrunc: + if (unionArray[i].getDConst() > 0) + newConstArray[i].setDConst(floor(unionArray[i].getDConst())); + else + newConstArray[i].setDConst(ceil(unionArray[i].getDConst())); + break; + case EOpRound: + newConstArray[i].setDConst(floor(0.5 + unionArray[i].getDConst())); + break; + case EOpRoundEven: + { + double flr = floor(unionArray[i].getDConst()); + bool even = flr / 2.0 == floor(flr / 2.0); + double rounded = even ? ceil(unionArray[i].getDConst() - 0.5) : floor(unionArray[i].getDConst() + 0.5); + newConstArray[i].setDConst(rounded); + break; + } + case EOpCeil: + newConstArray[i].setDConst(ceil(unionArray[i].getDConst())); + break; + case EOpFract: + { + double x = unionArray[i].getDConst(); + newConstArray[i].setDConst(x - floor(x)); + break; + } + + case EOpIsNan: + { + newConstArray[i].setBConst(isNan(unionArray[i].getDConst())); + break; + } + case EOpIsInf: + { + newConstArray[i].setBConst(isInf(unionArray[i].getDConst())); + break; + } + + case EOpConvIntToBool: + newConstArray[i].setBConst(unionArray[i].getIConst() != 0); break; + case EOpConvUintToBool: + newConstArray[i].setBConst(unionArray[i].getUConst() != 0); break; + case EOpConvBoolToInt: + newConstArray[i].setIConst(unionArray[i].getBConst()); break; + case EOpConvBoolToUint: + newConstArray[i].setUConst(unionArray[i].getBConst()); break; + case EOpConvIntToUint: + newConstArray[i].setUConst(unionArray[i].getIConst()); break; + case EOpConvUintToInt: + newConstArray[i].setIConst(unionArray[i].getUConst()); break; + + case EOpConvFloatToBool: + case EOpConvDoubleToBool: + newConstArray[i].setBConst(unionArray[i].getDConst() != 0); break; + + case EOpConvBoolToFloat: + case EOpConvBoolToDouble: + newConstArray[i].setDConst(unionArray[i].getBConst()); break; + + case EOpConvIntToFloat: + case EOpConvIntToDouble: + newConstArray[i].setDConst(unionArray[i].getIConst()); break; + + case EOpConvUintToFloat: + case EOpConvUintToDouble: + newConstArray[i].setDConst(unionArray[i].getUConst()); break; + + case EOpConvDoubleToFloat: + case EOpConvFloatToDouble: + newConstArray[i].setDConst(unionArray[i].getDConst()); break; + + case EOpConvFloatToUint: + case EOpConvDoubleToUint: + newConstArray[i].setUConst(static_cast(unionArray[i].getDConst())); break; + + case EOpConvFloatToInt: + case EOpConvDoubleToInt: + newConstArray[i].setIConst(static_cast(unionArray[i].getDConst())); break; + +#ifndef GLSLANG_WEB + case EOpConvInt8ToBool: + newConstArray[i].setBConst(unionArray[i].getI8Const() != 0); break; + case EOpConvUint8ToBool: + newConstArray[i].setBConst(unionArray[i].getU8Const() != 0); break; + case EOpConvInt16ToBool: + newConstArray[i].setBConst(unionArray[i].getI16Const() != 0); break; + case EOpConvUint16ToBool: + newConstArray[i].setBConst(unionArray[i].getU16Const() != 0); break; + case EOpConvInt64ToBool: + newConstArray[i].setBConst(unionArray[i].getI64Const() != 0); break; + case EOpConvUint64ToBool: + newConstArray[i].setBConst(unionArray[i].getI64Const() != 0); break; + case EOpConvFloat16ToBool: + newConstArray[i].setBConst(unionArray[i].getDConst() != 0); break; + + case EOpConvBoolToInt8: + newConstArray[i].setI8Const(unionArray[i].getBConst()); break; + case EOpConvBoolToUint8: + newConstArray[i].setU8Const(unionArray[i].getBConst()); break; + case EOpConvBoolToInt16: + newConstArray[i].setI16Const(unionArray[i].getBConst()); break; + case EOpConvBoolToUint16: + newConstArray[i].setU16Const(unionArray[i].getBConst()); break; + case EOpConvBoolToInt64: + newConstArray[i].setI64Const(unionArray[i].getBConst()); break; + case EOpConvBoolToUint64: + newConstArray[i].setU64Const(unionArray[i].getBConst()); break; + case EOpConvBoolToFloat16: + newConstArray[i].setDConst(unionArray[i].getBConst()); break; + + case EOpConvInt8ToInt16: + newConstArray[i].setI16Const(unionArray[i].getI8Const()); break; + case EOpConvInt8ToInt: + newConstArray[i].setIConst(unionArray[i].getI8Const()); break; + case EOpConvInt8ToInt64: + newConstArray[i].setI64Const(unionArray[i].getI8Const()); break; + case EOpConvInt8ToUint8: + newConstArray[i].setU8Const(unionArray[i].getI8Const()); break; + case EOpConvInt8ToUint16: + newConstArray[i].setU16Const(unionArray[i].getI8Const()); break; + case EOpConvInt8ToUint: + newConstArray[i].setUConst(unionArray[i].getI8Const()); break; + case EOpConvInt8ToUint64: + newConstArray[i].setU64Const(unionArray[i].getI8Const()); break; + case EOpConvUint8ToInt8: + newConstArray[i].setI8Const(unionArray[i].getU8Const()); break; + case EOpConvUint8ToInt16: + newConstArray[i].setI16Const(unionArray[i].getU8Const()); break; + case EOpConvUint8ToInt: + newConstArray[i].setIConst(unionArray[i].getU8Const()); break; + case EOpConvUint8ToInt64: + newConstArray[i].setI64Const(unionArray[i].getU8Const()); break; + case EOpConvUint8ToUint16: + newConstArray[i].setU16Const(unionArray[i].getU8Const()); break; + case EOpConvUint8ToUint: + newConstArray[i].setUConst(unionArray[i].getU8Const()); break; + case EOpConvUint8ToUint64: + newConstArray[i].setU64Const(unionArray[i].getU8Const()); break; + case EOpConvInt8ToFloat16: + newConstArray[i].setDConst(unionArray[i].getI8Const()); break; + case EOpConvInt8ToFloat: + newConstArray[i].setDConst(unionArray[i].getI8Const()); break; + case EOpConvInt8ToDouble: + newConstArray[i].setDConst(unionArray[i].getI8Const()); break; + case EOpConvUint8ToFloat16: + newConstArray[i].setDConst(unionArray[i].getU8Const()); break; + case EOpConvUint8ToFloat: + newConstArray[i].setDConst(unionArray[i].getU8Const()); break; + case EOpConvUint8ToDouble: + newConstArray[i].setDConst(unionArray[i].getU8Const()); break; + + case EOpConvInt16ToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getI16Const())); break; + case EOpConvInt16ToInt: + newConstArray[i].setIConst(unionArray[i].getI16Const()); break; + case EOpConvInt16ToInt64: + newConstArray[i].setI64Const(unionArray[i].getI16Const()); break; + case EOpConvInt16ToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getI16Const())); break; + case EOpConvInt16ToUint16: + newConstArray[i].setU16Const(unionArray[i].getI16Const()); break; + case EOpConvInt16ToUint: + newConstArray[i].setUConst(unionArray[i].getI16Const()); break; + case EOpConvInt16ToUint64: + newConstArray[i].setU64Const(unionArray[i].getI16Const()); break; + case EOpConvUint16ToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getU16Const())); break; + case EOpConvUint16ToInt16: + newConstArray[i].setI16Const(unionArray[i].getU16Const()); break; + case EOpConvUint16ToInt: + newConstArray[i].setIConst(unionArray[i].getU16Const()); break; + case EOpConvUint16ToInt64: + newConstArray[i].setI64Const(unionArray[i].getU16Const()); break; + case EOpConvUint16ToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getU16Const())); break; + + case EOpConvUint16ToUint: + newConstArray[i].setUConst(unionArray[i].getU16Const()); break; + case EOpConvUint16ToUint64: + newConstArray[i].setU64Const(unionArray[i].getU16Const()); break; + case EOpConvInt16ToFloat16: + newConstArray[i].setDConst(unionArray[i].getI16Const()); break; + case EOpConvInt16ToFloat: + newConstArray[i].setDConst(unionArray[i].getI16Const()); break; + case EOpConvInt16ToDouble: + newConstArray[i].setDConst(unionArray[i].getI16Const()); break; + case EOpConvUint16ToFloat16: + newConstArray[i].setDConst(unionArray[i].getU16Const()); break; + case EOpConvUint16ToFloat: + newConstArray[i].setDConst(unionArray[i].getU16Const()); break; + case EOpConvUint16ToDouble: + newConstArray[i].setDConst(unionArray[i].getU16Const()); break; + + case EOpConvIntToInt8: + newConstArray[i].setI8Const((signed char)unionArray[i].getIConst()); break; + case EOpConvIntToInt16: + newConstArray[i].setI16Const((signed short)unionArray[i].getIConst()); break; + case EOpConvIntToInt64: + newConstArray[i].setI64Const(unionArray[i].getIConst()); break; + case EOpConvIntToUint8: + newConstArray[i].setU8Const((unsigned char)unionArray[i].getIConst()); break; + case EOpConvIntToUint16: + newConstArray[i].setU16Const((unsigned char)unionArray[i].getIConst()); break; + case EOpConvIntToUint64: + newConstArray[i].setU64Const(unionArray[i].getIConst()); break; + + case EOpConvUintToInt8: + newConstArray[i].setI8Const((signed char)unionArray[i].getUConst()); break; + case EOpConvUintToInt16: + newConstArray[i].setI16Const((signed short)unionArray[i].getUConst()); break; + case EOpConvUintToInt64: + newConstArray[i].setI64Const(unionArray[i].getUConst()); break; + case EOpConvUintToUint8: + newConstArray[i].setU8Const((unsigned char)unionArray[i].getUConst()); break; + case EOpConvUintToUint16: + newConstArray[i].setU16Const((unsigned short)unionArray[i].getUConst()); break; + case EOpConvUintToUint64: + newConstArray[i].setU64Const(unionArray[i].getUConst()); break; + case EOpConvIntToFloat16: + newConstArray[i].setDConst(unionArray[i].getIConst()); break; + case EOpConvUintToFloat16: + newConstArray[i].setDConst(unionArray[i].getUConst()); break; + case EOpConvInt64ToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToInt16: + newConstArray[i].setI16Const(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToInt: + newConstArray[i].setIConst(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToUint16: + newConstArray[i].setU16Const(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToUint: + newConstArray[i].setUConst(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToUint64: + newConstArray[i].setU64Const(unionArray[i].getI64Const()); break; + case EOpConvUint64ToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToInt16: + newConstArray[i].setI16Const(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToInt: + newConstArray[i].setIConst(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToInt64: + newConstArray[i].setI64Const(unionArray[i].getU64Const()); break; + case EOpConvUint64ToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToUint16: + newConstArray[i].setU16Const(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToUint: + newConstArray[i].setUConst(static_cast(unionArray[i].getU64Const())); break; + case EOpConvInt64ToFloat16: + newConstArray[i].setDConst(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToFloat: + newConstArray[i].setDConst(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToDouble: + newConstArray[i].setDConst(static_cast(unionArray[i].getI64Const())); break; + case EOpConvUint64ToFloat16: + newConstArray[i].setDConst(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToFloat: + newConstArray[i].setDConst(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToDouble: + newConstArray[i].setDConst(static_cast(unionArray[i].getU64Const())); break; + case EOpConvFloat16ToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToInt16: + newConstArray[i].setI16Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToInt: + newConstArray[i].setIConst(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToInt64: + newConstArray[i].setI64Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToUint16: + newConstArray[i].setU16Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToUint: + newConstArray[i].setUConst(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToUint64: + newConstArray[i].setU64Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToFloat: + newConstArray[i].setDConst(unionArray[i].getDConst()); break; + case EOpConvFloat16ToDouble: + newConstArray[i].setDConst(unionArray[i].getDConst()); break; + case EOpConvFloatToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloatToInt16: + newConstArray[i].setI16Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloatToInt64: + newConstArray[i].setI64Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloatToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloatToUint16: + newConstArray[i].setU16Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloatToUint64: + newConstArray[i].setU64Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloatToFloat16: + newConstArray[i].setDConst(unionArray[i].getDConst()); break; + case EOpConvDoubleToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvDoubleToInt16: + newConstArray[i].setI16Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvDoubleToInt64: + newConstArray[i].setI64Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvDoubleToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvDoubleToUint16: + newConstArray[i].setU16Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvDoubleToUint64: + newConstArray[i].setU64Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvDoubleToFloat16: + newConstArray[i].setDConst(unionArray[i].getDConst()); break; + case EOpConvPtrToUint64: + case EOpConvUint64ToPtr: + case EOpConstructReference: + newConstArray[i].setU64Const(unionArray[i].getU64Const()); break; +#endif + + // TODO: 3.0 Functionality: unary constant folding: the rest of the ops have to be fleshed out + + case EOpSinh: + case EOpCosh: + case EOpTanh: + case EOpAsinh: + case EOpAcosh: + case EOpAtanh: + + case EOpFloatBitsToInt: + case EOpFloatBitsToUint: + case EOpIntBitsToFloat: + case EOpUintBitsToFloat: + case EOpDoubleBitsToInt64: + case EOpDoubleBitsToUint64: + case EOpInt64BitsToDouble: + case EOpUint64BitsToDouble: + case EOpFloat16BitsToInt16: + case EOpFloat16BitsToUint16: + case EOpInt16BitsToFloat16: + case EOpUint16BitsToFloat16: + default: + return nullptr; + } + } + + TIntermConstantUnion *newNode = new TIntermConstantUnion(newConstArray, returnType); + newNode->getWritableType().getQualifier().storage = EvqConst; + newNode->setLoc(getLoc()); + + return newNode; +} + +// +// Do constant folding for an aggregate node that has all its children +// as constants and an operator that requires constant folding. +// +TIntermTyped* TIntermediate::fold(TIntermAggregate* aggrNode) +{ + if (aggrNode == nullptr) + return aggrNode; + + if (! areAllChildConst(aggrNode)) + return aggrNode; + + if (aggrNode->isConstructor()) + return foldConstructor(aggrNode); + + TIntermSequence& children = aggrNode->getSequence(); + + // First, see if this is an operation to constant fold, kick out if not, + // see what size the result is if so. + + bool componentwise = false; // will also say componentwise if a scalar argument gets repeated to make per-component results + int objectSize; + switch (aggrNode->getOp()) { + case EOpAtan: + case EOpPow: + case EOpMin: + case EOpMax: + case EOpMix: + case EOpMod: + case EOpClamp: + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + case EOpVectorEqual: + case EOpVectorNotEqual: + componentwise = true; + objectSize = children[0]->getAsConstantUnion()->getType().computeNumComponents(); + break; + case EOpCross: + case EOpReflect: + case EOpRefract: + case EOpFaceForward: + objectSize = children[0]->getAsConstantUnion()->getType().computeNumComponents(); + break; + case EOpDistance: + case EOpDot: + objectSize = 1; + break; + case EOpOuterProduct: + objectSize = children[0]->getAsTyped()->getType().getVectorSize() * + children[1]->getAsTyped()->getType().getVectorSize(); + break; + case EOpStep: + componentwise = true; + objectSize = std::max(children[0]->getAsTyped()->getType().getVectorSize(), + children[1]->getAsTyped()->getType().getVectorSize()); + break; + case EOpSmoothStep: + componentwise = true; + objectSize = std::max(children[0]->getAsTyped()->getType().getVectorSize(), + children[2]->getAsTyped()->getType().getVectorSize()); + break; + default: + return aggrNode; + } + TConstUnionArray newConstArray(objectSize); + + TVector childConstUnions; + for (unsigned int arg = 0; arg < children.size(); ++arg) + childConstUnions.push_back(children[arg]->getAsConstantUnion()->getConstArray()); + + if (componentwise) { + for (int comp = 0; comp < objectSize; comp++) { + + // some arguments are scalars instead of matching vectors; simulate a smear + int arg0comp = std::min(comp, children[0]->getAsTyped()->getType().getVectorSize() - 1); + int arg1comp = 0; + if (children.size() > 1) + arg1comp = std::min(comp, children[1]->getAsTyped()->getType().getVectorSize() - 1); + int arg2comp = 0; + if (children.size() > 2) + arg2comp = std::min(comp, children[2]->getAsTyped()->getType().getVectorSize() - 1); + + switch (aggrNode->getOp()) { + case EOpAtan: + newConstArray[comp].setDConst(atan2(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst())); + break; + case EOpPow: + newConstArray[comp].setDConst(pow(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst())); + break; + case EOpMod: + { + double arg0 = childConstUnions[0][arg0comp].getDConst(); + double arg1 = childConstUnions[1][arg1comp].getDConst(); + double result = arg0 - arg1 * floor(arg0 / arg1); + newConstArray[comp].setDConst(result); + break; + } + case EOpMin: + switch(children[0]->getAsTyped()->getBasicType()) { + case EbtFloat16: + case EbtFloat: + case EbtDouble: + newConstArray[comp].setDConst(std::min(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst())); + break; + case EbtInt: + newConstArray[comp].setIConst(std::min(childConstUnions[0][arg0comp].getIConst(), childConstUnions[1][arg1comp].getIConst())); + break; + case EbtUint: + newConstArray[comp].setUConst(std::min(childConstUnions[0][arg0comp].getUConst(), childConstUnions[1][arg1comp].getUConst())); + break; +#ifndef GLSLANG_WEB + case EbtInt8: + newConstArray[comp].setI8Const(std::min(childConstUnions[0][arg0comp].getI8Const(), childConstUnions[1][arg1comp].getI8Const())); + break; + case EbtUint8: + newConstArray[comp].setU8Const(std::min(childConstUnions[0][arg0comp].getU8Const(), childConstUnions[1][arg1comp].getU8Const())); + break; + case EbtInt16: + newConstArray[comp].setI16Const(std::min(childConstUnions[0][arg0comp].getI16Const(), childConstUnions[1][arg1comp].getI16Const())); + break; + case EbtUint16: + newConstArray[comp].setU16Const(std::min(childConstUnions[0][arg0comp].getU16Const(), childConstUnions[1][arg1comp].getU16Const())); + break; + case EbtInt64: + newConstArray[comp].setI64Const(std::min(childConstUnions[0][arg0comp].getI64Const(), childConstUnions[1][arg1comp].getI64Const())); + break; + case EbtUint64: + newConstArray[comp].setU64Const(std::min(childConstUnions[0][arg0comp].getU64Const(), childConstUnions[1][arg1comp].getU64Const())); + break; +#endif + default: assert(false && "Default missing"); + } + break; + case EOpMax: + switch(children[0]->getAsTyped()->getBasicType()) { + case EbtFloat16: + case EbtFloat: + case EbtDouble: + newConstArray[comp].setDConst(std::max(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst())); + break; + case EbtInt: + newConstArray[comp].setIConst(std::max(childConstUnions[0][arg0comp].getIConst(), childConstUnions[1][arg1comp].getIConst())); + break; + case EbtUint: + newConstArray[comp].setUConst(std::max(childConstUnions[0][arg0comp].getUConst(), childConstUnions[1][arg1comp].getUConst())); + break; +#ifndef GLSLANG_WEB + case EbtInt8: + newConstArray[comp].setI8Const(std::max(childConstUnions[0][arg0comp].getI8Const(), childConstUnions[1][arg1comp].getI8Const())); + break; + case EbtUint8: + newConstArray[comp].setU8Const(std::max(childConstUnions[0][arg0comp].getU8Const(), childConstUnions[1][arg1comp].getU8Const())); + break; + case EbtInt16: + newConstArray[comp].setI16Const(std::max(childConstUnions[0][arg0comp].getI16Const(), childConstUnions[1][arg1comp].getI16Const())); + break; + case EbtUint16: + newConstArray[comp].setU16Const(std::max(childConstUnions[0][arg0comp].getU16Const(), childConstUnions[1][arg1comp].getU16Const())); + break; + case EbtInt64: + newConstArray[comp].setI64Const(std::max(childConstUnions[0][arg0comp].getI64Const(), childConstUnions[1][arg1comp].getI64Const())); + break; + case EbtUint64: + newConstArray[comp].setU64Const(std::max(childConstUnions[0][arg0comp].getU64Const(), childConstUnions[1][arg1comp].getU64Const())); + break; +#endif + default: assert(false && "Default missing"); + } + break; + case EOpClamp: + switch(children[0]->getAsTyped()->getBasicType()) { + case EbtFloat16: + case EbtFloat: + case EbtDouble: + newConstArray[comp].setDConst(std::min(std::max(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst()), + childConstUnions[2][arg2comp].getDConst())); + break; + case EbtUint: + newConstArray[comp].setUConst(std::min(std::max(childConstUnions[0][arg0comp].getUConst(), childConstUnions[1][arg1comp].getUConst()), + childConstUnions[2][arg2comp].getUConst())); + break; +#ifndef GLSLANG_WEB + case EbtInt8: + newConstArray[comp].setI8Const(std::min(std::max(childConstUnions[0][arg0comp].getI8Const(), childConstUnions[1][arg1comp].getI8Const()), + childConstUnions[2][arg2comp].getI8Const())); + break; + case EbtUint8: + newConstArray[comp].setU8Const(std::min(std::max(childConstUnions[0][arg0comp].getU8Const(), childConstUnions[1][arg1comp].getU8Const()), + childConstUnions[2][arg2comp].getU8Const())); + break; + case EbtInt16: + newConstArray[comp].setI16Const(std::min(std::max(childConstUnions[0][arg0comp].getI16Const(), childConstUnions[1][arg1comp].getI16Const()), + childConstUnions[2][arg2comp].getI16Const())); + break; + case EbtUint16: + newConstArray[comp].setU16Const(std::min(std::max(childConstUnions[0][arg0comp].getU16Const(), childConstUnions[1][arg1comp].getU16Const()), + childConstUnions[2][arg2comp].getU16Const())); + break; + case EbtInt: + newConstArray[comp].setIConst(std::min(std::max(childConstUnions[0][arg0comp].getIConst(), childConstUnions[1][arg1comp].getIConst()), + childConstUnions[2][arg2comp].getIConst())); + break; + case EbtInt64: + newConstArray[comp].setI64Const(std::min(std::max(childConstUnions[0][arg0comp].getI64Const(), childConstUnions[1][arg1comp].getI64Const()), + childConstUnions[2][arg2comp].getI64Const())); + break; + case EbtUint64: + newConstArray[comp].setU64Const(std::min(std::max(childConstUnions[0][arg0comp].getU64Const(), childConstUnions[1][arg1comp].getU64Const()), + childConstUnions[2][arg2comp].getU64Const())); + break; +#endif + default: assert(false && "Default missing"); + } + break; + case EOpLessThan: + newConstArray[comp].setBConst(childConstUnions[0][arg0comp] < childConstUnions[1][arg1comp]); + break; + case EOpGreaterThan: + newConstArray[comp].setBConst(childConstUnions[0][arg0comp] > childConstUnions[1][arg1comp]); + break; + case EOpLessThanEqual: + newConstArray[comp].setBConst(! (childConstUnions[0][arg0comp] > childConstUnions[1][arg1comp])); + break; + case EOpGreaterThanEqual: + newConstArray[comp].setBConst(! (childConstUnions[0][arg0comp] < childConstUnions[1][arg1comp])); + break; + case EOpVectorEqual: + newConstArray[comp].setBConst(childConstUnions[0][arg0comp] == childConstUnions[1][arg1comp]); + break; + case EOpVectorNotEqual: + newConstArray[comp].setBConst(childConstUnions[0][arg0comp] != childConstUnions[1][arg1comp]); + break; + case EOpMix: + if (!children[0]->getAsTyped()->isFloatingDomain()) + return aggrNode; + if (children[2]->getAsTyped()->getBasicType() == EbtBool) { + newConstArray[comp].setDConst(childConstUnions[2][arg2comp].getBConst() + ? childConstUnions[1][arg1comp].getDConst() + : childConstUnions[0][arg0comp].getDConst()); + } else { + newConstArray[comp].setDConst( + childConstUnions[0][arg0comp].getDConst() * (1.0 - childConstUnions[2][arg2comp].getDConst()) + + childConstUnions[1][arg1comp].getDConst() * childConstUnions[2][arg2comp].getDConst()); + } + break; + case EOpStep: + newConstArray[comp].setDConst(childConstUnions[1][arg1comp].getDConst() < childConstUnions[0][arg0comp].getDConst() ? 0.0 : 1.0); + break; + case EOpSmoothStep: + { + double t = (childConstUnions[2][arg2comp].getDConst() - childConstUnions[0][arg0comp].getDConst()) / + (childConstUnions[1][arg1comp].getDConst() - childConstUnions[0][arg0comp].getDConst()); + if (t < 0.0) + t = 0.0; + if (t > 1.0) + t = 1.0; + newConstArray[comp].setDConst(t * t * (3.0 - 2.0 * t)); + break; + } + default: + return aggrNode; + } + } + } else { + // Non-componentwise... + + int numComps = children[0]->getAsConstantUnion()->getType().computeNumComponents(); + double dot; + + switch (aggrNode->getOp()) { + case EOpDistance: + { + double sum = 0.0; + for (int comp = 0; comp < numComps; ++comp) { + double diff = childConstUnions[1][comp].getDConst() - childConstUnions[0][comp].getDConst(); + sum += diff * diff; + } + newConstArray[0].setDConst(sqrt(sum)); + break; + } + case EOpDot: + newConstArray[0].setDConst(childConstUnions[0].dot(childConstUnions[1])); + break; + case EOpCross: + newConstArray[0] = childConstUnions[0][1] * childConstUnions[1][2] - childConstUnions[0][2] * childConstUnions[1][1]; + newConstArray[1] = childConstUnions[0][2] * childConstUnions[1][0] - childConstUnions[0][0] * childConstUnions[1][2]; + newConstArray[2] = childConstUnions[0][0] * childConstUnions[1][1] - childConstUnions[0][1] * childConstUnions[1][0]; + break; + case EOpFaceForward: + // If dot(Nref, I) < 0 return N, otherwise return -N: Arguments are (N, I, Nref). + dot = childConstUnions[1].dot(childConstUnions[2]); + for (int comp = 0; comp < numComps; ++comp) { + if (dot < 0.0) + newConstArray[comp] = childConstUnions[0][comp]; + else + newConstArray[comp].setDConst(-childConstUnions[0][comp].getDConst()); + } + break; + case EOpReflect: + // I - 2 * dot(N, I) * N: Arguments are (I, N). + dot = childConstUnions[0].dot(childConstUnions[1]); + dot *= 2.0; + for (int comp = 0; comp < numComps; ++comp) + newConstArray[comp].setDConst(childConstUnions[0][comp].getDConst() - dot * childConstUnions[1][comp].getDConst()); + break; + case EOpRefract: + { + // Arguments are (I, N, eta). + // k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I)) + // if (k < 0.0) + // return dvec(0.0) + // else + // return eta * I - (eta * dot(N, I) + sqrt(k)) * N + dot = childConstUnions[0].dot(childConstUnions[1]); + double eta = childConstUnions[2][0].getDConst(); + double k = 1.0 - eta * eta * (1.0 - dot * dot); + if (k < 0.0) { + for (int comp = 0; comp < numComps; ++comp) + newConstArray[comp].setDConst(0.0); + } else { + for (int comp = 0; comp < numComps; ++comp) + newConstArray[comp].setDConst(eta * childConstUnions[0][comp].getDConst() - (eta * dot + sqrt(k)) * childConstUnions[1][comp].getDConst()); + } + break; + } + case EOpOuterProduct: + { + int numRows = numComps; + int numCols = children[1]->getAsConstantUnion()->getType().computeNumComponents(); + for (int row = 0; row < numRows; ++row) + for (int col = 0; col < numCols; ++col) + newConstArray[col * numRows + row] = childConstUnions[0][row] * childConstUnions[1][col]; + break; + } + default: + return aggrNode; + } + } + + TIntermConstantUnion *newNode = new TIntermConstantUnion(newConstArray, aggrNode->getType()); + newNode->getWritableType().getQualifier().storage = EvqConst; + newNode->setLoc(aggrNode->getLoc()); + + return newNode; +} + +bool TIntermediate::areAllChildConst(TIntermAggregate* aggrNode) +{ + bool allConstant = true; + + // check if all the child nodes are constants so that they can be inserted into + // the parent node + if (aggrNode) { + TIntermSequence& childSequenceVector = aggrNode->getSequence(); + for (TIntermSequence::iterator p = childSequenceVector.begin(); + p != childSequenceVector.end(); p++) { + if (!(*p)->getAsTyped()->getAsConstantUnion()) + return false; + } + } + + return allConstant; +} + +TIntermTyped* TIntermediate::foldConstructor(TIntermAggregate* aggrNode) +{ + bool error = false; + + TConstUnionArray unionArray(aggrNode->getType().computeNumComponents()); + if (aggrNode->getSequence().size() == 1) + error = parseConstTree(aggrNode, unionArray, aggrNode->getOp(), aggrNode->getType(), true); + else + error = parseConstTree(aggrNode, unionArray, aggrNode->getOp(), aggrNode->getType()); + + if (error) + return aggrNode; + + return addConstantUnion(unionArray, aggrNode->getType(), aggrNode->getLoc()); +} + +// +// Constant folding of a bracket (array-style) dereference or struct-like dot +// dereference. Can handle anything except a multi-character swizzle, though +// all swizzles may go to foldSwizzle(). +// +TIntermTyped* TIntermediate::foldDereference(TIntermTyped* node, int index, const TSourceLoc& loc) +{ + TType dereferencedType(node->getType(), index); + dereferencedType.getQualifier().storage = EvqConst; + TIntermTyped* result = 0; + int size = dereferencedType.computeNumComponents(); + + // arrays, vectors, matrices, all use simple multiplicative math + // while structures need to add up heterogeneous members + int start; + if (node->getType().isCoopMat()) + start = 0; + else if (node->isArray() || ! node->isStruct()) + start = size * index; + else { + // it is a structure + assert(node->isStruct()); + start = 0; + for (int i = 0; i < index; ++i) + start += (*node->getType().getStruct())[i].type->computeNumComponents(); + } + + result = addConstantUnion(TConstUnionArray(node->getAsConstantUnion()->getConstArray(), start, size), node->getType(), loc); + + if (result == 0) + result = node; + else + result->setType(dereferencedType); + + return result; +} + +// +// Make a constant vector node or constant scalar node, representing a given +// constant vector and constant swizzle into it. +// +TIntermTyped* TIntermediate::foldSwizzle(TIntermTyped* node, TSwizzleSelectors& selectors, const TSourceLoc& loc) +{ + const TConstUnionArray& unionArray = node->getAsConstantUnion()->getConstArray(); + TConstUnionArray constArray(selectors.size()); + + for (int i = 0; i < selectors.size(); i++) + constArray[i] = unionArray[selectors[i]]; + + TIntermTyped* result = addConstantUnion(constArray, node->getType(), loc); + + if (result == 0) + result = node; + else + result->setType(TType(node->getBasicType(), EvqConst, selectors.size())); + + return result; +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/InfoSink.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/InfoSink.cpp new file mode 100644 index 000000000..d00c42256 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/InfoSink.cpp @@ -0,0 +1,113 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/InfoSink.h" + +#include + +namespace glslang { + +void TInfoSinkBase::append(const char* s) +{ + if (outputStream & EString) { + if (s == nullptr) + sink.append("(null)"); + else { + checkMem(strlen(s)); + sink.append(s); + } + } + +//#ifdef _WIN32 +// if (outputStream & EDebugger) +// OutputDebugString(s); +//#endif + + if (outputStream & EStdOut) + fprintf(stdout, "%s", s); +} + +void TInfoSinkBase::append(int count, char c) +{ + if (outputStream & EString) { + checkMem(count); + sink.append(count, c); + } + +//#ifdef _WIN32 +// if (outputStream & EDebugger) { +// char str[2]; +// str[0] = c; +// str[1] = '\0'; +// OutputDebugString(str); +// } +//#endif + + if (outputStream & EStdOut) + fprintf(stdout, "%c", c); +} + +void TInfoSinkBase::append(const TPersistString& t) +{ + if (outputStream & EString) { + checkMem(t.size()); + sink.append(t); + } + +//#ifdef _WIN32 +// if (outputStream & EDebugger) +// OutputDebugString(t.c_str()); +//#endif + + if (outputStream & EStdOut) + fprintf(stdout, "%s", t.c_str()); +} + +void TInfoSinkBase::append(const TString& t) +{ + if (outputStream & EString) { + checkMem(t.size()); + sink.append(t.c_str()); + } + +//#ifdef _WIN32 +// if (outputStream & EDebugger) +// OutputDebugString(t.c_str()); +//#endif + + if (outputStream & EStdOut) + fprintf(stdout, "%s", t.c_str()); +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Initialize.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Initialize.cpp new file mode 100644 index 000000000..c848638d9 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Initialize.cpp @@ -0,0 +1,9198 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Create strings that declare built-in definitions, add built-ins programmatically +// that cannot be expressed in the strings, and establish mappings between +// built-in functions and operators. +// +// Where to put a built-in: +// TBuiltIns::initialize(version,profile) context-independent textual built-ins; add them to the right string +// TBuiltIns::initialize(resources,...) context-dependent textual built-ins; add them to the right string +// TBuiltIns::identifyBuiltIns(...,symbolTable) context-independent programmatic additions/mappings to the symbol table, +// including identifying what extensions are needed if a version does not allow a symbol +// TBuiltIns::identifyBuiltIns(...,symbolTable, resources) context-dependent programmatic additions/mappings to the symbol table, +// including identifying what extensions are needed if a version does not allow a symbol +// + +#include "../Include/intermediate.h" +#include "Initialize.h" + +namespace glslang { + +// TODO: ARB_Compatability: do full extension support +const bool ARBCompatibility = true; + +const bool ForwardCompatibility = false; + +// change this back to false if depending on textual spellings of texturing calls when consuming the AST +// Using PureOperatorBuiltins=false is deprecated. +bool PureOperatorBuiltins = true; + +namespace { + +// +// A set of definitions for tabling of the built-in functions. +// + +// Order matters here, as does correlation with the subsequent +// "const int ..." declarations and the ArgType enumerants. +const char* TypeString[] = { + "bool", "bvec2", "bvec3", "bvec4", + "float", "vec2", "vec3", "vec4", + "int", "ivec2", "ivec3", "ivec4", + "uint", "uvec2", "uvec3", "uvec4", +}; +const int TypeStringCount = sizeof(TypeString) / sizeof(char*); // number of entries in 'TypeString' +const int TypeStringRowShift = 2; // shift amount to go downe one row in 'TypeString' +const int TypeStringColumnMask = (1 << TypeStringRowShift) - 1; // reduce type to its column number in 'TypeString' +const int TypeStringScalarMask = ~TypeStringColumnMask; // take type to its scalar column in 'TypeString' + +enum ArgType { + // numbers hardcoded to correspond to 'TypeString'; order and value matter + TypeB = 1 << 0, // Boolean + TypeF = 1 << 1, // float 32 + TypeI = 1 << 2, // int 32 + TypeU = 1 << 3, // uint 32 + TypeF16 = 1 << 4, // float 16 + TypeF64 = 1 << 5, // float 64 + TypeI8 = 1 << 6, // int 8 + TypeI16 = 1 << 7, // int 16 + TypeI64 = 1 << 8, // int 64 + TypeU8 = 1 << 9, // uint 8 + TypeU16 = 1 << 10, // uint 16 + TypeU64 = 1 << 11, // uint 64 +}; +// Mixtures of the above, to help the function tables +const ArgType TypeFI = static_cast(TypeF | TypeI); +const ArgType TypeFIB = static_cast(TypeF | TypeI | TypeB); +const ArgType TypeIU = static_cast(TypeI | TypeU); + +// The relationships between arguments and return type, whether anything is +// output, or other unusual situations. +enum ArgClass { + ClassRegular = 0, // nothing special, just all vector widths with matching return type; traditional arithmetic + ClassLS = 1 << 0, // the last argument is also held fixed as a (type-matched) scalar while the others cycle + ClassXLS = 1 << 1, // the last argument is exclusively a (type-matched) scalar while the others cycle + ClassLS2 = 1 << 2, // the last two arguments are held fixed as a (type-matched) scalar while the others cycle + ClassFS = 1 << 3, // the first argument is held fixed as a (type-matched) scalar while the others cycle + ClassFS2 = 1 << 4, // the first two arguments are held fixed as a (type-matched) scalar while the others cycle + ClassLO = 1 << 5, // the last argument is an output + ClassB = 1 << 6, // return type cycles through only bool/bvec, matching vector width of args + ClassLB = 1 << 7, // last argument cycles through only bool/bvec, matching vector width of args + ClassV1 = 1 << 8, // scalar only + ClassFIO = 1 << 9, // first argument is inout + ClassRS = 1 << 10, // the return is held scalar as the arguments cycle + ClassNS = 1 << 11, // no scalar prototype + ClassCV = 1 << 12, // first argument is 'coherent volatile' + ClassFO = 1 << 13, // first argument is output + ClassV3 = 1 << 14, // vec3 only +}; +// Mixtures of the above, to help the function tables +const ArgClass ClassV1FIOCV = (ArgClass)(ClassV1 | ClassFIO | ClassCV); +const ArgClass ClassBNS = (ArgClass)(ClassB | ClassNS); +const ArgClass ClassRSNS = (ArgClass)(ClassRS | ClassNS); + +// A descriptor, for a single profile, of when something is available. +// If the current profile does not match 'profile' mask below, the other fields +// do not apply (nor validate). +// profiles == EBadProfile is the end of an array of these +struct Versioning { + EProfile profiles; // the profile(s) (mask) that the following fields are valid for + int minExtendedVersion; // earliest version when extensions are enabled; ignored if numExtensions is 0 + int minCoreVersion; // earliest version function is in core; 0 means never + int numExtensions; // how many extensions are in the 'extensions' list + const char** extensions; // list of extension names enabling the function +}; + +EProfile EDesktopProfile = static_cast(ENoProfile | ECoreProfile | ECompatibilityProfile); + +// Declare pointers to put into the table for versioning. +#ifdef GLSLANG_WEB + const Versioning* Es300Desktop130 = nullptr; +#else + const Versioning Es300Desktop130Version[] = { { EEsProfile, 0, 300, 0, nullptr }, + { EDesktopProfile, 0, 130, 0, nullptr }, + { EBadProfile } }; + const Versioning* Es300Desktop130 = &Es300Desktop130Version[0]; + + const Versioning Es310Desktop420Version[] = { { EEsProfile, 0, 310, 0, nullptr }, + { EDesktopProfile, 0, 420, 0, nullptr }, + { EBadProfile } }; + const Versioning* Es310Desktop420 = &Es310Desktop420Version[0]; + + const Versioning Es310Desktop450Version[] = { { EEsProfile, 0, 310, 0, nullptr }, + { EDesktopProfile, 0, 450, 0, nullptr }, + { EBadProfile } }; + const Versioning* Es310Desktop450 = &Es310Desktop450Version[0]; +#endif + +// The main descriptor of what a set of function prototypes can look like, and +// a pointer to extra versioning information, when needed. +struct BuiltInFunction { + TOperator op; // operator to map the name to + const char* name; // function name + int numArguments; // number of arguments (overloads with varying arguments need different entries) + ArgType types; // ArgType mask + ArgClass classes; // the ways this particular function entry manifests + const Versioning* versioning; // nullptr means always a valid version +}; + +// The tables can have the same built-in function name more than one time, +// but the exact same prototype must be indicated at most once. +// The prototypes that get declared are the union of all those indicated. +// This is important when different releases add new prototypes for the same name. +// It also also congnitively simpler tiling of the prototype space. +// In practice, most names can be fully represented with one entry. +// +// Table is terminated by an OpNull TOperator. + +const BuiltInFunction BaseFunctions[] = { +// TOperator, name, arg-count, ArgType, ArgClass, versioning +// --------- ---- --------- ------- -------- ---------- + { EOpRadians, "radians", 1, TypeF, ClassRegular, nullptr }, + { EOpDegrees, "degrees", 1, TypeF, ClassRegular, nullptr }, + { EOpSin, "sin", 1, TypeF, ClassRegular, nullptr }, + { EOpCos, "cos", 1, TypeF, ClassRegular, nullptr }, + { EOpTan, "tan", 1, TypeF, ClassRegular, nullptr }, + { EOpAsin, "asin", 1, TypeF, ClassRegular, nullptr }, + { EOpAcos, "acos", 1, TypeF, ClassRegular, nullptr }, + { EOpAtan, "atan", 2, TypeF, ClassRegular, nullptr }, + { EOpAtan, "atan", 1, TypeF, ClassRegular, nullptr }, + { EOpPow, "pow", 2, TypeF, ClassRegular, nullptr }, + { EOpExp, "exp", 1, TypeF, ClassRegular, nullptr }, + { EOpLog, "log", 1, TypeF, ClassRegular, nullptr }, + { EOpExp2, "exp2", 1, TypeF, ClassRegular, nullptr }, + { EOpLog2, "log2", 1, TypeF, ClassRegular, nullptr }, + { EOpSqrt, "sqrt", 1, TypeF, ClassRegular, nullptr }, + { EOpInverseSqrt, "inversesqrt", 1, TypeF, ClassRegular, nullptr }, + { EOpAbs, "abs", 1, TypeF, ClassRegular, nullptr }, + { EOpSign, "sign", 1, TypeF, ClassRegular, nullptr }, + { EOpFloor, "floor", 1, TypeF, ClassRegular, nullptr }, + { EOpCeil, "ceil", 1, TypeF, ClassRegular, nullptr }, + { EOpFract, "fract", 1, TypeF, ClassRegular, nullptr }, + { EOpMod, "mod", 2, TypeF, ClassLS, nullptr }, + { EOpMin, "min", 2, TypeF, ClassLS, nullptr }, + { EOpMax, "max", 2, TypeF, ClassLS, nullptr }, + { EOpClamp, "clamp", 3, TypeF, ClassLS2, nullptr }, + { EOpMix, "mix", 3, TypeF, ClassLS, nullptr }, + { EOpStep, "step", 2, TypeF, ClassFS, nullptr }, + { EOpSmoothStep, "smoothstep", 3, TypeF, ClassFS2, nullptr }, + { EOpNormalize, "normalize", 1, TypeF, ClassRegular, nullptr }, + { EOpFaceForward, "faceforward", 3, TypeF, ClassRegular, nullptr }, + { EOpReflect, "reflect", 2, TypeF, ClassRegular, nullptr }, + { EOpRefract, "refract", 3, TypeF, ClassXLS, nullptr }, + { EOpLength, "length", 1, TypeF, ClassRS, nullptr }, + { EOpDistance, "distance", 2, TypeF, ClassRS, nullptr }, + { EOpDot, "dot", 2, TypeF, ClassRS, nullptr }, + { EOpCross, "cross", 2, TypeF, ClassV3, nullptr }, + { EOpLessThan, "lessThan", 2, TypeFI, ClassBNS, nullptr }, + { EOpLessThanEqual, "lessThanEqual", 2, TypeFI, ClassBNS, nullptr }, + { EOpGreaterThan, "greaterThan", 2, TypeFI, ClassBNS, nullptr }, + { EOpGreaterThanEqual, "greaterThanEqual", 2, TypeFI, ClassBNS, nullptr }, + { EOpVectorEqual, "equal", 2, TypeFIB, ClassBNS, nullptr }, + { EOpVectorNotEqual, "notEqual", 2, TypeFIB, ClassBNS, nullptr }, + { EOpAny, "any", 1, TypeB, ClassRSNS, nullptr }, + { EOpAll, "all", 1, TypeB, ClassRSNS, nullptr }, + { EOpVectorLogicalNot, "not", 1, TypeB, ClassNS, nullptr }, + { EOpSinh, "sinh", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpCosh, "cosh", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpTanh, "tanh", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpAsinh, "asinh", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpAcosh, "acosh", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpAtanh, "atanh", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpAbs, "abs", 1, TypeI, ClassRegular, Es300Desktop130 }, + { EOpSign, "sign", 1, TypeI, ClassRegular, Es300Desktop130 }, + { EOpTrunc, "trunc", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpRound, "round", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpRoundEven, "roundEven", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpModf, "modf", 2, TypeF, ClassLO, Es300Desktop130 }, + { EOpMin, "min", 2, TypeIU, ClassLS, Es300Desktop130 }, + { EOpMax, "max", 2, TypeIU, ClassLS, Es300Desktop130 }, + { EOpClamp, "clamp", 3, TypeIU, ClassLS2, Es300Desktop130 }, + { EOpMix, "mix", 3, TypeF, ClassLB, Es300Desktop130 }, + { EOpIsInf, "isinf", 1, TypeF, ClassB, Es300Desktop130 }, + { EOpIsNan, "isnan", 1, TypeF, ClassB, Es300Desktop130 }, + { EOpLessThan, "lessThan", 2, TypeU, ClassBNS, Es300Desktop130 }, + { EOpLessThanEqual, "lessThanEqual", 2, TypeU, ClassBNS, Es300Desktop130 }, + { EOpGreaterThan, "greaterThan", 2, TypeU, ClassBNS, Es300Desktop130 }, + { EOpGreaterThanEqual, "greaterThanEqual", 2, TypeU, ClassBNS, Es300Desktop130 }, + { EOpVectorEqual, "equal", 2, TypeU, ClassBNS, Es300Desktop130 }, + { EOpVectorNotEqual, "notEqual", 2, TypeU, ClassBNS, Es300Desktop130 }, + { EOpAtomicAdd, "atomicAdd", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicMin, "atomicMin", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicMax, "atomicMax", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicAnd, "atomicAnd", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicOr, "atomicOr", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicXor, "atomicXor", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicExchange, "atomicExchange", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicCompSwap, "atomicCompSwap", 3, TypeIU, ClassV1FIOCV, Es310Desktop420 }, +#ifndef GLSLANG_WEB + { EOpMix, "mix", 3, TypeB, ClassRegular, Es310Desktop450 }, + { EOpMix, "mix", 3, TypeIU, ClassLB, Es310Desktop450 }, +#endif + + { EOpNull } +}; + +const BuiltInFunction DerivativeFunctions[] = { + { EOpDPdx, "dFdx", 1, TypeF, ClassRegular, nullptr }, + { EOpDPdy, "dFdy", 1, TypeF, ClassRegular, nullptr }, + { EOpFwidth, "fwidth", 1, TypeF, ClassRegular, nullptr }, + { EOpNull } +}; + +// For functions declared some other way, but still use the table to relate to operator. +struct CustomFunction { + TOperator op; // operator to map the name to + const char* name; // function name + const Versioning* versioning; // nullptr means always a valid version +}; + +const CustomFunction CustomFunctions[] = { + { EOpBarrier, "barrier", nullptr }, + { EOpMemoryBarrierShared, "memoryBarrierShared", nullptr }, + { EOpGroupMemoryBarrier, "groupMemoryBarrier", nullptr }, + { EOpMemoryBarrier, "memoryBarrier", nullptr }, + { EOpMemoryBarrierBuffer, "memoryBarrierBuffer", nullptr }, + + { EOpPackSnorm2x16, "packSnorm2x16", nullptr }, + { EOpUnpackSnorm2x16, "unpackSnorm2x16", nullptr }, + { EOpPackUnorm2x16, "packUnorm2x16", nullptr }, + { EOpUnpackUnorm2x16, "unpackUnorm2x16", nullptr }, + { EOpPackHalf2x16, "packHalf2x16", nullptr }, + { EOpUnpackHalf2x16, "unpackHalf2x16", nullptr }, + + { EOpMul, "matrixCompMult", nullptr }, + { EOpOuterProduct, "outerProduct", nullptr }, + { EOpTranspose, "transpose", nullptr }, + { EOpDeterminant, "determinant", nullptr }, + { EOpMatrixInverse, "inverse", nullptr }, + { EOpFloatBitsToInt, "floatBitsToInt", nullptr }, + { EOpFloatBitsToUint, "floatBitsToUint", nullptr }, + { EOpIntBitsToFloat, "intBitsToFloat", nullptr }, + { EOpUintBitsToFloat, "uintBitsToFloat", nullptr }, + + { EOpTextureQuerySize, "textureSize", nullptr }, + { EOpTextureQueryLod, "textureQueryLod", nullptr }, + { EOpTextureQueryLevels, "textureQueryLevels", nullptr }, + { EOpTextureQuerySamples, "textureSamples", nullptr }, + { EOpTexture, "texture", nullptr }, + { EOpTextureProj, "textureProj", nullptr }, + { EOpTextureLod, "textureLod", nullptr }, + { EOpTextureOffset, "textureOffset", nullptr }, + { EOpTextureFetch, "texelFetch", nullptr }, + { EOpTextureFetchOffset, "texelFetchOffset", nullptr }, + { EOpTextureProjOffset, "textureProjOffset", nullptr }, + { EOpTextureLodOffset, "textureLodOffset", nullptr }, + { EOpTextureProjLod, "textureProjLod", nullptr }, + { EOpTextureProjLodOffset, "textureProjLodOffset", nullptr }, + { EOpTextureGrad, "textureGrad", nullptr }, + { EOpTextureGradOffset, "textureGradOffset", nullptr }, + { EOpTextureProjGrad, "textureProjGrad", nullptr }, + { EOpTextureProjGradOffset, "textureProjGradOffset", nullptr }, + + { EOpNull } +}; + +// For the given table of functions, add all the indicated prototypes for each +// one, to be returned in the passed in decls. +void AddTabledBuiltin(TString& decls, const BuiltInFunction& function) +{ + const auto isScalarType = [](int type) { return (type & TypeStringColumnMask) == 0; }; + + // loop across these two: + // 0: the varying arg set, and + // 1: the fixed scalar args + const ArgClass ClassFixed = (ArgClass)(ClassLS | ClassXLS | ClassLS2 | ClassFS | ClassFS2); + for (int fixed = 0; fixed < ((function.classes & ClassFixed) > 0 ? 2 : 1); ++fixed) { + + if (fixed == 0 && (function.classes & ClassXLS)) + continue; + + // walk the type strings in TypeString[] + for (int type = 0; type < TypeStringCount; ++type) { + // skip types not selected: go from type to row number to type bit + if ((function.types & (1 << (type >> TypeStringRowShift))) == 0) + continue; + + // if we aren't on a scalar, and should be, skip + if ((function.classes & ClassV1) && !isScalarType(type)) + continue; + + // if we aren't on a 3-vector, and should be, skip + if ((function.classes & ClassV3) && (type & TypeStringColumnMask) != 2) + continue; + + // skip replication of all arg scalars between the varying arg set and the fixed args + if (fixed == 1 && type == (type & TypeStringScalarMask) && (function.classes & ClassXLS) == 0) + continue; + + // skip scalars when we are told to + if ((function.classes & ClassNS) && isScalarType(type)) + continue; + + // return type + if (function.classes & ClassB) + decls.append(TypeString[type & TypeStringColumnMask]); + else if (function.classes & ClassRS) + decls.append(TypeString[type & TypeStringScalarMask]); + else + decls.append(TypeString[type]); + decls.append(" "); + decls.append(function.name); + decls.append("("); + + // arguments + for (int arg = 0; arg < function.numArguments; ++arg) { + if (arg == function.numArguments - 1 && (function.classes & ClassLO)) + decls.append("out "); + if (arg == 0) { +#ifndef GLSLANG_WEB + if (function.classes & ClassCV) + decls.append("coherent volatile "); +#endif + if (function.classes & ClassFIO) + decls.append("inout "); + if (function.classes & ClassFO) + decls.append("out "); + } + if ((function.classes & ClassLB) && arg == function.numArguments - 1) + decls.append(TypeString[type & TypeStringColumnMask]); + else if (fixed && ((arg == function.numArguments - 1 && (function.classes & (ClassLS | ClassXLS | + ClassLS2))) || + (arg == function.numArguments - 2 && (function.classes & ClassLS2)) || + (arg == 0 && (function.classes & (ClassFS | ClassFS2))) || + (arg == 1 && (function.classes & ClassFS2)))) + decls.append(TypeString[type & TypeStringScalarMask]); + else + decls.append(TypeString[type]); + if (arg < function.numArguments - 1) + decls.append(","); + } + decls.append(");\n"); + } + } +} + +// See if the tabled versioning information allows the current version. +bool ValidVersion(const BuiltInFunction& function, int version, EProfile profile, const SpvVersion& /* spVersion */) +{ +#ifdef GLSLANG_WEB + // all entries in table are valid + return true; +#endif + + // nullptr means always valid + if (function.versioning == nullptr) + return true; + + // check for what is said about our current profile + for (const Versioning* v = function.versioning; v->profiles != EBadProfile; ++v) { + if ((v->profiles & profile) != 0) { + if (v->minCoreVersion <= version || (v->numExtensions > 0 && v->minExtendedVersion <= version)) + return true; + } + } + + return false; +} + +// Relate a single table of built-ins to their AST operator. +// This can get called redundantly (especially for the common built-ins, when +// called once per stage). This is a performance issue only, not a correctness +// concern. It is done for quality arising from simplicity, as there are subtleties +// to get correct if instead trying to do it surgically. +template +void RelateTabledBuiltins(const FunctionT* functions, TSymbolTable& symbolTable) +{ + while (functions->op != EOpNull) { + symbolTable.relateToOperator(functions->name, functions->op); + ++functions; + } +} + +} // end anonymous namespace + +// Add declarations for all tables of built-in functions. +void TBuiltIns::addTabledBuiltins(int version, EProfile profile, const SpvVersion& spvVersion) +{ + const auto forEachFunction = [&](TString& decls, const BuiltInFunction* function) { + while (function->op != EOpNull) { + if (ValidVersion(*function, version, profile, spvVersion)) + AddTabledBuiltin(decls, *function); + ++function; + } + }; + + forEachFunction(commonBuiltins, BaseFunctions); + forEachFunction(stageBuiltins[EShLangFragment], DerivativeFunctions); + + if ((profile == EEsProfile && version >= 320) || (profile != EEsProfile && version >= 450)) + forEachFunction(stageBuiltins[EShLangCompute], DerivativeFunctions); +} + +// Relate all tables of built-ins to the AST operators. +void TBuiltIns::relateTabledBuiltins(int /* version */, EProfile /* profile */, const SpvVersion& /* spvVersion */, EShLanguage /* stage */, TSymbolTable& symbolTable) +{ + RelateTabledBuiltins(BaseFunctions, symbolTable); + RelateTabledBuiltins(DerivativeFunctions, symbolTable); + RelateTabledBuiltins(CustomFunctions, symbolTable); +} + +inline bool IncludeLegacy(int version, EProfile profile, const SpvVersion& spvVersion) +{ + return profile != EEsProfile && (version <= 130 || (spvVersion.spv == 0 && ARBCompatibility) || profile == ECompatibilityProfile); +} + +// Construct TBuiltInParseables base class. This can be used for language-common constructs. +TBuiltInParseables::TBuiltInParseables() +{ +} + +// Destroy TBuiltInParseables. +TBuiltInParseables::~TBuiltInParseables() +{ +} + +TBuiltIns::TBuiltIns() +{ + // Set up textual representations for making all the permutations + // of texturing/imaging functions. + prefixes[EbtFloat] = ""; + prefixes[EbtInt] = "i"; + prefixes[EbtUint] = "u"; +#ifndef GLSLANG_WEB + prefixes[EbtFloat16] = "f16"; + prefixes[EbtInt8] = "i8"; + prefixes[EbtUint8] = "u8"; + prefixes[EbtInt16] = "i16"; + prefixes[EbtUint16] = "u16"; +#endif + + postfixes[2] = "2"; + postfixes[3] = "3"; + postfixes[4] = "4"; + + // Map from symbolic class of texturing dimension to numeric dimensions. + dimMap[Esd2D] = 2; + dimMap[Esd3D] = 3; + dimMap[EsdCube] = 3; +#ifndef GLSLANG_WEB + dimMap[Esd1D] = 1; + dimMap[EsdRect] = 2; + dimMap[EsdBuffer] = 1; + dimMap[EsdSubpass] = 2; // potentially unused for now +#endif +} + +TBuiltIns::~TBuiltIns() +{ +} + + +// +// Add all context-independent built-in functions and variables that are present +// for the given version and profile. Share common ones across stages, otherwise +// make stage-specific entries. +// +// Most built-ins variables can be added as simple text strings. Some need to +// be added programmatically, which is done later in IdentifyBuiltIns() below. +// +void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvVersion) +{ +#ifdef GLSLANG_WEB + version = 310; + profile = EEsProfile; +#endif + addTabledBuiltins(version, profile, spvVersion); + + //============================================================================ + // + // Prototypes for built-in functions used repeatly by different shaders + // + //============================================================================ + +#ifndef GLSLANG_WEB + // + // Derivatives Functions. + // + TString derivativeControls ( + "float dFdxFine(float p);" + "vec2 dFdxFine(vec2 p);" + "vec3 dFdxFine(vec3 p);" + "vec4 dFdxFine(vec4 p);" + + "float dFdyFine(float p);" + "vec2 dFdyFine(vec2 p);" + "vec3 dFdyFine(vec3 p);" + "vec4 dFdyFine(vec4 p);" + + "float fwidthFine(float p);" + "vec2 fwidthFine(vec2 p);" + "vec3 fwidthFine(vec3 p);" + "vec4 fwidthFine(vec4 p);" + + "float dFdxCoarse(float p);" + "vec2 dFdxCoarse(vec2 p);" + "vec3 dFdxCoarse(vec3 p);" + "vec4 dFdxCoarse(vec4 p);" + + "float dFdyCoarse(float p);" + "vec2 dFdyCoarse(vec2 p);" + "vec3 dFdyCoarse(vec3 p);" + "vec4 dFdyCoarse(vec4 p);" + + "float fwidthCoarse(float p);" + "vec2 fwidthCoarse(vec2 p);" + "vec3 fwidthCoarse(vec3 p);" + "vec4 fwidthCoarse(vec4 p);" + ); + + TString derivativesAndControl16bits ( + "float16_t dFdx(float16_t);" + "f16vec2 dFdx(f16vec2);" + "f16vec3 dFdx(f16vec3);" + "f16vec4 dFdx(f16vec4);" + + "float16_t dFdy(float16_t);" + "f16vec2 dFdy(f16vec2);" + "f16vec3 dFdy(f16vec3);" + "f16vec4 dFdy(f16vec4);" + + "float16_t dFdxFine(float16_t);" + "f16vec2 dFdxFine(f16vec2);" + "f16vec3 dFdxFine(f16vec3);" + "f16vec4 dFdxFine(f16vec4);" + + "float16_t dFdyFine(float16_t);" + "f16vec2 dFdyFine(f16vec2);" + "f16vec3 dFdyFine(f16vec3);" + "f16vec4 dFdyFine(f16vec4);" + + "float16_t dFdxCoarse(float16_t);" + "f16vec2 dFdxCoarse(f16vec2);" + "f16vec3 dFdxCoarse(f16vec3);" + "f16vec4 dFdxCoarse(f16vec4);" + + "float16_t dFdyCoarse(float16_t);" + "f16vec2 dFdyCoarse(f16vec2);" + "f16vec3 dFdyCoarse(f16vec3);" + "f16vec4 dFdyCoarse(f16vec4);" + + "float16_t fwidth(float16_t);" + "f16vec2 fwidth(f16vec2);" + "f16vec3 fwidth(f16vec3);" + "f16vec4 fwidth(f16vec4);" + + "float16_t fwidthFine(float16_t);" + "f16vec2 fwidthFine(f16vec2);" + "f16vec3 fwidthFine(f16vec3);" + "f16vec4 fwidthFine(f16vec4);" + + "float16_t fwidthCoarse(float16_t);" + "f16vec2 fwidthCoarse(f16vec2);" + "f16vec3 fwidthCoarse(f16vec3);" + "f16vec4 fwidthCoarse(f16vec4);" + ); + + TString derivativesAndControl64bits ( + "float64_t dFdx(float64_t);" + "f64vec2 dFdx(f64vec2);" + "f64vec3 dFdx(f64vec3);" + "f64vec4 dFdx(f64vec4);" + + "float64_t dFdy(float64_t);" + "f64vec2 dFdy(f64vec2);" + "f64vec3 dFdy(f64vec3);" + "f64vec4 dFdy(f64vec4);" + + "float64_t dFdxFine(float64_t);" + "f64vec2 dFdxFine(f64vec2);" + "f64vec3 dFdxFine(f64vec3);" + "f64vec4 dFdxFine(f64vec4);" + + "float64_t dFdyFine(float64_t);" + "f64vec2 dFdyFine(f64vec2);" + "f64vec3 dFdyFine(f64vec3);" + "f64vec4 dFdyFine(f64vec4);" + + "float64_t dFdxCoarse(float64_t);" + "f64vec2 dFdxCoarse(f64vec2);" + "f64vec3 dFdxCoarse(f64vec3);" + "f64vec4 dFdxCoarse(f64vec4);" + + "float64_t dFdyCoarse(float64_t);" + "f64vec2 dFdyCoarse(f64vec2);" + "f64vec3 dFdyCoarse(f64vec3);" + "f64vec4 dFdyCoarse(f64vec4);" + + "float64_t fwidth(float64_t);" + "f64vec2 fwidth(f64vec2);" + "f64vec3 fwidth(f64vec3);" + "f64vec4 fwidth(f64vec4);" + + "float64_t fwidthFine(float64_t);" + "f64vec2 fwidthFine(f64vec2);" + "f64vec3 fwidthFine(f64vec3);" + "f64vec4 fwidthFine(f64vec4);" + + "float64_t fwidthCoarse(float64_t);" + "f64vec2 fwidthCoarse(f64vec2);" + "f64vec3 fwidthCoarse(f64vec3);" + "f64vec4 fwidthCoarse(f64vec4);" + ); + + //============================================================================ + // + // Prototypes for built-in functions seen by both vertex and fragment shaders. + // + //============================================================================ + + // + // double functions added to desktop 4.00, but not fma, frexp, ldexp, or pack/unpack + // + if (profile != EEsProfile && version >= 150) { // ARB_gpu_shader_fp64 + commonBuiltins.append( + + "double sqrt(double);" + "dvec2 sqrt(dvec2);" + "dvec3 sqrt(dvec3);" + "dvec4 sqrt(dvec4);" + + "double inversesqrt(double);" + "dvec2 inversesqrt(dvec2);" + "dvec3 inversesqrt(dvec3);" + "dvec4 inversesqrt(dvec4);" + + "double abs(double);" + "dvec2 abs(dvec2);" + "dvec3 abs(dvec3);" + "dvec4 abs(dvec4);" + + "double sign(double);" + "dvec2 sign(dvec2);" + "dvec3 sign(dvec3);" + "dvec4 sign(dvec4);" + + "double floor(double);" + "dvec2 floor(dvec2);" + "dvec3 floor(dvec3);" + "dvec4 floor(dvec4);" + + "double trunc(double);" + "dvec2 trunc(dvec2);" + "dvec3 trunc(dvec3);" + "dvec4 trunc(dvec4);" + + "double round(double);" + "dvec2 round(dvec2);" + "dvec3 round(dvec3);" + "dvec4 round(dvec4);" + + "double roundEven(double);" + "dvec2 roundEven(dvec2);" + "dvec3 roundEven(dvec3);" + "dvec4 roundEven(dvec4);" + + "double ceil(double);" + "dvec2 ceil(dvec2);" + "dvec3 ceil(dvec3);" + "dvec4 ceil(dvec4);" + + "double fract(double);" + "dvec2 fract(dvec2);" + "dvec3 fract(dvec3);" + "dvec4 fract(dvec4);" + + "double mod(double, double);" + "dvec2 mod(dvec2 , double);" + "dvec3 mod(dvec3 , double);" + "dvec4 mod(dvec4 , double);" + "dvec2 mod(dvec2 , dvec2);" + "dvec3 mod(dvec3 , dvec3);" + "dvec4 mod(dvec4 , dvec4);" + + "double modf(double, out double);" + "dvec2 modf(dvec2, out dvec2);" + "dvec3 modf(dvec3, out dvec3);" + "dvec4 modf(dvec4, out dvec4);" + + "double min(double, double);" + "dvec2 min(dvec2, double);" + "dvec3 min(dvec3, double);" + "dvec4 min(dvec4, double);" + "dvec2 min(dvec2, dvec2);" + "dvec3 min(dvec3, dvec3);" + "dvec4 min(dvec4, dvec4);" + + "double max(double, double);" + "dvec2 max(dvec2 , double);" + "dvec3 max(dvec3 , double);" + "dvec4 max(dvec4 , double);" + "dvec2 max(dvec2 , dvec2);" + "dvec3 max(dvec3 , dvec3);" + "dvec4 max(dvec4 , dvec4);" + + "double clamp(double, double, double);" + "dvec2 clamp(dvec2 , double, double);" + "dvec3 clamp(dvec3 , double, double);" + "dvec4 clamp(dvec4 , double, double);" + "dvec2 clamp(dvec2 , dvec2 , dvec2);" + "dvec3 clamp(dvec3 , dvec3 , dvec3);" + "dvec4 clamp(dvec4 , dvec4 , dvec4);" + + "double mix(double, double, double);" + "dvec2 mix(dvec2, dvec2, double);" + "dvec3 mix(dvec3, dvec3, double);" + "dvec4 mix(dvec4, dvec4, double);" + "dvec2 mix(dvec2, dvec2, dvec2);" + "dvec3 mix(dvec3, dvec3, dvec3);" + "dvec4 mix(dvec4, dvec4, dvec4);" + "double mix(double, double, bool);" + "dvec2 mix(dvec2, dvec2, bvec2);" + "dvec3 mix(dvec3, dvec3, bvec3);" + "dvec4 mix(dvec4, dvec4, bvec4);" + + "double step(double, double);" + "dvec2 step(dvec2 , dvec2);" + "dvec3 step(dvec3 , dvec3);" + "dvec4 step(dvec4 , dvec4);" + "dvec2 step(double, dvec2);" + "dvec3 step(double, dvec3);" + "dvec4 step(double, dvec4);" + + "double smoothstep(double, double, double);" + "dvec2 smoothstep(dvec2 , dvec2 , dvec2);" + "dvec3 smoothstep(dvec3 , dvec3 , dvec3);" + "dvec4 smoothstep(dvec4 , dvec4 , dvec4);" + "dvec2 smoothstep(double, double, dvec2);" + "dvec3 smoothstep(double, double, dvec3);" + "dvec4 smoothstep(double, double, dvec4);" + + "bool isnan(double);" + "bvec2 isnan(dvec2);" + "bvec3 isnan(dvec3);" + "bvec4 isnan(dvec4);" + + "bool isinf(double);" + "bvec2 isinf(dvec2);" + "bvec3 isinf(dvec3);" + "bvec4 isinf(dvec4);" + + "double length(double);" + "double length(dvec2);" + "double length(dvec3);" + "double length(dvec4);" + + "double distance(double, double);" + "double distance(dvec2 , dvec2);" + "double distance(dvec3 , dvec3);" + "double distance(dvec4 , dvec4);" + + "double dot(double, double);" + "double dot(dvec2 , dvec2);" + "double dot(dvec3 , dvec3);" + "double dot(dvec4 , dvec4);" + + "dvec3 cross(dvec3, dvec3);" + + "double normalize(double);" + "dvec2 normalize(dvec2);" + "dvec3 normalize(dvec3);" + "dvec4 normalize(dvec4);" + + "double faceforward(double, double, double);" + "dvec2 faceforward(dvec2, dvec2, dvec2);" + "dvec3 faceforward(dvec3, dvec3, dvec3);" + "dvec4 faceforward(dvec4, dvec4, dvec4);" + + "double reflect(double, double);" + "dvec2 reflect(dvec2 , dvec2 );" + "dvec3 reflect(dvec3 , dvec3 );" + "dvec4 reflect(dvec4 , dvec4 );" + + "double refract(double, double, double);" + "dvec2 refract(dvec2 , dvec2 , double);" + "dvec3 refract(dvec3 , dvec3 , double);" + "dvec4 refract(dvec4 , dvec4 , double);" + + "dmat2 matrixCompMult(dmat2, dmat2);" + "dmat3 matrixCompMult(dmat3, dmat3);" + "dmat4 matrixCompMult(dmat4, dmat4);" + "dmat2x3 matrixCompMult(dmat2x3, dmat2x3);" + "dmat2x4 matrixCompMult(dmat2x4, dmat2x4);" + "dmat3x2 matrixCompMult(dmat3x2, dmat3x2);" + "dmat3x4 matrixCompMult(dmat3x4, dmat3x4);" + "dmat4x2 matrixCompMult(dmat4x2, dmat4x2);" + "dmat4x3 matrixCompMult(dmat4x3, dmat4x3);" + + "dmat2 outerProduct(dvec2, dvec2);" + "dmat3 outerProduct(dvec3, dvec3);" + "dmat4 outerProduct(dvec4, dvec4);" + "dmat2x3 outerProduct(dvec3, dvec2);" + "dmat3x2 outerProduct(dvec2, dvec3);" + "dmat2x4 outerProduct(dvec4, dvec2);" + "dmat4x2 outerProduct(dvec2, dvec4);" + "dmat3x4 outerProduct(dvec4, dvec3);" + "dmat4x3 outerProduct(dvec3, dvec4);" + + "dmat2 transpose(dmat2);" + "dmat3 transpose(dmat3);" + "dmat4 transpose(dmat4);" + "dmat2x3 transpose(dmat3x2);" + "dmat3x2 transpose(dmat2x3);" + "dmat2x4 transpose(dmat4x2);" + "dmat4x2 transpose(dmat2x4);" + "dmat3x4 transpose(dmat4x3);" + "dmat4x3 transpose(dmat3x4);" + + "double determinant(dmat2);" + "double determinant(dmat3);" + "double determinant(dmat4);" + + "dmat2 inverse(dmat2);" + "dmat3 inverse(dmat3);" + "dmat4 inverse(dmat4);" + + "bvec2 lessThan(dvec2, dvec2);" + "bvec3 lessThan(dvec3, dvec3);" + "bvec4 lessThan(dvec4, dvec4);" + + "bvec2 lessThanEqual(dvec2, dvec2);" + "bvec3 lessThanEqual(dvec3, dvec3);" + "bvec4 lessThanEqual(dvec4, dvec4);" + + "bvec2 greaterThan(dvec2, dvec2);" + "bvec3 greaterThan(dvec3, dvec3);" + "bvec4 greaterThan(dvec4, dvec4);" + + "bvec2 greaterThanEqual(dvec2, dvec2);" + "bvec3 greaterThanEqual(dvec3, dvec3);" + "bvec4 greaterThanEqual(dvec4, dvec4);" + + "bvec2 equal(dvec2, dvec2);" + "bvec3 equal(dvec3, dvec3);" + "bvec4 equal(dvec4, dvec4);" + + "bvec2 notEqual(dvec2, dvec2);" + "bvec3 notEqual(dvec3, dvec3);" + "bvec4 notEqual(dvec4, dvec4);" + + "\n"); + } + + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + + "int64_t abs(int64_t);" + "i64vec2 abs(i64vec2);" + "i64vec3 abs(i64vec3);" + "i64vec4 abs(i64vec4);" + + "int64_t sign(int64_t);" + "i64vec2 sign(i64vec2);" + "i64vec3 sign(i64vec3);" + "i64vec4 sign(i64vec4);" + + "int64_t min(int64_t, int64_t);" + "i64vec2 min(i64vec2, int64_t);" + "i64vec3 min(i64vec3, int64_t);" + "i64vec4 min(i64vec4, int64_t);" + "i64vec2 min(i64vec2, i64vec2);" + "i64vec3 min(i64vec3, i64vec3);" + "i64vec4 min(i64vec4, i64vec4);" + "uint64_t min(uint64_t, uint64_t);" + "u64vec2 min(u64vec2, uint64_t);" + "u64vec3 min(u64vec3, uint64_t);" + "u64vec4 min(u64vec4, uint64_t);" + "u64vec2 min(u64vec2, u64vec2);" + "u64vec3 min(u64vec3, u64vec3);" + "u64vec4 min(u64vec4, u64vec4);" + + "int64_t max(int64_t, int64_t);" + "i64vec2 max(i64vec2, int64_t);" + "i64vec3 max(i64vec3, int64_t);" + "i64vec4 max(i64vec4, int64_t);" + "i64vec2 max(i64vec2, i64vec2);" + "i64vec3 max(i64vec3, i64vec3);" + "i64vec4 max(i64vec4, i64vec4);" + "uint64_t max(uint64_t, uint64_t);" + "u64vec2 max(u64vec2, uint64_t);" + "u64vec3 max(u64vec3, uint64_t);" + "u64vec4 max(u64vec4, uint64_t);" + "u64vec2 max(u64vec2, u64vec2);" + "u64vec3 max(u64vec3, u64vec3);" + "u64vec4 max(u64vec4, u64vec4);" + + "int64_t clamp(int64_t, int64_t, int64_t);" + "i64vec2 clamp(i64vec2, int64_t, int64_t);" + "i64vec3 clamp(i64vec3, int64_t, int64_t);" + "i64vec4 clamp(i64vec4, int64_t, int64_t);" + "i64vec2 clamp(i64vec2, i64vec2, i64vec2);" + "i64vec3 clamp(i64vec3, i64vec3, i64vec3);" + "i64vec4 clamp(i64vec4, i64vec4, i64vec4);" + "uint64_t clamp(uint64_t, uint64_t, uint64_t);" + "u64vec2 clamp(u64vec2, uint64_t, uint64_t);" + "u64vec3 clamp(u64vec3, uint64_t, uint64_t);" + "u64vec4 clamp(u64vec4, uint64_t, uint64_t);" + "u64vec2 clamp(u64vec2, u64vec2, u64vec2);" + "u64vec3 clamp(u64vec3, u64vec3, u64vec3);" + "u64vec4 clamp(u64vec4, u64vec4, u64vec4);" + + "int64_t mix(int64_t, int64_t, bool);" + "i64vec2 mix(i64vec2, i64vec2, bvec2);" + "i64vec3 mix(i64vec3, i64vec3, bvec3);" + "i64vec4 mix(i64vec4, i64vec4, bvec4);" + "uint64_t mix(uint64_t, uint64_t, bool);" + "u64vec2 mix(u64vec2, u64vec2, bvec2);" + "u64vec3 mix(u64vec3, u64vec3, bvec3);" + "u64vec4 mix(u64vec4, u64vec4, bvec4);" + + "int64_t doubleBitsToInt64(double);" + "i64vec2 doubleBitsToInt64(dvec2);" + "i64vec3 doubleBitsToInt64(dvec3);" + "i64vec4 doubleBitsToInt64(dvec4);" + + "uint64_t doubleBitsToUint64(double);" + "u64vec2 doubleBitsToUint64(dvec2);" + "u64vec3 doubleBitsToUint64(dvec3);" + "u64vec4 doubleBitsToUint64(dvec4);" + + "double int64BitsToDouble(int64_t);" + "dvec2 int64BitsToDouble(i64vec2);" + "dvec3 int64BitsToDouble(i64vec3);" + "dvec4 int64BitsToDouble(i64vec4);" + + "double uint64BitsToDouble(uint64_t);" + "dvec2 uint64BitsToDouble(u64vec2);" + "dvec3 uint64BitsToDouble(u64vec3);" + "dvec4 uint64BitsToDouble(u64vec4);" + + "int64_t packInt2x32(ivec2);" + "uint64_t packUint2x32(uvec2);" + "ivec2 unpackInt2x32(int64_t);" + "uvec2 unpackUint2x32(uint64_t);" + + "bvec2 lessThan(i64vec2, i64vec2);" + "bvec3 lessThan(i64vec3, i64vec3);" + "bvec4 lessThan(i64vec4, i64vec4);" + "bvec2 lessThan(u64vec2, u64vec2);" + "bvec3 lessThan(u64vec3, u64vec3);" + "bvec4 lessThan(u64vec4, u64vec4);" + + "bvec2 lessThanEqual(i64vec2, i64vec2);" + "bvec3 lessThanEqual(i64vec3, i64vec3);" + "bvec4 lessThanEqual(i64vec4, i64vec4);" + "bvec2 lessThanEqual(u64vec2, u64vec2);" + "bvec3 lessThanEqual(u64vec3, u64vec3);" + "bvec4 lessThanEqual(u64vec4, u64vec4);" + + "bvec2 greaterThan(i64vec2, i64vec2);" + "bvec3 greaterThan(i64vec3, i64vec3);" + "bvec4 greaterThan(i64vec4, i64vec4);" + "bvec2 greaterThan(u64vec2, u64vec2);" + "bvec3 greaterThan(u64vec3, u64vec3);" + "bvec4 greaterThan(u64vec4, u64vec4);" + + "bvec2 greaterThanEqual(i64vec2, i64vec2);" + "bvec3 greaterThanEqual(i64vec3, i64vec3);" + "bvec4 greaterThanEqual(i64vec4, i64vec4);" + "bvec2 greaterThanEqual(u64vec2, u64vec2);" + "bvec3 greaterThanEqual(u64vec3, u64vec3);" + "bvec4 greaterThanEqual(u64vec4, u64vec4);" + + "bvec2 equal(i64vec2, i64vec2);" + "bvec3 equal(i64vec3, i64vec3);" + "bvec4 equal(i64vec4, i64vec4);" + "bvec2 equal(u64vec2, u64vec2);" + "bvec3 equal(u64vec3, u64vec3);" + "bvec4 equal(u64vec4, u64vec4);" + + "bvec2 notEqual(i64vec2, i64vec2);" + "bvec3 notEqual(i64vec3, i64vec3);" + "bvec4 notEqual(i64vec4, i64vec4);" + "bvec2 notEqual(u64vec2, u64vec2);" + "bvec3 notEqual(u64vec3, u64vec3);" + "bvec4 notEqual(u64vec4, u64vec4);" + + "int64_t findLSB(int64_t);" + "i64vec2 findLSB(i64vec2);" + "i64vec3 findLSB(i64vec3);" + "i64vec4 findLSB(i64vec4);" + + "int64_t findLSB(uint64_t);" + "i64vec2 findLSB(u64vec2);" + "i64vec3 findLSB(u64vec3);" + "i64vec4 findLSB(u64vec4);" + + "int64_t findMSB(int64_t);" + "i64vec2 findMSB(i64vec2);" + "i64vec3 findMSB(i64vec3);" + "i64vec4 findMSB(i64vec4);" + + "int64_t findMSB(uint64_t);" + "i64vec2 findMSB(u64vec2);" + "i64vec3 findMSB(u64vec3);" + "i64vec4 findMSB(u64vec4);" + + "\n" + ); + } + + // GL_AMD_shader_trinary_minmax + if (profile != EEsProfile && version >= 430) { + commonBuiltins.append( + "float min3(float, float, float);" + "vec2 min3(vec2, vec2, vec2);" + "vec3 min3(vec3, vec3, vec3);" + "vec4 min3(vec4, vec4, vec4);" + + "int min3(int, int, int);" + "ivec2 min3(ivec2, ivec2, ivec2);" + "ivec3 min3(ivec3, ivec3, ivec3);" + "ivec4 min3(ivec4, ivec4, ivec4);" + + "uint min3(uint, uint, uint);" + "uvec2 min3(uvec2, uvec2, uvec2);" + "uvec3 min3(uvec3, uvec3, uvec3);" + "uvec4 min3(uvec4, uvec4, uvec4);" + + "float max3(float, float, float);" + "vec2 max3(vec2, vec2, vec2);" + "vec3 max3(vec3, vec3, vec3);" + "vec4 max3(vec4, vec4, vec4);" + + "int max3(int, int, int);" + "ivec2 max3(ivec2, ivec2, ivec2);" + "ivec3 max3(ivec3, ivec3, ivec3);" + "ivec4 max3(ivec4, ivec4, ivec4);" + + "uint max3(uint, uint, uint);" + "uvec2 max3(uvec2, uvec2, uvec2);" + "uvec3 max3(uvec3, uvec3, uvec3);" + "uvec4 max3(uvec4, uvec4, uvec4);" + + "float mid3(float, float, float);" + "vec2 mid3(vec2, vec2, vec2);" + "vec3 mid3(vec3, vec3, vec3);" + "vec4 mid3(vec4, vec4, vec4);" + + "int mid3(int, int, int);" + "ivec2 mid3(ivec2, ivec2, ivec2);" + "ivec3 mid3(ivec3, ivec3, ivec3);" + "ivec4 mid3(ivec4, ivec4, ivec4);" + + "uint mid3(uint, uint, uint);" + "uvec2 mid3(uvec2, uvec2, uvec2);" + "uvec3 mid3(uvec3, uvec3, uvec3);" + "uvec4 mid3(uvec4, uvec4, uvec4);" + + "float16_t min3(float16_t, float16_t, float16_t);" + "f16vec2 min3(f16vec2, f16vec2, f16vec2);" + "f16vec3 min3(f16vec3, f16vec3, f16vec3);" + "f16vec4 min3(f16vec4, f16vec4, f16vec4);" + + "float16_t max3(float16_t, float16_t, float16_t);" + "f16vec2 max3(f16vec2, f16vec2, f16vec2);" + "f16vec3 max3(f16vec3, f16vec3, f16vec3);" + "f16vec4 max3(f16vec4, f16vec4, f16vec4);" + + "float16_t mid3(float16_t, float16_t, float16_t);" + "f16vec2 mid3(f16vec2, f16vec2, f16vec2);" + "f16vec3 mid3(f16vec3, f16vec3, f16vec3);" + "f16vec4 mid3(f16vec4, f16vec4, f16vec4);" + + "int16_t min3(int16_t, int16_t, int16_t);" + "i16vec2 min3(i16vec2, i16vec2, i16vec2);" + "i16vec3 min3(i16vec3, i16vec3, i16vec3);" + "i16vec4 min3(i16vec4, i16vec4, i16vec4);" + + "int16_t max3(int16_t, int16_t, int16_t);" + "i16vec2 max3(i16vec2, i16vec2, i16vec2);" + "i16vec3 max3(i16vec3, i16vec3, i16vec3);" + "i16vec4 max3(i16vec4, i16vec4, i16vec4);" + + "int16_t mid3(int16_t, int16_t, int16_t);" + "i16vec2 mid3(i16vec2, i16vec2, i16vec2);" + "i16vec3 mid3(i16vec3, i16vec3, i16vec3);" + "i16vec4 mid3(i16vec4, i16vec4, i16vec4);" + + "uint16_t min3(uint16_t, uint16_t, uint16_t);" + "u16vec2 min3(u16vec2, u16vec2, u16vec2);" + "u16vec3 min3(u16vec3, u16vec3, u16vec3);" + "u16vec4 min3(u16vec4, u16vec4, u16vec4);" + + "uint16_t max3(uint16_t, uint16_t, uint16_t);" + "u16vec2 max3(u16vec2, u16vec2, u16vec2);" + "u16vec3 max3(u16vec3, u16vec3, u16vec3);" + "u16vec4 max3(u16vec4, u16vec4, u16vec4);" + + "uint16_t mid3(uint16_t, uint16_t, uint16_t);" + "u16vec2 mid3(u16vec2, u16vec2, u16vec2);" + "u16vec3 mid3(u16vec3, u16vec3, u16vec3);" + "u16vec4 mid3(u16vec4, u16vec4, u16vec4);" + + "\n" + ); + } + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 430)) { + commonBuiltins.append( + "uint atomicAdd(coherent volatile inout uint, uint, int, int, int);" + " int atomicAdd(coherent volatile inout int, int, int, int, int);" + + "uint atomicMin(coherent volatile inout uint, uint, int, int, int);" + " int atomicMin(coherent volatile inout int, int, int, int, int);" + + "uint atomicMax(coherent volatile inout uint, uint, int, int, int);" + " int atomicMax(coherent volatile inout int, int, int, int, int);" + + "uint atomicAnd(coherent volatile inout uint, uint, int, int, int);" + " int atomicAnd(coherent volatile inout int, int, int, int, int);" + + "uint atomicOr (coherent volatile inout uint, uint, int, int, int);" + " int atomicOr (coherent volatile inout int, int, int, int, int);" + + "uint atomicXor(coherent volatile inout uint, uint, int, int, int);" + " int atomicXor(coherent volatile inout int, int, int, int, int);" + + "uint atomicExchange(coherent volatile inout uint, uint, int, int, int);" + " int atomicExchange(coherent volatile inout int, int, int, int, int);" + + "uint atomicCompSwap(coherent volatile inout uint, uint, uint, int, int, int, int, int);" + " int atomicCompSwap(coherent volatile inout int, int, int, int, int, int, int, int);" + + "uint atomicLoad(coherent volatile in uint, int, int, int);" + " int atomicLoad(coherent volatile in int, int, int, int);" + + "void atomicStore(coherent volatile out uint, uint, int, int, int);" + "void atomicStore(coherent volatile out int, int, int, int, int);" + + "\n"); + } + + if (profile != EEsProfile && version >= 440) { + commonBuiltins.append( + "uint64_t atomicMin(coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicMin(coherent volatile inout int64_t, int64_t);" + "uint64_t atomicMin(coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicMin(coherent volatile inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicMax(coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicMax(coherent volatile inout int64_t, int64_t);" + "uint64_t atomicMax(coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicMax(coherent volatile inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicAnd(coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicAnd(coherent volatile inout int64_t, int64_t);" + "uint64_t atomicAnd(coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicAnd(coherent volatile inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicOr (coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicOr (coherent volatile inout int64_t, int64_t);" + "uint64_t atomicOr (coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicOr (coherent volatile inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicXor(coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicXor(coherent volatile inout int64_t, int64_t);" + "uint64_t atomicXor(coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicXor(coherent volatile inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicAdd(coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicAdd(coherent volatile inout int64_t, int64_t);" + "uint64_t atomicAdd(coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicAdd(coherent volatile inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicExchange(coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicExchange(coherent volatile inout int64_t, int64_t);" + "uint64_t atomicExchange(coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicExchange(coherent volatile inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicCompSwap(coherent volatile inout uint64_t, uint64_t, uint64_t);" + " int64_t atomicCompSwap(coherent volatile inout int64_t, int64_t, int64_t);" + "uint64_t atomicCompSwap(coherent volatile inout uint64_t, uint64_t, uint64_t, int, int, int, int, int);" + " int64_t atomicCompSwap(coherent volatile inout int64_t, int64_t, int64_t, int, int, int, int, int);" + + "uint64_t atomicLoad(coherent volatile in uint64_t, int, int, int);" + " int64_t atomicLoad(coherent volatile in int64_t, int, int, int);" + + "void atomicStore(coherent volatile out uint64_t, uint64_t, int, int, int);" + "void atomicStore(coherent volatile out int64_t, int64_t, int, int, int);" + "\n"); + } +#endif + + if ((profile == EEsProfile && version >= 300) || + (profile != EEsProfile && version >= 150)) { // GL_ARB_shader_bit_encoding + commonBuiltins.append( + "int floatBitsToInt(highp float value);" + "ivec2 floatBitsToInt(highp vec2 value);" + "ivec3 floatBitsToInt(highp vec3 value);" + "ivec4 floatBitsToInt(highp vec4 value);" + + "uint floatBitsToUint(highp float value);" + "uvec2 floatBitsToUint(highp vec2 value);" + "uvec3 floatBitsToUint(highp vec3 value);" + "uvec4 floatBitsToUint(highp vec4 value);" + + "float intBitsToFloat(highp int value);" + "vec2 intBitsToFloat(highp ivec2 value);" + "vec3 intBitsToFloat(highp ivec3 value);" + "vec4 intBitsToFloat(highp ivec4 value);" + + "float uintBitsToFloat(highp uint value);" + "vec2 uintBitsToFloat(highp uvec2 value);" + "vec3 uintBitsToFloat(highp uvec3 value);" + "vec4 uintBitsToFloat(highp uvec4 value);" + + "\n"); + } + +#ifndef GLSLANG_WEB + if ((profile != EEsProfile && version >= 400) || + (profile == EEsProfile && version >= 310)) { // GL_OES_gpu_shader5 + + commonBuiltins.append( + "float fma(float, float, float );" + "vec2 fma(vec2, vec2, vec2 );" + "vec3 fma(vec3, vec3, vec3 );" + "vec4 fma(vec4, vec4, vec4 );" + "\n"); + } + + if (profile != EEsProfile && version >= 150) { // ARB_gpu_shader_fp64 + commonBuiltins.append( + "double fma(double, double, double);" + "dvec2 fma(dvec2, dvec2, dvec2 );" + "dvec3 fma(dvec3, dvec3, dvec3 );" + "dvec4 fma(dvec4, dvec4, dvec4 );" + "\n"); + } + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 400)) { + commonBuiltins.append( + "float frexp(highp float, out highp int);" + "vec2 frexp(highp vec2, out highp ivec2);" + "vec3 frexp(highp vec3, out highp ivec3);" + "vec4 frexp(highp vec4, out highp ivec4);" + + "float ldexp(highp float, highp int);" + "vec2 ldexp(highp vec2, highp ivec2);" + "vec3 ldexp(highp vec3, highp ivec3);" + "vec4 ldexp(highp vec4, highp ivec4);" + + "\n"); + } + + if (profile != EEsProfile && version >= 150) { // ARB_gpu_shader_fp64 + commonBuiltins.append( + "double frexp(double, out int);" + "dvec2 frexp( dvec2, out ivec2);" + "dvec3 frexp( dvec3, out ivec3);" + "dvec4 frexp( dvec4, out ivec4);" + + "double ldexp(double, int);" + "dvec2 ldexp( dvec2, ivec2);" + "dvec3 ldexp( dvec3, ivec3);" + "dvec4 ldexp( dvec4, ivec4);" + + "double packDouble2x32(uvec2);" + "uvec2 unpackDouble2x32(double);" + + "\n"); + } +#endif + + if ((profile == EEsProfile && version >= 300) || + (profile != EEsProfile && version >= 150)) { + commonBuiltins.append( + "highp uint packUnorm2x16(vec2);" + "vec2 unpackUnorm2x16(highp uint);" + "\n"); + } + + if ((profile == EEsProfile && version >= 300) || + (profile != EEsProfile && version >= 150)) { + commonBuiltins.append( + "highp uint packSnorm2x16(vec2);" + " vec2 unpackSnorm2x16(highp uint);" + "highp uint packHalf2x16(vec2);" + "\n"); + } + + if (profile == EEsProfile && version >= 300) { + commonBuiltins.append( + "mediump vec2 unpackHalf2x16(highp uint);" + "\n"); + } else if (profile != EEsProfile && version >= 150) { + commonBuiltins.append( + " vec2 unpackHalf2x16(highp uint);" + "\n"); + } + +#ifndef GLSLANG_WEB + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 150)) { + commonBuiltins.append( + "highp uint packSnorm4x8(vec4);" + "highp uint packUnorm4x8(vec4);" + "\n"); + } + + if (profile == EEsProfile && version >= 310) { + commonBuiltins.append( + "mediump vec4 unpackSnorm4x8(highp uint);" + "mediump vec4 unpackUnorm4x8(highp uint);" + "\n"); + } else if (profile != EEsProfile && version >= 150) { + commonBuiltins.append( + "vec4 unpackSnorm4x8(highp uint);" + "vec4 unpackUnorm4x8(highp uint);" + "\n"); + } +#endif + + // + // Matrix Functions. + // + commonBuiltins.append( + "mat2 matrixCompMult(mat2 x, mat2 y);" + "mat3 matrixCompMult(mat3 x, mat3 y);" + "mat4 matrixCompMult(mat4 x, mat4 y);" + + "\n"); + + // 120 is correct for both ES and desktop + if (version >= 120) { + commonBuiltins.append( + "mat2 outerProduct(vec2 c, vec2 r);" + "mat3 outerProduct(vec3 c, vec3 r);" + "mat4 outerProduct(vec4 c, vec4 r);" + "mat2x3 outerProduct(vec3 c, vec2 r);" + "mat3x2 outerProduct(vec2 c, vec3 r);" + "mat2x4 outerProduct(vec4 c, vec2 r);" + "mat4x2 outerProduct(vec2 c, vec4 r);" + "mat3x4 outerProduct(vec4 c, vec3 r);" + "mat4x3 outerProduct(vec3 c, vec4 r);" + + "mat2 transpose(mat2 m);" + "mat3 transpose(mat3 m);" + "mat4 transpose(mat4 m);" + "mat2x3 transpose(mat3x2 m);" + "mat3x2 transpose(mat2x3 m);" + "mat2x4 transpose(mat4x2 m);" + "mat4x2 transpose(mat2x4 m);" + "mat3x4 transpose(mat4x3 m);" + "mat4x3 transpose(mat3x4 m);" + + "mat2x3 matrixCompMult(mat2x3, mat2x3);" + "mat2x4 matrixCompMult(mat2x4, mat2x4);" + "mat3x2 matrixCompMult(mat3x2, mat3x2);" + "mat3x4 matrixCompMult(mat3x4, mat3x4);" + "mat4x2 matrixCompMult(mat4x2, mat4x2);" + "mat4x3 matrixCompMult(mat4x3, mat4x3);" + + "\n"); + + // 150 is correct for both ES and desktop + if (version >= 150) { + commonBuiltins.append( + "float determinant(mat2 m);" + "float determinant(mat3 m);" + "float determinant(mat4 m);" + + "mat2 inverse(mat2 m);" + "mat3 inverse(mat3 m);" + "mat4 inverse(mat4 m);" + + "\n"); + } + } + +#ifndef GLSLANG_WEB + // + // Original-style texture functions existing in all stages. + // (Per-stage functions below.) + // + if ((profile == EEsProfile && version == 100) || + profile == ECompatibilityProfile || + (profile == ECoreProfile && version < 420) || + profile == ENoProfile) { + if (spvVersion.spv == 0) { + commonBuiltins.append( + "vec4 texture2D(sampler2D, vec2);" + + "vec4 texture2DProj(sampler2D, vec3);" + "vec4 texture2DProj(sampler2D, vec4);" + + "vec4 texture3D(sampler3D, vec3);" // OES_texture_3D, but caught by keyword check + "vec4 texture3DProj(sampler3D, vec4);" // OES_texture_3D, but caught by keyword check + + "vec4 textureCube(samplerCube, vec3);" + + "\n"); + } + } + + if ( profile == ECompatibilityProfile || + (profile == ECoreProfile && version < 420) || + profile == ENoProfile) { + if (spvVersion.spv == 0) { + commonBuiltins.append( + "vec4 texture1D(sampler1D, float);" + + "vec4 texture1DProj(sampler1D, vec2);" + "vec4 texture1DProj(sampler1D, vec4);" + + "vec4 shadow1D(sampler1DShadow, vec3);" + "vec4 shadow2D(sampler2DShadow, vec3);" + "vec4 shadow1DProj(sampler1DShadow, vec4);" + "vec4 shadow2DProj(sampler2DShadow, vec4);" + + "vec4 texture2DRect(sampler2DRect, vec2);" // GL_ARB_texture_rectangle, caught by keyword check + "vec4 texture2DRectProj(sampler2DRect, vec3);" // GL_ARB_texture_rectangle, caught by keyword check + "vec4 texture2DRectProj(sampler2DRect, vec4);" // GL_ARB_texture_rectangle, caught by keyword check + "vec4 shadow2DRect(sampler2DRectShadow, vec3);" // GL_ARB_texture_rectangle, caught by keyword check + "vec4 shadow2DRectProj(sampler2DRectShadow, vec4);" // GL_ARB_texture_rectangle, caught by keyword check + + "\n"); + } + } + + if (profile == EEsProfile) { + if (spvVersion.spv == 0) { + if (version < 300) { + commonBuiltins.append( + "vec4 texture2D(samplerExternalOES, vec2 coord);" // GL_OES_EGL_image_external + "vec4 texture2DProj(samplerExternalOES, vec3);" // GL_OES_EGL_image_external + "vec4 texture2DProj(samplerExternalOES, vec4);" // GL_OES_EGL_image_external + "\n"); + } else { + commonBuiltins.append( + "highp ivec2 textureSize(samplerExternalOES, int lod);" // GL_OES_EGL_image_external_essl3 + "vec4 texture(samplerExternalOES, vec2);" // GL_OES_EGL_image_external_essl3 + "vec4 texture(samplerExternalOES, vec2, float bias);" // GL_OES_EGL_image_external_essl3 + "vec4 textureProj(samplerExternalOES, vec3);" // GL_OES_EGL_image_external_essl3 + "vec4 textureProj(samplerExternalOES, vec3, float bias);" // GL_OES_EGL_image_external_essl3 + "vec4 textureProj(samplerExternalOES, vec4);" // GL_OES_EGL_image_external_essl3 + "vec4 textureProj(samplerExternalOES, vec4, float bias);" // GL_OES_EGL_image_external_essl3 + "vec4 texelFetch(samplerExternalOES, ivec2, int lod);" // GL_OES_EGL_image_external_essl3 + "\n"); + } + commonBuiltins.append( + "highp ivec2 textureSize(__samplerExternal2DY2YEXT, int lod);" // GL_EXT_YUV_target + "vec4 texture(__samplerExternal2DY2YEXT, vec2);" // GL_EXT_YUV_target + "vec4 texture(__samplerExternal2DY2YEXT, vec2, float bias);" // GL_EXT_YUV_target + "vec4 textureProj(__samplerExternal2DY2YEXT, vec3);" // GL_EXT_YUV_target + "vec4 textureProj(__samplerExternal2DY2YEXT, vec3, float bias);" // GL_EXT_YUV_target + "vec4 textureProj(__samplerExternal2DY2YEXT, vec4);" // GL_EXT_YUV_target + "vec4 textureProj(__samplerExternal2DY2YEXT, vec4, float bias);" // GL_EXT_YUV_target + "vec4 texelFetch(__samplerExternal2DY2YEXT sampler, ivec2, int lod);" // GL_EXT_YUV_target + "\n"); + commonBuiltins.append( + "vec4 texture2DGradEXT(sampler2D, vec2, vec2, vec2);" // GL_EXT_shader_texture_lod + "vec4 texture2DProjGradEXT(sampler2D, vec3, vec2, vec2);" // GL_EXT_shader_texture_lod + "vec4 texture2DProjGradEXT(sampler2D, vec4, vec2, vec2);" // GL_EXT_shader_texture_lod + "vec4 textureCubeGradEXT(samplerCube, vec3, vec3, vec3);" // GL_EXT_shader_texture_lod + + "float shadow2DEXT(sampler2DShadow, vec3);" // GL_EXT_shadow_samplers + "float shadow2DProjEXT(sampler2DShadow, vec4);" // GL_EXT_shadow_samplers + + "\n"); + } + } + + if (version >= 300) { + commonBuiltins.append( + "highp ivec2 textureSize(samplerVideo, int lod);" + "vec4 texture(samplerVideo, vec2);" + "vec4 texture(samplerVideo, vec2, float bias);" + "vec4 textureProj(samplerVideo, vec3);" + "vec4 textureProj(samplerVideo, vec3, float bias);" + "vec4 textureProj(samplerVideo, vec4);" + "vec4 textureProj(samplerVideo, vec4, float bias);" + "vec4 texelFetch(samplerVideo, ivec2, int lod);" + "\n"); + } + + // + // Noise functions. + // + if (spvVersion.spv == 0 && profile != EEsProfile) { + commonBuiltins.append( + "float noise1(float x);" + "float noise1(vec2 x);" + "float noise1(vec3 x);" + "float noise1(vec4 x);" + + "vec2 noise2(float x);" + "vec2 noise2(vec2 x);" + "vec2 noise2(vec3 x);" + "vec2 noise2(vec4 x);" + + "vec3 noise3(float x);" + "vec3 noise3(vec2 x);" + "vec3 noise3(vec3 x);" + "vec3 noise3(vec4 x);" + + "vec4 noise4(float x);" + "vec4 noise4(vec2 x);" + "vec4 noise4(vec3 x);" + "vec4 noise4(vec4 x);" + + "\n"); + } + + if (spvVersion.vulkan == 0) { + // + // Atomic counter functions. + // + if ((profile != EEsProfile && version >= 300) || + (profile == EEsProfile && version >= 310)) { + commonBuiltins.append( + "uint atomicCounterIncrement(atomic_uint);" + "uint atomicCounterDecrement(atomic_uint);" + "uint atomicCounter(atomic_uint);" + + "\n"); + } + if (profile != EEsProfile && version >= 460) { + commonBuiltins.append( + "uint atomicCounterAdd(atomic_uint, uint);" + "uint atomicCounterSubtract(atomic_uint, uint);" + "uint atomicCounterMin(atomic_uint, uint);" + "uint atomicCounterMax(atomic_uint, uint);" + "uint atomicCounterAnd(atomic_uint, uint);" + "uint atomicCounterOr(atomic_uint, uint);" + "uint atomicCounterXor(atomic_uint, uint);" + "uint atomicCounterExchange(atomic_uint, uint);" + "uint atomicCounterCompSwap(atomic_uint, uint, uint);" + + "\n"); + } + } + + // Bitfield + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 400)) { + commonBuiltins.append( + " int bitfieldExtract( int, int, int);" + "ivec2 bitfieldExtract(ivec2, int, int);" + "ivec3 bitfieldExtract(ivec3, int, int);" + "ivec4 bitfieldExtract(ivec4, int, int);" + + " uint bitfieldExtract( uint, int, int);" + "uvec2 bitfieldExtract(uvec2, int, int);" + "uvec3 bitfieldExtract(uvec3, int, int);" + "uvec4 bitfieldExtract(uvec4, int, int);" + + " int bitfieldInsert( int base, int, int, int);" + "ivec2 bitfieldInsert(ivec2 base, ivec2, int, int);" + "ivec3 bitfieldInsert(ivec3 base, ivec3, int, int);" + "ivec4 bitfieldInsert(ivec4 base, ivec4, int, int);" + + " uint bitfieldInsert( uint base, uint, int, int);" + "uvec2 bitfieldInsert(uvec2 base, uvec2, int, int);" + "uvec3 bitfieldInsert(uvec3 base, uvec3, int, int);" + "uvec4 bitfieldInsert(uvec4 base, uvec4, int, int);" + + "\n"); + } + + if (profile != EEsProfile && version >= 400) { + commonBuiltins.append( + " int findLSB( int);" + "ivec2 findLSB(ivec2);" + "ivec3 findLSB(ivec3);" + "ivec4 findLSB(ivec4);" + + " int findLSB( uint);" + "ivec2 findLSB(uvec2);" + "ivec3 findLSB(uvec3);" + "ivec4 findLSB(uvec4);" + + "\n"); + } else if (profile == EEsProfile && version >= 310) { + commonBuiltins.append( + "lowp int findLSB( int);" + "lowp ivec2 findLSB(ivec2);" + "lowp ivec3 findLSB(ivec3);" + "lowp ivec4 findLSB(ivec4);" + + "lowp int findLSB( uint);" + "lowp ivec2 findLSB(uvec2);" + "lowp ivec3 findLSB(uvec3);" + "lowp ivec4 findLSB(uvec4);" + + "\n"); + } + + if (profile != EEsProfile && version >= 400) { + commonBuiltins.append( + " int bitCount( int);" + "ivec2 bitCount(ivec2);" + "ivec3 bitCount(ivec3);" + "ivec4 bitCount(ivec4);" + + " int bitCount( uint);" + "ivec2 bitCount(uvec2);" + "ivec3 bitCount(uvec3);" + "ivec4 bitCount(uvec4);" + + " int findMSB(highp int);" + "ivec2 findMSB(highp ivec2);" + "ivec3 findMSB(highp ivec3);" + "ivec4 findMSB(highp ivec4);" + + " int findMSB(highp uint);" + "ivec2 findMSB(highp uvec2);" + "ivec3 findMSB(highp uvec3);" + "ivec4 findMSB(highp uvec4);" + + "\n"); + } + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 400)) { + commonBuiltins.append( + " uint uaddCarry(highp uint, highp uint, out lowp uint carry);" + "uvec2 uaddCarry(highp uvec2, highp uvec2, out lowp uvec2 carry);" + "uvec3 uaddCarry(highp uvec3, highp uvec3, out lowp uvec3 carry);" + "uvec4 uaddCarry(highp uvec4, highp uvec4, out lowp uvec4 carry);" + + " uint usubBorrow(highp uint, highp uint, out lowp uint borrow);" + "uvec2 usubBorrow(highp uvec2, highp uvec2, out lowp uvec2 borrow);" + "uvec3 usubBorrow(highp uvec3, highp uvec3, out lowp uvec3 borrow);" + "uvec4 usubBorrow(highp uvec4, highp uvec4, out lowp uvec4 borrow);" + + "void umulExtended(highp uint, highp uint, out highp uint, out highp uint lsb);" + "void umulExtended(highp uvec2, highp uvec2, out highp uvec2, out highp uvec2 lsb);" + "void umulExtended(highp uvec3, highp uvec3, out highp uvec3, out highp uvec3 lsb);" + "void umulExtended(highp uvec4, highp uvec4, out highp uvec4, out highp uvec4 lsb);" + + "void imulExtended(highp int, highp int, out highp int, out highp int lsb);" + "void imulExtended(highp ivec2, highp ivec2, out highp ivec2, out highp ivec2 lsb);" + "void imulExtended(highp ivec3, highp ivec3, out highp ivec3, out highp ivec3 lsb);" + "void imulExtended(highp ivec4, highp ivec4, out highp ivec4, out highp ivec4 lsb);" + + " int bitfieldReverse(highp int);" + "ivec2 bitfieldReverse(highp ivec2);" + "ivec3 bitfieldReverse(highp ivec3);" + "ivec4 bitfieldReverse(highp ivec4);" + + " uint bitfieldReverse(highp uint);" + "uvec2 bitfieldReverse(highp uvec2);" + "uvec3 bitfieldReverse(highp uvec3);" + "uvec4 bitfieldReverse(highp uvec4);" + + "\n"); + } + + if (profile == EEsProfile && version >= 310) { + commonBuiltins.append( + "lowp int bitCount( int);" + "lowp ivec2 bitCount(ivec2);" + "lowp ivec3 bitCount(ivec3);" + "lowp ivec4 bitCount(ivec4);" + + "lowp int bitCount( uint);" + "lowp ivec2 bitCount(uvec2);" + "lowp ivec3 bitCount(uvec3);" + "lowp ivec4 bitCount(uvec4);" + + "lowp int findMSB(highp int);" + "lowp ivec2 findMSB(highp ivec2);" + "lowp ivec3 findMSB(highp ivec3);" + "lowp ivec4 findMSB(highp ivec4);" + + "lowp int findMSB(highp uint);" + "lowp ivec2 findMSB(highp uvec2);" + "lowp ivec3 findMSB(highp uvec3);" + "lowp ivec4 findMSB(highp uvec4);" + + "\n"); + } + + // GL_ARB_shader_ballot + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "uint64_t ballotARB(bool);" + + "float readInvocationARB(float, uint);" + "vec2 readInvocationARB(vec2, uint);" + "vec3 readInvocationARB(vec3, uint);" + "vec4 readInvocationARB(vec4, uint);" + + "int readInvocationARB(int, uint);" + "ivec2 readInvocationARB(ivec2, uint);" + "ivec3 readInvocationARB(ivec3, uint);" + "ivec4 readInvocationARB(ivec4, uint);" + + "uint readInvocationARB(uint, uint);" + "uvec2 readInvocationARB(uvec2, uint);" + "uvec3 readInvocationARB(uvec3, uint);" + "uvec4 readInvocationARB(uvec4, uint);" + + "float readFirstInvocationARB(float);" + "vec2 readFirstInvocationARB(vec2);" + "vec3 readFirstInvocationARB(vec3);" + "vec4 readFirstInvocationARB(vec4);" + + "int readFirstInvocationARB(int);" + "ivec2 readFirstInvocationARB(ivec2);" + "ivec3 readFirstInvocationARB(ivec3);" + "ivec4 readFirstInvocationARB(ivec4);" + + "uint readFirstInvocationARB(uint);" + "uvec2 readFirstInvocationARB(uvec2);" + "uvec3 readFirstInvocationARB(uvec3);" + "uvec4 readFirstInvocationARB(uvec4);" + + "\n"); + } + + // GL_ARB_shader_group_vote + if (profile != EEsProfile && version >= 430) { + commonBuiltins.append( + "bool anyInvocationARB(bool);" + "bool allInvocationsARB(bool);" + "bool allInvocationsEqualARB(bool);" + + "\n"); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + commonBuiltins.append( + "void subgroupBarrier();" + "void subgroupMemoryBarrier();" + "void subgroupMemoryBarrierBuffer();" + "void subgroupMemoryBarrierImage();" + "bool subgroupElect();" + + "bool subgroupAll(bool);\n" + "bool subgroupAny(bool);\n" + "uvec4 subgroupBallot(bool);\n" + "bool subgroupInverseBallot(uvec4);\n" + "bool subgroupBallotBitExtract(uvec4, uint);\n" + "uint subgroupBallotBitCount(uvec4);\n" + "uint subgroupBallotInclusiveBitCount(uvec4);\n" + "uint subgroupBallotExclusiveBitCount(uvec4);\n" + "uint subgroupBallotFindLSB(uvec4);\n" + "uint subgroupBallotFindMSB(uvec4);\n" + ); + + // Generate all flavors of subgroup ops. + static const char *subgroupOps[] = + { + "bool subgroupAllEqual(%s);\n", + "%s subgroupBroadcast(%s, uint);\n", + "%s subgroupBroadcastFirst(%s);\n", + "%s subgroupShuffle(%s, uint);\n", + "%s subgroupShuffleXor(%s, uint);\n", + "%s subgroupShuffleUp(%s, uint delta);\n", + "%s subgroupShuffleDown(%s, uint delta);\n", + "%s subgroupAdd(%s);\n", + "%s subgroupMul(%s);\n", + "%s subgroupMin(%s);\n", + "%s subgroupMax(%s);\n", + "%s subgroupAnd(%s);\n", + "%s subgroupOr(%s);\n", + "%s subgroupXor(%s);\n", + "%s subgroupInclusiveAdd(%s);\n", + "%s subgroupInclusiveMul(%s);\n", + "%s subgroupInclusiveMin(%s);\n", + "%s subgroupInclusiveMax(%s);\n", + "%s subgroupInclusiveAnd(%s);\n", + "%s subgroupInclusiveOr(%s);\n", + "%s subgroupInclusiveXor(%s);\n", + "%s subgroupExclusiveAdd(%s);\n", + "%s subgroupExclusiveMul(%s);\n", + "%s subgroupExclusiveMin(%s);\n", + "%s subgroupExclusiveMax(%s);\n", + "%s subgroupExclusiveAnd(%s);\n", + "%s subgroupExclusiveOr(%s);\n", + "%s subgroupExclusiveXor(%s);\n", + "%s subgroupClusteredAdd(%s, uint);\n", + "%s subgroupClusteredMul(%s, uint);\n", + "%s subgroupClusteredMin(%s, uint);\n", + "%s subgroupClusteredMax(%s, uint);\n", + "%s subgroupClusteredAnd(%s, uint);\n", + "%s subgroupClusteredOr(%s, uint);\n", + "%s subgroupClusteredXor(%s, uint);\n", + "%s subgroupQuadBroadcast(%s, uint);\n", + "%s subgroupQuadSwapHorizontal(%s);\n", + "%s subgroupQuadSwapVertical(%s);\n", + "%s subgroupQuadSwapDiagonal(%s);\n", + "uvec4 subgroupPartitionNV(%s);\n", + "%s subgroupPartitionedAddNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedMulNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedMinNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedMaxNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedAndNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedOrNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedXorNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveAddNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveMulNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveMinNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveMaxNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveAndNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveOrNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveXorNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveAddNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveMulNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveMinNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveMaxNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveAndNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveOrNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveXorNV(%s, uvec4 ballot);\n", + }; + + static const char *floatTypes[] = { + "float", "vec2", "vec3", "vec4", + "float16_t", "f16vec2", "f16vec3", "f16vec4", + }; + static const char *doubleTypes[] = { + "double", "dvec2", "dvec3", "dvec4", + }; + static const char *intTypes[] = { + "int8_t", "i8vec2", "i8vec3", "i8vec4", + "int16_t", "i16vec2", "i16vec3", "i16vec4", + "int", "ivec2", "ivec3", "ivec4", + "int64_t", "i64vec2", "i64vec3", "i64vec4", + "uint8_t", "u8vec2", "u8vec3", "u8vec4", + "uint16_t", "u16vec2", "u16vec3", "u16vec4", + "uint", "uvec2", "uvec3", "uvec4", + "uint64_t", "u64vec2", "u64vec3", "u64vec4", + }; + static const char *boolTypes[] = { + "bool", "bvec2", "bvec3", "bvec4", + }; + + for (size_t i = 0; i < sizeof(subgroupOps)/sizeof(subgroupOps[0]); ++i) { + const char *op = subgroupOps[i]; + + // Logical operations don't support float + bool logicalOp = strstr(op, "Or") || strstr(op, "And") || + (strstr(op, "Xor") && !strstr(op, "ShuffleXor")); + // Math operations don't support bool + bool mathOp = strstr(op, "Add") || strstr(op, "Mul") || strstr(op, "Min") || strstr(op, "Max"); + + const int bufSize = 256; + char buf[bufSize]; + + if (!logicalOp) { + for (size_t j = 0; j < sizeof(floatTypes)/sizeof(floatTypes[0]); ++j) { + snprintf(buf, bufSize, op, floatTypes[j], floatTypes[j]); + commonBuiltins.append(buf); + } + if (profile != EEsProfile && version >= 400) { + for (size_t j = 0; j < sizeof(doubleTypes)/sizeof(doubleTypes[0]); ++j) { + snprintf(buf, bufSize, op, doubleTypes[j], doubleTypes[j]); + commonBuiltins.append(buf); + } + } + } + if (!mathOp) { + for (size_t j = 0; j < sizeof(boolTypes)/sizeof(boolTypes[0]); ++j) { + snprintf(buf, bufSize, op, boolTypes[j], boolTypes[j]); + commonBuiltins.append(buf); + } + } + for (size_t j = 0; j < sizeof(intTypes)/sizeof(intTypes[0]); ++j) { + snprintf(buf, bufSize, op, intTypes[j], intTypes[j]); + commonBuiltins.append(buf); + } + } + + stageBuiltins[EShLangCompute].append( + "void subgroupMemoryBarrierShared();" + + "\n" + ); + stageBuiltins[EShLangMeshNV].append( + "void subgroupMemoryBarrierShared();" + "\n" + ); + stageBuiltins[EShLangTaskNV].append( + "void subgroupMemoryBarrierShared();" + "\n" + ); + } + + if (profile != EEsProfile && version >= 460) { + commonBuiltins.append( + "bool anyInvocation(bool);" + "bool allInvocations(bool);" + "bool allInvocationsEqual(bool);" + + "\n"); + } + + // GL_AMD_shader_ballot + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "float minInvocationsAMD(float);" + "vec2 minInvocationsAMD(vec2);" + "vec3 minInvocationsAMD(vec3);" + "vec4 minInvocationsAMD(vec4);" + + "int minInvocationsAMD(int);" + "ivec2 minInvocationsAMD(ivec2);" + "ivec3 minInvocationsAMD(ivec3);" + "ivec4 minInvocationsAMD(ivec4);" + + "uint minInvocationsAMD(uint);" + "uvec2 minInvocationsAMD(uvec2);" + "uvec3 minInvocationsAMD(uvec3);" + "uvec4 minInvocationsAMD(uvec4);" + + "double minInvocationsAMD(double);" + "dvec2 minInvocationsAMD(dvec2);" + "dvec3 minInvocationsAMD(dvec3);" + "dvec4 minInvocationsAMD(dvec4);" + + "int64_t minInvocationsAMD(int64_t);" + "i64vec2 minInvocationsAMD(i64vec2);" + "i64vec3 minInvocationsAMD(i64vec3);" + "i64vec4 minInvocationsAMD(i64vec4);" + + "uint64_t minInvocationsAMD(uint64_t);" + "u64vec2 minInvocationsAMD(u64vec2);" + "u64vec3 minInvocationsAMD(u64vec3);" + "u64vec4 minInvocationsAMD(u64vec4);" + + "float16_t minInvocationsAMD(float16_t);" + "f16vec2 minInvocationsAMD(f16vec2);" + "f16vec3 minInvocationsAMD(f16vec3);" + "f16vec4 minInvocationsAMD(f16vec4);" + + "int16_t minInvocationsAMD(int16_t);" + "i16vec2 minInvocationsAMD(i16vec2);" + "i16vec3 minInvocationsAMD(i16vec3);" + "i16vec4 minInvocationsAMD(i16vec4);" + + "uint16_t minInvocationsAMD(uint16_t);" + "u16vec2 minInvocationsAMD(u16vec2);" + "u16vec3 minInvocationsAMD(u16vec3);" + "u16vec4 minInvocationsAMD(u16vec4);" + + "float minInvocationsInclusiveScanAMD(float);" + "vec2 minInvocationsInclusiveScanAMD(vec2);" + "vec3 minInvocationsInclusiveScanAMD(vec3);" + "vec4 minInvocationsInclusiveScanAMD(vec4);" + + "int minInvocationsInclusiveScanAMD(int);" + "ivec2 minInvocationsInclusiveScanAMD(ivec2);" + "ivec3 minInvocationsInclusiveScanAMD(ivec3);" + "ivec4 minInvocationsInclusiveScanAMD(ivec4);" + + "uint minInvocationsInclusiveScanAMD(uint);" + "uvec2 minInvocationsInclusiveScanAMD(uvec2);" + "uvec3 minInvocationsInclusiveScanAMD(uvec3);" + "uvec4 minInvocationsInclusiveScanAMD(uvec4);" + + "double minInvocationsInclusiveScanAMD(double);" + "dvec2 minInvocationsInclusiveScanAMD(dvec2);" + "dvec3 minInvocationsInclusiveScanAMD(dvec3);" + "dvec4 minInvocationsInclusiveScanAMD(dvec4);" + + "int64_t minInvocationsInclusiveScanAMD(int64_t);" + "i64vec2 minInvocationsInclusiveScanAMD(i64vec2);" + "i64vec3 minInvocationsInclusiveScanAMD(i64vec3);" + "i64vec4 minInvocationsInclusiveScanAMD(i64vec4);" + + "uint64_t minInvocationsInclusiveScanAMD(uint64_t);" + "u64vec2 minInvocationsInclusiveScanAMD(u64vec2);" + "u64vec3 minInvocationsInclusiveScanAMD(u64vec3);" + "u64vec4 minInvocationsInclusiveScanAMD(u64vec4);" + + "float16_t minInvocationsInclusiveScanAMD(float16_t);" + "f16vec2 minInvocationsInclusiveScanAMD(f16vec2);" + "f16vec3 minInvocationsInclusiveScanAMD(f16vec3);" + "f16vec4 minInvocationsInclusiveScanAMD(f16vec4);" + + "int16_t minInvocationsInclusiveScanAMD(int16_t);" + "i16vec2 minInvocationsInclusiveScanAMD(i16vec2);" + "i16vec3 minInvocationsInclusiveScanAMD(i16vec3);" + "i16vec4 minInvocationsInclusiveScanAMD(i16vec4);" + + "uint16_t minInvocationsInclusiveScanAMD(uint16_t);" + "u16vec2 minInvocationsInclusiveScanAMD(u16vec2);" + "u16vec3 minInvocationsInclusiveScanAMD(u16vec3);" + "u16vec4 minInvocationsInclusiveScanAMD(u16vec4);" + + "float minInvocationsExclusiveScanAMD(float);" + "vec2 minInvocationsExclusiveScanAMD(vec2);" + "vec3 minInvocationsExclusiveScanAMD(vec3);" + "vec4 minInvocationsExclusiveScanAMD(vec4);" + + "int minInvocationsExclusiveScanAMD(int);" + "ivec2 minInvocationsExclusiveScanAMD(ivec2);" + "ivec3 minInvocationsExclusiveScanAMD(ivec3);" + "ivec4 minInvocationsExclusiveScanAMD(ivec4);" + + "uint minInvocationsExclusiveScanAMD(uint);" + "uvec2 minInvocationsExclusiveScanAMD(uvec2);" + "uvec3 minInvocationsExclusiveScanAMD(uvec3);" + "uvec4 minInvocationsExclusiveScanAMD(uvec4);" + + "double minInvocationsExclusiveScanAMD(double);" + "dvec2 minInvocationsExclusiveScanAMD(dvec2);" + "dvec3 minInvocationsExclusiveScanAMD(dvec3);" + "dvec4 minInvocationsExclusiveScanAMD(dvec4);" + + "int64_t minInvocationsExclusiveScanAMD(int64_t);" + "i64vec2 minInvocationsExclusiveScanAMD(i64vec2);" + "i64vec3 minInvocationsExclusiveScanAMD(i64vec3);" + "i64vec4 minInvocationsExclusiveScanAMD(i64vec4);" + + "uint64_t minInvocationsExclusiveScanAMD(uint64_t);" + "u64vec2 minInvocationsExclusiveScanAMD(u64vec2);" + "u64vec3 minInvocationsExclusiveScanAMD(u64vec3);" + "u64vec4 minInvocationsExclusiveScanAMD(u64vec4);" + + "float16_t minInvocationsExclusiveScanAMD(float16_t);" + "f16vec2 minInvocationsExclusiveScanAMD(f16vec2);" + "f16vec3 minInvocationsExclusiveScanAMD(f16vec3);" + "f16vec4 minInvocationsExclusiveScanAMD(f16vec4);" + + "int16_t minInvocationsExclusiveScanAMD(int16_t);" + "i16vec2 minInvocationsExclusiveScanAMD(i16vec2);" + "i16vec3 minInvocationsExclusiveScanAMD(i16vec3);" + "i16vec4 minInvocationsExclusiveScanAMD(i16vec4);" + + "uint16_t minInvocationsExclusiveScanAMD(uint16_t);" + "u16vec2 minInvocationsExclusiveScanAMD(u16vec2);" + "u16vec3 minInvocationsExclusiveScanAMD(u16vec3);" + "u16vec4 minInvocationsExclusiveScanAMD(u16vec4);" + + "float maxInvocationsAMD(float);" + "vec2 maxInvocationsAMD(vec2);" + "vec3 maxInvocationsAMD(vec3);" + "vec4 maxInvocationsAMD(vec4);" + + "int maxInvocationsAMD(int);" + "ivec2 maxInvocationsAMD(ivec2);" + "ivec3 maxInvocationsAMD(ivec3);" + "ivec4 maxInvocationsAMD(ivec4);" + + "uint maxInvocationsAMD(uint);" + "uvec2 maxInvocationsAMD(uvec2);" + "uvec3 maxInvocationsAMD(uvec3);" + "uvec4 maxInvocationsAMD(uvec4);" + + "double maxInvocationsAMD(double);" + "dvec2 maxInvocationsAMD(dvec2);" + "dvec3 maxInvocationsAMD(dvec3);" + "dvec4 maxInvocationsAMD(dvec4);" + + "int64_t maxInvocationsAMD(int64_t);" + "i64vec2 maxInvocationsAMD(i64vec2);" + "i64vec3 maxInvocationsAMD(i64vec3);" + "i64vec4 maxInvocationsAMD(i64vec4);" + + "uint64_t maxInvocationsAMD(uint64_t);" + "u64vec2 maxInvocationsAMD(u64vec2);" + "u64vec3 maxInvocationsAMD(u64vec3);" + "u64vec4 maxInvocationsAMD(u64vec4);" + + "float16_t maxInvocationsAMD(float16_t);" + "f16vec2 maxInvocationsAMD(f16vec2);" + "f16vec3 maxInvocationsAMD(f16vec3);" + "f16vec4 maxInvocationsAMD(f16vec4);" + + "int16_t maxInvocationsAMD(int16_t);" + "i16vec2 maxInvocationsAMD(i16vec2);" + "i16vec3 maxInvocationsAMD(i16vec3);" + "i16vec4 maxInvocationsAMD(i16vec4);" + + "uint16_t maxInvocationsAMD(uint16_t);" + "u16vec2 maxInvocationsAMD(u16vec2);" + "u16vec3 maxInvocationsAMD(u16vec3);" + "u16vec4 maxInvocationsAMD(u16vec4);" + + "float maxInvocationsInclusiveScanAMD(float);" + "vec2 maxInvocationsInclusiveScanAMD(vec2);" + "vec3 maxInvocationsInclusiveScanAMD(vec3);" + "vec4 maxInvocationsInclusiveScanAMD(vec4);" + + "int maxInvocationsInclusiveScanAMD(int);" + "ivec2 maxInvocationsInclusiveScanAMD(ivec2);" + "ivec3 maxInvocationsInclusiveScanAMD(ivec3);" + "ivec4 maxInvocationsInclusiveScanAMD(ivec4);" + + "uint maxInvocationsInclusiveScanAMD(uint);" + "uvec2 maxInvocationsInclusiveScanAMD(uvec2);" + "uvec3 maxInvocationsInclusiveScanAMD(uvec3);" + "uvec4 maxInvocationsInclusiveScanAMD(uvec4);" + + "double maxInvocationsInclusiveScanAMD(double);" + "dvec2 maxInvocationsInclusiveScanAMD(dvec2);" + "dvec3 maxInvocationsInclusiveScanAMD(dvec3);" + "dvec4 maxInvocationsInclusiveScanAMD(dvec4);" + + "int64_t maxInvocationsInclusiveScanAMD(int64_t);" + "i64vec2 maxInvocationsInclusiveScanAMD(i64vec2);" + "i64vec3 maxInvocationsInclusiveScanAMD(i64vec3);" + "i64vec4 maxInvocationsInclusiveScanAMD(i64vec4);" + + "uint64_t maxInvocationsInclusiveScanAMD(uint64_t);" + "u64vec2 maxInvocationsInclusiveScanAMD(u64vec2);" + "u64vec3 maxInvocationsInclusiveScanAMD(u64vec3);" + "u64vec4 maxInvocationsInclusiveScanAMD(u64vec4);" + + "float16_t maxInvocationsInclusiveScanAMD(float16_t);" + "f16vec2 maxInvocationsInclusiveScanAMD(f16vec2);" + "f16vec3 maxInvocationsInclusiveScanAMD(f16vec3);" + "f16vec4 maxInvocationsInclusiveScanAMD(f16vec4);" + + "int16_t maxInvocationsInclusiveScanAMD(int16_t);" + "i16vec2 maxInvocationsInclusiveScanAMD(i16vec2);" + "i16vec3 maxInvocationsInclusiveScanAMD(i16vec3);" + "i16vec4 maxInvocationsInclusiveScanAMD(i16vec4);" + + "uint16_t maxInvocationsInclusiveScanAMD(uint16_t);" + "u16vec2 maxInvocationsInclusiveScanAMD(u16vec2);" + "u16vec3 maxInvocationsInclusiveScanAMD(u16vec3);" + "u16vec4 maxInvocationsInclusiveScanAMD(u16vec4);" + + "float maxInvocationsExclusiveScanAMD(float);" + "vec2 maxInvocationsExclusiveScanAMD(vec2);" + "vec3 maxInvocationsExclusiveScanAMD(vec3);" + "vec4 maxInvocationsExclusiveScanAMD(vec4);" + + "int maxInvocationsExclusiveScanAMD(int);" + "ivec2 maxInvocationsExclusiveScanAMD(ivec2);" + "ivec3 maxInvocationsExclusiveScanAMD(ivec3);" + "ivec4 maxInvocationsExclusiveScanAMD(ivec4);" + + "uint maxInvocationsExclusiveScanAMD(uint);" + "uvec2 maxInvocationsExclusiveScanAMD(uvec2);" + "uvec3 maxInvocationsExclusiveScanAMD(uvec3);" + "uvec4 maxInvocationsExclusiveScanAMD(uvec4);" + + "double maxInvocationsExclusiveScanAMD(double);" + "dvec2 maxInvocationsExclusiveScanAMD(dvec2);" + "dvec3 maxInvocationsExclusiveScanAMD(dvec3);" + "dvec4 maxInvocationsExclusiveScanAMD(dvec4);" + + "int64_t maxInvocationsExclusiveScanAMD(int64_t);" + "i64vec2 maxInvocationsExclusiveScanAMD(i64vec2);" + "i64vec3 maxInvocationsExclusiveScanAMD(i64vec3);" + "i64vec4 maxInvocationsExclusiveScanAMD(i64vec4);" + + "uint64_t maxInvocationsExclusiveScanAMD(uint64_t);" + "u64vec2 maxInvocationsExclusiveScanAMD(u64vec2);" + "u64vec3 maxInvocationsExclusiveScanAMD(u64vec3);" + "u64vec4 maxInvocationsExclusiveScanAMD(u64vec4);" + + "float16_t maxInvocationsExclusiveScanAMD(float16_t);" + "f16vec2 maxInvocationsExclusiveScanAMD(f16vec2);" + "f16vec3 maxInvocationsExclusiveScanAMD(f16vec3);" + "f16vec4 maxInvocationsExclusiveScanAMD(f16vec4);" + + "int16_t maxInvocationsExclusiveScanAMD(int16_t);" + "i16vec2 maxInvocationsExclusiveScanAMD(i16vec2);" + "i16vec3 maxInvocationsExclusiveScanAMD(i16vec3);" + "i16vec4 maxInvocationsExclusiveScanAMD(i16vec4);" + + "uint16_t maxInvocationsExclusiveScanAMD(uint16_t);" + "u16vec2 maxInvocationsExclusiveScanAMD(u16vec2);" + "u16vec3 maxInvocationsExclusiveScanAMD(u16vec3);" + "u16vec4 maxInvocationsExclusiveScanAMD(u16vec4);" + + "float addInvocationsAMD(float);" + "vec2 addInvocationsAMD(vec2);" + "vec3 addInvocationsAMD(vec3);" + "vec4 addInvocationsAMD(vec4);" + + "int addInvocationsAMD(int);" + "ivec2 addInvocationsAMD(ivec2);" + "ivec3 addInvocationsAMD(ivec3);" + "ivec4 addInvocationsAMD(ivec4);" + + "uint addInvocationsAMD(uint);" + "uvec2 addInvocationsAMD(uvec2);" + "uvec3 addInvocationsAMD(uvec3);" + "uvec4 addInvocationsAMD(uvec4);" + + "double addInvocationsAMD(double);" + "dvec2 addInvocationsAMD(dvec2);" + "dvec3 addInvocationsAMD(dvec3);" + "dvec4 addInvocationsAMD(dvec4);" + + "int64_t addInvocationsAMD(int64_t);" + "i64vec2 addInvocationsAMD(i64vec2);" + "i64vec3 addInvocationsAMD(i64vec3);" + "i64vec4 addInvocationsAMD(i64vec4);" + + "uint64_t addInvocationsAMD(uint64_t);" + "u64vec2 addInvocationsAMD(u64vec2);" + "u64vec3 addInvocationsAMD(u64vec3);" + "u64vec4 addInvocationsAMD(u64vec4);" + + "float16_t addInvocationsAMD(float16_t);" + "f16vec2 addInvocationsAMD(f16vec2);" + "f16vec3 addInvocationsAMD(f16vec3);" + "f16vec4 addInvocationsAMD(f16vec4);" + + "int16_t addInvocationsAMD(int16_t);" + "i16vec2 addInvocationsAMD(i16vec2);" + "i16vec3 addInvocationsAMD(i16vec3);" + "i16vec4 addInvocationsAMD(i16vec4);" + + "uint16_t addInvocationsAMD(uint16_t);" + "u16vec2 addInvocationsAMD(u16vec2);" + "u16vec3 addInvocationsAMD(u16vec3);" + "u16vec4 addInvocationsAMD(u16vec4);" + + "float addInvocationsInclusiveScanAMD(float);" + "vec2 addInvocationsInclusiveScanAMD(vec2);" + "vec3 addInvocationsInclusiveScanAMD(vec3);" + "vec4 addInvocationsInclusiveScanAMD(vec4);" + + "int addInvocationsInclusiveScanAMD(int);" + "ivec2 addInvocationsInclusiveScanAMD(ivec2);" + "ivec3 addInvocationsInclusiveScanAMD(ivec3);" + "ivec4 addInvocationsInclusiveScanAMD(ivec4);" + + "uint addInvocationsInclusiveScanAMD(uint);" + "uvec2 addInvocationsInclusiveScanAMD(uvec2);" + "uvec3 addInvocationsInclusiveScanAMD(uvec3);" + "uvec4 addInvocationsInclusiveScanAMD(uvec4);" + + "double addInvocationsInclusiveScanAMD(double);" + "dvec2 addInvocationsInclusiveScanAMD(dvec2);" + "dvec3 addInvocationsInclusiveScanAMD(dvec3);" + "dvec4 addInvocationsInclusiveScanAMD(dvec4);" + + "int64_t addInvocationsInclusiveScanAMD(int64_t);" + "i64vec2 addInvocationsInclusiveScanAMD(i64vec2);" + "i64vec3 addInvocationsInclusiveScanAMD(i64vec3);" + "i64vec4 addInvocationsInclusiveScanAMD(i64vec4);" + + "uint64_t addInvocationsInclusiveScanAMD(uint64_t);" + "u64vec2 addInvocationsInclusiveScanAMD(u64vec2);" + "u64vec3 addInvocationsInclusiveScanAMD(u64vec3);" + "u64vec4 addInvocationsInclusiveScanAMD(u64vec4);" + + "float16_t addInvocationsInclusiveScanAMD(float16_t);" + "f16vec2 addInvocationsInclusiveScanAMD(f16vec2);" + "f16vec3 addInvocationsInclusiveScanAMD(f16vec3);" + "f16vec4 addInvocationsInclusiveScanAMD(f16vec4);" + + "int16_t addInvocationsInclusiveScanAMD(int16_t);" + "i16vec2 addInvocationsInclusiveScanAMD(i16vec2);" + "i16vec3 addInvocationsInclusiveScanAMD(i16vec3);" + "i16vec4 addInvocationsInclusiveScanAMD(i16vec4);" + + "uint16_t addInvocationsInclusiveScanAMD(uint16_t);" + "u16vec2 addInvocationsInclusiveScanAMD(u16vec2);" + "u16vec3 addInvocationsInclusiveScanAMD(u16vec3);" + "u16vec4 addInvocationsInclusiveScanAMD(u16vec4);" + + "float addInvocationsExclusiveScanAMD(float);" + "vec2 addInvocationsExclusiveScanAMD(vec2);" + "vec3 addInvocationsExclusiveScanAMD(vec3);" + "vec4 addInvocationsExclusiveScanAMD(vec4);" + + "int addInvocationsExclusiveScanAMD(int);" + "ivec2 addInvocationsExclusiveScanAMD(ivec2);" + "ivec3 addInvocationsExclusiveScanAMD(ivec3);" + "ivec4 addInvocationsExclusiveScanAMD(ivec4);" + + "uint addInvocationsExclusiveScanAMD(uint);" + "uvec2 addInvocationsExclusiveScanAMD(uvec2);" + "uvec3 addInvocationsExclusiveScanAMD(uvec3);" + "uvec4 addInvocationsExclusiveScanAMD(uvec4);" + + "double addInvocationsExclusiveScanAMD(double);" + "dvec2 addInvocationsExclusiveScanAMD(dvec2);" + "dvec3 addInvocationsExclusiveScanAMD(dvec3);" + "dvec4 addInvocationsExclusiveScanAMD(dvec4);" + + "int64_t addInvocationsExclusiveScanAMD(int64_t);" + "i64vec2 addInvocationsExclusiveScanAMD(i64vec2);" + "i64vec3 addInvocationsExclusiveScanAMD(i64vec3);" + "i64vec4 addInvocationsExclusiveScanAMD(i64vec4);" + + "uint64_t addInvocationsExclusiveScanAMD(uint64_t);" + "u64vec2 addInvocationsExclusiveScanAMD(u64vec2);" + "u64vec3 addInvocationsExclusiveScanAMD(u64vec3);" + "u64vec4 addInvocationsExclusiveScanAMD(u64vec4);" + + "float16_t addInvocationsExclusiveScanAMD(float16_t);" + "f16vec2 addInvocationsExclusiveScanAMD(f16vec2);" + "f16vec3 addInvocationsExclusiveScanAMD(f16vec3);" + "f16vec4 addInvocationsExclusiveScanAMD(f16vec4);" + + "int16_t addInvocationsExclusiveScanAMD(int16_t);" + "i16vec2 addInvocationsExclusiveScanAMD(i16vec2);" + "i16vec3 addInvocationsExclusiveScanAMD(i16vec3);" + "i16vec4 addInvocationsExclusiveScanAMD(i16vec4);" + + "uint16_t addInvocationsExclusiveScanAMD(uint16_t);" + "u16vec2 addInvocationsExclusiveScanAMD(u16vec2);" + "u16vec3 addInvocationsExclusiveScanAMD(u16vec3);" + "u16vec4 addInvocationsExclusiveScanAMD(u16vec4);" + + "float minInvocationsNonUniformAMD(float);" + "vec2 minInvocationsNonUniformAMD(vec2);" + "vec3 minInvocationsNonUniformAMD(vec3);" + "vec4 minInvocationsNonUniformAMD(vec4);" + + "int minInvocationsNonUniformAMD(int);" + "ivec2 minInvocationsNonUniformAMD(ivec2);" + "ivec3 minInvocationsNonUniformAMD(ivec3);" + "ivec4 minInvocationsNonUniformAMD(ivec4);" + + "uint minInvocationsNonUniformAMD(uint);" + "uvec2 minInvocationsNonUniformAMD(uvec2);" + "uvec3 minInvocationsNonUniformAMD(uvec3);" + "uvec4 minInvocationsNonUniformAMD(uvec4);" + + "double minInvocationsNonUniformAMD(double);" + "dvec2 minInvocationsNonUniformAMD(dvec2);" + "dvec3 minInvocationsNonUniformAMD(dvec3);" + "dvec4 minInvocationsNonUniformAMD(dvec4);" + + "int64_t minInvocationsNonUniformAMD(int64_t);" + "i64vec2 minInvocationsNonUniformAMD(i64vec2);" + "i64vec3 minInvocationsNonUniformAMD(i64vec3);" + "i64vec4 minInvocationsNonUniformAMD(i64vec4);" + + "uint64_t minInvocationsNonUniformAMD(uint64_t);" + "u64vec2 minInvocationsNonUniformAMD(u64vec2);" + "u64vec3 minInvocationsNonUniformAMD(u64vec3);" + "u64vec4 minInvocationsNonUniformAMD(u64vec4);" + + "float16_t minInvocationsNonUniformAMD(float16_t);" + "f16vec2 minInvocationsNonUniformAMD(f16vec2);" + "f16vec3 minInvocationsNonUniformAMD(f16vec3);" + "f16vec4 minInvocationsNonUniformAMD(f16vec4);" + + "int16_t minInvocationsNonUniformAMD(int16_t);" + "i16vec2 minInvocationsNonUniformAMD(i16vec2);" + "i16vec3 minInvocationsNonUniformAMD(i16vec3);" + "i16vec4 minInvocationsNonUniformAMD(i16vec4);" + + "uint16_t minInvocationsNonUniformAMD(uint16_t);" + "u16vec2 minInvocationsNonUniformAMD(u16vec2);" + "u16vec3 minInvocationsNonUniformAMD(u16vec3);" + "u16vec4 minInvocationsNonUniformAMD(u16vec4);" + + "float minInvocationsInclusiveScanNonUniformAMD(float);" + "vec2 minInvocationsInclusiveScanNonUniformAMD(vec2);" + "vec3 minInvocationsInclusiveScanNonUniformAMD(vec3);" + "vec4 minInvocationsInclusiveScanNonUniformAMD(vec4);" + + "int minInvocationsInclusiveScanNonUniformAMD(int);" + "ivec2 minInvocationsInclusiveScanNonUniformAMD(ivec2);" + "ivec3 minInvocationsInclusiveScanNonUniformAMD(ivec3);" + "ivec4 minInvocationsInclusiveScanNonUniformAMD(ivec4);" + + "uint minInvocationsInclusiveScanNonUniformAMD(uint);" + "uvec2 minInvocationsInclusiveScanNonUniformAMD(uvec2);" + "uvec3 minInvocationsInclusiveScanNonUniformAMD(uvec3);" + "uvec4 minInvocationsInclusiveScanNonUniformAMD(uvec4);" + + "double minInvocationsInclusiveScanNonUniformAMD(double);" + "dvec2 minInvocationsInclusiveScanNonUniformAMD(dvec2);" + "dvec3 minInvocationsInclusiveScanNonUniformAMD(dvec3);" + "dvec4 minInvocationsInclusiveScanNonUniformAMD(dvec4);" + + "int64_t minInvocationsInclusiveScanNonUniformAMD(int64_t);" + "i64vec2 minInvocationsInclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 minInvocationsInclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 minInvocationsInclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t minInvocationsInclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 minInvocationsInclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 minInvocationsInclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 minInvocationsInclusiveScanNonUniformAMD(u64vec4);" + + "float16_t minInvocationsInclusiveScanNonUniformAMD(float16_t);" + "f16vec2 minInvocationsInclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 minInvocationsInclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 minInvocationsInclusiveScanNonUniformAMD(f16vec4);" + + "int16_t minInvocationsInclusiveScanNonUniformAMD(int16_t);" + "i16vec2 minInvocationsInclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 minInvocationsInclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 minInvocationsInclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t minInvocationsInclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 minInvocationsInclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 minInvocationsInclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 minInvocationsInclusiveScanNonUniformAMD(u16vec4);" + + "float minInvocationsExclusiveScanNonUniformAMD(float);" + "vec2 minInvocationsExclusiveScanNonUniformAMD(vec2);" + "vec3 minInvocationsExclusiveScanNonUniformAMD(vec3);" + "vec4 minInvocationsExclusiveScanNonUniformAMD(vec4);" + + "int minInvocationsExclusiveScanNonUniformAMD(int);" + "ivec2 minInvocationsExclusiveScanNonUniformAMD(ivec2);" + "ivec3 minInvocationsExclusiveScanNonUniformAMD(ivec3);" + "ivec4 minInvocationsExclusiveScanNonUniformAMD(ivec4);" + + "uint minInvocationsExclusiveScanNonUniformAMD(uint);" + "uvec2 minInvocationsExclusiveScanNonUniformAMD(uvec2);" + "uvec3 minInvocationsExclusiveScanNonUniformAMD(uvec3);" + "uvec4 minInvocationsExclusiveScanNonUniformAMD(uvec4);" + + "double minInvocationsExclusiveScanNonUniformAMD(double);" + "dvec2 minInvocationsExclusiveScanNonUniformAMD(dvec2);" + "dvec3 minInvocationsExclusiveScanNonUniformAMD(dvec3);" + "dvec4 minInvocationsExclusiveScanNonUniformAMD(dvec4);" + + "int64_t minInvocationsExclusiveScanNonUniformAMD(int64_t);" + "i64vec2 minInvocationsExclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 minInvocationsExclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 minInvocationsExclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t minInvocationsExclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 minInvocationsExclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 minInvocationsExclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 minInvocationsExclusiveScanNonUniformAMD(u64vec4);" + + "float16_t minInvocationsExclusiveScanNonUniformAMD(float16_t);" + "f16vec2 minInvocationsExclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 minInvocationsExclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 minInvocationsExclusiveScanNonUniformAMD(f16vec4);" + + "int16_t minInvocationsExclusiveScanNonUniformAMD(int16_t);" + "i16vec2 minInvocationsExclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 minInvocationsExclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 minInvocationsExclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t minInvocationsExclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 minInvocationsExclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 minInvocationsExclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 minInvocationsExclusiveScanNonUniformAMD(u16vec4);" + + "float maxInvocationsNonUniformAMD(float);" + "vec2 maxInvocationsNonUniformAMD(vec2);" + "vec3 maxInvocationsNonUniformAMD(vec3);" + "vec4 maxInvocationsNonUniformAMD(vec4);" + + "int maxInvocationsNonUniformAMD(int);" + "ivec2 maxInvocationsNonUniformAMD(ivec2);" + "ivec3 maxInvocationsNonUniformAMD(ivec3);" + "ivec4 maxInvocationsNonUniformAMD(ivec4);" + + "uint maxInvocationsNonUniformAMD(uint);" + "uvec2 maxInvocationsNonUniformAMD(uvec2);" + "uvec3 maxInvocationsNonUniformAMD(uvec3);" + "uvec4 maxInvocationsNonUniformAMD(uvec4);" + + "double maxInvocationsNonUniformAMD(double);" + "dvec2 maxInvocationsNonUniformAMD(dvec2);" + "dvec3 maxInvocationsNonUniformAMD(dvec3);" + "dvec4 maxInvocationsNonUniformAMD(dvec4);" + + "int64_t maxInvocationsNonUniformAMD(int64_t);" + "i64vec2 maxInvocationsNonUniformAMD(i64vec2);" + "i64vec3 maxInvocationsNonUniformAMD(i64vec3);" + "i64vec4 maxInvocationsNonUniformAMD(i64vec4);" + + "uint64_t maxInvocationsNonUniformAMD(uint64_t);" + "u64vec2 maxInvocationsNonUniformAMD(u64vec2);" + "u64vec3 maxInvocationsNonUniformAMD(u64vec3);" + "u64vec4 maxInvocationsNonUniformAMD(u64vec4);" + + "float16_t maxInvocationsNonUniformAMD(float16_t);" + "f16vec2 maxInvocationsNonUniformAMD(f16vec2);" + "f16vec3 maxInvocationsNonUniformAMD(f16vec3);" + "f16vec4 maxInvocationsNonUniformAMD(f16vec4);" + + "int16_t maxInvocationsNonUniformAMD(int16_t);" + "i16vec2 maxInvocationsNonUniformAMD(i16vec2);" + "i16vec3 maxInvocationsNonUniformAMD(i16vec3);" + "i16vec4 maxInvocationsNonUniformAMD(i16vec4);" + + "uint16_t maxInvocationsNonUniformAMD(uint16_t);" + "u16vec2 maxInvocationsNonUniformAMD(u16vec2);" + "u16vec3 maxInvocationsNonUniformAMD(u16vec3);" + "u16vec4 maxInvocationsNonUniformAMD(u16vec4);" + + "float maxInvocationsInclusiveScanNonUniformAMD(float);" + "vec2 maxInvocationsInclusiveScanNonUniformAMD(vec2);" + "vec3 maxInvocationsInclusiveScanNonUniformAMD(vec3);" + "vec4 maxInvocationsInclusiveScanNonUniformAMD(vec4);" + + "int maxInvocationsInclusiveScanNonUniformAMD(int);" + "ivec2 maxInvocationsInclusiveScanNonUniformAMD(ivec2);" + "ivec3 maxInvocationsInclusiveScanNonUniformAMD(ivec3);" + "ivec4 maxInvocationsInclusiveScanNonUniformAMD(ivec4);" + + "uint maxInvocationsInclusiveScanNonUniformAMD(uint);" + "uvec2 maxInvocationsInclusiveScanNonUniformAMD(uvec2);" + "uvec3 maxInvocationsInclusiveScanNonUniformAMD(uvec3);" + "uvec4 maxInvocationsInclusiveScanNonUniformAMD(uvec4);" + + "double maxInvocationsInclusiveScanNonUniformAMD(double);" + "dvec2 maxInvocationsInclusiveScanNonUniformAMD(dvec2);" + "dvec3 maxInvocationsInclusiveScanNonUniformAMD(dvec3);" + "dvec4 maxInvocationsInclusiveScanNonUniformAMD(dvec4);" + + "int64_t maxInvocationsInclusiveScanNonUniformAMD(int64_t);" + "i64vec2 maxInvocationsInclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 maxInvocationsInclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 maxInvocationsInclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t maxInvocationsInclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 maxInvocationsInclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 maxInvocationsInclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 maxInvocationsInclusiveScanNonUniformAMD(u64vec4);" + + "float16_t maxInvocationsInclusiveScanNonUniformAMD(float16_t);" + "f16vec2 maxInvocationsInclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 maxInvocationsInclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 maxInvocationsInclusiveScanNonUniformAMD(f16vec4);" + + "int16_t maxInvocationsInclusiveScanNonUniformAMD(int16_t);" + "i16vec2 maxInvocationsInclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 maxInvocationsInclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 maxInvocationsInclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t maxInvocationsInclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 maxInvocationsInclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 maxInvocationsInclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 maxInvocationsInclusiveScanNonUniformAMD(u16vec4);" + + "float maxInvocationsExclusiveScanNonUniformAMD(float);" + "vec2 maxInvocationsExclusiveScanNonUniformAMD(vec2);" + "vec3 maxInvocationsExclusiveScanNonUniformAMD(vec3);" + "vec4 maxInvocationsExclusiveScanNonUniformAMD(vec4);" + + "int maxInvocationsExclusiveScanNonUniformAMD(int);" + "ivec2 maxInvocationsExclusiveScanNonUniformAMD(ivec2);" + "ivec3 maxInvocationsExclusiveScanNonUniformAMD(ivec3);" + "ivec4 maxInvocationsExclusiveScanNonUniformAMD(ivec4);" + + "uint maxInvocationsExclusiveScanNonUniformAMD(uint);" + "uvec2 maxInvocationsExclusiveScanNonUniformAMD(uvec2);" + "uvec3 maxInvocationsExclusiveScanNonUniformAMD(uvec3);" + "uvec4 maxInvocationsExclusiveScanNonUniformAMD(uvec4);" + + "double maxInvocationsExclusiveScanNonUniformAMD(double);" + "dvec2 maxInvocationsExclusiveScanNonUniformAMD(dvec2);" + "dvec3 maxInvocationsExclusiveScanNonUniformAMD(dvec3);" + "dvec4 maxInvocationsExclusiveScanNonUniformAMD(dvec4);" + + "int64_t maxInvocationsExclusiveScanNonUniformAMD(int64_t);" + "i64vec2 maxInvocationsExclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 maxInvocationsExclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 maxInvocationsExclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t maxInvocationsExclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 maxInvocationsExclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 maxInvocationsExclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 maxInvocationsExclusiveScanNonUniformAMD(u64vec4);" + + "float16_t maxInvocationsExclusiveScanNonUniformAMD(float16_t);" + "f16vec2 maxInvocationsExclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 maxInvocationsExclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 maxInvocationsExclusiveScanNonUniformAMD(f16vec4);" + + "int16_t maxInvocationsExclusiveScanNonUniformAMD(int16_t);" + "i16vec2 maxInvocationsExclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 maxInvocationsExclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 maxInvocationsExclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t maxInvocationsExclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 maxInvocationsExclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 maxInvocationsExclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 maxInvocationsExclusiveScanNonUniformAMD(u16vec4);" + + "float addInvocationsNonUniformAMD(float);" + "vec2 addInvocationsNonUniformAMD(vec2);" + "vec3 addInvocationsNonUniformAMD(vec3);" + "vec4 addInvocationsNonUniformAMD(vec4);" + + "int addInvocationsNonUniformAMD(int);" + "ivec2 addInvocationsNonUniformAMD(ivec2);" + "ivec3 addInvocationsNonUniformAMD(ivec3);" + "ivec4 addInvocationsNonUniformAMD(ivec4);" + + "uint addInvocationsNonUniformAMD(uint);" + "uvec2 addInvocationsNonUniformAMD(uvec2);" + "uvec3 addInvocationsNonUniformAMD(uvec3);" + "uvec4 addInvocationsNonUniformAMD(uvec4);" + + "double addInvocationsNonUniformAMD(double);" + "dvec2 addInvocationsNonUniformAMD(dvec2);" + "dvec3 addInvocationsNonUniformAMD(dvec3);" + "dvec4 addInvocationsNonUniformAMD(dvec4);" + + "int64_t addInvocationsNonUniformAMD(int64_t);" + "i64vec2 addInvocationsNonUniformAMD(i64vec2);" + "i64vec3 addInvocationsNonUniformAMD(i64vec3);" + "i64vec4 addInvocationsNonUniformAMD(i64vec4);" + + "uint64_t addInvocationsNonUniformAMD(uint64_t);" + "u64vec2 addInvocationsNonUniformAMD(u64vec2);" + "u64vec3 addInvocationsNonUniformAMD(u64vec3);" + "u64vec4 addInvocationsNonUniformAMD(u64vec4);" + + "float16_t addInvocationsNonUniformAMD(float16_t);" + "f16vec2 addInvocationsNonUniformAMD(f16vec2);" + "f16vec3 addInvocationsNonUniformAMD(f16vec3);" + "f16vec4 addInvocationsNonUniformAMD(f16vec4);" + + "int16_t addInvocationsNonUniformAMD(int16_t);" + "i16vec2 addInvocationsNonUniformAMD(i16vec2);" + "i16vec3 addInvocationsNonUniformAMD(i16vec3);" + "i16vec4 addInvocationsNonUniformAMD(i16vec4);" + + "uint16_t addInvocationsNonUniformAMD(uint16_t);" + "u16vec2 addInvocationsNonUniformAMD(u16vec2);" + "u16vec3 addInvocationsNonUniformAMD(u16vec3);" + "u16vec4 addInvocationsNonUniformAMD(u16vec4);" + + "float addInvocationsInclusiveScanNonUniformAMD(float);" + "vec2 addInvocationsInclusiveScanNonUniformAMD(vec2);" + "vec3 addInvocationsInclusiveScanNonUniformAMD(vec3);" + "vec4 addInvocationsInclusiveScanNonUniformAMD(vec4);" + + "int addInvocationsInclusiveScanNonUniformAMD(int);" + "ivec2 addInvocationsInclusiveScanNonUniformAMD(ivec2);" + "ivec3 addInvocationsInclusiveScanNonUniformAMD(ivec3);" + "ivec4 addInvocationsInclusiveScanNonUniformAMD(ivec4);" + + "uint addInvocationsInclusiveScanNonUniformAMD(uint);" + "uvec2 addInvocationsInclusiveScanNonUniformAMD(uvec2);" + "uvec3 addInvocationsInclusiveScanNonUniformAMD(uvec3);" + "uvec4 addInvocationsInclusiveScanNonUniformAMD(uvec4);" + + "double addInvocationsInclusiveScanNonUniformAMD(double);" + "dvec2 addInvocationsInclusiveScanNonUniformAMD(dvec2);" + "dvec3 addInvocationsInclusiveScanNonUniformAMD(dvec3);" + "dvec4 addInvocationsInclusiveScanNonUniformAMD(dvec4);" + + "int64_t addInvocationsInclusiveScanNonUniformAMD(int64_t);" + "i64vec2 addInvocationsInclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 addInvocationsInclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 addInvocationsInclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t addInvocationsInclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 addInvocationsInclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 addInvocationsInclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 addInvocationsInclusiveScanNonUniformAMD(u64vec4);" + + "float16_t addInvocationsInclusiveScanNonUniformAMD(float16_t);" + "f16vec2 addInvocationsInclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 addInvocationsInclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 addInvocationsInclusiveScanNonUniformAMD(f16vec4);" + + "int16_t addInvocationsInclusiveScanNonUniformAMD(int16_t);" + "i16vec2 addInvocationsInclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 addInvocationsInclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 addInvocationsInclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t addInvocationsInclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 addInvocationsInclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 addInvocationsInclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 addInvocationsInclusiveScanNonUniformAMD(u16vec4);" + + "float addInvocationsExclusiveScanNonUniformAMD(float);" + "vec2 addInvocationsExclusiveScanNonUniformAMD(vec2);" + "vec3 addInvocationsExclusiveScanNonUniformAMD(vec3);" + "vec4 addInvocationsExclusiveScanNonUniformAMD(vec4);" + + "int addInvocationsExclusiveScanNonUniformAMD(int);" + "ivec2 addInvocationsExclusiveScanNonUniformAMD(ivec2);" + "ivec3 addInvocationsExclusiveScanNonUniformAMD(ivec3);" + "ivec4 addInvocationsExclusiveScanNonUniformAMD(ivec4);" + + "uint addInvocationsExclusiveScanNonUniformAMD(uint);" + "uvec2 addInvocationsExclusiveScanNonUniformAMD(uvec2);" + "uvec3 addInvocationsExclusiveScanNonUniformAMD(uvec3);" + "uvec4 addInvocationsExclusiveScanNonUniformAMD(uvec4);" + + "double addInvocationsExclusiveScanNonUniformAMD(double);" + "dvec2 addInvocationsExclusiveScanNonUniformAMD(dvec2);" + "dvec3 addInvocationsExclusiveScanNonUniformAMD(dvec3);" + "dvec4 addInvocationsExclusiveScanNonUniformAMD(dvec4);" + + "int64_t addInvocationsExclusiveScanNonUniformAMD(int64_t);" + "i64vec2 addInvocationsExclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 addInvocationsExclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 addInvocationsExclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t addInvocationsExclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 addInvocationsExclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 addInvocationsExclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 addInvocationsExclusiveScanNonUniformAMD(u64vec4);" + + "float16_t addInvocationsExclusiveScanNonUniformAMD(float16_t);" + "f16vec2 addInvocationsExclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 addInvocationsExclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 addInvocationsExclusiveScanNonUniformAMD(f16vec4);" + + "int16_t addInvocationsExclusiveScanNonUniformAMD(int16_t);" + "i16vec2 addInvocationsExclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 addInvocationsExclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 addInvocationsExclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t addInvocationsExclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 addInvocationsExclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 addInvocationsExclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 addInvocationsExclusiveScanNonUniformAMD(u16vec4);" + + "float swizzleInvocationsAMD(float, uvec4);" + "vec2 swizzleInvocationsAMD(vec2, uvec4);" + "vec3 swizzleInvocationsAMD(vec3, uvec4);" + "vec4 swizzleInvocationsAMD(vec4, uvec4);" + + "int swizzleInvocationsAMD(int, uvec4);" + "ivec2 swizzleInvocationsAMD(ivec2, uvec4);" + "ivec3 swizzleInvocationsAMD(ivec3, uvec4);" + "ivec4 swizzleInvocationsAMD(ivec4, uvec4);" + + "uint swizzleInvocationsAMD(uint, uvec4);" + "uvec2 swizzleInvocationsAMD(uvec2, uvec4);" + "uvec3 swizzleInvocationsAMD(uvec3, uvec4);" + "uvec4 swizzleInvocationsAMD(uvec4, uvec4);" + + "float swizzleInvocationsMaskedAMD(float, uvec3);" + "vec2 swizzleInvocationsMaskedAMD(vec2, uvec3);" + "vec3 swizzleInvocationsMaskedAMD(vec3, uvec3);" + "vec4 swizzleInvocationsMaskedAMD(vec4, uvec3);" + + "int swizzleInvocationsMaskedAMD(int, uvec3);" + "ivec2 swizzleInvocationsMaskedAMD(ivec2, uvec3);" + "ivec3 swizzleInvocationsMaskedAMD(ivec3, uvec3);" + "ivec4 swizzleInvocationsMaskedAMD(ivec4, uvec3);" + + "uint swizzleInvocationsMaskedAMD(uint, uvec3);" + "uvec2 swizzleInvocationsMaskedAMD(uvec2, uvec3);" + "uvec3 swizzleInvocationsMaskedAMD(uvec3, uvec3);" + "uvec4 swizzleInvocationsMaskedAMD(uvec4, uvec3);" + + "float writeInvocationAMD(float, float, uint);" + "vec2 writeInvocationAMD(vec2, vec2, uint);" + "vec3 writeInvocationAMD(vec3, vec3, uint);" + "vec4 writeInvocationAMD(vec4, vec4, uint);" + + "int writeInvocationAMD(int, int, uint);" + "ivec2 writeInvocationAMD(ivec2, ivec2, uint);" + "ivec3 writeInvocationAMD(ivec3, ivec3, uint);" + "ivec4 writeInvocationAMD(ivec4, ivec4, uint);" + + "uint writeInvocationAMD(uint, uint, uint);" + "uvec2 writeInvocationAMD(uvec2, uvec2, uint);" + "uvec3 writeInvocationAMD(uvec3, uvec3, uint);" + "uvec4 writeInvocationAMD(uvec4, uvec4, uint);" + + "uint mbcntAMD(uint64_t);" + + "\n"); + } + + // GL_AMD_gcn_shader + if (profile != EEsProfile && version >= 440) { + commonBuiltins.append( + "float cubeFaceIndexAMD(vec3);" + "vec2 cubeFaceCoordAMD(vec3);" + "uint64_t timeAMD();" + + "in int gl_SIMDGroupSizeAMD;" + "\n"); + } + + // GL_AMD_shader_fragment_mask + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "uint fragmentMaskFetchAMD(sampler2DMS, ivec2);" + "uint fragmentMaskFetchAMD(isampler2DMS, ivec2);" + "uint fragmentMaskFetchAMD(usampler2DMS, ivec2);" + + "uint fragmentMaskFetchAMD(sampler2DMSArray, ivec3);" + "uint fragmentMaskFetchAMD(isampler2DMSArray, ivec3);" + "uint fragmentMaskFetchAMD(usampler2DMSArray, ivec3);" + + "vec4 fragmentFetchAMD(sampler2DMS, ivec2, uint);" + "ivec4 fragmentFetchAMD(isampler2DMS, ivec2, uint);" + "uvec4 fragmentFetchAMD(usampler2DMS, ivec2, uint);" + + "vec4 fragmentFetchAMD(sampler2DMSArray, ivec3, uint);" + "ivec4 fragmentFetchAMD(isampler2DMSArray, ivec3, uint);" + "uvec4 fragmentFetchAMD(usampler2DMSArray, ivec3, uint);" + + "\n"); + } + + if ((profile != EEsProfile && version >= 130) || + (profile == EEsProfile && version >= 300)) { + commonBuiltins.append( + "uint countLeadingZeros(uint);" + "uvec2 countLeadingZeros(uvec2);" + "uvec3 countLeadingZeros(uvec3);" + "uvec4 countLeadingZeros(uvec4);" + + "uint countTrailingZeros(uint);" + "uvec2 countTrailingZeros(uvec2);" + "uvec3 countTrailingZeros(uvec3);" + "uvec4 countTrailingZeros(uvec4);" + + "uint absoluteDifference(int, int);" + "uvec2 absoluteDifference(ivec2, ivec2);" + "uvec3 absoluteDifference(ivec3, ivec3);" + "uvec4 absoluteDifference(ivec4, ivec4);" + + "uint16_t absoluteDifference(int16_t, int16_t);" + "u16vec2 absoluteDifference(i16vec2, i16vec2);" + "u16vec3 absoluteDifference(i16vec3, i16vec3);" + "u16vec4 absoluteDifference(i16vec4, i16vec4);" + + "uint64_t absoluteDifference(int64_t, int64_t);" + "u64vec2 absoluteDifference(i64vec2, i64vec2);" + "u64vec3 absoluteDifference(i64vec3, i64vec3);" + "u64vec4 absoluteDifference(i64vec4, i64vec4);" + + "uint absoluteDifference(uint, uint);" + "uvec2 absoluteDifference(uvec2, uvec2);" + "uvec3 absoluteDifference(uvec3, uvec3);" + "uvec4 absoluteDifference(uvec4, uvec4);" + + "uint16_t absoluteDifference(uint16_t, uint16_t);" + "u16vec2 absoluteDifference(u16vec2, u16vec2);" + "u16vec3 absoluteDifference(u16vec3, u16vec3);" + "u16vec4 absoluteDifference(u16vec4, u16vec4);" + + "uint64_t absoluteDifference(uint64_t, uint64_t);" + "u64vec2 absoluteDifference(u64vec2, u64vec2);" + "u64vec3 absoluteDifference(u64vec3, u64vec3);" + "u64vec4 absoluteDifference(u64vec4, u64vec4);" + + "int addSaturate(int, int);" + "ivec2 addSaturate(ivec2, ivec2);" + "ivec3 addSaturate(ivec3, ivec3);" + "ivec4 addSaturate(ivec4, ivec4);" + + "int16_t addSaturate(int16_t, int16_t);" + "i16vec2 addSaturate(i16vec2, i16vec2);" + "i16vec3 addSaturate(i16vec3, i16vec3);" + "i16vec4 addSaturate(i16vec4, i16vec4);" + + "int64_t addSaturate(int64_t, int64_t);" + "i64vec2 addSaturate(i64vec2, i64vec2);" + "i64vec3 addSaturate(i64vec3, i64vec3);" + "i64vec4 addSaturate(i64vec4, i64vec4);" + + "uint addSaturate(uint, uint);" + "uvec2 addSaturate(uvec2, uvec2);" + "uvec3 addSaturate(uvec3, uvec3);" + "uvec4 addSaturate(uvec4, uvec4);" + + "uint16_t addSaturate(uint16_t, uint16_t);" + "u16vec2 addSaturate(u16vec2, u16vec2);" + "u16vec3 addSaturate(u16vec3, u16vec3);" + "u16vec4 addSaturate(u16vec4, u16vec4);" + + "uint64_t addSaturate(uint64_t, uint64_t);" + "u64vec2 addSaturate(u64vec2, u64vec2);" + "u64vec3 addSaturate(u64vec3, u64vec3);" + "u64vec4 addSaturate(u64vec4, u64vec4);" + + "int subtractSaturate(int, int);" + "ivec2 subtractSaturate(ivec2, ivec2);" + "ivec3 subtractSaturate(ivec3, ivec3);" + "ivec4 subtractSaturate(ivec4, ivec4);" + + "int16_t subtractSaturate(int16_t, int16_t);" + "i16vec2 subtractSaturate(i16vec2, i16vec2);" + "i16vec3 subtractSaturate(i16vec3, i16vec3);" + "i16vec4 subtractSaturate(i16vec4, i16vec4);" + + "int64_t subtractSaturate(int64_t, int64_t);" + "i64vec2 subtractSaturate(i64vec2, i64vec2);" + "i64vec3 subtractSaturate(i64vec3, i64vec3);" + "i64vec4 subtractSaturate(i64vec4, i64vec4);" + + "uint subtractSaturate(uint, uint);" + "uvec2 subtractSaturate(uvec2, uvec2);" + "uvec3 subtractSaturate(uvec3, uvec3);" + "uvec4 subtractSaturate(uvec4, uvec4);" + + "uint16_t subtractSaturate(uint16_t, uint16_t);" + "u16vec2 subtractSaturate(u16vec2, u16vec2);" + "u16vec3 subtractSaturate(u16vec3, u16vec3);" + "u16vec4 subtractSaturate(u16vec4, u16vec4);" + + "uint64_t subtractSaturate(uint64_t, uint64_t);" + "u64vec2 subtractSaturate(u64vec2, u64vec2);" + "u64vec3 subtractSaturate(u64vec3, u64vec3);" + "u64vec4 subtractSaturate(u64vec4, u64vec4);" + + "int average(int, int);" + "ivec2 average(ivec2, ivec2);" + "ivec3 average(ivec3, ivec3);" + "ivec4 average(ivec4, ivec4);" + + "int16_t average(int16_t, int16_t);" + "i16vec2 average(i16vec2, i16vec2);" + "i16vec3 average(i16vec3, i16vec3);" + "i16vec4 average(i16vec4, i16vec4);" + + "int64_t average(int64_t, int64_t);" + "i64vec2 average(i64vec2, i64vec2);" + "i64vec3 average(i64vec3, i64vec3);" + "i64vec4 average(i64vec4, i64vec4);" + + "uint average(uint, uint);" + "uvec2 average(uvec2, uvec2);" + "uvec3 average(uvec3, uvec3);" + "uvec4 average(uvec4, uvec4);" + + "uint16_t average(uint16_t, uint16_t);" + "u16vec2 average(u16vec2, u16vec2);" + "u16vec3 average(u16vec3, u16vec3);" + "u16vec4 average(u16vec4, u16vec4);" + + "uint64_t average(uint64_t, uint64_t);" + "u64vec2 average(u64vec2, u64vec2);" + "u64vec3 average(u64vec3, u64vec3);" + "u64vec4 average(u64vec4, u64vec4);" + + "int averageRounded(int, int);" + "ivec2 averageRounded(ivec2, ivec2);" + "ivec3 averageRounded(ivec3, ivec3);" + "ivec4 averageRounded(ivec4, ivec4);" + + "int16_t averageRounded(int16_t, int16_t);" + "i16vec2 averageRounded(i16vec2, i16vec2);" + "i16vec3 averageRounded(i16vec3, i16vec3);" + "i16vec4 averageRounded(i16vec4, i16vec4);" + + "int64_t averageRounded(int64_t, int64_t);" + "i64vec2 averageRounded(i64vec2, i64vec2);" + "i64vec3 averageRounded(i64vec3, i64vec3);" + "i64vec4 averageRounded(i64vec4, i64vec4);" + + "uint averageRounded(uint, uint);" + "uvec2 averageRounded(uvec2, uvec2);" + "uvec3 averageRounded(uvec3, uvec3);" + "uvec4 averageRounded(uvec4, uvec4);" + + "uint16_t averageRounded(uint16_t, uint16_t);" + "u16vec2 averageRounded(u16vec2, u16vec2);" + "u16vec3 averageRounded(u16vec3, u16vec3);" + "u16vec4 averageRounded(u16vec4, u16vec4);" + + "uint64_t averageRounded(uint64_t, uint64_t);" + "u64vec2 averageRounded(u64vec2, u64vec2);" + "u64vec3 averageRounded(u64vec3, u64vec3);" + "u64vec4 averageRounded(u64vec4, u64vec4);" + + "int multiply32x16(int, int);" + "ivec2 multiply32x16(ivec2, ivec2);" + "ivec3 multiply32x16(ivec3, ivec3);" + "ivec4 multiply32x16(ivec4, ivec4);" + + "uint multiply32x16(uint, uint);" + "uvec2 multiply32x16(uvec2, uvec2);" + "uvec3 multiply32x16(uvec3, uvec3);" + "uvec4 multiply32x16(uvec4, uvec4);" + "\n"); + } + + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) { + commonBuiltins.append( + "struct gl_TextureFootprint2DNV {" + "uvec2 anchor;" + "uvec2 offset;" + "uvec2 mask;" + "uint lod;" + "uint granularity;" + "};" + + "struct gl_TextureFootprint3DNV {" + "uvec3 anchor;" + "uvec3 offset;" + "uvec2 mask;" + "uint lod;" + "uint granularity;" + "};" + "bool textureFootprintNV(sampler2D, vec2, int, bool, out gl_TextureFootprint2DNV);" + "bool textureFootprintNV(sampler3D, vec3, int, bool, out gl_TextureFootprint3DNV);" + "bool textureFootprintNV(sampler2D, vec2, int, bool, out gl_TextureFootprint2DNV, float);" + "bool textureFootprintNV(sampler3D, vec3, int, bool, out gl_TextureFootprint3DNV, float);" + "bool textureFootprintClampNV(sampler2D, vec2, float, int, bool, out gl_TextureFootprint2DNV);" + "bool textureFootprintClampNV(sampler3D, vec3, float, int, bool, out gl_TextureFootprint3DNV);" + "bool textureFootprintClampNV(sampler2D, vec2, float, int, bool, out gl_TextureFootprint2DNV, float);" + "bool textureFootprintClampNV(sampler3D, vec3, float, int, bool, out gl_TextureFootprint3DNV, float);" + "bool textureFootprintLodNV(sampler2D, vec2, float, int, bool, out gl_TextureFootprint2DNV);" + "bool textureFootprintLodNV(sampler3D, vec3, float, int, bool, out gl_TextureFootprint3DNV);" + "bool textureFootprintGradNV(sampler2D, vec2, vec2, vec2, int, bool, out gl_TextureFootprint2DNV);" + "bool textureFootprintGradClampNV(sampler2D, vec2, vec2, vec2, float, int, bool, out gl_TextureFootprint2DNV);" + "\n"); + } + + if ((profile == EEsProfile && version >= 300 && version < 310) || + (profile != EEsProfile && version >= 150 && version < 450)) { // GL_EXT_shader_integer_mix + commonBuiltins.append("int mix(int, int, bool);" + "ivec2 mix(ivec2, ivec2, bvec2);" + "ivec3 mix(ivec3, ivec3, bvec3);" + "ivec4 mix(ivec4, ivec4, bvec4);" + "uint mix(uint, uint, bool );" + "uvec2 mix(uvec2, uvec2, bvec2);" + "uvec3 mix(uvec3, uvec3, bvec3);" + "uvec4 mix(uvec4, uvec4, bvec4);" + "bool mix(bool, bool, bool );" + "bvec2 mix(bvec2, bvec2, bvec2);" + "bvec3 mix(bvec3, bvec3, bvec3);" + "bvec4 mix(bvec4, bvec4, bvec4);" + + "\n"); + } + + // GL_AMD_gpu_shader_half_float/Explicit types + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "float16_t radians(float16_t);" + "f16vec2 radians(f16vec2);" + "f16vec3 radians(f16vec3);" + "f16vec4 radians(f16vec4);" + + "float16_t degrees(float16_t);" + "f16vec2 degrees(f16vec2);" + "f16vec3 degrees(f16vec3);" + "f16vec4 degrees(f16vec4);" + + "float16_t sin(float16_t);" + "f16vec2 sin(f16vec2);" + "f16vec3 sin(f16vec3);" + "f16vec4 sin(f16vec4);" + + "float16_t cos(float16_t);" + "f16vec2 cos(f16vec2);" + "f16vec3 cos(f16vec3);" + "f16vec4 cos(f16vec4);" + + "float16_t tan(float16_t);" + "f16vec2 tan(f16vec2);" + "f16vec3 tan(f16vec3);" + "f16vec4 tan(f16vec4);" + + "float16_t asin(float16_t);" + "f16vec2 asin(f16vec2);" + "f16vec3 asin(f16vec3);" + "f16vec4 asin(f16vec4);" + + "float16_t acos(float16_t);" + "f16vec2 acos(f16vec2);" + "f16vec3 acos(f16vec3);" + "f16vec4 acos(f16vec4);" + + "float16_t atan(float16_t, float16_t);" + "f16vec2 atan(f16vec2, f16vec2);" + "f16vec3 atan(f16vec3, f16vec3);" + "f16vec4 atan(f16vec4, f16vec4);" + + "float16_t atan(float16_t);" + "f16vec2 atan(f16vec2);" + "f16vec3 atan(f16vec3);" + "f16vec4 atan(f16vec4);" + + "float16_t sinh(float16_t);" + "f16vec2 sinh(f16vec2);" + "f16vec3 sinh(f16vec3);" + "f16vec4 sinh(f16vec4);" + + "float16_t cosh(float16_t);" + "f16vec2 cosh(f16vec2);" + "f16vec3 cosh(f16vec3);" + "f16vec4 cosh(f16vec4);" + + "float16_t tanh(float16_t);" + "f16vec2 tanh(f16vec2);" + "f16vec3 tanh(f16vec3);" + "f16vec4 tanh(f16vec4);" + + "float16_t asinh(float16_t);" + "f16vec2 asinh(f16vec2);" + "f16vec3 asinh(f16vec3);" + "f16vec4 asinh(f16vec4);" + + "float16_t acosh(float16_t);" + "f16vec2 acosh(f16vec2);" + "f16vec3 acosh(f16vec3);" + "f16vec4 acosh(f16vec4);" + + "float16_t atanh(float16_t);" + "f16vec2 atanh(f16vec2);" + "f16vec3 atanh(f16vec3);" + "f16vec4 atanh(f16vec4);" + + "float16_t pow(float16_t, float16_t);" + "f16vec2 pow(f16vec2, f16vec2);" + "f16vec3 pow(f16vec3, f16vec3);" + "f16vec4 pow(f16vec4, f16vec4);" + + "float16_t exp(float16_t);" + "f16vec2 exp(f16vec2);" + "f16vec3 exp(f16vec3);" + "f16vec4 exp(f16vec4);" + + "float16_t log(float16_t);" + "f16vec2 log(f16vec2);" + "f16vec3 log(f16vec3);" + "f16vec4 log(f16vec4);" + + "float16_t exp2(float16_t);" + "f16vec2 exp2(f16vec2);" + "f16vec3 exp2(f16vec3);" + "f16vec4 exp2(f16vec4);" + + "float16_t log2(float16_t);" + "f16vec2 log2(f16vec2);" + "f16vec3 log2(f16vec3);" + "f16vec4 log2(f16vec4);" + + "float16_t sqrt(float16_t);" + "f16vec2 sqrt(f16vec2);" + "f16vec3 sqrt(f16vec3);" + "f16vec4 sqrt(f16vec4);" + + "float16_t inversesqrt(float16_t);" + "f16vec2 inversesqrt(f16vec2);" + "f16vec3 inversesqrt(f16vec3);" + "f16vec4 inversesqrt(f16vec4);" + + "float16_t abs(float16_t);" + "f16vec2 abs(f16vec2);" + "f16vec3 abs(f16vec3);" + "f16vec4 abs(f16vec4);" + + "float16_t sign(float16_t);" + "f16vec2 sign(f16vec2);" + "f16vec3 sign(f16vec3);" + "f16vec4 sign(f16vec4);" + + "float16_t floor(float16_t);" + "f16vec2 floor(f16vec2);" + "f16vec3 floor(f16vec3);" + "f16vec4 floor(f16vec4);" + + "float16_t trunc(float16_t);" + "f16vec2 trunc(f16vec2);" + "f16vec3 trunc(f16vec3);" + "f16vec4 trunc(f16vec4);" + + "float16_t round(float16_t);" + "f16vec2 round(f16vec2);" + "f16vec3 round(f16vec3);" + "f16vec4 round(f16vec4);" + + "float16_t roundEven(float16_t);" + "f16vec2 roundEven(f16vec2);" + "f16vec3 roundEven(f16vec3);" + "f16vec4 roundEven(f16vec4);" + + "float16_t ceil(float16_t);" + "f16vec2 ceil(f16vec2);" + "f16vec3 ceil(f16vec3);" + "f16vec4 ceil(f16vec4);" + + "float16_t fract(float16_t);" + "f16vec2 fract(f16vec2);" + "f16vec3 fract(f16vec3);" + "f16vec4 fract(f16vec4);" + + "float16_t mod(float16_t, float16_t);" + "f16vec2 mod(f16vec2, float16_t);" + "f16vec3 mod(f16vec3, float16_t);" + "f16vec4 mod(f16vec4, float16_t);" + "f16vec2 mod(f16vec2, f16vec2);" + "f16vec3 mod(f16vec3, f16vec3);" + "f16vec4 mod(f16vec4, f16vec4);" + + "float16_t modf(float16_t, out float16_t);" + "f16vec2 modf(f16vec2, out f16vec2);" + "f16vec3 modf(f16vec3, out f16vec3);" + "f16vec4 modf(f16vec4, out f16vec4);" + + "float16_t min(float16_t, float16_t);" + "f16vec2 min(f16vec2, float16_t);" + "f16vec3 min(f16vec3, float16_t);" + "f16vec4 min(f16vec4, float16_t);" + "f16vec2 min(f16vec2, f16vec2);" + "f16vec3 min(f16vec3, f16vec3);" + "f16vec4 min(f16vec4, f16vec4);" + + "float16_t max(float16_t, float16_t);" + "f16vec2 max(f16vec2, float16_t);" + "f16vec3 max(f16vec3, float16_t);" + "f16vec4 max(f16vec4, float16_t);" + "f16vec2 max(f16vec2, f16vec2);" + "f16vec3 max(f16vec3, f16vec3);" + "f16vec4 max(f16vec4, f16vec4);" + + "float16_t clamp(float16_t, float16_t, float16_t);" + "f16vec2 clamp(f16vec2, float16_t, float16_t);" + "f16vec3 clamp(f16vec3, float16_t, float16_t);" + "f16vec4 clamp(f16vec4, float16_t, float16_t);" + "f16vec2 clamp(f16vec2, f16vec2, f16vec2);" + "f16vec3 clamp(f16vec3, f16vec3, f16vec3);" + "f16vec4 clamp(f16vec4, f16vec4, f16vec4);" + + "float16_t mix(float16_t, float16_t, float16_t);" + "f16vec2 mix(f16vec2, f16vec2, float16_t);" + "f16vec3 mix(f16vec3, f16vec3, float16_t);" + "f16vec4 mix(f16vec4, f16vec4, float16_t);" + "f16vec2 mix(f16vec2, f16vec2, f16vec2);" + "f16vec3 mix(f16vec3, f16vec3, f16vec3);" + "f16vec4 mix(f16vec4, f16vec4, f16vec4);" + "float16_t mix(float16_t, float16_t, bool);" + "f16vec2 mix(f16vec2, f16vec2, bvec2);" + "f16vec3 mix(f16vec3, f16vec3, bvec3);" + "f16vec4 mix(f16vec4, f16vec4, bvec4);" + + "float16_t step(float16_t, float16_t);" + "f16vec2 step(f16vec2, f16vec2);" + "f16vec3 step(f16vec3, f16vec3);" + "f16vec4 step(f16vec4, f16vec4);" + "f16vec2 step(float16_t, f16vec2);" + "f16vec3 step(float16_t, f16vec3);" + "f16vec4 step(float16_t, f16vec4);" + + "float16_t smoothstep(float16_t, float16_t, float16_t);" + "f16vec2 smoothstep(f16vec2, f16vec2, f16vec2);" + "f16vec3 smoothstep(f16vec3, f16vec3, f16vec3);" + "f16vec4 smoothstep(f16vec4, f16vec4, f16vec4);" + "f16vec2 smoothstep(float16_t, float16_t, f16vec2);" + "f16vec3 smoothstep(float16_t, float16_t, f16vec3);" + "f16vec4 smoothstep(float16_t, float16_t, f16vec4);" + + "bool isnan(float16_t);" + "bvec2 isnan(f16vec2);" + "bvec3 isnan(f16vec3);" + "bvec4 isnan(f16vec4);" + + "bool isinf(float16_t);" + "bvec2 isinf(f16vec2);" + "bvec3 isinf(f16vec3);" + "bvec4 isinf(f16vec4);" + + "float16_t fma(float16_t, float16_t, float16_t);" + "f16vec2 fma(f16vec2, f16vec2, f16vec2);" + "f16vec3 fma(f16vec3, f16vec3, f16vec3);" + "f16vec4 fma(f16vec4, f16vec4, f16vec4);" + + "float16_t frexp(float16_t, out int);" + "f16vec2 frexp(f16vec2, out ivec2);" + "f16vec3 frexp(f16vec3, out ivec3);" + "f16vec4 frexp(f16vec4, out ivec4);" + + "float16_t ldexp(float16_t, in int);" + "f16vec2 ldexp(f16vec2, in ivec2);" + "f16vec3 ldexp(f16vec3, in ivec3);" + "f16vec4 ldexp(f16vec4, in ivec4);" + + "uint packFloat2x16(f16vec2);" + "f16vec2 unpackFloat2x16(uint);" + + "float16_t length(float16_t);" + "float16_t length(f16vec2);" + "float16_t length(f16vec3);" + "float16_t length(f16vec4);" + + "float16_t distance(float16_t, float16_t);" + "float16_t distance(f16vec2, f16vec2);" + "float16_t distance(f16vec3, f16vec3);" + "float16_t distance(f16vec4, f16vec4);" + + "float16_t dot(float16_t, float16_t);" + "float16_t dot(f16vec2, f16vec2);" + "float16_t dot(f16vec3, f16vec3);" + "float16_t dot(f16vec4, f16vec4);" + + "f16vec3 cross(f16vec3, f16vec3);" + + "float16_t normalize(float16_t);" + "f16vec2 normalize(f16vec2);" + "f16vec3 normalize(f16vec3);" + "f16vec4 normalize(f16vec4);" + + "float16_t faceforward(float16_t, float16_t, float16_t);" + "f16vec2 faceforward(f16vec2, f16vec2, f16vec2);" + "f16vec3 faceforward(f16vec3, f16vec3, f16vec3);" + "f16vec4 faceforward(f16vec4, f16vec4, f16vec4);" + + "float16_t reflect(float16_t, float16_t);" + "f16vec2 reflect(f16vec2, f16vec2);" + "f16vec3 reflect(f16vec3, f16vec3);" + "f16vec4 reflect(f16vec4, f16vec4);" + + "float16_t refract(float16_t, float16_t, float16_t);" + "f16vec2 refract(f16vec2, f16vec2, float16_t);" + "f16vec3 refract(f16vec3, f16vec3, float16_t);" + "f16vec4 refract(f16vec4, f16vec4, float16_t);" + + "f16mat2 matrixCompMult(f16mat2, f16mat2);" + "f16mat3 matrixCompMult(f16mat3, f16mat3);" + "f16mat4 matrixCompMult(f16mat4, f16mat4);" + "f16mat2x3 matrixCompMult(f16mat2x3, f16mat2x3);" + "f16mat2x4 matrixCompMult(f16mat2x4, f16mat2x4);" + "f16mat3x2 matrixCompMult(f16mat3x2, f16mat3x2);" + "f16mat3x4 matrixCompMult(f16mat3x4, f16mat3x4);" + "f16mat4x2 matrixCompMult(f16mat4x2, f16mat4x2);" + "f16mat4x3 matrixCompMult(f16mat4x3, f16mat4x3);" + + "f16mat2 outerProduct(f16vec2, f16vec2);" + "f16mat3 outerProduct(f16vec3, f16vec3);" + "f16mat4 outerProduct(f16vec4, f16vec4);" + "f16mat2x3 outerProduct(f16vec3, f16vec2);" + "f16mat3x2 outerProduct(f16vec2, f16vec3);" + "f16mat2x4 outerProduct(f16vec4, f16vec2);" + "f16mat4x2 outerProduct(f16vec2, f16vec4);" + "f16mat3x4 outerProduct(f16vec4, f16vec3);" + "f16mat4x3 outerProduct(f16vec3, f16vec4);" + + "f16mat2 transpose(f16mat2);" + "f16mat3 transpose(f16mat3);" + "f16mat4 transpose(f16mat4);" + "f16mat2x3 transpose(f16mat3x2);" + "f16mat3x2 transpose(f16mat2x3);" + "f16mat2x4 transpose(f16mat4x2);" + "f16mat4x2 transpose(f16mat2x4);" + "f16mat3x4 transpose(f16mat4x3);" + "f16mat4x3 transpose(f16mat3x4);" + + "float16_t determinant(f16mat2);" + "float16_t determinant(f16mat3);" + "float16_t determinant(f16mat4);" + + "f16mat2 inverse(f16mat2);" + "f16mat3 inverse(f16mat3);" + "f16mat4 inverse(f16mat4);" + + "bvec2 lessThan(f16vec2, f16vec2);" + "bvec3 lessThan(f16vec3, f16vec3);" + "bvec4 lessThan(f16vec4, f16vec4);" + + "bvec2 lessThanEqual(f16vec2, f16vec2);" + "bvec3 lessThanEqual(f16vec3, f16vec3);" + "bvec4 lessThanEqual(f16vec4, f16vec4);" + + "bvec2 greaterThan(f16vec2, f16vec2);" + "bvec3 greaterThan(f16vec3, f16vec3);" + "bvec4 greaterThan(f16vec4, f16vec4);" + + "bvec2 greaterThanEqual(f16vec2, f16vec2);" + "bvec3 greaterThanEqual(f16vec3, f16vec3);" + "bvec4 greaterThanEqual(f16vec4, f16vec4);" + + "bvec2 equal(f16vec2, f16vec2);" + "bvec3 equal(f16vec3, f16vec3);" + "bvec4 equal(f16vec4, f16vec4);" + + "bvec2 notEqual(f16vec2, f16vec2);" + "bvec3 notEqual(f16vec3, f16vec3);" + "bvec4 notEqual(f16vec4, f16vec4);" + + "\n"); + } + + // Explicit types + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "int8_t abs(int8_t);" + "i8vec2 abs(i8vec2);" + "i8vec3 abs(i8vec3);" + "i8vec4 abs(i8vec4);" + + "int8_t sign(int8_t);" + "i8vec2 sign(i8vec2);" + "i8vec3 sign(i8vec3);" + "i8vec4 sign(i8vec4);" + + "int8_t min(int8_t x, int8_t y);" + "i8vec2 min(i8vec2 x, int8_t y);" + "i8vec3 min(i8vec3 x, int8_t y);" + "i8vec4 min(i8vec4 x, int8_t y);" + "i8vec2 min(i8vec2 x, i8vec2 y);" + "i8vec3 min(i8vec3 x, i8vec3 y);" + "i8vec4 min(i8vec4 x, i8vec4 y);" + + "uint8_t min(uint8_t x, uint8_t y);" + "u8vec2 min(u8vec2 x, uint8_t y);" + "u8vec3 min(u8vec3 x, uint8_t y);" + "u8vec4 min(u8vec4 x, uint8_t y);" + "u8vec2 min(u8vec2 x, u8vec2 y);" + "u8vec3 min(u8vec3 x, u8vec3 y);" + "u8vec4 min(u8vec4 x, u8vec4 y);" + + "int8_t max(int8_t x, int8_t y);" + "i8vec2 max(i8vec2 x, int8_t y);" + "i8vec3 max(i8vec3 x, int8_t y);" + "i8vec4 max(i8vec4 x, int8_t y);" + "i8vec2 max(i8vec2 x, i8vec2 y);" + "i8vec3 max(i8vec3 x, i8vec3 y);" + "i8vec4 max(i8vec4 x, i8vec4 y);" + + "uint8_t max(uint8_t x, uint8_t y);" + "u8vec2 max(u8vec2 x, uint8_t y);" + "u8vec3 max(u8vec3 x, uint8_t y);" + "u8vec4 max(u8vec4 x, uint8_t y);" + "u8vec2 max(u8vec2 x, u8vec2 y);" + "u8vec3 max(u8vec3 x, u8vec3 y);" + "u8vec4 max(u8vec4 x, u8vec4 y);" + + "int8_t clamp(int8_t x, int8_t minVal, int8_t maxVal);" + "i8vec2 clamp(i8vec2 x, int8_t minVal, int8_t maxVal);" + "i8vec3 clamp(i8vec3 x, int8_t minVal, int8_t maxVal);" + "i8vec4 clamp(i8vec4 x, int8_t minVal, int8_t maxVal);" + "i8vec2 clamp(i8vec2 x, i8vec2 minVal, i8vec2 maxVal);" + "i8vec3 clamp(i8vec3 x, i8vec3 minVal, i8vec3 maxVal);" + "i8vec4 clamp(i8vec4 x, i8vec4 minVal, i8vec4 maxVal);" + + "uint8_t clamp(uint8_t x, uint8_t minVal, uint8_t maxVal);" + "u8vec2 clamp(u8vec2 x, uint8_t minVal, uint8_t maxVal);" + "u8vec3 clamp(u8vec3 x, uint8_t minVal, uint8_t maxVal);" + "u8vec4 clamp(u8vec4 x, uint8_t minVal, uint8_t maxVal);" + "u8vec2 clamp(u8vec2 x, u8vec2 minVal, u8vec2 maxVal);" + "u8vec3 clamp(u8vec3 x, u8vec3 minVal, u8vec3 maxVal);" + "u8vec4 clamp(u8vec4 x, u8vec4 minVal, u8vec4 maxVal);" + + "int8_t mix(int8_t, int8_t, bool);" + "i8vec2 mix(i8vec2, i8vec2, bvec2);" + "i8vec3 mix(i8vec3, i8vec3, bvec3);" + "i8vec4 mix(i8vec4, i8vec4, bvec4);" + "uint8_t mix(uint8_t, uint8_t, bool);" + "u8vec2 mix(u8vec2, u8vec2, bvec2);" + "u8vec3 mix(u8vec3, u8vec3, bvec3);" + "u8vec4 mix(u8vec4, u8vec4, bvec4);" + + "bvec2 lessThan(i8vec2, i8vec2);" + "bvec3 lessThan(i8vec3, i8vec3);" + "bvec4 lessThan(i8vec4, i8vec4);" + "bvec2 lessThan(u8vec2, u8vec2);" + "bvec3 lessThan(u8vec3, u8vec3);" + "bvec4 lessThan(u8vec4, u8vec4);" + + "bvec2 lessThanEqual(i8vec2, i8vec2);" + "bvec3 lessThanEqual(i8vec3, i8vec3);" + "bvec4 lessThanEqual(i8vec4, i8vec4);" + "bvec2 lessThanEqual(u8vec2, u8vec2);" + "bvec3 lessThanEqual(u8vec3, u8vec3);" + "bvec4 lessThanEqual(u8vec4, u8vec4);" + + "bvec2 greaterThan(i8vec2, i8vec2);" + "bvec3 greaterThan(i8vec3, i8vec3);" + "bvec4 greaterThan(i8vec4, i8vec4);" + "bvec2 greaterThan(u8vec2, u8vec2);" + "bvec3 greaterThan(u8vec3, u8vec3);" + "bvec4 greaterThan(u8vec4, u8vec4);" + + "bvec2 greaterThanEqual(i8vec2, i8vec2);" + "bvec3 greaterThanEqual(i8vec3, i8vec3);" + "bvec4 greaterThanEqual(i8vec4, i8vec4);" + "bvec2 greaterThanEqual(u8vec2, u8vec2);" + "bvec3 greaterThanEqual(u8vec3, u8vec3);" + "bvec4 greaterThanEqual(u8vec4, u8vec4);" + + "bvec2 equal(i8vec2, i8vec2);" + "bvec3 equal(i8vec3, i8vec3);" + "bvec4 equal(i8vec4, i8vec4);" + "bvec2 equal(u8vec2, u8vec2);" + "bvec3 equal(u8vec3, u8vec3);" + "bvec4 equal(u8vec4, u8vec4);" + + "bvec2 notEqual(i8vec2, i8vec2);" + "bvec3 notEqual(i8vec3, i8vec3);" + "bvec4 notEqual(i8vec4, i8vec4);" + "bvec2 notEqual(u8vec2, u8vec2);" + "bvec3 notEqual(u8vec3, u8vec3);" + "bvec4 notEqual(u8vec4, u8vec4);" + + " int8_t bitfieldExtract( int8_t, int8_t, int8_t);" + "i8vec2 bitfieldExtract(i8vec2, int8_t, int8_t);" + "i8vec3 bitfieldExtract(i8vec3, int8_t, int8_t);" + "i8vec4 bitfieldExtract(i8vec4, int8_t, int8_t);" + + " uint8_t bitfieldExtract( uint8_t, int8_t, int8_t);" + "u8vec2 bitfieldExtract(u8vec2, int8_t, int8_t);" + "u8vec3 bitfieldExtract(u8vec3, int8_t, int8_t);" + "u8vec4 bitfieldExtract(u8vec4, int8_t, int8_t);" + + " int8_t bitfieldInsert( int8_t base, int8_t, int8_t, int8_t);" + "i8vec2 bitfieldInsert(i8vec2 base, i8vec2, int8_t, int8_t);" + "i8vec3 bitfieldInsert(i8vec3 base, i8vec3, int8_t, int8_t);" + "i8vec4 bitfieldInsert(i8vec4 base, i8vec4, int8_t, int8_t);" + + " uint8_t bitfieldInsert( uint8_t base, uint8_t, int8_t, int8_t);" + "u8vec2 bitfieldInsert(u8vec2 base, u8vec2, int8_t, int8_t);" + "u8vec3 bitfieldInsert(u8vec3 base, u8vec3, int8_t, int8_t);" + "u8vec4 bitfieldInsert(u8vec4 base, u8vec4, int8_t, int8_t);" + + " int8_t bitCount( int8_t);" + "i8vec2 bitCount(i8vec2);" + "i8vec3 bitCount(i8vec3);" + "i8vec4 bitCount(i8vec4);" + + " int8_t bitCount( uint8_t);" + "i8vec2 bitCount(u8vec2);" + "i8vec3 bitCount(u8vec3);" + "i8vec4 bitCount(u8vec4);" + + " int8_t findLSB( int8_t);" + "i8vec2 findLSB(i8vec2);" + "i8vec3 findLSB(i8vec3);" + "i8vec4 findLSB(i8vec4);" + + " int8_t findLSB( uint8_t);" + "i8vec2 findLSB(u8vec2);" + "i8vec3 findLSB(u8vec3);" + "i8vec4 findLSB(u8vec4);" + + " int8_t findMSB( int8_t);" + "i8vec2 findMSB(i8vec2);" + "i8vec3 findMSB(i8vec3);" + "i8vec4 findMSB(i8vec4);" + + " int8_t findMSB( uint8_t);" + "i8vec2 findMSB(u8vec2);" + "i8vec3 findMSB(u8vec3);" + "i8vec4 findMSB(u8vec4);" + + "int16_t abs(int16_t);" + "i16vec2 abs(i16vec2);" + "i16vec3 abs(i16vec3);" + "i16vec4 abs(i16vec4);" + + "int16_t sign(int16_t);" + "i16vec2 sign(i16vec2);" + "i16vec3 sign(i16vec3);" + "i16vec4 sign(i16vec4);" + + "int16_t min(int16_t x, int16_t y);" + "i16vec2 min(i16vec2 x, int16_t y);" + "i16vec3 min(i16vec3 x, int16_t y);" + "i16vec4 min(i16vec4 x, int16_t y);" + "i16vec2 min(i16vec2 x, i16vec2 y);" + "i16vec3 min(i16vec3 x, i16vec3 y);" + "i16vec4 min(i16vec4 x, i16vec4 y);" + + "uint16_t min(uint16_t x, uint16_t y);" + "u16vec2 min(u16vec2 x, uint16_t y);" + "u16vec3 min(u16vec3 x, uint16_t y);" + "u16vec4 min(u16vec4 x, uint16_t y);" + "u16vec2 min(u16vec2 x, u16vec2 y);" + "u16vec3 min(u16vec3 x, u16vec3 y);" + "u16vec4 min(u16vec4 x, u16vec4 y);" + + "int16_t max(int16_t x, int16_t y);" + "i16vec2 max(i16vec2 x, int16_t y);" + "i16vec3 max(i16vec3 x, int16_t y);" + "i16vec4 max(i16vec4 x, int16_t y);" + "i16vec2 max(i16vec2 x, i16vec2 y);" + "i16vec3 max(i16vec3 x, i16vec3 y);" + "i16vec4 max(i16vec4 x, i16vec4 y);" + + "uint16_t max(uint16_t x, uint16_t y);" + "u16vec2 max(u16vec2 x, uint16_t y);" + "u16vec3 max(u16vec3 x, uint16_t y);" + "u16vec4 max(u16vec4 x, uint16_t y);" + "u16vec2 max(u16vec2 x, u16vec2 y);" + "u16vec3 max(u16vec3 x, u16vec3 y);" + "u16vec4 max(u16vec4 x, u16vec4 y);" + + "int16_t clamp(int16_t x, int16_t minVal, int16_t maxVal);" + "i16vec2 clamp(i16vec2 x, int16_t minVal, int16_t maxVal);" + "i16vec3 clamp(i16vec3 x, int16_t minVal, int16_t maxVal);" + "i16vec4 clamp(i16vec4 x, int16_t minVal, int16_t maxVal);" + "i16vec2 clamp(i16vec2 x, i16vec2 minVal, i16vec2 maxVal);" + "i16vec3 clamp(i16vec3 x, i16vec3 minVal, i16vec3 maxVal);" + "i16vec4 clamp(i16vec4 x, i16vec4 minVal, i16vec4 maxVal);" + + "uint16_t clamp(uint16_t x, uint16_t minVal, uint16_t maxVal);" + "u16vec2 clamp(u16vec2 x, uint16_t minVal, uint16_t maxVal);" + "u16vec3 clamp(u16vec3 x, uint16_t minVal, uint16_t maxVal);" + "u16vec4 clamp(u16vec4 x, uint16_t minVal, uint16_t maxVal);" + "u16vec2 clamp(u16vec2 x, u16vec2 minVal, u16vec2 maxVal);" + "u16vec3 clamp(u16vec3 x, u16vec3 minVal, u16vec3 maxVal);" + "u16vec4 clamp(u16vec4 x, u16vec4 minVal, u16vec4 maxVal);" + + "int16_t mix(int16_t, int16_t, bool);" + "i16vec2 mix(i16vec2, i16vec2, bvec2);" + "i16vec3 mix(i16vec3, i16vec3, bvec3);" + "i16vec4 mix(i16vec4, i16vec4, bvec4);" + "uint16_t mix(uint16_t, uint16_t, bool);" + "u16vec2 mix(u16vec2, u16vec2, bvec2);" + "u16vec3 mix(u16vec3, u16vec3, bvec3);" + "u16vec4 mix(u16vec4, u16vec4, bvec4);" + + "float16_t frexp(float16_t, out int16_t);" + "f16vec2 frexp(f16vec2, out i16vec2);" + "f16vec3 frexp(f16vec3, out i16vec3);" + "f16vec4 frexp(f16vec4, out i16vec4);" + + "float16_t ldexp(float16_t, int16_t);" + "f16vec2 ldexp(f16vec2, i16vec2);" + "f16vec3 ldexp(f16vec3, i16vec3);" + "f16vec4 ldexp(f16vec4, i16vec4);" + + "int16_t halfBitsToInt16(float16_t);" + "i16vec2 halfBitsToInt16(f16vec2);" + "i16vec3 halhBitsToInt16(f16vec3);" + "i16vec4 halfBitsToInt16(f16vec4);" + + "uint16_t halfBitsToUint16(float16_t);" + "u16vec2 halfBitsToUint16(f16vec2);" + "u16vec3 halfBitsToUint16(f16vec3);" + "u16vec4 halfBitsToUint16(f16vec4);" + + "int16_t float16BitsToInt16(float16_t);" + "i16vec2 float16BitsToInt16(f16vec2);" + "i16vec3 float16BitsToInt16(f16vec3);" + "i16vec4 float16BitsToInt16(f16vec4);" + + "uint16_t float16BitsToUint16(float16_t);" + "u16vec2 float16BitsToUint16(f16vec2);" + "u16vec3 float16BitsToUint16(f16vec3);" + "u16vec4 float16BitsToUint16(f16vec4);" + + "float16_t int16BitsToFloat16(int16_t);" + "f16vec2 int16BitsToFloat16(i16vec2);" + "f16vec3 int16BitsToFloat16(i16vec3);" + "f16vec4 int16BitsToFloat16(i16vec4);" + + "float16_t uint16BitsToFloat16(uint16_t);" + "f16vec2 uint16BitsToFloat16(u16vec2);" + "f16vec3 uint16BitsToFloat16(u16vec3);" + "f16vec4 uint16BitsToFloat16(u16vec4);" + + "float16_t int16BitsToHalf(int16_t);" + "f16vec2 int16BitsToHalf(i16vec2);" + "f16vec3 int16BitsToHalf(i16vec3);" + "f16vec4 int16BitsToHalf(i16vec4);" + + "float16_t uint16BitsToHalf(uint16_t);" + "f16vec2 uint16BitsToHalf(u16vec2);" + "f16vec3 uint16BitsToHalf(u16vec3);" + "f16vec4 uint16BitsToHalf(u16vec4);" + + "int packInt2x16(i16vec2);" + "uint packUint2x16(u16vec2);" + "int64_t packInt4x16(i16vec4);" + "uint64_t packUint4x16(u16vec4);" + "i16vec2 unpackInt2x16(int);" + "u16vec2 unpackUint2x16(uint);" + "i16vec4 unpackInt4x16(int64_t);" + "u16vec4 unpackUint4x16(uint64_t);" + + "bvec2 lessThan(i16vec2, i16vec2);" + "bvec3 lessThan(i16vec3, i16vec3);" + "bvec4 lessThan(i16vec4, i16vec4);" + "bvec2 lessThan(u16vec2, u16vec2);" + "bvec3 lessThan(u16vec3, u16vec3);" + "bvec4 lessThan(u16vec4, u16vec4);" + + "bvec2 lessThanEqual(i16vec2, i16vec2);" + "bvec3 lessThanEqual(i16vec3, i16vec3);" + "bvec4 lessThanEqual(i16vec4, i16vec4);" + "bvec2 lessThanEqual(u16vec2, u16vec2);" + "bvec3 lessThanEqual(u16vec3, u16vec3);" + "bvec4 lessThanEqual(u16vec4, u16vec4);" + + "bvec2 greaterThan(i16vec2, i16vec2);" + "bvec3 greaterThan(i16vec3, i16vec3);" + "bvec4 greaterThan(i16vec4, i16vec4);" + "bvec2 greaterThan(u16vec2, u16vec2);" + "bvec3 greaterThan(u16vec3, u16vec3);" + "bvec4 greaterThan(u16vec4, u16vec4);" + + "bvec2 greaterThanEqual(i16vec2, i16vec2);" + "bvec3 greaterThanEqual(i16vec3, i16vec3);" + "bvec4 greaterThanEqual(i16vec4, i16vec4);" + "bvec2 greaterThanEqual(u16vec2, u16vec2);" + "bvec3 greaterThanEqual(u16vec3, u16vec3);" + "bvec4 greaterThanEqual(u16vec4, u16vec4);" + + "bvec2 equal(i16vec2, i16vec2);" + "bvec3 equal(i16vec3, i16vec3);" + "bvec4 equal(i16vec4, i16vec4);" + "bvec2 equal(u16vec2, u16vec2);" + "bvec3 equal(u16vec3, u16vec3);" + "bvec4 equal(u16vec4, u16vec4);" + + "bvec2 notEqual(i16vec2, i16vec2);" + "bvec3 notEqual(i16vec3, i16vec3);" + "bvec4 notEqual(i16vec4, i16vec4);" + "bvec2 notEqual(u16vec2, u16vec2);" + "bvec3 notEqual(u16vec3, u16vec3);" + "bvec4 notEqual(u16vec4, u16vec4);" + + " int16_t bitfieldExtract( int16_t, int16_t, int16_t);" + "i16vec2 bitfieldExtract(i16vec2, int16_t, int16_t);" + "i16vec3 bitfieldExtract(i16vec3, int16_t, int16_t);" + "i16vec4 bitfieldExtract(i16vec4, int16_t, int16_t);" + + " uint16_t bitfieldExtract( uint16_t, int16_t, int16_t);" + "u16vec2 bitfieldExtract(u16vec2, int16_t, int16_t);" + "u16vec3 bitfieldExtract(u16vec3, int16_t, int16_t);" + "u16vec4 bitfieldExtract(u16vec4, int16_t, int16_t);" + + " int16_t bitfieldInsert( int16_t base, int16_t, int16_t, int16_t);" + "i16vec2 bitfieldInsert(i16vec2 base, i16vec2, int16_t, int16_t);" + "i16vec3 bitfieldInsert(i16vec3 base, i16vec3, int16_t, int16_t);" + "i16vec4 bitfieldInsert(i16vec4 base, i16vec4, int16_t, int16_t);" + + " uint16_t bitfieldInsert( uint16_t base, uint16_t, int16_t, int16_t);" + "u16vec2 bitfieldInsert(u16vec2 base, u16vec2, int16_t, int16_t);" + "u16vec3 bitfieldInsert(u16vec3 base, u16vec3, int16_t, int16_t);" + "u16vec4 bitfieldInsert(u16vec4 base, u16vec4, int16_t, int16_t);" + + " int16_t bitCount( int16_t);" + "i16vec2 bitCount(i16vec2);" + "i16vec3 bitCount(i16vec3);" + "i16vec4 bitCount(i16vec4);" + + " int16_t bitCount( uint16_t);" + "i16vec2 bitCount(u16vec2);" + "i16vec3 bitCount(u16vec3);" + "i16vec4 bitCount(u16vec4);" + + " int16_t findLSB( int16_t);" + "i16vec2 findLSB(i16vec2);" + "i16vec3 findLSB(i16vec3);" + "i16vec4 findLSB(i16vec4);" + + " int16_t findLSB( uint16_t);" + "i16vec2 findLSB(u16vec2);" + "i16vec3 findLSB(u16vec3);" + "i16vec4 findLSB(u16vec4);" + + " int16_t findMSB( int16_t);" + "i16vec2 findMSB(i16vec2);" + "i16vec3 findMSB(i16vec3);" + "i16vec4 findMSB(i16vec4);" + + " int16_t findMSB( uint16_t);" + "i16vec2 findMSB(u16vec2);" + "i16vec3 findMSB(u16vec3);" + "i16vec4 findMSB(u16vec4);" + + "int16_t pack16(i8vec2);" + "uint16_t pack16(u8vec2);" + "int32_t pack32(i8vec4);" + "uint32_t pack32(u8vec4);" + "int32_t pack32(i16vec2);" + "uint32_t pack32(u16vec2);" + "int64_t pack64(i16vec4);" + "uint64_t pack64(u16vec4);" + "int64_t pack64(i32vec2);" + "uint64_t pack64(u32vec2);" + + "i8vec2 unpack8(int16_t);" + "u8vec2 unpack8(uint16_t);" + "i8vec4 unpack8(int32_t);" + "u8vec4 unpack8(uint32_t);" + "i16vec2 unpack16(int32_t);" + "u16vec2 unpack16(uint32_t);" + "i16vec4 unpack16(int64_t);" + "u16vec4 unpack16(uint64_t);" + "i32vec2 unpack32(int64_t);" + "u32vec2 unpack32(uint64_t);" + + "float64_t radians(float64_t);" + "f64vec2 radians(f64vec2);" + "f64vec3 radians(f64vec3);" + "f64vec4 radians(f64vec4);" + + "float64_t degrees(float64_t);" + "f64vec2 degrees(f64vec2);" + "f64vec3 degrees(f64vec3);" + "f64vec4 degrees(f64vec4);" + + "float64_t sin(float64_t);" + "f64vec2 sin(f64vec2);" + "f64vec3 sin(f64vec3);" + "f64vec4 sin(f64vec4);" + + "float64_t cos(float64_t);" + "f64vec2 cos(f64vec2);" + "f64vec3 cos(f64vec3);" + "f64vec4 cos(f64vec4);" + + "float64_t tan(float64_t);" + "f64vec2 tan(f64vec2);" + "f64vec3 tan(f64vec3);" + "f64vec4 tan(f64vec4);" + + "float64_t asin(float64_t);" + "f64vec2 asin(f64vec2);" + "f64vec3 asin(f64vec3);" + "f64vec4 asin(f64vec4);" + + "float64_t acos(float64_t);" + "f64vec2 acos(f64vec2);" + "f64vec3 acos(f64vec3);" + "f64vec4 acos(f64vec4);" + + "float64_t atan(float64_t, float64_t);" + "f64vec2 atan(f64vec2, f64vec2);" + "f64vec3 atan(f64vec3, f64vec3);" + "f64vec4 atan(f64vec4, f64vec4);" + + "float64_t atan(float64_t);" + "f64vec2 atan(f64vec2);" + "f64vec3 atan(f64vec3);" + "f64vec4 atan(f64vec4);" + + "float64_t sinh(float64_t);" + "f64vec2 sinh(f64vec2);" + "f64vec3 sinh(f64vec3);" + "f64vec4 sinh(f64vec4);" + + "float64_t cosh(float64_t);" + "f64vec2 cosh(f64vec2);" + "f64vec3 cosh(f64vec3);" + "f64vec4 cosh(f64vec4);" + + "float64_t tanh(float64_t);" + "f64vec2 tanh(f64vec2);" + "f64vec3 tanh(f64vec3);" + "f64vec4 tanh(f64vec4);" + + "float64_t asinh(float64_t);" + "f64vec2 asinh(f64vec2);" + "f64vec3 asinh(f64vec3);" + "f64vec4 asinh(f64vec4);" + + "float64_t acosh(float64_t);" + "f64vec2 acosh(f64vec2);" + "f64vec3 acosh(f64vec3);" + "f64vec4 acosh(f64vec4);" + + "float64_t atanh(float64_t);" + "f64vec2 atanh(f64vec2);" + "f64vec3 atanh(f64vec3);" + "f64vec4 atanh(f64vec4);" + + "float64_t pow(float64_t, float64_t);" + "f64vec2 pow(f64vec2, f64vec2);" + "f64vec3 pow(f64vec3, f64vec3);" + "f64vec4 pow(f64vec4, f64vec4);" + + "float64_t exp(float64_t);" + "f64vec2 exp(f64vec2);" + "f64vec3 exp(f64vec3);" + "f64vec4 exp(f64vec4);" + + "float64_t log(float64_t);" + "f64vec2 log(f64vec2);" + "f64vec3 log(f64vec3);" + "f64vec4 log(f64vec4);" + + "float64_t exp2(float64_t);" + "f64vec2 exp2(f64vec2);" + "f64vec3 exp2(f64vec3);" + "f64vec4 exp2(f64vec4);" + + "float64_t log2(float64_t);" + "f64vec2 log2(f64vec2);" + "f64vec3 log2(f64vec3);" + "f64vec4 log2(f64vec4);" + "\n"); + } + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangFragment].append(derivativesAndControl64bits); + stageBuiltins[EShLangFragment].append( + "float64_t interpolateAtCentroid(float64_t);" + "f64vec2 interpolateAtCentroid(f64vec2);" + "f64vec3 interpolateAtCentroid(f64vec3);" + "f64vec4 interpolateAtCentroid(f64vec4);" + + "float64_t interpolateAtSample(float64_t, int);" + "f64vec2 interpolateAtSample(f64vec2, int);" + "f64vec3 interpolateAtSample(f64vec3, int);" + "f64vec4 interpolateAtSample(f64vec4, int);" + + "float64_t interpolateAtOffset(float64_t, f64vec2);" + "f64vec2 interpolateAtOffset(f64vec2, f64vec2);" + "f64vec3 interpolateAtOffset(f64vec3, f64vec2);" + "f64vec4 interpolateAtOffset(f64vec4, f64vec2);" + + "\n"); + + } + + //============================================================================ + // + // Prototypes for built-in functions seen by vertex shaders only. + // (Except legacy lod functions, where it depends which release they are + // vertex only.) + // + //============================================================================ + + // + // Geometric Functions. + // + if (spvVersion.vulkan == 0 && IncludeLegacy(version, profile, spvVersion)) + stageBuiltins[EShLangVertex].append("vec4 ftransform();"); + + // + // Original-style texture Functions with lod. + // + TString* s; + if (version == 100) + s = &stageBuiltins[EShLangVertex]; + else + s = &commonBuiltins; + if ((profile == EEsProfile && version == 100) || + profile == ECompatibilityProfile || + (profile == ECoreProfile && version < 420) || + profile == ENoProfile) { + if (spvVersion.spv == 0) { + s->append( + "vec4 texture2DLod(sampler2D, vec2, float);" // GL_ARB_shader_texture_lod + "vec4 texture2DProjLod(sampler2D, vec3, float);" // GL_ARB_shader_texture_lod + "vec4 texture2DProjLod(sampler2D, vec4, float);" // GL_ARB_shader_texture_lod + "vec4 texture3DLod(sampler3D, vec3, float);" // GL_ARB_shader_texture_lod // OES_texture_3D, but caught by keyword check + "vec4 texture3DProjLod(sampler3D, vec4, float);" // GL_ARB_shader_texture_lod // OES_texture_3D, but caught by keyword check + "vec4 textureCubeLod(samplerCube, vec3, float);" // GL_ARB_shader_texture_lod + + "\n"); + } + } + if ( profile == ECompatibilityProfile || + (profile == ECoreProfile && version < 420) || + profile == ENoProfile) { + if (spvVersion.spv == 0) { + s->append( + "vec4 texture1DLod(sampler1D, float, float);" // GL_ARB_shader_texture_lod + "vec4 texture1DProjLod(sampler1D, vec2, float);" // GL_ARB_shader_texture_lod + "vec4 texture1DProjLod(sampler1D, vec4, float);" // GL_ARB_shader_texture_lod + "vec4 shadow1DLod(sampler1DShadow, vec3, float);" // GL_ARB_shader_texture_lod + "vec4 shadow2DLod(sampler2DShadow, vec3, float);" // GL_ARB_shader_texture_lod + "vec4 shadow1DProjLod(sampler1DShadow, vec4, float);" // GL_ARB_shader_texture_lod + "vec4 shadow2DProjLod(sampler2DShadow, vec4, float);" // GL_ARB_shader_texture_lod + + "vec4 texture1DGradARB(sampler1D, float, float, float);" // GL_ARB_shader_texture_lod + "vec4 texture1DProjGradARB(sampler1D, vec2, float, float);" // GL_ARB_shader_texture_lod + "vec4 texture1DProjGradARB(sampler1D, vec4, float, float);" // GL_ARB_shader_texture_lod + "vec4 texture2DGradARB(sampler2D, vec2, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DProjGradARB(sampler2D, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DProjGradARB(sampler2D, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture3DGradARB(sampler3D, vec3, vec3, vec3);" // GL_ARB_shader_texture_lod + "vec4 texture3DProjGradARB(sampler3D, vec4, vec3, vec3);" // GL_ARB_shader_texture_lod + "vec4 textureCubeGradARB(samplerCube, vec3, vec3, vec3);" // GL_ARB_shader_texture_lod + "vec4 shadow1DGradARB(sampler1DShadow, vec3, float, float);" // GL_ARB_shader_texture_lod + "vec4 shadow1DProjGradARB( sampler1DShadow, vec4, float, float);" // GL_ARB_shader_texture_lod + "vec4 shadow2DGradARB(sampler2DShadow, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 shadow2DProjGradARB( sampler2DShadow, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DRectGradARB(sampler2DRect, vec2, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DRectProjGradARB( sampler2DRect, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DRectProjGradARB( sampler2DRect, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 shadow2DRectGradARB( sampler2DRectShadow, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 shadow2DRectProjGradARB(sampler2DRectShadow, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod + + "\n"); + } + } + + if ((profile != EEsProfile && version >= 150) || + (profile == EEsProfile && version >= 310)) { + //============================================================================ + // + // Prototypes for built-in functions seen by geometry shaders only. + // + //============================================================================ + + if (profile != EEsProfile && version >= 400) { + stageBuiltins[EShLangGeometry].append( + "void EmitStreamVertex(int);" + "void EndStreamPrimitive(int);" + ); + } + stageBuiltins[EShLangGeometry].append( + "void EmitVertex();" + "void EndPrimitive();" + "\n"); + } +#endif + + //============================================================================ + // + // Prototypes for all control functions. + // + //============================================================================ + bool esBarrier = (profile == EEsProfile && version >= 310); + if ((profile != EEsProfile && version >= 150) || esBarrier) + stageBuiltins[EShLangTessControl].append( + "void barrier();" + ); + if ((profile != EEsProfile && version >= 420) || esBarrier) + stageBuiltins[EShLangCompute].append( + "void barrier();" + ); + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + stageBuiltins[EShLangMeshNV].append( + "void barrier();" + ); + stageBuiltins[EShLangTaskNV].append( + "void barrier();" + ); + } + if ((profile != EEsProfile && version >= 130) || esBarrier) + commonBuiltins.append( + "void memoryBarrier();" + ); + if ((profile != EEsProfile && version >= 420) || esBarrier) { + commonBuiltins.append( + "void memoryBarrierBuffer();" + ); + stageBuiltins[EShLangCompute].append( + "void memoryBarrierShared();" + "void groupMemoryBarrier();" + ); + } +#ifndef GLSLANG_WEB + if ((profile != EEsProfile && version >= 420) || esBarrier) { + if (spvVersion.vulkan == 0) { + commonBuiltins.append("void memoryBarrierAtomicCounter();"); + } + commonBuiltins.append("void memoryBarrierImage();"); + } + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + stageBuiltins[EShLangMeshNV].append( + "void memoryBarrierShared();" + "void groupMemoryBarrier();" + ); + stageBuiltins[EShLangTaskNV].append( + "void memoryBarrierShared();" + "void groupMemoryBarrier();" + ); + } + + commonBuiltins.append("void controlBarrier(int, int, int, int);\n" + "void memoryBarrier(int, int, int);\n"); + + commonBuiltins.append("void debugPrintfEXT();\n"); + + if (profile != EEsProfile && version >= 450) { + // coopMatStoreNV perhaps ought to have "out" on the buf parameter, but + // adding it introduces undesirable tempArgs on the stack. What we want + // is more like "buf" thought of as a pointer value being an in parameter. + stageBuiltins[EShLangCompute].append( + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent float16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent float[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "void coopMatStoreNV(fcoopmatNV m, volatile coherent float16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent float[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent float64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "fcoopmatNV coopMatMulAddNV(fcoopmatNV A, fcoopmatNV B, fcoopmatNV C);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent int8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent int16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent int[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent int64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent ivec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent ivec4[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent int8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent int16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent int[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent int64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent ivec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent ivec4[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "void coopMatStoreNV(icoopmatNV m, volatile coherent int8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent int16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent int[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent int64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent ivec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent ivec4[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "void coopMatStoreNV(ucoopmatNV m, volatile coherent int8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent int16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent int[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent int64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent ivec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent ivec4[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "icoopmatNV coopMatMulAddNV(icoopmatNV A, icoopmatNV B, icoopmatNV C);\n" + "ucoopmatNV coopMatMulAddNV(ucoopmatNV A, ucoopmatNV B, ucoopmatNV C);\n" + ); + } + + //============================================================================ + // + // Prototypes for built-in functions seen by fragment shaders only. + // + //============================================================================ + + // + // Original-style texture Functions with bias. + // + if (spvVersion.spv == 0 && (profile != EEsProfile || version == 100)) { + stageBuiltins[EShLangFragment].append( + "vec4 texture2D(sampler2D, vec2, float);" + "vec4 texture2DProj(sampler2D, vec3, float);" + "vec4 texture2DProj(sampler2D, vec4, float);" + "vec4 texture3D(sampler3D, vec3, float);" // OES_texture_3D + "vec4 texture3DProj(sampler3D, vec4, float);" // OES_texture_3D + "vec4 textureCube(samplerCube, vec3, float);" + + "\n"); + } + if (spvVersion.spv == 0 && (profile != EEsProfile && version > 100)) { + stageBuiltins[EShLangFragment].append( + "vec4 texture1D(sampler1D, float, float);" + "vec4 texture1DProj(sampler1D, vec2, float);" + "vec4 texture1DProj(sampler1D, vec4, float);" + "vec4 shadow1D(sampler1DShadow, vec3, float);" + "vec4 shadow2D(sampler2DShadow, vec3, float);" + "vec4 shadow1DProj(sampler1DShadow, vec4, float);" + "vec4 shadow2DProj(sampler2DShadow, vec4, float);" + + "\n"); + } + if (spvVersion.spv == 0 && profile == EEsProfile) { + stageBuiltins[EShLangFragment].append( + "vec4 texture2DLodEXT(sampler2D, vec2, float);" // GL_EXT_shader_texture_lod + "vec4 texture2DProjLodEXT(sampler2D, vec3, float);" // GL_EXT_shader_texture_lod + "vec4 texture2DProjLodEXT(sampler2D, vec4, float);" // GL_EXT_shader_texture_lod + "vec4 textureCubeLodEXT(samplerCube, vec3, float);" // GL_EXT_shader_texture_lod + + "\n"); + } + + // GL_ARB_derivative_control + if (profile != EEsProfile && version >= 400) { + stageBuiltins[EShLangFragment].append(derivativeControls); + stageBuiltins[EShLangFragment].append("\n"); + } + + // GL_OES_shader_multisample_interpolation + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 400)) { + stageBuiltins[EShLangFragment].append( + "float interpolateAtCentroid(float);" + "vec2 interpolateAtCentroid(vec2);" + "vec3 interpolateAtCentroid(vec3);" + "vec4 interpolateAtCentroid(vec4);" + + "float interpolateAtSample(float, int);" + "vec2 interpolateAtSample(vec2, int);" + "vec3 interpolateAtSample(vec3, int);" + "vec4 interpolateAtSample(vec4, int);" + + "float interpolateAtOffset(float, vec2);" + "vec2 interpolateAtOffset(vec2, vec2);" + "vec3 interpolateAtOffset(vec3, vec2);" + "vec4 interpolateAtOffset(vec4, vec2);" + + "\n"); + } + + stageBuiltins[EShLangFragment].append( + "void beginInvocationInterlockARB(void);" + "void endInvocationInterlockARB(void);"); + + stageBuiltins[EShLangFragment].append( + "bool helperInvocationEXT();" + "\n"); + + // GL_AMD_shader_explicit_vertex_parameter + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangFragment].append( + "float interpolateAtVertexAMD(float, uint);" + "vec2 interpolateAtVertexAMD(vec2, uint);" + "vec3 interpolateAtVertexAMD(vec3, uint);" + "vec4 interpolateAtVertexAMD(vec4, uint);" + + "int interpolateAtVertexAMD(int, uint);" + "ivec2 interpolateAtVertexAMD(ivec2, uint);" + "ivec3 interpolateAtVertexAMD(ivec3, uint);" + "ivec4 interpolateAtVertexAMD(ivec4, uint);" + + "uint interpolateAtVertexAMD(uint, uint);" + "uvec2 interpolateAtVertexAMD(uvec2, uint);" + "uvec3 interpolateAtVertexAMD(uvec3, uint);" + "uvec4 interpolateAtVertexAMD(uvec4, uint);" + + "float16_t interpolateAtVertexAMD(float16_t, uint);" + "f16vec2 interpolateAtVertexAMD(f16vec2, uint);" + "f16vec3 interpolateAtVertexAMD(f16vec3, uint);" + "f16vec4 interpolateAtVertexAMD(f16vec4, uint);" + + "\n"); + } + + // GL_AMD_gpu_shader_half_float + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangFragment].append(derivativesAndControl16bits); + stageBuiltins[EShLangFragment].append("\n"); + + stageBuiltins[EShLangFragment].append( + "float16_t interpolateAtCentroid(float16_t);" + "f16vec2 interpolateAtCentroid(f16vec2);" + "f16vec3 interpolateAtCentroid(f16vec3);" + "f16vec4 interpolateAtCentroid(f16vec4);" + + "float16_t interpolateAtSample(float16_t, int);" + "f16vec2 interpolateAtSample(f16vec2, int);" + "f16vec3 interpolateAtSample(f16vec3, int);" + "f16vec4 interpolateAtSample(f16vec4, int);" + + "float16_t interpolateAtOffset(float16_t, f16vec2);" + "f16vec2 interpolateAtOffset(f16vec2, f16vec2);" + "f16vec3 interpolateAtOffset(f16vec3, f16vec2);" + "f16vec4 interpolateAtOffset(f16vec4, f16vec2);" + + "\n"); + } + + // GL_ARB_shader_clock & GL_EXT_shader_realtime_clock + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "uvec2 clock2x32ARB();" + "uint64_t clockARB();" + "uvec2 clockRealtime2x32EXT();" + "uint64_t clockRealtimeEXT();" + "\n"); + } + + // GL_AMD_shader_fragment_mask + if (profile != EEsProfile && version >= 450 && spvVersion.vulkan > 0) { + stageBuiltins[EShLangFragment].append( + "uint fragmentMaskFetchAMD(subpassInputMS);" + "uint fragmentMaskFetchAMD(isubpassInputMS);" + "uint fragmentMaskFetchAMD(usubpassInputMS);" + + "vec4 fragmentFetchAMD(subpassInputMS, uint);" + "ivec4 fragmentFetchAMD(isubpassInputMS, uint);" + "uvec4 fragmentFetchAMD(usubpassInputMS, uint);" + + "\n"); + } + + // Builtins for GL_NV_ray_tracing/GL_EXT_ray_tracing/GL_EXT_ray_query + if (profile != EEsProfile && version >= 460) { + commonBuiltins.append("void rayQueryInitializeEXT(rayQueryEXT, accelerationStructureEXT, uint, uint, vec3, float, vec3, float);" + "void rayQueryTerminateEXT(rayQueryEXT);" + "void rayQueryGenerateIntersectionEXT(rayQueryEXT, float);" + "void rayQueryConfirmIntersectionEXT(rayQueryEXT);" + "bool rayQueryProceedEXT(rayQueryEXT);" + "uint rayQueryGetIntersectionTypeEXT(rayQueryEXT, bool);" + "float rayQueryGetRayTMinEXT(rayQueryEXT);" + "uint rayQueryGetRayFlagsEXT(rayQueryEXT);" + "vec3 rayQueryGetWorldRayOriginEXT(rayQueryEXT);" + "vec3 rayQueryGetWorldRayDirectionEXT(rayQueryEXT);" + "float rayQueryGetIntersectionTEXT(rayQueryEXT, bool);" + "int rayQueryGetIntersectionInstanceCustomIndexEXT(rayQueryEXT, bool);" + "int rayQueryGetIntersectionInstanceIdEXT(rayQueryEXT, bool);" + "uint rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT(rayQueryEXT, bool);" + "int rayQueryGetIntersectionGeometryIndexEXT(rayQueryEXT, bool);" + "int rayQueryGetIntersectionPrimitiveIndexEXT(rayQueryEXT, bool);" + "vec2 rayQueryGetIntersectionBarycentricsEXT(rayQueryEXT, bool);" + "bool rayQueryGetIntersectionFrontFaceEXT(rayQueryEXT, bool);" + "bool rayQueryGetIntersectionCandidateAABBOpaqueEXT(rayQueryEXT);" + "vec3 rayQueryGetIntersectionObjectRayDirectionEXT(rayQueryEXT, bool);" + "vec3 rayQueryGetIntersectionObjectRayOriginEXT(rayQueryEXT, bool);" + "mat4x3 rayQueryGetIntersectionObjectToWorldEXT(rayQueryEXT, bool);" + "mat4x3 rayQueryGetIntersectionWorldToObjectEXT(rayQueryEXT, bool);" + "\n"); + + stageBuiltins[EShLangRayGen].append( + "void traceNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void traceRayEXT(accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void executeCallableNV(uint, int);" + "void executeCallableEXT(uint, int);" + "\n"); + stageBuiltins[EShLangIntersect].append( + "bool reportIntersectionNV(float, uint);" + "bool reportIntersectionEXT(float, uint);" + "\n"); + stageBuiltins[EShLangAnyHit].append( + "void ignoreIntersectionNV();" + "void ignoreIntersectionEXT();" + "void terminateRayNV();" + "void terminateRayEXT();" + "\n"); + stageBuiltins[EShLangClosestHit].append( + "void traceNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void traceRayEXT(accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void executeCallableNV(uint, int);" + "void executeCallableEXT(uint, int);" + "\n"); + stageBuiltins[EShLangMiss].append( + "void traceNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void traceRayEXT(accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void executeCallableNV(uint, int);" + "void executeCallableEXT(uint, int);" + "\n"); + stageBuiltins[EShLangCallable].append( + "void executeCallableNV(uint, int);" + "void executeCallableEXT(uint, int);" + "\n"); + } + + //E_SPV_NV_compute_shader_derivatives + if ((profile == EEsProfile && version >= 320) || (profile != EEsProfile && version >= 450)) { + stageBuiltins[EShLangCompute].append(derivativeControls); + stageBuiltins[EShLangCompute].append("\n"); + } + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangCompute].append(derivativesAndControl16bits); + stageBuiltins[EShLangCompute].append(derivativesAndControl64bits); + stageBuiltins[EShLangCompute].append("\n"); + } + + // Builtins for GL_NV_mesh_shader + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + stageBuiltins[EShLangMeshNV].append( + "void writePackedPrimitiveIndices4x8NV(uint, uint);" + "\n"); + } +#endif + + //============================================================================ + // + // Standard Uniforms + // + //============================================================================ + + // + // Depth range in window coordinates, p. 33 + // + if (spvVersion.spv == 0) { + commonBuiltins.append( + "struct gl_DepthRangeParameters {" + ); + if (profile == EEsProfile) { + commonBuiltins.append( + "highp float near;" // n + "highp float far;" // f + "highp float diff;" // f - n + ); + } else { +#ifndef GLSLANG_WEB + commonBuiltins.append( + "float near;" // n + "float far;" // f + "float diff;" // f - n + ); +#endif + } + + commonBuiltins.append( + "};" + "uniform gl_DepthRangeParameters gl_DepthRange;" + "\n"); + } + +#ifndef GLSLANG_WEB + if (spvVersion.spv == 0 && IncludeLegacy(version, profile, spvVersion)) { + // + // Matrix state. p. 31, 32, 37, 39, 40. + // + commonBuiltins.append( + "uniform mat4 gl_ModelViewMatrix;" + "uniform mat4 gl_ProjectionMatrix;" + "uniform mat4 gl_ModelViewProjectionMatrix;" + + // + // Derived matrix state that provides inverse and transposed versions + // of the matrices above. + // + "uniform mat3 gl_NormalMatrix;" + + "uniform mat4 gl_ModelViewMatrixInverse;" + "uniform mat4 gl_ProjectionMatrixInverse;" + "uniform mat4 gl_ModelViewProjectionMatrixInverse;" + + "uniform mat4 gl_ModelViewMatrixTranspose;" + "uniform mat4 gl_ProjectionMatrixTranspose;" + "uniform mat4 gl_ModelViewProjectionMatrixTranspose;" + + "uniform mat4 gl_ModelViewMatrixInverseTranspose;" + "uniform mat4 gl_ProjectionMatrixInverseTranspose;" + "uniform mat4 gl_ModelViewProjectionMatrixInverseTranspose;" + + // + // Normal scaling p. 39. + // + "uniform float gl_NormalScale;" + + // + // Point Size, p. 66, 67. + // + "struct gl_PointParameters {" + "float size;" + "float sizeMin;" + "float sizeMax;" + "float fadeThresholdSize;" + "float distanceConstantAttenuation;" + "float distanceLinearAttenuation;" + "float distanceQuadraticAttenuation;" + "};" + + "uniform gl_PointParameters gl_Point;" + + // + // Material State p. 50, 55. + // + "struct gl_MaterialParameters {" + "vec4 emission;" // Ecm + "vec4 ambient;" // Acm + "vec4 diffuse;" // Dcm + "vec4 specular;" // Scm + "float shininess;" // Srm + "};" + "uniform gl_MaterialParameters gl_FrontMaterial;" + "uniform gl_MaterialParameters gl_BackMaterial;" + + // + // Light State p 50, 53, 55. + // + "struct gl_LightSourceParameters {" + "vec4 ambient;" // Acli + "vec4 diffuse;" // Dcli + "vec4 specular;" // Scli + "vec4 position;" // Ppli + "vec4 halfVector;" // Derived: Hi + "vec3 spotDirection;" // Sdli + "float spotExponent;" // Srli + "float spotCutoff;" // Crli + // (range: [0.0,90.0], 180.0) + "float spotCosCutoff;" // Derived: cos(Crli) + // (range: [1.0,0.0],-1.0) + "float constantAttenuation;" // K0 + "float linearAttenuation;" // K1 + "float quadraticAttenuation;"// K2 + "};" + + "struct gl_LightModelParameters {" + "vec4 ambient;" // Acs + "};" + + "uniform gl_LightModelParameters gl_LightModel;" + + // + // Derived state from products of light and material. + // + "struct gl_LightModelProducts {" + "vec4 sceneColor;" // Derived. Ecm + Acm * Acs + "};" + + "uniform gl_LightModelProducts gl_FrontLightModelProduct;" + "uniform gl_LightModelProducts gl_BackLightModelProduct;" + + "struct gl_LightProducts {" + "vec4 ambient;" // Acm * Acli + "vec4 diffuse;" // Dcm * Dcli + "vec4 specular;" // Scm * Scli + "};" + + // + // Fog p. 161 + // + "struct gl_FogParameters {" + "vec4 color;" + "float density;" + "float start;" + "float end;" + "float scale;" // 1 / (gl_FogEnd - gl_FogStart) + "};" + + "uniform gl_FogParameters gl_Fog;" + + "\n"); + } +#endif + + //============================================================================ + // + // Define the interface to the compute shader. + // + //============================================================================ + + if ((profile != EEsProfile && version >= 420) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangCompute].append( + "in highp uvec3 gl_NumWorkGroups;" + "const highp uvec3 gl_WorkGroupSize = uvec3(1,1,1);" + + "in highp uvec3 gl_WorkGroupID;" + "in highp uvec3 gl_LocalInvocationID;" + + "in highp uvec3 gl_GlobalInvocationID;" + "in highp uint gl_LocalInvocationIndex;" + + "\n"); + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangCompute].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "\n"); + } + +#ifndef GLSLANG_WEB + //============================================================================ + // + // Define the interface to the mesh/task shader. + // + //============================================================================ + + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + // per-vertex attributes + stageBuiltins[EShLangMeshNV].append( + "out gl_MeshPerVertexNV {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + "float gl_CullDistance[];" + "perviewNV vec4 gl_PositionPerViewNV[];" + "perviewNV float gl_ClipDistancePerViewNV[][];" + "perviewNV float gl_CullDistancePerViewNV[][];" + "} gl_MeshVerticesNV[];" + ); + + // per-primitive attributes + stageBuiltins[EShLangMeshNV].append( + "perprimitiveNV out gl_MeshPerPrimitiveNV {" + "int gl_PrimitiveID;" + "int gl_Layer;" + "int gl_ViewportIndex;" + "int gl_ViewportMask[];" + "perviewNV int gl_LayerPerViewNV[];" + "perviewNV int gl_ViewportMaskPerViewNV[][];" + "} gl_MeshPrimitivesNV[];" + ); + + stageBuiltins[EShLangMeshNV].append( + "out uint gl_PrimitiveCountNV;" + "out uint gl_PrimitiveIndicesNV[];" + + "in uint gl_MeshViewCountNV;" + "in uint gl_MeshViewIndicesNV[4];" + + "const highp uvec3 gl_WorkGroupSize = uvec3(1,1,1);" + + "in highp uvec3 gl_WorkGroupID;" + "in highp uvec3 gl_LocalInvocationID;" + + "in highp uvec3 gl_GlobalInvocationID;" + "in highp uint gl_LocalInvocationIndex;" + + "\n"); + + stageBuiltins[EShLangTaskNV].append( + "out uint gl_TaskCountNV;" + + "const highp uvec3 gl_WorkGroupSize = uvec3(1,1,1);" + + "in highp uvec3 gl_WorkGroupID;" + "in highp uvec3 gl_LocalInvocationID;" + + "in highp uvec3 gl_GlobalInvocationID;" + "in highp uint gl_LocalInvocationIndex;" + + "in uint gl_MeshViewCountNV;" + "in uint gl_MeshViewIndicesNV[4];" + + "\n"); + } + + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangMeshNV].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in int gl_DrawIDARB;" // GL_ARB_shader_draw_parameters + "\n"); + + stageBuiltins[EShLangTaskNV].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in int gl_DrawIDARB;" // GL_ARB_shader_draw_parameters + "\n"); + + if (version >= 460) { + stageBuiltins[EShLangMeshNV].append( + "in int gl_DrawID;" + "\n"); + + stageBuiltins[EShLangTaskNV].append( + "in int gl_DrawID;" + "\n"); + } + } + + //============================================================================ + // + // Define the interface to the vertex shader. + // + //============================================================================ + + if (profile != EEsProfile) { + if (version < 130) { + stageBuiltins[EShLangVertex].append( + "attribute vec4 gl_Color;" + "attribute vec4 gl_SecondaryColor;" + "attribute vec3 gl_Normal;" + "attribute vec4 gl_Vertex;" + "attribute vec4 gl_MultiTexCoord0;" + "attribute vec4 gl_MultiTexCoord1;" + "attribute vec4 gl_MultiTexCoord2;" + "attribute vec4 gl_MultiTexCoord3;" + "attribute vec4 gl_MultiTexCoord4;" + "attribute vec4 gl_MultiTexCoord5;" + "attribute vec4 gl_MultiTexCoord6;" + "attribute vec4 gl_MultiTexCoord7;" + "attribute float gl_FogCoord;" + "\n"); + } else if (IncludeLegacy(version, profile, spvVersion)) { + stageBuiltins[EShLangVertex].append( + "in vec4 gl_Color;" + "in vec4 gl_SecondaryColor;" + "in vec3 gl_Normal;" + "in vec4 gl_Vertex;" + "in vec4 gl_MultiTexCoord0;" + "in vec4 gl_MultiTexCoord1;" + "in vec4 gl_MultiTexCoord2;" + "in vec4 gl_MultiTexCoord3;" + "in vec4 gl_MultiTexCoord4;" + "in vec4 gl_MultiTexCoord5;" + "in vec4 gl_MultiTexCoord6;" + "in vec4 gl_MultiTexCoord7;" + "in float gl_FogCoord;" + "\n"); + } + + if (version < 150) { + if (version < 130) { + stageBuiltins[EShLangVertex].append( + " vec4 gl_ClipVertex;" // needs qualifier fixed later + "varying vec4 gl_FrontColor;" + "varying vec4 gl_BackColor;" + "varying vec4 gl_FrontSecondaryColor;" + "varying vec4 gl_BackSecondaryColor;" + "varying vec4 gl_TexCoord[];" + "varying float gl_FogFragCoord;" + "\n"); + } else if (IncludeLegacy(version, profile, spvVersion)) { + stageBuiltins[EShLangVertex].append( + " vec4 gl_ClipVertex;" // needs qualifier fixed later + "out vec4 gl_FrontColor;" + "out vec4 gl_BackColor;" + "out vec4 gl_FrontSecondaryColor;" + "out vec4 gl_BackSecondaryColor;" + "out vec4 gl_TexCoord[];" + "out float gl_FogFragCoord;" + "\n"); + } + stageBuiltins[EShLangVertex].append( + "vec4 gl_Position;" // needs qualifier fixed later + "float gl_PointSize;" // needs qualifier fixed later + ); + + if (version == 130 || version == 140) + stageBuiltins[EShLangVertex].append( + "out float gl_ClipDistance[];" + ); + } else { + // version >= 150 + stageBuiltins[EShLangVertex].append( + "out gl_PerVertex {" + "vec4 gl_Position;" // needs qualifier fixed later + "float gl_PointSize;" // needs qualifier fixed later + "float gl_ClipDistance[];" + ); + if (IncludeLegacy(version, profile, spvVersion)) + stageBuiltins[EShLangVertex].append( + "vec4 gl_ClipVertex;" // needs qualifier fixed later + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangVertex].append( + "float gl_CullDistance[];" + ); + stageBuiltins[EShLangVertex].append( + "};" + "\n"); + } + if (version >= 130 && spvVersion.vulkan == 0) + stageBuiltins[EShLangVertex].append( + "int gl_VertexID;" // needs qualifier fixed later + ); + if (version >= 140 && spvVersion.vulkan == 0) + stageBuiltins[EShLangVertex].append( + "int gl_InstanceID;" // needs qualifier fixed later + ); + if (spvVersion.vulkan > 0 && version >= 140) + stageBuiltins[EShLangVertex].append( + "in int gl_VertexIndex;" + "in int gl_InstanceIndex;" + ); + if (version >= 440) { + stageBuiltins[EShLangVertex].append( + "in int gl_BaseVertexARB;" + "in int gl_BaseInstanceARB;" + "in int gl_DrawIDARB;" + ); + } + if (version >= 410) { + stageBuiltins[EShLangVertex].append( + "out int gl_ViewportIndex;" + "out int gl_Layer;" + ); + } + if (version >= 460) { + stageBuiltins[EShLangVertex].append( + "in int gl_BaseVertex;" + "in int gl_BaseInstance;" + "in int gl_DrawID;" + ); + } + + if (version >= 450) + stageBuiltins[EShLangVertex].append( + "out int gl_ViewportMask[];" // GL_NV_viewport_array2 + "out int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering + "out vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "out vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "out int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + } else { + // ES profile + if (version == 100) { + stageBuiltins[EShLangVertex].append( + "highp vec4 gl_Position;" // needs qualifier fixed later + "mediump float gl_PointSize;" // needs qualifier fixed later + ); + } else { + if (spvVersion.vulkan == 0) + stageBuiltins[EShLangVertex].append( + "in highp int gl_VertexID;" // needs qualifier fixed later + "in highp int gl_InstanceID;" // needs qualifier fixed later + ); + if (spvVersion.vulkan > 0) +#endif + stageBuiltins[EShLangVertex].append( + "in highp int gl_VertexIndex;" + "in highp int gl_InstanceIndex;" + ); +#ifndef GLSLANG_WEB + if (version < 310) +#endif + stageBuiltins[EShLangVertex].append( + "highp vec4 gl_Position;" // needs qualifier fixed later + "highp float gl_PointSize;" // needs qualifier fixed later + ); +#ifndef GLSLANG_WEB + else + stageBuiltins[EShLangVertex].append( + "out gl_PerVertex {" + "highp vec4 gl_Position;" // needs qualifier fixed later + "highp float gl_PointSize;" // needs qualifier fixed later + "};" + ); + } + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangVertex].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + if (version >= 300 /* both ES and non-ES */) { + stageBuiltins[EShLangVertex].append( + "in highp uint gl_ViewID_OVR;" // GL_OVR_multiview, GL_OVR_multiview2 + "\n"); + } + + + //============================================================================ + // + // Define the interface to the geometry shader. + // + //============================================================================ + + if (profile == ECoreProfile || profile == ECompatibilityProfile) { + stageBuiltins[EShLangGeometry].append( + "in gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + ); + if (profile == ECompatibilityProfile) + stageBuiltins[EShLangGeometry].append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangGeometry].append( + "float gl_CullDistance[];" + "vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + stageBuiltins[EShLangGeometry].append( + "} gl_in[];" + + "in int gl_PrimitiveIDIn;" + "out gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + "\n"); + if (profile == ECompatibilityProfile && version >= 400) + stageBuiltins[EShLangGeometry].append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangGeometry].append( + "float gl_CullDistance[];" + ); + stageBuiltins[EShLangGeometry].append( + "};" + + "out int gl_PrimitiveID;" + "out int gl_Layer;"); + + if (version >= 150) + stageBuiltins[EShLangGeometry].append( + "out int gl_ViewportIndex;" + ); + + if (profile == ECompatibilityProfile && version < 400) + stageBuiltins[EShLangGeometry].append( + "out vec4 gl_ClipVertex;" + ); + + if (version >= 400) + stageBuiltins[EShLangGeometry].append( + "in int gl_InvocationID;" + ); + + if (version >= 450) + stageBuiltins[EShLangGeometry].append( + "out int gl_ViewportMask[];" // GL_NV_viewport_array2 + "out int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering + "out vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "out vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "out int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + + stageBuiltins[EShLangGeometry].append("\n"); + } else if (profile == EEsProfile && version >= 310) { + stageBuiltins[EShLangGeometry].append( + "in gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + "} gl_in[];" + "\n" + "in highp int gl_PrimitiveIDIn;" + "in highp int gl_InvocationID;" + "\n" + "out gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + "};" + "\n" + "out highp int gl_PrimitiveID;" + "out highp int gl_Layer;" + "\n" + ); + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangGeometry].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + //============================================================================ + // + // Define the interface to the tessellation control shader. + // + //============================================================================ + + if (profile != EEsProfile && version >= 150) { + // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below, + // as it depends on the resource sizing of gl_MaxPatchVertices. + + stageBuiltins[EShLangTessControl].append( + "in int gl_PatchVerticesIn;" + "in int gl_PrimitiveID;" + "in int gl_InvocationID;" + + "out gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + ); + if (profile == ECompatibilityProfile) + stageBuiltins[EShLangTessControl].append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangTessControl].append( + "float gl_CullDistance[];" + "int gl_ViewportMask[];" // GL_NV_viewport_array2 + "vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering + "vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + stageBuiltins[EShLangTessControl].append( + "} gl_out[];" + + "patch out float gl_TessLevelOuter[4];" + "patch out float gl_TessLevelInner[2];" + "\n"); + + if (version >= 410) + stageBuiltins[EShLangTessControl].append( + "out int gl_ViewportIndex;" + "out int gl_Layer;" + "\n"); + + } else { + // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below, + // as it depends on the resource sizing of gl_MaxPatchVertices. + + stageBuiltins[EShLangTessControl].append( + "in highp int gl_PatchVerticesIn;" + "in highp int gl_PrimitiveID;" + "in highp int gl_InvocationID;" + + "out gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + ); + stageBuiltins[EShLangTessControl].append( + "} gl_out[];" + + "patch out highp float gl_TessLevelOuter[4];" + "patch out highp float gl_TessLevelInner[2];" + "patch out highp vec4 gl_BoundingBoxOES[2];" + "patch out highp vec4 gl_BoundingBoxEXT[2];" + "\n"); + if (profile == EEsProfile && version >= 320) { + stageBuiltins[EShLangTessControl].append( + "patch out highp vec4 gl_BoundingBox[2];" + "\n" + ); + } + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangTessControl].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + //============================================================================ + // + // Define the interface to the tessellation evaluation shader. + // + //============================================================================ + + if (profile != EEsProfile && version >= 150) { + // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below, + // as it depends on the resource sizing of gl_MaxPatchVertices. + + stageBuiltins[EShLangTessEvaluation].append( + "in int gl_PatchVerticesIn;" + "in int gl_PrimitiveID;" + "in vec3 gl_TessCoord;" + + "patch in float gl_TessLevelOuter[4];" + "patch in float gl_TessLevelInner[2];" + + "out gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + ); + if (version >= 400 && profile == ECompatibilityProfile) + stageBuiltins[EShLangTessEvaluation].append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangTessEvaluation].append( + "float gl_CullDistance[];" + ); + stageBuiltins[EShLangTessEvaluation].append( + "};" + "\n"); + + if (version >= 410) + stageBuiltins[EShLangTessEvaluation].append( + "out int gl_ViewportIndex;" + "out int gl_Layer;" + "\n"); + + if (version >= 450) + stageBuiltins[EShLangTessEvaluation].append( + "out int gl_ViewportMask[];" // GL_NV_viewport_array2 + "out vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "out int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering + "out vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "out int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + + } else if (profile == EEsProfile && version >= 310) { + // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below, + // as it depends on the resource sizing of gl_MaxPatchVertices. + + stageBuiltins[EShLangTessEvaluation].append( + "in highp int gl_PatchVerticesIn;" + "in highp int gl_PrimitiveID;" + "in highp vec3 gl_TessCoord;" + + "patch in highp float gl_TessLevelOuter[4];" + "patch in highp float gl_TessLevelInner[2];" + + "out gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + ); + stageBuiltins[EShLangTessEvaluation].append( + "};" + "\n"); + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangTessEvaluation].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + //============================================================================ + // + // Define the interface to the fragment shader. + // + //============================================================================ + + if (profile != EEsProfile) { + + stageBuiltins[EShLangFragment].append( + "vec4 gl_FragCoord;" // needs qualifier fixed later + "bool gl_FrontFacing;" // needs qualifier fixed later + "float gl_FragDepth;" // needs qualifier fixed later + ); + if (version >= 120) + stageBuiltins[EShLangFragment].append( + "vec2 gl_PointCoord;" // needs qualifier fixed later + ); + if (version >= 140) + stageBuiltins[EShLangFragment].append( + "out int gl_FragStencilRefARB;" + ); + if (IncludeLegacy(version, profile, spvVersion) || (! ForwardCompatibility && version < 420)) + stageBuiltins[EShLangFragment].append( + "vec4 gl_FragColor;" // needs qualifier fixed later + ); + + if (version < 130) { + stageBuiltins[EShLangFragment].append( + "varying vec4 gl_Color;" + "varying vec4 gl_SecondaryColor;" + "varying vec4 gl_TexCoord[];" + "varying float gl_FogFragCoord;" + ); + } else { + stageBuiltins[EShLangFragment].append( + "in float gl_ClipDistance[];" + ); + + if (IncludeLegacy(version, profile, spvVersion)) { + if (version < 150) + stageBuiltins[EShLangFragment].append( + "in float gl_FogFragCoord;" + "in vec4 gl_TexCoord[];" + "in vec4 gl_Color;" + "in vec4 gl_SecondaryColor;" + ); + else + stageBuiltins[EShLangFragment].append( + "in gl_PerFragment {" + "in float gl_FogFragCoord;" + "in vec4 gl_TexCoord[];" + "in vec4 gl_Color;" + "in vec4 gl_SecondaryColor;" + "};" + ); + } + } + + if (version >= 150) + stageBuiltins[EShLangFragment].append( + "flat in int gl_PrimitiveID;" + ); + + if (version >= 130) { // ARB_sample_shading + stageBuiltins[EShLangFragment].append( + "flat in int gl_SampleID;" + " in vec2 gl_SamplePosition;" + " out int gl_SampleMask[];" + ); + + if (spvVersion.spv == 0) { + stageBuiltins[EShLangFragment].append( + "uniform int gl_NumSamples;" + ); + } + } + + if (version >= 400) + stageBuiltins[EShLangFragment].append( + "flat in int gl_SampleMaskIn[];" + ); + + if (version >= 430) + stageBuiltins[EShLangFragment].append( + "flat in int gl_Layer;" + "flat in int gl_ViewportIndex;" + ); + + if (version >= 450) + stageBuiltins[EShLangFragment].append( + "in float gl_CullDistance[];" + "bool gl_HelperInvocation;" // needs qualifier fixed later + ); + + if (version >= 450) + stageBuiltins[EShLangFragment].append( // GL_EXT_fragment_invocation_density + "flat in ivec2 gl_FragSizeEXT;" + "flat in int gl_FragInvocationCountEXT;" + ); + + if (version >= 450) + stageBuiltins[EShLangFragment].append( + "in vec2 gl_BaryCoordNoPerspAMD;" + "in vec2 gl_BaryCoordNoPerspCentroidAMD;" + "in vec2 gl_BaryCoordNoPerspSampleAMD;" + "in vec2 gl_BaryCoordSmoothAMD;" + "in vec2 gl_BaryCoordSmoothCentroidAMD;" + "in vec2 gl_BaryCoordSmoothSampleAMD;" + "in vec3 gl_BaryCoordPullModelAMD;" + ); + + if (version >= 430) + stageBuiltins[EShLangFragment].append( + "in bool gl_FragFullyCoveredNV;" + ); + if (version >= 450) + stageBuiltins[EShLangFragment].append( + "flat in ivec2 gl_FragmentSizeNV;" // GL_NV_shading_rate_image + "flat in int gl_InvocationsPerPixelNV;" + "in vec3 gl_BaryCoordNV;" // GL_NV_fragment_shader_barycentric + "in vec3 gl_BaryCoordNoPerspNV;" + ); + + } else { + // ES profile + + if (version == 100) { + stageBuiltins[EShLangFragment].append( + "mediump vec4 gl_FragCoord;" // needs qualifier fixed later + " bool gl_FrontFacing;" // needs qualifier fixed later + "mediump vec4 gl_FragColor;" // needs qualifier fixed later + "mediump vec2 gl_PointCoord;" // needs qualifier fixed later + ); + } +#endif + if (version >= 300) { + stageBuiltins[EShLangFragment].append( + "highp vec4 gl_FragCoord;" // needs qualifier fixed later + " bool gl_FrontFacing;" // needs qualifier fixed later + "mediump vec2 gl_PointCoord;" // needs qualifier fixed later + "highp float gl_FragDepth;" // needs qualifier fixed later + ); + } +#ifndef GLSLANG_WEB + if (version >= 310) { + stageBuiltins[EShLangFragment].append( + "bool gl_HelperInvocation;" // needs qualifier fixed later + "flat in highp int gl_PrimitiveID;" // needs qualifier fixed later + "flat in highp int gl_Layer;" // needs qualifier fixed later + ); + + stageBuiltins[EShLangFragment].append( // GL_OES_sample_variables + "flat in lowp int gl_SampleID;" + " in mediump vec2 gl_SamplePosition;" + "flat in highp int gl_SampleMaskIn[];" + " out highp int gl_SampleMask[];" + ); + if (spvVersion.spv == 0) + stageBuiltins[EShLangFragment].append( // GL_OES_sample_variables + "uniform lowp int gl_NumSamples;" + ); + } + stageBuiltins[EShLangFragment].append( + "highp float gl_FragDepthEXT;" // GL_EXT_frag_depth + ); + + if (version >= 310) + stageBuiltins[EShLangFragment].append( // GL_EXT_fragment_invocation_density + "flat in ivec2 gl_FragSizeEXT;" + "flat in int gl_FragInvocationCountEXT;" + ); + if (version >= 320) + stageBuiltins[EShLangFragment].append( // GL_NV_shading_rate_image + "flat in ivec2 gl_FragmentSizeNV;" + "flat in int gl_InvocationsPerPixelNV;" + ); + if (version >= 320) + stageBuiltins[EShLangFragment].append( + "in vec3 gl_BaryCoordNV;" + "in vec3 gl_BaryCoordNoPerspNV;" + ); + } +#endif + + stageBuiltins[EShLangFragment].append("\n"); + + if (version >= 130) + add2ndGenerationSamplingImaging(version, profile, spvVersion); + +#ifndef GLSLANG_WEB + + // GL_ARB_shader_ballot + if (profile != EEsProfile && version >= 450) { + const char* ballotDecls = + "uniform uint gl_SubGroupSizeARB;" + "in uint gl_SubGroupInvocationARB;" + "in uint64_t gl_SubGroupEqMaskARB;" + "in uint64_t gl_SubGroupGeMaskARB;" + "in uint64_t gl_SubGroupGtMaskARB;" + "in uint64_t gl_SubGroupLeMaskARB;" + "in uint64_t gl_SubGroupLtMaskARB;" + "\n"; + const char* fragmentBallotDecls = + "uniform uint gl_SubGroupSizeARB;" + "flat in uint gl_SubGroupInvocationARB;" + "flat in uint64_t gl_SubGroupEqMaskARB;" + "flat in uint64_t gl_SubGroupGeMaskARB;" + "flat in uint64_t gl_SubGroupGtMaskARB;" + "flat in uint64_t gl_SubGroupLeMaskARB;" + "flat in uint64_t gl_SubGroupLtMaskARB;" + "\n"; + stageBuiltins[EShLangVertex] .append(ballotDecls); + stageBuiltins[EShLangTessControl] .append(ballotDecls); + stageBuiltins[EShLangTessEvaluation].append(ballotDecls); + stageBuiltins[EShLangGeometry] .append(ballotDecls); + stageBuiltins[EShLangCompute] .append(ballotDecls); + stageBuiltins[EShLangFragment] .append(fragmentBallotDecls); + stageBuiltins[EShLangMeshNV] .append(ballotDecls); + stageBuiltins[EShLangTaskNV] .append(ballotDecls); + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangFragment].append( + "flat in highp int gl_DeviceIndex;" // GL_EXT_device_group + "flat in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + const char* subgroupDecls = + "in mediump uint gl_SubgroupSize;" + "in mediump uint gl_SubgroupInvocationID;" + "in highp uvec4 gl_SubgroupEqMask;" + "in highp uvec4 gl_SubgroupGeMask;" + "in highp uvec4 gl_SubgroupGtMask;" + "in highp uvec4 gl_SubgroupLeMask;" + "in highp uvec4 gl_SubgroupLtMask;" + // GL_NV_shader_sm_builtins + "in highp uint gl_WarpsPerSMNV;" + "in highp uint gl_SMCountNV;" + "in highp uint gl_WarpIDNV;" + "in highp uint gl_SMIDNV;" + "\n"; + const char* fragmentSubgroupDecls = + "flat in mediump uint gl_SubgroupSize;" + "flat in mediump uint gl_SubgroupInvocationID;" + "flat in highp uvec4 gl_SubgroupEqMask;" + "flat in highp uvec4 gl_SubgroupGeMask;" + "flat in highp uvec4 gl_SubgroupGtMask;" + "flat in highp uvec4 gl_SubgroupLeMask;" + "flat in highp uvec4 gl_SubgroupLtMask;" + // GL_NV_shader_sm_builtins + "flat in highp uint gl_WarpsPerSMNV;" + "flat in highp uint gl_SMCountNV;" + "flat in highp uint gl_WarpIDNV;" + "flat in highp uint gl_SMIDNV;" + "\n"; + const char* computeSubgroupDecls = + "in highp uint gl_NumSubgroups;" + "in highp uint gl_SubgroupID;" + "\n"; + + stageBuiltins[EShLangVertex] .append(subgroupDecls); + stageBuiltins[EShLangTessControl] .append(subgroupDecls); + stageBuiltins[EShLangTessEvaluation].append(subgroupDecls); + stageBuiltins[EShLangGeometry] .append(subgroupDecls); + stageBuiltins[EShLangCompute] .append(subgroupDecls); + stageBuiltins[EShLangCompute] .append(computeSubgroupDecls); + stageBuiltins[EShLangFragment] .append(fragmentSubgroupDecls); + stageBuiltins[EShLangMeshNV] .append(subgroupDecls); + stageBuiltins[EShLangMeshNV] .append(computeSubgroupDecls); + stageBuiltins[EShLangTaskNV] .append(subgroupDecls); + stageBuiltins[EShLangTaskNV] .append(computeSubgroupDecls); + stageBuiltins[EShLangRayGen] .append(subgroupDecls); + stageBuiltins[EShLangIntersect] .append(subgroupDecls); + stageBuiltins[EShLangAnyHit] .append(subgroupDecls); + stageBuiltins[EShLangClosestHit] .append(subgroupDecls); + stageBuiltins[EShLangMiss] .append(subgroupDecls); + stageBuiltins[EShLangCallable] .append(subgroupDecls); + } + + // GL_NV_ray_tracing/GL_EXT_ray_tracing + if (profile != EEsProfile && version >= 460) { + + const char *constRayFlags = + "const uint gl_RayFlagsNoneNV = 0U;" + "const uint gl_RayFlagsNoneEXT = 0U;" + "const uint gl_RayFlagsOpaqueNV = 1U;" + "const uint gl_RayFlagsOpaqueEXT = 1U;" + "const uint gl_RayFlagsNoOpaqueNV = 2U;" + "const uint gl_RayFlagsNoOpaqueEXT = 2U;" + "const uint gl_RayFlagsTerminateOnFirstHitNV = 4U;" + "const uint gl_RayFlagsTerminateOnFirstHitEXT = 4U;" + "const uint gl_RayFlagsSkipClosestHitShaderNV = 8U;" + "const uint gl_RayFlagsSkipClosestHitShaderEXT = 8U;" + "const uint gl_RayFlagsCullBackFacingTrianglesNV = 16U;" + "const uint gl_RayFlagsCullBackFacingTrianglesEXT = 16U;" + "const uint gl_RayFlagsCullFrontFacingTrianglesNV = 32U;" + "const uint gl_RayFlagsCullFrontFacingTrianglesEXT = 32U;" + "const uint gl_RayFlagsCullOpaqueNV = 64U;" + "const uint gl_RayFlagsCullOpaqueEXT = 64U;" + "const uint gl_RayFlagsCullNoOpaqueNV = 128U;" + "const uint gl_RayFlagsCullNoOpaqueEXT = 128U;" + "const uint gl_RayFlagsSkipTrianglesEXT = 256U;" + "const uint gl_RayFlagsSkipAABBEXT = 512U;" + "const uint gl_HitKindFrontFacingTriangleEXT = 254U;" + "const uint gl_HitKindBackFacingTriangleEXT = 255U;" + "\n"; + + const char *constRayQueryIntersection = + "const uint gl_RayQueryCandidateIntersectionEXT = 0U;" + "const uint gl_RayQueryCommittedIntersectionEXT = 1U;" + "const uint gl_RayQueryCommittedIntersectionNoneEXT = 0U;" + "const uint gl_RayQueryCommittedIntersectionTriangleEXT = 1U;" + "const uint gl_RayQueryCommittedIntersectionGeneratedEXT = 2U;" + "const uint gl_RayQueryCandidateIntersectionTriangleEXT = 0U;" + "const uint gl_RayQueryCandidateIntersectionAABBEXT = 1U;" + "\n"; + + const char *rayGenDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "\n"; + const char *intersectDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "in int gl_PrimitiveID;" + "in int gl_InstanceID;" + "in int gl_InstanceCustomIndexNV;" + "in int gl_InstanceCustomIndexEXT;" + "in int gl_GeometryIndexEXT;" + "in vec3 gl_WorldRayOriginNV;" + "in vec3 gl_WorldRayOriginEXT;" + "in vec3 gl_WorldRayDirectionNV;" + "in vec3 gl_WorldRayDirectionEXT;" + "in vec3 gl_ObjectRayOriginNV;" + "in vec3 gl_ObjectRayOriginEXT;" + "in vec3 gl_ObjectRayDirectionNV;" + "in vec3 gl_ObjectRayDirectionEXT;" + "in float gl_RayTminNV;" + "in float gl_RayTminEXT;" + "in float gl_RayTmaxNV;" + "in float gl_RayTmaxEXT;" + "in mat4x3 gl_ObjectToWorldNV;" + "in mat4x3 gl_ObjectToWorldEXT;" + "in mat3x4 gl_ObjectToWorld3x4EXT;" + "in mat4x3 gl_WorldToObjectNV;" + "in mat4x3 gl_WorldToObjectEXT;" + "in mat3x4 gl_WorldToObject3x4EXT;" + "in uint gl_IncomingRayFlagsNV;" + "in uint gl_IncomingRayFlagsEXT;" + "\n"; + const char *hitDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "in int gl_PrimitiveID;" + "in int gl_InstanceID;" + "in int gl_InstanceCustomIndexNV;" + "in int gl_InstanceCustomIndexEXT;" + "in int gl_GeometryIndexEXT;" + "in vec3 gl_WorldRayOriginNV;" + "in vec3 gl_WorldRayOriginEXT;" + "in vec3 gl_WorldRayDirectionNV;" + "in vec3 gl_WorldRayDirectionEXT;" + "in vec3 gl_ObjectRayOriginNV;" + "in vec3 gl_ObjectRayOriginEXT;" + "in vec3 gl_ObjectRayDirectionNV;" + "in vec3 gl_ObjectRayDirectionEXT;" + "in float gl_RayTminNV;" + "in float gl_RayTminEXT;" + "in float gl_RayTmaxNV;" + "in float gl_RayTmaxEXT;" + "in float gl_HitTNV;" + "in float gl_HitTEXT;" + "in uint gl_HitKindNV;" + "in uint gl_HitKindEXT;" + "in mat4x3 gl_ObjectToWorldNV;" + "in mat4x3 gl_ObjectToWorldEXT;" + "in mat3x4 gl_ObjectToWorld3x4EXT;" + "in mat4x3 gl_WorldToObjectNV;" + "in mat4x3 gl_WorldToObjectEXT;" + "in mat3x4 gl_WorldToObject3x4EXT;" + "in uint gl_IncomingRayFlagsNV;" + "in uint gl_IncomingRayFlagsEXT;" + "\n"; + const char *missDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "in vec3 gl_WorldRayOriginNV;" + "in vec3 gl_WorldRayOriginEXT;" + "in vec3 gl_WorldRayDirectionNV;" + "in vec3 gl_WorldRayDirectionEXT;" + "in vec3 gl_ObjectRayOriginNV;" + "in vec3 gl_ObjectRayDirectionNV;" + "in float gl_RayTminNV;" + "in float gl_RayTminEXT;" + "in float gl_RayTmaxNV;" + "in float gl_RayTmaxEXT;" + "in uint gl_IncomingRayFlagsNV;" + "in uint gl_IncomingRayFlagsEXT;" + "\n"; + + const char *callableDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "\n"; + + + commonBuiltins.append(constRayQueryIntersection); + commonBuiltins.append(constRayFlags); + + stageBuiltins[EShLangRayGen].append(rayGenDecls); + stageBuiltins[EShLangIntersect].append(intersectDecls); + stageBuiltins[EShLangAnyHit].append(hitDecls); + stageBuiltins[EShLangClosestHit].append(hitDecls); + stageBuiltins[EShLangMiss].append(missDecls); + stageBuiltins[EShLangCallable].append(callableDecls); + + } + if ((profile != EEsProfile && version >= 140)) { + const char *deviceIndex = + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "\n"; + + stageBuiltins[EShLangRayGen].append(deviceIndex); + stageBuiltins[EShLangIntersect].append(deviceIndex); + stageBuiltins[EShLangAnyHit].append(deviceIndex); + stageBuiltins[EShLangClosestHit].append(deviceIndex); + stageBuiltins[EShLangMiss].append(deviceIndex); + } + + if (version >= 300 /* both ES and non-ES */) { + stageBuiltins[EShLangFragment].append( + "flat in highp uint gl_ViewID_OVR;" // GL_OVR_multiview, GL_OVR_multiview2 + "\n"); + } + + if ((profile != EEsProfile && version >= 420) || + (profile == EEsProfile && version >= 310)) { + commonBuiltins.append("const int gl_ScopeDevice = 1;\n"); + commonBuiltins.append("const int gl_ScopeWorkgroup = 2;\n"); + commonBuiltins.append("const int gl_ScopeSubgroup = 3;\n"); + commonBuiltins.append("const int gl_ScopeInvocation = 4;\n"); + commonBuiltins.append("const int gl_ScopeQueueFamily = 5;\n"); + commonBuiltins.append("const int gl_ScopeShaderCallEXT = 6;\n"); + + commonBuiltins.append("const int gl_SemanticsRelaxed = 0x0;\n"); + commonBuiltins.append("const int gl_SemanticsAcquire = 0x2;\n"); + commonBuiltins.append("const int gl_SemanticsRelease = 0x4;\n"); + commonBuiltins.append("const int gl_SemanticsAcquireRelease = 0x8;\n"); + commonBuiltins.append("const int gl_SemanticsMakeAvailable = 0x2000;\n"); + commonBuiltins.append("const int gl_SemanticsMakeVisible = 0x4000;\n"); + commonBuiltins.append("const int gl_SemanticsVolatile = 0x8000;\n"); + + commonBuiltins.append("const int gl_StorageSemanticsNone = 0x0;\n"); + commonBuiltins.append("const int gl_StorageSemanticsBuffer = 0x40;\n"); + commonBuiltins.append("const int gl_StorageSemanticsShared = 0x100;\n"); + commonBuiltins.append("const int gl_StorageSemanticsImage = 0x800;\n"); + commonBuiltins.append("const int gl_StorageSemanticsOutput = 0x1000;\n"); + } +#endif + + // printf("%s\n", commonBuiltins.c_str()); + // printf("%s\n", stageBuiltins[EShLangFragment].c_str()); +} + +// +// Helper function for initialize(), to add the second set of names for texturing, +// when adding context-independent built-in functions. +// +void TBuiltIns::add2ndGenerationSamplingImaging(int version, EProfile profile, const SpvVersion& spvVersion) +{ + // + // In this function proper, enumerate the types, then calls the next set of functions + // to enumerate all the uses for that type. + // + + // enumerate all the types +#ifdef GLSLANG_WEB + const TBasicType bTypes[] = { EbtFloat, EbtInt, EbtUint }; + bool skipBuffer = true; + bool skipCubeArrayed = true; + const int image = 0; +#else + const TBasicType bTypes[] = { EbtFloat, EbtInt, EbtUint, EbtFloat16 }; + bool skipBuffer = (profile == EEsProfile && version < 310) || (profile != EEsProfile && version < 140); + bool skipCubeArrayed = (profile == EEsProfile && version < 310) || (profile != EEsProfile && version < 130); + for (int image = 0; image <= 1; ++image) // loop over "bool" image vs sampler +#endif + { + for (int shadow = 0; shadow <= 1; ++shadow) { // loop over "bool" shadow or not +#ifdef GLSLANG_WEB + const int ms = 0; +#else + for (int ms = 0; ms <= 1; ++ms) // loop over "bool" multisample or not +#endif + { + if ((ms || image) && shadow) + continue; + if (ms && profile != EEsProfile && version < 150) + continue; + if (ms && image && profile == EEsProfile) + continue; + if (ms && profile == EEsProfile && version < 310) + continue; + + for (int arrayed = 0; arrayed <= 1; ++arrayed) { // loop over "bool" arrayed or not +#ifdef GLSLANG_WEB + for (int dim = Esd2D; dim <= EsdCube; ++dim) { // 2D, 3D, and Cube +#else + for (int dim = Esd1D; dim < EsdNumDims; ++dim) { // 1D, ..., buffer, subpass + if (dim == EsdSubpass && spvVersion.vulkan == 0) + continue; + if (dim == EsdSubpass && (image || shadow || arrayed)) + continue; + if ((dim == Esd1D || dim == EsdRect) && profile == EEsProfile) + continue; + if (dim == EsdSubpass && spvVersion.vulkan == 0) + continue; + if (dim == EsdSubpass && (image || shadow || arrayed)) + continue; + if ((dim == Esd1D || dim == EsdRect) && profile == EEsProfile) + continue; + if (dim != Esd2D && dim != EsdSubpass && ms) + continue; + if (dim == EsdBuffer && skipBuffer) + continue; + if (dim == EsdBuffer && (shadow || arrayed || ms)) + continue; + if (ms && arrayed && profile == EEsProfile && version < 310) + continue; +#endif + if (dim == Esd3D && shadow) + continue; + if (dim == EsdCube && arrayed && skipCubeArrayed) + continue; + if ((dim == Esd3D || dim == EsdRect) && arrayed) + continue; + + // Loop over the bTypes + for (size_t bType = 0; bType < sizeof(bTypes)/sizeof(TBasicType); ++bType) { +#ifndef GLSLANG_WEB + if (bTypes[bType] == EbtFloat16 && (profile == EEsProfile || version < 450)) + continue; + if (dim == EsdRect && version < 140 && bType > 0) + continue; +#endif + if (shadow && (bTypes[bType] == EbtInt || bTypes[bType] == EbtUint)) + continue; + + // + // Now, make all the function prototypes for the type we just built... + // + TSampler sampler; +#ifndef GLSLANG_WEB + if (dim == EsdSubpass) { + sampler.setSubpass(bTypes[bType], ms ? true : false); + } else +#endif + if (image) { + sampler.setImage(bTypes[bType], (TSamplerDim)dim, arrayed ? true : false, + shadow ? true : false, + ms ? true : false); + } else { + sampler.set(bTypes[bType], (TSamplerDim)dim, arrayed ? true : false, + shadow ? true : false, + ms ? true : false); + } + + TString typeName = sampler.getString(); + +#ifndef GLSLANG_WEB + if (dim == EsdSubpass) { + addSubpassSampling(sampler, typeName, version, profile); + continue; + } +#endif + + addQueryFunctions(sampler, typeName, version, profile); + + if (image) + addImageFunctions(sampler, typeName, version, profile); + else { + addSamplingFunctions(sampler, typeName, version, profile); +#ifndef GLSLANG_WEB + addGatherFunctions(sampler, typeName, version, profile); + if (spvVersion.vulkan > 0 && sampler.isCombined() && !sampler.shadow) { + // Base Vulkan allows texelFetch() for + // textureBuffer (i.e. without sampler). + // + // GL_EXT_samplerless_texture_functions + // allows texelFetch() and query functions + // (other than textureQueryLod()) for all + // texture types. + sampler.setTexture(sampler.type, sampler.dim, sampler.arrayed, sampler.shadow, + sampler.ms); + TString textureTypeName = sampler.getString(); + addSamplingFunctions(sampler, textureTypeName, version, profile); + addQueryFunctions(sampler, textureTypeName, version, profile); + } +#endif + } + } + } + } + } + } + } + + // + // sparseTexelsResidentARB() + // + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append("bool sparseTexelsResidentARB(int code);\n"); + } +} + +// +// Helper function for add2ndGenerationSamplingImaging(), +// when adding context-independent built-in functions. +// +// Add all the query functions for the given type. +// +void TBuiltIns::addQueryFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile) +{ + // + // textureSize() and imageSize() + // + + int sizeDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0) - (sampler.dim == EsdCube ? 1 : 0); + +#ifdef GLSLANG_WEB + commonBuiltins.append("highp "); + commonBuiltins.append("ivec"); + commonBuiltins.append(postfixes[sizeDims]); + commonBuiltins.append(" textureSize("); + commonBuiltins.append(typeName); + commonBuiltins.append(",int);\n"); + return; +#endif + + if (sampler.isImage() && ((profile == EEsProfile && version < 310) || (profile != EEsProfile && version < 420))) + return; + + if (profile == EEsProfile) + commonBuiltins.append("highp "); + if (sizeDims == 1) + commonBuiltins.append("int"); + else { + commonBuiltins.append("ivec"); + commonBuiltins.append(postfixes[sizeDims]); + } + if (sampler.isImage()) + commonBuiltins.append(" imageSize(readonly writeonly volatile coherent "); + else + commonBuiltins.append(" textureSize("); + commonBuiltins.append(typeName); + if (! sampler.isImage() && ! sampler.isRect() && ! sampler.isBuffer() && ! sampler.isMultiSample()) + commonBuiltins.append(",int);\n"); + else + commonBuiltins.append(");\n"); + + // + // textureSamples() and imageSamples() + // + + // GL_ARB_shader_texture_image_samples + // TODO: spec issue? there are no memory qualifiers; how to query a writeonly/readonly image, etc? + if (profile != EEsProfile && version >= 430 && sampler.isMultiSample()) { + commonBuiltins.append("int "); + if (sampler.isImage()) + commonBuiltins.append("imageSamples(readonly writeonly volatile coherent "); + else + commonBuiltins.append("textureSamples("); + commonBuiltins.append(typeName); + commonBuiltins.append(");\n"); + } + + // + // textureQueryLod(), fragment stage only + // Also enabled with extension GL_ARB_texture_query_lod + + if (profile != EEsProfile && version >= 150 && sampler.isCombined() && sampler.dim != EsdRect && + ! sampler.isMultiSample() && ! sampler.isBuffer()) { + for (int f16TexAddr = 0; f16TexAddr < 2; ++f16TexAddr) { + if (f16TexAddr && sampler.type != EbtFloat16) + continue; + stageBuiltins[EShLangFragment].append("vec2 textureQueryLod("); + stageBuiltins[EShLangFragment].append(typeName); + if (dimMap[sampler.dim] == 1) + if (f16TexAddr) + stageBuiltins[EShLangFragment].append(", float16_t"); + else + stageBuiltins[EShLangFragment].append(", float"); + else { + if (f16TexAddr) + stageBuiltins[EShLangFragment].append(", f16vec"); + else + stageBuiltins[EShLangFragment].append(", vec"); + stageBuiltins[EShLangFragment].append(postfixes[dimMap[sampler.dim]]); + } + stageBuiltins[EShLangFragment].append(");\n"); + } + + stageBuiltins[EShLangCompute].append("vec2 textureQueryLod("); + stageBuiltins[EShLangCompute].append(typeName); + if (dimMap[sampler.dim] == 1) + stageBuiltins[EShLangCompute].append(", float"); + else { + stageBuiltins[EShLangCompute].append(", vec"); + stageBuiltins[EShLangCompute].append(postfixes[dimMap[sampler.dim]]); + } + stageBuiltins[EShLangCompute].append(");\n"); + } + + // + // textureQueryLevels() + // + + if (profile != EEsProfile && version >= 430 && ! sampler.isImage() && sampler.dim != EsdRect && + ! sampler.isMultiSample() && ! sampler.isBuffer()) { + commonBuiltins.append("int textureQueryLevels("); + commonBuiltins.append(typeName); + commonBuiltins.append(");\n"); + } +} + +// +// Helper function for add2ndGenerationSamplingImaging(), +// when adding context-independent built-in functions. +// +// Add all the image access functions for the given type. +// +void TBuiltIns::addImageFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile) +{ + int dims = dimMap[sampler.dim]; + // most things with an array add a dimension, except for cubemaps + if (sampler.arrayed && sampler.dim != EsdCube) + ++dims; + + TString imageParams = typeName; + if (dims == 1) + imageParams.append(", int"); + else { + imageParams.append(", ivec"); + imageParams.append(postfixes[dims]); + } + if (sampler.isMultiSample()) + imageParams.append(", int"); + + if (profile == EEsProfile) + commonBuiltins.append("highp "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4 imageLoad(readonly volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(");\n"); + + commonBuiltins.append("void imageStore(writeonly volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4);\n"); + + if (! sampler.is1D() && ! sampler.isBuffer() && profile != EEsProfile && version >= 450) { + commonBuiltins.append("int sparseImageLoadARB(readonly volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", out "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4"); + commonBuiltins.append(");\n"); + } + + if ( profile != EEsProfile || + (profile == EEsProfile && version >= 310)) { + if (sampler.type == EbtInt || sampler.type == EbtUint) { + const char* dataType = sampler.type == EbtInt ? "highp int" : "highp uint"; + + const int numBuiltins = 7; + + static const char* atomicFunc[numBuiltins] = { + " imageAtomicAdd(volatile coherent ", + " imageAtomicMin(volatile coherent ", + " imageAtomicMax(volatile coherent ", + " imageAtomicAnd(volatile coherent ", + " imageAtomicOr(volatile coherent ", + " imageAtomicXor(volatile coherent ", + " imageAtomicExchange(volatile coherent " + }; + + // Loop twice to add prototypes with/without scope/semantics + for (int j = 0; j < 2; ++j) { + for (size_t i = 0; i < numBuiltins; ++i) { + commonBuiltins.append(dataType); + commonBuiltins.append(atomicFunc[i]); + commonBuiltins.append(imageParams); + commonBuiltins.append(", "); + commonBuiltins.append(dataType); + if (j == 1) { + commonBuiltins.append(", int, int, int"); + } + commonBuiltins.append(");\n"); + } + + commonBuiltins.append(dataType); + commonBuiltins.append(" imageAtomicCompSwap(volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", "); + commonBuiltins.append(dataType); + commonBuiltins.append(", "); + commonBuiltins.append(dataType); + if (j == 1) { + commonBuiltins.append(", int, int, int, int, int"); + } + commonBuiltins.append(");\n"); + } + + commonBuiltins.append(dataType); + commonBuiltins.append(" imageAtomicLoad(volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", int, int, int);\n"); + + commonBuiltins.append("void imageAtomicStore(volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", "); + commonBuiltins.append(dataType); + commonBuiltins.append(", int, int, int);\n"); + + } else { + // not int or uint + // GL_ARB_ES3_1_compatibility + // TODO: spec issue: are there restrictions on the kind of layout() that can be used? what about dropping memory qualifiers? + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 310)) { + commonBuiltins.append("float imageAtomicExchange(volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float);\n"); + } + } + } + + if (sampler.dim == EsdRect || sampler.dim == EsdBuffer || sampler.shadow || sampler.isMultiSample()) + return; + + if (profile == EEsProfile || version < 450) + return; + + TString imageLodParams = typeName; + if (dims == 1) + imageLodParams.append(", int"); + else { + imageLodParams.append(", ivec"); + imageLodParams.append(postfixes[dims]); + } + imageLodParams.append(", int"); + + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4 imageLoadLodAMD(readonly volatile coherent "); + commonBuiltins.append(imageLodParams); + commonBuiltins.append(");\n"); + + commonBuiltins.append("void imageStoreLodAMD(writeonly volatile coherent "); + commonBuiltins.append(imageLodParams); + commonBuiltins.append(", "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4);\n"); + + if (! sampler.is1D()) { + commonBuiltins.append("int sparseImageLoadLodAMD(readonly volatile coherent "); + commonBuiltins.append(imageLodParams); + commonBuiltins.append(", out "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4"); + commonBuiltins.append(");\n"); + } +} + +// +// Helper function for initialize(), +// when adding context-independent built-in functions. +// +// Add all the subpass access functions for the given type. +// +void TBuiltIns::addSubpassSampling(TSampler sampler, const TString& typeName, int /*version*/, EProfile /*profile*/) +{ + stageBuiltins[EShLangFragment].append(prefixes[sampler.type]); + stageBuiltins[EShLangFragment].append("vec4 subpassLoad"); + stageBuiltins[EShLangFragment].append("("); + stageBuiltins[EShLangFragment].append(typeName.c_str()); + if (sampler.isMultiSample()) + stageBuiltins[EShLangFragment].append(", int"); + stageBuiltins[EShLangFragment].append(");\n"); +} + +// +// Helper function for add2ndGenerationSamplingImaging(), +// when adding context-independent built-in functions. +// +// Add all the texture lookup functions for the given type. +// +void TBuiltIns::addSamplingFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile) +{ +#ifdef GLSLANG_WEB + profile = EEsProfile; + version = 310; +#endif + + // + // texturing + // + for (int proj = 0; proj <= 1; ++proj) { // loop over "bool" projective or not + + if (proj && (sampler.dim == EsdCube || sampler.isBuffer() || sampler.arrayed || sampler.isMultiSample() + || !sampler.isCombined())) + continue; + + for (int lod = 0; lod <= 1; ++lod) { + + if (lod && (sampler.isBuffer() || sampler.isRect() || sampler.isMultiSample() || !sampler.isCombined())) + continue; + if (lod && sampler.dim == Esd2D && sampler.arrayed && sampler.shadow) + continue; + if (lod && sampler.dim == EsdCube && sampler.shadow) + continue; + + for (int bias = 0; bias <= 1; ++bias) { + + if (bias && (lod || sampler.isMultiSample() || !sampler.isCombined())) + continue; + if (bias && (sampler.dim == Esd2D || sampler.dim == EsdCube) && sampler.shadow && sampler.arrayed) + continue; + if (bias && (sampler.isRect() || sampler.isBuffer())) + continue; + + for (int offset = 0; offset <= 1; ++offset) { // loop over "bool" offset or not + + if (proj + offset + bias + lod > 3) + continue; + if (offset && (sampler.dim == EsdCube || sampler.isBuffer() || sampler.isMultiSample())) + continue; + + for (int fetch = 0; fetch <= 1; ++fetch) { // loop over "bool" fetch or not + + if (proj + offset + fetch + bias + lod > 3) + continue; + if (fetch && (lod || bias)) + continue; + if (fetch && (sampler.shadow || sampler.dim == EsdCube)) + continue; + if (fetch == 0 && (sampler.isMultiSample() || sampler.isBuffer() + || !sampler.isCombined())) + continue; + + for (int grad = 0; grad <= 1; ++grad) { // loop over "bool" grad or not + + if (grad && (lod || bias || sampler.isMultiSample() || !sampler.isCombined())) + continue; + if (grad && sampler.isBuffer()) + continue; + if (proj + offset + fetch + grad + bias + lod > 3) + continue; + + for (int extraProj = 0; extraProj <= 1; ++extraProj) { + bool compare = false; + int totalDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0); + // skip dummy unused second component for 1D non-array shadows + if (sampler.shadow && totalDims < 2) + totalDims = 2; + totalDims += (sampler.shadow ? 1 : 0) + proj; + if (totalDims > 4 && sampler.shadow) { + compare = true; + totalDims = 4; + } + assert(totalDims <= 4); + + if (extraProj && ! proj) + continue; + if (extraProj && (sampler.dim == Esd3D || sampler.shadow || !sampler.isCombined())) + continue; + + // loop over 16-bit floating-point texel addressing +#ifdef GLSLANG_WEB + const int f16TexAddr = 0; +#else + for (int f16TexAddr = 0; f16TexAddr <= 1; ++f16TexAddr) +#endif + { + if (f16TexAddr && sampler.type != EbtFloat16) + continue; + if (f16TexAddr && sampler.shadow && ! compare) { + compare = true; // compare argument is always present + totalDims--; + } + // loop over "bool" lod clamp +#ifdef GLSLANG_WEB + const int lodClamp = 0; +#else + for (int lodClamp = 0; lodClamp <= 1 ;++lodClamp) +#endif + { + if (lodClamp && (profile == EEsProfile || version < 450)) + continue; + if (lodClamp && (proj || lod || fetch)) + continue; + + // loop over "bool" sparse or not +#ifdef GLSLANG_WEB + const int sparse = 0; +#else + for (int sparse = 0; sparse <= 1; ++sparse) +#endif + { + if (sparse && (profile == EEsProfile || version < 450)) + continue; + // Sparse sampling is not for 1D/1D array texture, buffer texture, and + // projective texture + if (sparse && (sampler.is1D() || sampler.isBuffer() || proj)) + continue; + + TString s; + + // return type + if (sparse) + s.append("int "); + else { + if (sampler.shadow) + if (sampler.type == EbtFloat16) + s.append("float16_t "); + else + s.append("float "); + else { + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + } + + // name + if (sparse) { + if (fetch) + s.append("sparseTexel"); + else + s.append("sparseTexture"); + } + else { + if (fetch) + s.append("texel"); + else + s.append("texture"); + } + if (proj) + s.append("Proj"); + if (lod) + s.append("Lod"); + if (grad) + s.append("Grad"); + if (fetch) + s.append("Fetch"); + if (offset) + s.append("Offset"); + if (lodClamp) + s.append("Clamp"); + if (lodClamp || sparse) + s.append("ARB"); + s.append("("); + + // sampler type + s.append(typeName); + // P coordinate + if (extraProj) { + if (f16TexAddr) + s.append(",f16vec4"); + else + s.append(",vec4"); + } else { + s.append(","); + TBasicType t = fetch ? EbtInt : (f16TexAddr ? EbtFloat16 : EbtFloat); + if (totalDims == 1) + s.append(TType::getBasicString(t)); + else { + s.append(prefixes[t]); + s.append("vec"); + s.append(postfixes[totalDims]); + } + } + // non-optional compare + if (compare) + s.append(",float"); + + // non-optional lod argument (lod that's not driven by lod loop) or sample + if ((fetch && !sampler.isBuffer() && + !sampler.isRect() && !sampler.isMultiSample()) + || (sampler.isMultiSample() && fetch)) + s.append(",int"); + // non-optional lod + if (lod) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + + // gradient arguments + if (grad) { + if (dimMap[sampler.dim] == 1) { + if (f16TexAddr) + s.append(",float16_t,float16_t"); + else + s.append(",float,float"); + } else { + if (f16TexAddr) + s.append(",f16vec"); + else + s.append(",vec"); + s.append(postfixes[dimMap[sampler.dim]]); + if (f16TexAddr) + s.append(",f16vec"); + else + s.append(",vec"); + s.append(postfixes[dimMap[sampler.dim]]); + } + } + // offset + if (offset) { + if (dimMap[sampler.dim] == 1) + s.append(",int"); + else { + s.append(",ivec"); + s.append(postfixes[dimMap[sampler.dim]]); + } + } + + // lod clamp + if (lodClamp) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + // texel out (for sparse texture) + if (sparse) { + s.append(",out "); + if (sampler.shadow) + if (sampler.type == EbtFloat16) + s.append("float16_t"); + else + s.append("float"); + else { + s.append(prefixes[sampler.type]); + s.append("vec4"); + } + } + // optional bias + if (bias) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + s.append(");\n"); + + // Add to the per-language set of built-ins + if (bias || lodClamp) { + stageBuiltins[EShLangFragment].append(s); + stageBuiltins[EShLangCompute].append(s); + } else + commonBuiltins.append(s); + + } + } + } + } + } + } + } + } + } + } +} + +// +// Helper function for add2ndGenerationSamplingImaging(), +// when adding context-independent built-in functions. +// +// Add all the texture gather functions for the given type. +// +void TBuiltIns::addGatherFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile) +{ +#ifdef GLSLANG_WEB + profile = EEsProfile; + version = 310; +#endif + + switch (sampler.dim) { + case Esd2D: + case EsdRect: + case EsdCube: + break; + default: + return; + } + + if (sampler.isMultiSample()) + return; + + if (version < 140 && sampler.dim == EsdRect && sampler.type != EbtFloat) + return; + + for (int f16TexAddr = 0; f16TexAddr <= 1; ++f16TexAddr) { // loop over 16-bit floating-point texel addressing + + if (f16TexAddr && sampler.type != EbtFloat16) + continue; + for (int offset = 0; offset < 3; ++offset) { // loop over three forms of offset in the call name: none, Offset, and Offsets + + for (int comp = 0; comp < 2; ++comp) { // loop over presence of comp argument + + if (comp > 0 && sampler.shadow) + continue; + + if (offset > 0 && sampler.dim == EsdCube) + continue; + + for (int sparse = 0; sparse <= 1; ++sparse) { // loop over "bool" sparse or not + if (sparse && (profile == EEsProfile || version < 450)) + continue; + + TString s; + + // return type + if (sparse) + s.append("int "); + else { + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + + // name + if (sparse) + s.append("sparseTextureGather"); + else + s.append("textureGather"); + switch (offset) { + case 1: + s.append("Offset"); + break; + case 2: + s.append("Offsets"); + break; + default: + break; + } + if (sparse) + s.append("ARB"); + s.append("("); + + // sampler type argument + s.append(typeName); + + // P coordinate argument + if (f16TexAddr) + s.append(",f16vec"); + else + s.append(",vec"); + int totalDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0); + s.append(postfixes[totalDims]); + + // refZ argument + if (sampler.shadow) + s.append(",float"); + + // offset argument + if (offset > 0) { + s.append(",ivec2"); + if (offset == 2) + s.append("[4]"); + } + + // texel out (for sparse texture) + if (sparse) { + s.append(",out "); + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + + // comp argument + if (comp) + s.append(",int"); + + s.append(");\n"); + commonBuiltins.append(s); + } + } + } + } + + if (sampler.dim == EsdRect || sampler.shadow) + return; + + if (profile == EEsProfile || version < 450) + return; + + for (int bias = 0; bias < 2; ++bias) { // loop over presence of bias argument + + for (int lod = 0; lod < 2; ++lod) { // loop over presence of lod argument + + if ((lod && bias) || (lod == 0 && bias == 0)) + continue; + + for (int f16TexAddr = 0; f16TexAddr <= 1; ++f16TexAddr) { // loop over 16-bit floating-point texel addressing + + if (f16TexAddr && sampler.type != EbtFloat16) + continue; + + for (int offset = 0; offset < 3; ++offset) { // loop over three forms of offset in the call name: none, Offset, and Offsets + + for (int comp = 0; comp < 2; ++comp) { // loop over presence of comp argument + + if (comp == 0 && bias) + continue; + + if (offset > 0 && sampler.dim == EsdCube) + continue; + + for (int sparse = 0; sparse <= 1; ++sparse) { // loop over "bool" sparse or not + if (sparse && (profile == EEsProfile || version < 450)) + continue; + + TString s; + + // return type + if (sparse) + s.append("int "); + else { + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + + // name + if (sparse) + s.append("sparseTextureGather"); + else + s.append("textureGather"); + + if (lod) + s.append("Lod"); + + switch (offset) { + case 1: + s.append("Offset"); + break; + case 2: + s.append("Offsets"); + break; + default: + break; + } + + if (lod) + s.append("AMD"); + else if (sparse) + s.append("ARB"); + + s.append("("); + + // sampler type argument + s.append(typeName); + + // P coordinate argument + if (f16TexAddr) + s.append(",f16vec"); + else + s.append(",vec"); + int totalDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0); + s.append(postfixes[totalDims]); + + // lod argument + if (lod) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + + // offset argument + if (offset > 0) { + s.append(",ivec2"); + if (offset == 2) + s.append("[4]"); + } + + // texel out (for sparse texture) + if (sparse) { + s.append(",out "); + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + + // comp argument + if (comp) + s.append(",int"); + + // bias argument + if (bias) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + + s.append(");\n"); + if (bias) + stageBuiltins[EShLangFragment].append(s); + else + commonBuiltins.append(s); + } + } + } + } + } + } +} + +// +// Add context-dependent built-in functions and variables that are present +// for the given version and profile. All the results are put into just the +// commonBuiltins, because it is called for just a specific stage. So, +// add stage-specific entries to the commonBuiltins, and only if that stage +// was requested. +// +void TBuiltIns::initialize(const TBuiltInResource &resources, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language) +{ +#ifdef GLSLANG_WEB + version = 310; + profile = EEsProfile; +#endif + + // + // Initialize the context-dependent (resource-dependent) built-in strings for parsing. + // + + //============================================================================ + // + // Standard Uniforms + // + //============================================================================ + + TString& s = commonBuiltins; + const int maxSize = 200; + char builtInConstant[maxSize]; + + // + // Build string of implementation dependent constants. + // + + if (profile == EEsProfile) { + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexAttribs = %d;", resources.maxVertexAttribs); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexUniformVectors = %d;", resources.maxVertexUniformVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexTextureImageUnits = %d;", resources.maxVertexTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxCombinedTextureImageUnits = %d;", resources.maxCombinedTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxTextureImageUnits = %d;", resources.maxTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxFragmentUniformVectors = %d;", resources.maxFragmentUniformVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxDrawBuffers = %d;", resources.maxDrawBuffers); + s.append(builtInConstant); + + if (version == 100) { + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVaryingVectors = %d;", resources.maxVaryingVectors); + s.append(builtInConstant); + } else { + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexOutputVectors = %d;", resources.maxVertexOutputVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxFragmentInputVectors = %d;", resources.maxFragmentInputVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MinProgramTexelOffset = %d;", resources.minProgramTexelOffset); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxProgramTexelOffset = %d;", resources.maxProgramTexelOffset); + s.append(builtInConstant); + } + +#ifndef GLSLANG_WEB + if (version >= 310) { + // geometry + + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryInputComponents = %d;", resources.maxGeometryInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputComponents = %d;", resources.maxGeometryOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryImageUniforms = %d;", resources.maxGeometryImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTextureImageUnits = %d;", resources.maxGeometryTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputVertices = %d;", resources.maxGeometryOutputVertices); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTotalOutputComponents = %d;", resources.maxGeometryTotalOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryUniformComponents = %d;", resources.maxGeometryUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounters = %d;", resources.maxGeometryAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounterBuffers = %d;", resources.maxGeometryAtomicCounterBuffers); + s.append(builtInConstant); + + // tessellation + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlInputComponents = %d;", resources.maxTessControlInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlOutputComponents = %d;", resources.maxTessControlOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTextureImageUnits = %d;", resources.maxTessControlTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlUniformComponents = %d;", resources.maxTessControlUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTotalOutputComponents = %d;", resources.maxTessControlTotalOutputComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationInputComponents = %d;", resources.maxTessEvaluationInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationOutputComponents = %d;", resources.maxTessEvaluationOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationTextureImageUnits = %d;", resources.maxTessEvaluationTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationUniformComponents = %d;", resources.maxTessEvaluationUniformComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessPatchComponents = %d;", resources.maxTessPatchComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxPatchVertices = %d;", resources.maxPatchVertices); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessGenLevel = %d;", resources.maxTessGenLevel); + s.append(builtInConstant); + + // this is here instead of with the others in initialize(version, profile) due to the dependence on gl_MaxPatchVertices + if (language == EShLangTessControl || language == EShLangTessEvaluation) { + s.append( + "in gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + "highp vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "highp vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "} gl_in[gl_MaxPatchVertices];" + "\n"); + } + } + + if (version >= 320) { + // tessellation + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlImageUniforms = %d;", resources.maxTessControlImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationImageUniforms = %d;", resources.maxTessEvaluationImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounters = %d;", resources.maxTessControlAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounters = %d;", resources.maxTessEvaluationAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounterBuffers = %d;", resources.maxTessControlAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounterBuffers = %d;", resources.maxTessEvaluationAtomicCounterBuffers); + s.append(builtInConstant); + } + + if (version >= 100) { + // GL_EXT_blend_func_extended + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxDualSourceDrawBuffersEXT = %d;", resources.maxDualSourceDrawBuffersEXT); + s.append(builtInConstant); + // this is here instead of with the others in initialize(version, profile) due to the dependence on gl_MaxDualSourceDrawBuffersEXT + if (language == EShLangFragment) { + s.append( + "mediump vec4 gl_SecondaryFragColorEXT;" + "mediump vec4 gl_SecondaryFragDataEXT[gl_MaxDualSourceDrawBuffersEXT];" + "\n"); + } + } + } else { + // non-ES profile + + if (version > 400) { + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexUniformVectors = %d;", resources.maxVertexUniformVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentUniformVectors = %d;", resources.maxFragmentUniformVectors); + s.append(builtInConstant); + } + + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexAttribs = %d;", resources.maxVertexAttribs); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexTextureImageUnits = %d;", resources.maxVertexTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedTextureImageUnits = %d;", resources.maxCombinedTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTextureImageUnits = %d;", resources.maxTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxDrawBuffers = %d;", resources.maxDrawBuffers); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxLights = %d;", resources.maxLights); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxClipPlanes = %d;", resources.maxClipPlanes); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTextureUnits = %d;", resources.maxTextureUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTextureCoords = %d;", resources.maxTextureCoords); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexUniformComponents = %d;", resources.maxVertexUniformComponents); + s.append(builtInConstant); + + if (version < 150 || ARBCompatibility) { + snprintf(builtInConstant, maxSize, "const int gl_MaxVaryingFloats = %d;", resources.maxVaryingFloats); + s.append(builtInConstant); + } + + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentUniformComponents = %d;", resources.maxFragmentUniformComponents); + s.append(builtInConstant); + + if (spvVersion.spv == 0 && IncludeLegacy(version, profile, spvVersion)) { + // + // OpenGL'uniform' state. Page numbers are in reference to version + // 1.4 of the OpenGL specification. + // + + // + // Matrix state. p. 31, 32, 37, 39, 40. + // + s.append("uniform mat4 gl_TextureMatrix[gl_MaxTextureCoords];" + + // + // Derived matrix state that provides inverse and transposed versions + // of the matrices above. + // + "uniform mat4 gl_TextureMatrixInverse[gl_MaxTextureCoords];" + + "uniform mat4 gl_TextureMatrixTranspose[gl_MaxTextureCoords];" + + "uniform mat4 gl_TextureMatrixInverseTranspose[gl_MaxTextureCoords];" + + // + // Clip planes p. 42. + // + "uniform vec4 gl_ClipPlane[gl_MaxClipPlanes];" + + // + // Light State p 50, 53, 55. + // + "uniform gl_LightSourceParameters gl_LightSource[gl_MaxLights];" + + // + // Derived state from products of light. + // + "uniform gl_LightProducts gl_FrontLightProduct[gl_MaxLights];" + "uniform gl_LightProducts gl_BackLightProduct[gl_MaxLights];" + + // + // Texture Environment and Generation, p. 152, p. 40-42. + // + "uniform vec4 gl_TextureEnvColor[gl_MaxTextureImageUnits];" + "uniform vec4 gl_EyePlaneS[gl_MaxTextureCoords];" + "uniform vec4 gl_EyePlaneT[gl_MaxTextureCoords];" + "uniform vec4 gl_EyePlaneR[gl_MaxTextureCoords];" + "uniform vec4 gl_EyePlaneQ[gl_MaxTextureCoords];" + "uniform vec4 gl_ObjectPlaneS[gl_MaxTextureCoords];" + "uniform vec4 gl_ObjectPlaneT[gl_MaxTextureCoords];" + "uniform vec4 gl_ObjectPlaneR[gl_MaxTextureCoords];" + "uniform vec4 gl_ObjectPlaneQ[gl_MaxTextureCoords];"); + } + + if (version >= 130) { + snprintf(builtInConstant, maxSize, "const int gl_MaxClipDistances = %d;", resources.maxClipDistances); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxVaryingComponents = %d;", resources.maxVaryingComponents); + s.append(builtInConstant); + + // GL_ARB_shading_language_420pack + snprintf(builtInConstant, maxSize, "const mediump int gl_MinProgramTexelOffset = %d;", resources.minProgramTexelOffset); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxProgramTexelOffset = %d;", resources.maxProgramTexelOffset); + s.append(builtInConstant); + } + + // geometry + if (version >= 150) { + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryInputComponents = %d;", resources.maxGeometryInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputComponents = %d;", resources.maxGeometryOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTextureImageUnits = %d;", resources.maxGeometryTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputVertices = %d;", resources.maxGeometryOutputVertices); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTotalOutputComponents = %d;", resources.maxGeometryTotalOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryUniformComponents = %d;", resources.maxGeometryUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryVaryingComponents = %d;", resources.maxGeometryVaryingComponents); + s.append(builtInConstant); + + } + + if (version >= 150) { + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexOutputComponents = %d;", resources.maxVertexOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentInputComponents = %d;", resources.maxFragmentInputComponents); + s.append(builtInConstant); + } + + // tessellation + if (version >= 150) { + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlInputComponents = %d;", resources.maxTessControlInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlOutputComponents = %d;", resources.maxTessControlOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTextureImageUnits = %d;", resources.maxTessControlTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlUniformComponents = %d;", resources.maxTessControlUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTotalOutputComponents = %d;", resources.maxTessControlTotalOutputComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationInputComponents = %d;", resources.maxTessEvaluationInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationOutputComponents = %d;", resources.maxTessEvaluationOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationTextureImageUnits = %d;", resources.maxTessEvaluationTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationUniformComponents = %d;", resources.maxTessEvaluationUniformComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessPatchComponents = %d;", resources.maxTessPatchComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessGenLevel = %d;", resources.maxTessGenLevel); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxPatchVertices = %d;", resources.maxPatchVertices); + s.append(builtInConstant); + + // this is here instead of with the others in initialize(version, profile) due to the dependence on gl_MaxPatchVertices + if (language == EShLangTessControl || language == EShLangTessEvaluation) { + s.append( + "in gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + ); + if (profile == ECompatibilityProfile) + s.append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (profile != EEsProfile && version >= 450) + s.append( + "float gl_CullDistance[];" + "vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + s.append( + "} gl_in[gl_MaxPatchVertices];" + "\n"); + } + } + + if (version >= 150) { + snprintf(builtInConstant, maxSize, "const int gl_MaxViewports = %d;", resources.maxViewports); + s.append(builtInConstant); + } + + // images + if (version >= 130) { + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedImageUnitsAndFragmentOutputs = %d;", resources.maxCombinedImageUnitsAndFragmentOutputs); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxImageSamples = %d;", resources.maxImageSamples); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlImageUniforms = %d;", resources.maxTessControlImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationImageUniforms = %d;", resources.maxTessEvaluationImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryImageUniforms = %d;", resources.maxGeometryImageUniforms); + s.append(builtInConstant); + } + + // enhanced layouts + if (version >= 430) { + snprintf(builtInConstant, maxSize, "const int gl_MaxTransformFeedbackBuffers = %d;", resources.maxTransformFeedbackBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTransformFeedbackInterleavedComponents = %d;", resources.maxTransformFeedbackInterleavedComponents); + s.append(builtInConstant); + } +#endif + } + + // compute + if ((profile == EEsProfile && version >= 310) || (profile != EEsProfile && version >= 420)) { + snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxComputeWorkGroupCount = ivec3(%d,%d,%d);", resources.maxComputeWorkGroupCountX, + resources.maxComputeWorkGroupCountY, + resources.maxComputeWorkGroupCountZ); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxComputeWorkGroupSize = ivec3(%d,%d,%d);", resources.maxComputeWorkGroupSizeX, + resources.maxComputeWorkGroupSizeY, + resources.maxComputeWorkGroupSizeZ); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeUniformComponents = %d;", resources.maxComputeUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeTextureImageUnits = %d;", resources.maxComputeTextureImageUnits); + s.append(builtInConstant); + + s.append("\n"); + } + +#ifndef GLSLANG_WEB + // images (some in compute below) + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 130)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxImageUnits = %d;", resources.maxImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedShaderOutputResources = %d;", resources.maxCombinedShaderOutputResources); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexImageUniforms = %d;", resources.maxVertexImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentImageUniforms = %d;", resources.maxFragmentImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedImageUniforms = %d;", resources.maxCombinedImageUniforms); + s.append(builtInConstant); + } + + // compute + if ((profile == EEsProfile && version >= 310) || (profile != EEsProfile && version >= 420)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeImageUniforms = %d;", resources.maxComputeImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeAtomicCounters = %d;", resources.maxComputeAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeAtomicCounterBuffers = %d;", resources.maxComputeAtomicCounterBuffers); + s.append(builtInConstant); + + s.append("\n"); + } + + // atomic counters (some in compute below) + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 420)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexAtomicCounters = %d;", resources. maxVertexAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentAtomicCounters = %d;", resources. maxFragmentAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedAtomicCounters = %d;", resources. maxCombinedAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxAtomicCounterBindings = %d;", resources. maxAtomicCounterBindings); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexAtomicCounterBuffers = %d;", resources. maxVertexAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentAtomicCounterBuffers = %d;", resources. maxFragmentAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedAtomicCounterBuffers = %d;", resources. maxCombinedAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxAtomicCounterBufferSize = %d;", resources. maxAtomicCounterBufferSize); + s.append(builtInConstant); + } + if (profile != EEsProfile && version >= 420) { + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounters = %d;", resources. maxTessControlAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounters = %d;", resources. maxTessEvaluationAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounters = %d;", resources. maxGeometryAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounterBuffers = %d;", resources. maxTessControlAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounterBuffers = %d;", resources. maxTessEvaluationAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounterBuffers = %d;", resources. maxGeometryAtomicCounterBuffers); + s.append(builtInConstant); + + s.append("\n"); + } + + // GL_ARB_cull_distance + if (profile != EEsProfile && version >= 450) { + snprintf(builtInConstant, maxSize, "const int gl_MaxCullDistances = %d;", resources.maxCullDistances); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedClipAndCullDistances = %d;", resources.maxCombinedClipAndCullDistances); + s.append(builtInConstant); + } + + // GL_ARB_ES3_1_compatibility + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 310)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxSamples = %d;", resources.maxSamples); + s.append(builtInConstant); + } + + // SPV_NV_mesh_shader + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxMeshOutputVerticesNV = %d;", resources.maxMeshOutputVerticesNV); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxMeshOutputPrimitivesNV = %d;", resources.maxMeshOutputPrimitivesNV); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxMeshWorkGroupSizeNV = ivec3(%d,%d,%d);", resources.maxMeshWorkGroupSizeX_NV, + resources.maxMeshWorkGroupSizeY_NV, + resources.maxMeshWorkGroupSizeZ_NV); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxTaskWorkGroupSizeNV = ivec3(%d,%d,%d);", resources.maxTaskWorkGroupSizeX_NV, + resources.maxTaskWorkGroupSizeY_NV, + resources.maxTaskWorkGroupSizeZ_NV); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxMeshViewCountNV = %d;", resources.maxMeshViewCountNV); + s.append(builtInConstant); + + s.append("\n"); + } +#endif + + s.append("\n"); +} + +// +// To support special built-ins that have a special qualifier that cannot be declared textually +// in a shader, like gl_Position. +// +// This lets the type of the built-in be declared textually, and then have just its qualifier be +// updated afterward. +// +// Safe to call even if name is not present. +// +// Only use this for built-in variables that have a special qualifier in TStorageQualifier. +// New built-in variables should use a generic (textually declarable) qualifier in +// TStoraregQualifier and only call BuiltInVariable(). +// +static void SpecialQualifier(const char* name, TStorageQualifier qualifier, TBuiltInVariable builtIn, TSymbolTable& symbolTable) +{ + TSymbol* symbol = symbolTable.find(name); + if (symbol == nullptr) + return; + + TQualifier& symQualifier = symbol->getWritableType().getQualifier(); + symQualifier.storage = qualifier; + symQualifier.builtIn = builtIn; +} + +// +// To tag built-in variables with their TBuiltInVariable enum. Use this when the +// normal declaration text already gets the qualifier right, and all that's needed +// is setting the builtIn field. This should be the normal way for all new +// built-in variables. +// +// If SpecialQualifier() was called, this does not need to be called. +// +// Safe to call even if name is not present. +// +static void BuiltInVariable(const char* name, TBuiltInVariable builtIn, TSymbolTable& symbolTable) +{ + TSymbol* symbol = symbolTable.find(name); + if (symbol == nullptr) + return; + + TQualifier& symQualifier = symbol->getWritableType().getQualifier(); + symQualifier.builtIn = builtIn; +} + +// +// For built-in variables inside a named block. +// SpecialQualifier() won't ever go inside a block; their member's qualifier come +// from the qualification of the block. +// +// See comments above for other detail. +// +static void BuiltInVariable(const char* blockName, const char* name, TBuiltInVariable builtIn, TSymbolTable& symbolTable) +{ + TSymbol* symbol = symbolTable.find(blockName); + if (symbol == nullptr) + return; + + TTypeList& structure = *symbol->getWritableType().getWritableStruct(); + for (int i = 0; i < (int)structure.size(); ++i) { + if (structure[i].type->getFieldName().compare(name) == 0) { + structure[i].type->getQualifier().builtIn = builtIn; + return; + } + } +} + +// +// Finish adding/processing context-independent built-in symbols. +// 1) Programmatically add symbols that could not be added by simple text strings above. +// 2) Map built-in functions to operators, for those that will turn into an operation node +// instead of remaining a function call. +// 3) Tag extension-related symbols added to their base version with their extensions, so +// that if an early version has the extension turned off, there is an error reported on use. +// +void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable) +{ +#ifdef GLSLANG_WEB + version = 310; + profile = EEsProfile; +#endif + + // + // Tag built-in variables and functions with additional qualifier and extension information + // that cannot be declared with the text strings. + // + + // N.B.: a symbol should only be tagged once, and this function is called multiple times, once + // per stage that's used for this profile. So + // - generally, stick common ones in the fragment stage to ensure they are tagged exactly once + // - for ES, which has different precisions for different stages, the coarsest-grained tagging + // for a built-in used in many stages needs to be once for the fragment stage and once for + // the vertex stage + + switch(language) { + case EShLangVertex: + if (spvVersion.vulkan > 0) { + BuiltInVariable("gl_VertexIndex", EbvVertexIndex, symbolTable); + BuiltInVariable("gl_InstanceIndex", EbvInstanceIndex, symbolTable); + } + +#ifndef GLSLANG_WEB + if (spvVersion.vulkan == 0) { + SpecialQualifier("gl_VertexID", EvqVertexId, EbvVertexId, symbolTable); + SpecialQualifier("gl_InstanceID", EvqInstanceId, EbvInstanceId, symbolTable); + } + + if (profile != EEsProfile) { + if (version >= 440) { + symbolTable.setVariableExtensions("gl_BaseVertexARB", 1, &E_GL_ARB_shader_draw_parameters); + symbolTable.setVariableExtensions("gl_BaseInstanceARB", 1, &E_GL_ARB_shader_draw_parameters); + symbolTable.setVariableExtensions("gl_DrawIDARB", 1, &E_GL_ARB_shader_draw_parameters); + BuiltInVariable("gl_BaseVertexARB", EbvBaseVertex, symbolTable); + BuiltInVariable("gl_BaseInstanceARB", EbvBaseInstance, symbolTable); + BuiltInVariable("gl_DrawIDARB", EbvDrawId, symbolTable); + } + if (version >= 460) { + BuiltInVariable("gl_BaseVertex", EbvBaseVertex, symbolTable); + BuiltInVariable("gl_BaseInstance", EbvBaseInstance, symbolTable); + BuiltInVariable("gl_DrawID", EbvDrawId, symbolTable); + } + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + symbolTable.setFunctionExtensions("ballotARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setFunctionExtensions("readInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setFunctionExtensions("readFirstInvocationARB", 1, &E_GL_ARB_shader_ballot); + + if (version >= 430) { + symbolTable.setFunctionExtensions("anyInvocationARB", 1, &E_GL_ARB_shader_group_vote); + symbolTable.setFunctionExtensions("allInvocationsARB", 1, &E_GL_ARB_shader_group_vote); + symbolTable.setFunctionExtensions("allInvocationsEqualARB", 1, &E_GL_ARB_shader_group_vote); + } + } + + + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("minInvocationsAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("minInvocationsNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("swizzleInvocationsAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("swizzleInvocationsWithPatternAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("writeInvocationAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("mbcntAMD", 1, &E_GL_AMD_shader_ballot); + + symbolTable.setFunctionExtensions("minInvocationsInclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsInclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsInclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("minInvocationsInclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsInclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsInclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("minInvocationsExclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsExclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsExclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("minInvocationsExclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsExclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsExclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + } + + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("min3", 1, &E_GL_AMD_shader_trinary_minmax); + symbolTable.setFunctionExtensions("max3", 1, &E_GL_AMD_shader_trinary_minmax); + symbolTable.setFunctionExtensions("mid3", 1, &E_GL_AMD_shader_trinary_minmax); + } + + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_SIMDGroupSizeAMD", 1, &E_GL_AMD_gcn_shader); + SpecialQualifier("gl_SIMDGroupSizeAMD", EvqVaryingIn, EbvSubGroupSize, symbolTable); + + symbolTable.setFunctionExtensions("cubeFaceIndexAMD", 1, &E_GL_AMD_gcn_shader); + symbolTable.setFunctionExtensions("cubeFaceCoordAMD", 1, &E_GL_AMD_gcn_shader); + symbolTable.setFunctionExtensions("timeAMD", 1, &E_GL_AMD_gcn_shader); + } + + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("fragmentMaskFetchAMD", 1, &E_GL_AMD_shader_fragment_mask); + symbolTable.setFunctionExtensions("fragmentFetchAMD", 1, &E_GL_AMD_shader_fragment_mask); + } + + symbolTable.setFunctionExtensions("countLeadingZeros", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("countTrailingZeros", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("absoluteDifference", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("addSaturate", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("subtractSaturate", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("average", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("averageRounded", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("multiply32x16", 1, &E_GL_INTEL_shader_integer_functions2); + + symbolTable.setFunctionExtensions("textureFootprintNV", 1, &E_GL_NV_shader_texture_footprint); + symbolTable.setFunctionExtensions("textureFootprintClampNV", 1, &E_GL_NV_shader_texture_footprint); + symbolTable.setFunctionExtensions("textureFootprintLodNV", 1, &E_GL_NV_shader_texture_footprint); + symbolTable.setFunctionExtensions("textureFootprintGradNV", 1, &E_GL_NV_shader_texture_footprint); + symbolTable.setFunctionExtensions("textureFootprintGradClampNV", 1, &E_GL_NV_shader_texture_footprint); + // Compatibility variables, vertex only + if (spvVersion.spv == 0) { + BuiltInVariable("gl_Color", EbvColor, symbolTable); + BuiltInVariable("gl_SecondaryColor", EbvSecondaryColor, symbolTable); + BuiltInVariable("gl_Normal", EbvNormal, symbolTable); + BuiltInVariable("gl_Vertex", EbvVertex, symbolTable); + BuiltInVariable("gl_MultiTexCoord0", EbvMultiTexCoord0, symbolTable); + BuiltInVariable("gl_MultiTexCoord1", EbvMultiTexCoord1, symbolTable); + BuiltInVariable("gl_MultiTexCoord2", EbvMultiTexCoord2, symbolTable); + BuiltInVariable("gl_MultiTexCoord3", EbvMultiTexCoord3, symbolTable); + BuiltInVariable("gl_MultiTexCoord4", EbvMultiTexCoord4, symbolTable); + BuiltInVariable("gl_MultiTexCoord5", EbvMultiTexCoord5, symbolTable); + BuiltInVariable("gl_MultiTexCoord6", EbvMultiTexCoord6, symbolTable); + BuiltInVariable("gl_MultiTexCoord7", EbvMultiTexCoord7, symbolTable); + BuiltInVariable("gl_FogCoord", EbvFogFragCoord, symbolTable); + } + + if (profile == EEsProfile) { + if (spvVersion.spv == 0) { + symbolTable.setFunctionExtensions("texture2DGradEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjGradEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeGradEXT", 1, &E_GL_EXT_shader_texture_lod); + if (version == 310) + symbolTable.setFunctionExtensions("textureGatherOffsets", Num_AEP_gpu_shader5, AEP_gpu_shader5); + } + if (version == 310) + symbolTable.setFunctionExtensions("fma", Num_AEP_gpu_shader5, AEP_gpu_shader5); + } + + if (profile == EEsProfile && version < 320) { + symbolTable.setFunctionExtensions("imageAtomicAdd", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicMin", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicMax", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicAnd", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicOr", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicXor", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicExchange", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicCompSwap", 1, &E_GL_OES_shader_image_atomic); + } + + if (version >= 300 /* both ES and non-ES */) { + symbolTable.setVariableExtensions("gl_ViewID_OVR", Num_OVR_multiview_EXTs, OVR_multiview_EXTs); + BuiltInVariable("gl_ViewID_OVR", EbvViewIndex, symbolTable); + } + + if (profile == EEsProfile) { + symbolTable.setFunctionExtensions("shadow2DEXT", 1, &E_GL_EXT_shadow_samplers); + symbolTable.setFunctionExtensions("shadow2DProjEXT", 1, &E_GL_EXT_shadow_samplers); + } + // Fall through + + case EShLangTessControl: + if (profile == EEsProfile && version >= 310) { + BuiltInVariable("gl_BoundingBoxEXT", EbvBoundingBox, symbolTable); + symbolTable.setVariableExtensions("gl_BoundingBoxEXT", 1, + &E_GL_EXT_primitive_bounding_box); + BuiltInVariable("gl_BoundingBoxOES", EbvBoundingBox, symbolTable); + symbolTable.setVariableExtensions("gl_BoundingBoxOES", 1, + &E_GL_OES_primitive_bounding_box); + + if (version >= 320) { + BuiltInVariable("gl_BoundingBox", EbvBoundingBox, symbolTable); + } + } + // Fall through + + case EShLangTessEvaluation: + case EShLangGeometry: +#endif + SpecialQualifier("gl_Position", EvqPosition, EbvPosition, symbolTable); + SpecialQualifier("gl_PointSize", EvqPointSize, EbvPointSize, symbolTable); + + BuiltInVariable("gl_in", "gl_Position", EbvPosition, symbolTable); + BuiltInVariable("gl_in", "gl_PointSize", EbvPointSize, symbolTable); + + BuiltInVariable("gl_out", "gl_Position", EbvPosition, symbolTable); + BuiltInVariable("gl_out", "gl_PointSize", EbvPointSize, symbolTable); + +#ifndef GLSLANG_WEB + SpecialQualifier("gl_ClipVertex", EvqClipVertex, EbvClipVertex, symbolTable); + + BuiltInVariable("gl_in", "gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_in", "gl_CullDistance", EbvCullDistance, symbolTable); + + BuiltInVariable("gl_out", "gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_out", "gl_CullDistance", EbvCullDistance, symbolTable); + + BuiltInVariable("gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_CullDistance", EbvCullDistance, symbolTable); + BuiltInVariable("gl_PrimitiveIDIn", EbvPrimitiveId, symbolTable); + BuiltInVariable("gl_PrimitiveID", EbvPrimitiveId, symbolTable); + BuiltInVariable("gl_InvocationID", EbvInvocationId, symbolTable); + BuiltInVariable("gl_Layer", EbvLayer, symbolTable); + BuiltInVariable("gl_ViewportIndex", EbvViewportIndex, symbolTable); + + if (language != EShLangGeometry) { + symbolTable.setVariableExtensions("gl_Layer", Num_viewportEXTs, viewportEXTs); + symbolTable.setVariableExtensions("gl_ViewportIndex", Num_viewportEXTs, viewportEXTs); + } + symbolTable.setVariableExtensions("gl_ViewportMask", 1, &E_GL_NV_viewport_array2); + symbolTable.setVariableExtensions("gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_SecondaryViewportMaskNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + symbolTable.setVariableExtensions("gl_ViewportMaskPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + + BuiltInVariable("gl_ViewportMask", EbvViewportMaskNV, symbolTable); + BuiltInVariable("gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable); + BuiltInVariable("gl_SecondaryViewportMaskNV", EbvSecondaryViewportMaskNV, symbolTable); + BuiltInVariable("gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + BuiltInVariable("gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable); + + if (language == EShLangVertex || language == EShLangGeometry) { + symbolTable.setVariableExtensions("gl_in", "gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_in", "gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + + BuiltInVariable("gl_in", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable); + BuiltInVariable("gl_in", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + } + symbolTable.setVariableExtensions("gl_out", "gl_ViewportMask", 1, &E_GL_NV_viewport_array2); + symbolTable.setVariableExtensions("gl_out", "gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_out", "gl_SecondaryViewportMaskNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_out", "gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + symbolTable.setVariableExtensions("gl_out", "gl_ViewportMaskPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + + BuiltInVariable("gl_out", "gl_ViewportMask", EbvViewportMaskNV, symbolTable); + BuiltInVariable("gl_out", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable); + BuiltInVariable("gl_out", "gl_SecondaryViewportMaskNV", EbvSecondaryViewportMaskNV, symbolTable); + BuiltInVariable("gl_out", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + BuiltInVariable("gl_out", "gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable); + + BuiltInVariable("gl_PatchVerticesIn", EbvPatchVertices, symbolTable); + BuiltInVariable("gl_TessLevelOuter", EbvTessLevelOuter, symbolTable); + BuiltInVariable("gl_TessLevelInner", EbvTessLevelInner, symbolTable); + BuiltInVariable("gl_TessCoord", EbvTessCoord, symbolTable); + + if (version < 410) + symbolTable.setVariableExtensions("gl_ViewportIndex", 1, &E_GL_ARB_viewport_array); + + // Compatibility variables + + BuiltInVariable("gl_in", "gl_ClipVertex", EbvClipVertex, symbolTable); + BuiltInVariable("gl_in", "gl_FrontColor", EbvFrontColor, symbolTable); + BuiltInVariable("gl_in", "gl_BackColor", EbvBackColor, symbolTable); + BuiltInVariable("gl_in", "gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable); + BuiltInVariable("gl_in", "gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable); + BuiltInVariable("gl_in", "gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_in", "gl_FogFragCoord", EbvFogFragCoord, symbolTable); + + BuiltInVariable("gl_out", "gl_ClipVertex", EbvClipVertex, symbolTable); + BuiltInVariable("gl_out", "gl_FrontColor", EbvFrontColor, symbolTable); + BuiltInVariable("gl_out", "gl_BackColor", EbvBackColor, symbolTable); + BuiltInVariable("gl_out", "gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable); + BuiltInVariable("gl_out", "gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable); + BuiltInVariable("gl_out", "gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_out", "gl_FogFragCoord", EbvFogFragCoord, symbolTable); + + BuiltInVariable("gl_ClipVertex", EbvClipVertex, symbolTable); + BuiltInVariable("gl_FrontColor", EbvFrontColor, symbolTable); + BuiltInVariable("gl_BackColor", EbvBackColor, symbolTable); + BuiltInVariable("gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable); + BuiltInVariable("gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable); + BuiltInVariable("gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_FogFragCoord", EbvFogFragCoord, symbolTable); + + // gl_PointSize, when it needs to be tied to an extension, is always a member of a block. + // (Sometimes with an instance name, sometimes anonymous). + if (profile == EEsProfile) { + if (language == EShLangGeometry) { + symbolTable.setVariableExtensions("gl_PointSize", Num_AEP_geometry_point_size, AEP_geometry_point_size); + symbolTable.setVariableExtensions("gl_in", "gl_PointSize", Num_AEP_geometry_point_size, AEP_geometry_point_size); + } else if (language == EShLangTessEvaluation || language == EShLangTessControl) { + // gl_in tessellation settings of gl_PointSize are in the context-dependent paths + symbolTable.setVariableExtensions("gl_PointSize", Num_AEP_tessellation_point_size, AEP_tessellation_point_size); + symbolTable.setVariableExtensions("gl_out", "gl_PointSize", Num_AEP_tessellation_point_size, AEP_tessellation_point_size); + } + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + symbolTable.setVariableExtensions("gl_ViewIndex", 1, &E_GL_EXT_multiview); + BuiltInVariable("gl_ViewIndex", EbvViewIndex, symbolTable); + } + + if (profile != EEsProfile) { + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + } +#endif + break; + + case EShLangFragment: + SpecialQualifier("gl_FrontFacing", EvqFace, EbvFace, symbolTable); + SpecialQualifier("gl_FragCoord", EvqFragCoord, EbvFragCoord, symbolTable); + SpecialQualifier("gl_PointCoord", EvqPointCoord, EbvPointCoord, symbolTable); + if (spvVersion.spv == 0) + SpecialQualifier("gl_FragColor", EvqFragColor, EbvFragColor, symbolTable); + else { + TSymbol* symbol = symbolTable.find("gl_FragColor"); + if (symbol) { + symbol->getWritableType().getQualifier().storage = EvqVaryingOut; + symbol->getWritableType().getQualifier().layoutLocation = 0; + } + } + SpecialQualifier("gl_FragDepth", EvqFragDepth, EbvFragDepth, symbolTable); +#ifndef GLSLANG_WEB + SpecialQualifier("gl_FragDepthEXT", EvqFragDepth, EbvFragDepth, symbolTable); + SpecialQualifier("gl_HelperInvocation", EvqVaryingIn, EbvHelperInvocation, symbolTable); + + BuiltInVariable("gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_CullDistance", EbvCullDistance, symbolTable); + BuiltInVariable("gl_PrimitiveID", EbvPrimitiveId, symbolTable); + + if (profile != EEsProfile && version >= 140) { + symbolTable.setVariableExtensions("gl_FragStencilRefARB", 1, &E_GL_ARB_shader_stencil_export); + BuiltInVariable("gl_FragStencilRefARB", EbvFragStencilRef, symbolTable); + } + + if (profile != EEsProfile && version < 400) { + symbolTable.setFunctionExtensions("textureQueryLod", 1, &E_GL_ARB_texture_query_lod); + } + + if (profile != EEsProfile && version >= 460) { + symbolTable.setFunctionExtensions("rayQueryInitializeEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryTerminateEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGenerateIntersectionEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryConfirmIntersectionEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryProceedEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionTypeEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionTEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetRayFlagsEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetRayTMinEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionInstanceCustomIndexEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionInstanceIdEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionGeometryIndexEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionPrimitiveIndexEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionBarycentricsEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionFrontFaceEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionCandidateAABBOpaqueEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionObjectRayDirectionEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionObjectRayOriginEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionObjectToWorldEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionWorldToObjectEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetWorldRayOriginEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetWorldRayDirectionEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setVariableExtensions("gl_RayFlagsSkipAABBEXT", 1, &E_GL_EXT_ray_flags_primitive_culling); + symbolTable.setVariableExtensions("gl_RayFlagsSkipTrianglesEXT", 1, &E_GL_EXT_ray_flags_primitive_culling); + } + + if ((profile != EEsProfile && version >= 130) || + (profile == EEsProfile && version >= 310)) { + BuiltInVariable("gl_SampleID", EbvSampleId, symbolTable); + BuiltInVariable("gl_SamplePosition", EbvSamplePosition, symbolTable); + BuiltInVariable("gl_SampleMask", EbvSampleMask, symbolTable); + + if (profile != EEsProfile && version < 400) { + BuiltInVariable("gl_NumSamples", EbvSampleMask, symbolTable); + + symbolTable.setVariableExtensions("gl_SampleMask", 1, &E_GL_ARB_sample_shading); + symbolTable.setVariableExtensions("gl_SampleID", 1, &E_GL_ARB_sample_shading); + symbolTable.setVariableExtensions("gl_SamplePosition", 1, &E_GL_ARB_sample_shading); + symbolTable.setVariableExtensions("gl_NumSamples", 1, &E_GL_ARB_sample_shading); + } else { + BuiltInVariable("gl_SampleMaskIn", EbvSampleMask, symbolTable); + + if (profile == EEsProfile && version < 320) { + symbolTable.setVariableExtensions("gl_SampleID", 1, &E_GL_OES_sample_variables); + symbolTable.setVariableExtensions("gl_SamplePosition", 1, &E_GL_OES_sample_variables); + symbolTable.setVariableExtensions("gl_SampleMaskIn", 1, &E_GL_OES_sample_variables); + symbolTable.setVariableExtensions("gl_SampleMask", 1, &E_GL_OES_sample_variables); + symbolTable.setVariableExtensions("gl_NumSamples", 1, &E_GL_OES_sample_variables); + } + } + } + + BuiltInVariable("gl_Layer", EbvLayer, symbolTable); + BuiltInVariable("gl_ViewportIndex", EbvViewportIndex, symbolTable); + + // Compatibility variables + + BuiltInVariable("gl_in", "gl_FogFragCoord", EbvFogFragCoord, symbolTable); + BuiltInVariable("gl_in", "gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_in", "gl_Color", EbvColor, symbolTable); + BuiltInVariable("gl_in", "gl_SecondaryColor", EbvSecondaryColor, symbolTable); + + BuiltInVariable("gl_FogFragCoord", EbvFogFragCoord, symbolTable); + BuiltInVariable("gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_Color", EbvColor, symbolTable); + BuiltInVariable("gl_SecondaryColor", EbvSecondaryColor, symbolTable); + + // built-in functions + + if (profile == EEsProfile) { + if (spvVersion.spv == 0) { + symbolTable.setFunctionExtensions("texture2DLodEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjLodEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeLodEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DGradEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjGradEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeGradEXT", 1, &E_GL_EXT_shader_texture_lod); + if (version < 320) + symbolTable.setFunctionExtensions("textureGatherOffsets", Num_AEP_gpu_shader5, AEP_gpu_shader5); + } + if (version == 100) { + symbolTable.setFunctionExtensions("dFdx", 1, &E_GL_OES_standard_derivatives); + symbolTable.setFunctionExtensions("dFdy", 1, &E_GL_OES_standard_derivatives); + symbolTable.setFunctionExtensions("fwidth", 1, &E_GL_OES_standard_derivatives); + } + if (version == 310) { + symbolTable.setFunctionExtensions("fma", Num_AEP_gpu_shader5, AEP_gpu_shader5); + symbolTable.setFunctionExtensions("interpolateAtCentroid", 1, &E_GL_OES_shader_multisample_interpolation); + symbolTable.setFunctionExtensions("interpolateAtSample", 1, &E_GL_OES_shader_multisample_interpolation); + symbolTable.setFunctionExtensions("interpolateAtOffset", 1, &E_GL_OES_shader_multisample_interpolation); + } + } else if (version < 130) { + if (spvVersion.spv == 0) { + symbolTable.setFunctionExtensions("texture1DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture3DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture1DProjLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture3DProjLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow1DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow1DProjLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DProjLod", 1, &E_GL_ARB_shader_texture_lod); + } + } + + // E_GL_ARB_shader_texture_lod functions usable only with the extension enabled + if (profile != EEsProfile && spvVersion.spv == 0) { + symbolTable.setFunctionExtensions("texture1DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture1DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture3DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture3DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow1DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow1DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DRectGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DRectProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DRectGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DRectProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + } + + // E_GL_ARB_shader_image_load_store + if (profile != EEsProfile && version < 420) + symbolTable.setFunctionExtensions("memoryBarrier", 1, &E_GL_ARB_shader_image_load_store); + // All the image access functions are protected by checks on the type of the first argument. + + // E_GL_ARB_shader_atomic_counters + if (profile != EEsProfile && version < 420) { + symbolTable.setFunctionExtensions("atomicCounterIncrement", 1, &E_GL_ARB_shader_atomic_counters); + symbolTable.setFunctionExtensions("atomicCounterDecrement", 1, &E_GL_ARB_shader_atomic_counters); + symbolTable.setFunctionExtensions("atomicCounter" , 1, &E_GL_ARB_shader_atomic_counters); + } + + // E_GL_ARB_derivative_control + if (profile != EEsProfile && version < 450) { + symbolTable.setFunctionExtensions("dFdxFine", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("dFdyFine", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("fwidthFine", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("dFdxCoarse", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("dFdyCoarse", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("fwidthCoarse", 1, &E_GL_ARB_derivative_control); + } + + // E_GL_ARB_sparse_texture2 + if (profile != EEsProfile) + { + symbolTable.setFunctionExtensions("sparseTextureARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureLodARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTexelFetchARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTexelFetchOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureLodOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGradARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGradOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGatherARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGatherOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGatherOffsetsARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseImageLoadARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTexelsResident", 1, &E_GL_ARB_sparse_texture2); + } + + // E_GL_ARB_sparse_texture_clamp + if (profile != EEsProfile) + { + symbolTable.setFunctionExtensions("sparseTextureClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("sparseTextureOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("sparseTextureGradClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("sparseTextureGradOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("textureClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("textureOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("textureGradClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("textureGradOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + } + + // E_GL_AMD_shader_explicit_vertex_parameter + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_BaryCoordNoPerspAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordNoPerspCentroidAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordNoPerspSampleAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordSmoothAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordSmoothCentroidAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordSmoothSampleAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordPullModelAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + + symbolTable.setFunctionExtensions("interpolateAtVertexAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + + BuiltInVariable("gl_BaryCoordNoPerspAMD", EbvBaryCoordNoPersp, symbolTable); + BuiltInVariable("gl_BaryCoordNoPerspCentroidAMD", EbvBaryCoordNoPerspCentroid, symbolTable); + BuiltInVariable("gl_BaryCoordNoPerspSampleAMD", EbvBaryCoordNoPerspSample, symbolTable); + BuiltInVariable("gl_BaryCoordSmoothAMD", EbvBaryCoordSmooth, symbolTable); + BuiltInVariable("gl_BaryCoordSmoothCentroidAMD", EbvBaryCoordSmoothCentroid, symbolTable); + BuiltInVariable("gl_BaryCoordSmoothSampleAMD", EbvBaryCoordSmoothSample, symbolTable); + BuiltInVariable("gl_BaryCoordPullModelAMD", EbvBaryCoordPullModel, symbolTable); + } + + // E_GL_AMD_texture_gather_bias_lod + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("textureGatherLodAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("textureGatherLodOffsetAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("textureGatherLodOffsetsAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("sparseTextureGatherLodAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("sparseTextureGatherLodOffsetAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("sparseTextureGatherLodOffsetsAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + } + + // E_GL_AMD_shader_image_load_store_lod + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("imageLoadLodAMD", 1, &E_GL_AMD_shader_image_load_store_lod); + symbolTable.setFunctionExtensions("imageStoreLodAMD", 1, &E_GL_AMD_shader_image_load_store_lod); + symbolTable.setFunctionExtensions("sparseImageLoadLodAMD", 1, &E_GL_AMD_shader_image_load_store_lod); + } + if (profile != EEsProfile && version >= 430) { + symbolTable.setVariableExtensions("gl_FragFullyCoveredNV", 1, &E_GL_NV_conservative_raster_underestimation); + BuiltInVariable("gl_FragFullyCoveredNV", EbvFragFullyCoveredNV, symbolTable); + } + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) { + symbolTable.setVariableExtensions("gl_FragmentSizeNV", 1, &E_GL_NV_shading_rate_image); + symbolTable.setVariableExtensions("gl_InvocationsPerPixelNV", 1, &E_GL_NV_shading_rate_image); + BuiltInVariable("gl_FragmentSizeNV", EbvFragmentSizeNV, symbolTable); + BuiltInVariable("gl_InvocationsPerPixelNV", EbvInvocationsPerPixelNV, symbolTable); + symbolTable.setVariableExtensions("gl_BaryCoordNV", 1, &E_GL_NV_fragment_shader_barycentric); + symbolTable.setVariableExtensions("gl_BaryCoordNoPerspNV", 1, &E_GL_NV_fragment_shader_barycentric); + BuiltInVariable("gl_BaryCoordNV", EbvBaryCoordNV, symbolTable); + BuiltInVariable("gl_BaryCoordNoPerspNV", EbvBaryCoordNoPerspNV, symbolTable); + } + + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 310)) { + symbolTable.setVariableExtensions("gl_FragSizeEXT", 1, &E_GL_EXT_fragment_invocation_density); + symbolTable.setVariableExtensions("gl_FragInvocationCountEXT", 1, &E_GL_EXT_fragment_invocation_density); + BuiltInVariable("gl_FragSizeEXT", EbvFragSizeEXT, symbolTable); + BuiltInVariable("gl_FragInvocationCountEXT", EbvFragInvocationCountEXT, symbolTable); + } + + symbolTable.setVariableExtensions("gl_FragDepthEXT", 1, &E_GL_EXT_frag_depth); + + symbolTable.setFunctionExtensions("clockARB", 1, &E_GL_ARB_shader_clock); + symbolTable.setFunctionExtensions("clock2x32ARB", 1, &E_GL_ARB_shader_clock); + + symbolTable.setFunctionExtensions("clockRealtimeEXT", 1, &E_GL_EXT_shader_realtime_clock); + symbolTable.setFunctionExtensions("clockRealtime2x32EXT", 1, &E_GL_EXT_shader_realtime_clock); + + if (profile == EEsProfile && version < 320) { + symbolTable.setVariableExtensions("gl_PrimitiveID", Num_AEP_geometry_shader, AEP_geometry_shader); + symbolTable.setVariableExtensions("gl_Layer", Num_AEP_geometry_shader, AEP_geometry_shader); + } + + if (profile == EEsProfile && version < 320) { + symbolTable.setFunctionExtensions("imageAtomicAdd", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicMin", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicMax", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicAnd", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicOr", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicXor", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicExchange", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicCompSwap", 1, &E_GL_OES_shader_image_atomic); + } + + if (profile != EEsProfile && version < 330 ) { + symbolTable.setFunctionExtensions("floatBitsToInt", 1, &E_GL_ARB_shader_bit_encoding); + symbolTable.setFunctionExtensions("floatBitsToUint", 1, &E_GL_ARB_shader_bit_encoding); + symbolTable.setFunctionExtensions("intBitsToFloat", 1, &E_GL_ARB_shader_bit_encoding); + symbolTable.setFunctionExtensions("uintBitsToFloat", 1, &E_GL_ARB_shader_bit_encoding); + } + + if (profile != EEsProfile && version < 430 ) { + symbolTable.setFunctionExtensions("imageSize", 1, &E_GL_ARB_shader_image_size); + } + + // GL_ARB_shader_storage_buffer_object + if (profile != EEsProfile && version < 430 ) { + symbolTable.setFunctionExtensions("atomicAdd", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicMin", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicMax", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicAnd", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicOr", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicXor", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicExchange", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicCompSwap", 1, &E_GL_ARB_shader_storage_buffer_object); + } + + // GL_ARB_shading_language_packing + if (profile != EEsProfile && version < 400 ) { + symbolTable.setFunctionExtensions("packUnorm2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackUnorm2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("packSnorm4x8", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("packUnorm4x8", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackSnorm4x8", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackUnorm4x8", 1, &E_GL_ARB_shading_language_packing); + } + if (profile != EEsProfile && version < 420 ) { + symbolTable.setFunctionExtensions("packSnorm2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackSnorm2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackHalf2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("packHalf2x16", 1, &E_GL_ARB_shading_language_packing); + } + + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + symbolTable.setVariableExtensions("gl_ViewIndex", 1, &E_GL_EXT_multiview); + BuiltInVariable("gl_ViewIndex", EbvViewIndex, symbolTable); + if (version >= 300 /* both ES and non-ES */) { + symbolTable.setVariableExtensions("gl_ViewID_OVR", Num_OVR_multiview_EXTs, OVR_multiview_EXTs); + BuiltInVariable("gl_ViewID_OVR", EbvViewIndex, symbolTable); + } + + // GL_ARB_shader_ballot + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + symbolTable.setFunctionExtensions("subgroupBarrier", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupMemoryBarrier", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupMemoryBarrierBuffer", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupMemoryBarrierImage", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupElect", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupAll", 1, &E_GL_KHR_shader_subgroup_vote); + symbolTable.setFunctionExtensions("subgroupAny", 1, &E_GL_KHR_shader_subgroup_vote); + symbolTable.setFunctionExtensions("subgroupAllEqual", 1, &E_GL_KHR_shader_subgroup_vote); + symbolTable.setFunctionExtensions("subgroupBroadcast", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBroadcastFirst", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallot", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupInverseBallot", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotBitExtract", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotBitCount", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotInclusiveBitCount", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotExclusiveBitCount", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotFindLSB", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotFindMSB", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupShuffle", 1, &E_GL_KHR_shader_subgroup_shuffle); + symbolTable.setFunctionExtensions("subgroupShuffleXor", 1, &E_GL_KHR_shader_subgroup_shuffle); + symbolTable.setFunctionExtensions("subgroupShuffleUp", 1, &E_GL_KHR_shader_subgroup_shuffle_relative); + symbolTable.setFunctionExtensions("subgroupShuffleDown", 1, &E_GL_KHR_shader_subgroup_shuffle_relative); + symbolTable.setFunctionExtensions("subgroupAdd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupMul", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupMin", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupMax", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupAnd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupOr", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupXor", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveAdd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveMul", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveMin", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveMax", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveAnd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveOr", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveXor", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveAdd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveMul", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveMin", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveMax", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveAnd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveOr", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveXor", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupClusteredAdd", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredMul", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredMin", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredMax", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredAnd", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredOr", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredXor", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupQuadBroadcast", 1, &E_GL_KHR_shader_subgroup_quad); + symbolTable.setFunctionExtensions("subgroupQuadSwapHorizontal", 1, &E_GL_KHR_shader_subgroup_quad); + symbolTable.setFunctionExtensions("subgroupQuadSwapVertical", 1, &E_GL_KHR_shader_subgroup_quad); + symbolTable.setFunctionExtensions("subgroupQuadSwapDiagonal", 1, &E_GL_KHR_shader_subgroup_quad); + symbolTable.setFunctionExtensions("subgroupPartitionNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedAddNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedMulNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedMinNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedMaxNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedAndNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedOrNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedXorNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveAddNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveMulNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveMinNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveMaxNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveAndNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveOrNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveXorNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveAddNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveMulNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveMinNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveMaxNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveAndNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveOrNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveXorNV", 1, &E_GL_NV_shader_subgroup_partitioned); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + } + + if (profile == EEsProfile) { + symbolTable.setFunctionExtensions("shadow2DEXT", 1, &E_GL_EXT_shadow_samplers); + symbolTable.setFunctionExtensions("shadow2DProjEXT", 1, &E_GL_EXT_shadow_samplers); + } + + if (spvVersion.vulkan > 0) { + symbolTable.setVariableExtensions("gl_ScopeDevice", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_ScopeWorkgroup", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_ScopeSubgroup", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_ScopeInvocation", 1, &E_GL_KHR_memory_scope_semantics); + + symbolTable.setVariableExtensions("gl_SemanticsRelaxed", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsAcquire", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsRelease", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsAcquireRelease", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsMakeAvailable", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsMakeVisible", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsVolatile", 1, &E_GL_KHR_memory_scope_semantics); + + symbolTable.setVariableExtensions("gl_StorageSemanticsNone", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_StorageSemanticsBuffer", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_StorageSemanticsShared", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_StorageSemanticsImage", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_StorageSemanticsOutput", 1, &E_GL_KHR_memory_scope_semantics); + } + + symbolTable.setFunctionExtensions("helperInvocationEXT", 1, &E_GL_EXT_demote_to_helper_invocation); +#endif + break; + + case EShLangCompute: + BuiltInVariable("gl_NumWorkGroups", EbvNumWorkGroups, symbolTable); + BuiltInVariable("gl_WorkGroupSize", EbvWorkGroupSize, symbolTable); + BuiltInVariable("gl_WorkGroupID", EbvWorkGroupId, symbolTable); + BuiltInVariable("gl_LocalInvocationID", EbvLocalInvocationId, symbolTable); + BuiltInVariable("gl_GlobalInvocationID", EbvGlobalInvocationId, symbolTable); + BuiltInVariable("gl_LocalInvocationIndex", EbvLocalInvocationIndex, symbolTable); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + BuiltInVariable("gl_ViewIndex", EbvViewIndex, symbolTable); + +#ifndef GLSLANG_WEB + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + symbolTable.setVariableExtensions("gl_ViewIndex", 1, &E_GL_EXT_multiview); + } + + if (profile != EEsProfile && version < 430) { + symbolTable.setVariableExtensions("gl_NumWorkGroups", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_WorkGroupSize", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_WorkGroupID", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationID", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_GlobalInvocationID", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationIndex", 1, &E_GL_ARB_compute_shader); + + symbolTable.setVariableExtensions("gl_MaxComputeWorkGroupCount", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeWorkGroupSize", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeUniformComponents", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeTextureImageUnits", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeImageUniforms", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeAtomicCounters", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeAtomicCounterBuffers", 1, &E_GL_ARB_compute_shader); + + symbolTable.setFunctionExtensions("barrier", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("memoryBarrierAtomicCounter", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("memoryBarrierBuffer", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("memoryBarrierImage", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("memoryBarrierShared", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("groupMemoryBarrier", 1, &E_GL_ARB_compute_shader); + } + + + symbolTable.setFunctionExtensions("controlBarrier", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setFunctionExtensions("debugPrintfEXT", 1, &E_GL_EXT_debug_printf); + + // GL_ARB_shader_ballot + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic); + + BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable); + BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable); + + symbolTable.setFunctionExtensions("subgroupMemoryBarrierShared", 1, &E_GL_KHR_shader_subgroup_basic); + } + + { + const char *coopExt[2] = { E_GL_NV_cooperative_matrix, E_GL_NV_integer_cooperative_matrix }; + symbolTable.setFunctionExtensions("coopMatLoadNV", 2, coopExt); + symbolTable.setFunctionExtensions("coopMatStoreNV", 2, coopExt); + symbolTable.setFunctionExtensions("coopMatMulAddNV", 2, coopExt); + } + + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + symbolTable.setFunctionExtensions("dFdx", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdy", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("fwidth", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdxFine", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdyFine", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("fwidthFine", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdxCoarse", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdyCoarse", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("fwidthCoarse", 1, &E_GL_NV_compute_shader_derivatives); + } +#endif + break; + +#ifndef GLSLANG_WEB + case EShLangRayGen: + case EShLangIntersect: + case EShLangAnyHit: + case EShLangClosestHit: + case EShLangMiss: + case EShLangCallable: + if (profile != EEsProfile && version >= 460) { + const char *rtexts[] = { E_GL_NV_ray_tracing, E_GL_EXT_ray_tracing }; + symbolTable.setVariableExtensions("gl_LaunchIDNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_LaunchIDEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_LaunchSizeNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_LaunchSizeEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_PrimitiveID", 2, rtexts); + symbolTable.setVariableExtensions("gl_InstanceID", 2, rtexts); + symbolTable.setVariableExtensions("gl_InstanceCustomIndexNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_InstanceCustomIndexEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_GeometryIndexEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldRayOriginNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldRayOriginEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldRayDirectionNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldRayDirectionEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectRayOriginNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectRayOriginEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectRayDirectionNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectRayDirectionEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_RayTminNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_RayTminEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_RayTmaxNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_RayTmaxEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_HitTNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_HitTEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_HitKindNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_HitKindEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectToWorldNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectToWorldEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectToWorld3x4EXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldToObjectNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldToObjectEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldToObject3x4EXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_IncomingRayFlagsNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_IncomingRayFlagsEXT", 1, &E_GL_EXT_ray_tracing); + + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + + + symbolTable.setFunctionExtensions("traceNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("traceRayEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setFunctionExtensions("reportIntersectionNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("reportIntersectionEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setFunctionExtensions("ignoreIntersectionNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("ignoreIntersectionEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setFunctionExtensions("terminateRayNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("terminateRayEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setFunctionExtensions("executeCallableNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("executeCallableEXT", 1, &E_GL_EXT_ray_tracing); + + + BuiltInVariable("gl_LaunchIDNV", EbvLaunchId, symbolTable); + BuiltInVariable("gl_LaunchIDEXT", EbvLaunchId, symbolTable); + BuiltInVariable("gl_LaunchSizeNV", EbvLaunchSize, symbolTable); + BuiltInVariable("gl_LaunchSizeEXT", EbvLaunchSize, symbolTable); + BuiltInVariable("gl_PrimitiveID", EbvPrimitiveId, symbolTable); + BuiltInVariable("gl_InstanceID", EbvInstanceId, symbolTable); + BuiltInVariable("gl_InstanceCustomIndexNV", EbvInstanceCustomIndex,symbolTable); + BuiltInVariable("gl_InstanceCustomIndexEXT", EbvInstanceCustomIndex,symbolTable); + BuiltInVariable("gl_GeometryIndexEXT", EbvGeometryIndex, symbolTable); + BuiltInVariable("gl_WorldRayOriginNV", EbvWorldRayOrigin, symbolTable); + BuiltInVariable("gl_WorldRayOriginEXT", EbvWorldRayOrigin, symbolTable); + BuiltInVariable("gl_WorldRayDirectionNV", EbvWorldRayDirection, symbolTable); + BuiltInVariable("gl_WorldRayDirectionEXT", EbvWorldRayDirection, symbolTable); + BuiltInVariable("gl_ObjectRayOriginNV", EbvObjectRayOrigin, symbolTable); + BuiltInVariable("gl_ObjectRayOriginEXT", EbvObjectRayOrigin, symbolTable); + BuiltInVariable("gl_ObjectRayDirectionNV", EbvObjectRayDirection, symbolTable); + BuiltInVariable("gl_ObjectRayDirectionEXT", EbvObjectRayDirection, symbolTable); + BuiltInVariable("gl_RayTminNV", EbvRayTmin, symbolTable); + BuiltInVariable("gl_RayTminEXT", EbvRayTmin, symbolTable); + BuiltInVariable("gl_RayTmaxNV", EbvRayTmax, symbolTable); + BuiltInVariable("gl_RayTmaxEXT", EbvRayTmax, symbolTable); + BuiltInVariable("gl_HitTNV", EbvHitT, symbolTable); + BuiltInVariable("gl_HitTEXT", EbvHitT, symbolTable); + BuiltInVariable("gl_HitKindNV", EbvHitKind, symbolTable); + BuiltInVariable("gl_HitKindEXT", EbvHitKind, symbolTable); + BuiltInVariable("gl_ObjectToWorldNV", EbvObjectToWorld, symbolTable); + BuiltInVariable("gl_ObjectToWorldEXT", EbvObjectToWorld, symbolTable); + BuiltInVariable("gl_ObjectToWorld3x4EXT", EbvObjectToWorld3x4, symbolTable); + BuiltInVariable("gl_WorldToObjectNV", EbvWorldToObject, symbolTable); + BuiltInVariable("gl_WorldToObjectEXT", EbvWorldToObject, symbolTable); + BuiltInVariable("gl_WorldToObject3x4EXT", EbvWorldToObject3x4, symbolTable); + BuiltInVariable("gl_IncomingRayFlagsNV", EbvIncomingRayFlags, symbolTable); + BuiltInVariable("gl_IncomingRayFlagsEXT", EbvIncomingRayFlags, symbolTable); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + + // GL_ARB_shader_ballot + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + + // GL_KHR_shader_subgroup + symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable); + BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable); + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + } + break; + + case EShLangMeshNV: + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + // per-vertex builtins + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_Position", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_PointSize", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_ClipDistance", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_CullDistance", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_MeshVerticesNV", "gl_Position", EbvPosition, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_PointSize", EbvPointSize, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_CullDistance", EbvCullDistance, symbolTable); + + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_PositionPerViewNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_ClipDistancePerViewNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_CullDistancePerViewNV", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_MeshVerticesNV", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_ClipDistancePerViewNV", EbvClipDistancePerViewNV, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_CullDistancePerViewNV", EbvCullDistancePerViewNV, symbolTable); + + // per-primitive builtins + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_PrimitiveID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_Layer", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_ViewportIndex", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_ViewportMask", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_MeshPrimitivesNV", "gl_PrimitiveID", EbvPrimitiveId, symbolTable); + BuiltInVariable("gl_MeshPrimitivesNV", "gl_Layer", EbvLayer, symbolTable); + BuiltInVariable("gl_MeshPrimitivesNV", "gl_ViewportIndex", EbvViewportIndex, symbolTable); + BuiltInVariable("gl_MeshPrimitivesNV", "gl_ViewportMask", EbvViewportMaskNV, symbolTable); + + // per-view per-primitive builtins + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_LayerPerViewNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_ViewportMaskPerViewNV", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_MeshPrimitivesNV", "gl_LayerPerViewNV", EbvLayerPerViewNV, symbolTable); + BuiltInVariable("gl_MeshPrimitivesNV", "gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable); + + // other builtins + symbolTable.setVariableExtensions("gl_PrimitiveCountNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_PrimitiveIndicesNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshViewCountNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshViewIndicesNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_WorkGroupSize", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_WorkGroupID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_GlobalInvocationID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationIndex", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_PrimitiveCountNV", EbvPrimitiveCountNV, symbolTable); + BuiltInVariable("gl_PrimitiveIndicesNV", EbvPrimitiveIndicesNV, symbolTable); + BuiltInVariable("gl_MeshViewCountNV", EbvMeshViewCountNV, symbolTable); + BuiltInVariable("gl_MeshViewIndicesNV", EbvMeshViewIndicesNV, symbolTable); + BuiltInVariable("gl_WorkGroupSize", EbvWorkGroupSize, symbolTable); + BuiltInVariable("gl_WorkGroupID", EbvWorkGroupId, symbolTable); + BuiltInVariable("gl_LocalInvocationID", EbvLocalInvocationId, symbolTable); + BuiltInVariable("gl_GlobalInvocationID", EbvGlobalInvocationId, symbolTable); + BuiltInVariable("gl_LocalInvocationIndex", EbvLocalInvocationIndex, symbolTable); + + // builtin constants + symbolTable.setVariableExtensions("gl_MaxMeshOutputVerticesNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MaxMeshOutputPrimitivesNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MaxMeshWorkGroupSizeNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MaxMeshViewCountNV", 1, &E_GL_NV_mesh_shader); + + // builtin functions + symbolTable.setFunctionExtensions("barrier", 1, &E_GL_NV_mesh_shader); + symbolTable.setFunctionExtensions("memoryBarrierShared", 1, &E_GL_NV_mesh_shader); + symbolTable.setFunctionExtensions("groupMemoryBarrier", 1, &E_GL_NV_mesh_shader); + } + + if (profile != EEsProfile && version >= 450) { + // GL_EXT_device_group + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + + // GL_ARB_shader_draw_parameters + symbolTable.setVariableExtensions("gl_DrawIDARB", 1, &E_GL_ARB_shader_draw_parameters); + BuiltInVariable("gl_DrawIDARB", EbvDrawId, symbolTable); + if (version >= 460) { + BuiltInVariable("gl_DrawID", EbvDrawId, symbolTable); + } + + // GL_ARB_shader_ballot + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable); + BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable); + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + symbolTable.setFunctionExtensions("subgroupMemoryBarrierShared", 1, &E_GL_KHR_shader_subgroup_basic); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + } + break; + + case EShLangTaskNV: + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + symbolTable.setVariableExtensions("gl_TaskCountNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_WorkGroupSize", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_WorkGroupID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_GlobalInvocationID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationIndex", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshViewCountNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshViewIndicesNV", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_TaskCountNV", EbvTaskCountNV, symbolTable); + BuiltInVariable("gl_WorkGroupSize", EbvWorkGroupSize, symbolTable); + BuiltInVariable("gl_WorkGroupID", EbvWorkGroupId, symbolTable); + BuiltInVariable("gl_LocalInvocationID", EbvLocalInvocationId, symbolTable); + BuiltInVariable("gl_GlobalInvocationID", EbvGlobalInvocationId, symbolTable); + BuiltInVariable("gl_LocalInvocationIndex", EbvLocalInvocationIndex, symbolTable); + BuiltInVariable("gl_MeshViewCountNV", EbvMeshViewCountNV, symbolTable); + BuiltInVariable("gl_MeshViewIndicesNV", EbvMeshViewIndicesNV, symbolTable); + + symbolTable.setVariableExtensions("gl_MaxTaskWorkGroupSizeNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MaxMeshViewCountNV", 1, &E_GL_NV_mesh_shader); + + symbolTable.setFunctionExtensions("barrier", 1, &E_GL_NV_mesh_shader); + symbolTable.setFunctionExtensions("memoryBarrierShared", 1, &E_GL_NV_mesh_shader); + symbolTable.setFunctionExtensions("groupMemoryBarrier", 1, &E_GL_NV_mesh_shader); + } + + if (profile != EEsProfile && version >= 450) { + // GL_EXT_device_group + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + + // GL_ARB_shader_draw_parameters + symbolTable.setVariableExtensions("gl_DrawIDARB", 1, &E_GL_ARB_shader_draw_parameters); + BuiltInVariable("gl_DrawIDARB", EbvDrawId, symbolTable); + if (version >= 460) { + BuiltInVariable("gl_DrawID", EbvDrawId, symbolTable); + } + + // GL_ARB_shader_ballot + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable); + BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable); + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + symbolTable.setFunctionExtensions("subgroupMemoryBarrierShared", 1, &E_GL_KHR_shader_subgroup_basic); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + } + break; +#endif + + default: + assert(false && "Language not supported"); + break; + } + + // + // Next, identify which built-ins have a mapping to an operator. + // If PureOperatorBuiltins is false, those that are not identified as such are + // expected to be resolved through a library of functions, versus as + // operations. + // + + relateTabledBuiltins(version, profile, spvVersion, language, symbolTable); + +#ifndef GLSLANG_WEB + symbolTable.relateToOperator("doubleBitsToInt64", EOpDoubleBitsToInt64); + symbolTable.relateToOperator("doubleBitsToUint64", EOpDoubleBitsToUint64); + symbolTable.relateToOperator("int64BitsToDouble", EOpInt64BitsToDouble); + symbolTable.relateToOperator("uint64BitsToDouble", EOpUint64BitsToDouble); + symbolTable.relateToOperator("halfBitsToInt16", EOpFloat16BitsToInt16); + symbolTable.relateToOperator("halfBitsToUint16", EOpFloat16BitsToUint16); + symbolTable.relateToOperator("float16BitsToInt16", EOpFloat16BitsToInt16); + symbolTable.relateToOperator("float16BitsToUint16", EOpFloat16BitsToUint16); + symbolTable.relateToOperator("int16BitsToFloat16", EOpInt16BitsToFloat16); + symbolTable.relateToOperator("uint16BitsToFloat16", EOpUint16BitsToFloat16); + + symbolTable.relateToOperator("int16BitsToHalf", EOpInt16BitsToFloat16); + symbolTable.relateToOperator("uint16BitsToHalf", EOpUint16BitsToFloat16); + + symbolTable.relateToOperator("packSnorm4x8", EOpPackSnorm4x8); + symbolTable.relateToOperator("unpackSnorm4x8", EOpUnpackSnorm4x8); + symbolTable.relateToOperator("packUnorm4x8", EOpPackUnorm4x8); + symbolTable.relateToOperator("unpackUnorm4x8", EOpUnpackUnorm4x8); + + symbolTable.relateToOperator("packDouble2x32", EOpPackDouble2x32); + symbolTable.relateToOperator("unpackDouble2x32", EOpUnpackDouble2x32); + + symbolTable.relateToOperator("packInt2x32", EOpPackInt2x32); + symbolTable.relateToOperator("unpackInt2x32", EOpUnpackInt2x32); + symbolTable.relateToOperator("packUint2x32", EOpPackUint2x32); + symbolTable.relateToOperator("unpackUint2x32", EOpUnpackUint2x32); + + symbolTable.relateToOperator("packInt2x16", EOpPackInt2x16); + symbolTable.relateToOperator("unpackInt2x16", EOpUnpackInt2x16); + symbolTable.relateToOperator("packUint2x16", EOpPackUint2x16); + symbolTable.relateToOperator("unpackUint2x16", EOpUnpackUint2x16); + + symbolTable.relateToOperator("packInt4x16", EOpPackInt4x16); + symbolTable.relateToOperator("unpackInt4x16", EOpUnpackInt4x16); + symbolTable.relateToOperator("packUint4x16", EOpPackUint4x16); + symbolTable.relateToOperator("unpackUint4x16", EOpUnpackUint4x16); + symbolTable.relateToOperator("packFloat2x16", EOpPackFloat2x16); + symbolTable.relateToOperator("unpackFloat2x16", EOpUnpackFloat2x16); + + symbolTable.relateToOperator("pack16", EOpPack16); + symbolTable.relateToOperator("pack32", EOpPack32); + symbolTable.relateToOperator("pack64", EOpPack64); + + symbolTable.relateToOperator("unpack32", EOpUnpack32); + symbolTable.relateToOperator("unpack16", EOpUnpack16); + symbolTable.relateToOperator("unpack8", EOpUnpack8); + + symbolTable.relateToOperator("controlBarrier", EOpBarrier); + symbolTable.relateToOperator("memoryBarrierAtomicCounter", EOpMemoryBarrierAtomicCounter); + symbolTable.relateToOperator("memoryBarrierImage", EOpMemoryBarrierImage); + + symbolTable.relateToOperator("atomicLoad", EOpAtomicLoad); + symbolTable.relateToOperator("atomicStore", EOpAtomicStore); + + symbolTable.relateToOperator("atomicCounterIncrement", EOpAtomicCounterIncrement); + symbolTable.relateToOperator("atomicCounterDecrement", EOpAtomicCounterDecrement); + symbolTable.relateToOperator("atomicCounter", EOpAtomicCounter); + + symbolTable.relateToOperator("clockARB", EOpReadClockSubgroupKHR); + symbolTable.relateToOperator("clock2x32ARB", EOpReadClockSubgroupKHR); + + symbolTable.relateToOperator("clockRealtimeEXT", EOpReadClockDeviceKHR); + symbolTable.relateToOperator("clockRealtime2x32EXT", EOpReadClockDeviceKHR); + + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("atomicCounterAdd", EOpAtomicCounterAdd); + symbolTable.relateToOperator("atomicCounterSubtract", EOpAtomicCounterSubtract); + symbolTable.relateToOperator("atomicCounterMin", EOpAtomicCounterMin); + symbolTable.relateToOperator("atomicCounterMax", EOpAtomicCounterMax); + symbolTable.relateToOperator("atomicCounterAnd", EOpAtomicCounterAnd); + symbolTable.relateToOperator("atomicCounterOr", EOpAtomicCounterOr); + symbolTable.relateToOperator("atomicCounterXor", EOpAtomicCounterXor); + symbolTable.relateToOperator("atomicCounterExchange", EOpAtomicCounterExchange); + symbolTable.relateToOperator("atomicCounterCompSwap", EOpAtomicCounterCompSwap); + } + + symbolTable.relateToOperator("fma", EOpFma); + symbolTable.relateToOperator("frexp", EOpFrexp); + symbolTable.relateToOperator("ldexp", EOpLdexp); + symbolTable.relateToOperator("uaddCarry", EOpAddCarry); + symbolTable.relateToOperator("usubBorrow", EOpSubBorrow); + symbolTable.relateToOperator("umulExtended", EOpUMulExtended); + symbolTable.relateToOperator("imulExtended", EOpIMulExtended); + symbolTable.relateToOperator("bitfieldExtract", EOpBitfieldExtract); + symbolTable.relateToOperator("bitfieldInsert", EOpBitfieldInsert); + symbolTable.relateToOperator("bitfieldReverse", EOpBitFieldReverse); + symbolTable.relateToOperator("bitCount", EOpBitCount); + symbolTable.relateToOperator("findLSB", EOpFindLSB); + symbolTable.relateToOperator("findMSB", EOpFindMSB); + + symbolTable.relateToOperator("helperInvocationEXT", EOpIsHelperInvocation); + + symbolTable.relateToOperator("countLeadingZeros", EOpCountLeadingZeros); + symbolTable.relateToOperator("countTrailingZeros", EOpCountTrailingZeros); + symbolTable.relateToOperator("absoluteDifference", EOpAbsDifference); + symbolTable.relateToOperator("addSaturate", EOpAddSaturate); + symbolTable.relateToOperator("subtractSaturate", EOpSubSaturate); + symbolTable.relateToOperator("average", EOpAverage); + symbolTable.relateToOperator("averageRounded", EOpAverageRounded); + symbolTable.relateToOperator("multiply32x16", EOpMul32x16); + symbolTable.relateToOperator("debugPrintfEXT", EOpDebugPrintf); + + + if (PureOperatorBuiltins) { + symbolTable.relateToOperator("imageSize", EOpImageQuerySize); + symbolTable.relateToOperator("imageSamples", EOpImageQuerySamples); + symbolTable.relateToOperator("imageLoad", EOpImageLoad); + symbolTable.relateToOperator("imageStore", EOpImageStore); + symbolTable.relateToOperator("imageAtomicAdd", EOpImageAtomicAdd); + symbolTable.relateToOperator("imageAtomicMin", EOpImageAtomicMin); + symbolTable.relateToOperator("imageAtomicMax", EOpImageAtomicMax); + symbolTable.relateToOperator("imageAtomicAnd", EOpImageAtomicAnd); + symbolTable.relateToOperator("imageAtomicOr", EOpImageAtomicOr); + symbolTable.relateToOperator("imageAtomicXor", EOpImageAtomicXor); + symbolTable.relateToOperator("imageAtomicExchange", EOpImageAtomicExchange); + symbolTable.relateToOperator("imageAtomicCompSwap", EOpImageAtomicCompSwap); + symbolTable.relateToOperator("imageAtomicLoad", EOpImageAtomicLoad); + symbolTable.relateToOperator("imageAtomicStore", EOpImageAtomicStore); + + symbolTable.relateToOperator("subpassLoad", EOpSubpassLoad); + symbolTable.relateToOperator("subpassLoadMS", EOpSubpassLoadMS); + + symbolTable.relateToOperator("textureGather", EOpTextureGather); + symbolTable.relateToOperator("textureGatherOffset", EOpTextureGatherOffset); + symbolTable.relateToOperator("textureGatherOffsets", EOpTextureGatherOffsets); + + symbolTable.relateToOperator("noise1", EOpNoise); + symbolTable.relateToOperator("noise2", EOpNoise); + symbolTable.relateToOperator("noise3", EOpNoise); + symbolTable.relateToOperator("noise4", EOpNoise); + + symbolTable.relateToOperator("textureFootprintNV", EOpImageSampleFootprintNV); + symbolTable.relateToOperator("textureFootprintClampNV", EOpImageSampleFootprintClampNV); + symbolTable.relateToOperator("textureFootprintLodNV", EOpImageSampleFootprintLodNV); + symbolTable.relateToOperator("textureFootprintGradNV", EOpImageSampleFootprintGradNV); + symbolTable.relateToOperator("textureFootprintGradClampNV", EOpImageSampleFootprintGradClampNV); + + if (spvVersion.spv == 0 && IncludeLegacy(version, profile, spvVersion)) + symbolTable.relateToOperator("ftransform", EOpFtransform); + + if (spvVersion.spv == 0 && (IncludeLegacy(version, profile, spvVersion) || + (profile == EEsProfile && version == 100))) { + + symbolTable.relateToOperator("texture1D", EOpTexture); + symbolTable.relateToOperator("texture1DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("texture1DProj", EOpTextureProj); + symbolTable.relateToOperator("texture1DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("texture1DLod", EOpTextureLod); + symbolTable.relateToOperator("texture1DProjLod", EOpTextureProjLod); + + symbolTable.relateToOperator("texture2DRect", EOpTexture); + symbolTable.relateToOperator("texture2DRectProj", EOpTextureProj); + symbolTable.relateToOperator("texture2DRectGradARB", EOpTextureGrad); + symbolTable.relateToOperator("texture2DRectProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("shadow2DRect", EOpTexture); + symbolTable.relateToOperator("shadow2DRectProj", EOpTextureProj); + symbolTable.relateToOperator("shadow2DRectGradARB", EOpTextureGrad); + symbolTable.relateToOperator("shadow2DRectProjGradARB", EOpTextureProjGrad); + + symbolTable.relateToOperator("texture2D", EOpTexture); + symbolTable.relateToOperator("texture2DProj", EOpTextureProj); + symbolTable.relateToOperator("texture2DGradEXT", EOpTextureGrad); + symbolTable.relateToOperator("texture2DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("texture2DProjGradEXT", EOpTextureProjGrad); + symbolTable.relateToOperator("texture2DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("texture2DLod", EOpTextureLod); + symbolTable.relateToOperator("texture2DLodEXT", EOpTextureLod); + symbolTable.relateToOperator("texture2DProjLod", EOpTextureProjLod); + symbolTable.relateToOperator("texture2DProjLodEXT", EOpTextureProjLod); + + symbolTable.relateToOperator("texture3D", EOpTexture); + symbolTable.relateToOperator("texture3DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("texture3DProj", EOpTextureProj); + symbolTable.relateToOperator("texture3DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("texture3DLod", EOpTextureLod); + symbolTable.relateToOperator("texture3DProjLod", EOpTextureProjLod); + symbolTable.relateToOperator("textureCube", EOpTexture); + symbolTable.relateToOperator("textureCubeGradEXT", EOpTextureGrad); + symbolTable.relateToOperator("textureCubeGradARB", EOpTextureGrad); + symbolTable.relateToOperator("textureCubeLod", EOpTextureLod); + symbolTable.relateToOperator("textureCubeLodEXT", EOpTextureLod); + symbolTable.relateToOperator("shadow1D", EOpTexture); + symbolTable.relateToOperator("shadow1DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("shadow2D", EOpTexture); + symbolTable.relateToOperator("shadow2DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("shadow1DProj", EOpTextureProj); + symbolTable.relateToOperator("shadow2DProj", EOpTextureProj); + symbolTable.relateToOperator("shadow1DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("shadow2DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("shadow1DLod", EOpTextureLod); + symbolTable.relateToOperator("shadow2DLod", EOpTextureLod); + symbolTable.relateToOperator("shadow1DProjLod", EOpTextureProjLod); + symbolTable.relateToOperator("shadow2DProjLod", EOpTextureProjLod); + } + + if (profile != EEsProfile) { + symbolTable.relateToOperator("sparseTextureARB", EOpSparseTexture); + symbolTable.relateToOperator("sparseTextureLodARB", EOpSparseTextureLod); + symbolTable.relateToOperator("sparseTextureOffsetARB", EOpSparseTextureOffset); + symbolTable.relateToOperator("sparseTexelFetchARB", EOpSparseTextureFetch); + symbolTable.relateToOperator("sparseTexelFetchOffsetARB", EOpSparseTextureFetchOffset); + symbolTable.relateToOperator("sparseTextureLodOffsetARB", EOpSparseTextureLodOffset); + symbolTable.relateToOperator("sparseTextureGradARB", EOpSparseTextureGrad); + symbolTable.relateToOperator("sparseTextureGradOffsetARB", EOpSparseTextureGradOffset); + symbolTable.relateToOperator("sparseTextureGatherARB", EOpSparseTextureGather); + symbolTable.relateToOperator("sparseTextureGatherOffsetARB", EOpSparseTextureGatherOffset); + symbolTable.relateToOperator("sparseTextureGatherOffsetsARB", EOpSparseTextureGatherOffsets); + symbolTable.relateToOperator("sparseImageLoadARB", EOpSparseImageLoad); + symbolTable.relateToOperator("sparseTexelsResidentARB", EOpSparseTexelsResident); + + symbolTable.relateToOperator("sparseTextureClampARB", EOpSparseTextureClamp); + symbolTable.relateToOperator("sparseTextureOffsetClampARB", EOpSparseTextureOffsetClamp); + symbolTable.relateToOperator("sparseTextureGradClampARB", EOpSparseTextureGradClamp); + symbolTable.relateToOperator("sparseTextureGradOffsetClampARB", EOpSparseTextureGradOffsetClamp); + symbolTable.relateToOperator("textureClampARB", EOpTextureClamp); + symbolTable.relateToOperator("textureOffsetClampARB", EOpTextureOffsetClamp); + symbolTable.relateToOperator("textureGradClampARB", EOpTextureGradClamp); + symbolTable.relateToOperator("textureGradOffsetClampARB", EOpTextureGradOffsetClamp); + + symbolTable.relateToOperator("ballotARB", EOpBallot); + symbolTable.relateToOperator("readInvocationARB", EOpReadInvocation); + symbolTable.relateToOperator("readFirstInvocationARB", EOpReadFirstInvocation); + + if (version >= 430) { + symbolTable.relateToOperator("anyInvocationARB", EOpAnyInvocation); + symbolTable.relateToOperator("allInvocationsARB", EOpAllInvocations); + symbolTable.relateToOperator("allInvocationsEqualARB", EOpAllInvocationsEqual); + } + if (version >= 460) { + symbolTable.relateToOperator("anyInvocation", EOpAnyInvocation); + symbolTable.relateToOperator("allInvocations", EOpAllInvocations); + symbolTable.relateToOperator("allInvocationsEqual", EOpAllInvocationsEqual); + } + symbolTable.relateToOperator("minInvocationsAMD", EOpMinInvocations); + symbolTable.relateToOperator("maxInvocationsAMD", EOpMaxInvocations); + symbolTable.relateToOperator("addInvocationsAMD", EOpAddInvocations); + symbolTable.relateToOperator("minInvocationsNonUniformAMD", EOpMinInvocationsNonUniform); + symbolTable.relateToOperator("maxInvocationsNonUniformAMD", EOpMaxInvocationsNonUniform); + symbolTable.relateToOperator("addInvocationsNonUniformAMD", EOpAddInvocationsNonUniform); + symbolTable.relateToOperator("minInvocationsInclusiveScanAMD", EOpMinInvocationsInclusiveScan); + symbolTable.relateToOperator("maxInvocationsInclusiveScanAMD", EOpMaxInvocationsInclusiveScan); + symbolTable.relateToOperator("addInvocationsInclusiveScanAMD", EOpAddInvocationsInclusiveScan); + symbolTable.relateToOperator("minInvocationsInclusiveScanNonUniformAMD", EOpMinInvocationsInclusiveScanNonUniform); + symbolTable.relateToOperator("maxInvocationsInclusiveScanNonUniformAMD", EOpMaxInvocationsInclusiveScanNonUniform); + symbolTable.relateToOperator("addInvocationsInclusiveScanNonUniformAMD", EOpAddInvocationsInclusiveScanNonUniform); + symbolTable.relateToOperator("minInvocationsExclusiveScanAMD", EOpMinInvocationsExclusiveScan); + symbolTable.relateToOperator("maxInvocationsExclusiveScanAMD", EOpMaxInvocationsExclusiveScan); + symbolTable.relateToOperator("addInvocationsExclusiveScanAMD", EOpAddInvocationsExclusiveScan); + symbolTable.relateToOperator("minInvocationsExclusiveScanNonUniformAMD", EOpMinInvocationsExclusiveScanNonUniform); + symbolTable.relateToOperator("maxInvocationsExclusiveScanNonUniformAMD", EOpMaxInvocationsExclusiveScanNonUniform); + symbolTable.relateToOperator("addInvocationsExclusiveScanNonUniformAMD", EOpAddInvocationsExclusiveScanNonUniform); + symbolTable.relateToOperator("swizzleInvocationsAMD", EOpSwizzleInvocations); + symbolTable.relateToOperator("swizzleInvocationsMaskedAMD", EOpSwizzleInvocationsMasked); + symbolTable.relateToOperator("writeInvocationAMD", EOpWriteInvocation); + symbolTable.relateToOperator("mbcntAMD", EOpMbcnt); + + symbolTable.relateToOperator("min3", EOpMin3); + symbolTable.relateToOperator("max3", EOpMax3); + symbolTable.relateToOperator("mid3", EOpMid3); + + symbolTable.relateToOperator("cubeFaceIndexAMD", EOpCubeFaceIndex); + symbolTable.relateToOperator("cubeFaceCoordAMD", EOpCubeFaceCoord); + symbolTable.relateToOperator("timeAMD", EOpTime); + + symbolTable.relateToOperator("textureGatherLodAMD", EOpTextureGatherLod); + symbolTable.relateToOperator("textureGatherLodOffsetAMD", EOpTextureGatherLodOffset); + symbolTable.relateToOperator("textureGatherLodOffsetsAMD", EOpTextureGatherLodOffsets); + symbolTable.relateToOperator("sparseTextureGatherLodAMD", EOpSparseTextureGatherLod); + symbolTable.relateToOperator("sparseTextureGatherLodOffsetAMD", EOpSparseTextureGatherLodOffset); + symbolTable.relateToOperator("sparseTextureGatherLodOffsetsAMD", EOpSparseTextureGatherLodOffsets); + + symbolTable.relateToOperator("imageLoadLodAMD", EOpImageLoadLod); + symbolTable.relateToOperator("imageStoreLodAMD", EOpImageStoreLod); + symbolTable.relateToOperator("sparseImageLoadLodAMD", EOpSparseImageLoadLod); + + symbolTable.relateToOperator("fragmentMaskFetchAMD", EOpFragmentMaskFetch); + symbolTable.relateToOperator("fragmentFetchAMD", EOpFragmentFetch); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.relateToOperator("subgroupBarrier", EOpSubgroupBarrier); + symbolTable.relateToOperator("subgroupMemoryBarrier", EOpSubgroupMemoryBarrier); + symbolTable.relateToOperator("subgroupMemoryBarrierBuffer", EOpSubgroupMemoryBarrierBuffer); + symbolTable.relateToOperator("subgroupMemoryBarrierImage", EOpSubgroupMemoryBarrierImage); + symbolTable.relateToOperator("subgroupElect", EOpSubgroupElect); + symbolTable.relateToOperator("subgroupAll", EOpSubgroupAll); + symbolTable.relateToOperator("subgroupAny", EOpSubgroupAny); + symbolTable.relateToOperator("subgroupAllEqual", EOpSubgroupAllEqual); + symbolTable.relateToOperator("subgroupBroadcast", EOpSubgroupBroadcast); + symbolTable.relateToOperator("subgroupBroadcastFirst", EOpSubgroupBroadcastFirst); + symbolTable.relateToOperator("subgroupBallot", EOpSubgroupBallot); + symbolTable.relateToOperator("subgroupInverseBallot", EOpSubgroupInverseBallot); + symbolTable.relateToOperator("subgroupBallotBitExtract", EOpSubgroupBallotBitExtract); + symbolTable.relateToOperator("subgroupBallotBitCount", EOpSubgroupBallotBitCount); + symbolTable.relateToOperator("subgroupBallotInclusiveBitCount", EOpSubgroupBallotInclusiveBitCount); + symbolTable.relateToOperator("subgroupBallotExclusiveBitCount", EOpSubgroupBallotExclusiveBitCount); + symbolTable.relateToOperator("subgroupBallotFindLSB", EOpSubgroupBallotFindLSB); + symbolTable.relateToOperator("subgroupBallotFindMSB", EOpSubgroupBallotFindMSB); + symbolTable.relateToOperator("subgroupShuffle", EOpSubgroupShuffle); + symbolTable.relateToOperator("subgroupShuffleXor", EOpSubgroupShuffleXor); + symbolTable.relateToOperator("subgroupShuffleUp", EOpSubgroupShuffleUp); + symbolTable.relateToOperator("subgroupShuffleDown", EOpSubgroupShuffleDown); + symbolTable.relateToOperator("subgroupAdd", EOpSubgroupAdd); + symbolTable.relateToOperator("subgroupMul", EOpSubgroupMul); + symbolTable.relateToOperator("subgroupMin", EOpSubgroupMin); + symbolTable.relateToOperator("subgroupMax", EOpSubgroupMax); + symbolTable.relateToOperator("subgroupAnd", EOpSubgroupAnd); + symbolTable.relateToOperator("subgroupOr", EOpSubgroupOr); + symbolTable.relateToOperator("subgroupXor", EOpSubgroupXor); + symbolTable.relateToOperator("subgroupInclusiveAdd", EOpSubgroupInclusiveAdd); + symbolTable.relateToOperator("subgroupInclusiveMul", EOpSubgroupInclusiveMul); + symbolTable.relateToOperator("subgroupInclusiveMin", EOpSubgroupInclusiveMin); + symbolTable.relateToOperator("subgroupInclusiveMax", EOpSubgroupInclusiveMax); + symbolTable.relateToOperator("subgroupInclusiveAnd", EOpSubgroupInclusiveAnd); + symbolTable.relateToOperator("subgroupInclusiveOr", EOpSubgroupInclusiveOr); + symbolTable.relateToOperator("subgroupInclusiveXor", EOpSubgroupInclusiveXor); + symbolTable.relateToOperator("subgroupExclusiveAdd", EOpSubgroupExclusiveAdd); + symbolTable.relateToOperator("subgroupExclusiveMul", EOpSubgroupExclusiveMul); + symbolTable.relateToOperator("subgroupExclusiveMin", EOpSubgroupExclusiveMin); + symbolTable.relateToOperator("subgroupExclusiveMax", EOpSubgroupExclusiveMax); + symbolTable.relateToOperator("subgroupExclusiveAnd", EOpSubgroupExclusiveAnd); + symbolTable.relateToOperator("subgroupExclusiveOr", EOpSubgroupExclusiveOr); + symbolTable.relateToOperator("subgroupExclusiveXor", EOpSubgroupExclusiveXor); + symbolTable.relateToOperator("subgroupClusteredAdd", EOpSubgroupClusteredAdd); + symbolTable.relateToOperator("subgroupClusteredMul", EOpSubgroupClusteredMul); + symbolTable.relateToOperator("subgroupClusteredMin", EOpSubgroupClusteredMin); + symbolTable.relateToOperator("subgroupClusteredMax", EOpSubgroupClusteredMax); + symbolTable.relateToOperator("subgroupClusteredAnd", EOpSubgroupClusteredAnd); + symbolTable.relateToOperator("subgroupClusteredOr", EOpSubgroupClusteredOr); + symbolTable.relateToOperator("subgroupClusteredXor", EOpSubgroupClusteredXor); + symbolTable.relateToOperator("subgroupQuadBroadcast", EOpSubgroupQuadBroadcast); + symbolTable.relateToOperator("subgroupQuadSwapHorizontal", EOpSubgroupQuadSwapHorizontal); + symbolTable.relateToOperator("subgroupQuadSwapVertical", EOpSubgroupQuadSwapVertical); + symbolTable.relateToOperator("subgroupQuadSwapDiagonal", EOpSubgroupQuadSwapDiagonal); + + symbolTable.relateToOperator("subgroupPartitionNV", EOpSubgroupPartition); + symbolTable.relateToOperator("subgroupPartitionedAddNV", EOpSubgroupPartitionedAdd); + symbolTable.relateToOperator("subgroupPartitionedMulNV", EOpSubgroupPartitionedMul); + symbolTable.relateToOperator("subgroupPartitionedMinNV", EOpSubgroupPartitionedMin); + symbolTable.relateToOperator("subgroupPartitionedMaxNV", EOpSubgroupPartitionedMax); + symbolTable.relateToOperator("subgroupPartitionedAndNV", EOpSubgroupPartitionedAnd); + symbolTable.relateToOperator("subgroupPartitionedOrNV", EOpSubgroupPartitionedOr); + symbolTable.relateToOperator("subgroupPartitionedXorNV", EOpSubgroupPartitionedXor); + symbolTable.relateToOperator("subgroupPartitionedInclusiveAddNV", EOpSubgroupPartitionedInclusiveAdd); + symbolTable.relateToOperator("subgroupPartitionedInclusiveMulNV", EOpSubgroupPartitionedInclusiveMul); + symbolTable.relateToOperator("subgroupPartitionedInclusiveMinNV", EOpSubgroupPartitionedInclusiveMin); + symbolTable.relateToOperator("subgroupPartitionedInclusiveMaxNV", EOpSubgroupPartitionedInclusiveMax); + symbolTable.relateToOperator("subgroupPartitionedInclusiveAndNV", EOpSubgroupPartitionedInclusiveAnd); + symbolTable.relateToOperator("subgroupPartitionedInclusiveOrNV", EOpSubgroupPartitionedInclusiveOr); + symbolTable.relateToOperator("subgroupPartitionedInclusiveXorNV", EOpSubgroupPartitionedInclusiveXor); + symbolTable.relateToOperator("subgroupPartitionedExclusiveAddNV", EOpSubgroupPartitionedExclusiveAdd); + symbolTable.relateToOperator("subgroupPartitionedExclusiveMulNV", EOpSubgroupPartitionedExclusiveMul); + symbolTable.relateToOperator("subgroupPartitionedExclusiveMinNV", EOpSubgroupPartitionedExclusiveMin); + symbolTable.relateToOperator("subgroupPartitionedExclusiveMaxNV", EOpSubgroupPartitionedExclusiveMax); + symbolTable.relateToOperator("subgroupPartitionedExclusiveAndNV", EOpSubgroupPartitionedExclusiveAnd); + symbolTable.relateToOperator("subgroupPartitionedExclusiveOrNV", EOpSubgroupPartitionedExclusiveOr); + symbolTable.relateToOperator("subgroupPartitionedExclusiveXorNV", EOpSubgroupPartitionedExclusiveXor); + } + + if (profile == EEsProfile) { + symbolTable.relateToOperator("shadow2DEXT", EOpTexture); + symbolTable.relateToOperator("shadow2DProjEXT", EOpTextureProj); + } + } + + switch(language) { + case EShLangVertex: + break; + + case EShLangTessControl: + case EShLangTessEvaluation: + break; + + case EShLangGeometry: + symbolTable.relateToOperator("EmitStreamVertex", EOpEmitStreamVertex); + symbolTable.relateToOperator("EndStreamPrimitive", EOpEndStreamPrimitive); + symbolTable.relateToOperator("EmitVertex", EOpEmitVertex); + symbolTable.relateToOperator("EndPrimitive", EOpEndPrimitive); + break; + + case EShLangFragment: + if (profile != EEsProfile && version >= 400) { + symbolTable.relateToOperator("dFdxFine", EOpDPdxFine); + symbolTable.relateToOperator("dFdyFine", EOpDPdyFine); + symbolTable.relateToOperator("fwidthFine", EOpFwidthFine); + symbolTable.relateToOperator("dFdxCoarse", EOpDPdxCoarse); + symbolTable.relateToOperator("dFdyCoarse", EOpDPdyCoarse); + symbolTable.relateToOperator("fwidthCoarse", EOpFwidthCoarse); + } + + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("rayQueryInitializeEXT", EOpRayQueryInitialize); + symbolTable.relateToOperator("rayQueryTerminateEXT", EOpRayQueryTerminate); + symbolTable.relateToOperator("rayQueryGenerateIntersectionEXT", EOpRayQueryGenerateIntersection); + symbolTable.relateToOperator("rayQueryConfirmIntersectionEXT", EOpRayQueryConfirmIntersection); + symbolTable.relateToOperator("rayQueryProceedEXT", EOpRayQueryProceed); + symbolTable.relateToOperator("rayQueryGetIntersectionTypeEXT", EOpRayQueryGetIntersectionType); + symbolTable.relateToOperator("rayQueryGetRayTMinEXT", EOpRayQueryGetRayTMin); + symbolTable.relateToOperator("rayQueryGetRayFlagsEXT", EOpRayQueryGetRayFlags); + symbolTable.relateToOperator("rayQueryGetIntersectionTEXT", EOpRayQueryGetIntersectionT); + symbolTable.relateToOperator("rayQueryGetIntersectionInstanceCustomIndexEXT", EOpRayQueryGetIntersectionInstanceCustomIndex); + symbolTable.relateToOperator("rayQueryGetIntersectionInstanceIdEXT", EOpRayQueryGetIntersectionInstanceId); + symbolTable.relateToOperator("rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT", EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset); + symbolTable.relateToOperator("rayQueryGetIntersectionGeometryIndexEXT", EOpRayQueryGetIntersectionGeometryIndex); + symbolTable.relateToOperator("rayQueryGetIntersectionPrimitiveIndexEXT", EOpRayQueryGetIntersectionPrimitiveIndex); + symbolTable.relateToOperator("rayQueryGetIntersectionBarycentricsEXT", EOpRayQueryGetIntersectionBarycentrics); + symbolTable.relateToOperator("rayQueryGetIntersectionFrontFaceEXT", EOpRayQueryGetIntersectionFrontFace); + symbolTable.relateToOperator("rayQueryGetIntersectionCandidateAABBOpaqueEXT", EOpRayQueryGetIntersectionCandidateAABBOpaque); + symbolTable.relateToOperator("rayQueryGetIntersectionObjectRayDirectionEXT", EOpRayQueryGetIntersectionObjectRayDirection); + symbolTable.relateToOperator("rayQueryGetIntersectionObjectRayOriginEXT", EOpRayQueryGetIntersectionObjectRayOrigin); + symbolTable.relateToOperator("rayQueryGetWorldRayDirectionEXT", EOpRayQueryGetWorldRayDirection); + symbolTable.relateToOperator("rayQueryGetWorldRayOriginEXT", EOpRayQueryGetWorldRayOrigin); + symbolTable.relateToOperator("rayQueryGetIntersectionObjectToWorldEXT", EOpRayQueryGetIntersectionObjectToWorld); + symbolTable.relateToOperator("rayQueryGetIntersectionWorldToObjectEXT", EOpRayQueryGetIntersectionWorldToObject); + } + + symbolTable.relateToOperator("interpolateAtCentroid", EOpInterpolateAtCentroid); + symbolTable.relateToOperator("interpolateAtSample", EOpInterpolateAtSample); + symbolTable.relateToOperator("interpolateAtOffset", EOpInterpolateAtOffset); + + if (profile != EEsProfile) + symbolTable.relateToOperator("interpolateAtVertexAMD", EOpInterpolateAtVertex); + + symbolTable.relateToOperator("beginInvocationInterlockARB", EOpBeginInvocationInterlock); + symbolTable.relateToOperator("endInvocationInterlockARB", EOpEndInvocationInterlock); + + break; + + case EShLangCompute: + symbolTable.relateToOperator("subgroupMemoryBarrierShared", EOpSubgroupMemoryBarrierShared); + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) { + symbolTable.relateToOperator("dFdx", EOpDPdx); + symbolTable.relateToOperator("dFdy", EOpDPdy); + symbolTable.relateToOperator("fwidth", EOpFwidth); + symbolTable.relateToOperator("dFdxFine", EOpDPdxFine); + symbolTable.relateToOperator("dFdyFine", EOpDPdyFine); + symbolTable.relateToOperator("fwidthFine", EOpFwidthFine); + symbolTable.relateToOperator("dFdxCoarse", EOpDPdxCoarse); + symbolTable.relateToOperator("dFdyCoarse", EOpDPdyCoarse); + symbolTable.relateToOperator("fwidthCoarse",EOpFwidthCoarse); + } + symbolTable.relateToOperator("coopMatLoadNV", EOpCooperativeMatrixLoad); + symbolTable.relateToOperator("coopMatStoreNV", EOpCooperativeMatrixStore); + symbolTable.relateToOperator("coopMatMulAddNV", EOpCooperativeMatrixMulAdd); + break; + + case EShLangRayGen: + case EShLangClosestHit: + case EShLangMiss: + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("traceNV", EOpTrace); + symbolTable.relateToOperator("traceRayEXT", EOpTrace); + symbolTable.relateToOperator("executeCallableNV", EOpExecuteCallable); + symbolTable.relateToOperator("executeCallableEXT", EOpExecuteCallable); + } + break; + case EShLangIntersect: + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("reportIntersectionNV", EOpReportIntersection); + symbolTable.relateToOperator("reportIntersectionEXT", EOpReportIntersection); + } + break; + case EShLangAnyHit: + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("ignoreIntersectionNV", EOpIgnoreIntersection); + symbolTable.relateToOperator("ignoreIntersectionEXT", EOpIgnoreIntersection); + symbolTable.relateToOperator("terminateRayNV", EOpTerminateRay); + symbolTable.relateToOperator("terminateRayEXT", EOpTerminateRay); + } + break; + case EShLangCallable: + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("executeCallableNV", EOpExecuteCallable); + symbolTable.relateToOperator("executeCallableEXT", EOpExecuteCallable); + } + break; + case EShLangMeshNV: + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + symbolTable.relateToOperator("writePackedPrimitiveIndices4x8NV", EOpWritePackedPrimitiveIndices4x8NV); + } + // fall through + case EShLangTaskNV: + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + symbolTable.relateToOperator("memoryBarrierShared", EOpMemoryBarrierShared); + symbolTable.relateToOperator("groupMemoryBarrier", EOpGroupMemoryBarrier); + symbolTable.relateToOperator("subgroupMemoryBarrierShared", EOpSubgroupMemoryBarrierShared); + } + break; + + default: + assert(false && "Language not supported"); + } +#endif +} + +// +// Add context-dependent (resource-specific) built-ins not handled by the above. These +// would be ones that need to be programmatically added because they cannot +// be added by simple text strings. For these, also +// 1) Map built-in functions to operators, for those that will turn into an operation node +// instead of remaining a function call. +// 2) Tag extension-related symbols added to their base version with their extensions, so +// that if an early version has the extension turned off, there is an error reported on use. +// +void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources) +{ +#ifndef GLSLANG_WEB + if (profile != EEsProfile && version >= 430 && version < 440) { + symbolTable.setVariableExtensions("gl_MaxTransformFeedbackBuffers", 1, &E_GL_ARB_enhanced_layouts); + symbolTable.setVariableExtensions("gl_MaxTransformFeedbackInterleavedComponents", 1, &E_GL_ARB_enhanced_layouts); + } + if (profile != EEsProfile && version >= 130 && version < 420) { + symbolTable.setVariableExtensions("gl_MinProgramTexelOffset", 1, &E_GL_ARB_shading_language_420pack); + symbolTable.setVariableExtensions("gl_MaxProgramTexelOffset", 1, &E_GL_ARB_shading_language_420pack); + } + if (profile != EEsProfile && version >= 150 && version < 410) + symbolTable.setVariableExtensions("gl_MaxViewports", 1, &E_GL_ARB_viewport_array); + + switch(language) { + case EShLangFragment: + // Set up gl_FragData based on current array size. + if (version == 100 || IncludeLegacy(version, profile, spvVersion) || (! ForwardCompatibility && profile != EEsProfile && version < 420)) { + TPrecisionQualifier pq = profile == EEsProfile ? EpqMedium : EpqNone; + TType fragData(EbtFloat, EvqFragColor, pq, 4); + TArraySizes* arraySizes = new TArraySizes; + arraySizes->addInnerSize(resources.maxDrawBuffers); + fragData.transferArraySizes(arraySizes); + symbolTable.insert(*new TVariable(NewPoolTString("gl_FragData"), fragData)); + SpecialQualifier("gl_FragData", EvqFragColor, EbvFragData, symbolTable); + } + + // GL_EXT_blend_func_extended + if (profile == EEsProfile && version >= 100) { + symbolTable.setVariableExtensions("gl_MaxDualSourceDrawBuffersEXT", 1, &E_GL_EXT_blend_func_extended); + symbolTable.setVariableExtensions("gl_SecondaryFragColorEXT", 1, &E_GL_EXT_blend_func_extended); + symbolTable.setVariableExtensions("gl_SecondaryFragDataEXT", 1, &E_GL_EXT_blend_func_extended); + SpecialQualifier("gl_SecondaryFragColorEXT", EvqVaryingOut, EbvSecondaryFragColorEXT, symbolTable); + SpecialQualifier("gl_SecondaryFragDataEXT", EvqVaryingOut, EbvSecondaryFragDataEXT, symbolTable); + } + + break; + + case EShLangTessControl: + case EShLangTessEvaluation: + // Because of the context-dependent array size (gl_MaxPatchVertices), + // these variables were added later than the others and need to be mapped now. + + // standard members + BuiltInVariable("gl_in", "gl_Position", EbvPosition, symbolTable); + BuiltInVariable("gl_in", "gl_PointSize", EbvPointSize, symbolTable); + BuiltInVariable("gl_in", "gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_in", "gl_CullDistance", EbvCullDistance, symbolTable); + + // compatibility members + BuiltInVariable("gl_in", "gl_ClipVertex", EbvClipVertex, symbolTable); + BuiltInVariable("gl_in", "gl_FrontColor", EbvFrontColor, symbolTable); + BuiltInVariable("gl_in", "gl_BackColor", EbvBackColor, symbolTable); + BuiltInVariable("gl_in", "gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable); + BuiltInVariable("gl_in", "gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable); + BuiltInVariable("gl_in", "gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_in", "gl_FogFragCoord", EbvFogFragCoord, symbolTable); + + symbolTable.setVariableExtensions("gl_in", "gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_in", "gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + + BuiltInVariable("gl_in", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable); + BuiltInVariable("gl_in", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + + // extension requirements + if (profile == EEsProfile) { + symbolTable.setVariableExtensions("gl_in", "gl_PointSize", Num_AEP_tessellation_point_size, AEP_tessellation_point_size); + } + + break; + + default: + break; + } +#endif +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Initialize.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Initialize.h new file mode 100644 index 000000000..ac8ec33e9 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Initialize.h @@ -0,0 +1,112 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013-2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _INITIALIZE_INCLUDED_ +#define _INITIALIZE_INCLUDED_ + +#include "../Include/ResourceLimits.h" +#include "../Include/Common.h" +#include "../Include/ShHandle.h" +#include "SymbolTable.h" +#include "Versions.h" + +namespace glslang { + +// +// This is made to hold parseable strings for almost all the built-in +// functions and variables for one specific combination of version +// and profile. (Some still need to be added programmatically.) +// This is a base class for language-specific derivations, which +// can be used for language independent builtins. +// +// The strings are organized by +// commonBuiltins: intersection of all stages' built-ins, processed just once +// stageBuiltins[]: anything a stage needs that's not in commonBuiltins +// +class TBuiltInParseables { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + TBuiltInParseables(); + virtual ~TBuiltInParseables(); + virtual void initialize(int version, EProfile, const SpvVersion& spvVersion) = 0; + virtual void initialize(const TBuiltInResource& resources, int version, EProfile, const SpvVersion& spvVersion, EShLanguage) = 0; + virtual const TString& getCommonString() const { return commonBuiltins; } + virtual const TString& getStageString(EShLanguage language) const { return stageBuiltins[language]; } + + virtual void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable) = 0; + virtual void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources) = 0; + +protected: + TString commonBuiltins; + TString stageBuiltins[EShLangCount]; +}; + +// +// This is a GLSL specific derivation of TBuiltInParseables. To present a stable +// interface and match other similar code, it is called TBuiltIns, rather +// than TBuiltInParseablesGlsl. +// +class TBuiltIns : public TBuiltInParseables { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + TBuiltIns(); + virtual ~TBuiltIns(); + void initialize(int version, EProfile, const SpvVersion& spvVersion); + void initialize(const TBuiltInResource& resources, int version, EProfile, const SpvVersion& spvVersion, EShLanguage); + + void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable); + void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources); + +protected: + void addTabledBuiltins(int version, EProfile profile, const SpvVersion& spvVersion); + void relateTabledBuiltins(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage, TSymbolTable&); + void add2ndGenerationSamplingImaging(int version, EProfile profile, const SpvVersion& spvVersion); + void addSubpassSampling(TSampler, const TString& typeName, int version, EProfile profile); + void addQueryFunctions(TSampler, const TString& typeName, int version, EProfile profile); + void addImageFunctions(TSampler, const TString& typeName, int version, EProfile profile); + void addSamplingFunctions(TSampler, const TString& typeName, int version, EProfile profile); + void addGatherFunctions(TSampler, const TString& typeName, int version, EProfile profile); + + // Helpers for making textual representations of the permutations + // of texturing/imaging functions. + const char* postfixes[5]; + const char* prefixes[EbtNumTypes]; + int dimMap[EsdNumDims]; +}; + +} // end namespace glslang + +#endif // _INITIALIZE_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/IntermTraverse.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/IntermTraverse.cpp new file mode 100644 index 000000000..f46010b71 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/IntermTraverse.cpp @@ -0,0 +1,302 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (c) 2002-2010 The ANGLE Project Authors. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/intermediate.h" + +namespace glslang { + +// +// Traverse the intermediate representation tree, and +// call a node type specific function for each node. +// Done recursively through the member function Traverse(). +// Node types can be skipped if their function to call is 0, +// but their subtree will still be traversed. +// Nodes with children can have their whole subtree skipped +// if preVisit is turned on and the type specific function +// returns false. +// +// preVisit, postVisit, and rightToLeft control what order +// nodes are visited in. +// + +// +// Traversal functions for terminals are straightforward.... +// +void TIntermMethod::traverse(TIntermTraverser*) +{ + // Tree should always resolve all methods as a non-method. +} + +void TIntermSymbol::traverse(TIntermTraverser *it) +{ + it->visitSymbol(this); +} + +void TIntermConstantUnion::traverse(TIntermTraverser *it) +{ + it->visitConstantUnion(this); +} + +// +// Traverse a binary node. +// +void TIntermBinary::traverse(TIntermTraverser *it) +{ + bool visit = true; + + // + // visit the node before children if pre-visiting. + // + if (it->preVisit) + visit = it->visitBinary(EvPreVisit, this); + + // + // Visit the children, in the right order. + // + if (visit) { + it->incrementDepth(this); + + if (it->rightToLeft) { + if (right) + right->traverse(it); + + if (it->inVisit) + visit = it->visitBinary(EvInVisit, this); + + if (visit && left) + left->traverse(it); + } else { + if (left) + left->traverse(it); + + if (it->inVisit) + visit = it->visitBinary(EvInVisit, this); + + if (visit && right) + right->traverse(it); + } + + it->decrementDepth(); + } + + // + // Visit the node after the children, if requested and the traversal + // hasn't been canceled yet. + // + if (visit && it->postVisit) + it->visitBinary(EvPostVisit, this); +} + +// +// Traverse a unary node. Same comments in binary node apply here. +// +void TIntermUnary::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitUnary(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + operand->traverse(it); + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitUnary(EvPostVisit, this); +} + +// +// Traverse an aggregate node. Same comments in binary node apply here. +// +void TIntermAggregate::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitAggregate(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + + if (it->rightToLeft) { + for (TIntermSequence::reverse_iterator sit = sequence.rbegin(); sit != sequence.rend(); sit++) { + (*sit)->traverse(it); + + if (visit && it->inVisit) { + if (*sit != sequence.front()) + visit = it->visitAggregate(EvInVisit, this); + } + } + } else { + for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); sit++) { + (*sit)->traverse(it); + + if (visit && it->inVisit) { + if (*sit != sequence.back()) + visit = it->visitAggregate(EvInVisit, this); + } + } + } + + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitAggregate(EvPostVisit, this); +} + +// +// Traverse a selection node. Same comments in binary node apply here. +// +void TIntermSelection::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitSelection(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + if (it->rightToLeft) { + if (falseBlock) + falseBlock->traverse(it); + if (trueBlock) + trueBlock->traverse(it); + condition->traverse(it); + } else { + condition->traverse(it); + if (trueBlock) + trueBlock->traverse(it); + if (falseBlock) + falseBlock->traverse(it); + } + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitSelection(EvPostVisit, this); +} + +// +// Traverse a loop node. Same comments in binary node apply here. +// +void TIntermLoop::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitLoop(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + + if (it->rightToLeft) { + if (terminal) + terminal->traverse(it); + + if (body) + body->traverse(it); + + if (test) + test->traverse(it); + } else { + if (test) + test->traverse(it); + + if (body) + body->traverse(it); + + if (terminal) + terminal->traverse(it); + } + + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitLoop(EvPostVisit, this); +} + +// +// Traverse a branch node. Same comments in binary node apply here. +// +void TIntermBranch::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitBranch(EvPreVisit, this); + + if (visit && expression) { + it->incrementDepth(this); + expression->traverse(it); + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitBranch(EvPostVisit, this); +} + +// +// Traverse a switch node. +// +void TIntermSwitch::traverse(TIntermTraverser* it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitSwitch(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + if (it->rightToLeft) { + body->traverse(it); + condition->traverse(it); + } else { + condition->traverse(it); + body->traverse(it); + } + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitSwitch(EvPostVisit, this); +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Intermediate.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Intermediate.cpp new file mode 100755 index 000000000..d7049d8d5 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Intermediate.cpp @@ -0,0 +1,3992 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2015 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Build the intermediate representation. +// + +#include "localintermediate.h" +#include "RemoveTree.h" +#include "SymbolTable.h" +#include "propagateNoContraction.h" + +#include +#include +#include + +namespace glslang { + +//////////////////////////////////////////////////////////////////////////// +// +// First set of functions are to help build the intermediate representation. +// These functions are not member functions of the nodes. +// They are called from parser productions. +// +///////////////////////////////////////////////////////////////////////////// + +// +// Add a terminal node for an identifier in an expression. +// +// Returns the added node. +// + +TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TConstUnionArray& constArray, + TIntermTyped* constSubtree, const TSourceLoc& loc) +{ + TIntermSymbol* node = new TIntermSymbol(id, name, type); + node->setLoc(loc); + node->setConstArray(constArray); + node->setConstSubtree(constSubtree); + + return node; +} + +TIntermSymbol* TIntermediate::addSymbol(const TIntermSymbol& intermSymbol) +{ + return addSymbol(intermSymbol.getId(), + intermSymbol.getName(), + intermSymbol.getType(), + intermSymbol.getConstArray(), + intermSymbol.getConstSubtree(), + intermSymbol.getLoc()); +} + +TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable) +{ + glslang::TSourceLoc loc; // just a null location + loc.init(); + + return addSymbol(variable, loc); +} + +TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable, const TSourceLoc& loc) +{ + return addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), variable.getConstArray(), variable.getConstSubtree(), loc); +} + +TIntermSymbol* TIntermediate::addSymbol(const TType& type, const TSourceLoc& loc) +{ + TConstUnionArray unionArray; // just a null constant + + return addSymbol(0, "", type, unionArray, nullptr, loc); +} + +// +// Connect two nodes with a new parent that does a binary operation on the nodes. +// +// Returns the added node. +// +// Returns nullptr if the working conversions and promotions could not be found. +// +TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc loc) +{ + // No operations work on blocks + if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock) + return nullptr; + + // Convert "reference +/- int" and "reference - reference" to integer math + if ((op == EOpAdd || op == EOpSub) && extensionRequested(E_GL_EXT_buffer_reference2)) { + + // No addressing math on struct with unsized array. + if ((left->isReference() && left->getType().getReferentType()->containsUnsizedArray()) || + (right->isReference() && right->getType().getReferentType()->containsUnsizedArray())) { + return nullptr; + } + + if (left->isReference() && isTypeInt(right->getBasicType())) { + const TType& referenceType = left->getType(); + TIntermConstantUnion* size = addConstantUnion((unsigned long long)computeBufferReferenceTypeSize(left->getType()), loc, true); + left = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, left, TType(EbtUint64)); + + right = createConversion(EbtInt64, right); + right = addBinaryMath(EOpMul, right, size, loc); + + TIntermTyped *node = addBinaryMath(op, left, right, loc); + node = addBuiltInFunctionCall(loc, EOpConvUint64ToPtr, true, node, referenceType); + return node; + } + + if (op == EOpAdd && right->isReference() && isTypeInt(left->getBasicType())) { + const TType& referenceType = right->getType(); + TIntermConstantUnion* size = addConstantUnion((unsigned long long)computeBufferReferenceTypeSize(right->getType()), loc, true); + right = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, right, TType(EbtUint64)); + + left = createConversion(EbtInt64, left); + left = addBinaryMath(EOpMul, left, size, loc); + + TIntermTyped *node = addBinaryMath(op, left, right, loc); + node = addBuiltInFunctionCall(loc, EOpConvUint64ToPtr, true, node, referenceType); + return node; + } + + if (op == EOpSub && left->isReference() && right->isReference()) { + TIntermConstantUnion* size = addConstantUnion((long long)computeBufferReferenceTypeSize(left->getType()), loc, true); + + left = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, left, TType(EbtUint64)); + right = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, right, TType(EbtUint64)); + + left = addBuiltInFunctionCall(loc, EOpConvUint64ToInt64, true, left, TType(EbtInt64)); + right = addBuiltInFunctionCall(loc, EOpConvUint64ToInt64, true, right, TType(EbtInt64)); + + left = addBinaryMath(EOpSub, left, right, loc); + + TIntermTyped *node = addBinaryMath(EOpDiv, left, size, loc); + return node; + } + + // No other math operators supported on references + if (left->isReference() || right->isReference()) { + return nullptr; + } + } + + // Try converting the children's base types to compatible types. + auto children = addConversion(op, left, right); + left = std::get<0>(children); + right = std::get<1>(children); + + if (left == nullptr || right == nullptr) + return nullptr; + + // Convert the children's type shape to be compatible. + addBiShapeConversion(op, left, right); + if (left == nullptr || right == nullptr) + return nullptr; + + // + // Need a new node holding things together. Make + // one and promote it to the right type. + // + TIntermBinary* node = addBinaryNode(op, left, right, loc); + if (! promote(node)) + return nullptr; + + node->updatePrecision(); + + // + // If they are both (non-specialization) constants, they must be folded. + // (Unless it's the sequence (comma) operator, but that's handled in addComma().) + // + TIntermConstantUnion *leftTempConstant = node->getLeft()->getAsConstantUnion(); + TIntermConstantUnion *rightTempConstant = node->getRight()->getAsConstantUnion(); + if (leftTempConstant && rightTempConstant) { + TIntermTyped* folded = leftTempConstant->fold(node->getOp(), rightTempConstant); + if (folded) + return folded; + } + + // If can propagate spec-constantness and if the operation is an allowed + // specialization-constant operation, make a spec-constant. + if (specConstantPropagates(*node->getLeft(), *node->getRight()) && isSpecializationOperation(*node)) + node->getWritableType().getQualifier().makeSpecConstant(); + + // If must propagate nonuniform, make a nonuniform. + if ((node->getLeft()->getQualifier().isNonUniform() || node->getRight()->getQualifier().isNonUniform()) && + isNonuniformPropagating(node->getOp())) + node->getWritableType().getQualifier().nonUniform = true; + + return node; +} + +// +// Low level: add binary node (no promotions or other argument modifications) +// +TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc loc) const +{ + // build the node + TIntermBinary* node = new TIntermBinary(op); + if (loc.line == 0) + loc = left->getLoc(); + node->setLoc(loc); + node->setLeft(left); + node->setRight(right); + + return node; +} + +// +// like non-type form, but sets node's type. +// +TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc loc, const TType& type) const +{ + TIntermBinary* node = addBinaryNode(op, left, right, loc); + node->setType(type); + return node; +} + +// +// Low level: add unary node (no promotions or other argument modifications) +// +TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, TSourceLoc loc) const +{ + TIntermUnary* node = new TIntermUnary(op); + if (loc.line == 0) + loc = child->getLoc(); + node->setLoc(loc); + node->setOperand(child); + + return node; +} + +// +// like non-type form, but sets node's type. +// +TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, TSourceLoc loc, const TType& type) const +{ + TIntermUnary* node = addUnaryNode(op, child, loc); + node->setType(type); + return node; +} + +// +// Connect two nodes through an assignment. +// +// Returns the added node. +// +// Returns nullptr if the 'right' type could not be converted to match the 'left' type, +// or the resulting operation cannot be properly promoted. +// +TIntermTyped* TIntermediate::addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc loc) +{ + // No block assignment + if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock) + return nullptr; + + // Convert "reference += int" to "reference = reference + int". We need this because the + // "reference + int" calculation involves a cast back to the original type, which makes it + // not an lvalue. + if ((op == EOpAddAssign || op == EOpSubAssign) && left->isReference() && + extensionRequested(E_GL_EXT_buffer_reference2)) { + + if (!(right->getType().isScalar() && right->getType().isIntegerDomain())) + return nullptr; + + TIntermTyped* node = addBinaryMath(op == EOpAddAssign ? EOpAdd : EOpSub, left, right, loc); + if (!node) + return nullptr; + + TIntermSymbol* symbol = left->getAsSymbolNode(); + left = addSymbol(*symbol); + + node = addAssign(EOpAssign, left, node, loc); + return node; + } + + // + // Like adding binary math, except the conversion can only go + // from right to left. + // + + // convert base types, nullptr return means not possible + right = addConversion(op, left->getType(), right); + if (right == nullptr) + return nullptr; + + // convert shape + right = addUniShapeConversion(op, left->getType(), right); + + // build the node + TIntermBinary* node = addBinaryNode(op, left, right, loc); + + if (! promote(node)) + return nullptr; + + node->updatePrecision(); + + return node; +} + +// +// Connect two nodes through an index operator, where the left node is the base +// of an array or struct, and the right node is a direct or indirect offset. +// +// Returns the added node. +// The caller should set the type of the returned node. +// +TIntermTyped* TIntermediate::addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, TSourceLoc loc) +{ + // caller should set the type + return addBinaryNode(op, base, index, loc); +} + +// +// Add one node as the parent of another that it operates on. +// +// Returns the added node. +// +TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermTyped* child, TSourceLoc loc) +{ + if (child == 0) + return nullptr; + + if (child->getType().getBasicType() == EbtBlock) + return nullptr; + + switch (op) { + case EOpLogicalNot: + if (getSource() == EShSourceHlsl) { + break; // HLSL can promote logical not + } + + if (child->getType().getBasicType() != EbtBool || child->getType().isMatrix() || child->getType().isArray() || child->getType().isVector()) { + return nullptr; + } + break; + + case EOpPostIncrement: + case EOpPreIncrement: + case EOpPostDecrement: + case EOpPreDecrement: + case EOpNegative: + if (child->getType().getBasicType() == EbtStruct || child->getType().isArray()) + return nullptr; + default: break; // some compilers want this + } + + // + // Do we need to promote the operand? + // + TBasicType newType = EbtVoid; + switch (op) { + case EOpConstructBool: newType = EbtBool; break; + case EOpConstructFloat: newType = EbtFloat; break; + case EOpConstructInt: newType = EbtInt; break; + case EOpConstructUint: newType = EbtUint; break; +#ifndef GLSLANG_WEB + case EOpConstructInt8: newType = EbtInt8; break; + case EOpConstructUint8: newType = EbtUint8; break; + case EOpConstructInt16: newType = EbtInt16; break; + case EOpConstructUint16: newType = EbtUint16; break; + case EOpConstructInt64: newType = EbtInt64; break; + case EOpConstructUint64: newType = EbtUint64; break; + case EOpConstructDouble: newType = EbtDouble; break; + case EOpConstructFloat16: newType = EbtFloat16; break; +#endif + default: break; // some compilers want this + } + + if (newType != EbtVoid) { + child = addConversion(op, TType(newType, EvqTemporary, child->getVectorSize(), + child->getMatrixCols(), + child->getMatrixRows(), + child->isVector()), + child); + if (child == nullptr) + return nullptr; + } + + // + // For constructors, we are now done, it was all in the conversion. + // TODO: but, did this bypass constant folding? + // + switch (op) { + case EOpConstructInt8: + case EOpConstructUint8: + case EOpConstructInt16: + case EOpConstructUint16: + case EOpConstructInt: + case EOpConstructUint: + case EOpConstructInt64: + case EOpConstructUint64: + case EOpConstructBool: + case EOpConstructFloat: + case EOpConstructDouble: + case EOpConstructFloat16: + return child; + default: break; // some compilers want this + } + + // + // Make a new node for the operator. + // + TIntermUnary* node = addUnaryNode(op, child, loc); + + if (! promote(node)) + return nullptr; + + node->updatePrecision(); + + // If it's a (non-specialization) constant, it must be folded. + if (node->getOperand()->getAsConstantUnion()) + return node->getOperand()->getAsConstantUnion()->fold(op, node->getType()); + + // If it's a specialization constant, the result is too, + // if the operation is allowed for specialization constants. + if (node->getOperand()->getType().getQualifier().isSpecConstant() && isSpecializationOperation(*node)) + node->getWritableType().getQualifier().makeSpecConstant(); + + // If must propagate nonuniform, make a nonuniform. + if (node->getOperand()->getQualifier().isNonUniform() && isNonuniformPropagating(node->getOp())) + node->getWritableType().getQualifier().nonUniform = true; + + return node; +} + +TIntermTyped* TIntermediate::addBuiltInFunctionCall(const TSourceLoc& loc, TOperator op, bool unary, + TIntermNode* childNode, const TType& returnType) +{ + if (unary) { + // + // Treat it like a unary operator. + // addUnaryMath() should get the type correct on its own; + // including constness (which would differ from the prototype). + // + TIntermTyped* child = childNode->getAsTyped(); + if (child == nullptr) + return nullptr; + + if (child->getAsConstantUnion()) { + TIntermTyped* folded = child->getAsConstantUnion()->fold(op, returnType); + if (folded) + return folded; + } + + return addUnaryNode(op, child, child->getLoc(), returnType); + } else { + // setAggregateOperater() calls fold() for constant folding + TIntermTyped* node = setAggregateOperator(childNode, op, returnType, loc); + + return node; + } +} + +// +// This is the safe way to change the operator on an aggregate, as it +// does lots of error checking and fixing. Especially for establishing +// a function call's operation on its set of parameters. Sequences +// of instructions are also aggregates, but they just directly set +// their operator to EOpSequence. +// +// Returns an aggregate node, which could be the one passed in if +// it was already an aggregate. +// +TIntermTyped* TIntermediate::setAggregateOperator(TIntermNode* node, TOperator op, const TType& type, TSourceLoc loc) +{ + TIntermAggregate* aggNode; + + // + // Make sure we have an aggregate. If not turn it into one. + // + if (node != nullptr) { + aggNode = node->getAsAggregate(); + if (aggNode == nullptr || aggNode->getOp() != EOpNull) { + // + // Make an aggregate containing this node. + // + aggNode = new TIntermAggregate(); + aggNode->getSequence().push_back(node); + if (loc.line == 0) + loc = node->getLoc(); + } + } else + aggNode = new TIntermAggregate(); + + // + // Set the operator. + // + aggNode->setOperator(op); + if (loc.line != 0) + aggNode->setLoc(loc); + + aggNode->setType(type); + + return fold(aggNode); +} + +bool TIntermediate::isConversionAllowed(TOperator op, TIntermTyped* node) const +{ + // + // Does the base type even allow the operation? + // + switch (node->getBasicType()) { + case EbtVoid: + return false; + case EbtAtomicUint: + case EbtSampler: + case EbtAccStruct: + // opaque types can be passed to functions + if (op == EOpFunction) + break; + + // HLSL can assign samplers directly (no constructor) + if (getSource() == EShSourceHlsl && node->getBasicType() == EbtSampler) + break; + + // samplers can get assigned via a sampler constructor + // (well, not yet, but code in the rest of this function is ready for it) + if (node->getBasicType() == EbtSampler && op == EOpAssign && + node->getAsOperator() != nullptr && node->getAsOperator()->getOp() == EOpConstructTextureSampler) + break; + + // otherwise, opaque types can't even be operated on, let alone converted + return false; + default: + break; + } + + return true; +} + +bool TIntermediate::buildConvertOp(TBasicType dst, TBasicType src, TOperator& newOp) const +{ + switch (dst) { +#ifndef GLSLANG_WEB + case EbtDouble: + switch (src) { + case EbtUint: newOp = EOpConvUintToDouble; break; + case EbtBool: newOp = EOpConvBoolToDouble; break; + case EbtFloat: newOp = EOpConvFloatToDouble; break; + case EbtInt: newOp = EOpConvIntToDouble; break; + case EbtInt8: newOp = EOpConvInt8ToDouble; break; + case EbtUint8: newOp = EOpConvUint8ToDouble; break; + case EbtInt16: newOp = EOpConvInt16ToDouble; break; + case EbtUint16: newOp = EOpConvUint16ToDouble; break; + case EbtFloat16: newOp = EOpConvFloat16ToDouble; break; + case EbtInt64: newOp = EOpConvInt64ToDouble; break; + case EbtUint64: newOp = EOpConvUint64ToDouble; break; + default: + return false; + } + break; +#endif + case EbtFloat: + switch (src) { + case EbtInt: newOp = EOpConvIntToFloat; break; + case EbtUint: newOp = EOpConvUintToFloat; break; + case EbtBool: newOp = EOpConvBoolToFloat; break; +#ifndef GLSLANG_WEB + case EbtDouble: newOp = EOpConvDoubleToFloat; break; + case EbtInt8: newOp = EOpConvInt8ToFloat; break; + case EbtUint8: newOp = EOpConvUint8ToFloat; break; + case EbtInt16: newOp = EOpConvInt16ToFloat; break; + case EbtUint16: newOp = EOpConvUint16ToFloat; break; + case EbtFloat16: newOp = EOpConvFloat16ToFloat; break; + case EbtInt64: newOp = EOpConvInt64ToFloat; break; + case EbtUint64: newOp = EOpConvUint64ToFloat; break; +#endif + default: + return false; + } + break; +#ifndef GLSLANG_WEB + case EbtFloat16: + switch (src) { + case EbtInt8: newOp = EOpConvInt8ToFloat16; break; + case EbtUint8: newOp = EOpConvUint8ToFloat16; break; + case EbtInt16: newOp = EOpConvInt16ToFloat16; break; + case EbtUint16: newOp = EOpConvUint16ToFloat16; break; + case EbtInt: newOp = EOpConvIntToFloat16; break; + case EbtUint: newOp = EOpConvUintToFloat16; break; + case EbtBool: newOp = EOpConvBoolToFloat16; break; + case EbtFloat: newOp = EOpConvFloatToFloat16; break; + case EbtDouble: newOp = EOpConvDoubleToFloat16; break; + case EbtInt64: newOp = EOpConvInt64ToFloat16; break; + case EbtUint64: newOp = EOpConvUint64ToFloat16; break; + default: + return false; + } + break; +#endif + case EbtBool: + switch (src) { + case EbtInt: newOp = EOpConvIntToBool; break; + case EbtUint: newOp = EOpConvUintToBool; break; + case EbtFloat: newOp = EOpConvFloatToBool; break; +#ifndef GLSLANG_WEB + case EbtDouble: newOp = EOpConvDoubleToBool; break; + case EbtInt8: newOp = EOpConvInt8ToBool; break; + case EbtUint8: newOp = EOpConvUint8ToBool; break; + case EbtInt16: newOp = EOpConvInt16ToBool; break; + case EbtUint16: newOp = EOpConvUint16ToBool; break; + case EbtFloat16: newOp = EOpConvFloat16ToBool; break; + case EbtInt64: newOp = EOpConvInt64ToBool; break; + case EbtUint64: newOp = EOpConvUint64ToBool; break; +#endif + default: + return false; + } + break; +#ifndef GLSLANG_WEB + case EbtInt8: + switch (src) { + case EbtUint8: newOp = EOpConvUint8ToInt8; break; + case EbtInt16: newOp = EOpConvInt16ToInt8; break; + case EbtUint16: newOp = EOpConvUint16ToInt8; break; + case EbtInt: newOp = EOpConvIntToInt8; break; + case EbtUint: newOp = EOpConvUintToInt8; break; + case EbtInt64: newOp = EOpConvInt64ToInt8; break; + case EbtUint64: newOp = EOpConvUint64ToInt8; break; + case EbtBool: newOp = EOpConvBoolToInt8; break; + case EbtFloat: newOp = EOpConvFloatToInt8; break; + case EbtDouble: newOp = EOpConvDoubleToInt8; break; + case EbtFloat16: newOp = EOpConvFloat16ToInt8; break; + default: + return false; + } + break; + case EbtUint8: + switch (src) { + case EbtInt8: newOp = EOpConvInt8ToUint8; break; + case EbtInt16: newOp = EOpConvInt16ToUint8; break; + case EbtUint16: newOp = EOpConvUint16ToUint8; break; + case EbtInt: newOp = EOpConvIntToUint8; break; + case EbtUint: newOp = EOpConvUintToUint8; break; + case EbtInt64: newOp = EOpConvInt64ToUint8; break; + case EbtUint64: newOp = EOpConvUint64ToUint8; break; + case EbtBool: newOp = EOpConvBoolToUint8; break; + case EbtFloat: newOp = EOpConvFloatToUint8; break; + case EbtDouble: newOp = EOpConvDoubleToUint8; break; + case EbtFloat16: newOp = EOpConvFloat16ToUint8; break; + default: + return false; + } + break; + + case EbtInt16: + switch (src) { + case EbtUint8: newOp = EOpConvUint8ToInt16; break; + case EbtInt8: newOp = EOpConvInt8ToInt16; break; + case EbtUint16: newOp = EOpConvUint16ToInt16; break; + case EbtInt: newOp = EOpConvIntToInt16; break; + case EbtUint: newOp = EOpConvUintToInt16; break; + case EbtInt64: newOp = EOpConvInt64ToInt16; break; + case EbtUint64: newOp = EOpConvUint64ToInt16; break; + case EbtBool: newOp = EOpConvBoolToInt16; break; + case EbtFloat: newOp = EOpConvFloatToInt16; break; + case EbtDouble: newOp = EOpConvDoubleToInt16; break; + case EbtFloat16: newOp = EOpConvFloat16ToInt16; break; + default: + return false; + } + break; + case EbtUint16: + switch (src) { + case EbtInt8: newOp = EOpConvInt8ToUint16; break; + case EbtUint8: newOp = EOpConvUint8ToUint16; break; + case EbtInt16: newOp = EOpConvInt16ToUint16; break; + case EbtInt: newOp = EOpConvIntToUint16; break; + case EbtUint: newOp = EOpConvUintToUint16; break; + case EbtInt64: newOp = EOpConvInt64ToUint16; break; + case EbtUint64: newOp = EOpConvUint64ToUint16; break; + case EbtBool: newOp = EOpConvBoolToUint16; break; + case EbtFloat: newOp = EOpConvFloatToUint16; break; + case EbtDouble: newOp = EOpConvDoubleToUint16; break; + case EbtFloat16: newOp = EOpConvFloat16ToUint16; break; + default: + return false; + } + break; +#endif + + case EbtInt: + switch (src) { + case EbtUint: newOp = EOpConvUintToInt; break; + case EbtBool: newOp = EOpConvBoolToInt; break; + case EbtFloat: newOp = EOpConvFloatToInt; break; +#ifndef GLSLANG_WEB + case EbtInt8: newOp = EOpConvInt8ToInt; break; + case EbtUint8: newOp = EOpConvUint8ToInt; break; + case EbtInt16: newOp = EOpConvInt16ToInt; break; + case EbtUint16: newOp = EOpConvUint16ToInt; break; + case EbtDouble: newOp = EOpConvDoubleToInt; break; + case EbtFloat16: newOp = EOpConvFloat16ToInt; break; + case EbtInt64: newOp = EOpConvInt64ToInt; break; + case EbtUint64: newOp = EOpConvUint64ToInt; break; +#endif + default: + return false; + } + break; + case EbtUint: + switch (src) { + case EbtInt: newOp = EOpConvIntToUint; break; + case EbtBool: newOp = EOpConvBoolToUint; break; + case EbtFloat: newOp = EOpConvFloatToUint; break; +#ifndef GLSLANG_WEB + case EbtInt8: newOp = EOpConvInt8ToUint; break; + case EbtUint8: newOp = EOpConvUint8ToUint; break; + case EbtInt16: newOp = EOpConvInt16ToUint; break; + case EbtUint16: newOp = EOpConvUint16ToUint; break; + case EbtDouble: newOp = EOpConvDoubleToUint; break; + case EbtFloat16: newOp = EOpConvFloat16ToUint; break; + case EbtInt64: newOp = EOpConvInt64ToUint; break; + case EbtUint64: newOp = EOpConvUint64ToUint; break; +#endif + default: + return false; + } + break; +#ifndef GLSLANG_WEB + case EbtInt64: + switch (src) { + case EbtInt8: newOp = EOpConvInt8ToInt64; break; + case EbtUint8: newOp = EOpConvUint8ToInt64; break; + case EbtInt16: newOp = EOpConvInt16ToInt64; break; + case EbtUint16: newOp = EOpConvUint16ToInt64; break; + case EbtInt: newOp = EOpConvIntToInt64; break; + case EbtUint: newOp = EOpConvUintToInt64; break; + case EbtBool: newOp = EOpConvBoolToInt64; break; + case EbtFloat: newOp = EOpConvFloatToInt64; break; + case EbtDouble: newOp = EOpConvDoubleToInt64; break; + case EbtFloat16: newOp = EOpConvFloat16ToInt64; break; + case EbtUint64: newOp = EOpConvUint64ToInt64; break; + default: + return false; + } + break; + case EbtUint64: + switch (src) { + case EbtInt8: newOp = EOpConvInt8ToUint64; break; + case EbtUint8: newOp = EOpConvUint8ToUint64; break; + case EbtInt16: newOp = EOpConvInt16ToUint64; break; + case EbtUint16: newOp = EOpConvUint16ToUint64; break; + case EbtInt: newOp = EOpConvIntToUint64; break; + case EbtUint: newOp = EOpConvUintToUint64; break; + case EbtBool: newOp = EOpConvBoolToUint64; break; + case EbtFloat: newOp = EOpConvFloatToUint64; break; + case EbtDouble: newOp = EOpConvDoubleToUint64; break; + case EbtFloat16: newOp = EOpConvFloat16ToUint64; break; + case EbtInt64: newOp = EOpConvInt64ToUint64; break; + default: + return false; + } + break; +#endif + default: + return false; + } + return true; +} + +// This is 'mechanism' here, it does any conversion told. +// It is about basic type, not about shape. +// The policy comes from the shader or the calling code. +TIntermTyped* TIntermediate::createConversion(TBasicType convertTo, TIntermTyped* node) const +{ + // + // Add a new newNode for the conversion. + // + +#ifndef GLSLANG_WEB + bool convertToIntTypes = (convertTo == EbtInt8 || convertTo == EbtUint8 || + convertTo == EbtInt16 || convertTo == EbtUint16 || + convertTo == EbtInt || convertTo == EbtUint || + convertTo == EbtInt64 || convertTo == EbtUint64); + + bool convertFromIntTypes = (node->getBasicType() == EbtInt8 || node->getBasicType() == EbtUint8 || + node->getBasicType() == EbtInt16 || node->getBasicType() == EbtUint16 || + node->getBasicType() == EbtInt || node->getBasicType() == EbtUint || + node->getBasicType() == EbtInt64 || node->getBasicType() == EbtUint64); + + bool convertToFloatTypes = (convertTo == EbtFloat16 || convertTo == EbtFloat || convertTo == EbtDouble); + + bool convertFromFloatTypes = (node->getBasicType() == EbtFloat16 || + node->getBasicType() == EbtFloat || + node->getBasicType() == EbtDouble); + + if (! getArithemeticInt8Enabled()) { + if (((convertTo == EbtInt8 || convertTo == EbtUint8) && ! convertFromIntTypes) || + ((node->getBasicType() == EbtInt8 || node->getBasicType() == EbtUint8) && ! convertToIntTypes)) + return nullptr; + } + + if (! getArithemeticInt16Enabled()) { + if (((convertTo == EbtInt16 || convertTo == EbtUint16) && ! convertFromIntTypes) || + ((node->getBasicType() == EbtInt16 || node->getBasicType() == EbtUint16) && ! convertToIntTypes)) + return nullptr; + } + + if (! getArithemeticFloat16Enabled()) { + if ((convertTo == EbtFloat16 && ! convertFromFloatTypes) || + (node->getBasicType() == EbtFloat16 && ! convertToFloatTypes)) + return nullptr; + } +#endif + + TIntermUnary* newNode = nullptr; + TOperator newOp = EOpNull; + if (!buildConvertOp(convertTo, node->getBasicType(), newOp)) { + return nullptr; + } + + TType newType(convertTo, EvqTemporary, node->getVectorSize(), node->getMatrixCols(), node->getMatrixRows()); + newNode = addUnaryNode(newOp, node, node->getLoc(), newType); + + if (node->getAsConstantUnion()) { +#ifndef GLSLANG_WEB + // 8/16-bit storage extensions don't support 8/16-bit constants, so don't fold conversions + // to those types + if ((getArithemeticInt8Enabled() || !(convertTo == EbtInt8 || convertTo == EbtUint8)) && + (getArithemeticInt16Enabled() || !(convertTo == EbtInt16 || convertTo == EbtUint16)) && + (getArithemeticFloat16Enabled() || !(convertTo == EbtFloat16))) +#endif + { + TIntermTyped* folded = node->getAsConstantUnion()->fold(newOp, newType); + if (folded) + return folded; + } + } + + // Propagate specialization-constant-ness, if allowed + if (node->getType().getQualifier().isSpecConstant() && isSpecializationOperation(*newNode)) + newNode->getWritableType().getQualifier().makeSpecConstant(); + + return newNode; +} + +TIntermTyped* TIntermediate::addConversion(TBasicType convertTo, TIntermTyped* node) const +{ + return createConversion(convertTo, node); +} + +// For converting a pair of operands to a binary operation to compatible +// types with each other, relative to the operation in 'op'. +// This does not cover assignment operations, which is asymmetric in that the +// left type is not changeable. +// See addConversion(op, type, node) for assignments and unary operation +// conversions. +// +// Generally, this is focused on basic type conversion, not shape conversion. +// See addShapeConversion() for shape conversions. +// +// Returns the converted pair of nodes. +// Returns when there is no conversion. +std::tuple +TIntermediate::addConversion(TOperator op, TIntermTyped* node0, TIntermTyped* node1) +{ + if (!isConversionAllowed(op, node0) || !isConversionAllowed(op, node1)) + return std::make_tuple(nullptr, nullptr); + + if (node0->getType() != node1->getType()) { + // If differing structure, then no conversions. + if (node0->isStruct() || node1->isStruct()) + return std::make_tuple(nullptr, nullptr); + + // If differing arrays, then no conversions. + if (node0->getType().isArray() || node1->getType().isArray()) + return std::make_tuple(nullptr, nullptr); + + // No implicit conversions for operations involving cooperative matrices + if (node0->getType().isCoopMat() || node1->getType().isCoopMat()) + return std::make_tuple(node0, node1); + } + + auto promoteTo = std::make_tuple(EbtNumTypes, EbtNumTypes); + + switch (op) { + // + // List all the binary ops that can implicitly convert one operand to the other's type; + // This implements the 'policy' for implicit type conversion. + // + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + case EOpEqual: + case EOpNotEqual: + + case EOpAdd: + case EOpSub: + case EOpMul: + case EOpDiv: + case EOpMod: + + case EOpVectorTimesScalar: + case EOpVectorTimesMatrix: + case EOpMatrixTimesVector: + case EOpMatrixTimesScalar: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + + case EOpSequence: // used by ?: + + if (node0->getBasicType() == node1->getBasicType()) + return std::make_tuple(node0, node1); + + promoteTo = getConversionDestinatonType(node0->getBasicType(), node1->getBasicType(), op); + if (std::get<0>(promoteTo) == EbtNumTypes || std::get<1>(promoteTo) == EbtNumTypes) + return std::make_tuple(nullptr, nullptr); + + break; + + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + if (getSource() == EShSourceHlsl) + promoteTo = std::make_tuple(EbtBool, EbtBool); + else + return std::make_tuple(node0, node1); + break; + + // There are no conversions needed for GLSL; the shift amount just needs to be an + // integer type, as does the base. + // HLSL can promote bools to ints to make this work. + case EOpLeftShift: + case EOpRightShift: + if (getSource() == EShSourceHlsl) { + TBasicType node0BasicType = node0->getBasicType(); + if (node0BasicType == EbtBool) + node0BasicType = EbtInt; + if (node1->getBasicType() == EbtBool) + promoteTo = std::make_tuple(node0BasicType, EbtInt); + else + promoteTo = std::make_tuple(node0BasicType, node1->getBasicType()); + } else { + if (isTypeInt(node0->getBasicType()) && isTypeInt(node1->getBasicType())) + return std::make_tuple(node0, node1); + else + return std::make_tuple(nullptr, nullptr); + } + break; + + default: + if (node0->getType() == node1->getType()) + return std::make_tuple(node0, node1); + + return std::make_tuple(nullptr, nullptr); + } + + TIntermTyped* newNode0; + TIntermTyped* newNode1; + + if (std::get<0>(promoteTo) != node0->getType().getBasicType()) { + if (node0->getAsConstantUnion()) + newNode0 = promoteConstantUnion(std::get<0>(promoteTo), node0->getAsConstantUnion()); + else + newNode0 = createConversion(std::get<0>(promoteTo), node0); + } else + newNode0 = node0; + + if (std::get<1>(promoteTo) != node1->getType().getBasicType()) { + if (node1->getAsConstantUnion()) + newNode1 = promoteConstantUnion(std::get<1>(promoteTo), node1->getAsConstantUnion()); + else + newNode1 = createConversion(std::get<1>(promoteTo), node1); + } else + newNode1 = node1; + + return std::make_tuple(newNode0, newNode1); +} + +// +// Convert the node's type to the given type, as allowed by the operation involved: 'op'. +// For implicit conversions, 'op' is not the requested conversion, it is the explicit +// operation requiring the implicit conversion. +// +// Binary operation conversions should be handled by addConversion(op, node, node), not here. +// +// Returns a node representing the conversion, which could be the same +// node passed in if no conversion was needed. +// +// Generally, this is focused on basic type conversion, not shape conversion. +// See addShapeConversion() for shape conversions. +// +// Return nullptr if a conversion can't be done. +// +TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TIntermTyped* node) +{ + if (!isConversionAllowed(op, node)) + return nullptr; + + // Otherwise, if types are identical, no problem + if (type == node->getType()) + return node; + + // If one's a structure, then no conversions. + if (type.isStruct() || node->isStruct()) + return nullptr; + + // If one's an array, then no conversions. + if (type.isArray() || node->getType().isArray()) + return nullptr; + + // Note: callers are responsible for other aspects of shape, + // like vector and matrix sizes. + + TBasicType promoteTo; + // GL_EXT_shader_16bit_storage can't do OpConstantComposite with + // 16-bit types, so disable promotion for those types. + bool canPromoteConstant = true; + + switch (op) { + // + // Explicit conversions (unary operations) + // + case EOpConstructBool: + promoteTo = EbtBool; + break; + case EOpConstructFloat: + promoteTo = EbtFloat; + break; + case EOpConstructInt: + promoteTo = EbtInt; + break; + case EOpConstructUint: + promoteTo = EbtUint; + break; +#ifndef GLSLANG_WEB + case EOpConstructDouble: + promoteTo = EbtDouble; + break; + case EOpConstructFloat16: + promoteTo = EbtFloat16; + canPromoteConstant = extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_float16); + break; + case EOpConstructInt8: + promoteTo = EbtInt8; + canPromoteConstant = extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int8); + break; + case EOpConstructUint8: + promoteTo = EbtUint8; + canPromoteConstant = extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int8); + break; + case EOpConstructInt16: + promoteTo = EbtInt16; + canPromoteConstant = extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int16); + break; + case EOpConstructUint16: + promoteTo = EbtUint16; + canPromoteConstant = extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int16); + break; + case EOpConstructInt64: + promoteTo = EbtInt64; + break; + case EOpConstructUint64: + promoteTo = EbtUint64; + break; +#endif + + case EOpLogicalNot: + + case EOpFunctionCall: + + case EOpReturn: + case EOpAssign: + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpVectorTimesScalarAssign: + case EOpMatrixTimesScalarAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + + case EOpAtan: + case EOpClamp: + case EOpCross: + case EOpDistance: + case EOpDot: + case EOpDst: + case EOpFaceForward: + case EOpFma: + case EOpFrexp: + case EOpLdexp: + case EOpMix: + case EOpLit: + case EOpMax: + case EOpMin: + case EOpMod: + case EOpModf: + case EOpPow: + case EOpReflect: + case EOpRefract: + case EOpSmoothStep: + case EOpStep: + + case EOpSequence: + case EOpConstructStruct: + case EOpConstructCooperativeMatrix: + + if (type.isReference() || node->getType().isReference()) { + // types must match to assign a reference + if (type == node->getType()) + return node; + else + return nullptr; + } + + if (type.getBasicType() == node->getType().getBasicType()) + return node; + + if (canImplicitlyPromote(node->getBasicType(), type.getBasicType(), op)) + promoteTo = type.getBasicType(); + else + return nullptr; + break; + + // For GLSL, there are no conversions needed; the shift amount just needs to be an + // integer type, as do the base/result. + // HLSL can convert the shift from a bool to an int. + case EOpLeftShiftAssign: + case EOpRightShiftAssign: + { + if (getSource() == EShSourceHlsl && node->getType().getBasicType() == EbtBool) + promoteTo = type.getBasicType(); + else { + if (isTypeInt(type.getBasicType()) && isTypeInt(node->getBasicType())) + return node; + else + return nullptr; + } + break; + } + + default: + // default is to require a match; all exceptions should have case statements above + + if (type.getBasicType() == node->getType().getBasicType()) + return node; + else + return nullptr; + } + + if (canPromoteConstant && node->getAsConstantUnion()) + return promoteConstantUnion(promoteTo, node->getAsConstantUnion()); + + // + // Add a new newNode for the conversion. + // + TIntermTyped* newNode = createConversion(promoteTo, node); + + return newNode; +} + +// Convert the node's shape of type for the given type, as allowed by the +// operation involved: 'op'. This is for situations where there is only one +// direction to consider doing the shape conversion. +// +// This implements policy, it call addShapeConversion() for the mechanism. +// +// Generally, the AST represents allowed GLSL shapes, so this isn't needed +// for GLSL. Bad shapes are caught in conversion or promotion. +// +// Return 'node' if no conversion was done. Promotion handles final shape +// checking. +// +TIntermTyped* TIntermediate::addUniShapeConversion(TOperator op, const TType& type, TIntermTyped* node) +{ + // some source languages don't do this + switch (getSource()) { + case EShSourceHlsl: + break; + case EShSourceGlsl: + default: + return node; + } + + // some operations don't do this + switch (op) { + case EOpFunctionCall: + case EOpReturn: + break; + + case EOpMulAssign: + // want to support vector *= scalar native ops in AST and lower, not smear, similarly for + // matrix *= scalar, etc. + + case EOpAddAssign: + case EOpSubAssign: + case EOpDivAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpRightShiftAssign: + case EOpLeftShiftAssign: + if (node->getVectorSize() == 1) + return node; + break; + + case EOpAssign: + break; + + case EOpMix: + break; + + default: + return node; + } + + return addShapeConversion(type, node); +} + +// Convert the nodes' shapes to be compatible for the operation 'op'. +// +// This implements policy, it call addShapeConversion() for the mechanism. +// +// Generally, the AST represents allowed GLSL shapes, so this isn't needed +// for GLSL. Bad shapes are caught in conversion or promotion. +// +void TIntermediate::addBiShapeConversion(TOperator op, TIntermTyped*& lhsNode, TIntermTyped*& rhsNode) +{ + // some source languages don't do this + switch (getSource()) { + case EShSourceHlsl: + break; + case EShSourceGlsl: + default: + return; + } + + // some operations don't do this + // 'break' will mean attempt bidirectional conversion + switch (op) { + case EOpMulAssign: + case EOpAssign: + case EOpAddAssign: + case EOpSubAssign: + case EOpDivAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpRightShiftAssign: + case EOpLeftShiftAssign: + // switch to unidirectional conversion (the lhs can't change) + rhsNode = addUniShapeConversion(op, lhsNode->getType(), rhsNode); + return; + + case EOpMul: + // matrix multiply does not change shapes + if (lhsNode->isMatrix() && rhsNode->isMatrix()) + return; + case EOpAdd: + case EOpSub: + case EOpDiv: + // want to support vector * scalar native ops in AST and lower, not smear, similarly for + // matrix * vector, etc. + if (lhsNode->getVectorSize() == 1 || rhsNode->getVectorSize() == 1) + return; + break; + + case EOpRightShift: + case EOpLeftShift: + // can natively support the right operand being a scalar and the left a vector, + // but not the reverse + if (rhsNode->getVectorSize() == 1) + return; + break; + + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + + case EOpEqual: + case EOpNotEqual: + + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + + case EOpMix: + break; + + default: + return; + } + + // Do bidirectional conversions + if (lhsNode->getType().isScalarOrVec1() || rhsNode->getType().isScalarOrVec1()) { + if (lhsNode->getType().isScalarOrVec1()) + lhsNode = addShapeConversion(rhsNode->getType(), lhsNode); + else + rhsNode = addShapeConversion(lhsNode->getType(), rhsNode); + } + lhsNode = addShapeConversion(rhsNode->getType(), lhsNode); + rhsNode = addShapeConversion(lhsNode->getType(), rhsNode); +} + +// Convert the node's shape of type for the given type, as allowed by the +// operation involved: 'op'. +// +// Generally, the AST represents allowed GLSL shapes, so this isn't needed +// for GLSL. Bad shapes are caught in conversion or promotion. +// +// Return 'node' if no conversion was done. Promotion handles final shape +// checking. +// +TIntermTyped* TIntermediate::addShapeConversion(const TType& type, TIntermTyped* node) +{ + // no conversion needed + if (node->getType() == type) + return node; + + // structures and arrays don't change shape, either to or from + if (node->getType().isStruct() || node->getType().isArray() || + type.isStruct() || type.isArray()) + return node; + + // The new node that handles the conversion + TOperator constructorOp = mapTypeToConstructorOp(type); + + if (getSource() == EShSourceHlsl) { + // HLSL rules for scalar, vector and matrix conversions: + // 1) scalar can become anything, initializing every component with its value + // 2) vector and matrix can become scalar, first element is used (warning: truncation) + // 3) matrix can become matrix with less rows and/or columns (warning: truncation) + // 4) vector can become vector with less rows size (warning: truncation) + // 5a) vector 4 can become 2x2 matrix (special case) (same packing layout, its a reinterpret) + // 5b) 2x2 matrix can become vector 4 (special case) (same packing layout, its a reinterpret) + + const TType &sourceType = node->getType(); + + // rule 1 for scalar to matrix is special + if (sourceType.isScalarOrVec1() && type.isMatrix()) { + + // HLSL semantics: the scalar (or vec1) is replicated to every component of the matrix. Left to its + // own devices, the constructor from a scalar would populate the diagonal. This forces replication + // to every matrix element. + + // Note that if the node is complex (e.g, a function call), we don't want to duplicate it here + // repeatedly, so we copy it to a temp, then use the temp. + const int matSize = type.computeNumComponents(); + TIntermAggregate* rhsAggregate = new TIntermAggregate(); + + const bool isSimple = (node->getAsSymbolNode() != nullptr) || (node->getAsConstantUnion() != nullptr); + + if (!isSimple) { + assert(0); // TODO: use node replicator service when available. + } + + for (int x = 0; x < matSize; ++x) + rhsAggregate->getSequence().push_back(node); + + return setAggregateOperator(rhsAggregate, constructorOp, type, node->getLoc()); + } + + // rule 1 and 2 + if ((sourceType.isScalar() && !type.isScalar()) || (!sourceType.isScalar() && type.isScalar())) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + + // rule 3 and 5b + if (sourceType.isMatrix()) { + // rule 3 + if (type.isMatrix()) { + if ((sourceType.getMatrixCols() != type.getMatrixCols() || sourceType.getMatrixRows() != type.getMatrixRows()) && + sourceType.getMatrixCols() >= type.getMatrixCols() && sourceType.getMatrixRows() >= type.getMatrixRows()) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + // rule 5b + } else if (type.isVector()) { + if (type.getVectorSize() == 4 && sourceType.getMatrixCols() == 2 && sourceType.getMatrixRows() == 2) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + } + } + + // rule 4 and 5a + if (sourceType.isVector()) { + // rule 4 + if (type.isVector()) + { + if (sourceType.getVectorSize() > type.getVectorSize()) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + // rule 5a + } else if (type.isMatrix()) { + if (sourceType.getVectorSize() == 4 && type.getMatrixCols() == 2 && type.getMatrixRows() == 2) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + } + } + } + + // scalar -> vector or vec1 -> vector or + // vector -> scalar or + // bigger vector -> smaller vector + if ((node->getType().isScalarOrVec1() && type.isVector()) || + (node->getType().isVector() && type.isScalar()) || + (node->isVector() && type.isVector() && node->getVectorSize() > type.getVectorSize())) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + + return node; +} + +bool TIntermediate::isIntegralPromotion(TBasicType from, TBasicType to) const +{ + // integral promotions + if (to == EbtInt) { + switch(from) { + case EbtInt8: + case EbtInt16: + case EbtUint8: + case EbtUint16: + return true; + default: + break; + } + } + return false; +} + +bool TIntermediate::isFPPromotion(TBasicType from, TBasicType to) const +{ + // floating-point promotions + if (to == EbtDouble) { + switch(from) { + case EbtFloat16: + case EbtFloat: + return true; + default: + break; + } + } + return false; +} + +bool TIntermediate::isIntegralConversion(TBasicType from, TBasicType to) const +{ +#ifdef GLSLANG_WEB + return false; +#endif + + switch (from) { + case EbtInt: + switch(to) { + case EbtUint: + return version >= 400 || getSource() == EShSourceHlsl; + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtUint: + switch(to) { + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtInt8: + switch (to) { + case EbtUint8: + case EbtInt16: + case EbtUint16: + case EbtUint: + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtUint8: + switch (to) { + case EbtInt16: + case EbtUint16: + case EbtUint: + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtInt16: + switch(to) { + case EbtUint16: + case EbtUint: + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtUint16: + switch(to) { + case EbtUint: + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtInt64: + if (to == EbtUint64) { + return true; + } + break; + default: + break; + } + return false; +} + +bool TIntermediate::isFPConversion(TBasicType from, TBasicType to) const +{ +#ifdef GLSLANG_WEB + return false; +#endif + + if (to == EbtFloat && from == EbtFloat16) { + return true; + } else { + return false; + } +} + +bool TIntermediate::isFPIntegralConversion(TBasicType from, TBasicType to) const +{ + switch (from) { + case EbtInt: + case EbtUint: + switch(to) { + case EbtFloat: + case EbtDouble: + return true; + default: + break; + } + break; +#ifndef GLSLANG_WEB + case EbtInt8: + case EbtUint8: + case EbtInt16: + case EbtUint16: + switch (to) { + case EbtFloat16: + case EbtFloat: + case EbtDouble: + return true; + default: + break; + } + break; + case EbtInt64: + case EbtUint64: + if (to == EbtDouble) { + return true; + } + break; +#endif + default: + break; + } + return false; +} + +// +// See if the 'from' type is allowed to be implicitly converted to the +// 'to' type. This is not about vector/array/struct, only about basic type. +// +bool TIntermediate::canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op) const +{ + if ((isEsProfile() && version < 310 ) || version == 110) + return false; + + if (from == to) + return true; + + // TODO: Move more policies into language-specific handlers. + // Some languages allow more general (or potentially, more specific) conversions under some conditions. + if (getSource() == EShSourceHlsl) { + const bool fromConvertable = (from == EbtFloat || from == EbtDouble || from == EbtInt || from == EbtUint || from == EbtBool); + const bool toConvertable = (to == EbtFloat || to == EbtDouble || to == EbtInt || to == EbtUint || to == EbtBool); + + if (fromConvertable && toConvertable) { + switch (op) { + case EOpAndAssign: // assignments can perform arbitrary conversions + case EOpInclusiveOrAssign: // ... + case EOpExclusiveOrAssign: // ... + case EOpAssign: // ... + case EOpAddAssign: // ... + case EOpSubAssign: // ... + case EOpMulAssign: // ... + case EOpVectorTimesScalarAssign: // ... + case EOpMatrixTimesScalarAssign: // ... + case EOpDivAssign: // ... + case EOpModAssign: // ... + case EOpReturn: // function returns can also perform arbitrary conversions + case EOpFunctionCall: // conversion of a calling parameter + case EOpLogicalNot: + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + case EOpConstructStruct: + return true; + default: + break; + } + } + } + + bool explicitTypesEnabled = extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int8) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int16) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int32) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int64) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_float16) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_float32) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_float64); + + if (explicitTypesEnabled) { + // integral promotions + if (isIntegralPromotion(from, to)) { + return true; + } + + // floating-point promotions + if (isFPPromotion(from, to)) { + return true; + } + + // integral conversions + if (isIntegralConversion(from, to)) { + return true; + } + + // floating-point conversions + if (isFPConversion(from, to)) { + return true; + } + + // floating-integral conversions + if (isFPIntegralConversion(from, to)) { + return true; + } + + // hlsl supported conversions + if (getSource() == EShSourceHlsl) { + if (from == EbtBool && (to == EbtInt || to == EbtUint || to == EbtFloat)) + return true; + } + } else if (isEsProfile()) { + switch (to) { + case EbtFloat: + switch (from) { + case EbtInt: + case EbtUint: + return extensionRequested(E_GL_EXT_shader_implicit_conversions); + case EbtFloat: + return true; + default: + return false; + } + case EbtUint: + switch (from) { + case EbtInt: + return extensionRequested(E_GL_EXT_shader_implicit_conversions); + case EbtUint: + return true; + default: + return false; + } + default: + return false; + } + } else { + switch (to) { + case EbtDouble: + switch (from) { + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: + case EbtFloat: + case EbtDouble: + return true; + case EbtInt16: + case EbtUint16: + return extensionRequested(E_GL_AMD_gpu_shader_int16); + case EbtFloat16: + return extensionRequested(E_GL_AMD_gpu_shader_half_float); + default: + return false; + } + case EbtFloat: + switch (from) { + case EbtInt: + case EbtUint: + case EbtFloat: + return true; + case EbtBool: + return getSource() == EShSourceHlsl; + case EbtInt16: + case EbtUint16: + return extensionRequested(E_GL_AMD_gpu_shader_int16); + case EbtFloat16: + return + extensionRequested(E_GL_AMD_gpu_shader_half_float) || getSource() == EShSourceHlsl; + default: + return false; + } + case EbtUint: + switch (from) { + case EbtInt: + return version >= 400 || getSource() == EShSourceHlsl; + case EbtUint: + return true; + case EbtBool: + return getSource() == EShSourceHlsl; + case EbtInt16: + case EbtUint16: + return extensionRequested(E_GL_AMD_gpu_shader_int16); + default: + return false; + } + case EbtInt: + switch (from) { + case EbtInt: + return true; + case EbtBool: + return getSource() == EShSourceHlsl; + case EbtInt16: + return extensionRequested(E_GL_AMD_gpu_shader_int16); + default: + return false; + } + case EbtUint64: + switch (from) { + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: + return true; + case EbtInt16: + case EbtUint16: + return extensionRequested(E_GL_AMD_gpu_shader_int16); + default: + return false; + } + case EbtInt64: + switch (from) { + case EbtInt: + case EbtInt64: + return true; + case EbtInt16: + return extensionRequested(E_GL_AMD_gpu_shader_int16); + default: + return false; + } + case EbtFloat16: + switch (from) { + case EbtInt16: + case EbtUint16: + return extensionRequested(E_GL_AMD_gpu_shader_int16); + case EbtFloat16: + return extensionRequested(E_GL_AMD_gpu_shader_half_float); + default: + break; + } + return false; + case EbtUint16: + switch (from) { + case EbtInt16: + case EbtUint16: + return extensionRequested(E_GL_AMD_gpu_shader_int16); + default: + break; + } + return false; + default: + return false; + } + } + + return false; +} + +static bool canSignedIntTypeRepresentAllUnsignedValues(TBasicType sintType, TBasicType uintType) +{ +#ifdef GLSLANG_WEB + return false; +#endif + + switch(sintType) { + case EbtInt8: + switch(uintType) { + case EbtUint8: + case EbtUint16: + case EbtUint: + case EbtUint64: + return false; + default: + assert(false); + return false; + } + break; + case EbtInt16: + switch(uintType) { + case EbtUint8: + return true; + case EbtUint16: + case EbtUint: + case EbtUint64: + return false; + default: + assert(false); + return false; + } + break; + case EbtInt: + switch(uintType) { + case EbtUint8: + case EbtUint16: + return true; + case EbtUint: + return false; + default: + assert(false); + return false; + } + break; + case EbtInt64: + switch(uintType) { + case EbtUint8: + case EbtUint16: + case EbtUint: + return true; + case EbtUint64: + return false; + default: + assert(false); + return false; + } + break; + default: + assert(false); + return false; + } +} + + +static TBasicType getCorrespondingUnsignedType(TBasicType type) +{ +#ifdef GLSLANG_WEB + assert(type == EbtInt); + return EbtUint; +#endif + + switch(type) { + case EbtInt8: + return EbtUint8; + case EbtInt16: + return EbtUint16; + case EbtInt: + return EbtUint; + case EbtInt64: + return EbtUint64; + default: + assert(false); + return EbtNumTypes; + } +} + +// Implements the following rules +// - If either operand has type float64_t or derived from float64_t, +// the other shall be converted to float64_t or derived type. +// - Otherwise, if either operand has type float32_t or derived from +// float32_t, the other shall be converted to float32_t or derived type. +// - Otherwise, if either operand has type float16_t or derived from +// float16_t, the other shall be converted to float16_t or derived type. +// - Otherwise, if both operands have integer types the following rules +// shall be applied to the operands: +// - If both operands have the same type, no further conversion +// is needed. +// - Otherwise, if both operands have signed integer types or both +// have unsigned integer types, the operand with the type of lesser +// integer conversion rank shall be converted to the type of the +// operand with greater rank. +// - Otherwise, if the operand that has unsigned integer type has rank +// greater than or equal to the rank of the type of the other +// operand, the operand with signed integer type shall be converted +// to the type of the operand with unsigned integer type. +// - Otherwise, if the type of the operand with signed integer type can +// represent all of the values of the type of the operand with +// unsigned integer type, the operand with unsigned integer type +// shall be converted to the type of the operand with signed +// integer type. +// - Otherwise, both operands shall be converted to the unsigned +// integer type corresponding to the type of the operand with signed +// integer type. + +std::tuple TIntermediate::getConversionDestinatonType(TBasicType type0, TBasicType type1, TOperator op) const +{ + TBasicType res0 = EbtNumTypes; + TBasicType res1 = EbtNumTypes; + + if ((isEsProfile() && + (version < 310 || !extensionRequested(E_GL_EXT_shader_implicit_conversions))) || + version == 110) + return std::make_tuple(res0, res1); + + if (getSource() == EShSourceHlsl) { + if (canImplicitlyPromote(type1, type0, op)) { + res0 = type0; + res1 = type0; + } else if (canImplicitlyPromote(type0, type1, op)) { + res0 = type1; + res1 = type1; + } + return std::make_tuple(res0, res1); + } + + if ((type0 == EbtDouble && canImplicitlyPromote(type1, EbtDouble, op)) || + (type1 == EbtDouble && canImplicitlyPromote(type0, EbtDouble, op)) ) { + res0 = EbtDouble; + res1 = EbtDouble; + } else if ((type0 == EbtFloat && canImplicitlyPromote(type1, EbtFloat, op)) || + (type1 == EbtFloat && canImplicitlyPromote(type0, EbtFloat, op)) ) { + res0 = EbtFloat; + res1 = EbtFloat; + } else if ((type0 == EbtFloat16 && canImplicitlyPromote(type1, EbtFloat16, op)) || + (type1 == EbtFloat16 && canImplicitlyPromote(type0, EbtFloat16, op)) ) { + res0 = EbtFloat16; + res1 = EbtFloat16; + } else if (isTypeInt(type0) && isTypeInt(type1) && + (canImplicitlyPromote(type0, type1, op) || canImplicitlyPromote(type1, type0, op))) { + if ((isTypeSignedInt(type0) && isTypeSignedInt(type1)) || + (isTypeUnsignedInt(type0) && isTypeUnsignedInt(type1))) { + if (getTypeRank(type0) < getTypeRank(type1)) { + res0 = type1; + res1 = type1; + } else { + res0 = type0; + res1 = type0; + } + } else if (isTypeUnsignedInt(type0) && (getTypeRank(type0) > getTypeRank(type1))) { + res0 = type0; + res1 = type0; + } else if (isTypeUnsignedInt(type1) && (getTypeRank(type1) > getTypeRank(type0))) { + res0 = type1; + res1 = type1; + } else if (isTypeSignedInt(type0)) { + if (canSignedIntTypeRepresentAllUnsignedValues(type0, type1)) { + res0 = type0; + res1 = type0; + } else { + res0 = getCorrespondingUnsignedType(type0); + res1 = getCorrespondingUnsignedType(type0); + } + } else if (isTypeSignedInt(type1)) { + if (canSignedIntTypeRepresentAllUnsignedValues(type1, type0)) { + res0 = type1; + res1 = type1; + } else { + res0 = getCorrespondingUnsignedType(type1); + res1 = getCorrespondingUnsignedType(type1); + } + } + } + + return std::make_tuple(res0, res1); +} + +// +// Given a type, find what operation would fully construct it. +// +TOperator TIntermediate::mapTypeToConstructorOp(const TType& type) const +{ + TOperator op = EOpNull; + + if (type.getQualifier().isNonUniform()) + return EOpConstructNonuniform; + + if (type.isCoopMat()) + return EOpConstructCooperativeMatrix; + + switch (type.getBasicType()) { + case EbtStruct: + op = EOpConstructStruct; + break; + case EbtSampler: + if (type.getSampler().isCombined()) + op = EOpConstructTextureSampler; + break; + case EbtFloat: + if (type.isMatrix()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructMat2x2; break; + case 3: op = EOpConstructMat2x3; break; + case 4: op = EOpConstructMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructMat3x2; break; + case 3: op = EOpConstructMat3x3; break; + case 4: op = EOpConstructMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructMat4x2; break; + case 3: op = EOpConstructMat4x3; break; + case 4: op = EOpConstructMat4x4; break; + default: break; // some compilers want this + } + break; + default: break; // some compilers want this + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructFloat; break; + case 2: op = EOpConstructVec2; break; + case 3: op = EOpConstructVec3; break; + case 4: op = EOpConstructVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtInt: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructIMat2x2; break; + case 3: op = EOpConstructIMat2x3; break; + case 4: op = EOpConstructIMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructIMat3x2; break; + case 3: op = EOpConstructIMat3x3; break; + case 4: op = EOpConstructIMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructIMat4x2; break; + case 3: op = EOpConstructIMat4x3; break; + case 4: op = EOpConstructIMat4x4; break; + default: break; // some compilers want this + } + break; + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructInt; break; + case 2: op = EOpConstructIVec2; break; + case 3: op = EOpConstructIVec3; break; + case 4: op = EOpConstructIVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtUint: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructUMat2x2; break; + case 3: op = EOpConstructUMat2x3; break; + case 4: op = EOpConstructUMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructUMat3x2; break; + case 3: op = EOpConstructUMat3x3; break; + case 4: op = EOpConstructUMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructUMat4x2; break; + case 3: op = EOpConstructUMat4x3; break; + case 4: op = EOpConstructUMat4x4; break; + default: break; // some compilers want this + } + break; + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructUint; break; + case 2: op = EOpConstructUVec2; break; + case 3: op = EOpConstructUVec3; break; + case 4: op = EOpConstructUVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtBool: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructBMat2x2; break; + case 3: op = EOpConstructBMat2x3; break; + case 4: op = EOpConstructBMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructBMat3x2; break; + case 3: op = EOpConstructBMat3x3; break; + case 4: op = EOpConstructBMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructBMat4x2; break; + case 3: op = EOpConstructBMat4x3; break; + case 4: op = EOpConstructBMat4x4; break; + default: break; // some compilers want this + } + break; + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructBool; break; + case 2: op = EOpConstructBVec2; break; + case 3: op = EOpConstructBVec3; break; + case 4: op = EOpConstructBVec4; break; + default: break; // some compilers want this + } + } + break; +#ifndef GLSLANG_WEB + case EbtDouble: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructDMat2x2; break; + case 3: op = EOpConstructDMat2x3; break; + case 4: op = EOpConstructDMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructDMat3x2; break; + case 3: op = EOpConstructDMat3x3; break; + case 4: op = EOpConstructDMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructDMat4x2; break; + case 3: op = EOpConstructDMat4x3; break; + case 4: op = EOpConstructDMat4x4; break; + default: break; // some compilers want this + } + break; + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructDouble; break; + case 2: op = EOpConstructDVec2; break; + case 3: op = EOpConstructDVec3; break; + case 4: op = EOpConstructDVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtFloat16: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructF16Mat2x2; break; + case 3: op = EOpConstructF16Mat2x3; break; + case 4: op = EOpConstructF16Mat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructF16Mat3x2; break; + case 3: op = EOpConstructF16Mat3x3; break; + case 4: op = EOpConstructF16Mat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructF16Mat4x2; break; + case 3: op = EOpConstructF16Mat4x3; break; + case 4: op = EOpConstructF16Mat4x4; break; + default: break; // some compilers want this + } + break; + } + } + else { + switch (type.getVectorSize()) { + case 1: op = EOpConstructFloat16; break; + case 2: op = EOpConstructF16Vec2; break; + case 3: op = EOpConstructF16Vec3; break; + case 4: op = EOpConstructF16Vec4; break; + default: break; // some compilers want this + } + } + break; + case EbtInt8: + switch(type.getVectorSize()) { + case 1: op = EOpConstructInt8; break; + case 2: op = EOpConstructI8Vec2; break; + case 3: op = EOpConstructI8Vec3; break; + case 4: op = EOpConstructI8Vec4; break; + default: break; // some compilers want this + } + break; + case EbtUint8: + switch(type.getVectorSize()) { + case 1: op = EOpConstructUint8; break; + case 2: op = EOpConstructU8Vec2; break; + case 3: op = EOpConstructU8Vec3; break; + case 4: op = EOpConstructU8Vec4; break; + default: break; // some compilers want this + } + break; + case EbtInt16: + switch(type.getVectorSize()) { + case 1: op = EOpConstructInt16; break; + case 2: op = EOpConstructI16Vec2; break; + case 3: op = EOpConstructI16Vec3; break; + case 4: op = EOpConstructI16Vec4; break; + default: break; // some compilers want this + } + break; + case EbtUint16: + switch(type.getVectorSize()) { + case 1: op = EOpConstructUint16; break; + case 2: op = EOpConstructU16Vec2; break; + case 3: op = EOpConstructU16Vec3; break; + case 4: op = EOpConstructU16Vec4; break; + default: break; // some compilers want this + } + break; + case EbtInt64: + switch(type.getVectorSize()) { + case 1: op = EOpConstructInt64; break; + case 2: op = EOpConstructI64Vec2; break; + case 3: op = EOpConstructI64Vec3; break; + case 4: op = EOpConstructI64Vec4; break; + default: break; // some compilers want this + } + break; + case EbtUint64: + switch(type.getVectorSize()) { + case 1: op = EOpConstructUint64; break; + case 2: op = EOpConstructU64Vec2; break; + case 3: op = EOpConstructU64Vec3; break; + case 4: op = EOpConstructU64Vec4; break; + default: break; // some compilers want this + } + break; + case EbtReference: + op = EOpConstructReference; + break; +#endif + default: + break; + } + + return op; +} + +// +// Safe way to combine two nodes into an aggregate. Works with null pointers, +// a node that's not a aggregate yet, etc. +// +// Returns the resulting aggregate, unless nullptr was passed in for +// both existing nodes. +// +TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right) +{ + if (left == nullptr && right == nullptr) + return nullptr; + + TIntermAggregate* aggNode = nullptr; + if (left != nullptr) + aggNode = left->getAsAggregate(); + if (aggNode == nullptr || aggNode->getOp() != EOpNull) { + aggNode = new TIntermAggregate; + if (left != nullptr) + aggNode->getSequence().push_back(left); + } + + if (right != nullptr) + aggNode->getSequence().push_back(right); + + return aggNode; +} + +TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc& loc) +{ + TIntermAggregate* aggNode = growAggregate(left, right); + if (aggNode) + aggNode->setLoc(loc); + + return aggNode; +} + +// +// Turn an existing node into an aggregate. +// +// Returns an aggregate, unless nullptr was passed in for the existing node. +// +TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node) +{ + if (node == nullptr) + return nullptr; + + TIntermAggregate* aggNode = new TIntermAggregate; + aggNode->getSequence().push_back(node); + aggNode->setLoc(node->getLoc()); + + return aggNode; +} + +TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node, const TSourceLoc& loc) +{ + if (node == nullptr) + return nullptr; + + TIntermAggregate* aggNode = new TIntermAggregate; + aggNode->getSequence().push_back(node); + aggNode->setLoc(loc); + + return aggNode; +} + +// +// Make an aggregate with an empty sequence. +// +TIntermAggregate* TIntermediate::makeAggregate(const TSourceLoc& loc) +{ + TIntermAggregate* aggNode = new TIntermAggregate; + aggNode->setLoc(loc); + + return aggNode; +} + +// +// For "if" test nodes. There are three children; a condition, +// a true path, and a false path. The two paths are in the +// nodePair. +// +// Returns the selection node created. +// +TIntermSelection* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& loc) +{ + // + // Don't prune the false path for compile-time constants; it's needed + // for static access analysis. + // + + TIntermSelection* node = new TIntermSelection(cond, nodePair.node1, nodePair.node2); + node->setLoc(loc); + + return node; +} + +TIntermTyped* TIntermediate::addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc) +{ + // However, the lowest precedence operators of the sequence operator ( , ) and the assignment operators + // ... are not included in the operators that can create a constant expression. + // + // if (left->getType().getQualifier().storage == EvqConst && + // right->getType().getQualifier().storage == EvqConst) { + + // return right; + //} + + TIntermTyped *commaAggregate = growAggregate(left, right, loc); + commaAggregate->getAsAggregate()->setOperator(EOpComma); + commaAggregate->setType(right->getType()); + commaAggregate->getWritableType().getQualifier().makeTemporary(); + + return commaAggregate; +} + +TIntermTyped* TIntermediate::addMethod(TIntermTyped* object, const TType& type, const TString* name, const TSourceLoc& loc) +{ + TIntermMethod* method = new TIntermMethod(object, type, *name); + method->setLoc(loc); + + return method; +} + +// +// For "?:" test nodes. There are three children; a condition, +// a true path, and a false path. The two paths are specified +// as separate parameters. For vector 'cond', the true and false +// are not paths, but vectors to mix. +// +// Specialization constant operations include +// - The ternary operator ( ? : ) +// +// Returns the selection node created, or nullptr if one could not be. +// +TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, + const TSourceLoc& loc) +{ + // If it's void, go to the if-then-else selection() + if (trueBlock->getBasicType() == EbtVoid && falseBlock->getBasicType() == EbtVoid) { + TIntermNodePair pair = { trueBlock, falseBlock }; + TIntermSelection* selection = addSelection(cond, pair, loc); + if (getSource() == EShSourceHlsl) + selection->setNoShortCircuit(); + + return selection; + } + + // + // Get compatible types. + // + auto children = addConversion(EOpSequence, trueBlock, falseBlock); + trueBlock = std::get<0>(children); + falseBlock = std::get<1>(children); + + if (trueBlock == nullptr || falseBlock == nullptr) + return nullptr; + + // Handle a vector condition as a mix + if (!cond->getType().isScalarOrVec1()) { + TType targetVectorType(trueBlock->getType().getBasicType(), EvqTemporary, + cond->getType().getVectorSize()); + // smear true/false operands as needed + trueBlock = addUniShapeConversion(EOpMix, targetVectorType, trueBlock); + falseBlock = addUniShapeConversion(EOpMix, targetVectorType, falseBlock); + + // After conversion, types have to match. + if (falseBlock->getType() != trueBlock->getType()) + return nullptr; + + // make the mix operation + TIntermAggregate* mix = makeAggregate(loc); + mix = growAggregate(mix, falseBlock); + mix = growAggregate(mix, trueBlock); + mix = growAggregate(mix, cond); + mix->setType(targetVectorType); + mix->setOp(EOpMix); + + return mix; + } + + // Now have a scalar condition... + + // Convert true and false expressions to matching types + addBiShapeConversion(EOpMix, trueBlock, falseBlock); + + // After conversion, types have to match. + if (falseBlock->getType() != trueBlock->getType()) + return nullptr; + + // Eliminate the selection when the condition is a scalar and all operands are constant. + if (cond->getAsConstantUnion() && trueBlock->getAsConstantUnion() && falseBlock->getAsConstantUnion()) { + if (cond->getAsConstantUnion()->getConstArray()[0].getBConst()) + return trueBlock; + else + return falseBlock; + } + + // + // Make a selection node. + // + TIntermSelection* node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType()); + node->setLoc(loc); + node->getQualifier().precision = std::max(trueBlock->getQualifier().precision, falseBlock->getQualifier().precision); + + if ((cond->getQualifier().isConstant() && specConstantPropagates(*trueBlock, *falseBlock)) || + (cond->getQualifier().isSpecConstant() && trueBlock->getQualifier().isConstant() && + falseBlock->getQualifier().isConstant())) + node->getQualifier().makeSpecConstant(); + else + node->getQualifier().makeTemporary(); + + if (getSource() == EShSourceHlsl) + node->setNoShortCircuit(); + + return node; +} + +// +// Constant terminal nodes. Has a union that contains bool, float or int constants +// +// Returns the constant union node created. +// + +TIntermConstantUnion* TIntermediate::addConstantUnion(const TConstUnionArray& unionArray, const TType& t, const TSourceLoc& loc, bool literal) const +{ + TIntermConstantUnion* node = new TIntermConstantUnion(unionArray, t); + node->getQualifier().storage = EvqConst; + node->setLoc(loc); + if (literal) + node->setLiteral(); + + return node; +} +TIntermConstantUnion* TIntermediate::addConstantUnion(signed char i8, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setI8Const(i8); + + return addConstantUnion(unionArray, TType(EbtInt8, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned char u8, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setUConst(u8); + + return addConstantUnion(unionArray, TType(EbtUint8, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(signed short i16, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setI16Const(i16); + + return addConstantUnion(unionArray, TType(EbtInt16, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned short u16, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setU16Const(u16); + + return addConstantUnion(unionArray, TType(EbtUint16, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(int i, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setIConst(i); + + return addConstantUnion(unionArray, TType(EbtInt, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned int u, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setUConst(u); + + return addConstantUnion(unionArray, TType(EbtUint, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(long long i64, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setI64Const(i64); + + return addConstantUnion(unionArray, TType(EbtInt64, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned long long u64, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setU64Const(u64); + + return addConstantUnion(unionArray, TType(EbtUint64, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(bool b, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setBConst(b); + + return addConstantUnion(unionArray, TType(EbtBool, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(double d, TBasicType baseType, const TSourceLoc& loc, bool literal) const +{ + assert(baseType == EbtFloat || baseType == EbtDouble || baseType == EbtFloat16); + + TConstUnionArray unionArray(1); + unionArray[0].setDConst(d); + + return addConstantUnion(unionArray, TType(baseType, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(const TString* s, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setSConst(s); + + return addConstantUnion(unionArray, TType(EbtString, EvqConst), loc, literal); +} + +// Put vector swizzle selectors onto the given sequence +void TIntermediate::pushSelector(TIntermSequence& sequence, const TVectorSelector& selector, const TSourceLoc& loc) +{ + TIntermConstantUnion* constIntNode = addConstantUnion(selector, loc); + sequence.push_back(constIntNode); +} + +// Put matrix swizzle selectors onto the given sequence +void TIntermediate::pushSelector(TIntermSequence& sequence, const TMatrixSelector& selector, const TSourceLoc& loc) +{ + TIntermConstantUnion* constIntNode = addConstantUnion(selector.coord1, loc); + sequence.push_back(constIntNode); + constIntNode = addConstantUnion(selector.coord2, loc); + sequence.push_back(constIntNode); +} + +// Make an aggregate node that has a sequence of all selectors. +template TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors& selector, const TSourceLoc& loc); +template TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors& selector, const TSourceLoc& loc); +template +TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors& selector, const TSourceLoc& loc) +{ + TIntermAggregate* node = new TIntermAggregate(EOpSequence); + + node->setLoc(loc); + TIntermSequence &sequenceVector = node->getSequence(); + + for (int i = 0; i < selector.size(); i++) + pushSelector(sequenceVector, selector[i], loc); + + return node; +} + +// +// Follow the left branches down to the root of an l-value +// expression (just "." and []). +// +// Return the base of the l-value (where following indexing quits working). +// Return nullptr if a chain following dereferences cannot be followed. +// +// 'swizzleOkay' says whether or not it is okay to consider a swizzle +// a valid part of the dereference chain. +// +const TIntermTyped* TIntermediate::findLValueBase(const TIntermTyped* node, bool swizzleOkay) +{ + do { + const TIntermBinary* binary = node->getAsBinaryNode(); + if (binary == nullptr) + return node; + TOperator op = binary->getOp(); + if (op != EOpIndexDirect && op != EOpIndexIndirect && op != EOpIndexDirectStruct && op != EOpVectorSwizzle && op != EOpMatrixSwizzle) + return nullptr; + if (! swizzleOkay) { + if (op == EOpVectorSwizzle || op == EOpMatrixSwizzle) + return nullptr; + if ((op == EOpIndexDirect || op == EOpIndexIndirect) && + (binary->getLeft()->getType().isVector() || binary->getLeft()->getType().isScalar()) && + ! binary->getLeft()->getType().isArray()) + return nullptr; + } + node = node->getAsBinaryNode()->getLeft(); + } while (true); +} + +// +// Create while and do-while loop nodes. +// +TIntermLoop* TIntermediate::addLoop(TIntermNode* body, TIntermTyped* test, TIntermTyped* terminal, bool testFirst, + const TSourceLoc& loc) +{ + TIntermLoop* node = new TIntermLoop(body, test, terminal, testFirst); + node->setLoc(loc); + + return node; +} + +// +// Create a for-loop sequence. +// +TIntermAggregate* TIntermediate::addForLoop(TIntermNode* body, TIntermNode* initializer, TIntermTyped* test, + TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc, TIntermLoop*& node) +{ + node = new TIntermLoop(body, test, terminal, testFirst); + node->setLoc(loc); + + // make a sequence of the initializer and statement, but try to reuse the + // aggregate already created for whatever is in the initializer, if there is one + TIntermAggregate* loopSequence = (initializer == nullptr || + initializer->getAsAggregate() == nullptr) ? makeAggregate(initializer, loc) + : initializer->getAsAggregate(); + if (loopSequence != nullptr && loopSequence->getOp() == EOpSequence) + loopSequence->setOp(EOpNull); + loopSequence = growAggregate(loopSequence, node); + loopSequence->setOperator(EOpSequence); + + return loopSequence; +} + +// +// Add branches. +// +TIntermBranch* TIntermediate::addBranch(TOperator branchOp, const TSourceLoc& loc) +{ + return addBranch(branchOp, nullptr, loc); +} + +TIntermBranch* TIntermediate::addBranch(TOperator branchOp, TIntermTyped* expression, const TSourceLoc& loc) +{ + TIntermBranch* node = new TIntermBranch(branchOp, expression); + node->setLoc(loc); + + return node; +} + +// +// This is to be executed after the final root is put on top by the parsing +// process. +// +bool TIntermediate::postProcess(TIntermNode* root, EShLanguage /*language*/) +{ + if (root == nullptr) + return true; + + // Finish off the top-level sequence + TIntermAggregate* aggRoot = root->getAsAggregate(); + if (aggRoot && aggRoot->getOp() == EOpNull) + aggRoot->setOperator(EOpSequence); + +#ifndef GLSLANG_WEB + // Propagate 'noContraction' label in backward from 'precise' variables. + glslang::PropagateNoContraction(*this); + + switch (textureSamplerTransformMode) { + case EShTexSampTransKeep: + break; + case EShTexSampTransUpgradeTextureRemoveSampler: + performTextureUpgradeAndSamplerRemovalTransformation(root); + break; + case EShTexSampTransCount: + assert(0); + break; + } +#endif + + return true; +} + +void TIntermediate::addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage language, TSymbolTable& symbolTable) +{ + // Add top-level nodes for declarations that must be checked cross + // compilation unit by a linker, yet might not have been referenced + // by the AST. + // + // Almost entirely, translation of symbols is driven by what's present + // in the AST traversal, not by translating the symbol table. + // + // However, there are some special cases: + // - From the specification: "Special built-in inputs gl_VertexID and + // gl_InstanceID are also considered active vertex attributes." + // - Linker-based type mismatch error reporting needs to see all + // uniforms/ins/outs variables and blocks. + // - ftransform() can make gl_Vertex and gl_ModelViewProjectionMatrix active. + // + + // if (ftransformUsed) { + // TODO: 1.1 lowering functionality: track ftransform() usage + // addSymbolLinkageNode(root, symbolTable, "gl_Vertex"); + // addSymbolLinkageNode(root, symbolTable, "gl_ModelViewProjectionMatrix"); + //} + + if (language == EShLangVertex) { + // the names won't be found in the symbol table unless the versions are right, + // so version logic does not need to be repeated here + addSymbolLinkageNode(linkage, symbolTable, "gl_VertexID"); + addSymbolLinkageNode(linkage, symbolTable, "gl_InstanceID"); + } + + // Add a child to the root node for the linker objects + linkage->setOperator(EOpLinkerObjects); + treeRoot = growAggregate(treeRoot, linkage); +} + +// +// Add the given name or symbol to the list of nodes at the end of the tree used +// for link-time checking and external linkage. +// + +void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable& symbolTable, const TString& name) +{ + TSymbol* symbol = symbolTable.find(name); + if (symbol) + addSymbolLinkageNode(linkage, *symbol->getAsVariable()); +} + +void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol& symbol) +{ + const TVariable* variable = symbol.getAsVariable(); + if (! variable) { + // This must be a member of an anonymous block, and we need to add the whole block + const TAnonMember* anon = symbol.getAsAnonMember(); + variable = &anon->getAnonContainer(); + } + TIntermSymbol* node = addSymbol(*variable); + linkage = growAggregate(linkage, node); +} + +// +// Add a caller->callee relationship to the call graph. +// Assumes the strings are unique per signature. +// +void TIntermediate::addToCallGraph(TInfoSink& /*infoSink*/, const TString& caller, const TString& callee) +{ + // Duplicates are okay, but faster to not keep them, and they come grouped by caller, + // as long as new ones are push on the same end we check on for duplicates + for (TGraph::const_iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (call->caller != caller) + break; + if (call->callee == callee) + return; + } + + callGraph.push_front(TCall(caller, callee)); +} + +// +// This deletes the tree. +// +void TIntermediate::removeTree() +{ + if (treeRoot) + RemoveAllTreeNodes(treeRoot); +} + +// +// Implement the part of KHR_vulkan_glsl that lists the set of operations +// that can result in a specialization constant operation. +// +// "5.x Specialization Constant Operations" +// +// Only some operations discussed in this section may be applied to a +// specialization constant and still yield a result that is as +// specialization constant. The operations allowed are listed below. +// When a specialization constant is operated on with one of these +// operators and with another constant or specialization constant, the +// result is implicitly a specialization constant. +// +// - int(), uint(), and bool() constructors for type conversions +// from any of the following types to any of the following types: +// * int +// * uint +// * bool +// - vector versions of the above conversion constructors +// - allowed implicit conversions of the above +// - swizzles (e.g., foo.yx) +// - The following when applied to integer or unsigned integer types: +// * unary negative ( - ) +// * binary operations ( + , - , * , / , % ) +// * shift ( <<, >> ) +// * bitwise operations ( & , | , ^ ) +// - The following when applied to integer or unsigned integer scalar types: +// * comparison ( == , != , > , >= , < , <= ) +// - The following when applied to the Boolean scalar type: +// * not ( ! ) +// * logical operations ( && , || , ^^ ) +// * comparison ( == , != )" +// +// This function just handles binary and unary nodes. Construction +// rules are handled in construction paths that are not covered by the unary +// and binary paths, while required conversions will still show up here +// as unary converters in the from a construction operator. +// +bool TIntermediate::isSpecializationOperation(const TIntermOperator& node) const +{ + // The operations resulting in floating point are quite limited + // (However, some floating-point operations result in bool, like ">", + // so are handled later.) + if (node.getType().isFloatingDomain()) { + switch (node.getOp()) { + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + case EOpVectorSwizzle: + case EOpConvFloatToDouble: + case EOpConvDoubleToFloat: + case EOpConvFloat16ToFloat: + case EOpConvFloatToFloat16: + case EOpConvFloat16ToDouble: + case EOpConvDoubleToFloat16: + return true; + default: + return false; + } + } + + // Check for floating-point arguments + if (const TIntermBinary* bin = node.getAsBinaryNode()) + if (bin->getLeft() ->getType().isFloatingDomain() || + bin->getRight()->getType().isFloatingDomain()) + return false; + + // So, for now, we can assume everything left is non-floating-point... + + // Now check for integer/bool-based operations + switch (node.getOp()) { + + // dereference/swizzle + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + case EOpVectorSwizzle: + + // (u)int* -> bool + case EOpConvInt8ToBool: + case EOpConvInt16ToBool: + case EOpConvIntToBool: + case EOpConvInt64ToBool: + case EOpConvUint8ToBool: + case EOpConvUint16ToBool: + case EOpConvUintToBool: + case EOpConvUint64ToBool: + + // bool -> (u)int* + case EOpConvBoolToInt8: + case EOpConvBoolToInt16: + case EOpConvBoolToInt: + case EOpConvBoolToInt64: + case EOpConvBoolToUint8: + case EOpConvBoolToUint16: + case EOpConvBoolToUint: + case EOpConvBoolToUint64: + + // int8_t -> (u)int* + case EOpConvInt8ToInt16: + case EOpConvInt8ToInt: + case EOpConvInt8ToInt64: + case EOpConvInt8ToUint8: + case EOpConvInt8ToUint16: + case EOpConvInt8ToUint: + case EOpConvInt8ToUint64: + + // int16_t -> (u)int* + case EOpConvInt16ToInt8: + case EOpConvInt16ToInt: + case EOpConvInt16ToInt64: + case EOpConvInt16ToUint8: + case EOpConvInt16ToUint16: + case EOpConvInt16ToUint: + case EOpConvInt16ToUint64: + + // int32_t -> (u)int* + case EOpConvIntToInt8: + case EOpConvIntToInt16: + case EOpConvIntToInt64: + case EOpConvIntToUint8: + case EOpConvIntToUint16: + case EOpConvIntToUint: + case EOpConvIntToUint64: + + // int64_t -> (u)int* + case EOpConvInt64ToInt8: + case EOpConvInt64ToInt16: + case EOpConvInt64ToInt: + case EOpConvInt64ToUint8: + case EOpConvInt64ToUint16: + case EOpConvInt64ToUint: + case EOpConvInt64ToUint64: + + // uint8_t -> (u)int* + case EOpConvUint8ToInt8: + case EOpConvUint8ToInt16: + case EOpConvUint8ToInt: + case EOpConvUint8ToInt64: + case EOpConvUint8ToUint16: + case EOpConvUint8ToUint: + case EOpConvUint8ToUint64: + + // uint16_t -> (u)int* + case EOpConvUint16ToInt8: + case EOpConvUint16ToInt16: + case EOpConvUint16ToInt: + case EOpConvUint16ToInt64: + case EOpConvUint16ToUint8: + case EOpConvUint16ToUint: + case EOpConvUint16ToUint64: + + // uint32_t -> (u)int* + case EOpConvUintToInt8: + case EOpConvUintToInt16: + case EOpConvUintToInt: + case EOpConvUintToInt64: + case EOpConvUintToUint8: + case EOpConvUintToUint16: + case EOpConvUintToUint64: + + // uint64_t -> (u)int* + case EOpConvUint64ToInt8: + case EOpConvUint64ToInt16: + case EOpConvUint64ToInt: + case EOpConvUint64ToInt64: + case EOpConvUint64ToUint8: + case EOpConvUint64ToUint16: + case EOpConvUint64ToUint: + + // unary operations + case EOpNegative: + case EOpLogicalNot: + case EOpBitwiseNot: + + // binary operations + case EOpAdd: + case EOpSub: + case EOpMul: + case EOpVectorTimesScalar: + case EOpDiv: + case EOpMod: + case EOpRightShift: + case EOpLeftShift: + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpLogicalOr: + case EOpLogicalXor: + case EOpLogicalAnd: + case EOpEqual: + case EOpNotEqual: + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + return true; + default: + return false; + } +} + +// Is the operation one that must propagate nonuniform? +bool TIntermediate::isNonuniformPropagating(TOperator op) const +{ + // "* All Operators in Section 5.1 (Operators), except for assignment, + // arithmetic assignment, and sequence + // * Component selection in Section 5.5 + // * Matrix components in Section 5.6 + // * Structure and Array Operations in Section 5.7, except for the length + // method." + switch (op) { + case EOpPostIncrement: + case EOpPostDecrement: + case EOpPreIncrement: + case EOpPreDecrement: + + case EOpNegative: + case EOpLogicalNot: + case EOpVectorLogicalNot: + case EOpBitwiseNot: + + case EOpAdd: + case EOpSub: + case EOpMul: + case EOpDiv: + case EOpMod: + case EOpRightShift: + case EOpLeftShift: + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpEqual: + case EOpNotEqual: + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + case EOpVectorTimesScalar: + case EOpVectorTimesMatrix: + case EOpMatrixTimesVector: + case EOpMatrixTimesScalar: + + case EOpLogicalOr: + case EOpLogicalXor: + case EOpLogicalAnd: + + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + case EOpVectorSwizzle: + return true; + + default: + break; + } + + return false; +} + +//////////////////////////////////////////////////////////////// +// +// Member functions of the nodes used for building the tree. +// +//////////////////////////////////////////////////////////////// + +// +// Say whether or not an operation node changes the value of a variable. +// +// Returns true if state is modified. +// +bool TIntermOperator::modifiesState() const +{ + switch (op) { + case EOpPostIncrement: + case EOpPostDecrement: + case EOpPreIncrement: + case EOpPreDecrement: + case EOpAssign: + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpVectorTimesMatrixAssign: + case EOpVectorTimesScalarAssign: + case EOpMatrixTimesScalarAssign: + case EOpMatrixTimesMatrixAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpLeftShiftAssign: + case EOpRightShiftAssign: + return true; + default: + return false; + } +} + +// +// returns true if the operator is for one of the constructors +// +bool TIntermOperator::isConstructor() const +{ + return op > EOpConstructGuardStart && op < EOpConstructGuardEnd; +} + +// +// Make sure the type of an operator is appropriate for its +// combination of operation and operand type. This will invoke +// promoteUnary, promoteBinary, etc as needed. +// +// Returns false if nothing makes sense. +// +bool TIntermediate::promote(TIntermOperator* node) +{ + if (node == nullptr) + return false; + + if (node->getAsUnaryNode()) + return promoteUnary(*node->getAsUnaryNode()); + + if (node->getAsBinaryNode()) + return promoteBinary(*node->getAsBinaryNode()); + + if (node->getAsAggregate()) + return promoteAggregate(*node->getAsAggregate()); + + return false; +} + +// +// See TIntermediate::promote +// +bool TIntermediate::promoteUnary(TIntermUnary& node) +{ + const TOperator op = node.getOp(); + TIntermTyped* operand = node.getOperand(); + + switch (op) { + case EOpLogicalNot: + // Convert operand to a boolean type + if (operand->getBasicType() != EbtBool) { + // Add constructor to boolean type. If that fails, we can't do it, so return false. + TIntermTyped* converted = addConversion(op, TType(EbtBool), operand); + if (converted == nullptr) + return false; + + // Use the result of converting the node to a bool. + node.setOperand(operand = converted); // also updates stack variable + } + break; + case EOpBitwiseNot: + if (!isTypeInt(operand->getBasicType())) + return false; + break; + case EOpNegative: + case EOpPostIncrement: + case EOpPostDecrement: + case EOpPreIncrement: + case EOpPreDecrement: + if (!isTypeInt(operand->getBasicType()) && + operand->getBasicType() != EbtFloat && + operand->getBasicType() != EbtFloat16 && + operand->getBasicType() != EbtDouble) + + return false; + break; + default: + // HLSL uses this path for initial function signature finding for built-ins + // taking a single argument, which generally don't participate in + // operator-based type promotion (type conversion will occur later). + // For now, scalar argument cases are relying on the setType() call below. + if (getSource() == EShSourceHlsl) + break; + + // GLSL only allows integer arguments for the cases identified above in the + // case statements. + if (operand->getBasicType() != EbtFloat) + return false; + } + + node.setType(operand->getType()); + node.getWritableType().getQualifier().makeTemporary(); + + return true; +} + +void TIntermUnary::updatePrecision() +{ + if (getBasicType() == EbtInt || getBasicType() == EbtUint || getBasicType() == EbtFloat || getBasicType() == EbtFloat16) { + if (operand->getQualifier().precision > getQualifier().precision) + getQualifier().precision = operand->getQualifier().precision; + } +} + +// +// See TIntermediate::promote +// +bool TIntermediate::promoteBinary(TIntermBinary& node) +{ + TOperator op = node.getOp(); + TIntermTyped* left = node.getLeft(); + TIntermTyped* right = node.getRight(); + + // Arrays and structures have to be exact matches. + if ((left->isArray() || right->isArray() || left->getBasicType() == EbtStruct || right->getBasicType() == EbtStruct) + && left->getType() != right->getType()) + return false; + + // Base assumption: just make the type the same as the left + // operand. Only deviations from this will be coded. + node.setType(left->getType()); + node.getWritableType().getQualifier().clear(); + + // Composite and opaque types don't having pending operator changes, e.g., + // array, structure, and samplers. Just establish final type and correctness. + if (left->isArray() || left->getBasicType() == EbtStruct || left->getBasicType() == EbtSampler) { + switch (op) { + case EOpEqual: + case EOpNotEqual: + if (left->getBasicType() == EbtSampler) { + // can't compare samplers + return false; + } else { + // Promote to conditional + node.setType(TType(EbtBool)); + } + + return true; + + case EOpAssign: + // Keep type from above + + return true; + + default: + return false; + } + } + + // + // We now have only scalars, vectors, and matrices to worry about. + // + + // HLSL implicitly promotes bool -> int for numeric operations. + // (Implicit conversions to make the operands match each other's types were already done.) + if (getSource() == EShSourceHlsl && + (left->getBasicType() == EbtBool || right->getBasicType() == EbtBool)) { + switch (op) { + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + + case EOpRightShift: + case EOpLeftShift: + + case EOpMod: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpMul: + if (left->getBasicType() == EbtBool) + left = createConversion(EbtInt, left); + if (right->getBasicType() == EbtBool) + right = createConversion(EbtInt, right); + if (left == nullptr || right == nullptr) + return false; + node.setLeft(left); + node.setRight(right); + + // Update the original base assumption on result type.. + node.setType(left->getType()); + node.getWritableType().getQualifier().clear(); + + break; + + default: + break; + } + } + + // Do general type checks against individual operands (comparing left and right is coming up, checking mixed shapes after that) + switch (op) { + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + // Relational comparisons need numeric types and will promote to scalar Boolean. + if (left->getBasicType() == EbtBool) + return false; + + node.setType(TType(EbtBool, EvqTemporary, left->getVectorSize())); + break; + + case EOpEqual: + case EOpNotEqual: + if (getSource() == EShSourceHlsl) { + const int resultWidth = std::max(left->getVectorSize(), right->getVectorSize()); + + // In HLSL, == or != on vectors means component-wise comparison. + if (resultWidth > 1) { + op = (op == EOpEqual) ? EOpVectorEqual : EOpVectorNotEqual; + node.setOp(op); + } + + node.setType(TType(EbtBool, EvqTemporary, resultWidth)); + } else { + // All the above comparisons result in a bool (but not the vector compares) + node.setType(TType(EbtBool)); + } + break; + + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + // logical ops operate only on Booleans or vectors of Booleans. + if (left->getBasicType() != EbtBool || left->isMatrix()) + return false; + + if (getSource() == EShSourceGlsl) { + // logical ops operate only on scalar Booleans and will promote to scalar Boolean. + if (left->isVector()) + return false; + } + + node.setType(TType(EbtBool, EvqTemporary, left->getVectorSize())); + break; + + case EOpRightShift: + case EOpLeftShift: + case EOpRightShiftAssign: + case EOpLeftShiftAssign: + + case EOpMod: + case EOpModAssign: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + if (getSource() == EShSourceHlsl) + break; + + // Check for integer-only operands. + if (!isTypeInt(left->getBasicType()) && !isTypeInt(right->getBasicType())) + return false; + if (left->isMatrix() || right->isMatrix()) + return false; + + break; + + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpMul: + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpDivAssign: + // check for non-Boolean operands + if (left->getBasicType() == EbtBool || right->getBasicType() == EbtBool) + return false; + + default: + break; + } + + // Compare left and right, and finish with the cases where the operand types must match + switch (op) { + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + + case EOpEqual: + case EOpNotEqual: + case EOpVectorEqual: + case EOpVectorNotEqual: + + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + return left->getType() == right->getType(); + + case EOpMod: + case EOpModAssign: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + + case EOpAdd: + case EOpSub: + case EOpDiv: + + case EOpAddAssign: + case EOpSubAssign: + case EOpDivAssign: + // Quick out in case the types do match + if (left->getType() == right->getType()) + return true; + + // Fall through + + case EOpMul: + case EOpMulAssign: + // At least the basic type has to match + if (left->getBasicType() != right->getBasicType()) + return false; + + default: + break; + } + + if (left->getType().isCoopMat() || right->getType().isCoopMat()) { + if (left->getType().isCoopMat() && right->getType().isCoopMat() && + *left->getType().getTypeParameters() != *right->getType().getTypeParameters()) { + return false; + } + switch (op) { + case EOpMul: + case EOpMulAssign: + if (left->getType().isCoopMat() && right->getType().isCoopMat()) { + return false; + } + if (op == EOpMulAssign && right->getType().isCoopMat()) { + return false; + } + node.setOp(op == EOpMulAssign ? EOpMatrixTimesScalarAssign : EOpMatrixTimesScalar); + if (right->getType().isCoopMat()) { + node.setType(right->getType()); + } + return true; + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpAssign: + // These require both to be cooperative matrices + if (!left->getType().isCoopMat() || !right->getType().isCoopMat()) { + return false; + } + return true; + default: + break; + } + return false; + } + + // Finish handling the case, for all ops, where both operands are scalars. + if (left->isScalar() && right->isScalar()) + return true; + + // Finish handling the case, for all ops, where there are two vectors of different sizes + if (left->isVector() && right->isVector() && left->getVectorSize() != right->getVectorSize() && right->getVectorSize() > 1) + return false; + + // + // We now have a mix of scalars, vectors, or matrices, for non-relational operations. + // + + // Can these two operands be combined, what is the resulting type? + TBasicType basicType = left->getBasicType(); + switch (op) { + case EOpMul: + if (!left->isMatrix() && right->isMatrix()) { + if (left->isVector()) { + if (left->getVectorSize() != right->getMatrixRows()) + return false; + node.setOp(op = EOpVectorTimesMatrix); + node.setType(TType(basicType, EvqTemporary, right->getMatrixCols())); + } else { + node.setOp(op = EOpMatrixTimesScalar); + node.setType(TType(basicType, EvqTemporary, 0, right->getMatrixCols(), right->getMatrixRows())); + } + } else if (left->isMatrix() && !right->isMatrix()) { + if (right->isVector()) { + if (left->getMatrixCols() != right->getVectorSize()) + return false; + node.setOp(op = EOpMatrixTimesVector); + node.setType(TType(basicType, EvqTemporary, left->getMatrixRows())); + } else { + node.setOp(op = EOpMatrixTimesScalar); + } + } else if (left->isMatrix() && right->isMatrix()) { + if (left->getMatrixCols() != right->getMatrixRows()) + return false; + node.setOp(op = EOpMatrixTimesMatrix); + node.setType(TType(basicType, EvqTemporary, 0, right->getMatrixCols(), left->getMatrixRows())); + } else if (! left->isMatrix() && ! right->isMatrix()) { + if (left->isVector() && right->isVector()) { + ; // leave as component product + } else if (left->isVector() || right->isVector()) { + node.setOp(op = EOpVectorTimesScalar); + if (right->isVector()) + node.setType(TType(basicType, EvqTemporary, right->getVectorSize())); + } + } else { + return false; + } + break; + case EOpMulAssign: + if (! left->isMatrix() && right->isMatrix()) { + if (left->isVector()) { + if (left->getVectorSize() != right->getMatrixRows() || left->getVectorSize() != right->getMatrixCols()) + return false; + node.setOp(op = EOpVectorTimesMatrixAssign); + } else { + return false; + } + } else if (left->isMatrix() && !right->isMatrix()) { + if (right->isVector()) { + return false; + } else { + node.setOp(op = EOpMatrixTimesScalarAssign); + } + } else if (left->isMatrix() && right->isMatrix()) { + if (left->getMatrixCols() != right->getMatrixCols() || left->getMatrixCols() != right->getMatrixRows()) + return false; + node.setOp(op = EOpMatrixTimesMatrixAssign); + } else if (!left->isMatrix() && !right->isMatrix()) { + if (left->isVector() && right->isVector()) { + // leave as component product + } else if (left->isVector() || right->isVector()) { + if (! left->isVector()) + return false; + node.setOp(op = EOpVectorTimesScalarAssign); + } + } else { + return false; + } + break; + + case EOpRightShift: + case EOpLeftShift: + case EOpRightShiftAssign: + case EOpLeftShiftAssign: + if (right->isVector() && (! left->isVector() || right->getVectorSize() != left->getVectorSize())) + return false; + break; + + case EOpAssign: + if (left->getVectorSize() != right->getVectorSize() || left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows()) + return false; + // fall through + + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpMod: + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpAddAssign: + case EOpSubAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + + if ((left->isMatrix() && right->isVector()) || + (left->isVector() && right->isMatrix()) || + left->getBasicType() != right->getBasicType()) + return false; + if (left->isMatrix() && right->isMatrix() && (left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows())) + return false; + if (left->isVector() && right->isVector() && left->getVectorSize() != right->getVectorSize()) + return false; + if (right->isVector() || right->isMatrix()) { + node.getWritableType().shallowCopy(right->getType()); + node.getWritableType().getQualifier().makeTemporary(); + } + break; + + default: + return false; + } + + // + // One more check for assignment. + // + switch (op) { + // The resulting type has to match the left operand. + case EOpAssign: + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpLeftShiftAssign: + case EOpRightShiftAssign: + if (node.getType() != left->getType()) + return false; + break; + default: + break; + } + + return true; +} + +// +// See TIntermediate::promote +// +bool TIntermediate::promoteAggregate(TIntermAggregate& node) +{ + TOperator op = node.getOp(); + TIntermSequence& args = node.getSequence(); + const int numArgs = static_cast(args.size()); + + // Presently, only hlsl does intrinsic promotions. + if (getSource() != EShSourceHlsl) + return true; + + // set of opcodes that can be promoted in this manner. + switch (op) { + case EOpAtan: + case EOpClamp: + case EOpCross: + case EOpDistance: + case EOpDot: + case EOpDst: + case EOpFaceForward: + // case EOpFindMSB: TODO: + // case EOpFindLSB: TODO: + case EOpFma: + case EOpMod: + case EOpFrexp: + case EOpLdexp: + case EOpMix: + case EOpLit: + case EOpMax: + case EOpMin: + case EOpModf: + // case EOpGenMul: TODO: + case EOpPow: + case EOpReflect: + case EOpRefract: + // case EOpSinCos: TODO: + case EOpSmoothStep: + case EOpStep: + break; + default: + return true; + } + + // TODO: array and struct behavior + + // Try converting all nodes to the given node's type + TIntermSequence convertedArgs(numArgs, nullptr); + + // Try to convert all types to the nonConvArg type. + for (int nonConvArg = 0; nonConvArg < numArgs; ++nonConvArg) { + // Try converting all args to this arg's type + for (int convArg = 0; convArg < numArgs; ++convArg) { + convertedArgs[convArg] = addConversion(op, args[nonConvArg]->getAsTyped()->getType(), + args[convArg]->getAsTyped()); + } + + // If we successfully converted all the args, use the result. + if (std::all_of(convertedArgs.begin(), convertedArgs.end(), + [](const TIntermNode* node) { return node != nullptr; })) { + + std::swap(args, convertedArgs); + return true; + } + } + + return false; +} + +void TIntermBinary::updatePrecision() +{ + if (getBasicType() == EbtInt || getBasicType() == EbtUint || getBasicType() == EbtFloat || getBasicType() == EbtFloat16) { + getQualifier().precision = std::max(right->getQualifier().precision, left->getQualifier().precision); + if (getQualifier().precision != EpqNone) { + left->propagatePrecision(getQualifier().precision); + right->propagatePrecision(getQualifier().precision); + } + } +} + +void TIntermTyped::propagatePrecision(TPrecisionQualifier newPrecision) +{ + if (getQualifier().precision != EpqNone || (getBasicType() != EbtInt && getBasicType() != EbtUint && getBasicType() != EbtFloat && getBasicType() != EbtFloat16)) + return; + + getQualifier().precision = newPrecision; + + TIntermBinary* binaryNode = getAsBinaryNode(); + if (binaryNode) { + binaryNode->getLeft()->propagatePrecision(newPrecision); + binaryNode->getRight()->propagatePrecision(newPrecision); + + return; + } + + TIntermUnary* unaryNode = getAsUnaryNode(); + if (unaryNode) { + unaryNode->getOperand()->propagatePrecision(newPrecision); + + return; + } + + TIntermAggregate* aggregateNode = getAsAggregate(); + if (aggregateNode) { + TIntermSequence operands = aggregateNode->getSequence(); + for (unsigned int i = 0; i < operands.size(); ++i) { + TIntermTyped* typedNode = operands[i]->getAsTyped(); + if (! typedNode) + break; + typedNode->propagatePrecision(newPrecision); + } + + return; + } + + TIntermSelection* selectionNode = getAsSelectionNode(); + if (selectionNode) { + TIntermTyped* typedNode = selectionNode->getTrueBlock()->getAsTyped(); + if (typedNode) { + typedNode->propagatePrecision(newPrecision); + typedNode = selectionNode->getFalseBlock()->getAsTyped(); + if (typedNode) + typedNode->propagatePrecision(newPrecision); + } + + return; + } +} + +TIntermTyped* TIntermediate::promoteConstantUnion(TBasicType promoteTo, TIntermConstantUnion* node) const +{ + const TConstUnionArray& rightUnionArray = node->getConstArray(); + int size = node->getType().computeNumComponents(); + + TConstUnionArray leftUnionArray(size); + + for (int i=0; i < size; i++) { + +#define PROMOTE(Set, CType, Get) leftUnionArray[i].Set(static_cast(rightUnionArray[i].Get())) +#define PROMOTE_TO_BOOL(Get) leftUnionArray[i].setBConst(rightUnionArray[i].Get() != 0) + +#ifdef GLSLANG_WEB +#define TO_ALL(Get) \ + switch (promoteTo) { \ + case EbtFloat: PROMOTE(setDConst, double, Get); break; \ + case EbtInt: PROMOTE(setIConst, int, Get); break; \ + case EbtUint: PROMOTE(setUConst, unsigned int, Get); break; \ + case EbtBool: PROMOTE_TO_BOOL(Get); break; \ + default: return node; \ + } +#else +#define TO_ALL(Get) \ + switch (promoteTo) { \ + case EbtFloat16: PROMOTE(setDConst, double, Get); break; \ + case EbtFloat: PROMOTE(setDConst, double, Get); break; \ + case EbtDouble: PROMOTE(setDConst, double, Get); break; \ + case EbtInt8: PROMOTE(setI8Const, char, Get); break; \ + case EbtInt16: PROMOTE(setI16Const, short, Get); break; \ + case EbtInt: PROMOTE(setIConst, int, Get); break; \ + case EbtInt64: PROMOTE(setI64Const, long long, Get); break; \ + case EbtUint8: PROMOTE(setU8Const, unsigned char, Get); break; \ + case EbtUint16: PROMOTE(setU16Const, unsigned short, Get); break; \ + case EbtUint: PROMOTE(setUConst, unsigned int, Get); break; \ + case EbtUint64: PROMOTE(setU64Const, unsigned long long, Get); break; \ + case EbtBool: PROMOTE_TO_BOOL(Get); break; \ + default: return node; \ + } +#endif + + switch (node->getType().getBasicType()) { + case EbtFloat: TO_ALL(getDConst); break; + case EbtInt: TO_ALL(getIConst); break; + case EbtUint: TO_ALL(getUConst); break; + case EbtBool: TO_ALL(getBConst); break; +#ifndef GLSLANG_WEB + case EbtFloat16: TO_ALL(getDConst); break; + case EbtDouble: TO_ALL(getDConst); break; + case EbtInt8: TO_ALL(getI8Const); break; + case EbtInt16: TO_ALL(getI16Const); break; + case EbtInt64: TO_ALL(getI64Const); break; + case EbtUint8: TO_ALL(getU8Const); break; + case EbtUint16: TO_ALL(getU16Const); break; + case EbtUint64: TO_ALL(getU64Const); break; +#endif + default: return node; + } + } + + const TType& t = node->getType(); + + return addConstantUnion(leftUnionArray, TType(promoteTo, t.getQualifier().storage, t.getVectorSize(), t.getMatrixCols(), t.getMatrixRows()), + node->getLoc()); +} + +void TIntermAggregate::setPragmaTable(const TPragmaTable& pTable) +{ + assert(pragmaTable == nullptr); + pragmaTable = new TPragmaTable; + *pragmaTable = pTable; +} + +// If either node is a specialization constant, while the other is +// a constant (or specialization constant), the result is still +// a specialization constant. +bool TIntermediate::specConstantPropagates(const TIntermTyped& node1, const TIntermTyped& node2) +{ + return (node1.getType().getQualifier().isSpecConstant() && node2.getType().getQualifier().isConstant()) || + (node2.getType().getQualifier().isSpecConstant() && node1.getType().getQualifier().isConstant()); +} + +struct TextureUpgradeAndSamplerRemovalTransform : public TIntermTraverser { + void visitSymbol(TIntermSymbol* symbol) override { + if (symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isTexture()) { + symbol->getWritableType().getSampler().setCombined(true); + } + } + bool visitAggregate(TVisit, TIntermAggregate* ag) override { + using namespace std; + TIntermSequence& seq = ag->getSequence(); + TQualifierList& qual = ag->getQualifierList(); + + // qual and seq are indexed using the same indices, so we have to modify both in lock-step + assert(seq.size() == qual.size() || qual.empty()); + + size_t write = 0; + for (size_t i = 0; i < seq.size(); ++i) { + TIntermSymbol* symbol = seq[i]->getAsSymbolNode(); + if (symbol && symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isPureSampler()) { + // remove pure sampler variables + continue; + } + + TIntermNode* result = seq[i]; + + // replace constructors with sampler/textures + TIntermAggregate *constructor = seq[i]->getAsAggregate(); + if (constructor && constructor->getOp() == EOpConstructTextureSampler) { + if (!constructor->getSequence().empty()) + result = constructor->getSequence()[0]; + } + + // write new node & qualifier + seq[write] = result; + if (!qual.empty()) + qual[write] = qual[i]; + write++; + } + + seq.resize(write); + if (!qual.empty()) + qual.resize(write); + + return true; + } +}; + +void TIntermediate::performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root) +{ + TextureUpgradeAndSamplerRemovalTransform transform; + root->traverse(&transform); +} + +const char* TIntermediate::getResourceName(TResourceType res) +{ + switch (res) { + case EResSampler: return "shift-sampler-binding"; + case EResTexture: return "shift-texture-binding"; + case EResImage: return "shift-image-binding"; + case EResUbo: return "shift-UBO-binding"; + case EResSsbo: return "shift-ssbo-binding"; + case EResUav: return "shift-uav-binding"; + default: + assert(0); // internal error: should only be called with valid resource types. + return nullptr; + } +} + + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/LiveTraverser.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/LiveTraverser.h new file mode 100644 index 000000000..7333bc964 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/LiveTraverser.h @@ -0,0 +1,138 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +#include "../Include/Common.h" +#include "reflection.h" +#include "localintermediate.h" + +#include "gl_types.h" + +#include +#include + +namespace glslang { + +// +// The traverser: mostly pass through, except +// - processing function-call nodes to push live functions onto the stack of functions to process +// - processing selection nodes to trim semantically dead code +// +// This is in the glslang namespace directly so it can be a friend of TReflection. +// This can be derived from to implement reflection database traversers or +// binding mappers: anything that wants to traverse the live subset of the tree. +// + +class TLiveTraverser : public TIntermTraverser { +public: + TLiveTraverser(const TIntermediate& i, bool traverseAll = false, + bool preVisit = true, bool inVisit = false, bool postVisit = false) : + TIntermTraverser(preVisit, inVisit, postVisit), + intermediate(i), traverseAll(traverseAll) + { } + + // + // Given a function name, find its subroot in the tree, and push it onto the stack of + // functions left to process. + // + void pushFunction(const TString& name) + { + TIntermSequence& globals = intermediate.getTreeRoot()->getAsAggregate()->getSequence(); + for (unsigned int f = 0; f < globals.size(); ++f) { + TIntermAggregate* candidate = globals[f]->getAsAggregate(); + if (candidate && candidate->getOp() == EOpFunction && candidate->getName() == name) { + functions.push_back(candidate); + break; + } + } + } + + typedef std::list TFunctionStack; + TFunctionStack functions; + +protected: + // To catch which function calls are not dead, and hence which functions must be visited. + virtual bool visitAggregate(TVisit, TIntermAggregate* node) + { + if (!traverseAll) + if (node->getOp() == EOpFunctionCall) + addFunctionCall(node); + + return true; // traverse this subtree + } + + // To prune semantically dead paths. + virtual bool visitSelection(TVisit /* visit */, TIntermSelection* node) + { + if (traverseAll) + return true; // traverse all code + + TIntermConstantUnion* constant = node->getCondition()->getAsConstantUnion(); + if (constant) { + // cull the path that is dead + if (constant->getConstArray()[0].getBConst() == true && node->getTrueBlock()) + node->getTrueBlock()->traverse(this); + if (constant->getConstArray()[0].getBConst() == false && node->getFalseBlock()) + node->getFalseBlock()->traverse(this); + + return false; // don't traverse any more, we did it all above + } else + return true; // traverse the whole subtree + } + + // Track live functions as well as uniforms, so that we don't visit dead functions + // and only visit each function once. + void addFunctionCall(TIntermAggregate* call) + { + // // just use the map to ensure we process each function at most once + if (liveFunctions.find(call->getName()) == liveFunctions.end()) { + liveFunctions.insert(call->getName()); + pushFunction(call->getName()); + } + } + + const TIntermediate& intermediate; + typedef std::unordered_set TLiveFunctions; + TLiveFunctions liveFunctions; + bool traverseAll; + +private: + // prevent copy & copy construct + TLiveTraverser(TLiveTraverser&); + TLiveTraverser& operator=(TLiveTraverser&); +}; + +} // namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ParseContextBase.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ParseContextBase.cpp new file mode 100644 index 000000000..b46400914 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ParseContextBase.cpp @@ -0,0 +1,641 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// Implement the TParseContextBase class. + +#include + +#include "ParseHelper.h" + +extern int yyparse(glslang::TParseContext*); + +namespace glslang { + +// +// Used to output syntax, parsing, and semantic errors. +// + +void TParseContextBase::outputMessage(const TSourceLoc& loc, const char* szReason, + const char* szToken, + const char* szExtraInfoFormat, + TPrefixType prefix, va_list args) +{ + const int maxSize = MaxTokenLength + 200; + char szExtraInfo[maxSize]; + + safe_vsprintf(szExtraInfo, maxSize, szExtraInfoFormat, args); + + infoSink.info.prefix(prefix); + infoSink.info.location(loc); + infoSink.info << "'" << szToken << "' : " << szReason << " " << szExtraInfo << "\n"; + + if (prefix == EPrefixError) { + ++numErrors; + } +} + +#if !defined(GLSLANG_WEB) || defined(GLSLANG_WEB_DEVEL) + +void C_DECL TParseContextBase::error(const TSourceLoc& loc, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) +{ + if (messages & EShMsgOnlyPreprocessor) + return; + va_list args; + va_start(args, szExtraInfoFormat); + outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args); + va_end(args); + + if ((messages & EShMsgCascadingErrors) == 0) + currentScanner->setEndOfInput(); +} + +void C_DECL TParseContextBase::warn(const TSourceLoc& loc, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) +{ + if (suppressWarnings()) + return; + va_list args; + va_start(args, szExtraInfoFormat); + outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args); + va_end(args); +} + +void C_DECL TParseContextBase::ppError(const TSourceLoc& loc, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) +{ + va_list args; + va_start(args, szExtraInfoFormat); + outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args); + va_end(args); + + if ((messages & EShMsgCascadingErrors) == 0) + currentScanner->setEndOfInput(); +} + +void C_DECL TParseContextBase::ppWarn(const TSourceLoc& loc, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) +{ + va_list args; + va_start(args, szExtraInfoFormat); + outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args); + va_end(args); +} + +#endif + +// +// Both test and if necessary, spit out an error, to see if the node is really +// an l-value that can be operated on this way. +// +// Returns true if there was an error. +// +bool TParseContextBase::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + TIntermBinary* binaryNode = node->getAsBinaryNode(); + + if (binaryNode) { + switch(binaryNode->getOp()) { + case EOpIndexDirect: + case EOpIndexIndirect: // fall through + case EOpIndexDirectStruct: // fall through + case EOpVectorSwizzle: + case EOpMatrixSwizzle: + return lValueErrorCheck(loc, op, binaryNode->getLeft()); + default: + break; + } + error(loc, " l-value required", op, "", ""); + + return true; + } + + const char* symbol = nullptr; + TIntermSymbol* symNode = node->getAsSymbolNode(); + if (symNode != nullptr) + symbol = symNode->getName().c_str(); + + const char* message = nullptr; + switch (node->getQualifier().storage) { + case EvqConst: message = "can't modify a const"; break; + case EvqConstReadOnly: message = "can't modify a const"; break; + case EvqUniform: message = "can't modify a uniform"; break; +#ifndef GLSLANG_WEB + case EvqBuffer: + if (node->getQualifier().isReadOnly()) + message = "can't modify a readonly buffer"; + if (node->getQualifier().isShaderRecord()) + message = "can't modify a shaderrecordnv qualified buffer"; + break; + case EvqHitAttr: + if (language != EShLangIntersect) + message = "cannot modify hitAttributeNV in this stage"; + break; +#endif + + default: + // + // Type that can't be written to? + // + switch (node->getBasicType()) { + case EbtSampler: + message = "can't modify a sampler"; + break; + case EbtVoid: + message = "can't modify void"; + break; +#ifndef GLSLANG_WEB + case EbtAtomicUint: + message = "can't modify an atomic_uint"; + break; + case EbtAccStruct: + message = "can't modify accelerationStructureNV"; + break; + case EbtRayQuery: + message = "can't modify rayQueryEXT"; + break; +#endif + default: + break; + } + } + + if (message == nullptr && binaryNode == nullptr && symNode == nullptr) { + error(loc, " l-value required", op, "", ""); + + return true; + } + + // + // Everything else is okay, no error. + // + if (message == nullptr) + return false; + + // + // If we get here, we have an error and a message. + // + if (symNode) + error(loc, " l-value required", op, "\"%s\" (%s)", symbol, message); + else + error(loc, " l-value required", op, "(%s)", message); + + return true; +} + +// Test for and give an error if the node can't be read from. +void TParseContextBase::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + if (! node) + return; + + TIntermBinary* binaryNode = node->getAsBinaryNode(); + if (binaryNode) { + switch(binaryNode->getOp()) { + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + case EOpVectorSwizzle: + case EOpMatrixSwizzle: + rValueErrorCheck(loc, op, binaryNode->getLeft()); + default: + break; + } + + return; + } + + TIntermSymbol* symNode = node->getAsSymbolNode(); + if (symNode && symNode->getQualifier().isWriteOnly()) + error(loc, "can't read from writeonly object: ", op, symNode->getName().c_str()); +} + +// Add 'symbol' to the list of deferred linkage symbols, which +// are later processed in finish(), at which point the symbol +// must still be valid. +// It is okay if the symbol's type will be subsequently edited; +// the modifications will be tracked. +// Order is preserved, to avoid creating novel forward references. +void TParseContextBase::trackLinkage(TSymbol& symbol) +{ + if (!parsingBuiltins) + linkageSymbols.push_back(&symbol); +} + +// Ensure index is in bounds, correct if necessary. +// Give an error if not. +void TParseContextBase::checkIndex(const TSourceLoc& loc, const TType& type, int& index) +{ + const auto sizeIsSpecializationExpression = [&type]() { + return type.containsSpecializationSize() && + type.getArraySizes()->getOuterNode() != nullptr && + type.getArraySizes()->getOuterNode()->getAsSymbolNode() == nullptr; }; + + if (index < 0) { + error(loc, "", "[", "index out of range '%d'", index); + index = 0; + } else if (type.isArray()) { + if (type.isSizedArray() && !sizeIsSpecializationExpression() && + index >= type.getOuterArraySize()) { + error(loc, "", "[", "array index out of range '%d'", index); + index = type.getOuterArraySize() - 1; + } + } else if (type.isVector()) { + if (index >= type.getVectorSize()) { + error(loc, "", "[", "vector index out of range '%d'", index); + index = type.getVectorSize() - 1; + } + } else if (type.isMatrix()) { + if (index >= type.getMatrixCols()) { + error(loc, "", "[", "matrix index out of range '%d'", index); + index = type.getMatrixCols() - 1; + } + } +} + +// Make a shared symbol have a non-shared version that can be edited by the current +// compile, such that editing its type will not change the shared version and will +// effect all nodes already sharing it (non-shallow type), +// or adopting its full type after being edited (shallow type). +void TParseContextBase::makeEditable(TSymbol*& symbol) +{ + // copyUp() does a deep copy of the type. + symbol = symbolTable.copyUp(symbol); + + // Save it (deferred, so it can be edited first) in the AST for linker use. + if (symbol) + trackLinkage(*symbol); +} + +// Return a writable version of the variable 'name'. +// +// Return nullptr if 'name' is not found. This should mean +// something is seriously wrong (e.g., compiler asking self for +// built-in that doesn't exist). +TVariable* TParseContextBase::getEditableVariable(const char* name) +{ + bool builtIn; + TSymbol* symbol = symbolTable.find(name, &builtIn); + + assert(symbol != nullptr); + if (symbol == nullptr) + return nullptr; + + if (builtIn) + makeEditable(symbol); + + return symbol->getAsVariable(); +} + +// Select the best matching function for 'call' from 'candidateList'. +// +// Assumptions +// +// There is no exact match, so a selection algorithm needs to run. That is, the +// language-specific handler should check for exact match first, to +// decide what to do, before calling this selector. +// +// Input +// +// * list of candidate signatures to select from +// * the call +// * a predicate function convertible(from, to) that says whether or not type +// 'from' can implicitly convert to type 'to' (it includes the case of what +// the calling language would consider a matching type with no conversion +// needed) +// * a predicate function better(from1, from2, to1, to2) that says whether or +// not a conversion from <-> to2 is considered better than a conversion +// from <-> to1 (both in and out directions need testing, as declared by the +// formal parameter) +// +// Output +// +// * best matching candidate (or none, if no viable candidates found) +// * whether there was a tie for the best match (ambiguous overload selection, +// caller's choice for how to report) +// +const TFunction* TParseContextBase::selectFunction( + const TVector candidateList, + const TFunction& call, + std::function convertible, + std::function better, + /* output */ bool& tie) +{ +// +// Operation +// +// 1. Prune the input list of candidates down to a list of viable candidates, +// where each viable candidate has +// +// * at least as many parameters as there are calling arguments, with any +// remaining parameters being optional or having default values +// * each parameter is true under convertible(A, B), where A is the calling +// type for in and B is the formal type, and in addition, for out B is the +// calling type and A is the formal type +// +// 2. If there are no viable candidates, return with no match. +// +// 3. If there is only one viable candidate, it is the best match. +// +// 4. If there are multiple viable candidates, select the first viable candidate +// as the incumbent. Compare the incumbent to the next viable candidate, and if +// that candidate is better (bullets below), make it the incumbent. Repeat, with +// a linear walk through the viable candidate list. The final incumbent will be +// returned as the best match. A viable candidate is better than the incumbent if +// +// * it has a function argument with a better(...) conversion than the incumbent, +// for all directions needed by in and out +// * the incumbent has no argument with a better(...) conversion then the +// candidate, for either in or out (as needed) +// +// 5. Check for ambiguity by comparing the best match against all other viable +// candidates. If any other viable candidate has a function argument with a +// better(...) conversion than the best candidate (for either in or out +// directions), return that there was a tie for best. +// + + tie = false; + + // 1. prune to viable... + TVector viableCandidates; + for (auto it = candidateList.begin(); it != candidateList.end(); ++it) { + const TFunction& candidate = *(*it); + + // to even be a potential match, number of arguments must be >= the number of + // fixed (non-default) parameters, and <= the total (including parameter with defaults). + if (call.getParamCount() < candidate.getFixedParamCount() || + call.getParamCount() > candidate.getParamCount()) + continue; + + // see if arguments are convertible + bool viable = true; + + // The call can have fewer parameters than the candidate, if some have defaults. + const int paramCount = std::min(call.getParamCount(), candidate.getParamCount()); + for (int param = 0; param < paramCount; ++param) { + if (candidate[param].type->getQualifier().isParamInput()) { + if (! convertible(*call[param].type, *candidate[param].type, candidate.getBuiltInOp(), param)) { + viable = false; + break; + } + } + if (candidate[param].type->getQualifier().isParamOutput()) { + if (! convertible(*candidate[param].type, *call[param].type, candidate.getBuiltInOp(), param)) { + viable = false; + break; + } + } + } + + if (viable) + viableCandidates.push_back(&candidate); + } + + // 2. none viable... + if (viableCandidates.size() == 0) + return nullptr; + + // 3. only one viable... + if (viableCandidates.size() == 1) + return viableCandidates.front(); + + // 4. find best... + const auto betterParam = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool { + // is call -> can2 better than call -> can1 for any parameter + bool hasBetterParam = false; + for (int param = 0; param < call.getParamCount(); ++param) { + if (better(*call[param].type, *can1[param].type, *can2[param].type)) { + hasBetterParam = true; + break; + } + } + return hasBetterParam; + }; + + const auto equivalentParams = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool { + // is call -> can2 equivalent to call -> can1 for all the call parameters? + for (int param = 0; param < call.getParamCount(); ++param) { + if (better(*call[param].type, *can1[param].type, *can2[param].type) || + better(*call[param].type, *can2[param].type, *can1[param].type)) + return false; + } + return true; + }; + + const TFunction* incumbent = viableCandidates.front(); + for (auto it = viableCandidates.begin() + 1; it != viableCandidates.end(); ++it) { + const TFunction& candidate = *(*it); + if (betterParam(*incumbent, candidate) && ! betterParam(candidate, *incumbent)) + incumbent = &candidate; + } + + // 5. ambiguity... + for (auto it = viableCandidates.begin(); it != viableCandidates.end(); ++it) { + if (incumbent == *it) + continue; + const TFunction& candidate = *(*it); + + // In the case of default parameters, it may have an identical initial set, which is + // also ambiguous + if (betterParam(*incumbent, candidate) || equivalentParams(*incumbent, candidate)) + tie = true; + } + + return incumbent; +} + +// +// Look at a '.' field selector string and change it into numerical selectors +// for a vector or scalar. +// +// Always return some form of swizzle, so the result is always usable. +// +void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TString& compString, int vecSize, + TSwizzleSelectors& selector) +{ + // Too long? + if (compString.size() > MaxSwizzleSelectors) + error(loc, "vector swizzle too long", compString.c_str(), ""); + + // Use this to test that all swizzle characters are from the same swizzle-namespace-set + enum { + exyzw, + ergba, + estpq, + } fieldSet[MaxSwizzleSelectors]; + + // Decode the swizzle string. + int size = std::min(MaxSwizzleSelectors, (int)compString.size()); + for (int i = 0; i < size; ++i) { + switch (compString[i]) { + case 'x': + selector.push_back(0); + fieldSet[i] = exyzw; + break; + case 'r': + selector.push_back(0); + fieldSet[i] = ergba; + break; + case 's': + selector.push_back(0); + fieldSet[i] = estpq; + break; + + case 'y': + selector.push_back(1); + fieldSet[i] = exyzw; + break; + case 'g': + selector.push_back(1); + fieldSet[i] = ergba; + break; + case 't': + selector.push_back(1); + fieldSet[i] = estpq; + break; + + case 'z': + selector.push_back(2); + fieldSet[i] = exyzw; + break; + case 'b': + selector.push_back(2); + fieldSet[i] = ergba; + break; + case 'p': + selector.push_back(2); + fieldSet[i] = estpq; + break; + + case 'w': + selector.push_back(3); + fieldSet[i] = exyzw; + break; + case 'a': + selector.push_back(3); + fieldSet[i] = ergba; + break; + case 'q': + selector.push_back(3); + fieldSet[i] = estpq; + break; + + default: + error(loc, "unknown swizzle selection", compString.c_str(), ""); + break; + } + } + + // Additional error checking. + for (int i = 0; i < selector.size(); ++i) { + if (selector[i] >= vecSize) { + error(loc, "vector swizzle selection out of range", compString.c_str(), ""); + selector.resize(i); + break; + } + + if (i > 0 && fieldSet[i] != fieldSet[i-1]) { + error(loc, "vector swizzle selectors not from the same set", compString.c_str(), ""); + selector.resize(i); + break; + } + } + + // Ensure it is valid. + if (selector.size() == 0) + selector.push_back(0); +} + +#ifdef ENABLE_HLSL +// +// Make the passed-in variable information become a member of the +// global uniform block. If this doesn't exist yet, make it. +// +void TParseContextBase::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) +{ + // Make the global block, if not yet made. + if (globalUniformBlock == nullptr) { + TQualifier blockQualifier; + blockQualifier.clear(); + blockQualifier.storage = EvqUniform; + TType blockType(new TTypeList, *NewPoolTString(getGlobalUniformBlockName()), blockQualifier); + setUniformBlockDefaults(blockType); + globalUniformBlock = new TVariable(NewPoolTString(""), blockType, true); + firstNewMember = 0; + } + + // Update with binding and set + globalUniformBlock->getWritableType().getQualifier().layoutBinding = globalUniformBinding; + globalUniformBlock->getWritableType().getQualifier().layoutSet = globalUniformSet; + + // Add the requested member as a member to the global block. + TType* type = new TType; + type->shallowCopy(memberType); + type->setFieldName(memberName); + if (typeList) + type->setStruct(typeList); + TTypeLoc typeLoc = {type, loc}; + globalUniformBlock->getType().getWritableStruct()->push_back(typeLoc); + + // Insert into the symbol table. + if (firstNewMember == 0) { + // This is the first request; we need a normal symbol table insert + if (symbolTable.insert(*globalUniformBlock)) + trackLinkage(*globalUniformBlock); + else + error(loc, "failed to insert the global constant buffer", "uniform", ""); + } else { + // This is a follow-on request; we need to amend the first insert + symbolTable.amend(*globalUniformBlock, firstNewMember); + } + + ++firstNewMember; +} +#endif + +void TParseContextBase::finish() +{ + if (parsingBuiltins) + return; + + // Transfer the linkage symbols to AST nodes, preserving order. + TIntermAggregate* linkage = new TIntermAggregate; + for (auto i = linkageSymbols.begin(); i != linkageSymbols.end(); ++i) + intermediate.addSymbolLinkageNode(linkage, **i); + intermediate.addSymbolLinkageNodes(linkage, getLanguage(), symbolTable); +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ParseHelper.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ParseHelper.cpp new file mode 100644 index 000000000..b29a310f0 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ParseHelper.cpp @@ -0,0 +1,8442 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2015 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// Copyright (C) 2017, 2019 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "ParseHelper.h" +#include "Scan.h" + +#include "../OSDependent/osinclude.h" +#include + +#include "preprocessor/PpContext.h" + +extern int yyparse(glslang::TParseContext*); + +namespace glslang { + +TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, + int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, + TInfoSink& infoSink, bool forwardCompatible, EShMessages messages, + const TString* entryPoint) : + TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, + infoSink, forwardCompatible, messages, entryPoint), + inMain(false), + blockName(nullptr), + limits(resources.limits) +#ifndef GLSLANG_WEB + , + atomicUintOffsets(nullptr), anyIndexLimits(false) +#endif +{ + // decide whether precision qualifiers should be ignored or respected + if (isEsProfile() || spvVersion.vulkan > 0) { + precisionManager.respectPrecisionQualifiers(); + if (! parsingBuiltins && language == EShLangFragment && !isEsProfile() && spvVersion.vulkan > 0) + precisionManager.warnAboutDefaults(); + } + + setPrecisionDefaults(); + + globalUniformDefaults.clear(); + globalUniformDefaults.layoutMatrix = ElmColumnMajor; + globalUniformDefaults.layoutPacking = spvVersion.spv != 0 ? ElpStd140 : ElpShared; + + globalBufferDefaults.clear(); + globalBufferDefaults.layoutMatrix = ElmColumnMajor; + globalBufferDefaults.layoutPacking = spvVersion.spv != 0 ? ElpStd430 : ElpShared; + + // use storage buffer on SPIR-V 1.3 and up + if (spvVersion.spv >= EShTargetSpv_1_3) + intermediate.setUseStorageBuffer(); + + globalInputDefaults.clear(); + globalOutputDefaults.clear(); + +#ifndef GLSLANG_WEB + // "Shaders in the transform + // feedback capturing mode have an initial global default of + // layout(xfb_buffer = 0) out;" + if (language == EShLangVertex || + language == EShLangTessControl || + language == EShLangTessEvaluation || + language == EShLangGeometry) + globalOutputDefaults.layoutXfbBuffer = 0; + + if (language == EShLangGeometry) + globalOutputDefaults.layoutStream = 0; +#endif + + if (entryPoint != nullptr && entryPoint->size() > 0 && *entryPoint != "main") + infoSink.info.message(EPrefixError, "Source entry point must be \"main\""); +} + +TParseContext::~TParseContext() +{ +#ifndef GLSLANG_WEB + delete [] atomicUintOffsets; +#endif +} + +// Set up all default precisions as needed by the current environment. +// Intended just as a TParseContext constructor helper. +void TParseContext::setPrecisionDefaults() +{ + // Set all precision defaults to EpqNone, which is correct for all types + // when not obeying precision qualifiers, and correct for types that don't + // have defaults (thus getting an error on use) when obeying precision + // qualifiers. + + for (int type = 0; type < EbtNumTypes; ++type) + defaultPrecision[type] = EpqNone; + + for (int type = 0; type < maxSamplerIndex; ++type) + defaultSamplerPrecision[type] = EpqNone; + + // replace with real precision defaults for those that have them + if (obeyPrecisionQualifiers()) { + if (isEsProfile()) { + // Most don't have defaults, a few default to lowp. + TSampler sampler; + sampler.set(EbtFloat, Esd2D); + defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; + sampler.set(EbtFloat, EsdCube); + defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; + sampler.set(EbtFloat, Esd2D); + sampler.setExternal(true); + defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; + } + + if (profile == EEsProfile) { + // Most don't have defaults, a few default to lowp. + TSampler sampler; + sampler.set(EbtFloat, Esd2D); + defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; + sampler.set(EbtFloat, EsdCube); + defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; + sampler.set(EbtFloat, Esd2D); + sampler.video = true; + defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; + } + + // If we are parsing built-in computational variables/functions, it is meaningful to record + // whether the built-in has no precision qualifier, as that ambiguity + // is used to resolve the precision from the supplied arguments/operands instead. + // So, we don't actually want to replace EpqNone with a default precision for built-ins. + if (! parsingBuiltins) { + if (isEsProfile() && language == EShLangFragment) { + defaultPrecision[EbtInt] = EpqMedium; + defaultPrecision[EbtUint] = EpqMedium; + } else { + defaultPrecision[EbtInt] = EpqHigh; + defaultPrecision[EbtUint] = EpqHigh; + defaultPrecision[EbtFloat] = EpqHigh; + } + + if (!isEsProfile()) { + // Non-ES profile + // All sampler precisions default to highp. + for (int type = 0; type < maxSamplerIndex; ++type) + defaultSamplerPrecision[type] = EpqHigh; + } + } + + defaultPrecision[EbtSampler] = EpqLow; + defaultPrecision[EbtAtomicUint] = EpqHigh; + } +} + +void TParseContext::setLimits(const TBuiltInResource& r) +{ + resources = r; + intermediate.setLimits(r); + +#ifndef GLSLANG_WEB + anyIndexLimits = ! limits.generalAttributeMatrixVectorIndexing || + ! limits.generalConstantMatrixVectorIndexing || + ! limits.generalSamplerIndexing || + ! limits.generalUniformIndexing || + ! limits.generalVariableIndexing || + ! limits.generalVaryingIndexing; + + + // "Each binding point tracks its own current default offset for + // inheritance of subsequent variables using the same binding. The initial state of compilation is that all + // binding points have an offset of 0." + atomicUintOffsets = new int[resources.maxAtomicCounterBindings]; + for (int b = 0; b < resources.maxAtomicCounterBindings; ++b) + atomicUintOffsets[b] = 0; +#endif +} + +// +// Parse an array of strings using yyparse, going through the +// preprocessor to tokenize the shader strings, then through +// the GLSL scanner. +// +// Returns true for successful acceptance of the shader, false if any errors. +// +bool TParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError) +{ + currentScanner = &input; + ppContext.setInput(input, versionWillBeError); + yyparse(this); + + finish(); + + return numErrors == 0; +} + +// This is called from bison when it has a parse (syntax) error +// Note though that to stop cascading errors, we set EOF, which +// will usually cause a syntax error, so be more accurate that +// compilation is terminating. +void TParseContext::parserError(const char* s) +{ + if (! getScanner()->atEndOfInput() || numErrors == 0) + error(getCurrentLoc(), "", "", s, ""); + else + error(getCurrentLoc(), "compilation terminated", "", ""); +} + +void TParseContext::handlePragma(const TSourceLoc& loc, const TVector& tokens) +{ +#ifndef GLSLANG_WEB + if (pragmaCallback) + pragmaCallback(loc.line, tokens); + + if (tokens.size() == 0) + return; + + if (tokens[0].compare("optimize") == 0) { + if (tokens.size() != 4) { + error(loc, "optimize pragma syntax is incorrect", "#pragma", ""); + return; + } + + if (tokens[1].compare("(") != 0) { + error(loc, "\"(\" expected after 'optimize' keyword", "#pragma", ""); + return; + } + + if (tokens[2].compare("on") == 0) + contextPragma.optimize = true; + else if (tokens[2].compare("off") == 0) + contextPragma.optimize = false; + else { + error(loc, "\"on\" or \"off\" expected after '(' for 'optimize' pragma", "#pragma", ""); + return; + } + + if (tokens[3].compare(")") != 0) { + error(loc, "\")\" expected to end 'optimize' pragma", "#pragma", ""); + return; + } + } else if (tokens[0].compare("debug") == 0) { + if (tokens.size() != 4) { + error(loc, "debug pragma syntax is incorrect", "#pragma", ""); + return; + } + + if (tokens[1].compare("(") != 0) { + error(loc, "\"(\" expected after 'debug' keyword", "#pragma", ""); + return; + } + + if (tokens[2].compare("on") == 0) + contextPragma.debug = true; + else if (tokens[2].compare("off") == 0) + contextPragma.debug = false; + else { + error(loc, "\"on\" or \"off\" expected after '(' for 'debug' pragma", "#pragma", ""); + return; + } + + if (tokens[3].compare(")") != 0) { + error(loc, "\")\" expected to end 'debug' pragma", "#pragma", ""); + return; + } + } else if (spvVersion.spv > 0 && tokens[0].compare("use_storage_buffer") == 0) { + if (tokens.size() != 1) + error(loc, "extra tokens", "#pragma", ""); + intermediate.setUseStorageBuffer(); + } else if (spvVersion.spv > 0 && tokens[0].compare("use_vulkan_memory_model") == 0) { + if (tokens.size() != 1) + error(loc, "extra tokens", "#pragma", ""); + intermediate.setUseVulkanMemoryModel(); + } else if (spvVersion.spv > 0 && tokens[0].compare("use_variable_pointers") == 0) { + if (tokens.size() != 1) + error(loc, "extra tokens", "#pragma", ""); + if (spvVersion.spv < glslang::EShTargetSpv_1_3) + error(loc, "requires SPIR-V 1.3", "#pragma use_variable_pointers", ""); + intermediate.setUseVariablePointers(); + } else if (tokens[0].compare("once") == 0) { + warn(loc, "not implemented", "#pragma once", ""); + } else if (tokens[0].compare("glslang_binary_double_output") == 0) + intermediate.setBinaryDoubleOutput(); +#endif +} + +// +// Handle seeing a variable identifier in the grammar. +// +TIntermTyped* TParseContext::handleVariable(const TSourceLoc& loc, TSymbol* symbol, const TString* string) +{ + TIntermTyped* node = nullptr; + + // Error check for requiring specific extensions present. + if (symbol && symbol->getNumExtensions()) + requireExtensions(loc, symbol->getNumExtensions(), symbol->getExtensions(), symbol->getName().c_str()); + +#ifndef GLSLANG_WEB + if (symbol && symbol->isReadOnly()) { + // All shared things containing an unsized array must be copied up + // on first use, so that all future references will share its array structure, + // so that editing the implicit size will effect all nodes consuming it, + // and so that editing the implicit size won't change the shared one. + // + // If this is a variable or a block, check it and all it contains, but if this + // is a member of an anonymous block, check the whole block, as the whole block + // will need to be copied up if it contains an unsized array. + // + // This check is being done before the block-name check further down, so guard + // for that too. + if (!symbol->getType().isUnusableName()) { + if (symbol->getType().containsUnsizedArray() || + (symbol->getAsAnonMember() && + symbol->getAsAnonMember()->getAnonContainer().getType().containsUnsizedArray())) + makeEditable(symbol); + } + } +#endif + + const TVariable* variable; + const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr; + if (anon) { + // It was a member of an anonymous container. + + // Create a subtree for its dereference. + variable = anon->getAnonContainer().getAsVariable(); + TIntermTyped* container = intermediate.addSymbol(*variable, loc); + TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc); + node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc); + + node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type); + if (node->getType().hiddenMember()) + error(loc, "member of nameless block was not redeclared", string->c_str(), ""); + } else { + // Not a member of an anonymous container. + + // The symbol table search was done in the lexical phase. + // See if it was a variable. + variable = symbol ? symbol->getAsVariable() : nullptr; + if (variable) { + if (variable->getType().isUnusableName()) { + error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), ""); + variable = nullptr; + } + } else { + if (symbol) + error(loc, "variable name expected", string->c_str(), ""); + } + + // Recovery, if it wasn't found or was not a variable. + if (! variable) + variable = new TVariable(string, TType(EbtVoid)); + + if (variable->getType().getQualifier().isFrontEndConstant()) + node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc); + else + node = intermediate.addSymbol(*variable, loc); + } + + if (variable->getType().getQualifier().isIo()) + intermediate.addIoAccessed(*string); + + if (variable->getType().isReference() && + variable->getType().getQualifier().bufferReferenceNeedsVulkanMemoryModel()) { + intermediate.setUseVulkanMemoryModel(); + } + + return node; +} + +// +// Handle seeing a base[index] dereference in the grammar. +// +TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index) +{ + int indexValue = 0; + if (index->getQualifier().isFrontEndConstant()) + indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst(); + + // basic type checks... + variableCheck(base); + + if (! base->isArray() && ! base->isMatrix() && ! base->isVector() && ! base->getType().isCoopMat() && + ! base->isReference()) { + if (base->getAsSymbolNode()) + error(loc, " left of '[' is not of type array, matrix, or vector ", base->getAsSymbolNode()->getName().c_str(), ""); + else + error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", ""); + + // Insert dummy error-recovery result + return intermediate.addConstantUnion(0.0, EbtFloat, loc); + } + + if (!base->isArray() && base->isVector()) { + if (base->getType().contains16BitFloat()) + requireFloat16Arithmetic(loc, "[", "does not operate on types containing float16"); + if (base->getType().contains16BitInt()) + requireInt16Arithmetic(loc, "[", "does not operate on types containing (u)int16"); + if (base->getType().contains8BitInt()) + requireInt8Arithmetic(loc, "[", "does not operate on types containing (u)int8"); + } + + // check for constant folding + if (base->getType().getQualifier().isFrontEndConstant() && index->getQualifier().isFrontEndConstant()) { + // both base and index are front-end constants + checkIndex(loc, base->getType(), indexValue); + return intermediate.foldDereference(base, indexValue, loc); + } + + // at least one of base and index is not a front-end constant variable... + TIntermTyped* result = nullptr; + +#ifndef GLSLANG_WEB + if (base->isReference() && ! base->isArray()) { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference2, "buffer reference indexing"); + if (base->getType().getReferentType()->containsUnsizedArray()) { + error(loc, "cannot index reference to buffer containing an unsized array", "", ""); + result = nullptr; + } else { + result = intermediate.addBinaryMath(EOpAdd, base, index, loc); + if (result != nullptr) + result->setType(base->getType()); + } + if (result == nullptr) { + error(loc, "cannot index buffer reference", "", ""); + result = intermediate.addConstantUnion(0.0, EbtFloat, loc); + } + return result; + } + if (base->getAsSymbolNode() && isIoResizeArray(base->getType())) + handleIoResizeArrayAccess(loc, base); +#endif + + if (index->getQualifier().isFrontEndConstant()) + checkIndex(loc, base->getType(), indexValue); + + if (index->getQualifier().isFrontEndConstant()) { +#ifndef GLSLANG_WEB + if (base->getType().isUnsizedArray()) { + base->getWritableType().updateImplicitArraySize(indexValue + 1); + // For 2D per-view builtin arrays, update the inner dimension size in parent type + if (base->getQualifier().isPerView() && base->getQualifier().builtIn != EbvNone) { + TIntermBinary* binaryNode = base->getAsBinaryNode(); + if (binaryNode) { + TType& leftType = binaryNode->getLeft()->getWritableType(); + TArraySizes& arraySizes = *leftType.getArraySizes(); + assert(arraySizes.getNumDims() == 2); + arraySizes.setDimSize(1, std::max(arraySizes.getDimSize(1), indexValue + 1)); + } + } + } else +#endif + checkIndex(loc, base->getType(), indexValue); + result = intermediate.addIndex(EOpIndexDirect, base, index, loc); + } else { +#ifndef GLSLANG_WEB + if (base->getType().isUnsizedArray()) { + // we have a variable index into an unsized array, which is okay, + // depending on the situation + if (base->getAsSymbolNode() && isIoResizeArray(base->getType())) + error(loc, "", "[", "array must be sized by a redeclaration or layout qualifier before being indexed with a variable"); + else { + // it is okay for a run-time sized array + checkRuntimeSizable(loc, *base); + } + base->getWritableType().setArrayVariablyIndexed(); + } +#endif + if (base->getBasicType() == EbtBlock) { + if (base->getQualifier().storage == EvqBuffer) + requireProfile(base->getLoc(), ~EEsProfile, "variable indexing buffer block array"); + else if (base->getQualifier().storage == EvqUniform) + profileRequires(base->getLoc(), EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, + "variable indexing uniform block array"); + else { + // input/output blocks either don't exist or can't be variably indexed + } + } else if (language == EShLangFragment && base->getQualifier().isPipeOutput()) + requireProfile(base->getLoc(), ~EEsProfile, "variable indexing fragment shader output array"); + else if (base->getBasicType() == EbtSampler && version >= 130) { + const char* explanation = "variable indexing sampler array"; + requireProfile(base->getLoc(), EEsProfile | ECoreProfile | ECompatibilityProfile, explanation); + profileRequires(base->getLoc(), EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, explanation); + profileRequires(base->getLoc(), ECoreProfile | ECompatibilityProfile, 400, nullptr, explanation); + } + + result = intermediate.addIndex(EOpIndexIndirect, base, index, loc); + } + + // Insert valid dereferenced result type + TType newType(base->getType(), 0); + if (base->getType().getQualifier().isConstant() && index->getQualifier().isConstant()) { + newType.getQualifier().storage = EvqConst; + // If base or index is a specialization constant, the result should also be a specialization constant. + if (base->getType().getQualifier().isSpecConstant() || index->getQualifier().isSpecConstant()) { + newType.getQualifier().makeSpecConstant(); + } + } else { + newType.getQualifier().storage = EvqTemporary; + newType.getQualifier().specConstant = false; + } + result->setType(newType); + +#ifndef GLSLANG_WEB + inheritMemoryQualifiers(base->getQualifier(), result->getWritableType().getQualifier()); + + // Propagate nonuniform + if (base->getQualifier().isNonUniform() || index->getQualifier().isNonUniform()) + result->getWritableType().getQualifier().nonUniform = true; + + if (anyIndexLimits) + handleIndexLimits(loc, base, index); +#endif + + return result; +} + +#ifndef GLSLANG_WEB + +// for ES 2.0 (version 100) limitations for almost all index operations except vertex-shader uniforms +void TParseContext::handleIndexLimits(const TSourceLoc& /*loc*/, TIntermTyped* base, TIntermTyped* index) +{ + if ((! limits.generalSamplerIndexing && base->getBasicType() == EbtSampler) || + (! limits.generalUniformIndexing && base->getQualifier().isUniformOrBuffer() && language != EShLangVertex) || + (! limits.generalAttributeMatrixVectorIndexing && base->getQualifier().isPipeInput() && language == EShLangVertex && (base->getType().isMatrix() || base->getType().isVector())) || + (! limits.generalConstantMatrixVectorIndexing && base->getAsConstantUnion()) || + (! limits.generalVariableIndexing && ! base->getType().getQualifier().isUniformOrBuffer() && + ! base->getType().getQualifier().isPipeInput() && + ! base->getType().getQualifier().isPipeOutput() && + ! base->getType().getQualifier().isConstant()) || + (! limits.generalVaryingIndexing && (base->getType().getQualifier().isPipeInput() || + base->getType().getQualifier().isPipeOutput()))) { + // it's too early to know what the inductive variables are, save it for post processing + needsIndexLimitationChecking.push_back(index); + } +} + +// Make a shared symbol have a non-shared version that can be edited by the current +// compile, such that editing its type will not change the shared version and will +// effect all nodes sharing it. +void TParseContext::makeEditable(TSymbol*& symbol) +{ + TParseContextBase::makeEditable(symbol); + + // See if it's tied to IO resizing + if (isIoResizeArray(symbol->getType())) + ioArraySymbolResizeList.push_back(symbol); +} + +// Return true if this is a geometry shader input array or tessellation control output array +// or mesh shader output array. +bool TParseContext::isIoResizeArray(const TType& type) const +{ + return type.isArray() && + ((language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn) || + (language == EShLangTessControl && type.getQualifier().storage == EvqVaryingOut && + ! type.getQualifier().patch) || + (language == EShLangFragment && type.getQualifier().storage == EvqVaryingIn && + type.getQualifier().pervertexNV) || + (language == EShLangMeshNV && type.getQualifier().storage == EvqVaryingOut && + !type.getQualifier().perTaskNV)); +} + +// If an array is not isIoResizeArray() but is an io array, make sure it has the right size +void TParseContext::fixIoArraySize(const TSourceLoc& loc, TType& type) +{ + if (! type.isArray() || type.getQualifier().patch || symbolTable.atBuiltInLevel()) + return; + + assert(! isIoResizeArray(type)); + + if (type.getQualifier().storage != EvqVaryingIn || type.getQualifier().patch) + return; + + if (language == EShLangTessControl || language == EShLangTessEvaluation) { + if (type.getOuterArraySize() != resources.maxPatchVertices) { + if (type.isSizedArray()) + error(loc, "tessellation input array size must be gl_MaxPatchVertices or implicitly sized", "[]", ""); + type.changeOuterArraySize(resources.maxPatchVertices); + } + } +} + +// Issue any errors if the non-array object is missing arrayness WRT +// shader I/O that has array requirements. +// All arrayness checking is handled in array paths, this is for +void TParseContext::ioArrayCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) +{ + if (! type.isArray() && ! symbolTable.atBuiltInLevel()) { + if (type.getQualifier().isArrayedIo(language) && !type.getQualifier().layoutPassthrough) + error(loc, "type must be an array:", type.getStorageQualifierString(), identifier.c_str()); + } +} + +// Handle a dereference of a geometry shader input array or tessellation control output array. +// See ioArraySymbolResizeList comment in ParseHelper.h. +// +void TParseContext::handleIoResizeArrayAccess(const TSourceLoc& /*loc*/, TIntermTyped* base) +{ + TIntermSymbol* symbolNode = base->getAsSymbolNode(); + assert(symbolNode); + if (! symbolNode) + return; + + // fix array size, if it can be fixed and needs to be fixed (will allow variable indexing) + if (symbolNode->getType().isUnsizedArray()) { + int newSize = getIoArrayImplicitSize(symbolNode->getType().getQualifier()); + if (newSize > 0) + symbolNode->getWritableType().changeOuterArraySize(newSize); + } +} + +// If there has been an input primitive declaration (geometry shader) or an output +// number of vertices declaration(tessellation shader), make sure all input array types +// match it in size. Types come either from nodes in the AST or symbols in the +// symbol table. +// +// Types without an array size will be given one. +// Types already having a size that is wrong will get an error. +// +void TParseContext::checkIoArraysConsistency(const TSourceLoc &loc, bool tailOnly) +{ + int requiredSize = 0; + TString featureString; + size_t listSize = ioArraySymbolResizeList.size(); + size_t i = 0; + + // If tailOnly = true, only check the last array symbol in the list. + if (tailOnly) { + i = listSize - 1; + } + for (bool firstIteration = true; i < listSize; ++i) { + TType &type = ioArraySymbolResizeList[i]->getWritableType(); + + // As I/O array sizes don't change, fetch requiredSize only once, + // except for mesh shaders which could have different I/O array sizes based on type qualifiers. + if (firstIteration || (language == EShLangMeshNV)) { + requiredSize = getIoArrayImplicitSize(type.getQualifier(), &featureString); + if (requiredSize == 0) + break; + firstIteration = false; + } + + checkIoArrayConsistency(loc, requiredSize, featureString.c_str(), type, + ioArraySymbolResizeList[i]->getName()); + } +} + +int TParseContext::getIoArrayImplicitSize(const TQualifier &qualifier, TString *featureString) const +{ + int expectedSize = 0; + TString str = "unknown"; + unsigned int maxVertices = intermediate.getVertices() != TQualifier::layoutNotSet ? intermediate.getVertices() : 0; + + if (language == EShLangGeometry) { + expectedSize = TQualifier::mapGeometryToSize(intermediate.getInputPrimitive()); + str = TQualifier::getGeometryString(intermediate.getInputPrimitive()); + } + else if (language == EShLangTessControl) { + expectedSize = maxVertices; + str = "vertices"; + } else if (language == EShLangFragment) { + // Number of vertices for Fragment shader is always three. + expectedSize = 3; + str = "vertices"; + } else if (language == EShLangMeshNV) { + unsigned int maxPrimitives = + intermediate.getPrimitives() != TQualifier::layoutNotSet ? intermediate.getPrimitives() : 0; + if (qualifier.builtIn == EbvPrimitiveIndicesNV) { + expectedSize = maxPrimitives * TQualifier::mapGeometryToSize(intermediate.getOutputPrimitive()); + str = "max_primitives*"; + str += TQualifier::getGeometryString(intermediate.getOutputPrimitive()); + } + else if (qualifier.isPerPrimitive()) { + expectedSize = maxPrimitives; + str = "max_primitives"; + } + else { + expectedSize = maxVertices; + str = "max_vertices"; + } + } + if (featureString) + *featureString = str; + return expectedSize; +} + +void TParseContext::checkIoArrayConsistency(const TSourceLoc& loc, int requiredSize, const char* feature, TType& type, const TString& name) +{ + if (type.isUnsizedArray()) + type.changeOuterArraySize(requiredSize); + else if (type.getOuterArraySize() != requiredSize) { + if (language == EShLangGeometry) + error(loc, "inconsistent input primitive for array size of", feature, name.c_str()); + else if (language == EShLangTessControl) + error(loc, "inconsistent output number of vertices for array size of", feature, name.c_str()); + else if (language == EShLangFragment) { + if (type.getOuterArraySize() > requiredSize) + error(loc, " cannot be greater than 3 for pervertexNV", feature, name.c_str()); + } + else if (language == EShLangMeshNV) + error(loc, "inconsistent output array size of", feature, name.c_str()); + else + assert(0); + } +} + +#endif // GLSLANG_WEB + +// Handle seeing a binary node with a math operation. +// Returns nullptr if not semantically allowed. +TIntermTyped* TParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right) +{ + rValueErrorCheck(loc, str, left->getAsTyped()); + rValueErrorCheck(loc, str, right->getAsTyped()); + + bool allowed = true; + switch (op) { + // TODO: Bring more source language-specific checks up from intermediate.cpp + // to the specific parse helpers for that source language. + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + if (! left->isScalar() || ! right->isScalar()) + allowed = false; + break; + default: + break; + } + + if (((left->getType().contains16BitFloat() || right->getType().contains16BitFloat()) && !float16Arithmetic()) || + ((left->getType().contains16BitInt() || right->getType().contains16BitInt()) && !int16Arithmetic()) || + ((left->getType().contains8BitInt() || right->getType().contains8BitInt()) && !int8Arithmetic())) { + allowed = false; + } + + TIntermTyped* result = nullptr; + if (allowed) + result = intermediate.addBinaryMath(op, left, right, loc); + + if (result == nullptr) + binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString()); + + return result; +} + +// Handle seeing a unary node with a math operation. +TIntermTyped* TParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* childNode) +{ + rValueErrorCheck(loc, str, childNode); + + bool allowed = true; + if ((childNode->getType().contains16BitFloat() && !float16Arithmetic()) || + (childNode->getType().contains16BitInt() && !int16Arithmetic()) || + (childNode->getType().contains8BitInt() && !int8Arithmetic())) { + allowed = false; + } + + TIntermTyped* result = nullptr; + if (allowed) + result = intermediate.addUnaryMath(op, childNode, loc); + + if (result) + return result; + else + unaryOpError(loc, str, childNode->getCompleteString()); + + return childNode; +} + +// +// Handle seeing a base.field dereference in the grammar. +// +TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field) +{ + variableCheck(base); + + // + // .length() can't be resolved until we later see the function-calling syntax. + // Save away the name in the AST for now. Processing is completed in + // handleLengthMethod(). + // + if (field == "length") { + if (base->isArray()) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, ".length"); + profileRequires(loc, EEsProfile, 300, nullptr, ".length"); + } else if (base->isVector() || base->isMatrix()) { + const char* feature = ".length() on vectors and matrices"; + requireProfile(loc, ~EEsProfile, feature); + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, feature); + } else if (!base->getType().isCoopMat()) { + error(loc, "does not operate on this type:", field.c_str(), base->getType().getCompleteString().c_str()); + + return base; + } + + return intermediate.addMethod(base, TType(EbtInt), &field, loc); + } + + // It's not .length() if we get to here. + + if (base->isArray()) { + error(loc, "cannot apply to an array:", ".", field.c_str()); + + return base; + } + + if (base->getType().isCoopMat()) { + error(loc, "cannot apply to a cooperative matrix type:", ".", field.c_str()); + return base; + } + + // It's neither an array nor .length() if we get here, + // leaving swizzles and struct/block dereferences. + + TIntermTyped* result = base; + if ((base->isVector() || base->isScalar()) && + (base->isFloatingDomain() || base->isIntegerDomain() || base->getBasicType() == EbtBool)) { + result = handleDotSwizzle(loc, base, field); + } else if (base->isStruct() || base->isReference()) { + const TTypeList* fields = base->isReference() ? + base->getType().getReferentType()->getStruct() : + base->getType().getStruct(); + bool fieldFound = false; + int member; + for (member = 0; member < (int)fields->size(); ++member) { + if ((*fields)[member].type->getFieldName() == field) { + fieldFound = true; + break; + } + } + if (fieldFound) { + if (base->getType().getQualifier().isFrontEndConstant()) + result = intermediate.foldDereference(base, member, loc); + else { + blockMemberExtensionCheck(loc, base, member, field); + TIntermTyped* index = intermediate.addConstantUnion(member, loc); + result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc); + result->setType(*(*fields)[member].type); + if ((*fields)[member].type->getQualifier().isIo()) + intermediate.addIoAccessed(field); + } + inheritMemoryQualifiers(base->getQualifier(), result->getWritableType().getQualifier()); + } else + error(loc, "no such field in structure", field.c_str(), ""); + } else + error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str()); + + // Propagate noContraction up the dereference chain + if (base->getQualifier().isNoContraction()) + result->getWritableType().getQualifier().setNoContraction(); + + // Propagate nonuniform + if (base->getQualifier().isNonUniform()) + result->getWritableType().getQualifier().nonUniform = true; + + return result; +} + +// +// Handle seeing a base.swizzle, a subset of base.identifier in the grammar. +// +TIntermTyped* TParseContext::handleDotSwizzle(const TSourceLoc& loc, TIntermTyped* base, const TString& field) +{ + TIntermTyped* result = base; + if (base->isScalar()) { + const char* dotFeature = "scalar swizzle"; + requireProfile(loc, ~EEsProfile, dotFeature); + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, dotFeature); + } + + TSwizzleSelectors selectors; + parseSwizzleSelector(loc, field, base->getVectorSize(), selectors); + + if (base->isVector() && selectors.size() != 1 && base->getType().contains16BitFloat()) + requireFloat16Arithmetic(loc, ".", "can't swizzle types containing float16"); + if (base->isVector() && selectors.size() != 1 && base->getType().contains16BitInt()) + requireInt16Arithmetic(loc, ".", "can't swizzle types containing (u)int16"); + if (base->isVector() && selectors.size() != 1 && base->getType().contains8BitInt()) + requireInt8Arithmetic(loc, ".", "can't swizzle types containing (u)int8"); + + if (base->isScalar()) { + if (selectors.size() == 1) + return result; + else { + TType type(base->getBasicType(), EvqTemporary, selectors.size()); + // Swizzle operations propagate specialization-constantness + if (base->getQualifier().isSpecConstant()) + type.getQualifier().makeSpecConstant(); + return addConstructor(loc, base, type); + } + } + + if (base->getType().getQualifier().isFrontEndConstant()) + result = intermediate.foldSwizzle(base, selectors, loc); + else { + if (selectors.size() == 1) { + TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc); + result = intermediate.addIndex(EOpIndexDirect, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision)); + } else { + TIntermTyped* index = intermediate.addSwizzle(selectors, loc); + result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, selectors.size())); + } + // Swizzle operations propagate specialization-constantness + if (base->getType().getQualifier().isSpecConstant()) + result->getWritableType().getQualifier().makeSpecConstant(); + } + + return result; +} + +void TParseContext::blockMemberExtensionCheck(const TSourceLoc& loc, const TIntermTyped* base, int member, const TString& memberName) +{ + // a block that needs extension checking is either 'base', or if arrayed, + // one level removed to the left + const TIntermSymbol* baseSymbol = nullptr; + if (base->getAsBinaryNode() == nullptr) + baseSymbol = base->getAsSymbolNode(); + else + baseSymbol = base->getAsBinaryNode()->getLeft()->getAsSymbolNode(); + if (baseSymbol == nullptr) + return; + const TSymbol* symbol = symbolTable.find(baseSymbol->getName()); + if (symbol == nullptr) + return; + const TVariable* variable = symbol->getAsVariable(); + if (variable == nullptr) + return; + if (!variable->hasMemberExtensions()) + return; + + // We now have a variable that is the base of a dot reference + // with members that need extension checking. + if (variable->getNumMemberExtensions(member) > 0) + requireExtensions(loc, variable->getNumMemberExtensions(member), variable->getMemberExtensions(member), memberName.c_str()); +} + +// +// Handle seeing a function declarator in the grammar. This is the precursor +// to recognizing a function prototype or function definition. +// +TFunction* TParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype) +{ + // ES can't declare prototypes inside functions + if (! symbolTable.atGlobalLevel()) + requireProfile(loc, ~EEsProfile, "local function declaration"); + + // + // Multiple declarations of the same function name are allowed. + // + // If this is a definition, the definition production code will check for redefinitions + // (we don't know at this point if it's a definition or not). + // + // Redeclarations (full signature match) are allowed. But, return types and parameter qualifiers must also match. + // - except ES 100, which only allows a single prototype + // + // ES 100 does not allow redefining, but does allow overloading of built-in functions. + // ES 300 does not allow redefining or overloading of built-in functions. + // + bool builtIn; + TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn); + if (symbol && symbol->getAsFunction() && builtIn) + requireProfile(loc, ~EEsProfile, "redefinition of built-in function"); + const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0; + if (prevDec) { + if (prevDec->isPrototyped() && prototype) + profileRequires(loc, EEsProfile, 300, nullptr, "multiple prototypes for same function"); + if (prevDec->getType() != function.getType()) + error(loc, "overloaded functions must have the same return type", function.getName().c_str(), ""); + for (int i = 0; i < prevDec->getParamCount(); ++i) { + if ((*prevDec)[i].type->getQualifier().storage != function[i].type->getQualifier().storage) + error(loc, "overloaded functions must have the same parameter storage qualifiers for argument", function[i].type->getStorageQualifierString(), "%d", i+1); + + if ((*prevDec)[i].type->getQualifier().precision != function[i].type->getQualifier().precision) + error(loc, "overloaded functions must have the same parameter precision qualifiers for argument", function[i].type->getPrecisionQualifierString(), "%d", i+1); + } + } + + arrayObjectCheck(loc, function.getType(), "array in function return type"); + + if (prototype) { + // All built-in functions are defined, even though they don't have a body. + // Count their prototype as a definition instead. + if (symbolTable.atBuiltInLevel()) + function.setDefined(); + else { + if (prevDec && ! builtIn) + symbol->getAsFunction()->setPrototyped(); // need a writable one, but like having prevDec as a const + function.setPrototyped(); + } + } + + // This insert won't actually insert it if it's a duplicate signature, but it will still check for + // other forms of name collisions. + if (! symbolTable.insert(function)) + error(loc, "function name is redeclaration of existing name", function.getName().c_str(), ""); + + // + // If this is a redeclaration, it could also be a definition, + // in which case, we need to use the parameter names from this one, and not the one that's + // being redeclared. So, pass back this declaration, not the one in the symbol table. + // + return &function; +} + +// +// Handle seeing the function prototype in front of a function definition in the grammar. +// The body is handled after this function returns. +// +TIntermAggregate* TParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function) +{ + currentCaller = function.getMangledName(); + TSymbol* symbol = symbolTable.find(function.getMangledName()); + TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr; + + if (! prevDec) + error(loc, "can't find function", function.getName().c_str(), ""); + // Note: 'prevDec' could be 'function' if this is the first time we've seen function + // as it would have just been put in the symbol table. Otherwise, we're looking up + // an earlier occurrence. + + if (prevDec && prevDec->isDefined()) { + // Then this function already has a body. + error(loc, "function already has a body", function.getName().c_str(), ""); + } + if (prevDec && ! prevDec->isDefined()) { + prevDec->setDefined(); + + // Remember the return type for later checking for RETURN statements. + currentFunctionType = &(prevDec->getType()); + } else + currentFunctionType = new TType(EbtVoid); + functionReturnsValue = false; + + if (function.getName() == "kore") { + function.changeName(NewPoolTString("main")); + } + + // Check for entry point + if (function.getName().compare(intermediate.getEntryPointName().c_str()) == 0) { + intermediate.setEntryPointMangledName(function.getMangledName().c_str()); + intermediate.incrementEntryPointCount(); + inMain = true; + } else + inMain = false; + + // + // Raise error message if main function takes any parameters or returns anything other than void + // + if (inMain) { + if (function.getParamCount() > 0) + error(loc, "function cannot take any parameter(s)", function.getName().c_str(), ""); + if (function.getType().getBasicType() != EbtVoid) + error(loc, "", function.getType().getBasicTypeString().c_str(), "entry point cannot return a value"); + } + + // + // New symbol table scope for body of function plus its arguments + // + symbolTable.push(); + + // + // Insert parameters into the symbol table. + // If the parameter has no name, it's not an error, just don't insert it + // (could be used for unused args). + // + // Also, accumulate the list of parameters into the HIL, so lower level code + // knows where to find parameters. + // + TIntermAggregate* paramNodes = new TIntermAggregate; + for (int i = 0; i < function.getParamCount(); i++) { + TParameter& param = function[i]; + if (param.name != nullptr) { + TVariable *variable = new TVariable(param.name, *param.type); + + // Insert the parameters with name in the symbol table. + if (! symbolTable.insert(*variable)) + error(loc, "redefinition", variable->getName().c_str(), ""); + else { + // Transfer ownership of name pointer to symbol table. + param.name = nullptr; + + // Add the parameter to the HIL + paramNodes = intermediate.growAggregate(paramNodes, + intermediate.addSymbol(*variable, loc), + loc); + } + } else + paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc); + } + intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc); + loopNestingLevel = 0; + statementNestingLevel = 0; + controlFlowNestingLevel = 0; + postEntryPointReturn = false; + + return paramNodes; +} + +// +// Handle seeing function call syntax in the grammar, which could be any of +// - .length() method +// - constructor +// - a call to a built-in function mapped to an operator +// - a call to a built-in function that will remain a function call (e.g., texturing) +// - user function +// - subroutine call (not implemented yet) +// +TIntermTyped* TParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermNode* arguments) +{ + TIntermTyped* result = nullptr; + + if (function->getBuiltInOp() == EOpArrayLength) + result = handleLengthMethod(loc, function, arguments); + else if (function->getBuiltInOp() != EOpNull) { + // + // Then this should be a constructor. + // Don't go through the symbol table for constructors. + // Their parameters will be verified algorithmically. + // + TType type(EbtVoid); // use this to get the type back + if (! constructorError(loc, arguments, *function, function->getBuiltInOp(), type)) { + // + // It's a constructor, of type 'type'. + // + result = addConstructor(loc, arguments, type); + if (result == nullptr) + error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), ""); + } + } else { + // + // Find it in the symbol table. + // + const TFunction* fnCandidate; + bool builtIn; + fnCandidate = findFunction(loc, *function, builtIn); + if (fnCandidate) { + // This is a declared function that might map to + // - a built-in operator, + // - a built-in function not mapped to an operator, or + // - a user function. + + // Error check for a function requiring specific extensions present. + if (builtIn && fnCandidate->getNumExtensions()) + requireExtensions(loc, fnCandidate->getNumExtensions(), fnCandidate->getExtensions(), fnCandidate->getName().c_str()); + + if (builtIn && fnCandidate->getType().contains16BitFloat()) + requireFloat16Arithmetic(loc, "built-in function", "float16 types can only be in uniform block or buffer storage"); + if (builtIn && fnCandidate->getType().contains16BitInt()) + requireInt16Arithmetic(loc, "built-in function", "(u)int16 types can only be in uniform block or buffer storage"); + if (builtIn && fnCandidate->getType().contains8BitInt()) + requireInt8Arithmetic(loc, "built-in function", "(u)int8 types can only be in uniform block or buffer storage"); + + if (arguments != nullptr) { + // Make sure qualifications work for these arguments. + TIntermAggregate* aggregate = arguments->getAsAggregate(); + for (int i = 0; i < fnCandidate->getParamCount(); ++i) { + // At this early point there is a slight ambiguity between whether an aggregate 'arguments' + // is the single argument itself or its children are the arguments. Only one argument + // means take 'arguments' itself as the one argument. + TIntermNode* arg = fnCandidate->getParamCount() == 1 ? arguments : (aggregate ? aggregate->getSequence()[i] : arguments); + TQualifier& formalQualifier = (*fnCandidate)[i].type->getQualifier(); + if (formalQualifier.isParamOutput()) { + if (lValueErrorCheck(arguments->getLoc(), "assign", arg->getAsTyped())) + error(arguments->getLoc(), "Non-L-value cannot be passed for 'out' or 'inout' parameters.", "out", ""); + } + const TType& argType = arg->getAsTyped()->getType(); + const TQualifier& argQualifier = argType.getQualifier(); + if (argQualifier.isMemory() && (argType.containsOpaque() || argType.isReference())) { + const char* message = "argument cannot drop memory qualifier when passed to formal parameter"; +#ifndef GLSLANG_WEB + if (argQualifier.volatil && ! formalQualifier.volatil) + error(arguments->getLoc(), message, "volatile", ""); + if (argQualifier.coherent && ! (formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "coherent", ""); + if (argQualifier.devicecoherent && ! (formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "devicecoherent", ""); + if (argQualifier.queuefamilycoherent && ! (formalQualifier.queuefamilycoherent || formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "queuefamilycoherent", ""); + if (argQualifier.workgroupcoherent && ! (formalQualifier.workgroupcoherent || formalQualifier.queuefamilycoherent || formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "workgroupcoherent", ""); + if (argQualifier.subgroupcoherent && ! (formalQualifier.subgroupcoherent || formalQualifier.workgroupcoherent || formalQualifier.queuefamilycoherent || formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "subgroupcoherent", ""); + if (argQualifier.readonly && ! formalQualifier.readonly) + error(arguments->getLoc(), message, "readonly", ""); + if (argQualifier.writeonly && ! formalQualifier.writeonly) + error(arguments->getLoc(), message, "writeonly", ""); + // Don't check 'restrict', it is different than the rest: + // "...but only restrict can be taken away from a calling argument, by a formal parameter that + // lacks the restrict qualifier..." +#endif + } + if (!builtIn && argQualifier.getFormat() != formalQualifier.getFormat()) { + // we have mismatched formats, which should only be allowed if writeonly + // and at least one format is unknown + if (!formalQualifier.isWriteOnly() || (formalQualifier.getFormat() != ElfNone && + argQualifier.getFormat() != ElfNone)) + error(arguments->getLoc(), "image formats must match", "format", ""); + } + if (builtIn && arg->getAsTyped()->getType().contains16BitFloat()) + requireFloat16Arithmetic(arguments->getLoc(), "built-in function", "float16 types can only be in uniform block or buffer storage"); + if (builtIn && arg->getAsTyped()->getType().contains16BitInt()) + requireInt16Arithmetic(arguments->getLoc(), "built-in function", "(u)int16 types can only be in uniform block or buffer storage"); + if (builtIn && arg->getAsTyped()->getType().contains8BitInt()) + requireInt8Arithmetic(arguments->getLoc(), "built-in function", "(u)int8 types can only be in uniform block or buffer storage"); + + // TODO 4.5 functionality: A shader will fail to compile + // if the value passed to the memargument of an atomic memory function does not correspond to a buffer or + // shared variable. It is acceptable to pass an element of an array or a single component of a vector to the + // memargument of an atomic memory function, as long as the underlying array or vector is a buffer or + // shared variable. + } + + // Convert 'in' arguments + addInputArgumentConversions(*fnCandidate, arguments); // arguments may be modified if it's just a single argument node + } + + if (builtIn && fnCandidate->getBuiltInOp() != EOpNull) { + // A function call mapped to a built-in operation. + result = handleBuiltInFunctionCall(loc, arguments, *fnCandidate); + } else { + // This is a function call not mapped to built-in operator. + // It could still be a built-in function, but only if PureOperatorBuiltins == false. + result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc); + TIntermAggregate* call = result->getAsAggregate(); + call->setName(fnCandidate->getMangledName()); + + // this is how we know whether the given function is a built-in function or a user-defined function + // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also + // if builtIn == true, it's definitely a built-in function with EOpNull + if (! builtIn) { + call->setUserDefined(); + if (symbolTable.atGlobalLevel()) { + requireProfile(loc, ~EEsProfile, "calling user function from global scope"); + intermediate.addToCallGraph(infoSink, "main(", fnCandidate->getMangledName()); + } else + intermediate.addToCallGraph(infoSink, currentCaller, fnCandidate->getMangledName()); + } + +#ifndef GLSLANG_WEB + if (builtIn) + nonOpBuiltInCheck(loc, *fnCandidate, *call); + else +#endif + userFunctionCallCheck(loc, *call); + } + + // Convert 'out' arguments. If it was a constant folded built-in, it won't be an aggregate anymore. + // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output. + // Also, build the qualifier list for user function calls, which are always called with an aggregate. + if (result->getAsAggregate()) { + TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList(); + for (int i = 0; i < fnCandidate->getParamCount(); ++i) { + TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage; + qualifierList.push_back(qual); + } + result = addOutputArgumentConversions(*fnCandidate, *result->getAsAggregate()); + } + + if (result->getAsTyped()->getType().isCoopMat() && + !result->getAsTyped()->getType().isParameterized()) { + assert(fnCandidate->getBuiltInOp() == EOpCooperativeMatrixMulAdd); + + result->setType(result->getAsAggregate()->getSequence()[2]->getAsTyped()->getType()); + } + } + } + + // generic error recovery + // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to reduce cascades + if (result == nullptr) + result = intermediate.addConstantUnion(0.0, EbtFloat, loc); + + return result; +} + +TIntermTyped* TParseContext::handleBuiltInFunctionCall(TSourceLoc loc, TIntermNode* arguments, + const TFunction& function) +{ + checkLocation(loc, function.getBuiltInOp()); + TIntermTyped *result = intermediate.addBuiltInFunctionCall(loc, function.getBuiltInOp(), + function.getParamCount() == 1, + arguments, function.getType()); + if (obeyPrecisionQualifiers()) + computeBuiltinPrecisions(*result, function); + + if (result == nullptr) { + if (arguments == nullptr) + error(loc, " wrong operand type", "Internal Error", + "built in unary operator function. Type: %s", ""); + else + error(arguments->getLoc(), " wrong operand type", "Internal Error", + "built in unary operator function. Type: %s", + static_cast(arguments)->getCompleteString().c_str()); + } else if (result->getAsOperator()) + builtInOpCheck(loc, function, *result->getAsOperator()); + + return result; +} + +// "The operation of a built-in function can have a different precision +// qualification than the precision qualification of the resulting value. +// These two precision qualifications are established as follows. +// +// The precision qualification of the operation of a built-in function is +// based on the precision qualification of its input arguments and formal +// parameters: When a formal parameter specifies a precision qualifier, +// that is used, otherwise, the precision qualification of the calling +// argument is used. The highest precision of these will be the precision +// qualification of the operation of the built-in function. Generally, +// this is applied across all arguments to a built-in function, with the +// exceptions being: +// - bitfieldExtract and bitfieldInsert ignore the 'offset' and 'bits' +// arguments. +// - interpolateAt* functions only look at the 'interpolant' argument. +// +// The precision qualification of the result of a built-in function is +// determined in one of the following ways: +// +// - For the texture sampling, image load, and image store functions, +// the precision of the return type matches the precision of the +// sampler type +// +// Otherwise: +// +// - For prototypes that do not specify a resulting precision qualifier, +// the precision will be the same as the precision of the operation. +// +// - For prototypes that do specify a resulting precision qualifier, +// the specified precision qualifier is the precision qualification of +// the result." +// +void TParseContext::computeBuiltinPrecisions(TIntermTyped& node, const TFunction& function) +{ + TPrecisionQualifier operationPrecision = EpqNone; + TPrecisionQualifier resultPrecision = EpqNone; + + TIntermOperator* opNode = node.getAsOperator(); + if (opNode == nullptr) + return; + + if (TIntermUnary* unaryNode = node.getAsUnaryNode()) { + operationPrecision = std::max(function[0].type->getQualifier().precision, + unaryNode->getOperand()->getType().getQualifier().precision); + if (function.getType().getBasicType() != EbtBool) + resultPrecision = function.getType().getQualifier().precision == EpqNone ? + operationPrecision : + function.getType().getQualifier().precision; + } else if (TIntermAggregate* agg = node.getAsAggregate()) { + TIntermSequence& sequence = agg->getSequence(); + unsigned int numArgs = (unsigned int)sequence.size(); + switch (agg->getOp()) { + case EOpBitfieldExtract: + numArgs = 1; + break; + case EOpBitfieldInsert: + numArgs = 2; + break; + case EOpInterpolateAtCentroid: + case EOpInterpolateAtOffset: + case EOpInterpolateAtSample: + numArgs = 1; + break; + case EOpDebugPrintf: + numArgs = 0; + break; + default: + break; + } + // find the maximum precision from the arguments and parameters + for (unsigned int arg = 0; arg < numArgs; ++arg) { + operationPrecision = std::max(operationPrecision, sequence[arg]->getAsTyped()->getQualifier().precision); + operationPrecision = std::max(operationPrecision, function[arg].type->getQualifier().precision); + } + // compute the result precision + if (agg->isSampling() || + agg->getOp() == EOpImageLoad || agg->getOp() == EOpImageStore || + agg->getOp() == EOpImageLoadLod || agg->getOp() == EOpImageStoreLod) + resultPrecision = sequence[0]->getAsTyped()->getQualifier().precision; + else if (function.getType().getBasicType() != EbtBool) + resultPrecision = function.getType().getQualifier().precision == EpqNone ? + operationPrecision : + function.getType().getQualifier().precision; + } + + // Propagate precision through this node and its children. That algorithm stops + // when a precision is found, so start by clearing this subroot precision + opNode->getQualifier().precision = EpqNone; + if (operationPrecision != EpqNone) { + opNode->propagatePrecision(operationPrecision); + opNode->setOperationPrecision(operationPrecision); + } + // Now, set the result precision, which might not match + opNode->getQualifier().precision = resultPrecision; +} + +TIntermNode* TParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value) +{ +#ifndef GLSLANG_WEB + storage16BitAssignmentCheck(loc, value->getType(), "return"); +#endif + + functionReturnsValue = true; + if (currentFunctionType->getBasicType() == EbtVoid) { + error(loc, "void function cannot return a value", "return", ""); + return intermediate.addBranch(EOpReturn, loc); + } else if (*currentFunctionType != value->getType()) { + TIntermTyped* converted = intermediate.addConversion(EOpReturn, *currentFunctionType, value); + if (converted) { + if (*currentFunctionType != converted->getType()) + error(loc, "cannot convert return value to function return type", "return", ""); + if (version < 420) + warn(loc, "type conversion on return values was not explicitly allowed until version 420", "return", ""); + return intermediate.addBranch(EOpReturn, converted, loc); + } else { + error(loc, "type does not match, or is not convertible to, the function's return type", "return", ""); + return intermediate.addBranch(EOpReturn, value, loc); + } + } else + return intermediate.addBranch(EOpReturn, value, loc); +} + +// See if the operation is being done in an illegal location. +void TParseContext::checkLocation(const TSourceLoc& loc, TOperator op) +{ +#ifndef GLSLANG_WEB + switch (op) { + case EOpBarrier: + if (language == EShLangTessControl) { + if (controlFlowNestingLevel > 0) + error(loc, "tessellation control barrier() cannot be placed within flow control", "", ""); + if (! inMain) + error(loc, "tessellation control barrier() must be in main()", "", ""); + else if (postEntryPointReturn) + error(loc, "tessellation control barrier() cannot be placed after a return from main()", "", ""); + } + break; + case EOpBeginInvocationInterlock: + if (language != EShLangFragment) + error(loc, "beginInvocationInterlockARB() must be in a fragment shader", "", ""); + if (! inMain) + error(loc, "beginInvocationInterlockARB() must be in main()", "", ""); + else if (postEntryPointReturn) + error(loc, "beginInvocationInterlockARB() cannot be placed after a return from main()", "", ""); + if (controlFlowNestingLevel > 0) + error(loc, "beginInvocationInterlockARB() cannot be placed within flow control", "", ""); + + if (beginInvocationInterlockCount > 0) + error(loc, "beginInvocationInterlockARB() must only be called once", "", ""); + if (endInvocationInterlockCount > 0) + error(loc, "beginInvocationInterlockARB() must be called before endInvocationInterlockARB()", "", ""); + + beginInvocationInterlockCount++; + + // default to pixel_interlock_ordered + if (intermediate.getInterlockOrdering() == EioNone) + intermediate.setInterlockOrdering(EioPixelInterlockOrdered); + break; + case EOpEndInvocationInterlock: + if (language != EShLangFragment) + error(loc, "endInvocationInterlockARB() must be in a fragment shader", "", ""); + if (! inMain) + error(loc, "endInvocationInterlockARB() must be in main()", "", ""); + else if (postEntryPointReturn) + error(loc, "endInvocationInterlockARB() cannot be placed after a return from main()", "", ""); + if (controlFlowNestingLevel > 0) + error(loc, "endInvocationInterlockARB() cannot be placed within flow control", "", ""); + + if (endInvocationInterlockCount > 0) + error(loc, "endInvocationInterlockARB() must only be called once", "", ""); + if (beginInvocationInterlockCount == 0) + error(loc, "beginInvocationInterlockARB() must be called before endInvocationInterlockARB()", "", ""); + + endInvocationInterlockCount++; + break; + default: + break; + } +#endif +} + +// Finish processing object.length(). This started earlier in handleDotDereference(), where +// the ".length" part was recognized and semantically checked, and finished here where the +// function syntax "()" is recognized. +// +// Return resulting tree node. +TIntermTyped* TParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction* function, TIntermNode* intermNode) +{ + int length = 0; + + if (function->getParamCount() > 0) + error(loc, "method does not accept any arguments", function->getName().c_str(), ""); + else { + const TType& type = intermNode->getAsTyped()->getType(); + if (type.isArray()) { + if (type.isUnsizedArray()) { +#ifndef GLSLANG_WEB + if (intermNode->getAsSymbolNode() && isIoResizeArray(type)) { + // We could be between a layout declaration that gives a built-in io array implicit size and + // a user redeclaration of that array, meaning we have to substitute its implicit size here + // without actually redeclaring the array. (It is an error to use a member before the + // redeclaration, but not an error to use the array name itself.) + const TString& name = intermNode->getAsSymbolNode()->getName(); + if (name == "gl_in" || name == "gl_out" || name == "gl_MeshVerticesNV" || + name == "gl_MeshPrimitivesNV") { + length = getIoArrayImplicitSize(type.getQualifier()); + } + } +#endif + if (length == 0) { +#ifndef GLSLANG_WEB + if (intermNode->getAsSymbolNode() && isIoResizeArray(type)) + error(loc, "", function->getName().c_str(), "array must first be sized by a redeclaration or layout qualifier"); + else if (isRuntimeLength(*intermNode->getAsTyped())) { + // Create a unary op and let the back end handle it + return intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, intermNode, TType(EbtInt)); + } else +#endif + error(loc, "", function->getName().c_str(), "array must be declared with a size before using this method"); + } + } else if (type.getOuterArrayNode()) { + // If the array's outer size is specified by an intermediate node, it means the array's length + // was specified by a specialization constant. In such a case, we should return the node of the + // specialization constants to represent the length. + return type.getOuterArrayNode(); + } else + length = type.getOuterArraySize(); + } else if (type.isMatrix()) + length = type.getMatrixCols(); + else if (type.isVector()) + length = type.getVectorSize(); + else if (type.isCoopMat()) + return intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, intermNode, TType(EbtInt)); + else { + // we should not get here, because earlier semantic checking should have prevented this path + error(loc, ".length()", "unexpected use of .length()", ""); + } + } + + if (length == 0) + length = 1; + + return intermediate.addConstantUnion(length, loc); +} + +// +// Add any needed implicit conversions for function-call arguments to input parameters. +// +void TParseContext::addInputArgumentConversions(const TFunction& function, TIntermNode*& arguments) const +{ +#ifndef GLSLANG_WEB + TIntermAggregate* aggregate = arguments->getAsAggregate(); + + // Process each argument's conversion + for (int i = 0; i < function.getParamCount(); ++i) { + // At this early point there is a slight ambiguity between whether an aggregate 'arguments' + // is the single argument itself or its children are the arguments. Only one argument + // means take 'arguments' itself as the one argument. + TIntermTyped* arg = function.getParamCount() == 1 ? arguments->getAsTyped() : (aggregate ? aggregate->getSequence()[i]->getAsTyped() : arguments->getAsTyped()); + if (*function[i].type != arg->getType()) { + if (function[i].type->getQualifier().isParamInput() && + !function[i].type->isCoopMat()) { + // In-qualified arguments just need an extra node added above the argument to + // convert to the correct type. + arg = intermediate.addConversion(EOpFunctionCall, *function[i].type, arg); + if (arg) { + if (function.getParamCount() == 1) + arguments = arg; + else { + if (aggregate) + aggregate->getSequence()[i] = arg; + else + arguments = arg; + } + } + } + } + } +#endif +} + +// +// Add any needed implicit output conversions for function-call arguments. This +// can require a new tree topology, complicated further by whether the function +// has a return value. +// +// Returns a node of a subtree that evaluates to the return value of the function. +// +TIntermTyped* TParseContext::addOutputArgumentConversions(const TFunction& function, TIntermAggregate& intermNode) const +{ +#ifdef GLSLANG_WEB + return &intermNode; +#else + TIntermSequence& arguments = intermNode.getSequence(); + + // Will there be any output conversions? + bool outputConversions = false; + for (int i = 0; i < function.getParamCount(); ++i) { + if (*function[i].type != arguments[i]->getAsTyped()->getType() && function[i].type->getQualifier().isParamOutput()) { + outputConversions = true; + break; + } + } + + if (! outputConversions) + return &intermNode; + + // Setup for the new tree, if needed: + // + // Output conversions need a different tree topology. + // Out-qualified arguments need a temporary of the correct type, with the call + // followed by an assignment of the temporary to the original argument: + // void: function(arg, ...) -> ( function(tempArg, ...), arg = tempArg, ...) + // ret = function(arg, ...) -> ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet) + // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment. + TIntermTyped* conversionTree = nullptr; + TVariable* tempRet = nullptr; + if (intermNode.getBasicType() != EbtVoid) { + // do the "tempRet = function(...), " bit from above + tempRet = makeInternalVariable("tempReturn", intermNode.getType()); + TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, intermNode.getLoc()); + conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, intermNode.getLoc()); + } else + conversionTree = &intermNode; + + conversionTree = intermediate.makeAggregate(conversionTree); + + // Process each argument's conversion + for (int i = 0; i < function.getParamCount(); ++i) { + if (*function[i].type != arguments[i]->getAsTyped()->getType()) { + if (function[i].type->getQualifier().isParamOutput()) { + // Out-qualified arguments need to use the topology set up above. + // do the " ...(tempArg, ...), arg = tempArg" bit from above + TType paramType; + paramType.shallowCopy(*function[i].type); + if (arguments[i]->getAsTyped()->getType().isParameterized() && + !paramType.isParameterized()) { + paramType.shallowCopy(arguments[i]->getAsTyped()->getType()); + paramType.copyTypeParameters(*arguments[i]->getAsTyped()->getType().getTypeParameters()); + } + TVariable* tempArg = makeInternalVariable("tempArg", paramType); + tempArg->getWritableType().getQualifier().makeTemporary(); + TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, intermNode.getLoc()); + TIntermTyped* tempAssign = intermediate.addAssign(EOpAssign, arguments[i]->getAsTyped(), tempArgNode, arguments[i]->getLoc()); + conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc()); + // replace the argument with another node for the same tempArg variable + arguments[i] = intermediate.addSymbol(*tempArg, intermNode.getLoc()); + } + } + } + + // Finalize the tree topology (see bigger comment above). + if (tempRet) { + // do the "..., tempRet" bit from above + TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, intermNode.getLoc()); + conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, intermNode.getLoc()); + } + conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), intermNode.getLoc()); + + return conversionTree; +#endif +} + +void TParseContext::memorySemanticsCheck(const TSourceLoc& loc, const TFunction& fnCandidate, const TIntermOperator& callNode) +{ + const TIntermSequence* argp = &callNode.getAsAggregate()->getSequence(); + + //const int gl_SemanticsRelaxed = 0x0; + const int gl_SemanticsAcquire = 0x2; + const int gl_SemanticsRelease = 0x4; + const int gl_SemanticsAcquireRelease = 0x8; + const int gl_SemanticsMakeAvailable = 0x2000; + const int gl_SemanticsMakeVisible = 0x4000; + const int gl_SemanticsVolatile = 0x8000; + + //const int gl_StorageSemanticsNone = 0x0; + const int gl_StorageSemanticsBuffer = 0x40; + const int gl_StorageSemanticsShared = 0x100; + const int gl_StorageSemanticsImage = 0x800; + const int gl_StorageSemanticsOutput = 0x1000; + + + unsigned int semantics = 0, storageClassSemantics = 0; + unsigned int semantics2 = 0, storageClassSemantics2 = 0; + + const TIntermTyped* arg0 = (*argp)[0]->getAsTyped(); + const bool isMS = arg0->getBasicType() == EbtSampler && arg0->getType().getSampler().isMultiSample(); + + // Grab the semantics and storage class semantics from the operands, based on opcode + switch (callNode.getOp()) { + case EOpAtomicAdd: + case EOpAtomicMin: + case EOpAtomicMax: + case EOpAtomicAnd: + case EOpAtomicOr: + case EOpAtomicXor: + case EOpAtomicExchange: + case EOpAtomicStore: + storageClassSemantics = (*argp)[3]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[4]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpAtomicLoad: + storageClassSemantics = (*argp)[2]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[3]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpAtomicCompSwap: + storageClassSemantics = (*argp)[4]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[5]->getAsConstantUnion()->getConstArray()[0].getIConst(); + storageClassSemantics2 = (*argp)[6]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics2 = (*argp)[7]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + + case EOpImageAtomicAdd: + case EOpImageAtomicMin: + case EOpImageAtomicMax: + case EOpImageAtomicAnd: + case EOpImageAtomicOr: + case EOpImageAtomicXor: + case EOpImageAtomicExchange: + case EOpImageAtomicStore: + storageClassSemantics = (*argp)[isMS ? 5 : 4]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[isMS ? 6 : 5]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpImageAtomicLoad: + storageClassSemantics = (*argp)[isMS ? 4 : 3]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[isMS ? 5 : 4]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpImageAtomicCompSwap: + storageClassSemantics = (*argp)[isMS ? 6 : 5]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[isMS ? 7 : 6]->getAsConstantUnion()->getConstArray()[0].getIConst(); + storageClassSemantics2 = (*argp)[isMS ? 8 : 7]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics2 = (*argp)[isMS ? 9 : 8]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + + case EOpBarrier: + storageClassSemantics = (*argp)[2]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[3]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpMemoryBarrier: + storageClassSemantics = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[2]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + default: + break; + } + + if ((semantics & gl_SemanticsAcquire) && + (callNode.getOp() == EOpAtomicStore || callNode.getOp() == EOpImageAtomicStore)) { + error(loc, "gl_SemanticsAcquire must not be used with (image) atomic store", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsRelease) && + (callNode.getOp() == EOpAtomicLoad || callNode.getOp() == EOpImageAtomicLoad)) { + error(loc, "gl_SemanticsRelease must not be used with (image) atomic load", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsAcquireRelease) && + (callNode.getOp() == EOpAtomicStore || callNode.getOp() == EOpImageAtomicStore || + callNode.getOp() == EOpAtomicLoad || callNode.getOp() == EOpImageAtomicLoad)) { + error(loc, "gl_SemanticsAcquireRelease must not be used with (image) atomic load/store", + fnCandidate.getName().c_str(), ""); + } + if (((semantics | semantics2) & ~(gl_SemanticsAcquire | + gl_SemanticsRelease | + gl_SemanticsAcquireRelease | + gl_SemanticsMakeAvailable | + gl_SemanticsMakeVisible | + gl_SemanticsVolatile))) { + error(loc, "Invalid semantics value", fnCandidate.getName().c_str(), ""); + } + if (((storageClassSemantics | storageClassSemantics2) & ~(gl_StorageSemanticsBuffer | + gl_StorageSemanticsShared | + gl_StorageSemanticsImage | + gl_StorageSemanticsOutput))) { + error(loc, "Invalid storage class semantics value", fnCandidate.getName().c_str(), ""); + } + + if (callNode.getOp() == EOpMemoryBarrier) { + if (!IsPow2(semantics & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "Semantics must include exactly one of gl_SemanticsRelease, gl_SemanticsAcquire, or " + "gl_SemanticsAcquireRelease", fnCandidate.getName().c_str(), ""); + } + } else { + if (semantics & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease)) { + if (!IsPow2(semantics & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "Semantics must not include multiple of gl_SemanticsRelease, gl_SemanticsAcquire, or " + "gl_SemanticsAcquireRelease", fnCandidate.getName().c_str(), ""); + } + } + if (semantics2 & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease)) { + if (!IsPow2(semantics2 & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "semUnequal must not include multiple of gl_SemanticsRelease, gl_SemanticsAcquire, or " + "gl_SemanticsAcquireRelease", fnCandidate.getName().c_str(), ""); + } + } + } + if (callNode.getOp() == EOpMemoryBarrier) { + if (storageClassSemantics == 0) { + error(loc, "Storage class semantics must not be zero", fnCandidate.getName().c_str(), ""); + } + } + if (callNode.getOp() == EOpBarrier && semantics != 0 && storageClassSemantics == 0) { + error(loc, "Storage class semantics must not be zero", fnCandidate.getName().c_str(), ""); + } + if ((callNode.getOp() == EOpAtomicCompSwap || callNode.getOp() == EOpImageAtomicCompSwap) && + (semantics2 & (gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "semUnequal must not be gl_SemanticsRelease or gl_SemanticsAcquireRelease", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsMakeAvailable) && + !(semantics & (gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "gl_SemanticsMakeAvailable requires gl_SemanticsRelease or gl_SemanticsAcquireRelease", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsMakeVisible) && + !(semantics & (gl_SemanticsAcquire | gl_SemanticsAcquireRelease))) { + error(loc, "gl_SemanticsMakeVisible requires gl_SemanticsAcquire or gl_SemanticsAcquireRelease", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsVolatile) && + (callNode.getOp() == EOpMemoryBarrier || callNode.getOp() == EOpBarrier)) { + error(loc, "gl_SemanticsVolatile must not be used with memoryBarrier or controlBarrier", + fnCandidate.getName().c_str(), ""); + } + if ((callNode.getOp() == EOpAtomicCompSwap || callNode.getOp() == EOpImageAtomicCompSwap) && + ((semantics ^ semantics2) & gl_SemanticsVolatile)) { + error(loc, "semEqual and semUnequal must either both include gl_SemanticsVolatile or neither", + fnCandidate.getName().c_str(), ""); + } +} + +// +// Do additional checking of built-in function calls that is not caught +// by normal semantic checks on argument type, extension tagging, etc. +// +// Assumes there has been a semantically correct match to a built-in function prototype. +// +void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode) +{ + // Set up convenience accessors to the argument(s). There is almost always + // multiple arguments for the cases below, but when there might be one, + // check the unaryArg first. + const TIntermSequence* argp = nullptr; // confusing to use [] syntax on a pointer, so this is to help get a reference + const TIntermTyped* unaryArg = nullptr; + const TIntermTyped* arg0 = nullptr; + if (callNode.getAsAggregate()) { + argp = &callNode.getAsAggregate()->getSequence(); + if (argp->size() > 0) + arg0 = (*argp)[0]->getAsTyped(); + } else { + assert(callNode.getAsUnaryNode()); + unaryArg = callNode.getAsUnaryNode()->getOperand(); + arg0 = unaryArg; + } + + TString featureString; + const char* feature = nullptr; + switch (callNode.getOp()) { +#ifndef GLSLANG_WEB + case EOpTextureGather: + case EOpTextureGatherOffset: + case EOpTextureGatherOffsets: + { + // Figure out which variants are allowed by what extensions, + // and what arguments must be constant for which situations. + + featureString = fnCandidate.getName(); + featureString += "(...)"; + feature = featureString.c_str(); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + int compArg = -1; // track which argument, if any, is the constant component argument + switch (callNode.getOp()) { + case EOpTextureGather: + // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5, + // otherwise, need GL_ARB_texture_gather. + if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || fnCandidate[0].type->getSampler().shadow) { + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 2; + } else + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature); + break; + case EOpTextureGatherOffset: + // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument + if (fnCandidate[0].type->getSampler().dim == Esd2D && ! fnCandidate[0].type->getSampler().shadow && fnCandidate.getParamCount() == 3) + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature); + else + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! (*argp)[fnCandidate[0].type->getSampler().shadow ? 3 : 2]->getAsConstantUnion()) + profileRequires(loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, + "non-constant offset argument"); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + break; + case EOpTextureGatherOffsets: + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + // check for constant offsets + if (! (*argp)[fnCandidate[0].type->getSampler().shadow ? 3 : 2]->getAsConstantUnion()) + error(loc, "must be a compile-time constant:", feature, "offsets argument"); + break; + default: + break; + } + + if (compArg > 0 && compArg < fnCandidate.getParamCount()) { + if ((*argp)[compArg]->getAsConstantUnion()) { + int value = (*argp)[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (value < 0 || value > 3) + error(loc, "must be 0, 1, 2, or 3:", feature, "component argument"); + } else + error(loc, "must be a compile-time constant:", feature, "component argument"); + } + + bool bias = false; + if (callNode.getOp() == EOpTextureGather) + bias = fnCandidate.getParamCount() > 3; + else if (callNode.getOp() == EOpTextureGatherOffset || + callNode.getOp() == EOpTextureGatherOffsets) + bias = fnCandidate.getParamCount() > 4; + + if (bias) { + featureString = fnCandidate.getName(); + featureString += "with bias argument"; + feature = featureString.c_str(); + profileRequires(loc, ~EEsProfile, 450, nullptr, feature); + requireExtensions(loc, 1, &E_GL_AMD_texture_gather_bias_lod, feature); + } + break; + } + case EOpSparseTextureGather: + case EOpSparseTextureGatherOffset: + case EOpSparseTextureGatherOffsets: + { + bool bias = false; + if (callNode.getOp() == EOpSparseTextureGather) + bias = fnCandidate.getParamCount() > 4; + else if (callNode.getOp() == EOpSparseTextureGatherOffset || + callNode.getOp() == EOpSparseTextureGatherOffsets) + bias = fnCandidate.getParamCount() > 5; + + if (bias) { + featureString = fnCandidate.getName(); + featureString += "with bias argument"; + feature = featureString.c_str(); + profileRequires(loc, ~EEsProfile, 450, nullptr, feature); + requireExtensions(loc, 1, &E_GL_AMD_texture_gather_bias_lod, feature); + } + + break; + } + + case EOpSparseTextureGatherLod: + case EOpSparseTextureGatherLodOffset: + case EOpSparseTextureGatherLodOffsets: + { + requireExtensions(loc, 1, &E_GL_ARB_sparse_texture2, fnCandidate.getName().c_str()); + break; + } + + case EOpSwizzleInvocations: + { + if (! (*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "offset", ""); + else { + unsigned offset[4] = {}; + offset[0] = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getUConst(); + offset[1] = (*argp)[1]->getAsConstantUnion()->getConstArray()[1].getUConst(); + offset[2] = (*argp)[1]->getAsConstantUnion()->getConstArray()[2].getUConst(); + offset[3] = (*argp)[1]->getAsConstantUnion()->getConstArray()[3].getUConst(); + if (offset[0] > 3 || offset[1] > 3 || offset[2] > 3 || offset[3] > 3) + error(loc, "components must be in the range [0, 3]", "offset", ""); + } + + break; + } + + case EOpSwizzleInvocationsMasked: + { + if (! (*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "mask", ""); + else { + unsigned mask[3] = {}; + mask[0] = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getUConst(); + mask[1] = (*argp)[1]->getAsConstantUnion()->getConstArray()[1].getUConst(); + mask[2] = (*argp)[1]->getAsConstantUnion()->getConstArray()[2].getUConst(); + if (mask[0] > 31 || mask[1] > 31 || mask[2] > 31) + error(loc, "components must be in the range [0, 31]", "mask", ""); + } + + break; + } +#endif + + case EOpTextureOffset: + case EOpTextureFetchOffset: + case EOpTextureProjOffset: + case EOpTextureLodOffset: + case EOpTextureProjLodOffset: + case EOpTextureGradOffset: + case EOpTextureProjGradOffset: + { + // Handle texture-offset limits checking + // Pick which argument has to hold constant offsets + int arg = -1; + switch (callNode.getOp()) { + case EOpTextureOffset: arg = 2; break; + case EOpTextureFetchOffset: arg = (arg0->getType().getSampler().isRect()) ? 2 : 3; break; + case EOpTextureProjOffset: arg = 2; break; + case EOpTextureLodOffset: arg = 3; break; + case EOpTextureProjLodOffset: arg = 3; break; + case EOpTextureGradOffset: arg = 4; break; + case EOpTextureProjGradOffset: arg = 4; break; + default: + assert(0); + break; + } + + if (arg > 0) { + +#ifndef GLSLANG_WEB + bool f16ShadowCompare = (*argp)[1]->getAsTyped()->getBasicType() == EbtFloat16 && + arg0->getType().getSampler().shadow; + if (f16ShadowCompare) + ++arg; +#endif + if (! (*argp)[arg]->getAsTyped()->getQualifier().isConstant()) + error(loc, "argument must be compile-time constant", "texel offset", ""); + else if ((*argp)[arg]->getAsConstantUnion()) { + const TType& type = (*argp)[arg]->getAsTyped()->getType(); + for (int c = 0; c < type.getVectorSize(); ++c) { + int offset = (*argp)[arg]->getAsConstantUnion()->getConstArray()[c].getIConst(); + if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset) + error(loc, "value is out of range:", "texel offset", + "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]"); + } + } + } + + break; + } + +#ifndef GLSLANG_WEB + case EOpTrace: + if (!(*argp)[10]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "payload number", ""); + break; + case EOpExecuteCallable: + if (!(*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "callable data number", ""); + break; + + case EOpRayQueryGetIntersectionType: + case EOpRayQueryGetIntersectionT: + case EOpRayQueryGetIntersectionInstanceCustomIndex: + case EOpRayQueryGetIntersectionInstanceId: + case EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: + case EOpRayQueryGetIntersectionGeometryIndex: + case EOpRayQueryGetIntersectionPrimitiveIndex: + case EOpRayQueryGetIntersectionBarycentrics: + case EOpRayQueryGetIntersectionFrontFace: + case EOpRayQueryGetIntersectionObjectRayDirection: + case EOpRayQueryGetIntersectionObjectRayOrigin: + case EOpRayQueryGetIntersectionObjectToWorld: + case EOpRayQueryGetIntersectionWorldToObject: + if (!(*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "committed", ""); + break; + + case EOpTextureQuerySamples: + case EOpImageQuerySamples: + // GL_ARB_shader_texture_image_samples + profileRequires(loc, ~EEsProfile, 450, E_GL_ARB_shader_texture_image_samples, "textureSamples and imageSamples"); + break; + + case EOpImageAtomicAdd: + case EOpImageAtomicMin: + case EOpImageAtomicMax: + case EOpImageAtomicAnd: + case EOpImageAtomicOr: + case EOpImageAtomicXor: + case EOpImageAtomicExchange: + case EOpImageAtomicCompSwap: + case EOpImageAtomicLoad: + case EOpImageAtomicStore: + { + // Make sure the image types have the correct layout() format and correct argument types + const TType& imageType = arg0->getType(); + if (imageType.getSampler().type == EbtInt || imageType.getSampler().type == EbtUint) { + if (imageType.getQualifier().getFormat() != ElfR32i && imageType.getQualifier().getFormat() != ElfR32ui) + error(loc, "only supported on image with format r32i or r32ui", fnCandidate.getName().c_str(), ""); + } else { + if (fnCandidate.getName().compare(0, 19, "imageAtomicExchange") != 0) + error(loc, "only supported on integer images", fnCandidate.getName().c_str(), ""); + else if (imageType.getQualifier().getFormat() != ElfR32f && isEsProfile()) + error(loc, "only supported on image with format r32f", fnCandidate.getName().c_str(), ""); + } + + const size_t maxArgs = imageType.getSampler().isMultiSample() ? 5 : 4; + if (argp->size() > maxArgs) { + requireExtensions(loc, 1, &E_GL_KHR_memory_scope_semantics, fnCandidate.getName().c_str()); + memorySemanticsCheck(loc, fnCandidate, callNode); + } + + break; + } + + case EOpAtomicAdd: + case EOpAtomicMin: + case EOpAtomicMax: + case EOpAtomicAnd: + case EOpAtomicOr: + case EOpAtomicXor: + case EOpAtomicExchange: + case EOpAtomicCompSwap: + case EOpAtomicLoad: + case EOpAtomicStore: + { + if (argp->size() > 3) { + requireExtensions(loc, 1, &E_GL_KHR_memory_scope_semantics, fnCandidate.getName().c_str()); + memorySemanticsCheck(loc, fnCandidate, callNode); + } else if (arg0->getType().getBasicType() == EbtInt64 || arg0->getType().getBasicType() == EbtUint64) { + const char* const extensions[2] = { E_GL_NV_shader_atomic_int64, + E_GL_EXT_shader_atomic_int64 }; + requireExtensions(loc, 2, extensions, fnCandidate.getName().c_str()); + } + break; + } + + case EOpInterpolateAtCentroid: + case EOpInterpolateAtSample: + case EOpInterpolateAtOffset: + case EOpInterpolateAtVertex: + // Make sure the first argument is an interpolant, or an array element of an interpolant + if (arg0->getType().getQualifier().storage != EvqVaryingIn) { + // It might still be an array element. + // + // We could check more, but the semantics of the first argument are already met; the + // only way to turn an array into a float/vec* is array dereference and swizzle. + // + // ES and desktop 4.3 and earlier: swizzles may not be used + // desktop 4.4 and later: swizzles may be used + bool swizzleOkay = (!isEsProfile()) && (version >= 440); + const TIntermTyped* base = TIntermediate::findLValueBase(arg0, swizzleOkay); + if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn) + error(loc, "first argument must be an interpolant, or interpolant-array element", fnCandidate.getName().c_str(), ""); + } + + if (callNode.getOp() == EOpInterpolateAtVertex) { + if (!arg0->getType().getQualifier().isExplicitInterpolation()) + error(loc, "argument must be qualified as __explicitInterpAMD in", "interpolant", ""); + else { + if (! (*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "vertex index", ""); + else { + unsigned vertexIdx = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getUConst(); + if (vertexIdx > 2) + error(loc, "must be in the range [0, 2]", "vertex index", ""); + } + } + } + break; + + case EOpEmitStreamVertex: + case EOpEndStreamPrimitive: + intermediate.setMultiStream(); + break; + + case EOpSubgroupClusteredAdd: + case EOpSubgroupClusteredMul: + case EOpSubgroupClusteredMin: + case EOpSubgroupClusteredMax: + case EOpSubgroupClusteredAnd: + case EOpSubgroupClusteredOr: + case EOpSubgroupClusteredXor: + // The as used in the subgroupClustered() operations must be: + // - An integral constant expression. + // - At least 1. + // - A power of 2. + if ((*argp)[1]->getAsConstantUnion() == nullptr) + error(loc, "argument must be compile-time constant", "cluster size", ""); + else { + int size = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (size < 1) + error(loc, "argument must be at least 1", "cluster size", ""); + else if (!IsPow2(size)) + error(loc, "argument must be a power of 2", "cluster size", ""); + } + break; + + case EOpSubgroupBroadcast: + case EOpSubgroupQuadBroadcast: + if (spvVersion.spv < EShTargetSpv_1_5) { + // must be an integral constant expression. + if ((*argp)[1]->getAsConstantUnion() == nullptr) + error(loc, "argument must be compile-time constant", "id", ""); + } + break; + + case EOpBarrier: + case EOpMemoryBarrier: + if (argp->size() > 0) { + requireExtensions(loc, 1, &E_GL_KHR_memory_scope_semantics, fnCandidate.getName().c_str()); + memorySemanticsCheck(loc, fnCandidate, callNode); + } + break; + + case EOpMix: + if (profile == EEsProfile && version < 310) { + // Look for specific signatures + if ((*argp)[0]->getAsTyped()->getBasicType() != EbtFloat && + (*argp)[1]->getAsTyped()->getBasicType() != EbtFloat && + (*argp)[2]->getAsTyped()->getBasicType() == EbtBool) { + requireExtensions(loc, 1, &E_GL_EXT_shader_integer_mix, "specific signature of builtin mix"); + } + } + + if (profile != EEsProfile && version < 450) { + if ((*argp)[0]->getAsTyped()->getBasicType() != EbtFloat && + (*argp)[0]->getAsTyped()->getBasicType() != EbtDouble && + (*argp)[1]->getAsTyped()->getBasicType() != EbtFloat && + (*argp)[1]->getAsTyped()->getBasicType() != EbtDouble && + (*argp)[2]->getAsTyped()->getBasicType() == EbtBool) { + requireExtensions(loc, 1, &E_GL_EXT_shader_integer_mix, fnCandidate.getName().c_str()); + } + } + + break; +#endif + + default: + break; + } + + // Texture operations on texture objects (aside from texelFetch on a + // textureBuffer) require EXT_samplerless_texture_functions. + switch (callNode.getOp()) { + case EOpTextureQuerySize: + case EOpTextureQueryLevels: + case EOpTextureQuerySamples: + case EOpTextureFetch: + case EOpTextureFetchOffset: + { + const TSampler& sampler = fnCandidate[0].type->getSampler(); + + const bool isTexture = sampler.isTexture() && !sampler.isCombined(); + const bool isBuffer = sampler.isBuffer(); + const bool isFetch = callNode.getOp() == EOpTextureFetch || callNode.getOp() == EOpTextureFetchOffset; + + if (isTexture && (!isBuffer || !isFetch)) + requireExtensions(loc, 1, &E_GL_EXT_samplerless_texture_functions, fnCandidate.getName().c_str()); + + break; + } + + default: + break; + } + + if (callNode.isSubgroup()) { + // these require SPIR-V 1.3 + if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_3) + error(loc, "requires SPIR-V 1.3", "subgroup op", ""); + + // Check that if extended types are being used that the correct extensions are enabled. + if (arg0 != nullptr) { + const TType& type = arg0->getType(); + switch (type.getBasicType()) { + default: + break; + case EbtInt8: + case EbtUint8: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_int8, type.getCompleteString().c_str()); + break; + case EbtInt16: + case EbtUint16: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_int16, type.getCompleteString().c_str()); + break; + case EbtInt64: + case EbtUint64: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_int64, type.getCompleteString().c_str()); + break; + case EbtFloat16: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_float16, type.getCompleteString().c_str()); + break; + } + } + } +} + +#ifndef GLSLANG_WEB + +extern bool PureOperatorBuiltins; + +// Deprecated! Use PureOperatorBuiltins == true instead, in which case this +// functionality is handled in builtInOpCheck() instead of here. +// +// Do additional checking of built-in function calls that were not mapped +// to built-in operations (e.g., texturing functions). +// +// Assumes there has been a semantically correct match to a built-in function. +// +void TParseContext::nonOpBuiltInCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermAggregate& callNode) +{ + // Further maintenance of this function is deprecated, because the "correct" + // future-oriented design is to not have to do string compares on function names. + + // If PureOperatorBuiltins == true, then all built-ins should be mapped + // to a TOperator, and this function would then never get called. + + assert(PureOperatorBuiltins == false); + + // built-in texturing functions get their return value precision from the precision of the sampler + if (fnCandidate.getType().getQualifier().precision == EpqNone && + fnCandidate.getParamCount() > 0 && fnCandidate[0].type->getBasicType() == EbtSampler) + callNode.getQualifier().precision = callNode.getSequence()[0]->getAsTyped()->getQualifier().precision; + + if (fnCandidate.getName().compare(0, 7, "texture") == 0) { + if (fnCandidate.getName().compare(0, 13, "textureGather") == 0) { + TString featureString = fnCandidate.getName() + "(...)"; + const char* feature = featureString.c_str(); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + + int compArg = -1; // track which argument, if any, is the constant component argument + if (fnCandidate.getName().compare("textureGatherOffset") == 0) { + // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument + if (fnCandidate[0].type->getSampler().dim == Esd2D && ! fnCandidate[0].type->getSampler().shadow && fnCandidate.getParamCount() == 3) + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature); + else + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + int offsetArg = fnCandidate[0].type->getSampler().shadow ? 3 : 2; + if (! callNode.getSequence()[offsetArg]->getAsConstantUnion()) + profileRequires(loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, + "non-constant offset argument"); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + } else if (fnCandidate.getName().compare("textureGatherOffsets") == 0) { + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + // check for constant offsets + int offsetArg = fnCandidate[0].type->getSampler().shadow ? 3 : 2; + if (! callNode.getSequence()[offsetArg]->getAsConstantUnion()) + error(loc, "must be a compile-time constant:", feature, "offsets argument"); + } else if (fnCandidate.getName().compare("textureGather") == 0) { + // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5, + // otherwise, need GL_ARB_texture_gather. + if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || fnCandidate[0].type->getSampler().shadow) { + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 2; + } else + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature); + } + + if (compArg > 0 && compArg < fnCandidate.getParamCount()) { + if (callNode.getSequence()[compArg]->getAsConstantUnion()) { + int value = callNode.getSequence()[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (value < 0 || value > 3) + error(loc, "must be 0, 1, 2, or 3:", feature, "component argument"); + } else + error(loc, "must be a compile-time constant:", feature, "component argument"); + } + } else { + // this is only for functions not starting "textureGather"... + if (fnCandidate.getName().find("Offset") != TString::npos) { + + // Handle texture-offset limits checking + int arg = -1; + if (fnCandidate.getName().compare("textureOffset") == 0) + arg = 2; + else if (fnCandidate.getName().compare("texelFetchOffset") == 0) + arg = 3; + else if (fnCandidate.getName().compare("textureProjOffset") == 0) + arg = 2; + else if (fnCandidate.getName().compare("textureLodOffset") == 0) + arg = 3; + else if (fnCandidate.getName().compare("textureProjLodOffset") == 0) + arg = 3; + else if (fnCandidate.getName().compare("textureGradOffset") == 0) + arg = 4; + else if (fnCandidate.getName().compare("textureProjGradOffset") == 0) + arg = 4; + + if (arg > 0) { + if (! callNode.getSequence()[arg]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "texel offset", ""); + else { + const TType& type = callNode.getSequence()[arg]->getAsTyped()->getType(); + for (int c = 0; c < type.getVectorSize(); ++c) { + int offset = callNode.getSequence()[arg]->getAsConstantUnion()->getConstArray()[c].getIConst(); + if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset) + error(loc, "value is out of range:", "texel offset", "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]"); + } + } + } + } + } + } + + // GL_ARB_shader_texture_image_samples + if (fnCandidate.getName().compare(0, 14, "textureSamples") == 0 || fnCandidate.getName().compare(0, 12, "imageSamples") == 0) + profileRequires(loc, ~EEsProfile, 450, E_GL_ARB_shader_texture_image_samples, "textureSamples and imageSamples"); + + if (fnCandidate.getName().compare(0, 11, "imageAtomic") == 0) { + const TType& imageType = callNode.getSequence()[0]->getAsTyped()->getType(); + if (imageType.getSampler().type == EbtInt || imageType.getSampler().type == EbtUint) { + if (imageType.getQualifier().getFormat() != ElfR32i && imageType.getQualifier().getFormat() != ElfR32ui) + error(loc, "only supported on image with format r32i or r32ui", fnCandidate.getName().c_str(), ""); + } else { + if (fnCandidate.getName().compare(0, 19, "imageAtomicExchange") != 0) + error(loc, "only supported on integer images", fnCandidate.getName().c_str(), ""); + else if (imageType.getQualifier().getFormat() != ElfR32f && isEsProfile()) + error(loc, "only supported on image with format r32f", fnCandidate.getName().c_str(), ""); + } + } +} + +#endif + +// +// Do any extra checking for a user function call. +// +void TParseContext::userFunctionCallCheck(const TSourceLoc& loc, TIntermAggregate& callNode) +{ + TIntermSequence& arguments = callNode.getSequence(); + + for (int i = 0; i < (int)arguments.size(); ++i) + samplerConstructorLocationCheck(loc, "call argument", arguments[i]); +} + +// +// Emit an error if this is a sampler constructor +// +void TParseContext::samplerConstructorLocationCheck(const TSourceLoc& loc, const char* token, TIntermNode* node) +{ + if (node->getAsOperator() && node->getAsOperator()->getOp() == EOpConstructTextureSampler) + error(loc, "sampler constructor must appear at point of use", token, ""); +} + +// +// Handle seeing a built-in constructor in a grammar production. +// +TFunction* TParseContext::handleConstructorCall(const TSourceLoc& loc, const TPublicType& publicType) +{ + TType type(publicType); + type.getQualifier().precision = EpqNone; + + if (type.isArray()) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed constructor"); + profileRequires(loc, EEsProfile, 300, nullptr, "arrayed constructor"); + } + + TOperator op = intermediate.mapTypeToConstructorOp(type); + + if (op == EOpNull) { + error(loc, "cannot construct this type", type.getBasicString(), ""); + op = EOpConstructFloat; + TType errorType(EbtFloat); + type.shallowCopy(errorType); + } + + TString empty(""); + + return new TFunction(&empty, type, op); +} + +// Handle seeing a precision qualifier in the grammar. +void TParseContext::handlePrecisionQualifier(const TSourceLoc& /*loc*/, TQualifier& qualifier, TPrecisionQualifier precision) +{ + if (obeyPrecisionQualifiers()) + qualifier.precision = precision; +} + +// Check for messages to give on seeing a precision qualifier used in a +// declaration in the grammar. +void TParseContext::checkPrecisionQualifier(const TSourceLoc& loc, TPrecisionQualifier) +{ + if (precisionManager.shouldWarnAboutDefaults()) { + warn(loc, "all default precisions are highp; use precision statements to quiet warning, e.g.:\n" + " \"precision mediump int; precision highp float;\"", "", ""); + precisionManager.defaultWarningGiven(); + } +} + +// +// Same error message for all places assignments don't work. +// +void TParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right) +{ + error(loc, "", op, "cannot convert from '%s' to '%s'", + right.c_str(), left.c_str()); +} + +// +// Same error message for all places unary operations don't work. +// +void TParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand) +{ + error(loc, " wrong operand type", op, + "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)", + op, operand.c_str()); +} + +// +// Same error message for all binary operations don't work. +// +void TParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right) +{ + error(loc, " wrong operand types:", op, + "no operation '%s' exists that takes a left-hand operand of type '%s' and " + "a right operand of type '%s' (or there is no acceptable conversion)", + op, left.c_str(), right.c_str()); +} + +// +// A basic type of EbtVoid is a key that the name string was seen in the source, but +// it was not found as a variable in the symbol table. If so, give the error +// message and insert a dummy variable in the symbol table to prevent future errors. +// +void TParseContext::variableCheck(TIntermTyped*& nodePtr) +{ + TIntermSymbol* symbol = nodePtr->getAsSymbolNode(); + if (! symbol) + return; + + if (symbol->getType().getBasicType() == EbtVoid) { + const char *extraInfoFormat = ""; + if (spvVersion.vulkan != 0 && symbol->getName() == "gl_VertexID") { + extraInfoFormat = "(Did you mean gl_VertexIndex?)"; + } else if (spvVersion.vulkan != 0 && symbol->getName() == "gl_InstanceID") { + extraInfoFormat = "(Did you mean gl_InstanceIndex?)"; + } + error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), extraInfoFormat); + + // Add to symbol table to prevent future error messages on the same name + if (symbol->getName().size() > 0) { + TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat)); + symbolTable.insert(*fakeVariable); + + // substitute a symbol node for this new variable + nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc()); + } + } else { + switch (symbol->getQualifier().storage) { + case EvqPointCoord: + profileRequires(symbol->getLoc(), ENoProfile, 120, nullptr, "gl_PointCoord"); + break; + default: break; // some compilers want this + } + } +} + +// +// Both test and if necessary, spit out an error, to see if the node is really +// an l-value that can be operated on this way. +// +// Returns true if there was an error. +// +bool TParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + TIntermBinary* binaryNode = node->getAsBinaryNode(); + + if (binaryNode) { + bool errorReturn = false; + + switch(binaryNode->getOp()) { +#ifndef GLSLANG_WEB + case EOpIndexDirect: + case EOpIndexIndirect: + // ... tessellation control shader ... + // If a per-vertex output variable is used as an l-value, it is a + // compile-time or link-time error if the expression indicating the + // vertex index is not the identifier gl_InvocationID. + if (language == EShLangTessControl) { + const TType& leftType = binaryNode->getLeft()->getType(); + if (leftType.getQualifier().storage == EvqVaryingOut && ! leftType.getQualifier().patch && binaryNode->getLeft()->getAsSymbolNode()) { + // we have a per-vertex output + const TIntermSymbol* rightSymbol = binaryNode->getRight()->getAsSymbolNode(); + if (! rightSymbol || rightSymbol->getQualifier().builtIn != EbvInvocationId) + error(loc, "tessellation-control per-vertex output l-value must be indexed with gl_InvocationID", "[]", ""); + } + } + break; // left node is checked by base class +#endif + case EOpVectorSwizzle: + errorReturn = lValueErrorCheck(loc, op, binaryNode->getLeft()); + if (!errorReturn) { + int offset[4] = {0,0,0,0}; + + TIntermTyped* rightNode = binaryNode->getRight(); + TIntermAggregate *aggrNode = rightNode->getAsAggregate(); + + for (TIntermSequence::iterator p = aggrNode->getSequence().begin(); + p != aggrNode->getSequence().end(); p++) { + int value = (*p)->getAsTyped()->getAsConstantUnion()->getConstArray()[0].getIConst(); + offset[value]++; + if (offset[value] > 1) { + error(loc, " l-value of swizzle cannot have duplicate components", op, "", ""); + + return true; + } + } + } + + return errorReturn; + default: + break; + } + + if (errorReturn) { + error(loc, " l-value required", op, "", ""); + return true; + } + } + + if (binaryNode && binaryNode->getOp() == EOpIndexDirectStruct && binaryNode->getLeft()->isReference()) + return false; + + // Let the base class check errors + if (TParseContextBase::lValueErrorCheck(loc, op, node)) + return true; + + const char* symbol = nullptr; + TIntermSymbol* symNode = node->getAsSymbolNode(); + if (symNode != nullptr) + symbol = symNode->getName().c_str(); + + const char* message = nullptr; + switch (node->getQualifier().storage) { + case EvqVaryingIn: message = "can't modify shader input"; break; + case EvqInstanceId: message = "can't modify gl_InstanceID"; break; + case EvqVertexId: message = "can't modify gl_VertexID"; break; + case EvqFace: message = "can't modify gl_FrontFace"; break; + case EvqFragCoord: message = "can't modify gl_FragCoord"; break; + case EvqPointCoord: message = "can't modify gl_PointCoord"; break; + case EvqFragDepth: + intermediate.setDepthReplacing(); + // "In addition, it is an error to statically write to gl_FragDepth in the fragment shader." + if (isEsProfile() && intermediate.getEarlyFragmentTests()) + message = "can't modify gl_FragDepth if using early_fragment_tests"; + break; + + default: + break; + } + + if (message == nullptr && binaryNode == nullptr && symNode == nullptr) { + error(loc, " l-value required", op, "", ""); + + return true; + } + + // + // Everything else is okay, no error. + // + if (message == nullptr) + return false; + + // + // If we get here, we have an error and a message. + // + if (symNode) + error(loc, " l-value required", op, "\"%s\" (%s)", symbol, message); + else + error(loc, " l-value required", op, "(%s)", message); + + return true; +} + +// Test for and give an error if the node can't be read from. +void TParseContext::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + // Let the base class check errors + TParseContextBase::rValueErrorCheck(loc, op, node); + + TIntermSymbol* symNode = node->getAsSymbolNode(); + if (!(symNode && symNode->getQualifier().isWriteOnly())) // base class checks + if (symNode && symNode->getQualifier().isExplicitInterpolation()) + error(loc, "can't read from explicitly-interpolated object: ", op, symNode->getName().c_str()); +} + +// +// Both test, and if necessary spit out an error, to see if the node is really +// a constant. +// +void TParseContext::constantValueCheck(TIntermTyped* node, const char* token) +{ + if (! node->getQualifier().isConstant()) + error(node->getLoc(), "constant expression required", token, ""); +} + +// +// Both test, and if necessary spit out an error, to see if the node is really +// an integer. +// +void TParseContext::integerCheck(const TIntermTyped* node, const char* token) +{ + if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar()) + return; + + error(node->getLoc(), "scalar integer expression required", token, ""); +} + +// +// Both test, and if necessary spit out an error, to see if we are currently +// globally scoped. +// +void TParseContext::globalCheck(const TSourceLoc& loc, const char* token) +{ + if (! symbolTable.atGlobalLevel()) + error(loc, "not allowed in nested scope", token, ""); +} + +// +// Reserved errors for GLSL. +// +void TParseContext::reservedErrorCheck(const TSourceLoc& loc, const TString& identifier) +{ + // "Identifiers starting with "gl_" are reserved for use by OpenGL, and may not be + // declared in a shader; this results in a compile-time error." + if (! symbolTable.atBuiltInLevel()) { + if (builtInName(identifier)) + error(loc, "identifiers starting with \"gl_\" are reserved", identifier.c_str(), ""); + + // "__" are not supposed to be an error. ES 300 (and desktop) added the clarification: + // "In addition, all identifiers containing two consecutive underscores (__) are + // reserved; using such a name does not itself result in an error, but may result + // in undefined behavior." + // however, before that, ES tests required an error. + if (identifier.find("__") != TString::npos) { + if (isEsProfile() && version < 300) + error(loc, "identifiers containing consecutive underscores (\"__\") are reserved, and an error if version < 300", identifier.c_str(), ""); + else + warn(loc, "identifiers containing consecutive underscores (\"__\") are reserved", identifier.c_str(), ""); + } + } +} + +// +// Reserved errors for the preprocessor. +// +void TParseContext::reservedPpErrorCheck(const TSourceLoc& loc, const char* identifier, const char* op) +{ + // "__" are not supposed to be an error. ES 300 (and desktop) added the clarification: + // "All macro names containing two consecutive underscores ( __ ) are reserved; + // defining such a name does not itself result in an error, but may result in + // undefined behavior. All macro names prefixed with "GL_" ("GL" followed by a + // single underscore) are also reserved, and defining such a name results in a + // compile-time error." + // however, before that, ES tests required an error. + if (strncmp(identifier, "GL_", 3) == 0) + ppError(loc, "names beginning with \"GL_\" can't be (un)defined:", op, identifier); + else if (strncmp(identifier, "defined", 8) == 0) + ppError(loc, "\"defined\" can't be (un)defined:", op, identifier); + else if (strstr(identifier, "__") != 0) { + if (isEsProfile() && version >= 300 && + (strcmp(identifier, "__LINE__") == 0 || + strcmp(identifier, "__FILE__") == 0 || + strcmp(identifier, "__VERSION__") == 0)) + ppError(loc, "predefined names can't be (un)defined:", op, identifier); + else { + if (isEsProfile() && version < 300) + ppError(loc, "names containing consecutive underscores are reserved, and an error if version < 300:", op, identifier); + else + ppWarn(loc, "names containing consecutive underscores are reserved:", op, identifier); + } + } +} + +// +// See if this version/profile allows use of the line-continuation character '\'. +// +// Returns true if a line continuation should be done. +// +bool TParseContext::lineContinuationCheck(const TSourceLoc& loc, bool endOfComment) +{ +#ifdef GLSLANG_WEB + return true; +#endif + + const char* message = "line continuation"; + + bool lineContinuationAllowed = (isEsProfile() && version >= 300) || + (!isEsProfile() && (version >= 420 || extensionTurnedOn(E_GL_ARB_shading_language_420pack))); + + if (endOfComment) { + if (lineContinuationAllowed) + warn(loc, "used at end of comment; the following line is still part of the comment", message, ""); + else + warn(loc, "used at end of comment, but this version does not provide line continuation", message, ""); + + return lineContinuationAllowed; + } + + if (relaxedErrors()) { + if (! lineContinuationAllowed) + warn(loc, "not allowed in this version", message, ""); + return true; + } else { + profileRequires(loc, EEsProfile, 300, nullptr, message); + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, message); + } + + return lineContinuationAllowed; +} + +bool TParseContext::builtInName(const TString& identifier) +{ + return identifier.compare(0, 3, "gl_") == 0; +} + +// +// Make sure there is enough data and not too many arguments provided to the +// constructor to build something of the type of the constructor. Also returns +// the type of the constructor. +// +// Part of establishing type is establishing specialization-constness. +// We don't yet know "top down" whether type is a specialization constant, +// but a const constructor can becomes a specialization constant if any of +// its children are, subject to KHR_vulkan_glsl rules: +// +// - int(), uint(), and bool() constructors for type conversions +// from any of the following types to any of the following types: +// * int +// * uint +// * bool +// - vector versions of the above conversion constructors +// +// Returns true if there was an error in construction. +// +bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function, TOperator op, TType& type) +{ + // See if the constructor does not establish the main type, only requalifies + // it, in which case the type comes from the argument instead of from the + // constructor function. + switch (op) { +#ifndef GLSLANG_WEB + case EOpConstructNonuniform: + if (node != nullptr && node->getAsTyped() != nullptr) { + type.shallowCopy(node->getAsTyped()->getType()); + type.getQualifier().makeTemporary(); + type.getQualifier().nonUniform = true; + } + break; +#endif + default: + type.shallowCopy(function.getType()); + break; + } + + // See if it's a matrix + bool constructingMatrix = false; + switch (op) { + case EOpConstructTextureSampler: + return constructorTextureSamplerError(loc, function); + case EOpConstructMat2x2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: + case EOpConstructMat3x3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: + case EOpConstructMat4x4: +#ifndef GLSLANG_WEB + case EOpConstructDMat2x2: + case EOpConstructDMat2x3: + case EOpConstructDMat2x4: + case EOpConstructDMat3x2: + case EOpConstructDMat3x3: + case EOpConstructDMat3x4: + case EOpConstructDMat4x2: + case EOpConstructDMat4x3: + case EOpConstructDMat4x4: + case EOpConstructF16Mat2x2: + case EOpConstructF16Mat2x3: + case EOpConstructF16Mat2x4: + case EOpConstructF16Mat3x2: + case EOpConstructF16Mat3x3: + case EOpConstructF16Mat3x4: + case EOpConstructF16Mat4x2: + case EOpConstructF16Mat4x3: + case EOpConstructF16Mat4x4: +#endif + constructingMatrix = true; + break; + default: + break; + } + + // + // Walk the arguments for first-pass checks and collection of information. + // + + int size = 0; + bool constType = true; + bool specConstType = false; // value is only valid if constType is true + bool full = false; + bool overFull = false; + bool matrixInMatrix = false; + bool arrayArg = false; + bool floatArgument = false; + for (int arg = 0; arg < function.getParamCount(); ++arg) { + if (function[arg].type->isArray()) { + if (function[arg].type->isUnsizedArray()) { + // Can't construct from an unsized array. + error(loc, "array argument must be sized", "constructor", ""); + return true; + } + arrayArg = true; + } + if (constructingMatrix && function[arg].type->isMatrix()) + matrixInMatrix = true; + + // 'full' will go to true when enough args have been seen. If we loop + // again, there is an extra argument. + if (full) { + // For vectors and matrices, it's okay to have too many components + // available, but not okay to have unused arguments. + overFull = true; + } + + size += function[arg].type->computeNumComponents(); + if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents()) + full = true; + + if (! function[arg].type->getQualifier().isConstant()) + constType = false; + if (function[arg].type->getQualifier().isSpecConstant()) + specConstType = true; + if (function[arg].type->isFloatingDomain()) + floatArgument = true; + if (type.isStruct()) { + if (function[arg].type->contains16BitFloat()) { + requireFloat16Arithmetic(loc, "constructor", "can't construct structure containing 16-bit type"); + } + if (function[arg].type->contains16BitInt()) { + requireInt16Arithmetic(loc, "constructor", "can't construct structure containing 16-bit type"); + } + if (function[arg].type->contains8BitInt()) { + requireInt8Arithmetic(loc, "constructor", "can't construct structure containing 8-bit type"); + } + } + } + if (op == EOpConstructNonuniform) + constType = false; + +#ifndef GLSLANG_WEB + switch (op) { + case EOpConstructFloat16: + case EOpConstructF16Vec2: + case EOpConstructF16Vec3: + case EOpConstructF16Vec4: + if (type.isArray()) + requireFloat16Arithmetic(loc, "constructor", "16-bit arrays not supported"); + if (type.isVector() && function.getParamCount() != 1) + requireFloat16Arithmetic(loc, "constructor", "16-bit vectors only take vector types"); + break; + case EOpConstructUint16: + case EOpConstructU16Vec2: + case EOpConstructU16Vec3: + case EOpConstructU16Vec4: + case EOpConstructInt16: + case EOpConstructI16Vec2: + case EOpConstructI16Vec3: + case EOpConstructI16Vec4: + if (type.isArray()) + requireInt16Arithmetic(loc, "constructor", "16-bit arrays not supported"); + if (type.isVector() && function.getParamCount() != 1) + requireInt16Arithmetic(loc, "constructor", "16-bit vectors only take vector types"); + break; + case EOpConstructUint8: + case EOpConstructU8Vec2: + case EOpConstructU8Vec3: + case EOpConstructU8Vec4: + case EOpConstructInt8: + case EOpConstructI8Vec2: + case EOpConstructI8Vec3: + case EOpConstructI8Vec4: + if (type.isArray()) + requireInt8Arithmetic(loc, "constructor", "8-bit arrays not supported"); + if (type.isVector() && function.getParamCount() != 1) + requireInt8Arithmetic(loc, "constructor", "8-bit vectors only take vector types"); + break; + default: + break; + } +#endif + + // inherit constness from children + if (constType) { + bool makeSpecConst; + // Finish pinning down spec-const semantics + if (specConstType) { + switch (op) { + case EOpConstructInt8: + case EOpConstructInt: + case EOpConstructUint: + case EOpConstructBool: + case EOpConstructBVec2: + case EOpConstructBVec3: + case EOpConstructBVec4: + case EOpConstructIVec2: + case EOpConstructIVec3: + case EOpConstructIVec4: + case EOpConstructUVec2: + case EOpConstructUVec3: + case EOpConstructUVec4: +#ifndef GLSLANG_WEB + case EOpConstructUint8: + case EOpConstructInt16: + case EOpConstructUint16: + case EOpConstructInt64: + case EOpConstructUint64: + case EOpConstructI8Vec2: + case EOpConstructI8Vec3: + case EOpConstructI8Vec4: + case EOpConstructU8Vec2: + case EOpConstructU8Vec3: + case EOpConstructU8Vec4: + case EOpConstructI16Vec2: + case EOpConstructI16Vec3: + case EOpConstructI16Vec4: + case EOpConstructU16Vec2: + case EOpConstructU16Vec3: + case EOpConstructU16Vec4: + case EOpConstructI64Vec2: + case EOpConstructI64Vec3: + case EOpConstructI64Vec4: + case EOpConstructU64Vec2: + case EOpConstructU64Vec3: + case EOpConstructU64Vec4: +#endif + // This was the list of valid ones, if they aren't converting from float + // and aren't making an array. + makeSpecConst = ! floatArgument && ! type.isArray(); + break; + default: + // anything else wasn't white-listed in the spec as a conversion + makeSpecConst = false; + break; + } + } else + makeSpecConst = false; + + if (makeSpecConst) + type.getQualifier().makeSpecConstant(); + else if (specConstType) + type.getQualifier().makeTemporary(); + else + type.getQualifier().storage = EvqConst; + } + + if (type.isArray()) { + if (function.getParamCount() == 0) { + error(loc, "array constructor must have at least one argument", "constructor", ""); + return true; + } + + if (type.isUnsizedArray()) { + // auto adapt the constructor type to the number of arguments + type.changeOuterArraySize(function.getParamCount()); + } else if (type.getOuterArraySize() != function.getParamCount()) { + error(loc, "array constructor needs one argument per array element", "constructor", ""); + return true; + } + + if (type.isArrayOfArrays()) { + // Types have to match, but we're still making the type. + // Finish making the type, and the comparison is done later + // when checking for conversion. + TArraySizes& arraySizes = *type.getArraySizes(); + + // At least the dimensionalities have to match. + if (! function[0].type->isArray() || + arraySizes.getNumDims() != function[0].type->getArraySizes()->getNumDims() + 1) { + error(loc, "array constructor argument not correct type to construct array element", "constructor", ""); + return true; + } + + if (arraySizes.isInnerUnsized()) { + // "Arrays of arrays ..., and the size for any dimension is optional" + // That means we need to adopt (from the first argument) the other array sizes into the type. + for (int d = 1; d < arraySizes.getNumDims(); ++d) { + if (arraySizes.getDimSize(d) == UnsizedArraySize) { + arraySizes.setDimSize(d, function[0].type->getArraySizes()->getDimSize(d - 1)); + } + } + } + } + } + + if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) { + error(loc, "constructing non-array constituent from array argument", "constructor", ""); + return true; + } + + if (matrixInMatrix && ! type.isArray()) { + profileRequires(loc, ENoProfile, 120, nullptr, "constructing matrix from matrix"); + + // "If a matrix argument is given to a matrix constructor, + // it is a compile-time error to have any other arguments." + if (function.getParamCount() != 1) + error(loc, "matrix constructed from matrix can only have one argument", "constructor", ""); + return false; + } + + if (overFull) { + error(loc, "too many arguments", "constructor", ""); + return true; + } + + if (op == EOpConstructStruct && ! type.isArray() && (int)type.getStruct()->size() != function.getParamCount()) { + error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", ""); + return true; + } + + if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) || + (op == EOpConstructStruct && size < type.computeNumComponents())) { + error(loc, "not enough data provided for construction", "constructor", ""); + return true; + } + + if (type.isCoopMat() && function.getParamCount() != 1) { + error(loc, "wrong number of arguments", "constructor", ""); + return true; + } + if (type.isCoopMat() && + !(function[0].type->isScalar() || function[0].type->isCoopMat())) { + error(loc, "Cooperative matrix constructor argument must be scalar or cooperative matrix", "constructor", ""); + return true; + } + + TIntermTyped* typed = node->getAsTyped(); + if (typed == nullptr) { + error(loc, "constructor argument does not have a type", "constructor", ""); + return true; + } + if (op != EOpConstructStruct && op != EOpConstructNonuniform && typed->getBasicType() == EbtSampler) { + error(loc, "cannot convert a sampler", "constructor", ""); + return true; + } + if (op != EOpConstructStruct && typed->isAtomic()) { + error(loc, "cannot convert an atomic_uint", "constructor", ""); + return true; + } + if (typed->getBasicType() == EbtVoid) { + error(loc, "cannot convert a void", "constructor", ""); + return true; + } + + return false; +} + +// Verify all the correct semantics for constructing a combined texture/sampler. +// Return true if the semantics are incorrect. +bool TParseContext::constructorTextureSamplerError(const TSourceLoc& loc, const TFunction& function) +{ + TString constructorName = function.getType().getBasicTypeString(); // TODO: performance: should not be making copy; interface needs to change + const char* token = constructorName.c_str(); + + // exactly two arguments needed + if (function.getParamCount() != 2) { + error(loc, "sampler-constructor requires two arguments", token, ""); + return true; + } + + // For now, not allowing arrayed constructors, the rest of this function + // is set up to allow them, if this test is removed: + if (function.getType().isArray()) { + error(loc, "sampler-constructor cannot make an array of samplers", token, ""); + return true; + } + + // first argument + // * the constructor's first argument must be a texture type + // * the dimensionality (1D, 2D, 3D, Cube, Rect, Buffer, MS, and Array) + // of the texture type must match that of the constructed sampler type + // (that is, the suffixes of the type of the first argument and the + // type of the constructor will be spelled the same way) + if (function[0].type->getBasicType() != EbtSampler || + ! function[0].type->getSampler().isTexture() || + function[0].type->isArray()) { + error(loc, "sampler-constructor first argument must be a scalar *texture* type", token, ""); + return true; + } + // simulate the first argument's impact on the result type, so it can be compared with the encapsulated operator!=() + TSampler texture = function.getType().getSampler(); + texture.setCombined(false); + texture.shadow = false; + if (texture != function[0].type->getSampler()) { + error(loc, "sampler-constructor first argument must be a *texture* type" + " matching the dimensionality and sampled type of the constructor", token, ""); + return true; + } + + // second argument + // * the constructor's second argument must be a scalar of type + // *sampler* or *samplerShadow* + if ( function[1].type->getBasicType() != EbtSampler || + ! function[1].type->getSampler().isPureSampler() || + function[1].type->isArray()) { + error(loc, "sampler-constructor second argument must be a scalar sampler or samplerShadow", token, ""); + return true; + } + + return false; +} + +// Checks to see if a void variable has been declared and raise an error message for such a case +// +// returns true in case of an error +// +bool TParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType) +{ + if (basicType == EbtVoid) { + error(loc, "illegal use of type 'void'", identifier.c_str(), ""); + return true; + } + + return false; +} + +// Checks to see if the node (for the expression) contains a scalar boolean expression or not +void TParseContext::boolCheck(const TSourceLoc& loc, const TIntermTyped* type) +{ + if (type->getBasicType() != EbtBool || type->isArray() || type->isMatrix() || type->isVector()) + error(loc, "boolean expression expected", "", ""); +} + +// This function checks to see if the node (for the expression) contains a scalar boolean expression or not +void TParseContext::boolCheck(const TSourceLoc& loc, const TPublicType& pType) +{ + if (pType.basicType != EbtBool || pType.arraySizes || pType.matrixCols > 1 || (pType.vectorSize > 1)) + error(loc, "boolean expression expected", "", ""); +} + +void TParseContext::samplerCheck(const TSourceLoc& loc, const TType& type, const TString& identifier, TIntermTyped* /*initializer*/) +{ + // Check that the appropriate extension is enabled if external sampler is used. + // There are two extensions. The correct one must be used based on GLSL version. + if (type.getBasicType() == EbtSampler && type.getSampler().isExternal()) { + if (version < 300) { + requireExtensions(loc, 1, &E_GL_OES_EGL_image_external, "samplerExternalOES"); + } else { + requireExtensions(loc, 1, &E_GL_OES_EGL_image_external_essl3, "samplerExternalOES"); + } + } + if (type.getSampler().isYuv()) { + requireExtensions(loc, 1, &E_GL_EXT_YUV_target, "__samplerExternal2DY2YEXT"); + } + + if (type.getQualifier().storage == EvqUniform) + return; + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtSampler)) + error(loc, "non-uniform struct contains a sampler or image:", type.getBasicTypeString().c_str(), identifier.c_str()); + else if (type.getBasicType() == EbtSampler && type.getQualifier().storage != EvqUniform) { + // non-uniform sampler + // not yet: okay if it has an initializer + // if (! initializer) + error(loc, "sampler/image types can only be used in uniform variables or function parameters:", type.getBasicTypeString().c_str(), identifier.c_str()); + } +} + +#ifndef GLSLANG_WEB + +void TParseContext::atomicUintCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) +{ + if (type.getQualifier().storage == EvqUniform) + return; + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtAtomicUint)) + error(loc, "non-uniform struct contains an atomic_uint:", type.getBasicTypeString().c_str(), identifier.c_str()); + else if (type.getBasicType() == EbtAtomicUint && type.getQualifier().storage != EvqUniform) + error(loc, "atomic_uints can only be used in uniform variables or function parameters:", type.getBasicTypeString().c_str(), identifier.c_str()); +} + +void TParseContext::accStructCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) +{ + if (type.getQualifier().storage == EvqUniform) + return; + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtAccStruct)) + error(loc, "non-uniform struct contains an accelerationStructureNV:", type.getBasicTypeString().c_str(), identifier.c_str()); + else if (type.getBasicType() == EbtAccStruct && type.getQualifier().storage != EvqUniform) + error(loc, "accelerationStructureNV can only be used in uniform variables or function parameters:", + type.getBasicTypeString().c_str(), identifier.c_str()); + +} + +#endif // GLSLANG_WEB + +void TParseContext::transparentOpaqueCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) +{ + if (parsingBuiltins) + return; + + if (type.getQualifier().storage != EvqUniform) + return; + + if (type.containsNonOpaque()) { + // Vulkan doesn't allow transparent uniforms outside of blocks + if (spvVersion.vulkan > 0) + vulkanRemoved(loc, "non-opaque uniforms outside a block"); + // OpenGL wants locations on these (unless they are getting automapped) + //if (spvVersion.openGl > 0 && !type.getQualifier().hasLocation() && !intermediate.getAutoMapLocations()) + // error(loc, "non-opaque uniform variables need a layout(location=L)", identifier.c_str(), ""); + } +} + +// +// Qualifier checks knowing the qualifier and that it is a member of a struct/block. +// +void TParseContext::memberQualifierCheck(glslang::TPublicType& publicType) +{ + globalQualifierFixCheck(publicType.loc, publicType.qualifier); + checkNoShaderLayouts(publicType.loc, publicType.shaderQualifiers); + if (publicType.qualifier.isNonUniform()) { + error(publicType.loc, "not allowed on block or structure members", "nonuniformEXT", ""); + publicType.qualifier.nonUniform = false; + } +} + +// +// Check/fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level. +// +void TParseContext::globalQualifierFixCheck(const TSourceLoc& loc, TQualifier& qualifier) +{ + bool nonuniformOkay = false; + + // move from parameter/unknown qualifiers to pipeline in/out qualifiers + switch (qualifier.storage) { + case EvqIn: + profileRequires(loc, ENoProfile, 130, nullptr, "in for stage inputs"); + profileRequires(loc, EEsProfile, 300, nullptr, "in for stage inputs"); + qualifier.storage = EvqVaryingIn; + nonuniformOkay = true; + break; + case EvqOut: + profileRequires(loc, ENoProfile, 130, nullptr, "out for stage outputs"); + profileRequires(loc, EEsProfile, 300, nullptr, "out for stage outputs"); + qualifier.storage = EvqVaryingOut; + break; + case EvqInOut: + qualifier.storage = EvqVaryingIn; + error(loc, "cannot use 'inout' at global scope", "", ""); + break; + case EvqGlobal: + case EvqTemporary: + nonuniformOkay = true; + break; + default: + break; + } + + if (!nonuniformOkay && qualifier.isNonUniform()) + error(loc, "for non-parameter, can only apply to 'in' or no storage qualifier", "nonuniformEXT", ""); + + invariantCheck(loc, qualifier); +} + +// +// Check a full qualifier and type (no variable yet) at global level. +// +void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQualifier& qualifier, const TPublicType& publicType) +{ + if (! symbolTable.atGlobalLevel()) + return; + + if (!(publicType.userDef && publicType.userDef->isReference())) { + if (qualifier.isMemoryQualifierImageAndSSBOOnly() && ! publicType.isImage() && publicType.qualifier.storage != EvqBuffer) { + error(loc, "memory qualifiers cannot be used on this type", "", ""); + } else if (qualifier.isMemory() && (publicType.basicType != EbtSampler) && !publicType.qualifier.isUniformOrBuffer()) { + error(loc, "memory qualifiers cannot be used on this type", "", ""); + } + } + + if (qualifier.storage == EvqBuffer && + publicType.basicType != EbtBlock && + !qualifier.hasBufferReference()) + error(loc, "buffers can be declared only as blocks", "buffer", ""); + + if (qualifier.storage != EvqVaryingIn && publicType.basicType == EbtDouble && + extensionTurnedOn(E_GL_ARB_vertex_attrib_64bit) && language == EShLangVertex && + version < 400) { + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 410, E_GL_ARB_gpu_shader_fp64, "vertex-shader `double` type"); + } + if (qualifier.storage != EvqVaryingIn && qualifier.storage != EvqVaryingOut) + return; + + if (publicType.shaderQualifiers.hasBlendEquation()) + error(loc, "can only be applied to a standalone 'out'", "blend equation", ""); + + // now, knowing it is a shader in/out, do all the in/out semantic checks + + if (publicType.basicType == EbtBool && !parsingBuiltins) { + error(loc, "cannot be bool", GetStorageQualifierString(qualifier.storage), ""); + return; + } + + if (isTypeInt(publicType.basicType) || publicType.basicType == EbtDouble) + profileRequires(loc, EEsProfile, 300, nullptr, "shader input/output"); + + if (!qualifier.flat && !qualifier.isExplicitInterpolation() && !qualifier.isPervertexNV()) { + if (isTypeInt(publicType.basicType) || + publicType.basicType == EbtDouble || + (publicType.userDef && ( publicType.userDef->containsBasicType(EbtInt) + || publicType.userDef->containsBasicType(EbtUint) + || publicType.userDef->contains16BitInt() + || publicType.userDef->contains8BitInt() + || publicType.userDef->contains64BitInt() + || publicType.userDef->containsDouble()))) { + if (qualifier.storage == EvqVaryingIn && language == EShLangFragment) + error(loc, "must be qualified as flat", TType::getBasicString(publicType.basicType), GetStorageQualifierString(qualifier.storage)); + else if (qualifier.storage == EvqVaryingOut && language == EShLangVertex && version == 300) + error(loc, "must be qualified as flat", TType::getBasicString(publicType.basicType), GetStorageQualifierString(qualifier.storage)); + } + } + + if (qualifier.isPatch() && qualifier.isInterpolation()) + error(loc, "cannot use interpolation qualifiers with patch", "patch", ""); + + if (qualifier.isTaskMemory() && publicType.basicType != EbtBlock) + error(loc, "taskNV variables can be declared only as blocks", "taskNV", ""); + + if (qualifier.storage == EvqVaryingIn) { + switch (language) { + case EShLangVertex: + if (publicType.basicType == EbtStruct) { + error(loc, "cannot be a structure or array", GetStorageQualifierString(qualifier.storage), ""); + return; + } + if (publicType.arraySizes) { + requireProfile(loc, ~EEsProfile, "vertex input arrays"); + profileRequires(loc, ENoProfile, 150, nullptr, "vertex input arrays"); + } + if (publicType.basicType == EbtDouble) + profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_vertex_attrib_64bit, "vertex-shader `double` type input"); + if (qualifier.isAuxiliary() || qualifier.isInterpolation() || qualifier.isMemory() || qualifier.invariant) + error(loc, "vertex input cannot be further qualified", "", ""); + break; + case EShLangFragment: + if (publicType.userDef) { + profileRequires(loc, EEsProfile, 300, nullptr, "fragment-shader struct input"); + profileRequires(loc, ~EEsProfile, 150, nullptr, "fragment-shader struct input"); + if (publicType.userDef->containsStructure()) + requireProfile(loc, ~EEsProfile, "fragment-shader struct input containing structure"); + if (publicType.userDef->containsArray()) + requireProfile(loc, ~EEsProfile, "fragment-shader struct input containing an array"); + } + break; + case EShLangCompute: + if (! symbolTable.atBuiltInLevel()) + error(loc, "global storage input qualifier cannot be used in a compute shader", "in", ""); + break; +#ifndef GLSLANG_WEB + case EShLangTessControl: + if (qualifier.patch) + error(loc, "can only use on output in tessellation-control shader", "patch", ""); + break; +#endif + default: + break; + } + } else { + // qualifier.storage == EvqVaryingOut + switch (language) { + case EShLangVertex: + if (publicType.userDef) { + profileRequires(loc, EEsProfile, 300, nullptr, "vertex-shader struct output"); + profileRequires(loc, ~EEsProfile, 150, nullptr, "vertex-shader struct output"); + if (publicType.userDef->containsStructure()) + requireProfile(loc, ~EEsProfile, "vertex-shader struct output containing structure"); + if (publicType.userDef->containsArray()) + requireProfile(loc, ~EEsProfile, "vertex-shader struct output containing an array"); + } + + break; + case EShLangFragment: + profileRequires(loc, EEsProfile, 300, nullptr, "fragment shader output"); + if (publicType.basicType == EbtStruct) { + error(loc, "cannot be a structure", GetStorageQualifierString(qualifier.storage), ""); + return; + } + if (publicType.matrixRows > 0) { + error(loc, "cannot be a matrix", GetStorageQualifierString(qualifier.storage), ""); + return; + } + if (qualifier.isAuxiliary()) + error(loc, "can't use auxiliary qualifier on a fragment output", "centroid/sample/patch", ""); + if (qualifier.isInterpolation()) + error(loc, "can't use interpolation qualifier on a fragment output", "flat/smooth/noperspective", ""); + if (publicType.basicType == EbtDouble || publicType.basicType == EbtInt64 || publicType.basicType == EbtUint64) + error(loc, "cannot contain a double, int64, or uint64", GetStorageQualifierString(qualifier.storage), ""); + break; + + case EShLangCompute: + error(loc, "global storage output qualifier cannot be used in a compute shader", "out", ""); + break; +#ifndef GLSLANG_WEB + case EShLangTessEvaluation: + if (qualifier.patch) + error(loc, "can only use on input in tessellation-evaluation shader", "patch", ""); + break; +#endif + default: + break; + } + } +} + +// +// Merge characteristics of the 'src' qualifier into the 'dst'. +// If there is duplication, issue error messages, unless 'force' +// is specified, which means to just override default settings. +// +// Also, when force is false, it will be assumed that 'src' follows +// 'dst', for the purpose of error checking order for versions +// that require specific orderings of qualifiers. +// +void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, const TQualifier& src, bool force) +{ + // Multiple auxiliary qualifiers (mostly done later by 'individual qualifiers') + if (src.isAuxiliary() && dst.isAuxiliary()) + error(loc, "can only have one auxiliary qualifier (centroid, patch, and sample)", "", ""); + + // Multiple interpolation qualifiers (mostly done later by 'individual qualifiers') + if (src.isInterpolation() && dst.isInterpolation()) + error(loc, "can only have one interpolation qualifier (flat, smooth, noperspective, __explicitInterpAMD)", "", ""); + + // Ordering + if (! force && ((!isEsProfile() && version < 420) || + (isEsProfile() && version < 310)) + && ! extensionTurnedOn(E_GL_ARB_shading_language_420pack)) { + // non-function parameters + if (src.isNoContraction() && (dst.invariant || dst.isInterpolation() || dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone)) + error(loc, "precise qualifier must appear first", "", ""); + if (src.invariant && (dst.isInterpolation() || dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone)) + error(loc, "invariant qualifier must appear before interpolation, storage, and precision qualifiers ", "", ""); + else if (src.isInterpolation() && (dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone)) + error(loc, "interpolation qualifiers must appear before storage and precision qualifiers", "", ""); + else if (src.isAuxiliary() && (dst.storage != EvqTemporary || dst.precision != EpqNone)) + error(loc, "Auxiliary qualifiers (centroid, patch, and sample) must appear before storage and precision qualifiers", "", ""); + else if (src.storage != EvqTemporary && (dst.precision != EpqNone)) + error(loc, "precision qualifier must appear as last qualifier", "", ""); + + // function parameters + if (src.isNoContraction() && (dst.storage == EvqConst || dst.storage == EvqIn || dst.storage == EvqOut)) + error(loc, "precise qualifier must appear first", "", ""); + if (src.storage == EvqConst && (dst.storage == EvqIn || dst.storage == EvqOut)) + error(loc, "in/out must appear before const", "", ""); + } + + // Storage qualification + if (dst.storage == EvqTemporary || dst.storage == EvqGlobal) + dst.storage = src.storage; + else if ((dst.storage == EvqIn && src.storage == EvqOut) || + (dst.storage == EvqOut && src.storage == EvqIn)) + dst.storage = EvqInOut; + else if ((dst.storage == EvqIn && src.storage == EvqConst) || + (dst.storage == EvqConst && src.storage == EvqIn)) + dst.storage = EvqConstReadOnly; + else if (src.storage != EvqTemporary && + src.storage != EvqGlobal) + error(loc, "too many storage qualifiers", GetStorageQualifierString(src.storage), ""); + + // Precision qualifiers + if (! force && src.precision != EpqNone && dst.precision != EpqNone) + error(loc, "only one precision qualifier allowed", GetPrecisionQualifierString(src.precision), ""); + if (dst.precision == EpqNone || (force && src.precision != EpqNone)) + dst.precision = src.precision; + +#ifndef GLSLANG_WEB + if (!force && ((src.coherent && (dst.devicecoherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.subgroupcoherent || dst.shadercallcoherent)) || + (src.devicecoherent && (dst.coherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.subgroupcoherent || dst.shadercallcoherent)) || + (src.queuefamilycoherent && (dst.coherent || dst.devicecoherent || dst.workgroupcoherent || dst.subgroupcoherent || dst.shadercallcoherent)) || + (src.workgroupcoherent && (dst.coherent || dst.devicecoherent || dst.queuefamilycoherent || dst.subgroupcoherent || dst.shadercallcoherent)) || + (src.subgroupcoherent && (dst.coherent || dst.devicecoherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.shadercallcoherent)) || + (src.shadercallcoherent && (dst.coherent || dst.devicecoherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.subgroupcoherent)))) { + error(loc, "only one coherent/devicecoherent/queuefamilycoherent/workgroupcoherent/subgroupcoherent/shadercallcoherent qualifier allowed", + GetPrecisionQualifierString(src.precision), ""); + } +#endif + // Layout qualifiers + mergeObjectLayoutQualifiers(dst, src, false); + + // individual qualifiers + bool repeated = false; + #define MERGE_SINGLETON(field) repeated |= dst.field && src.field; dst.field |= src.field; + MERGE_SINGLETON(invariant); + MERGE_SINGLETON(centroid); + MERGE_SINGLETON(smooth); + MERGE_SINGLETON(flat); + MERGE_SINGLETON(specConstant); +#ifndef GLSLANG_WEB + MERGE_SINGLETON(noContraction); + MERGE_SINGLETON(nopersp); + MERGE_SINGLETON(explicitInterp); + MERGE_SINGLETON(perPrimitiveNV); + MERGE_SINGLETON(perViewNV); + MERGE_SINGLETON(perTaskNV); + MERGE_SINGLETON(patch); + MERGE_SINGLETON(sample); + MERGE_SINGLETON(coherent); + MERGE_SINGLETON(devicecoherent); + MERGE_SINGLETON(queuefamilycoherent); + MERGE_SINGLETON(workgroupcoherent); + MERGE_SINGLETON(subgroupcoherent); + MERGE_SINGLETON(shadercallcoherent); + MERGE_SINGLETON(nonprivate); + MERGE_SINGLETON(volatil); + MERGE_SINGLETON(restrict); + MERGE_SINGLETON(readonly); + MERGE_SINGLETON(writeonly); + MERGE_SINGLETON(nonUniform); +#endif + + if (repeated) + error(loc, "replicated qualifiers", "", ""); +} + +void TParseContext::setDefaultPrecision(const TSourceLoc& loc, TPublicType& publicType, TPrecisionQualifier qualifier) +{ + TBasicType basicType = publicType.basicType; + + if (basicType == EbtSampler) { + defaultSamplerPrecision[computeSamplerTypeIndex(publicType.sampler)] = qualifier; + + return; // all is well + } + + if (basicType == EbtInt || basicType == EbtFloat) { + if (publicType.isScalar()) { + defaultPrecision[basicType] = qualifier; + if (basicType == EbtInt) { + defaultPrecision[EbtUint] = qualifier; + precisionManager.explicitIntDefaultSeen(); + } else + precisionManager.explicitFloatDefaultSeen(); + + return; // all is well + } + } + + if (basicType == EbtAtomicUint) { + if (qualifier != EpqHigh) + error(loc, "can only apply highp to atomic_uint", "precision", ""); + + return; + } + + error(loc, "cannot apply precision statement to this type; use 'float', 'int' or a sampler type", TType::getBasicString(basicType), ""); +} + +// used to flatten the sampler type space into a single dimension +// correlates with the declaration of defaultSamplerPrecision[] +int TParseContext::computeSamplerTypeIndex(TSampler& sampler) +{ + int arrayIndex = sampler.arrayed ? 1 : 0; + int shadowIndex = sampler.shadow ? 1 : 0; + int externalIndex = sampler.isExternal() ? 1 : 0; + int imageIndex = sampler.isImageClass() ? 1 : 0; + int msIndex = sampler.isMultiSample() ? 1 : 0; + int videoIndex = sampler.isVideo() ? 1 : 0; + + int flattened = EsdNumDims * (EbtNumTypes * (2 * (2 * (2 * (2 * (2 * arrayIndex + msIndex) + imageIndex) + shadowIndex) + + externalIndex) + videoIndex) + sampler.type) + sampler.dim; + assert(flattened < maxSamplerIndex); + + return flattened; +} + +TPrecisionQualifier TParseContext::getDefaultPrecision(TPublicType& publicType) +{ + if (publicType.basicType == EbtSampler) + return defaultSamplerPrecision[computeSamplerTypeIndex(publicType.sampler)]; + else + return defaultPrecision[publicType.basicType]; +} + +void TParseContext::precisionQualifierCheck(const TSourceLoc& loc, TBasicType baseType, TQualifier& qualifier) +{ + // Built-in symbols are allowed some ambiguous precisions, to be pinned down + // later by context. + if (! obeyPrecisionQualifiers() || parsingBuiltins) + return; + +#ifndef GLSLANG_WEB + if (baseType == EbtAtomicUint && qualifier.precision != EpqNone && qualifier.precision != EpqHigh) + error(loc, "atomic counters can only be highp", "atomic_uint", ""); +#endif + + if (baseType == EbtFloat || baseType == EbtUint || baseType == EbtInt || baseType == EbtSampler || baseType == EbtAtomicUint) { + if (qualifier.precision == EpqNone) { + if (relaxedErrors()) + warn(loc, "type requires declaration of default precision qualifier", TType::getBasicString(baseType), "substituting 'mediump'"); + else + error(loc, "type requires declaration of default precision qualifier", TType::getBasicString(baseType), ""); + qualifier.precision = EpqMedium; + defaultPrecision[baseType] = EpqMedium; + } + } else if (qualifier.precision != EpqNone) + error(loc, "type cannot have precision qualifier", TType::getBasicString(baseType), ""); +} + +void TParseContext::parameterTypeCheck(const TSourceLoc& loc, TStorageQualifier qualifier, const TType& type) +{ + if ((qualifier == EvqOut || qualifier == EvqInOut) && type.isOpaque()) + error(loc, "samplers and atomic_uints cannot be output parameters", type.getBasicTypeString().c_str(), ""); + if (!parsingBuiltins && type.contains16BitFloat()) + requireFloat16Arithmetic(loc, type.getBasicTypeString().c_str(), "float16 types can only be in uniform block or buffer storage"); + if (!parsingBuiltins && type.contains16BitInt()) + requireInt16Arithmetic(loc, type.getBasicTypeString().c_str(), "(u)int16 types can only be in uniform block or buffer storage"); + if (!parsingBuiltins && type.contains8BitInt()) + requireInt8Arithmetic(loc, type.getBasicTypeString().c_str(), "(u)int8 types can only be in uniform block or buffer storage"); +} + +bool TParseContext::containsFieldWithBasicType(const TType& type, TBasicType basicType) +{ + if (type.getBasicType() == basicType) + return true; + + if (type.getBasicType() == EbtStruct) { + const TTypeList& structure = *type.getStruct(); + for (unsigned int i = 0; i < structure.size(); ++i) { + if (containsFieldWithBasicType(*structure[i].type, basicType)) + return true; + } + } + + return false; +} + +// +// Do size checking for an array type's size. +// +void TParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair, const char *sizeType) +{ + bool isConst = false; + sizePair.node = nullptr; + + int size = 1; + + TIntermConstantUnion* constant = expr->getAsConstantUnion(); + if (constant) { + // handle true (non-specialization) constant + size = constant->getConstArray()[0].getIConst(); + isConst = true; + } else { + // see if it's a specialization constant instead + if (expr->getQualifier().isSpecConstant()) { + isConst = true; + sizePair.node = expr; + TIntermSymbol* symbol = expr->getAsSymbolNode(); + if (symbol && symbol->getConstArray().size() > 0) + size = symbol->getConstArray()[0].getIConst(); + } else if (expr->getAsUnaryNode() && + expr->getAsUnaryNode()->getOp() == glslang::EOpArrayLength && + expr->getAsUnaryNode()->getOperand()->getType().isCoopMat()) { + isConst = true; + size = 1; + sizePair.node = expr->getAsUnaryNode(); + } + } + + sizePair.size = size; + + if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) { + error(loc, sizeType, "", "must be a constant integer expression"); + return; + } + + if (size <= 0) { + error(loc, sizeType, "", "must be a positive integer"); + return; + } +} + +// +// See if this qualifier can be an array. +// +// Returns true if there is an error. +// +bool TParseContext::arrayQualifierError(const TSourceLoc& loc, const TQualifier& qualifier) +{ + if (qualifier.storage == EvqConst) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, "const array"); + profileRequires(loc, EEsProfile, 300, nullptr, "const array"); + } + + if (qualifier.storage == EvqVaryingIn && language == EShLangVertex) { + requireProfile(loc, ~EEsProfile, "vertex input arrays"); + profileRequires(loc, ENoProfile, 150, nullptr, "vertex input arrays"); + } + + return false; +} + +// +// See if this qualifier and type combination can be an array. +// Assumes arrayQualifierError() was also called to catch the type-invariant tests. +// +// Returns true if there is an error. +// +bool TParseContext::arrayError(const TSourceLoc& loc, const TType& type) +{ + if (type.getQualifier().storage == EvqVaryingOut && language == EShLangVertex) { + if (type.isArrayOfArrays()) + requireProfile(loc, ~EEsProfile, "vertex-shader array-of-array output"); + else if (type.isStruct()) + requireProfile(loc, ~EEsProfile, "vertex-shader array-of-struct output"); + } + if (type.getQualifier().storage == EvqVaryingIn && language == EShLangFragment) { + if (type.isArrayOfArrays()) + requireProfile(loc, ~EEsProfile, "fragment-shader array-of-array input"); + else if (type.isStruct()) + requireProfile(loc, ~EEsProfile, "fragment-shader array-of-struct input"); + } + if (type.getQualifier().storage == EvqVaryingOut && language == EShLangFragment) { + if (type.isArrayOfArrays()) + requireProfile(loc, ~EEsProfile, "fragment-shader array-of-array output"); + } + + return false; +} + +// +// Require array to be completely sized +// +void TParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes) +{ + if (!parsingBuiltins && arraySizes.hasUnsized()) + error(loc, "array size required", "", ""); +} + +void TParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type) +{ + const TTypeList& structure = *type.getStruct(); + for (int m = 0; m < (int)structure.size(); ++m) { + const TType& member = *structure[m].type; + if (member.isArray()) + arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes()); + } +} + +void TParseContext::arraySizesCheck(const TSourceLoc& loc, const TQualifier& qualifier, TArraySizes* arraySizes, + const TIntermTyped* initializer, bool lastMember) +{ + assert(arraySizes); + + // always allow special built-in ins/outs sized to topologies + if (parsingBuiltins) + return; + + // initializer must be a sized array, in which case + // allow the initializer to set any unknown array sizes + if (initializer != nullptr) { + if (initializer->getType().isUnsizedArray()) + error(loc, "array initializer must be sized", "[]", ""); + return; + } + + // No environment allows any non-outer-dimension to be implicitly sized + if (arraySizes->isInnerUnsized()) { + error(loc, "only outermost dimension of an array of arrays can be implicitly sized", "[]", ""); + arraySizes->clearInnerUnsized(); + } + + if (arraySizes->isInnerSpecialization() && + (qualifier.storage != EvqTemporary && qualifier.storage != EvqGlobal && qualifier.storage != EvqShared && qualifier.storage != EvqConst)) + error(loc, "only outermost dimension of an array of arrays can be a specialization constant", "[]", ""); + +#ifndef GLSLANG_WEB + + // desktop always allows outer-dimension-unsized variable arrays, + if (!isEsProfile()) + return; + + // for ES, if size isn't coming from an initializer, it has to be explicitly declared now, + // with very few exceptions + + // implicitly-sized io exceptions: + switch (language) { + case EShLangGeometry: + if (qualifier.storage == EvqVaryingIn) + if ((isEsProfile() && version >= 320) || + extensionsTurnedOn(Num_AEP_geometry_shader, AEP_geometry_shader)) + return; + break; + case EShLangTessControl: + if ( qualifier.storage == EvqVaryingIn || + (qualifier.storage == EvqVaryingOut && ! qualifier.isPatch())) + if ((isEsProfile() && version >= 320) || + extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader)) + return; + break; + case EShLangTessEvaluation: + if ((qualifier.storage == EvqVaryingIn && ! qualifier.isPatch()) || + qualifier.storage == EvqVaryingOut) + if ((isEsProfile() && version >= 320) || + extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader)) + return; + break; + case EShLangMeshNV: + if (qualifier.storage == EvqVaryingOut) + if ((isEsProfile() && version >= 320) || + extensionTurnedOn(E_GL_NV_mesh_shader)) + return; + break; + default: + break; + } + +#endif + + // last member of ssbo block exception: + if (qualifier.storage == EvqBuffer && lastMember) + return; + + arraySizeRequiredCheck(loc, *arraySizes); +} + +void TParseContext::arrayOfArrayVersionCheck(const TSourceLoc& loc, const TArraySizes* sizes) +{ + if (sizes == nullptr || sizes->getNumDims() == 1) + return; + + const char* feature = "arrays of arrays"; + + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, nullptr, feature); +} + +// +// Do all the semantic checking for declaring or redeclaring an array, with and +// without a size, and make the right changes to the symbol table. +// +void TParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type, TSymbol*& symbol) +{ + if (symbol == nullptr) { + bool currentScope; + symbol = symbolTable.find(identifier, nullptr, ¤tScope); + + if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) { + // bad shader (errors already reported) trying to redeclare a built-in name as an array + symbol = nullptr; + return; + } + if (symbol == nullptr || ! currentScope) { + // + // Successfully process a new definition. + // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations) + // + symbol = new TVariable(&identifier, type); + symbolTable.insert(*symbol); + if (symbolTable.atGlobalLevel()) + trackLinkage(*symbol); + +#ifndef GLSLANG_WEB + if (! symbolTable.atBuiltInLevel()) { + if (isIoResizeArray(type)) { + ioArraySymbolResizeList.push_back(symbol); + checkIoArraysConsistency(loc, true); + } else + fixIoArraySize(loc, symbol->getWritableType()); + } +#endif + + return; + } + if (symbol->getAsAnonMember()) { + error(loc, "cannot redeclare a user-block member array", identifier.c_str(), ""); + symbol = nullptr; + return; + } + } + + // + // Process a redeclaration. + // + + if (symbol == nullptr) { + error(loc, "array variable name expected", identifier.c_str(), ""); + return; + } + + // redeclareBuiltinVariable() should have already done the copyUp() + TType& existingType = symbol->getWritableType(); + + if (! existingType.isArray()) { + error(loc, "redeclaring non-array as array", identifier.c_str(), ""); + return; + } + + if (! existingType.sameElementType(type)) { + error(loc, "redeclaration of array with a different element type", identifier.c_str(), ""); + return; + } + + if (! existingType.sameInnerArrayness(type)) { + error(loc, "redeclaration of array with a different array dimensions or sizes", identifier.c_str(), ""); + return; + } + +#ifndef GLSLANG_WEB + if (existingType.isSizedArray()) { + // be more leniant for input arrays to geometry shaders and tessellation control outputs, where the redeclaration is the same size + if (! (isIoResizeArray(type) && existingType.getOuterArraySize() == type.getOuterArraySize())) + error(loc, "redeclaration of array with size", identifier.c_str(), ""); + return; + } + + arrayLimitCheck(loc, identifier, type.getOuterArraySize()); + + existingType.updateArraySizes(type); + + if (isIoResizeArray(type)) + checkIoArraysConsistency(loc); +#endif +} + +#ifndef GLSLANG_WEB + +// Policy and error check for needing a runtime sized array. +void TParseContext::checkRuntimeSizable(const TSourceLoc& loc, const TIntermTyped& base) +{ + // runtime length implies runtime sizeable, so no problem + if (isRuntimeLength(base)) + return; + + // Check for last member of a bufferreference type, which is runtime sizeable + // but doesn't support runtime length + if (base.getType().getQualifier().storage == EvqBuffer) { + const TIntermBinary* binary = base.getAsBinaryNode(); + if (binary != nullptr && + binary->getOp() == EOpIndexDirectStruct && + binary->getLeft()->isReference()) { + + const int index = binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + const int memberCount = (int)binary->getLeft()->getType().getReferentType()->getStruct()->size(); + if (index == memberCount - 1) + return; + } + } + + // check for additional things allowed by GL_EXT_nonuniform_qualifier + if (base.getBasicType() == EbtSampler || base.getBasicType() == EbtAccStruct || base.getBasicType() == EbtRayQuery || + (base.getBasicType() == EbtBlock && base.getType().getQualifier().isUniformOrBuffer())) + requireExtensions(loc, 1, &E_GL_EXT_nonuniform_qualifier, "variable index"); + else + error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable"); +} + +// Policy decision for whether a run-time .length() is allowed. +bool TParseContext::isRuntimeLength(const TIntermTyped& base) const +{ + if (base.getType().getQualifier().storage == EvqBuffer) { + // in a buffer block + const TIntermBinary* binary = base.getAsBinaryNode(); + if (binary != nullptr && binary->getOp() == EOpIndexDirectStruct) { + // is it the last member? + const int index = binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + + if (binary->getLeft()->isReference()) + return false; + + const int memberCount = (int)binary->getLeft()->getType().getStruct()->size(); + if (index == memberCount - 1) + return true; + } + } + + return false; +} + +// Check if mesh perviewNV attributes have a view dimension +// and resize it to gl_MaxMeshViewCountNV when implicitly sized. +void TParseContext::checkAndResizeMeshViewDim(const TSourceLoc& loc, TType& type, bool isBlockMember) +{ + // see if member is a per-view attribute + if (!type.getQualifier().isPerView()) + return; + + if ((isBlockMember && type.isArray()) || (!isBlockMember && type.isArrayOfArrays())) { + // since we don't have the maxMeshViewCountNV set during parsing builtins, we hardcode the value. + int maxViewCount = parsingBuiltins ? 4 : resources.maxMeshViewCountNV; + // For block members, outermost array dimension is the view dimension. + // For non-block members, outermost array dimension is the vertex/primitive dimension + // and 2nd outermost is the view dimension. + int viewDim = isBlockMember ? 0 : 1; + int viewDimSize = type.getArraySizes()->getDimSize(viewDim); + + if (viewDimSize != UnsizedArraySize && viewDimSize != maxViewCount) + error(loc, "mesh view output array size must be gl_MaxMeshViewCountNV or implicitly sized", "[]", ""); + else if (viewDimSize == UnsizedArraySize) + type.getArraySizes()->setDimSize(viewDim, maxViewCount); + } + else { + error(loc, "requires a view array dimension", "perviewNV", ""); + } +} + +#endif // GLSLANG_WEB + +// Returns true if the first argument to the #line directive is the line number for the next line. +// +// Desktop, pre-version 3.30: "After processing this directive +// (including its new-line), the implementation will behave as if it is compiling at line number line+1 and +// source string number source-string-number." +// +// Desktop, version 3.30 and later, and ES: "After processing this directive +// (including its new-line), the implementation will behave as if it is compiling at line number line and +// source string number source-string-number. +bool TParseContext::lineDirectiveShouldSetNextLine() const +{ + return isEsProfile() || version >= 330; +} + +// +// Enforce non-initializer type/qualifier rules. +// +void TParseContext::nonInitConstCheck(const TSourceLoc& loc, TString& identifier, TType& type) +{ + // + // Make the qualifier make sense, given that there is not an initializer. + // + if (type.getQualifier().storage == EvqConst || + type.getQualifier().storage == EvqConstReadOnly) { + type.getQualifier().makeTemporary(); + error(loc, "variables with qualifier 'const' must be initialized", identifier.c_str(), ""); + } +} + +// +// See if the identifier is a built-in symbol that can be redeclared, and if so, +// copy the symbol table's read-only built-in variable to the current +// global level, where it can be modified based on the passed in type. +// +// Returns nullptr if no redeclaration took place; meaning a normal declaration still +// needs to occur for it, not necessarily an error. +// +// Returns a redeclared and type-modified variable if a redeclarated occurred. +// +TSymbol* TParseContext::redeclareBuiltinVariable(const TSourceLoc& loc, const TString& identifier, + const TQualifier& qualifier, const TShaderQualifiers& publicType) +{ +#ifndef GLSLANG_WEB + if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel()) + return nullptr; + + bool nonEsRedecls = (!isEsProfile() && (version >= 130 || identifier == "gl_TexCoord")); + bool esRedecls = (isEsProfile() && + (version >= 320 || extensionsTurnedOn(Num_AEP_shader_io_blocks, AEP_shader_io_blocks))); + if (! esRedecls && ! nonEsRedecls) + return nullptr; + + // Special case when using GL_ARB_separate_shader_objects + bool ssoPre150 = false; // means the only reason this variable is redeclared is due to this combination + if (!isEsProfile() && version <= 140 && extensionTurnedOn(E_GL_ARB_separate_shader_objects)) { + if (identifier == "gl_Position" || + identifier == "gl_PointSize" || + identifier == "gl_ClipVertex" || + identifier == "gl_FogFragCoord") + ssoPre150 = true; + } + + // Potentially redeclaring a built-in variable... + + if (ssoPre150 || + (identifier == "gl_FragDepth" && ((nonEsRedecls && version >= 420) || esRedecls)) || + (identifier == "gl_FragCoord" && ((nonEsRedecls && version >= 150) || esRedecls)) || + identifier == "gl_ClipDistance" || + identifier == "gl_CullDistance" || + identifier == "gl_FrontColor" || + identifier == "gl_BackColor" || + identifier == "gl_FrontSecondaryColor" || + identifier == "gl_BackSecondaryColor" || + identifier == "gl_SecondaryColor" || + (identifier == "gl_Color" && language == EShLangFragment) || + (identifier == "gl_FragStencilRefARB" && (nonEsRedecls && version >= 140) + && language == EShLangFragment) || + identifier == "gl_SampleMask" || + identifier == "gl_Layer" || + identifier == "gl_PrimitiveIndicesNV" || + identifier == "gl_TexCoord") { + + // Find the existing symbol, if any. + bool builtIn; + TSymbol* symbol = symbolTable.find(identifier, &builtIn); + + // If the symbol was not found, this must be a version/profile/stage + // that doesn't have it. + if (! symbol) + return nullptr; + + // If it wasn't at a built-in level, then it's already been redeclared; + // that is, this is a redeclaration of a redeclaration; reuse that initial + // redeclaration. Otherwise, make the new one. + if (builtIn) + makeEditable(symbol); + + // Now, modify the type of the copy, as per the type of the current redeclaration. + + TQualifier& symbolQualifier = symbol->getWritableType().getQualifier(); + if (ssoPre150) { + if (intermediate.inIoAccessed(identifier)) + error(loc, "cannot redeclare after use", identifier.c_str(), ""); + if (qualifier.hasLayout()) + error(loc, "cannot apply layout qualifier to", "redeclaration", symbol->getName().c_str()); + if (qualifier.isMemory() || qualifier.isAuxiliary() || (language == EShLangVertex && qualifier.storage != EvqVaryingOut) || + (language == EShLangFragment && qualifier.storage != EvqVaryingIn)) + error(loc, "cannot change storage, memory, or auxiliary qualification of", "redeclaration", symbol->getName().c_str()); + if (! qualifier.smooth) + error(loc, "cannot change interpolation qualification of", "redeclaration", symbol->getName().c_str()); + } else if (identifier == "gl_FrontColor" || + identifier == "gl_BackColor" || + identifier == "gl_FrontSecondaryColor" || + identifier == "gl_BackSecondaryColor" || + identifier == "gl_SecondaryColor" || + identifier == "gl_Color") { + symbolQualifier.flat = qualifier.flat; + symbolQualifier.smooth = qualifier.smooth; + symbolQualifier.nopersp = qualifier.nopersp; + if (qualifier.hasLayout()) + error(loc, "cannot apply layout qualifier to", "redeclaration", symbol->getName().c_str()); + if (qualifier.isMemory() || qualifier.isAuxiliary() || symbol->getType().getQualifier().storage != qualifier.storage) + error(loc, "cannot change storage, memory, or auxiliary qualification of", "redeclaration", symbol->getName().c_str()); + } else if (identifier == "gl_TexCoord" || + identifier == "gl_ClipDistance" || + identifier == "gl_CullDistance") { + if (qualifier.hasLayout() || qualifier.isMemory() || qualifier.isAuxiliary() || + qualifier.nopersp != symbolQualifier.nopersp || qualifier.flat != symbolQualifier.flat || + symbolQualifier.storage != qualifier.storage) + error(loc, "cannot change qualification of", "redeclaration", symbol->getName().c_str()); + } else if (identifier == "gl_FragCoord") { + if (intermediate.inIoAccessed("gl_FragCoord")) + error(loc, "cannot redeclare after use", "gl_FragCoord", ""); + if (qualifier.nopersp != symbolQualifier.nopersp || qualifier.flat != symbolQualifier.flat || + qualifier.isMemory() || qualifier.isAuxiliary()) + error(loc, "can only change layout qualification of", "redeclaration", symbol->getName().c_str()); + if (qualifier.storage != EvqVaryingIn) + error(loc, "cannot change input storage qualification of", "redeclaration", symbol->getName().c_str()); + if (! builtIn && (publicType.pixelCenterInteger != intermediate.getPixelCenterInteger() || + publicType.originUpperLeft != intermediate.getOriginUpperLeft())) + error(loc, "cannot redeclare with different qualification:", "redeclaration", symbol->getName().c_str()); + if (publicType.pixelCenterInteger) + intermediate.setPixelCenterInteger(); + if (publicType.originUpperLeft) + intermediate.setOriginUpperLeft(); + } else if (identifier == "gl_FragDepth") { + if (qualifier.nopersp != symbolQualifier.nopersp || qualifier.flat != symbolQualifier.flat || + qualifier.isMemory() || qualifier.isAuxiliary()) + error(loc, "can only change layout qualification of", "redeclaration", symbol->getName().c_str()); + if (qualifier.storage != EvqVaryingOut) + error(loc, "cannot change output storage qualification of", "redeclaration", symbol->getName().c_str()); + if (publicType.layoutDepth != EldNone) { + if (intermediate.inIoAccessed("gl_FragDepth")) + error(loc, "cannot redeclare after use", "gl_FragDepth", ""); + if (! intermediate.setDepth(publicType.layoutDepth)) + error(loc, "all redeclarations must use the same depth layout on", "redeclaration", symbol->getName().c_str()); + } + } + else if ( + identifier == "gl_PrimitiveIndicesNV" || + identifier == "gl_FragStencilRefARB") { + if (qualifier.hasLayout()) + error(loc, "cannot apply layout qualifier to", "redeclaration", symbol->getName().c_str()); + if (qualifier.storage != EvqVaryingOut) + error(loc, "cannot change output storage qualification of", "redeclaration", symbol->getName().c_str()); + } + else if (identifier == "gl_SampleMask") { + if (!publicType.layoutOverrideCoverage) { + error(loc, "redeclaration only allowed for override_coverage layout", "redeclaration", symbol->getName().c_str()); + } + intermediate.setLayoutOverrideCoverage(); + } + else if (identifier == "gl_Layer") { + if (!qualifier.layoutViewportRelative && qualifier.layoutSecondaryViewportRelativeOffset == -2048) + error(loc, "redeclaration only allowed for viewport_relative or secondary_view_offset layout", "redeclaration", symbol->getName().c_str()); + symbolQualifier.layoutViewportRelative = qualifier.layoutViewportRelative; + symbolQualifier.layoutSecondaryViewportRelativeOffset = qualifier.layoutSecondaryViewportRelativeOffset; + } + + // TODO: semantics quality: separate smooth from nothing declared, then use IsInterpolation for several tests above + + return symbol; + } +#endif + + return nullptr; +} + +// +// Either redeclare the requested block, or give an error message why it can't be done. +// +// TODO: functionality: explicitly sizing members of redeclared blocks is not giving them an explicit size +void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newTypeList, const TString& blockName, + const TString* instanceName, TArraySizes* arraySizes) +{ +#ifndef GLSLANG_WEB + const char* feature = "built-in block redeclaration"; + profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, feature); + profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_separate_shader_objects, feature); + + if (blockName != "gl_PerVertex" && blockName != "gl_PerFragment" && + blockName != "gl_MeshPerVertexNV" && blockName != "gl_MeshPerPrimitiveNV") { + error(loc, "cannot redeclare block: ", "block declaration", blockName.c_str()); + return; + } + + // Redeclaring a built-in block... + + if (instanceName && ! builtInName(*instanceName)) { + error(loc, "cannot redeclare a built-in block with a user name", instanceName->c_str(), ""); + return; + } + + // Blocks with instance names are easy to find, lookup the instance name, + // Anonymous blocks need to be found via a member. + bool builtIn; + TSymbol* block; + if (instanceName) + block = symbolTable.find(*instanceName, &builtIn); + else + block = symbolTable.find(newTypeList.front().type->getFieldName(), &builtIn); + + // If the block was not found, this must be a version/profile/stage + // that doesn't have it, or the instance name is wrong. + const char* errorName = instanceName ? instanceName->c_str() : newTypeList.front().type->getFieldName().c_str(); + if (! block) { + error(loc, "no declaration found for redeclaration", errorName, ""); + return; + } + // Built-in blocks cannot be redeclared more than once, which if happened, + // we'd be finding the already redeclared one here, rather than the built in. + if (! builtIn) { + error(loc, "can only redeclare a built-in block once, and before any use", blockName.c_str(), ""); + return; + } + + // Copy the block to make a writable version, to insert into the block table after editing. + block = symbolTable.copyUpDeferredInsert(block); + + if (block->getType().getBasicType() != EbtBlock) { + error(loc, "cannot redeclare a non block as a block", errorName, ""); + return; + } + + // Fix XFB stuff up, it applies to the order of the redeclaration, not + // the order of the original members. + if (currentBlockQualifier.storage == EvqVaryingOut && globalOutputDefaults.hasXfbBuffer()) { + if (!currentBlockQualifier.hasXfbBuffer()) + currentBlockQualifier.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; + if (!currentBlockQualifier.hasStream()) + currentBlockQualifier.layoutStream = globalOutputDefaults.layoutStream; + fixXfbOffsets(currentBlockQualifier, newTypeList); + } + + // Edit and error check the container against the redeclaration + // - remove unused members + // - ensure remaining qualifiers/types match + + TType& type = block->getWritableType(); + + // if gl_PerVertex is redeclared for the purpose of passing through "gl_Position" + // for passthrough purpose, the redeclared block should have the same qualifers as + // the current one + if (currentBlockQualifier.layoutPassthrough) { + type.getQualifier().layoutPassthrough = currentBlockQualifier.layoutPassthrough; + type.getQualifier().storage = currentBlockQualifier.storage; + type.getQualifier().layoutStream = currentBlockQualifier.layoutStream; + type.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer; + } + + TTypeList::iterator member = type.getWritableStruct()->begin(); + size_t numOriginalMembersFound = 0; + while (member != type.getStruct()->end()) { + // look for match + bool found = false; + TTypeList::const_iterator newMember; + TSourceLoc memberLoc; + memberLoc.init(); + for (newMember = newTypeList.begin(); newMember != newTypeList.end(); ++newMember) { + if (member->type->getFieldName() == newMember->type->getFieldName()) { + found = true; + memberLoc = newMember->loc; + break; + } + } + + if (found) { + ++numOriginalMembersFound; + // - ensure match between redeclared members' types + // - check for things that can't be changed + // - update things that can be changed + TType& oldType = *member->type; + const TType& newType = *newMember->type; + if (! newType.sameElementType(oldType)) + error(memberLoc, "cannot redeclare block member with a different type", member->type->getFieldName().c_str(), ""); + if (oldType.isArray() != newType.isArray()) + error(memberLoc, "cannot change arrayness of redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! oldType.getQualifier().isPerView() && ! oldType.sameArrayness(newType) && oldType.isSizedArray()) + error(memberLoc, "cannot change array size of redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! oldType.getQualifier().isPerView() && newType.isArray()) + arrayLimitCheck(loc, member->type->getFieldName(), newType.getOuterArraySize()); + if (oldType.getQualifier().isPerView() && ! newType.getQualifier().isPerView()) + error(memberLoc, "missing perviewNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! oldType.getQualifier().isPerView() && newType.getQualifier().isPerView()) + error(memberLoc, "cannot add perviewNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + else if (newType.getQualifier().isPerView()) { + if (oldType.getArraySizes()->getNumDims() != newType.getArraySizes()->getNumDims()) + error(memberLoc, "cannot change arrayness of redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! newType.isUnsizedArray() && newType.getOuterArraySize() != resources.maxMeshViewCountNV) + error(loc, "mesh view output array size must be gl_MaxMeshViewCountNV or implicitly sized", "[]", ""); + else if (newType.getArraySizes()->getNumDims() == 2) { + int innerDimSize = newType.getArraySizes()->getDimSize(1); + arrayLimitCheck(memberLoc, member->type->getFieldName(), innerDimSize); + oldType.getArraySizes()->setDimSize(1, innerDimSize); + } + } + if (oldType.getQualifier().isPerPrimitive() && ! newType.getQualifier().isPerPrimitive()) + error(memberLoc, "missing perprimitiveNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! oldType.getQualifier().isPerPrimitive() && newType.getQualifier().isPerPrimitive()) + error(memberLoc, "cannot add perprimitiveNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + if (newType.getQualifier().isMemory()) + error(memberLoc, "cannot add memory qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + if (newType.getQualifier().hasNonXfbLayout()) + error(memberLoc, "cannot add non-XFB layout to redeclared block member", member->type->getFieldName().c_str(), ""); + if (newType.getQualifier().patch) + error(memberLoc, "cannot add patch to redeclared block member", member->type->getFieldName().c_str(), ""); + if (newType.getQualifier().hasXfbBuffer() && + newType.getQualifier().layoutXfbBuffer != currentBlockQualifier.layoutXfbBuffer) + error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", ""); + if (newType.getQualifier().hasStream() && + newType.getQualifier().layoutStream != currentBlockQualifier.layoutStream) + error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_stream", ""); + oldType.getQualifier().centroid = newType.getQualifier().centroid; + oldType.getQualifier().sample = newType.getQualifier().sample; + oldType.getQualifier().invariant = newType.getQualifier().invariant; + oldType.getQualifier().noContraction = newType.getQualifier().noContraction; + oldType.getQualifier().smooth = newType.getQualifier().smooth; + oldType.getQualifier().flat = newType.getQualifier().flat; + oldType.getQualifier().nopersp = newType.getQualifier().nopersp; + oldType.getQualifier().layoutXfbOffset = newType.getQualifier().layoutXfbOffset; + oldType.getQualifier().layoutXfbBuffer = newType.getQualifier().layoutXfbBuffer; + oldType.getQualifier().layoutXfbStride = newType.getQualifier().layoutXfbStride; + if (oldType.getQualifier().layoutXfbOffset != TQualifier::layoutXfbBufferEnd) { + // If any member has an xfb_offset, then the block's xfb_buffer inherents current xfb_buffer, + // and for xfb processing, the member needs it as well, along with xfb_stride. + type.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer; + oldType.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer; + } + if (oldType.isUnsizedArray() && newType.isSizedArray()) + oldType.changeOuterArraySize(newType.getOuterArraySize()); + + // check and process the member's type, which will include managing xfb information + layoutTypeCheck(loc, oldType); + + // go to next member + ++member; + } else { + // For missing members of anonymous blocks that have been redeclared, + // hide the original (shared) declaration. + // Instance-named blocks can just have the member removed. + if (instanceName) + member = type.getWritableStruct()->erase(member); + else { + member->type->hideMember(); + ++member; + } + } + } + + if (spvVersion.vulkan > 0) { + // ...then streams apply to built-in blocks, instead of them being only on stream 0 + type.getQualifier().layoutStream = currentBlockQualifier.layoutStream; + } + + if (numOriginalMembersFound < newTypeList.size()) + error(loc, "block redeclaration has extra members", blockName.c_str(), ""); + if (type.isArray() != (arraySizes != nullptr) || + (type.isArray() && arraySizes != nullptr && type.getArraySizes()->getNumDims() != arraySizes->getNumDims())) + error(loc, "cannot change arrayness of redeclared block", blockName.c_str(), ""); + else if (type.isArray()) { + // At this point, we know both are arrays and both have the same number of dimensions. + + // It is okay for a built-in block redeclaration to be unsized, and keep the size of the + // original block declaration. + if (!arraySizes->isSized() && type.isSizedArray()) + arraySizes->changeOuterSize(type.getOuterArraySize()); + + // And, okay to be giving a size to the array, by the redeclaration + if (!type.isSizedArray() && arraySizes->isSized()) + type.changeOuterArraySize(arraySizes->getOuterSize()); + + // Now, they must match in all dimensions. + if (type.isSizedArray() && *type.getArraySizes() != *arraySizes) + error(loc, "cannot change array size of redeclared block", blockName.c_str(), ""); + } + + symbolTable.insert(*block); + + // Check for general layout qualifier errors + layoutObjectCheck(loc, *block); + + // Tracking for implicit sizing of array + if (isIoResizeArray(block->getType())) { + ioArraySymbolResizeList.push_back(block); + checkIoArraysConsistency(loc, true); + } else if (block->getType().isArray()) + fixIoArraySize(loc, block->getWritableType()); + + // Save it in the AST for linker use. + trackLinkage(*block); +#endif // GLSLANG_WEB +} + +void TParseContext::paramCheckFixStorage(const TSourceLoc& loc, const TStorageQualifier& qualifier, TType& type) +{ + switch (qualifier) { + case EvqConst: + case EvqConstReadOnly: + type.getQualifier().storage = EvqConstReadOnly; + break; + case EvqIn: + case EvqOut: + case EvqInOut: + type.getQualifier().storage = qualifier; + break; + case EvqGlobal: + case EvqTemporary: + type.getQualifier().storage = EvqIn; + break; + default: + type.getQualifier().storage = EvqIn; + error(loc, "storage qualifier not allowed on function parameter", GetStorageQualifierString(qualifier), ""); + break; + } +} + +void TParseContext::paramCheckFix(const TSourceLoc& loc, const TQualifier& qualifier, TType& type) +{ +#ifndef GLSLANG_WEB + if (qualifier.isMemory()) { + type.getQualifier().volatil = qualifier.volatil; + type.getQualifier().coherent = qualifier.coherent; + type.getQualifier().devicecoherent = qualifier.devicecoherent ; + type.getQualifier().queuefamilycoherent = qualifier.queuefamilycoherent; + type.getQualifier().workgroupcoherent = qualifier.workgroupcoherent; + type.getQualifier().subgroupcoherent = qualifier.subgroupcoherent; + type.getQualifier().shadercallcoherent = qualifier.shadercallcoherent; + type.getQualifier().nonprivate = qualifier.nonprivate; + type.getQualifier().readonly = qualifier.readonly; + type.getQualifier().writeonly = qualifier.writeonly; + type.getQualifier().restrict = qualifier.restrict; + } +#endif + + if (qualifier.isAuxiliary() || + qualifier.isInterpolation()) + error(loc, "cannot use auxiliary or interpolation qualifiers on a function parameter", "", ""); + if (qualifier.hasLayout()) + error(loc, "cannot use layout qualifiers on a function parameter", "", ""); + if (qualifier.invariant) + error(loc, "cannot use invariant qualifier on a function parameter", "", ""); + if (qualifier.isNoContraction()) { + if (qualifier.isParamOutput()) + type.getQualifier().setNoContraction(); + else + warn(loc, "qualifier has no effect on non-output parameters", "precise", ""); + } + if (qualifier.isNonUniform()) + type.getQualifier().nonUniform = qualifier.nonUniform; + + paramCheckFixStorage(loc, qualifier.storage, type); +} + +void TParseContext::nestedBlockCheck(const TSourceLoc& loc) +{ + if (structNestingLevel > 0) + error(loc, "cannot nest a block definition inside a structure or block", "", ""); + ++structNestingLevel; +} + +void TParseContext::nestedStructCheck(const TSourceLoc& loc) +{ + if (structNestingLevel > 0) + error(loc, "cannot nest a structure definition inside a structure or block", "", ""); + ++structNestingLevel; +} + +void TParseContext::arrayObjectCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ + // Some versions don't allow comparing arrays or structures containing arrays + if (type.containsArray()) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, op); + profileRequires(loc, EEsProfile, 300, nullptr, op); + } +} + +void TParseContext::opaqueCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ + if (containsFieldWithBasicType(type, EbtSampler)) + error(loc, "can't use with samplers or structs containing samplers", op, ""); +} + +void TParseContext::referenceCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ +#ifndef GLSLANG_WEB + if (containsFieldWithBasicType(type, EbtReference)) + error(loc, "can't use with reference types", op, ""); +#endif +} + +void TParseContext::storage16BitAssignmentCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ +#ifndef GLSLANG_WEB + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtFloat16)) + requireFloat16Arithmetic(loc, op, "can't use with structs containing float16"); + + if (type.isArray() && type.getBasicType() == EbtFloat16) + requireFloat16Arithmetic(loc, op, "can't use with arrays containing float16"); + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtInt16)) + requireInt16Arithmetic(loc, op, "can't use with structs containing int16"); + + if (type.isArray() && type.getBasicType() == EbtInt16) + requireInt16Arithmetic(loc, op, "can't use with arrays containing int16"); + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtUint16)) + requireInt16Arithmetic(loc, op, "can't use with structs containing uint16"); + + if (type.isArray() && type.getBasicType() == EbtUint16) + requireInt16Arithmetic(loc, op, "can't use with arrays containing uint16"); + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtInt8)) + requireInt8Arithmetic(loc, op, "can't use with structs containing int8"); + + if (type.isArray() && type.getBasicType() == EbtInt8) + requireInt8Arithmetic(loc, op, "can't use with arrays containing int8"); + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtUint8)) + requireInt8Arithmetic(loc, op, "can't use with structs containing uint8"); + + if (type.isArray() && type.getBasicType() == EbtUint8) + requireInt8Arithmetic(loc, op, "can't use with arrays containing uint8"); +#endif +} + +void TParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ + if (type.containsSpecializationSize()) + error(loc, "can't use with types containing arrays sized with a specialization constant", op, ""); +} + +void TParseContext::structTypeCheck(const TSourceLoc& /*loc*/, TPublicType& publicType) +{ + const TTypeList& typeList = *publicType.userDef->getStruct(); + + // fix and check for member storage qualifiers and types that don't belong within a structure + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + if (memberQualifier.isAuxiliary() || + memberQualifier.isInterpolation() || + (memberQualifier.storage != EvqTemporary && memberQualifier.storage != EvqGlobal)) + error(memberLoc, "cannot use storage or interpolation qualifiers on structure members", typeList[member].type->getFieldName().c_str(), ""); + if (memberQualifier.isMemory()) + error(memberLoc, "cannot use memory qualifiers on structure members", typeList[member].type->getFieldName().c_str(), ""); + if (memberQualifier.hasLayout()) { + error(memberLoc, "cannot use layout qualifiers on structure members", typeList[member].type->getFieldName().c_str(), ""); + memberQualifier.clearLayout(); + } + if (memberQualifier.invariant) + error(memberLoc, "cannot use invariant qualifier on structure members", typeList[member].type->getFieldName().c_str(), ""); + } +} + +// +// See if this loop satisfies the limitations for ES 2.0 (version 100) for loops in Appendex A: +// +// "The loop index has type int or float. +// +// "The for statement has the form: +// for ( init-declaration ; condition ; expression ) +// init-declaration has the form: type-specifier identifier = constant-expression +// condition has the form: loop-index relational_operator constant-expression +// where relational_operator is one of: > >= < <= == or != +// expression [sic] has one of the following forms: +// loop-index++ +// loop-index-- +// loop-index += constant-expression +// loop-index -= constant-expression +// +// The body is handled in an AST traversal. +// +void TParseContext::inductiveLoopCheck(const TSourceLoc& loc, TIntermNode* init, TIntermLoop* loop) +{ +#ifndef GLSLANG_WEB + // loop index init must exist and be a declaration, which shows up in the AST as an aggregate of size 1 of the declaration + bool badInit = false; + if (! init || ! init->getAsAggregate() || init->getAsAggregate()->getSequence().size() != 1) + badInit = true; + TIntermBinary* binaryInit = 0; + if (! badInit) { + // get the declaration assignment + binaryInit = init->getAsAggregate()->getSequence()[0]->getAsBinaryNode(); + if (! binaryInit) + badInit = true; + } + if (badInit) { + error(loc, "inductive-loop init-declaration requires the form \"type-specifier loop-index = constant-expression\"", "limitations", ""); + return; + } + + // loop index must be type int or float + if (! binaryInit->getType().isScalar() || (binaryInit->getBasicType() != EbtInt && binaryInit->getBasicType() != EbtFloat)) { + error(loc, "inductive loop requires a scalar 'int' or 'float' loop index", "limitations", ""); + return; + } + + // init is the form "loop-index = constant" + if (binaryInit->getOp() != EOpAssign || ! binaryInit->getLeft()->getAsSymbolNode() || ! binaryInit->getRight()->getAsConstantUnion()) { + error(loc, "inductive-loop init-declaration requires the form \"type-specifier loop-index = constant-expression\"", "limitations", ""); + return; + } + + // get the unique id of the loop index + int loopIndex = binaryInit->getLeft()->getAsSymbolNode()->getId(); + inductiveLoopIds.insert(loopIndex); + + // condition's form must be "loop-index relational-operator constant-expression" + bool badCond = ! loop->getTest(); + if (! badCond) { + TIntermBinary* binaryCond = loop->getTest()->getAsBinaryNode(); + badCond = ! binaryCond; + if (! badCond) { + switch (binaryCond->getOp()) { + case EOpGreaterThan: + case EOpGreaterThanEqual: + case EOpLessThan: + case EOpLessThanEqual: + case EOpEqual: + case EOpNotEqual: + break; + default: + badCond = true; + } + } + if (binaryCond && (! binaryCond->getLeft()->getAsSymbolNode() || + binaryCond->getLeft()->getAsSymbolNode()->getId() != loopIndex || + ! binaryCond->getRight()->getAsConstantUnion())) + badCond = true; + } + if (badCond) { + error(loc, "inductive-loop condition requires the form \"loop-index constant-expression\"", "limitations", ""); + return; + } + + // loop-index++ + // loop-index-- + // loop-index += constant-expression + // loop-index -= constant-expression + bool badTerminal = ! loop->getTerminal(); + if (! badTerminal) { + TIntermUnary* unaryTerminal = loop->getTerminal()->getAsUnaryNode(); + TIntermBinary* binaryTerminal = loop->getTerminal()->getAsBinaryNode(); + if (unaryTerminal || binaryTerminal) { + switch(loop->getTerminal()->getAsOperator()->getOp()) { + case EOpPostDecrement: + case EOpPostIncrement: + case EOpAddAssign: + case EOpSubAssign: + break; + default: + badTerminal = true; + } + } else + badTerminal = true; + if (binaryTerminal && (! binaryTerminal->getLeft()->getAsSymbolNode() || + binaryTerminal->getLeft()->getAsSymbolNode()->getId() != loopIndex || + ! binaryTerminal->getRight()->getAsConstantUnion())) + badTerminal = true; + if (unaryTerminal && (! unaryTerminal->getOperand()->getAsSymbolNode() || + unaryTerminal->getOperand()->getAsSymbolNode()->getId() != loopIndex)) + badTerminal = true; + } + if (badTerminal) { + error(loc, "inductive-loop termination requires the form \"loop-index++, loop-index--, loop-index += constant-expression, or loop-index -= constant-expression\"", "limitations", ""); + return; + } + + // the body + inductiveLoopBodyCheck(loop->getBody(), loopIndex, symbolTable); +#endif +} + +#ifndef GLSLANG_WEB +// Do limit checks for built-in arrays. +void TParseContext::arrayLimitCheck(const TSourceLoc& loc, const TString& identifier, int size) +{ + if (identifier.compare("gl_TexCoord") == 0) + limitCheck(loc, size, "gl_MaxTextureCoords", "gl_TexCoord array size"); + else if (identifier.compare("gl_ClipDistance") == 0) + limitCheck(loc, size, "gl_MaxClipDistances", "gl_ClipDistance array size"); + else if (identifier.compare("gl_CullDistance") == 0) + limitCheck(loc, size, "gl_MaxCullDistances", "gl_CullDistance array size"); + else if (identifier.compare("gl_ClipDistancePerViewNV") == 0) + limitCheck(loc, size, "gl_MaxClipDistances", "gl_ClipDistancePerViewNV array size"); + else if (identifier.compare("gl_CullDistancePerViewNV") == 0) + limitCheck(loc, size, "gl_MaxCullDistances", "gl_CullDistancePerViewNV array size"); +} +#endif // GLSLANG_WEB + +// See if the provided value is less than or equal to the symbol indicated by limit, +// which should be a constant in the symbol table. +void TParseContext::limitCheck(const TSourceLoc& loc, int value, const char* limit, const char* feature) +{ + TSymbol* symbol = symbolTable.find(limit); + assert(symbol->getAsVariable()); + const TConstUnionArray& constArray = symbol->getAsVariable()->getConstArray(); + assert(! constArray.empty()); + if (value > constArray[0].getIConst()) + error(loc, "must be less than or equal to", feature, "%s (%d)", limit, constArray[0].getIConst()); +} + +#ifndef GLSLANG_WEB + +// +// Do any additional error checking, etc., once we know the parsing is done. +// +void TParseContext::finish() +{ + TParseContextBase::finish(); + + if (parsingBuiltins) + return; + + // Check on array indexes for ES 2.0 (version 100) limitations. + for (size_t i = 0; i < needsIndexLimitationChecking.size(); ++i) + constantIndexExpressionCheck(needsIndexLimitationChecking[i]); + + // Check for stages that are enabled by extension. + // Can't do this at the beginning, it is chicken and egg to add a stage by + // extension. + // Stage-specific features were correctly tested for already, this is just + // about the stage itself. + switch (language) { + case EShLangGeometry: + if (isEsProfile() && version == 310) + requireExtensions(getCurrentLoc(), Num_AEP_geometry_shader, AEP_geometry_shader, "geometry shaders"); + break; + case EShLangTessControl: + case EShLangTessEvaluation: + if (isEsProfile() && version == 310) + requireExtensions(getCurrentLoc(), Num_AEP_tessellation_shader, AEP_tessellation_shader, "tessellation shaders"); + else if (!isEsProfile() && version < 400) + requireExtensions(getCurrentLoc(), 1, &E_GL_ARB_tessellation_shader, "tessellation shaders"); + break; + case EShLangCompute: + if (!isEsProfile() && version < 430) + requireExtensions(getCurrentLoc(), 1, &E_GL_ARB_compute_shader, "compute shaders"); + break; + case EShLangTaskNV: + requireExtensions(getCurrentLoc(), 1, &E_GL_NV_mesh_shader, "task shaders"); + break; + case EShLangMeshNV: + requireExtensions(getCurrentLoc(), 1, &E_GL_NV_mesh_shader, "mesh shaders"); + break; + default: + break; + } + + // Set default outputs for GL_NV_geometry_shader_passthrough + if (language == EShLangGeometry && extensionTurnedOn(E_SPV_NV_geometry_shader_passthrough)) { + if (intermediate.getOutputPrimitive() == ElgNone) { + switch (intermediate.getInputPrimitive()) { + case ElgPoints: intermediate.setOutputPrimitive(ElgPoints); break; + case ElgLines: intermediate.setOutputPrimitive(ElgLineStrip); break; + case ElgTriangles: intermediate.setOutputPrimitive(ElgTriangleStrip); break; + default: break; + } + } + if (intermediate.getVertices() == TQualifier::layoutNotSet) { + switch (intermediate.getInputPrimitive()) { + case ElgPoints: intermediate.setVertices(1); break; + case ElgLines: intermediate.setVertices(2); break; + case ElgTriangles: intermediate.setVertices(3); break; + default: break; + } + } + } +} +#endif // GLSLANG_WEB + +// +// Layout qualifier stuff. +// + +// Put the id's layout qualification into the public type, for qualifiers not having a number set. +// This is before we know any type information for error checking. +void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publicType, TString& id) +{ + std::transform(id.begin(), id.end(), id.begin(), ::tolower); + + if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) { + publicType.qualifier.layoutMatrix = ElmColumnMajor; + return; + } + if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) { + publicType.qualifier.layoutMatrix = ElmRowMajor; + return; + } + if (id == TQualifier::getLayoutPackingString(ElpPacked)) { + if (spvVersion.spv != 0) + spvRemoved(loc, "packed"); + publicType.qualifier.layoutPacking = ElpPacked; + return; + } + if (id == TQualifier::getLayoutPackingString(ElpShared)) { + if (spvVersion.spv != 0) + spvRemoved(loc, "shared"); + publicType.qualifier.layoutPacking = ElpShared; + return; + } + if (id == TQualifier::getLayoutPackingString(ElpStd140)) { + publicType.qualifier.layoutPacking = ElpStd140; + return; + } +#ifndef GLSLANG_WEB + if (id == TQualifier::getLayoutPackingString(ElpStd430)) { + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, "std430"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_shader_storage_buffer_object, "std430"); + profileRequires(loc, EEsProfile, 310, nullptr, "std430"); + publicType.qualifier.layoutPacking = ElpStd430; + return; + } + if (id == TQualifier::getLayoutPackingString(ElpScalar)) { + requireVulkan(loc, "scalar"); + requireExtensions(loc, 1, &E_GL_EXT_scalar_block_layout, "scalar block layout"); + publicType.qualifier.layoutPacking = ElpScalar; + return; + } + // TODO: compile-time performance: may need to stop doing linear searches + for (TLayoutFormat format = (TLayoutFormat)(ElfNone + 1); format < ElfCount; format = (TLayoutFormat)(format + 1)) { + if (id == TQualifier::getLayoutFormatString(format)) { + if ((format > ElfEsFloatGuard && format < ElfFloatGuard) || + (format > ElfEsIntGuard && format < ElfIntGuard) || + (format > ElfEsUintGuard && format < ElfCount)) + requireProfile(loc, ENoProfile | ECoreProfile | ECompatibilityProfile, "image load-store format"); + profileRequires(loc, ENoProfile | ECoreProfile | ECompatibilityProfile, 420, E_GL_ARB_shader_image_load_store, "image load store"); + profileRequires(loc, EEsProfile, 310, E_GL_ARB_shader_image_load_store, "image load store"); + publicType.qualifier.layoutFormat = format; + return; + } + } + if (id == "push_constant") { + requireVulkan(loc, "push_constant"); + publicType.qualifier.layoutPushConstant = true; + return; + } + if (id == "buffer_reference") { + requireVulkan(loc, "buffer_reference"); + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference, "buffer_reference"); + publicType.qualifier.layoutBufferReference = true; + intermediate.setUseStorageBuffer(); + intermediate.setUsePhysicalStorageBuffer(); + return; + } + if (language == EShLangGeometry || language == EShLangTessEvaluation || language == EShLangMeshNV) { + if (id == TQualifier::getGeometryString(ElgTriangles)) { + publicType.shaderQualifiers.geometry = ElgTriangles; + return; + } + if (language == EShLangGeometry || language == EShLangMeshNV) { + if (id == TQualifier::getGeometryString(ElgPoints)) { + publicType.shaderQualifiers.geometry = ElgPoints; + return; + } + if (id == TQualifier::getGeometryString(ElgLines)) { + publicType.shaderQualifiers.geometry = ElgLines; + return; + } + if (language == EShLangGeometry) { + if (id == TQualifier::getGeometryString(ElgLineStrip)) { + publicType.shaderQualifiers.geometry = ElgLineStrip; + return; + } + if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) { + publicType.shaderQualifiers.geometry = ElgLinesAdjacency; + return; + } + if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) { + publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency; + return; + } + if (id == TQualifier::getGeometryString(ElgTriangleStrip)) { + publicType.shaderQualifiers.geometry = ElgTriangleStrip; + return; + } + if (id == "passthrough") { + requireExtensions(loc, 1, &E_SPV_NV_geometry_shader_passthrough, "geometry shader passthrough"); + publicType.qualifier.layoutPassthrough = true; + intermediate.setGeoPassthroughEXT(); + return; + } + } + } else { + assert(language == EShLangTessEvaluation); + + // input primitive + if (id == TQualifier::getGeometryString(ElgTriangles)) { + publicType.shaderQualifiers.geometry = ElgTriangles; + return; + } + if (id == TQualifier::getGeometryString(ElgQuads)) { + publicType.shaderQualifiers.geometry = ElgQuads; + return; + } + if (id == TQualifier::getGeometryString(ElgIsolines)) { + publicType.shaderQualifiers.geometry = ElgIsolines; + return; + } + + // vertex spacing + if (id == TQualifier::getVertexSpacingString(EvsEqual)) { + publicType.shaderQualifiers.spacing = EvsEqual; + return; + } + if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) { + publicType.shaderQualifiers.spacing = EvsFractionalEven; + return; + } + if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) { + publicType.shaderQualifiers.spacing = EvsFractionalOdd; + return; + } + + // triangle order + if (id == TQualifier::getVertexOrderString(EvoCw)) { + publicType.shaderQualifiers.order = EvoCw; + return; + } + if (id == TQualifier::getVertexOrderString(EvoCcw)) { + publicType.shaderQualifiers.order = EvoCcw; + return; + } + + // point mode + if (id == "point_mode") { + publicType.shaderQualifiers.pointMode = true; + return; + } + } + } + if (language == EShLangFragment) { + if (id == "origin_upper_left") { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "origin_upper_left"); + publicType.shaderQualifiers.originUpperLeft = true; + return; + } + if (id == "pixel_center_integer") { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "pixel_center_integer"); + publicType.shaderQualifiers.pixelCenterInteger = true; + return; + } + if (id == "early_fragment_tests") { + profileRequires(loc, ENoProfile | ECoreProfile | ECompatibilityProfile, 420, E_GL_ARB_shader_image_load_store, "early_fragment_tests"); + profileRequires(loc, EEsProfile, 310, nullptr, "early_fragment_tests"); + publicType.shaderQualifiers.earlyFragmentTests = true; + return; + } + if (id == "post_depth_coverage") { + requireExtensions(loc, Num_post_depth_coverageEXTs, post_depth_coverageEXTs, "post depth coverage"); + if (extensionTurnedOn(E_GL_ARB_post_depth_coverage)) { + publicType.shaderQualifiers.earlyFragmentTests = true; + } + publicType.shaderQualifiers.postDepthCoverage = true; + return; + } + for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth+1)) { + if (id == TQualifier::getLayoutDepthString(depth)) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "depth layout qualifier"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 420, nullptr, "depth layout qualifier"); + publicType.shaderQualifiers.layoutDepth = depth; + return; + } + } + for (TInterlockOrdering order = (TInterlockOrdering)(EioNone + 1); order < EioCount; order = (TInterlockOrdering)(order+1)) { + if (id == TQualifier::getInterlockOrderingString(order)) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "fragment shader interlock layout qualifier"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 450, nullptr, "fragment shader interlock layout qualifier"); + requireExtensions(loc, 1, &E_GL_ARB_fragment_shader_interlock, TQualifier::getInterlockOrderingString(order)); + if (order == EioShadingRateInterlockOrdered || order == EioShadingRateInterlockUnordered) + requireExtensions(loc, 1, &E_GL_NV_shading_rate_image, TQualifier::getInterlockOrderingString(order)); + publicType.shaderQualifiers.interlockOrdering = order; + return; + } + } + if (id.compare(0, 13, "blend_support") == 0) { + bool found = false; + for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) { + if (id == TQualifier::getBlendEquationString(be)) { + profileRequires(loc, EEsProfile, 320, E_GL_KHR_blend_equation_advanced, "blend equation"); + profileRequires(loc, ~EEsProfile, 0, E_GL_KHR_blend_equation_advanced, "blend equation"); + intermediate.addBlendEquation(be); + publicType.shaderQualifiers.blendEquation = true; + found = true; + break; + } + } + if (! found) + error(loc, "unknown blend equation", "blend_support", ""); + return; + } + if (id == "override_coverage") { + requireExtensions(loc, 1, &E_GL_NV_sample_mask_override_coverage, "sample mask override coverage"); + publicType.shaderQualifiers.layoutOverrideCoverage = true; + return; + } + } + if (language == EShLangVertex || + language == EShLangTessControl || + language == EShLangTessEvaluation || + language == EShLangGeometry ) { + if (id == "viewport_relative") { + requireExtensions(loc, 1, &E_GL_NV_viewport_array2, "view port array2"); + publicType.qualifier.layoutViewportRelative = true; + return; + } + } else { + if (language == EShLangRayGen || language == EShLangIntersect || + language == EShLangAnyHit || language == EShLangClosestHit || + language == EShLangMiss || language == EShLangCallable) { + if (id == "shaderrecordnv" || id == "shaderrecordext") { + if (id == "shaderrecordnv") { + requireExtensions(loc, 1, &E_GL_NV_ray_tracing, "shader record NV"); + } else { + requireExtensions(loc, 1, &E_GL_EXT_ray_tracing, "shader record EXT"); + } + publicType.qualifier.layoutShaderRecord = true; + return; + } + + } + } + if (language == EShLangCompute) { + if (id.compare(0, 17, "derivative_group_") == 0) { + requireExtensions(loc, 1, &E_GL_NV_compute_shader_derivatives, "compute shader derivatives"); + if (id == "derivative_group_quadsnv") { + publicType.shaderQualifiers.layoutDerivativeGroupQuads = true; + return; + } else if (id == "derivative_group_linearnv") { + publicType.shaderQualifiers.layoutDerivativeGroupLinear = true; + return; + } + } + } +#endif + + error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), ""); +} + +// Put the id's layout qualifier value into the public type, for qualifiers having a number set. +// This is before we know any type information for error checking. +void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publicType, TString& id, const TIntermTyped* node) +{ + const char* feature = "layout-id value"; + const char* nonLiteralFeature = "non-literal layout-id value"; + + integerCheck(node, feature); + const TIntermConstantUnion* constUnion = node->getAsConstantUnion(); + int value; + bool nonLiteral = false; + if (constUnion) { + value = constUnion->getConstArray()[0].getIConst(); + if (! constUnion->isLiteral()) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, nonLiteralFeature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, nonLiteralFeature); + } + } else { + // grammar should have give out the error message + value = 0; + nonLiteral = true; + } + + if (value < 0) { + error(loc, "cannot be negative", feature, ""); + return; + } + + std::transform(id.begin(), id.end(), id.begin(), ::tolower); + + if (id == "offset") { + // "offset" can be for either + // - uniform offsets + // - atomic_uint offsets + const char* feature = "offset"; + if (spvVersion.spv == 0) { + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, feature); + const char* exts[2] = { E_GL_ARB_enhanced_layouts, E_GL_ARB_shader_atomic_counters }; + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 420, 2, exts, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + } + publicType.qualifier.layoutOffset = value; + publicType.qualifier.explicitOffset = true; + if (nonLiteral) + error(loc, "needs a literal integer", "offset", ""); + return; + } else if (id == "align") { + const char* feature = "uniform buffer-member align"; + if (spvVersion.spv == 0) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, feature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature); + } + // "The specified alignment must be a power of 2, or a compile-time error results." + if (! IsPow2(value)) + error(loc, "must be a power of 2", "align", ""); + else + publicType.qualifier.layoutAlign = value; + if (nonLiteral) + error(loc, "needs a literal integer", "align", ""); + return; + } else if (id == "location") { + profileRequires(loc, EEsProfile, 300, nullptr, "location"); + const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + // GL_ARB_explicit_uniform_location requires 330 or GL_ARB_explicit_attrib_location we do not need to add it here + profileRequires(loc, ~EEsProfile, 330, 2, exts, "location"); + if ((unsigned int)value >= TQualifier::layoutLocationEnd) + error(loc, "location is too large", id.c_str(), ""); + else + publicType.qualifier.layoutLocation = value; + if (nonLiteral) + error(loc, "needs a literal integer", "location", ""); + return; + } else if (id == "set") { + if ((unsigned int)value >= TQualifier::layoutSetEnd) + error(loc, "set is too large", id.c_str(), ""); + else + publicType.qualifier.layoutSet = value; + if (value != 0) + requireVulkan(loc, "descriptor set"); + if (nonLiteral) + error(loc, "needs a literal integer", "set", ""); + return; + } else if (id == "binding") { +#ifndef GLSLANG_WEB + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, "binding"); + profileRequires(loc, EEsProfile, 310, nullptr, "binding"); +#endif + if ((unsigned int)value >= TQualifier::layoutBindingEnd) + error(loc, "binding is too large", id.c_str(), ""); + else + publicType.qualifier.layoutBinding = value; + if (nonLiteral) + error(loc, "needs a literal integer", "binding", ""); + return; + } + if (id == "constant_id") { + requireSpv(loc, "constant_id"); + if (value >= (int)TQualifier::layoutSpecConstantIdEnd) { + error(loc, "specialization-constant id is too large", id.c_str(), ""); + } else { + publicType.qualifier.layoutSpecConstantId = value; + publicType.qualifier.specConstant = true; + if (! intermediate.addUsedConstantId(value)) + error(loc, "specialization-constant id already used", id.c_str(), ""); + } + if (nonLiteral) + error(loc, "needs a literal integer", "constant_id", ""); + return; + } +#ifndef GLSLANG_WEB + if (id == "component") { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "component"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, "component"); + if ((unsigned)value >= TQualifier::layoutComponentEnd) + error(loc, "component is too large", id.c_str(), ""); + else + publicType.qualifier.layoutComponent = value; + if (nonLiteral) + error(loc, "needs a literal integer", "component", ""); + return; + } + if (id.compare(0, 4, "xfb_") == 0) { + // "Any shader making any static use (after preprocessing) of any of these + // *xfb_* qualifiers will cause the shader to be in a transform feedback + // capturing mode and hence responsible for describing the transform feedback + // setup." + intermediate.setXfbMode(); + const char* feature = "transform feedback qualifier"; + requireStage(loc, (EShLanguageMask)(EShLangVertexMask | EShLangGeometryMask | EShLangTessControlMask | EShLangTessEvaluationMask), feature); + requireProfile(loc, ECoreProfile | ECompatibilityProfile, feature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature); + if (id == "xfb_buffer") { + // "It is a compile-time error to specify an *xfb_buffer* that is greater than + // the implementation-dependent constant gl_MaxTransformFeedbackBuffers." + if (value >= resources.maxTransformFeedbackBuffers) + error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d", resources.maxTransformFeedbackBuffers); + if (value >= (int)TQualifier::layoutXfbBufferEnd) + error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd-1); + else + publicType.qualifier.layoutXfbBuffer = value; + if (nonLiteral) + error(loc, "needs a literal integer", "xfb_buffer", ""); + return; + } else if (id == "xfb_offset") { + if (value >= (int)TQualifier::layoutXfbOffsetEnd) + error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd-1); + else + publicType.qualifier.layoutXfbOffset = value; + if (nonLiteral) + error(loc, "needs a literal integer", "xfb_offset", ""); + return; + } else if (id == "xfb_stride") { + // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the + // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents." + if (value > 4 * resources.maxTransformFeedbackInterleavedComponents) { + error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d", + resources.maxTransformFeedbackInterleavedComponents); + } + if (value >= (int)TQualifier::layoutXfbStrideEnd) + error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd-1); + else + publicType.qualifier.layoutXfbStride = value; + if (nonLiteral) + error(loc, "needs a literal integer", "xfb_stride", ""); + return; + } + } + if (id == "input_attachment_index") { + requireVulkan(loc, "input_attachment_index"); + if (value >= (int)TQualifier::layoutAttachmentEnd) + error(loc, "attachment index is too large", id.c_str(), ""); + else + publicType.qualifier.layoutAttachment = value; + if (nonLiteral) + error(loc, "needs a literal integer", "input_attachment_index", ""); + return; + } + if (id == "num_views") { + requireExtensions(loc, Num_OVR_multiview_EXTs, OVR_multiview_EXTs, "num_views"); + publicType.shaderQualifiers.numViews = value; + if (nonLiteral) + error(loc, "needs a literal integer", "num_views", ""); + return; + } + if (language == EShLangVertex || + language == EShLangTessControl || + language == EShLangTessEvaluation || + language == EShLangGeometry) { + if (id == "secondary_view_offset") { + requireExtensions(loc, 1, &E_GL_NV_stereo_view_rendering, "stereo view rendering"); + publicType.qualifier.layoutSecondaryViewportRelativeOffset = value; + if (nonLiteral) + error(loc, "needs a literal integer", "secondary_view_offset", ""); + return; + } + } + + if (id == "buffer_reference_align") { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference, "buffer_reference_align"); + if (! IsPow2(value)) + error(loc, "must be a power of 2", "buffer_reference_align", ""); + else + publicType.qualifier.layoutBufferReferenceAlign = (unsigned int)std::log2(value); + if (nonLiteral) + error(loc, "needs a literal integer", "buffer_reference_align", ""); + return; + } +#endif + + switch (language) { +#ifndef GLSLANG_WEB + case EShLangTessControl: + if (id == "vertices") { + if (value == 0) + error(loc, "must be greater than 0", "vertices", ""); + else + publicType.shaderQualifiers.vertices = value; + if (nonLiteral) + error(loc, "needs a literal integer", "vertices", ""); + return; + } + break; + + case EShLangGeometry: + if (id == "invocations") { + profileRequires(loc, ECompatibilityProfile | ECoreProfile, 400, nullptr, "invocations"); + if (value == 0) + error(loc, "must be at least 1", "invocations", ""); + else + publicType.shaderQualifiers.invocations = value; + if (nonLiteral) + error(loc, "needs a literal integer", "invocations", ""); + return; + } + if (id == "max_vertices") { + publicType.shaderQualifiers.vertices = value; + if (value > resources.maxGeometryOutputVertices) + error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", ""); + if (nonLiteral) + error(loc, "needs a literal integer", "max_vertices", ""); + return; + } + if (id == "stream") { + requireProfile(loc, ~EEsProfile, "selecting output stream"); + publicType.qualifier.layoutStream = value; + if (value > 0) + intermediate.setMultiStream(); + if (nonLiteral) + error(loc, "needs a literal integer", "stream", ""); + return; + } + break; + + case EShLangFragment: + if (id == "index") { + requireProfile(loc, ECompatibilityProfile | ECoreProfile | EEsProfile, "index layout qualifier on fragment output"); + const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + profileRequires(loc, ECompatibilityProfile | ECoreProfile, 330, 2, exts, "index layout qualifier on fragment output"); + profileRequires(loc, EEsProfile ,310, E_GL_EXT_blend_func_extended, "index layout qualifier on fragment output"); + // "It is also a compile-time error if a fragment shader sets a layout index to less than 0 or greater than 1." + if (value < 0 || value > 1) { + value = 0; + error(loc, "value must be 0 or 1", "index", ""); + } + + publicType.qualifier.layoutIndex = value; + if (nonLiteral) + error(loc, "needs a literal integer", "index", ""); + return; + } + break; + + case EShLangMeshNV: + if (id == "max_vertices") { + requireExtensions(loc, 1, &E_GL_NV_mesh_shader, "max_vertices"); + publicType.shaderQualifiers.vertices = value; + if (value > resources.maxMeshOutputVerticesNV) + error(loc, "too large, must be less than gl_MaxMeshOutputVerticesNV", "max_vertices", ""); + if (nonLiteral) + error(loc, "needs a literal integer", "max_vertices", ""); + return; + } + if (id == "max_primitives") { + requireExtensions(loc, 1, &E_GL_NV_mesh_shader, "max_primitives"); + publicType.shaderQualifiers.primitives = value; + if (value > resources.maxMeshOutputPrimitivesNV) + error(loc, "too large, must be less than gl_MaxMeshOutputPrimitivesNV", "max_primitives", ""); + if (nonLiteral) + error(loc, "needs a literal integer", "max_primitives", ""); + return; + } + // Fall through + + case EShLangTaskNV: + // Fall through +#endif + case EShLangCompute: + if (id.compare(0, 11, "local_size_") == 0) { +#ifndef GLSLANG_WEB + if (language == EShLangMeshNV || language == EShLangTaskNV) { + requireExtensions(loc, 1, &E_GL_NV_mesh_shader, "gl_WorkGroupSize"); + } else { + profileRequires(loc, EEsProfile, 310, 0, "gl_WorkGroupSize"); + profileRequires(loc, ~EEsProfile, 430, E_GL_ARB_compute_shader, "gl_WorkGroupSize"); + } +#endif + if (nonLiteral) + error(loc, "needs a literal integer", "local_size", ""); + if (id.size() == 12 && value == 0) { + error(loc, "must be at least 1", id.c_str(), ""); + return; + } + if (id == "local_size_x") { + publicType.shaderQualifiers.localSize[0] = value; + publicType.shaderQualifiers.localSizeNotDefault[0] = true; + return; + } + if (id == "local_size_y") { + publicType.shaderQualifiers.localSize[1] = value; + publicType.shaderQualifiers.localSizeNotDefault[1] = true; + return; + } + if (id == "local_size_z") { + publicType.shaderQualifiers.localSize[2] = value; + publicType.shaderQualifiers.localSizeNotDefault[2] = true; + return; + } + if (spvVersion.spv != 0) { + if (id == "local_size_x_id") { + publicType.shaderQualifiers.localSizeSpecId[0] = value; + return; + } + if (id == "local_size_y_id") { + publicType.shaderQualifiers.localSizeSpecId[1] = value; + return; + } + if (id == "local_size_z_id") { + publicType.shaderQualifiers.localSizeSpecId[2] = value; + return; + } + } + } + break; + + default: + break; + } + + error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), ""); +} + +// Merge any layout qualifier information from src into dst, leaving everything else in dst alone +// +// "More than one layout qualifier may appear in a single declaration. +// Additionally, the same layout-qualifier-name can occur multiple times +// within a layout qualifier or across multiple layout qualifiers in the +// same declaration. When the same layout-qualifier-name occurs +// multiple times, in a single declaration, the last occurrence overrides +// the former occurrence(s). Further, if such a layout-qualifier-name +// will effect subsequent declarations or other observable behavior, it +// is only the last occurrence that will have any effect, behaving as if +// the earlier occurrence(s) within the declaration are not present. +// This is also true for overriding layout-qualifier-names, where one +// overrides the other (e.g., row_major vs. column_major); only the last +// occurrence has any effect." +void TParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly) +{ + if (src.hasMatrix()) + dst.layoutMatrix = src.layoutMatrix; + if (src.hasPacking()) + dst.layoutPacking = src.layoutPacking; + +#ifndef GLSLANG_WEB + if (src.hasStream()) + dst.layoutStream = src.layoutStream; + if (src.hasFormat()) + dst.layoutFormat = src.layoutFormat; + if (src.hasXfbBuffer()) + dst.layoutXfbBuffer = src.layoutXfbBuffer; + if (src.hasBufferReferenceAlign()) + dst.layoutBufferReferenceAlign = src.layoutBufferReferenceAlign; +#endif + + if (src.hasAlign()) + dst.layoutAlign = src.layoutAlign; + + if (! inheritOnly) { + if (src.hasLocation()) + dst.layoutLocation = src.layoutLocation; + if (src.hasOffset()) + dst.layoutOffset = src.layoutOffset; + if (src.hasSet()) + dst.layoutSet = src.layoutSet; + if (src.layoutBinding != TQualifier::layoutBindingEnd) + dst.layoutBinding = src.layoutBinding; + + if (src.hasSpecConstantId()) + dst.layoutSpecConstantId = src.layoutSpecConstantId; + +#ifndef GLSLANG_WEB + if (src.hasComponent()) + dst.layoutComponent = src.layoutComponent; + if (src.hasIndex()) + dst.layoutIndex = src.layoutIndex; + if (src.hasXfbStride()) + dst.layoutXfbStride = src.layoutXfbStride; + if (src.hasXfbOffset()) + dst.layoutXfbOffset = src.layoutXfbOffset; + if (src.hasAttachment()) + dst.layoutAttachment = src.layoutAttachment; + if (src.layoutPushConstant) + dst.layoutPushConstant = true; + + if (src.layoutBufferReference) + dst.layoutBufferReference = true; + + if (src.layoutPassthrough) + dst.layoutPassthrough = true; + if (src.layoutViewportRelative) + dst.layoutViewportRelative = true; + if (src.layoutSecondaryViewportRelativeOffset != -2048) + dst.layoutSecondaryViewportRelativeOffset = src.layoutSecondaryViewportRelativeOffset; + if (src.layoutShaderRecord) + dst.layoutShaderRecord = true; + if (src.pervertexNV) + dst.pervertexNV = true; +#endif + } +} + +// Do error layout error checking given a full variable/block declaration. +void TParseContext::layoutObjectCheck(const TSourceLoc& loc, const TSymbol& symbol) +{ + const TType& type = symbol.getType(); + const TQualifier& qualifier = type.getQualifier(); + + // first, cross check WRT to just the type + layoutTypeCheck(loc, type); + + // now, any remaining error checking based on the object itself + + if (qualifier.hasAnyLocation()) { + switch (qualifier.storage) { + case EvqUniform: + case EvqBuffer: + if (symbol.getAsVariable() == nullptr) + error(loc, "can only be used on variable declaration", "location", ""); + break; + default: + break; + } + } + + // user-variable location check, which are required for SPIR-V in/out: + // - variables have it directly, + // - blocks have it on each member (already enforced), so check first one + if (spvVersion.spv > 0 && !parsingBuiltins && qualifier.builtIn == EbvNone && + !qualifier.hasLocation() && !intermediate.getAutoMapLocations()) { + + switch (qualifier.storage) { + case EvqVaryingIn: + case EvqVaryingOut: + //if (!type.getQualifier().isTaskMemory() && + // (type.getBasicType() != EbtBlock || + // (!(*type.getStruct())[0].type->getQualifier().hasLocation() && + // (*type.getStruct())[0].type->getQualifier().builtIn == EbvNone))) + // error(loc, "SPIR-V requires location for user input/output", "location", ""); + break; + default: + break; + } + } + + // Check packing and matrix + if (qualifier.hasUniformLayout()) { + switch (qualifier.storage) { + case EvqUniform: + case EvqBuffer: + if (type.getBasicType() != EbtBlock) { + if (qualifier.hasMatrix()) + error(loc, "cannot specify matrix layout on a variable declaration", "layout", ""); + if (qualifier.hasPacking()) + error(loc, "cannot specify packing on a variable declaration", "layout", ""); + // "The offset qualifier can only be used on block members of blocks..." + if (qualifier.hasOffset() && !type.isAtomic()) + error(loc, "cannot specify on a variable declaration", "offset", ""); + // "The align qualifier can only be used on blocks or block members..." + if (qualifier.hasAlign()) + error(loc, "cannot specify on a variable declaration", "align", ""); + if (qualifier.isPushConstant()) + error(loc, "can only specify on a uniform block", "push_constant", ""); + if (qualifier.isShaderRecord()) + error(loc, "can only specify on a buffer block", "shaderRecordNV", ""); + } + break; + default: + // these were already filtered by layoutTypeCheck() (or its callees) + break; + } + } +} + +// "For some blocks declared as arrays, the location can only be applied at the block level: +// When a block is declared as an array where additional locations are needed for each member +// for each block array element, it is a compile-time error to specify locations on the block +// members. That is, when locations would be under specified by applying them on block members, +// they are not allowed on block members. For arrayed interfaces (those generally having an +// extra level of arrayness due to interface expansion), the outer array is stripped before +// applying this rule." +void TParseContext::layoutMemberLocationArrayCheck(const TSourceLoc& loc, bool memberWithLocation, + TArraySizes* arraySizes) +{ + if (memberWithLocation && arraySizes != nullptr) { + if (arraySizes->getNumDims() > (currentBlockQualifier.isArrayedIo(language) ? 1 : 0)) + error(loc, "cannot use in a block array where new locations are needed for each block element", + "location", ""); + } +} + +// Do layout error checking with respect to a type. +void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type) +{ + const TQualifier& qualifier = type.getQualifier(); + + // first, intra-layout qualifier-only error checking + layoutQualifierCheck(loc, qualifier); + + // now, error checking combining type and qualifier + + if (qualifier.hasAnyLocation()) { + if (qualifier.hasLocation()) { + if (qualifier.storage == EvqVaryingOut && language == EShLangFragment) { + if (qualifier.layoutLocation >= (unsigned int)resources.maxDrawBuffers) + error(loc, "too large for fragment output", "location", ""); + } + } + if (qualifier.hasComponent()) { + // "It is a compile-time error if this sequence of components gets larger than 3." + if (qualifier.layoutComponent + type.getVectorSize() * (type.getBasicType() == EbtDouble ? 2 : 1) > 4) + error(loc, "type overflows the available 4 components", "component", ""); + + // "It is a compile-time error to apply the component qualifier to a matrix, a structure, a block, or an array containing any of these." + if (type.isMatrix() || type.getBasicType() == EbtBlock || type.getBasicType() == EbtStruct) + error(loc, "cannot apply to a matrix, structure, or block", "component", ""); + + // " It is a compile-time error to use component 1 or 3 as the beginning of a double or dvec2." + if (type.getBasicType() == EbtDouble) + if (qualifier.layoutComponent & 1) + error(loc, "doubles cannot start on an odd-numbered component", "component", ""); + } + + switch (qualifier.storage) { + case EvqVaryingIn: + case EvqVaryingOut: + if (type.getBasicType() == EbtBlock) + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, "location qualifier on in/out block"); + if (type.getQualifier().isTaskMemory()) + error(loc, "cannot apply to taskNV in/out blocks", "location", ""); + break; + case EvqUniform: + case EvqBuffer: + if (type.getBasicType() == EbtBlock) + error(loc, "cannot apply to uniform or buffer block", "location", ""); + break; +#ifndef GLSLANG_WEB + case EvqPayload: + case EvqPayloadIn: + case EvqHitAttr: + case EvqCallableData: + case EvqCallableDataIn: + break; +#endif + default: + error(loc, "can only apply to uniform, buffer, in, or out storage qualifiers", "location", ""); + break; + } + + bool typeCollision; + int repeated = intermediate.addUsedLocation(qualifier, type, typeCollision); + if (repeated >= 0 && ! typeCollision) + error(loc, "overlapping use of location", "location", "%d", repeated); + // "fragment-shader outputs ... if two variables are placed within the same + // location, they must have the same underlying type (floating-point or integer)" + if (typeCollision && language == EShLangFragment && qualifier.isPipeOutput()) + error(loc, "fragment outputs sharing the same location must be the same basic type", "location", "%d", repeated); + } + +#ifndef GLSLANG_WEB + if (qualifier.hasXfbOffset() && qualifier.hasXfbBuffer()) { + int repeated = intermediate.addXfbBufferOffset(type); + if (repeated >= 0) + error(loc, "overlapping offsets at", "xfb_offset", "offset %d in buffer %d", repeated, qualifier.layoutXfbBuffer); + + // "The offset must be a multiple of the size of the first component of the first + // qualified variable or block member, or a compile-time error results. Further, if applied to an aggregate + // containing a double or 64-bit integer, the offset must also be a multiple of 8..." + if ((type.containsBasicType(EbtDouble) || type.containsBasicType(EbtInt64) || type.containsBasicType(EbtUint64)) && + ! IsMultipleOfPow2(qualifier.layoutXfbOffset, 8)) + error(loc, "type contains double or 64-bit integer; xfb_offset must be a multiple of 8", "xfb_offset", ""); + else if ((type.containsBasicType(EbtBool) || type.containsBasicType(EbtFloat) || + type.containsBasicType(EbtInt) || type.containsBasicType(EbtUint)) && + ! IsMultipleOfPow2(qualifier.layoutXfbOffset, 4)) + error(loc, "must be a multiple of size of first component", "xfb_offset", ""); + // ..., if applied to an aggregate containing a half float or 16-bit integer, the offset must also be a multiple of 2..." + else if ((type.contains16BitFloat() || type.containsBasicType(EbtInt16) || type.containsBasicType(EbtUint16)) && + !IsMultipleOfPow2(qualifier.layoutXfbOffset, 2)) + error(loc, "type contains half float or 16-bit integer; xfb_offset must be a multiple of 2", "xfb_offset", ""); + } + if (qualifier.hasXfbStride() && qualifier.hasXfbBuffer()) { + if (! intermediate.setXfbBufferStride(qualifier.layoutXfbBuffer, qualifier.layoutXfbStride)) + error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", qualifier.layoutXfbBuffer); + } +#endif + + if (qualifier.hasBinding()) { + // Binding checking, from the spec: + // + // "If the binding point for any uniform or shader storage block instance is less than zero, or greater than or + // equal to the implementation-dependent maximum number of uniform buffer bindings, a compile-time + // error will occur. When the binding identifier is used with a uniform or shader storage block instanced as + // an array of size N, all elements of the array from binding through binding + N - 1 must be within this + // range." + // + if (! type.isOpaque() && type.getBasicType() != EbtBlock) + error(loc, "requires block, or sampler/image, or atomic-counter type", "binding", ""); + if (type.getBasicType() == EbtSampler) { + int lastBinding = qualifier.layoutBinding; + if (type.isArray()) { + if (spvVersion.vulkan > 0) + lastBinding += 1; + else { + if (type.isSizedArray()) + lastBinding += type.getCumulativeArraySize(); + else { + lastBinding += 1; +#ifndef GLSLANG_WEB + if (spvVersion.vulkan == 0) + warn(loc, "assuming binding count of one for compile-time checking of binding numbers for unsized array", "[]", ""); +#endif + } + } + } +#ifndef GLSLANG_WEB + if (spvVersion.vulkan == 0 && lastBinding >= resources.maxCombinedTextureImageUnits) + error(loc, "sampler binding not less than gl_MaxCombinedTextureImageUnits", "binding", type.isArray() ? "(using array)" : ""); +#endif + } + if (type.isAtomic()) { + if (qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) { + error(loc, "atomic_uint binding is too large; see gl_MaxAtomicCounterBindings", "binding", ""); + return; + } + } + } else if (!intermediate.getAutoMapBindings()) { + // some types require bindings + + // atomic_uint + if (type.isAtomic()) + error(loc, "layout(binding=X) is required", "atomic_uint", ""); + + // SPIR-V + if (spvVersion.spv > 0) { + if (qualifier.isUniformOrBuffer()) { + if (type.getBasicType() == EbtBlock && !qualifier.isPushConstant() && + !qualifier.isShaderRecord() && + !qualifier.hasAttachment() && + !qualifier.hasBufferReference()) + error(loc, "uniform/buffer blocks require layout(binding=X)", "binding", ""); + else if (spvVersion.vulkan > 0 && type.getBasicType() == EbtSampler) + error(loc, "sampler/texture/image requires layout(binding=X)", "binding", ""); + } + } + } + + // some things can't have arrays of arrays + if (type.isArrayOfArrays()) { + if (spvVersion.vulkan > 0) { + if (type.isOpaque() || (type.getQualifier().isUniformOrBuffer() && type.getBasicType() == EbtBlock)) + warn(loc, "Generating SPIR-V array-of-arrays, but Vulkan only supports single array level for this resource", "[][]", ""); + } + } + + // "The offset qualifier can only be used on block members of blocks..." + if (qualifier.hasOffset()) { + if (type.getBasicType() == EbtBlock) + error(loc, "only applies to block members, not blocks", "offset", ""); + } + + // Image format + if (qualifier.hasFormat()) { + if (! type.isImage()) + error(loc, "only apply to images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + else { + if (type.getSampler().type == EbtFloat && qualifier.getFormat() > ElfFloatGuard) + error(loc, "does not apply to floating point images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + if (type.getSampler().type == EbtInt && (qualifier.getFormat() < ElfFloatGuard || qualifier.getFormat() > ElfIntGuard)) + error(loc, "does not apply to signed integer images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + if (type.getSampler().type == EbtUint && qualifier.getFormat() < ElfIntGuard) + error(loc, "does not apply to unsigned integer images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + + if (isEsProfile()) { + // "Except for image variables qualified with the format qualifiers r32f, r32i, and r32ui, image variables must + // specify either memory qualifier readonly or the memory qualifier writeonly." + if (! (qualifier.getFormat() == ElfR32f || qualifier.getFormat() == ElfR32i || qualifier.getFormat() == ElfR32ui)) { + if (! qualifier.isReadOnly() && ! qualifier.isWriteOnly()) + error(loc, "format requires readonly or writeonly memory qualifier", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + } + } + } + } else if (type.isImage() && ! qualifier.isWriteOnly()) { + const char *explanation = "image variables not declared 'writeonly' and without a format layout qualifier"; + requireProfile(loc, ECoreProfile | ECompatibilityProfile, explanation); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 0, E_GL_EXT_shader_image_load_formatted, explanation); + } + + if (qualifier.isPushConstant() && type.getBasicType() != EbtBlock) + error(loc, "can only be used with a block", "push_constant", ""); + + if (qualifier.hasBufferReference() && type.getBasicType() != EbtBlock) + error(loc, "can only be used with a block", "buffer_reference", ""); + + if (qualifier.isShaderRecord() && type.getBasicType() != EbtBlock) + error(loc, "can only be used with a block", "shaderRecordNV", ""); + + // input attachment + if (type.isSubpass()) { + if (! qualifier.hasAttachment()) + error(loc, "requires an input_attachment_index layout qualifier", "subpass", ""); + } else { + if (qualifier.hasAttachment()) + error(loc, "can only be used with a subpass", "input_attachment_index", ""); + } + + // specialization-constant id + if (qualifier.hasSpecConstantId()) { + if (type.getQualifier().storage != EvqConst) + error(loc, "can only be applied to 'const'-qualified scalar", "constant_id", ""); + if (! type.isScalar()) + error(loc, "can only be applied to a scalar", "constant_id", ""); + switch (type.getBasicType()) + { + case EbtInt8: + case EbtUint8: + case EbtInt16: + case EbtUint16: + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: + case EbtBool: + case EbtFloat: + case EbtDouble: + case EbtFloat16: + break; + default: + error(loc, "cannot be applied to this type", "constant_id", ""); + break; + } + } +} + +// Do layout error checking that can be done within a layout qualifier proper, not needing to know +// if there are blocks, atomic counters, variables, etc. +void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier& qualifier) +{ + if (qualifier.storage == EvqShared && qualifier.hasLayout()) + error(loc, "cannot apply layout qualifiers to a shared variable", "shared", ""); + + // "It is a compile-time error to use *component* without also specifying the location qualifier (order does not matter)." + if (qualifier.hasComponent() && ! qualifier.hasLocation()) + error(loc, "must specify 'location' to use 'component'", "component", ""); + + if (qualifier.hasAnyLocation()) { + + // "As with input layout qualifiers, all shaders except compute shaders + // allow *location* layout qualifiers on output variable declarations, + // output block declarations, and output block member declarations." + + switch (qualifier.storage) { +#ifndef GLSLANG_WEB + case EvqVaryingIn: + { + const char* feature = "location qualifier on input"; + if (isEsProfile() && version < 310) + requireStage(loc, EShLangVertex, feature); + else + requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature); + if (language == EShLangVertex) { + const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + profileRequires(loc, ~EEsProfile, 330, 2, exts, feature); + profileRequires(loc, EEsProfile, 300, nullptr, feature); + } else { + profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_separate_shader_objects, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + } + break; + } + case EvqVaryingOut: + { + const char* feature = "location qualifier on output"; + if (isEsProfile() && version < 310) + requireStage(loc, EShLangFragment, feature); + else + requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature); + if (language == EShLangFragment) { + const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + profileRequires(loc, ~EEsProfile, 330, 2, exts, feature); + profileRequires(loc, EEsProfile, 300, nullptr, feature); + } else { + profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_separate_shader_objects, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + } + break; + } +#endif + case EvqUniform: + case EvqBuffer: + { + const char* feature = "location qualifier on uniform or buffer"; + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile | ENoProfile, feature); + profileRequires(loc, ~EEsProfile, 330, E_GL_ARB_explicit_attrib_location, feature); + profileRequires(loc, ~EEsProfile, 430, E_GL_ARB_explicit_uniform_location, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + break; + } + default: + break; + } + if (qualifier.hasIndex()) { + if (qualifier.storage != EvqVaryingOut) + error(loc, "can only be used on an output", "index", ""); + if (! qualifier.hasLocation()) + error(loc, "can only be used with an explicit location", "index", ""); + } + } + + if (qualifier.hasBinding()) { + if (! qualifier.isUniformOrBuffer() && !qualifier.isTaskMemory()) + error(loc, "requires uniform or buffer storage qualifier", "binding", ""); + } + if (qualifier.hasStream()) { + if (!qualifier.isPipeOutput()) + error(loc, "can only be used on an output", "stream", ""); + } + if (qualifier.hasXfb()) { + if (!qualifier.isPipeOutput()) + error(loc, "can only be used on an output", "xfb layout qualifier", ""); + } + if (qualifier.hasUniformLayout()) { + if (! qualifier.isUniformOrBuffer() && !qualifier.isTaskMemory()) { + if (qualifier.hasMatrix() || qualifier.hasPacking()) + error(loc, "matrix or packing qualifiers can only be used on a uniform or buffer", "layout", ""); + if (qualifier.hasOffset() || qualifier.hasAlign()) + error(loc, "offset/align can only be used on a uniform or buffer", "layout", ""); + } + } + if (qualifier.isPushConstant()) { + if (qualifier.storage != EvqUniform) + error(loc, "can only be used with a uniform", "push_constant", ""); + if (qualifier.hasSet()) + error(loc, "cannot be used with push_constant", "set", ""); + } + if (qualifier.hasBufferReference()) { + if (qualifier.storage != EvqBuffer) + error(loc, "can only be used with buffer", "buffer_reference", ""); + } + if (qualifier.isShaderRecord()) { + if (qualifier.storage != EvqBuffer) + error(loc, "can only be used with a buffer", "shaderRecordNV", ""); + if (qualifier.hasBinding()) + error(loc, "cannot be used with shaderRecordNV", "binding", ""); + if (qualifier.hasSet()) + error(loc, "cannot be used with shaderRecordNV", "set", ""); + + } + if (qualifier.storage == EvqHitAttr && qualifier.hasLayout()) { + error(loc, "cannot apply layout qualifiers to hitAttributeNV variable", "hitAttributeNV", ""); + } +} + +// For places that can't have shader-level layout qualifiers +void TParseContext::checkNoShaderLayouts(const TSourceLoc& loc, const TShaderQualifiers& shaderQualifiers) +{ +#ifndef GLSLANG_WEB + const char* message = "can only apply to a standalone qualifier"; + + if (shaderQualifiers.geometry != ElgNone) + error(loc, message, TQualifier::getGeometryString(shaderQualifiers.geometry), ""); + if (shaderQualifiers.spacing != EvsNone) + error(loc, message, TQualifier::getVertexSpacingString(shaderQualifiers.spacing), ""); + if (shaderQualifiers.order != EvoNone) + error(loc, message, TQualifier::getVertexOrderString(shaderQualifiers.order), ""); + if (shaderQualifiers.pointMode) + error(loc, message, "point_mode", ""); + if (shaderQualifiers.invocations != TQualifier::layoutNotSet) + error(loc, message, "invocations", ""); + for (int i = 0; i < 3; ++i) { + if (shaderQualifiers.localSize[i] > 1) + error(loc, message, "local_size", ""); + if (shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) + error(loc, message, "local_size id", ""); + } + if (shaderQualifiers.vertices != TQualifier::layoutNotSet) { + if (language == EShLangGeometry || language == EShLangMeshNV) + error(loc, message, "max_vertices", ""); + else if (language == EShLangTessControl) + error(loc, message, "vertices", ""); + else + assert(0); + } + if (shaderQualifiers.earlyFragmentTests) + error(loc, message, "early_fragment_tests", ""); + if (shaderQualifiers.postDepthCoverage) + error(loc, message, "post_depth_coverage", ""); + if (shaderQualifiers.primitives != TQualifier::layoutNotSet) { + if (language == EShLangMeshNV) + error(loc, message, "max_primitives", ""); + else + assert(0); + } + if (shaderQualifiers.hasBlendEquation()) + error(loc, message, "blend equation", ""); + if (shaderQualifiers.numViews != TQualifier::layoutNotSet) + error(loc, message, "num_views", ""); + if (shaderQualifiers.interlockOrdering != EioNone) + error(loc, message, TQualifier::getInterlockOrderingString(shaderQualifiers.interlockOrdering), ""); +#endif +} + +// Correct and/or advance an object's offset layout qualifier. +void TParseContext::fixOffset(const TSourceLoc& loc, TSymbol& symbol) +{ + const TQualifier& qualifier = symbol.getType().getQualifier(); +#ifndef GLSLANG_WEB + if (symbol.getType().isAtomic()) { + if (qualifier.hasBinding() && (int)qualifier.layoutBinding < resources.maxAtomicCounterBindings) { + + // Set the offset + int offset; + if (qualifier.hasOffset()) + offset = qualifier.layoutOffset; + else + offset = atomicUintOffsets[qualifier.layoutBinding]; + + if (offset % 4 != 0) + error(loc, "atomic counters offset should align based on 4:", "offset", "%d", offset); + + symbol.getWritableType().getQualifier().layoutOffset = offset; + + // Check for overlap + int numOffsets = 4; + if (symbol.getType().isArray()) { + if (symbol.getType().isSizedArray() && !symbol.getType().getArraySizes()->isInnerUnsized()) + numOffsets *= symbol.getType().getCumulativeArraySize(); + else { + // "It is a compile-time error to declare an unsized array of atomic_uint." + error(loc, "array must be explicitly sized", "atomic_uint", ""); + } + } + int repeated = intermediate.addUsedOffsets(qualifier.layoutBinding, offset, numOffsets); + if (repeated >= 0) + error(loc, "atomic counters sharing the same offset:", "offset", "%d", repeated); + + // Bump the default offset + atomicUintOffsets[qualifier.layoutBinding] = offset + numOffsets; + } + } +#endif +} + +// +// Look up a function name in the symbol table, and make sure it is a function. +// +// Return the function symbol if found, otherwise nullptr. +// +const TFunction* TParseContext::findFunction(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + if (symbolTable.isFunctionNameVariable(call.getName())) { + error(loc, "can't use function syntax on variable", call.getName().c_str(), ""); + return nullptr; + } + +#ifdef GLSLANG_WEB + return findFunctionExact(loc, call, builtIn); +#endif + + const TFunction* function = nullptr; + + // debugPrintfEXT has var args and is in the symbol table as "debugPrintfEXT()", + // mangled to "debugPrintfEXT(" + if (call.getName() == "debugPrintfEXT") { + TSymbol* symbol = symbolTable.find("debugPrintfEXT(", &builtIn); + if (symbol) + return symbol->getAsFunction(); + } + + bool explicitTypesEnabled = extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int8) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int16) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int32) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int64) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float16) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float32) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float64); + + if (isEsProfile()) + function = (extensionTurnedOn(E_GL_EXT_shader_implicit_conversions) && version >= 310) ? + findFunction120(loc, call, builtIn) : findFunctionExact(loc, call, builtIn); + else if (version < 120) + function = findFunctionExact(loc, call, builtIn); + else if (version < 400) + function = extensionTurnedOn(E_GL_ARB_gpu_shader_fp64) ? findFunction400(loc, call, builtIn) : findFunction120(loc, call, builtIn); + else if (explicitTypesEnabled) + function = findFunctionExplicitTypes(loc, call, builtIn); + else + function = findFunction400(loc, call, builtIn); + + return function; +} + +// Function finding algorithm for ES and desktop 110. +const TFunction* TParseContext::findFunctionExact(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); + if (symbol == nullptr) { + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + + return nullptr; + } + + return symbol->getAsFunction(); +} + +// Function finding algorithm for desktop versions 120 through 330. +const TFunction* TParseContext::findFunction120(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + // first, look for an exact match + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); + if (symbol) + return symbol->getAsFunction(); + + // exact match not found, look through a list of overloaded functions of the same name + + // "If no exact match is found, then [implicit conversions] will be applied to find a match. Mismatched types + // on input parameters (in or inout or default) must have a conversion from the calling argument type to the + // formal parameter type. Mismatched types on output parameters (out or inout) must have a conversion + // from the formal parameter type to the calling argument type. When argument conversions are used to find + // a match, it is a semantic error if there are multiple ways to apply these conversions to make the call match + // more than one function." + + const TFunction* candidate = nullptr; + TVector candidateList; + symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); + + for (auto it = candidateList.begin(); it != candidateList.end(); ++it) { + const TFunction& function = *(*it); + + // to even be a potential match, number of arguments has to match + if (call.getParamCount() != function.getParamCount()) + continue; + + bool possibleMatch = true; + for (int i = 0; i < function.getParamCount(); ++i) { + // same types is easy + if (*function[i].type == *call[i].type) + continue; + + // We have a mismatch in type, see if it is implicitly convertible + + if (function[i].type->isArray() || call[i].type->isArray() || + ! function[i].type->sameElementShape(*call[i].type)) + possibleMatch = false; + else { + // do direction-specific checks for conversion of basic type + if (function[i].type->getQualifier().isParamInput()) { + if (! intermediate.canImplicitlyPromote(call[i].type->getBasicType(), function[i].type->getBasicType())) + possibleMatch = false; + } + if (function[i].type->getQualifier().isParamOutput()) { + if (! intermediate.canImplicitlyPromote(function[i].type->getBasicType(), call[i].type->getBasicType())) + possibleMatch = false; + } + } + if (! possibleMatch) + break; + } + if (possibleMatch) { + if (candidate) { + // our second match, meaning ambiguity + error(loc, "ambiguous function signature match: multiple signatures match under implicit type conversion", call.getName().c_str(), ""); + } else + candidate = &function; + } + } + + if (candidate == nullptr) + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + + return candidate; +} + +// Function finding algorithm for desktop version 400 and above. +// +// "When function calls are resolved, an exact type match for all the arguments +// is sought. If an exact match is found, all other functions are ignored, and +// the exact match is used. If no exact match is found, then the implicit +// conversions in section 4.1.10 Implicit Conversions will be applied to find +// a match. Mismatched types on input parameters (in or inout or default) must +// have a conversion from the calling argument type to the formal parameter type. +// Mismatched types on output parameters (out or inout) must have a conversion +// from the formal parameter type to the calling argument type. +// +// "If implicit conversions can be used to find more than one matching function, +// a single best-matching function is sought. To determine a best match, the +// conversions between calling argument and formal parameter types are compared +// for each function argument and pair of matching functions. After these +// comparisons are performed, each pair of matching functions are compared. +// A function declaration A is considered a better match than function +// declaration B if +// +// * for at least one function argument, the conversion for that argument in A +// is better than the corresponding conversion in B; and +// * there is no function argument for which the conversion in B is better than +// the corresponding conversion in A. +// +// "If a single function declaration is considered a better match than every +// other matching function declaration, it will be used. Otherwise, a +// compile-time semantic error for an ambiguous overloaded function call occurs. +// +// "To determine whether the conversion for a single argument in one match is +// better than that for another match, the following rules are applied, in order: +// +// 1. An exact match is better than a match involving any implicit conversion. +// 2. A match involving an implicit conversion from float to double is better +// than a match involving any other implicit conversion. +// 3. A match involving an implicit conversion from either int or uint to float +// is better than a match involving an implicit conversion from either int +// or uint to double. +// +// "If none of the rules above apply to a particular pair of conversions, neither +// conversion is considered better than the other." +// +const TFunction* TParseContext::findFunction400(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + // first, look for an exact match + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); + if (symbol) + return symbol->getAsFunction(); + + // no exact match, use the generic selector, parameterized by the GLSL rules + + // create list of candidates to send + TVector candidateList; + symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); + + // can 'from' convert to 'to'? + const auto convertible = [this,builtIn](const TType& from, const TType& to, TOperator, int) -> bool { + if (from == to) + return true; + if (from.coopMatParameterOK(to)) + return true; + // Allow a sized array to be passed through an unsized array parameter, for coopMatLoad/Store functions + if (builtIn && from.isArray() && to.isUnsizedArray()) { + TType fromElementType(from, 0); + TType toElementType(to, 0); + if (fromElementType == toElementType) + return true; + } + if (from.isArray() || to.isArray() || ! from.sameElementShape(to)) + return false; + if (from.isCoopMat() && to.isCoopMat()) + return from.sameCoopMatBaseType(to); + return intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType()); + }; + + // Is 'to2' a better conversion than 'to1'? + // Ties should not be considered as better. + // Assumes 'convertible' already said true. + const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool { + // 1. exact match + if (from == to2) + return from != to1; + if (from == to1) + return false; + + // 2. float -> double is better + if (from.getBasicType() == EbtFloat) { + if (to2.getBasicType() == EbtDouble && to1.getBasicType() != EbtDouble) + return true; + } + + // 3. -> float is better than -> double + return to2.getBasicType() == EbtFloat && to1.getBasicType() == EbtDouble; + }; + + // for ambiguity reporting + bool tie = false; + + // send to the generic selector + const TFunction* bestMatch = selectFunction(candidateList, call, convertible, better, tie); + + if (bestMatch == nullptr) + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + else if (tie) + error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), ""); + + return bestMatch; +} + +// "To determine whether the conversion for a single argument in one match +// is better than that for another match, the conversion is assigned of the +// three ranks ordered from best to worst: +// 1. Exact match: no conversion. +// 2. Promotion: integral or floating-point promotion. +// 3. Conversion: integral conversion, floating-point conversion, +// floating-integral conversion. +// A conversion C1 is better than a conversion C2 if the rank of C1 is +// better than the rank of C2." +const TFunction* TParseContext::findFunctionExplicitTypes(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + // first, look for an exact match + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); + if (symbol) + return symbol->getAsFunction(); + + // no exact match, use the generic selector, parameterized by the GLSL rules + + // create list of candidates to send + TVector candidateList; + symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); + + // can 'from' convert to 'to'? + const auto convertible = [this,builtIn](const TType& from, const TType& to, TOperator, int) -> bool { + if (from == to) + return true; + if (from.coopMatParameterOK(to)) + return true; + // Allow a sized array to be passed through an unsized array parameter, for coopMatLoad/Store functions + if (builtIn && from.isArray() && to.isUnsizedArray()) { + TType fromElementType(from, 0); + TType toElementType(to, 0); + if (fromElementType == toElementType) + return true; + } + if (from.isArray() || to.isArray() || ! from.sameElementShape(to)) + return false; + if (from.isCoopMat() && to.isCoopMat()) + return from.sameCoopMatBaseType(to); + return intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType()); + }; + + // Is 'to2' a better conversion than 'to1'? + // Ties should not be considered as better. + // Assumes 'convertible' already said true. + const auto better = [this](const TType& from, const TType& to1, const TType& to2) -> bool { + // 1. exact match + if (from == to2) + return from != to1; + if (from == to1) + return false; + + // 2. Promotion (integral, floating-point) is better + TBasicType from_type = from.getBasicType(); + TBasicType to1_type = to1.getBasicType(); + TBasicType to2_type = to2.getBasicType(); + bool isPromotion1 = (intermediate.isIntegralPromotion(from_type, to1_type) || + intermediate.isFPPromotion(from_type, to1_type)); + bool isPromotion2 = (intermediate.isIntegralPromotion(from_type, to2_type) || + intermediate.isFPPromotion(from_type, to2_type)); + if (isPromotion2) + return !isPromotion1; + if(isPromotion1) + return false; + + // 3. Conversion (integral, floating-point , floating-integral) + bool isConversion1 = (intermediate.isIntegralConversion(from_type, to1_type) || + intermediate.isFPConversion(from_type, to1_type) || + intermediate.isFPIntegralConversion(from_type, to1_type)); + bool isConversion2 = (intermediate.isIntegralConversion(from_type, to2_type) || + intermediate.isFPConversion(from_type, to2_type) || + intermediate.isFPIntegralConversion(from_type, to2_type)); + + return isConversion2 && !isConversion1; + }; + + // for ambiguity reporting + bool tie = false; + + // send to the generic selector + const TFunction* bestMatch = selectFunction(candidateList, call, convertible, better, tie); + + if (bestMatch == nullptr) + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + else if (tie) + error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), ""); + + return bestMatch; +} + +// When a declaration includes a type, but not a variable name, it can be used +// to establish defaults. +void TParseContext::declareTypeDefaults(const TSourceLoc& loc, const TPublicType& publicType) +{ +#ifndef GLSLANG_WEB + if (publicType.basicType == EbtAtomicUint && publicType.qualifier.hasBinding()) { + if (publicType.qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) { + error(loc, "atomic_uint binding is too large", "binding", ""); + return; + } + + if(publicType.qualifier.hasOffset()) { + atomicUintOffsets[publicType.qualifier.layoutBinding] = publicType.qualifier.layoutOffset; + } + return; + } + + if (publicType.qualifier.hasLayout() && !publicType.qualifier.hasBufferReference()) + warn(loc, "useless application of layout qualifier", "layout", ""); +#endif +} + +// +// Do everything necessary to handle a variable (non-block) declaration. +// Either redeclaring a variable, or making a new one, updating the symbol +// table, and all error checking. +// +// Returns a subtree node that computes an initializer, if needed. +// Returns nullptr if there is no code to execute for initialization. +// +// 'publicType' is the type part of the declaration (to the left) +// 'arraySizes' is the arrayness tagged on the identifier (to the right) +// +TIntermNode* TParseContext::declareVariable(const TSourceLoc& loc, TString& identifier, const TPublicType& publicType, + TArraySizes* arraySizes, TIntermTyped* initializer) +{ + // Make a fresh type that combines the characteristics from the individual + // identifier syntax and the declaration-type syntax. + TType type(publicType); + type.transferArraySizes(arraySizes); + type.copyArrayInnerSizes(publicType.arraySizes); + arrayOfArrayVersionCheck(loc, type.getArraySizes()); + + if (type.isCoopMat()) { + intermediate.setUseVulkanMemoryModel(); + intermediate.setUseStorageBuffer(); + + if (!publicType.typeParameters || publicType.typeParameters->getNumDims() != 4) { + error(loc, "expected four type parameters", identifier.c_str(), ""); + } + if (publicType.typeParameters) { + if (isTypeFloat(publicType.basicType) && + publicType.typeParameters->getDimSize(0) != 16 && + publicType.typeParameters->getDimSize(0) != 32 && + publicType.typeParameters->getDimSize(0) != 64) { + error(loc, "expected 16, 32, or 64 bits for first type parameter", identifier.c_str(), ""); + } + if (isTypeInt(publicType.basicType) && + publicType.typeParameters->getDimSize(0) != 8 && + publicType.typeParameters->getDimSize(0) != 32) { + error(loc, "expected 8 or 32 bits for first type parameter", identifier.c_str(), ""); + } + } + + } else { + if (publicType.typeParameters && publicType.typeParameters->getNumDims() != 0) { + error(loc, "unexpected type parameters", identifier.c_str(), ""); + } + } + + if (voidErrorCheck(loc, identifier, type.getBasicType())) + return nullptr; + + if (initializer) + rValueErrorCheck(loc, "initializer", initializer); + else + nonInitConstCheck(loc, identifier, type); + + samplerCheck(loc, type, identifier, initializer); + transparentOpaqueCheck(loc, type, identifier); +#ifndef GLSLANG_WEB + atomicUintCheck(loc, type, identifier); + accStructCheck(loc, type, identifier); + checkAndResizeMeshViewDim(loc, type, /*isBlockMember*/ false); +#endif + if (type.getQualifier().storage == EvqConst && type.containsReference()) { + error(loc, "variables with reference type can't have qualifier 'const'", "qualifier", ""); + } + + if (type.getQualifier().storage != EvqUniform && type.getQualifier().storage != EvqBuffer) { + if (type.contains16BitFloat()) + requireFloat16Arithmetic(loc, "qualifier", "float16 types can only be in uniform block or buffer storage"); + if (type.contains16BitInt()) + requireInt16Arithmetic(loc, "qualifier", "(u)int16 types can only be in uniform block or buffer storage"); + if (type.contains8BitInt()) + requireInt8Arithmetic(loc, "qualifier", "(u)int8 types can only be in uniform block or buffer storage"); + } + + if (type.getQualifier().storage == EvqShared && type.containsCoopMat()) + error(loc, "qualifier", "Cooperative matrix types must not be used in shared memory", ""); + + if (identifier != "gl_FragCoord" && (publicType.shaderQualifiers.originUpperLeft || publicType.shaderQualifiers.pixelCenterInteger)) + error(loc, "can only apply origin_upper_left and pixel_center_origin to gl_FragCoord", "layout qualifier", ""); + if (identifier != "gl_FragDepth" && publicType.shaderQualifiers.getDepth() != EldNone) + error(loc, "can only apply depth layout to gl_FragDepth", "layout qualifier", ""); + + // Check for redeclaration of built-ins and/or attempting to declare a reserved name + TSymbol* symbol = redeclareBuiltinVariable(loc, identifier, type.getQualifier(), publicType.shaderQualifiers); + if (symbol == nullptr) + reservedErrorCheck(loc, identifier); + + inheritGlobalDefaults(type.getQualifier()); + + // Declare the variable + if (type.isArray()) { + // Check that implicit sizing is only where allowed. + arraySizesCheck(loc, type.getQualifier(), type.getArraySizes(), initializer, false); + + if (! arrayQualifierError(loc, type.getQualifier()) && ! arrayError(loc, type)) + declareArray(loc, identifier, type, symbol); + + if (initializer) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, "initializer"); + profileRequires(loc, EEsProfile, 300, nullptr, "initializer"); + } + } else { + // non-array case + if (symbol == nullptr) + symbol = declareNonArray(loc, identifier, type); + else if (type != symbol->getType()) + error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str()); + } + + if (symbol == nullptr) + return nullptr; + + // Deal with initializer + TIntermNode* initNode = nullptr; + if (symbol != nullptr && initializer) { + TVariable* variable = symbol->getAsVariable(); + if (! variable) { + error(loc, "initializer requires a variable, not a member", identifier.c_str(), ""); + return nullptr; + } + initNode = executeInitializer(loc, initializer, variable); + } + + // look for errors in layout qualifier use + layoutObjectCheck(loc, *symbol); + + // fix up + fixOffset(loc, *symbol); + + return initNode; +} + +// Pick up global defaults from the provide global defaults into dst. +void TParseContext::inheritGlobalDefaults(TQualifier& dst) const +{ +#ifndef GLSLANG_WEB + if (dst.storage == EvqVaryingOut) { + if (! dst.hasStream() && language == EShLangGeometry) + dst.layoutStream = globalOutputDefaults.layoutStream; + if (! dst.hasXfbBuffer()) + dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; + } +#endif +} + +// +// Make an internal-only variable whose name is for debug purposes only +// and won't be searched for. Callers will only use the return value to use +// the variable, not the name to look it up. It is okay if the name +// is the same as other names; there won't be any conflict. +// +TVariable* TParseContext::makeInternalVariable(const char* name, const TType& type) const +{ + TString* nameString = NewPoolTString(name); + TVariable* variable = new TVariable(nameString, type); + symbolTable.makeInternalVariable(*variable); + + return variable; +} + +// +// Declare a non-array variable, the main point being there is no redeclaration +// for resizing allowed. +// +// Return the successfully declared variable. +// +TVariable* TParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type) +{ + // make a new variable + TVariable* variable = new TVariable(&identifier, type); + +#ifndef GLSLANG_WEB + ioArrayCheck(loc, type, identifier); +#endif + + // add variable to symbol table + if (symbolTable.insert(*variable)) { + if (symbolTable.atGlobalLevel()) + trackLinkage(*variable); + return variable; + } + + error(loc, "redefinition", variable->getName().c_str(), ""); + return nullptr; +} + +// +// Handle all types of initializers from the grammar. +// +// Returning nullptr just means there is no code to execute to handle the +// initializer, which will, for example, be the case for constant initializers. +// +TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable) +{ + // + // Identifier must be of type constant, a global, or a temporary, and + // starting at version 120, desktop allows uniforms to have initializers. + // + TStorageQualifier qualifier = variable->getType().getQualifier().storage; + if (! (qualifier == EvqTemporary || qualifier == EvqGlobal || qualifier == EvqConst || + (qualifier == EvqUniform && !isEsProfile() && version >= 120))) { + error(loc, " cannot initialize this type of qualifier ", variable->getType().getStorageQualifierString(), ""); + return nullptr; + } + arrayObjectCheck(loc, variable->getType(), "array initializer"); + + // + // If the initializer was from braces { ... }, we convert the whole subtree to a + // constructor-style subtree, allowing the rest of the code to operate + // identically for both kinds of initializers. + // + // Type can't be deduced from the initializer list, so a skeletal type to + // follow has to be passed in. Constness and specialization-constness + // should be deduced bottom up, not dictated by the skeletal type. + // + TType skeletalType; + skeletalType.shallowCopy(variable->getType()); + skeletalType.getQualifier().makeTemporary(); +#ifndef GLSLANG_WEB + initializer = convertInitializerList(loc, skeletalType, initializer); +#endif + if (! initializer) { + // error recovery; don't leave const without constant values + if (qualifier == EvqConst) + variable->getWritableType().getQualifier().makeTemporary(); + return nullptr; + } + + // Fix outer arrayness if variable is unsized, getting size from the initializer + if (initializer->getType().isSizedArray() && variable->getType().isUnsizedArray()) + variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize()); + + // Inner arrayness can also get set by an initializer + if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() && + initializer->getType().getArraySizes()->getNumDims() == + variable->getType().getArraySizes()->getNumDims()) { + // adopt unsized sizes from the initializer's sizes + for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) { + if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize) { + variable->getWritableType().getArraySizes()->setDimSize(d, + initializer->getType().getArraySizes()->getDimSize(d)); + } + } + } + + // Uniforms require a compile-time constant initializer + if (qualifier == EvqUniform && ! initializer->getType().getQualifier().isFrontEndConstant()) { + error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str()); + variable->getWritableType().getQualifier().makeTemporary(); + return nullptr; + } + // Global consts require a constant initializer (specialization constant is okay) + if (qualifier == EvqConst && symbolTable.atGlobalLevel() && ! initializer->getType().getQualifier().isConstant()) { + error(loc, "global const initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str()); + variable->getWritableType().getQualifier().makeTemporary(); + return nullptr; + } + + // Const variables require a constant initializer, depending on version + if (qualifier == EvqConst) { + if (! initializer->getType().getQualifier().isConstant()) { + const char* initFeature = "non-constant initializer"; + requireProfile(loc, ~EEsProfile, initFeature); + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + variable->getWritableType().getQualifier().storage = EvqConstReadOnly; + qualifier = EvqConstReadOnly; + } + } else { + // Non-const global variables in ES need a const initializer. + // + // "In declarations of global variables with no storage qualifier or with a const + // qualifier any initializer must be a constant expression." + if (symbolTable.atGlobalLevel() && ! initializer->getType().getQualifier().isConstant()) { + const char* initFeature = "non-constant global initializer (needs GL_EXT_shader_non_constant_global_initializers)"; + if (isEsProfile()) { + if (relaxedErrors() && ! extensionTurnedOn(E_GL_EXT_shader_non_constant_global_initializers)) + warn(loc, "not allowed in this version", initFeature, ""); + else + profileRequires(loc, EEsProfile, 0, E_GL_EXT_shader_non_constant_global_initializers, initFeature); + } + } + } + + if (qualifier == EvqConst || qualifier == EvqUniform) { + // Compile-time tagging of the variable with its constant value... + + initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer); + if (! initializer || ! initializer->getType().getQualifier().isConstant() || variable->getType() != initializer->getType()) { + error(loc, "non-matching or non-convertible constant type for const initializer", + variable->getType().getStorageQualifierString(), ""); + variable->getWritableType().getQualifier().makeTemporary(); + return nullptr; + } + + // We either have a folded constant in getAsConstantUnion, or we have to use + // the initializer's subtree in the AST to represent the computation of a + // specialization constant. + assert(initializer->getAsConstantUnion() || initializer->getType().getQualifier().isSpecConstant()); + if (initializer->getAsConstantUnion()) + variable->setConstArray(initializer->getAsConstantUnion()->getConstArray()); + else { + // It's a specialization constant. + variable->getWritableType().getQualifier().makeSpecConstant(); + + // Keep the subtree that computes the specialization constant with the variable. + // Later, a symbol node will adopt the subtree from the variable. + variable->setConstSubtree(initializer); + } + } else { + // normal assigning of a value to a variable... + specializationCheck(loc, initializer->getType(), "initializer"); + TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc); + TIntermTyped* initNode = intermediate.addAssign(EOpAssign, intermSymbol, initializer, loc); + if (! initNode) + assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); + + return initNode; + } + + return nullptr; +} + +// +// Reprocess any initializer-list (the "{ ... }" syntax) parts of the +// initializer. +// +// Need to hierarchically assign correct types and implicit +// conversions. Will do this mimicking the same process used for +// creating a constructor-style initializer, ensuring we get the +// same form. However, it has to in parallel walk the 'type' +// passed in, as type cannot be deduced from an initializer list. +// +TIntermTyped* TParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type, TIntermTyped* initializer) +{ + // Will operate recursively. Once a subtree is found that is constructor style, + // everything below it is already good: Only the "top part" of the initializer + // can be an initializer list, where "top part" can extend for several (or all) levels. + + // see if we have bottomed out in the tree within the initializer-list part + TIntermAggregate* initList = initializer->getAsAggregate(); + if (! initList || initList->getOp() != EOpNull) + return initializer; + + // Of the initializer-list set of nodes, need to process bottom up, + // so recurse deep, then process on the way up. + + // Go down the tree here... + if (type.isArray()) { + // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate. + // Later on, initializer execution code will deal with array size logic. + TType arrayType; + arrayType.shallowCopy(type); // sharing struct stuff is fine + arrayType.copyArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below + + // edit array sizes to fill in unsized dimensions + arrayType.changeOuterArraySize((int)initList->getSequence().size()); + TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped(); + if (arrayType.isArrayOfArrays() && firstInit->getType().isArray() && + arrayType.getArraySizes()->getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) { + for (int d = 1; d < arrayType.getArraySizes()->getNumDims(); ++d) { + if (arrayType.getArraySizes()->getDimSize(d) == UnsizedArraySize) + arrayType.getArraySizes()->setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1)); + } + } + + TType elementType(arrayType, 0); // dereferenced type + for (size_t i = 0; i < initList->getSequence().size(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, elementType, initList->getSequence()[i]->getAsTyped()); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + + return addConstructor(loc, initList, arrayType); + } else if (type.isStruct()) { + if (type.getStruct()->size() != initList->getSequence().size()) { + error(loc, "wrong number of structure members", "initializer list", ""); + return nullptr; + } + for (size_t i = 0; i < type.getStruct()->size(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type, initList->getSequence()[i]->getAsTyped()); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + } else if (type.isMatrix()) { + if (type.getMatrixCols() != (int)initList->getSequence().size()) { + error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str()); + return nullptr; + } + TType vectorType(type, 0); // dereferenced type + for (int i = 0; i < type.getMatrixCols(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, vectorType, initList->getSequence()[i]->getAsTyped()); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + } else if (type.isVector()) { + if (type.getVectorSize() != (int)initList->getSequence().size()) { + error(loc, "wrong vector size (or rows in a matrix column):", "initializer list", type.getCompleteString().c_str()); + return nullptr; + } + } else { + error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str()); + return nullptr; + } + + // Now that the subtree is processed, process this node as if the + // initializer list is a set of arguments to a constructor. + TIntermNode* emulatedConstructorArguments; + if (initList->getSequence().size() == 1) + emulatedConstructorArguments = initList->getSequence()[0]; + else + emulatedConstructorArguments = initList; + return addConstructor(loc, emulatedConstructorArguments, type); +} + +// +// Test for the correctness of the parameters passed to various constructor functions +// and also convert them to the right data type, if allowed and required. +// +// 'node' is what to construct from. +// 'type' is what type to construct. +// +// Returns nullptr for an error or the constructed node (aggregate or typed) for no error. +// +TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode* node, const TType& type) +{ + if (node == nullptr || node->getAsTyped() == nullptr) + return nullptr; + rValueErrorCheck(loc, "constructor", node->getAsTyped()); + + TIntermAggregate* aggrNode = node->getAsAggregate(); + TOperator op = intermediate.mapTypeToConstructorOp(type); + + // Combined texture-sampler constructors are completely semantic checked + // in constructorTextureSamplerError() + if (op == EOpConstructTextureSampler) { + if (aggrNode->getSequence()[1]->getAsTyped()->getType().getSampler().shadow) { + // Transfer depth into the texture (SPIR-V image) type, as a hint + // for tools to know this texture/image is a depth image. + aggrNode->getSequence()[0]->getAsTyped()->getWritableType().getSampler().shadow = true; + } + return intermediate.setAggregateOperator(aggrNode, op, type, loc); + } + + TTypeList::const_iterator memberTypes; + if (op == EOpConstructStruct) + memberTypes = type.getStruct()->begin(); + + TType elementType; + if (type.isArray()) { + TType dereferenced(type, 0); + elementType.shallowCopy(dereferenced); + } else + elementType.shallowCopy(type); + + bool singleArg; + if (aggrNode) { + if (aggrNode->getOp() != EOpNull) + singleArg = true; + else + singleArg = false; + } else + singleArg = true; + + TIntermTyped *newNode; + if (singleArg) { + // If structure constructor or array constructor is being called + // for only one parameter inside the structure, we need to call constructAggregate function once. + if (type.isArray()) + newNode = constructAggregate(node, elementType, 1, node->getLoc()); + else if (op == EOpConstructStruct) + newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc()); + else + newNode = constructBuiltIn(type, op, node->getAsTyped(), node->getLoc(), false); + + if (newNode && (type.isArray() || op == EOpConstructStruct)) + newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc); + + return newNode; + } + + // + // Handle list of arguments. + // + TIntermSequence &sequenceVector = aggrNode->getSequence(); // Stores the information about the parameter to the constructor + // if the structure constructor contains more than one parameter, then construct + // each parameter + + int paramCount = 0; // keeps track of the constructor parameter number being checked + + // for each parameter to the constructor call, check to see if the right type is passed or convert them + // to the right type if possible (and allowed). + // for structure constructors, just check if the right type is passed, no conversion is allowed. + for (TIntermSequence::iterator p = sequenceVector.begin(); + p != sequenceVector.end(); p++, paramCount++) { + if (type.isArray()) + newNode = constructAggregate(*p, elementType, paramCount+1, node->getLoc()); + else if (op == EOpConstructStruct) + newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount+1, node->getLoc()); + else + newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true); + + if (newNode) + *p = newNode; + else + return nullptr; + } + + return intermediate.setAggregateOperator(aggrNode, op, type, loc); +} + +// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value +// for the parameter to the constructor (passed to this function). Essentially, it converts +// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a +// float, then float is converted to int. +// +// Returns nullptr for an error or the constructed node. +// +TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node, const TSourceLoc& loc, + bool subset) +{ + // If we are changing a matrix in both domain of basic type and to a non matrix, + // do the shape change first (by default, below, basic type is changed before shape). + // This avoids requesting a matrix of a new type that is going to be discarded anyway. + // TODO: This could be generalized to more type combinations, but that would require + // more extensive testing and full algorithm rework. For now, the need to do two changes makes + // the recursive call work, and avoids the most egregious case of creating integer matrices. + if (node->getType().isMatrix() && (type.isScalar() || type.isVector()) && + type.isFloatingDomain() != node->getType().isFloatingDomain()) { + TType transitionType(node->getBasicType(), glslang::EvqTemporary, type.getVectorSize(), 0, 0, node->isVector()); + TOperator transitionOp = intermediate.mapTypeToConstructorOp(transitionType); + node = constructBuiltIn(transitionType, transitionOp, node, loc, false); + } + + TIntermTyped* newNode; + TOperator basicOp; + + // + // First, convert types as needed. + // + switch (op) { + case EOpConstructVec2: + case EOpConstructVec3: + case EOpConstructVec4: + case EOpConstructMat2x2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: + case EOpConstructMat3x3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: + case EOpConstructMat4x4: + case EOpConstructFloat: + basicOp = EOpConstructFloat; + break; + + case EOpConstructIVec2: + case EOpConstructIVec3: + case EOpConstructIVec4: + case EOpConstructInt: + basicOp = EOpConstructInt; + break; + + case EOpConstructUVec2: + if (node->getType().getBasicType() == EbtReference) { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference_uvec2, "reference conversion to uvec2"); + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvPtrToUvec2, true, node, + type); + return newNode; + } + case EOpConstructUVec3: + case EOpConstructUVec4: + case EOpConstructUint: + basicOp = EOpConstructUint; + break; + + case EOpConstructBVec2: + case EOpConstructBVec3: + case EOpConstructBVec4: + case EOpConstructBool: + basicOp = EOpConstructBool; + break; + +#ifndef GLSLANG_WEB + + case EOpConstructDVec2: + case EOpConstructDVec3: + case EOpConstructDVec4: + case EOpConstructDMat2x2: + case EOpConstructDMat2x3: + case EOpConstructDMat2x4: + case EOpConstructDMat3x2: + case EOpConstructDMat3x3: + case EOpConstructDMat3x4: + case EOpConstructDMat4x2: + case EOpConstructDMat4x3: + case EOpConstructDMat4x4: + case EOpConstructDouble: + basicOp = EOpConstructDouble; + break; + + case EOpConstructF16Vec2: + case EOpConstructF16Vec3: + case EOpConstructF16Vec4: + case EOpConstructF16Mat2x2: + case EOpConstructF16Mat2x3: + case EOpConstructF16Mat2x4: + case EOpConstructF16Mat3x2: + case EOpConstructF16Mat3x3: + case EOpConstructF16Mat3x4: + case EOpConstructF16Mat4x2: + case EOpConstructF16Mat4x3: + case EOpConstructF16Mat4x4: + case EOpConstructFloat16: + basicOp = EOpConstructFloat16; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticFloat16Enabled()) { + TType tempType(EbtFloat, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructFloat16) + aggregateOp = EOpConstructFloat; + else + aggregateOp = (TOperator)(EOpConstructVec2 + op - EOpConstructF16Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtFloat16, newNode); + return newNode; + } + break; + + case EOpConstructI8Vec2: + case EOpConstructI8Vec3: + case EOpConstructI8Vec4: + case EOpConstructInt8: + basicOp = EOpConstructInt8; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticInt8Enabled()) { + TType tempType(EbtInt, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructInt8) + aggregateOp = EOpConstructInt; + else + aggregateOp = (TOperator)(EOpConstructIVec2 + op - EOpConstructI8Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtInt8, newNode); + return newNode; + } + break; + + case EOpConstructU8Vec2: + case EOpConstructU8Vec3: + case EOpConstructU8Vec4: + case EOpConstructUint8: + basicOp = EOpConstructUint8; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticInt8Enabled()) { + TType tempType(EbtUint, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructUint8) + aggregateOp = EOpConstructUint; + else + aggregateOp = (TOperator)(EOpConstructUVec2 + op - EOpConstructU8Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtUint8, newNode); + return newNode; + } + break; + + case EOpConstructI16Vec2: + case EOpConstructI16Vec3: + case EOpConstructI16Vec4: + case EOpConstructInt16: + basicOp = EOpConstructInt16; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticInt16Enabled()) { + TType tempType(EbtInt, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructInt16) + aggregateOp = EOpConstructInt; + else + aggregateOp = (TOperator)(EOpConstructIVec2 + op - EOpConstructI16Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtInt16, newNode); + return newNode; + } + break; + + case EOpConstructU16Vec2: + case EOpConstructU16Vec3: + case EOpConstructU16Vec4: + case EOpConstructUint16: + basicOp = EOpConstructUint16; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticInt16Enabled()) { + TType tempType(EbtUint, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructUint16) + aggregateOp = EOpConstructUint; + else + aggregateOp = (TOperator)(EOpConstructUVec2 + op - EOpConstructU16Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtUint16, newNode); + return newNode; + } + break; + + case EOpConstructI64Vec2: + case EOpConstructI64Vec3: + case EOpConstructI64Vec4: + case EOpConstructInt64: + basicOp = EOpConstructInt64; + break; + + case EOpConstructUint64: + if (type.isScalar() && node->getType().isReference()) { + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvPtrToUint64, true, node, type); + return newNode; + } + // fall through + case EOpConstructU64Vec2: + case EOpConstructU64Vec3: + case EOpConstructU64Vec4: + basicOp = EOpConstructUint64; + break; + + case EOpConstructNonuniform: + // Make a nonuniform copy of node + newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpCopyObject, true, node, type); + return newNode; + + case EOpConstructReference: + // construct reference from reference + if (node->getType().isReference()) { + newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConstructReference, true, node, type); + return newNode; + // construct reference from uint64 + } else if (node->getType().isScalar() && node->getType().getBasicType() == EbtUint64) { + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvUint64ToPtr, true, node, + type); + return newNode; + // construct reference from uvec2 + } else if (node->getType().isVector() && node->getType().getBasicType() == EbtUint && + node->getVectorSize() == 2) { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference_uvec2, "uvec2 conversion to reference"); + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvUvec2ToPtr, true, node, + type); + return newNode; + } else { + return nullptr; + } + + case EOpConstructCooperativeMatrix: + if (!node->getType().isCoopMat()) { + if (type.getBasicType() != node->getType().getBasicType()) { + node = intermediate.addConversion(type.getBasicType(), node); + } + node = intermediate.setAggregateOperator(node, EOpConstructCooperativeMatrix, type, node->getLoc()); + } else { + TOperator op = EOpNull; + switch (type.getBasicType()) { + default: + assert(0); + break; + case EbtInt: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToInt; break; + case EbtFloat16: op = EOpConvFloat16ToInt; break; + case EbtUint8: op = EOpConvUint8ToInt; break; + case EbtInt8: op = EOpConvInt8ToInt; break; + case EbtUint: op = EOpConvUintToInt; break; + default: assert(0); + } + break; + case EbtUint: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToUint; break; + case EbtFloat16: op = EOpConvFloat16ToUint; break; + case EbtUint8: op = EOpConvUint8ToUint; break; + case EbtInt8: op = EOpConvInt8ToUint; break; + case EbtInt: op = EOpConvIntToUint; break; + case EbtUint: op = EOpConvUintToInt8; break; + default: assert(0); + } + break; + case EbtInt8: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToInt8; break; + case EbtFloat16: op = EOpConvFloat16ToInt8; break; + case EbtUint8: op = EOpConvUint8ToInt8; break; + case EbtInt: op = EOpConvIntToInt8; break; + case EbtUint: op = EOpConvUintToInt8; break; + default: assert(0); + } + break; + case EbtUint8: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToUint8; break; + case EbtFloat16: op = EOpConvFloat16ToUint8; break; + case EbtInt8: op = EOpConvInt8ToUint8; break; + case EbtInt: op = EOpConvIntToUint8; break; + case EbtUint: op = EOpConvUintToUint8; break; + default: assert(0); + } + break; + case EbtFloat: + switch (node->getType().getBasicType()) { + case EbtFloat16: op = EOpConvFloat16ToFloat; break; + case EbtInt8: op = EOpConvInt8ToFloat; break; + case EbtUint8: op = EOpConvUint8ToFloat; break; + case EbtInt: op = EOpConvIntToFloat; break; + case EbtUint: op = EOpConvUintToFloat; break; + default: assert(0); + } + break; + case EbtFloat16: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToFloat16; break; + case EbtInt8: op = EOpConvInt8ToFloat16; break; + case EbtUint8: op = EOpConvUint8ToFloat16; break; + case EbtInt: op = EOpConvIntToFloat16; break; + case EbtUint: op = EOpConvUintToFloat16; break; + default: assert(0); + } + break; + } + + node = intermediate.addUnaryNode(op, node, node->getLoc(), type); + // If it's a (non-specialization) constant, it must be folded. + if (node->getAsUnaryNode()->getOperand()->getAsConstantUnion()) + return node->getAsUnaryNode()->getOperand()->getAsConstantUnion()->fold(op, node->getType()); + } + + return node; + +#endif // GLSLANG_WEB + + default: + error(loc, "unsupported construction", "", ""); + + return nullptr; + } + newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc()); + if (newNode == nullptr) { + error(loc, "can't convert", "constructor", ""); + return nullptr; + } + + // + // Now, if there still isn't an operation to do the construction, and we need one, add one. + // + + // Otherwise, skip out early. + if (subset || (newNode != node && newNode->getType() == type)) + return newNode; + + // setAggregateOperator will insert a new node for the constructor, as needed. + return intermediate.setAggregateOperator(newNode, op, type, loc); +} + +// This function tests for the type of the parameters to the structure or array constructor. Raises +// an error message if the expected type does not match the parameter passed to the constructor. +// +// Returns nullptr for an error or the input node itself if the expected and the given parameter types match. +// +TIntermTyped* TParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount, const TSourceLoc& loc) +{ + TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped()); + if (! converted || converted->getType() != type) { + error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount, + node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str()); + + return nullptr; + } + + return converted; +} + +// If a memory qualifier is present in 'to', also make it present in 'from'. +void TParseContext::inheritMemoryQualifiers(const TQualifier& from, TQualifier& to) +{ +#ifndef GLSLANG_WEB + if (from.isReadOnly()) + to.readonly = from.readonly; + if (from.isWriteOnly()) + to.writeonly = from.writeonly; + if (from.coherent) + to.coherent = from.coherent; + if (from.volatil) + to.volatil = from.volatil; + if (from.restrict) + to.restrict = from.restrict; +#endif +} + +// +// Do everything needed to add an interface block. +// +void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, const TString* instanceName, + TArraySizes* arraySizes) +{ + blockStageIoCheck(loc, currentBlockQualifier); + blockQualifierCheck(loc, currentBlockQualifier, instanceName != nullptr); + if (arraySizes != nullptr) { + arraySizesCheck(loc, currentBlockQualifier, arraySizes, nullptr, false); + arrayOfArrayVersionCheck(loc, arraySizes); + if (arraySizes->getNumDims() > 1) + requireProfile(loc, ~EEsProfile, "array-of-array of block"); + } + + // Inherit and check member storage qualifiers WRT to the block-level qualifier. + for (unsigned int member = 0; member < typeList.size(); ++member) { + TType& memberType = *typeList[member].type; + TQualifier& memberQualifier = memberType.getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + globalQualifierFixCheck(memberLoc, memberQualifier); + if (memberQualifier.storage != EvqTemporary && memberQualifier.storage != EvqGlobal && memberQualifier.storage != currentBlockQualifier.storage) + error(memberLoc, "member storage qualifier cannot contradict block storage qualifier", memberType.getFieldName().c_str(), ""); + memberQualifier.storage = currentBlockQualifier.storage; +#ifndef GLSLANG_WEB + inheritMemoryQualifiers(currentBlockQualifier, memberQualifier); + if (currentBlockQualifier.perPrimitiveNV) + memberQualifier.perPrimitiveNV = currentBlockQualifier.perPrimitiveNV; + if (currentBlockQualifier.perViewNV) + memberQualifier.perViewNV = currentBlockQualifier.perViewNV; + if (currentBlockQualifier.perTaskNV) + memberQualifier.perTaskNV = currentBlockQualifier.perTaskNV; +#endif + if ((currentBlockQualifier.storage == EvqUniform || currentBlockQualifier.storage == EvqBuffer) && (memberQualifier.isInterpolation() || memberQualifier.isAuxiliary())) + error(memberLoc, "member of uniform or buffer block cannot have an auxiliary or interpolation qualifier", memberType.getFieldName().c_str(), ""); + if (memberType.isArray()) + arraySizesCheck(memberLoc, currentBlockQualifier, memberType.getArraySizes(), nullptr, member == typeList.size() - 1); + if (memberQualifier.hasOffset()) { + if (spvVersion.spv == 0) { + requireProfile(memberLoc, ~EEsProfile, "offset on block member"); + profileRequires(memberLoc, ~EEsProfile, 440, E_GL_ARB_enhanced_layouts, "offset on block member"); + } + } + + if (memberType.containsOpaque()) + error(memberLoc, "member of block cannot be or contain a sampler, image, or atomic_uint type", typeList[member].type->getFieldName().c_str(), ""); + + if (memberType.containsCoopMat()) + error(memberLoc, "member of block cannot be or contain a cooperative matrix type", typeList[member].type->getFieldName().c_str(), ""); + } + + // This might be a redeclaration of a built-in block. If so, redeclareBuiltinBlock() will + // do all the rest. + if (! symbolTable.atBuiltInLevel() && builtInName(*blockName)) { + redeclareBuiltinBlock(loc, typeList, *blockName, instanceName, arraySizes); + return; + } + + // Not a redeclaration of a built-in; check that all names are user names. + reservedErrorCheck(loc, *blockName); + if (instanceName) + reservedErrorCheck(loc, *instanceName); + for (unsigned int member = 0; member < typeList.size(); ++member) + reservedErrorCheck(typeList[member].loc, typeList[member].type->getFieldName()); + + // Make default block qualification, and adjust the member qualifications + + TQualifier defaultQualification; + switch (currentBlockQualifier.storage) { + case EvqUniform: defaultQualification = globalUniformDefaults; break; + case EvqBuffer: defaultQualification = globalBufferDefaults; break; + case EvqVaryingIn: defaultQualification = globalInputDefaults; break; + case EvqVaryingOut: defaultQualification = globalOutputDefaults; break; + default: defaultQualification.clear(); break; + } + + // Special case for "push_constant uniform", which has a default of std430, + // contrary to normal uniform defaults, and can't have a default tracked for it. + if ((currentBlockQualifier.isPushConstant() && !currentBlockQualifier.hasPacking()) || + (currentBlockQualifier.isShaderRecord() && !currentBlockQualifier.hasPacking())) + currentBlockQualifier.layoutPacking = ElpStd430; + + // Special case for "taskNV in/out", which has a default of std430, + if (currentBlockQualifier.isTaskMemory() && !currentBlockQualifier.hasPacking()) + currentBlockQualifier.layoutPacking = ElpStd430; + + // fix and check for member layout qualifiers + + mergeObjectLayoutQualifiers(defaultQualification, currentBlockQualifier, true); + + // "The align qualifier can only be used on blocks or block members, and only for blocks declared with std140 or std430 layouts." + if (currentBlockQualifier.hasAlign()) { + if (defaultQualification.layoutPacking != ElpStd140 && + defaultQualification.layoutPacking != ElpStd430 && + defaultQualification.layoutPacking != ElpScalar) { + error(loc, "can only be used with std140, std430, or scalar layout packing", "align", ""); + defaultQualification.layoutAlign = -1; + } + } + + bool memberWithLocation = false; + bool memberWithoutLocation = false; + bool memberWithPerViewQualifier = false; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; +#ifndef GLSLANG_WEB + if (memberQualifier.hasStream()) { + if (defaultQualification.layoutStream != memberQualifier.layoutStream) + error(memberLoc, "member cannot contradict block", "stream", ""); + } + + // "This includes a block's inheritance of the + // current global default buffer, a block member's inheritance of the block's + // buffer, and the requirement that any *xfb_buffer* declared on a block + // member must match the buffer inherited from the block." + if (memberQualifier.hasXfbBuffer()) { + if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer) + error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", ""); + } +#endif + + if (memberQualifier.hasPacking()) + error(memberLoc, "member of block cannot have a packing layout qualifier", typeList[member].type->getFieldName().c_str(), ""); + if (memberQualifier.hasLocation()) { + const char* feature = "location on block member"; + switch (currentBlockQualifier.storage) { +#ifndef GLSLANG_WEB + case EvqVaryingIn: + case EvqVaryingOut: + requireProfile(memberLoc, ECoreProfile | ECompatibilityProfile | EEsProfile, feature); + profileRequires(memberLoc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature); + profileRequires(memberLoc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, feature); + memberWithLocation = true; + break; +#endif + default: + error(memberLoc, "can only use in an in/out block", feature, ""); + break; + } + } else + memberWithoutLocation = true; + + // "The offset qualifier can only be used on block members of blocks declared with std140 or std430 layouts." + // "The align qualifier can only be used on blocks or block members, and only for blocks declared with std140 or std430 layouts." + if (memberQualifier.hasAlign() || memberQualifier.hasOffset()) { + if (defaultQualification.layoutPacking != ElpStd140 && + defaultQualification.layoutPacking != ElpStd430 && + defaultQualification.layoutPacking != ElpScalar) + error(memberLoc, "can only be used with std140, std430, or scalar layout packing", "offset/align", ""); + } + + if (memberQualifier.isPerView()) { + memberWithPerViewQualifier = true; + } + + TQualifier newMemberQualification = defaultQualification; + mergeQualifiers(memberLoc, newMemberQualification, memberQualifier, false); + memberQualifier = newMemberQualification; + } + + layoutMemberLocationArrayCheck(loc, memberWithLocation, arraySizes); + +#ifndef GLSLANG_WEB + // Ensure that the block has an XfbBuffer assigned. This is needed + // because if the block has a XfbOffset assigned, then it is + // assumed that it has implicitly assigned the current global + // XfbBuffer, and because it's members need to be assigned a + // XfbOffset if they lack it. + if (currentBlockQualifier.storage == EvqVaryingOut && globalOutputDefaults.hasXfbBuffer()) { + if (!currentBlockQualifier.hasXfbBuffer() && currentBlockQualifier.hasXfbOffset()) + currentBlockQualifier.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; + } +#endif + + // Process the members + fixBlockLocations(loc, currentBlockQualifier, typeList, memberWithLocation, memberWithoutLocation); + fixXfbOffsets(currentBlockQualifier, typeList); + fixBlockUniformOffsets(currentBlockQualifier, typeList); + for (unsigned int member = 0; member < typeList.size(); ++member) + layoutTypeCheck(typeList[member].loc, *typeList[member].type); + +#ifndef GLSLANG_WEB + if (memberWithPerViewQualifier) { + for (unsigned int member = 0; member < typeList.size(); ++member) { + checkAndResizeMeshViewDim(typeList[member].loc, *typeList[member].type, /*isBlockMember*/ true); + } + } +#endif + + // reverse merge, so that currentBlockQualifier now has all layout information + // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers) + mergeObjectLayoutQualifiers(currentBlockQualifier, defaultQualification, true); + + // + // Build and add the interface block as a new type named 'blockName' + // + + TType blockType(&typeList, *blockName, currentBlockQualifier); + if (arraySizes != nullptr) + blockType.transferArraySizes(arraySizes); + +#ifndef GLSLANG_WEB + if (arraySizes == nullptr) + ioArrayCheck(loc, blockType, instanceName ? *instanceName : *blockName); + if (currentBlockQualifier.hasBufferReference()) { + + if (currentBlockQualifier.storage != EvqBuffer) + error(loc, "can only be used with buffer", "buffer_reference", ""); + + // Create the block reference type. If it was forward-declared, detect that + // as a referent struct type with no members. Replace the referent type with + // blockType. + TType blockNameType(EbtReference, blockType, *blockName); + TVariable* blockNameVar = new TVariable(blockName, blockNameType, true); + if (! symbolTable.insert(*blockNameVar)) { + TSymbol* existingName = symbolTable.find(*blockName); + if (existingName->getType().isReference() && + existingName->getType().getReferentType()->getStruct() && + existingName->getType().getReferentType()->getStruct()->size() == 0 && + existingName->getType().getQualifier().storage == blockType.getQualifier().storage) { + existingName->getType().getReferentType()->deepCopy(blockType); + } else { + error(loc, "block name cannot be redefined", blockName->c_str(), ""); + } + } + if (!instanceName) { + return; + } + } else +#endif + { + // + // Don't make a user-defined type out of block name; that will cause an error + // if the same block name gets reused in a different interface. + // + // "Block names have no other use within a shader + // beyond interface matching; it is a compile-time error to use a block name at global scope for anything + // other than as a block name (e.g., use of a block name for a global variable name or function name is + // currently reserved)." + // + // Use the symbol table to prevent normal reuse of the block's name, as a variable entry, + // whose type is EbtBlock, but without all the structure; that will come from the type + // the instances point to. + // + TType blockNameType(EbtBlock, blockType.getQualifier().storage); + TVariable* blockNameVar = new TVariable(blockName, blockNameType); + if (! symbolTable.insert(*blockNameVar)) { + TSymbol* existingName = symbolTable.find(*blockName); + if (existingName->getType().getBasicType() == EbtBlock) { + if (existingName->getType().getQualifier().storage == blockType.getQualifier().storage) { + error(loc, "Cannot reuse block name within the same interface:", blockName->c_str(), blockType.getStorageQualifierString()); + return; + } + } else { + error(loc, "block name cannot redefine a non-block name", blockName->c_str(), ""); + return; + } + } + } + + // Add the variable, as anonymous or named instanceName. + // Make an anonymous variable if no name was provided. + if (! instanceName) + instanceName = NewPoolTString(""); + + TVariable& variable = *new TVariable(instanceName, blockType); + if (! symbolTable.insert(variable)) { + if (*instanceName == "") + error(loc, "nameless block contains a member that already has a name at global scope", blockName->c_str(), ""); + else + error(loc, "block instance name redefinition", variable.getName().c_str(), ""); + + return; + } + + // Check for general layout qualifier errors + layoutObjectCheck(loc, variable); + +#ifndef GLSLANG_WEB + // fix up + if (isIoResizeArray(blockType)) { + ioArraySymbolResizeList.push_back(&variable); + checkIoArraysConsistency(loc, true); + } else + fixIoArraySize(loc, variable.getWritableType()); +#endif + + // Save it in the AST for linker use. + trackLinkage(variable); +} + +// Do all block-declaration checking regarding the combination of in/out/uniform/buffer +// with a particular stage. +void TParseContext::blockStageIoCheck(const TSourceLoc& loc, const TQualifier& qualifier) +{ + const char *extsrt[2] = { E_GL_NV_ray_tracing, E_GL_EXT_ray_tracing }; + switch (qualifier.storage) { + case EvqUniform: + profileRequires(loc, EEsProfile, 300, nullptr, "uniform block"); + profileRequires(loc, ENoProfile, 140, E_GL_ARB_uniform_buffer_object, "uniform block"); + if (currentBlockQualifier.layoutPacking == ElpStd430 && ! currentBlockQualifier.isPushConstant()) + requireExtensions(loc, 1, &E_GL_EXT_scalar_block_layout, "std430 requires the buffer storage qualifier"); + break; + case EvqBuffer: + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, "buffer block"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_shader_storage_buffer_object, "buffer block"); + profileRequires(loc, EEsProfile, 310, nullptr, "buffer block"); + break; + case EvqVaryingIn: + profileRequires(loc, ~EEsProfile, 150, E_GL_ARB_separate_shader_objects, "input block"); + // It is a compile-time error to have an input block in a vertex shader or an output block in a fragment shader + // "Compute shaders do not permit user-defined input variables..." + requireStage(loc, (EShLanguageMask)(EShLangTessControlMask|EShLangTessEvaluationMask|EShLangGeometryMask| + EShLangFragmentMask|EShLangMeshNVMask), "input block"); + if (language == EShLangFragment) { + profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, "fragment input block"); + } else if (language == EShLangMeshNV && ! qualifier.isTaskMemory()) { + error(loc, "input blocks cannot be used in a mesh shader", "out", ""); + } + break; + case EvqVaryingOut: + profileRequires(loc, ~EEsProfile, 150, E_GL_ARB_separate_shader_objects, "output block"); + requireStage(loc, (EShLanguageMask)(EShLangVertexMask|EShLangTessControlMask|EShLangTessEvaluationMask| + EShLangGeometryMask|EShLangMeshNVMask|EShLangTaskNVMask), "output block"); + // ES 310 can have a block before shader_io is turned on, so skip this test for built-ins + if (language == EShLangVertex && ! parsingBuiltins) { + profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, "vertex output block"); + } else if (language == EShLangMeshNV && qualifier.isTaskMemory()) { + error(loc, "can only use on input blocks in mesh shader", "taskNV", ""); + } else if (language == EShLangTaskNV && ! qualifier.isTaskMemory()) { + error(loc, "output blocks cannot be used in a task shader", "out", ""); + } + break; +#ifndef GLSLANG_WEB + case EvqPayload: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "rayPayloadNV block"); + requireStage(loc, (EShLanguageMask)(EShLangRayGenMask | EShLangAnyHitMask | EShLangClosestHitMask | EShLangMissMask), + "rayPayloadNV block"); + break; + case EvqPayloadIn: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "rayPayloadInNV block"); + requireStage(loc, (EShLanguageMask)(EShLangAnyHitMask | EShLangClosestHitMask | EShLangMissMask), + "rayPayloadInNV block"); + break; + case EvqHitAttr: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "hitAttributeNV block"); + requireStage(loc, (EShLanguageMask)(EShLangIntersectMask | EShLangAnyHitMask | EShLangClosestHitMask), "hitAttributeNV block"); + break; + case EvqCallableData: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "callableDataNV block"); + requireStage(loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), + "callableDataNV block"); + break; + case EvqCallableDataIn: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "callableDataInNV block"); + requireStage(loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInNV block"); + break; +#endif + default: + error(loc, "only uniform, buffer, in, or out blocks are supported", blockName->c_str(), ""); + break; + } +} + +// Do all block-declaration checking regarding its qualifiers. +void TParseContext::blockQualifierCheck(const TSourceLoc& loc, const TQualifier& qualifier, bool /*instanceName*/) +{ + // The 4.5 specification says: + // + // interface-block : + // layout-qualifieropt interface-qualifier block-name { member-list } instance-nameopt ; + // + // interface-qualifier : + // in + // out + // patch in + // patch out + // uniform + // buffer + // + // Note however memory qualifiers aren't included, yet the specification also says + // + // "...memory qualifiers may also be used in the declaration of shader storage blocks..." + + if (qualifier.isInterpolation()) + error(loc, "cannot use interpolation qualifiers on an interface block", "flat/smooth/noperspective", ""); + if (qualifier.centroid) + error(loc, "cannot use centroid qualifier on an interface block", "centroid", ""); + if (qualifier.isSample()) + error(loc, "cannot use sample qualifier on an interface block", "sample", ""); + if (qualifier.invariant) + error(loc, "cannot use invariant qualifier on an interface block", "invariant", ""); + if (qualifier.isPushConstant()) + intermediate.addPushConstantCount(); + if (qualifier.isShaderRecord()) + intermediate.addShaderRecordCount(); + if (qualifier.isTaskMemory()) + intermediate.addTaskNVCount(); +} + +// +// "For a block, this process applies to the entire block, or until the first member +// is reached that has a location layout qualifier. When a block member is declared with a location +// qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level +// declaration. Subsequent members are again assigned consecutive locations, based on the newest location, +// until the next member declared with a location qualifier. The values used for locations do not have to be +// declared in increasing order." +void TParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation) +{ + // "If a block has no block-level location layout qualifier, it is required that either all or none of its members + // have a location layout qualifier, or a compile-time error results." + if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation) + error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", ""); + else { + if (memberWithLocation) { + // remove any block-level location and make it per *every* member + int nextLocation = 0; // by the rule above, initial value is not relevant + if (qualifier.hasAnyLocation()) { + nextLocation = qualifier.layoutLocation; + qualifier.layoutLocation = TQualifier::layoutLocationEnd; + if (qualifier.hasComponent()) { + // "It is a compile-time error to apply the *component* qualifier to a ... block" + error(loc, "cannot apply to a block", "component", ""); + } + if (qualifier.hasIndex()) { + error(loc, "cannot apply to a block", "index", ""); + } + } + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + if (! memberQualifier.hasLocation()) { + if (nextLocation >= (int)TQualifier::layoutLocationEnd) + error(memberLoc, "location is too large", "location", ""); + memberQualifier.layoutLocation = nextLocation; + memberQualifier.layoutComponent = TQualifier::layoutComponentEnd; + } + nextLocation = memberQualifier.layoutLocation + intermediate.computeTypeLocationSize( + *typeList[member].type, language); + } + } + } +} + +void TParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList) +{ +#ifndef GLSLANG_WEB + // "If a block is qualified with xfb_offset, all its + // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any + // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer + // offsets." + + if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset()) + return; + + int nextOffset = qualifier.layoutXfbOffset; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + bool contains64BitType = false; + bool contains32BitType = false; + bool contains16BitType = false; + int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType, contains32BitType, contains16BitType); + // see if we need to auto-assign an offset to this member + if (! memberQualifier.hasXfbOffset()) { + // "if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8" + if (contains64BitType) + RoundToPow2(nextOffset, 8); + else if (contains32BitType) + RoundToPow2(nextOffset, 4); + else if (contains16BitType) + RoundToPow2(nextOffset, 2); + memberQualifier.layoutXfbOffset = nextOffset; + } else + nextOffset = memberQualifier.layoutXfbOffset; + nextOffset += memberSize; + } + + // The above gave all block members an offset, so we can take it off the block now, + // which will avoid double counting the offset usage. + qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd; +#endif +} + +// Calculate and save the offset of each block member, using the recursively +// defined block offset rules and the user-provided offset and align. +// +// Also, compute and save the total size of the block. For the block's size, arrayness +// is not taken into account, as each element is backed by a separate buffer. +// +void TParseContext::fixBlockUniformOffsets(TQualifier& qualifier, TTypeList& typeList) +{ + if (!qualifier.isUniformOrBuffer() && !qualifier.isTaskMemory()) + return; + if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430 && qualifier.layoutPacking != ElpScalar) + return; + + int offset = 0; + int memberSize; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + + // "When align is applied to an array, it effects only the start of the array, not the array's internal stride." + + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix; + int dummyStride; + int memberAlignment = intermediate.getMemberAlignment(*typeList[member].type, memberSize, dummyStride, qualifier.layoutPacking, + subMatrixLayout != ElmNone ? subMatrixLayout == ElmRowMajor : qualifier.layoutMatrix == ElmRowMajor); + if (memberQualifier.hasOffset()) { + // "The specified offset must be a multiple + // of the base alignment of the type of the block member it qualifies, or a compile-time error results." + if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment)) + error(memberLoc, "must be a multiple of the member's alignment", "offset", ""); + + // GLSL: "It is a compile-time error to specify an offset that is smaller than the offset of the previous + // member in the block or that lies within the previous member of the block" + if (spvVersion.spv == 0) { + if (memberQualifier.layoutOffset < offset) + error(memberLoc, "cannot lie in previous members", "offset", ""); + + // "The offset qualifier forces the qualified member to start at or after the specified + // integral-constant expression, which will be its byte offset from the beginning of the buffer. + // "The actual offset of a member is computed as + // follows: If offset was declared, start with that offset, otherwise start with the next available offset." + offset = std::max(offset, memberQualifier.layoutOffset); + } else { + // TODO: Vulkan: "It is a compile-time error to have any offset, explicit or assigned, + // that lies within another member of the block." + + offset = memberQualifier.layoutOffset; + } + } + + // "The actual alignment of a member will be the greater of the specified align alignment and the standard + // (e.g., std140) base alignment for the member's type." + if (memberQualifier.hasAlign()) + memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign); + + // "If the resulting offset is not a multiple of the actual alignment, + // increase it to the first offset that is a multiple of + // the actual alignment." + RoundToPow2(offset, memberAlignment); + typeList[member].type->getQualifier().layoutOffset = offset; + offset += memberSize; + } +} + +// For an identifier that is already declared, add more qualification to it. +void TParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier) +{ + TSymbol* symbol = symbolTable.find(identifier); + + // A forward declaration of a block reference looks to the grammar like adding + // a qualifier to an existing symbol. Detect this and create the block reference + // type with an empty type list, which will be filled in later in + // TParseContext::declareBlock. + if (!symbol && qualifier.hasBufferReference()) { + TTypeList typeList; + TType blockType(&typeList, identifier, qualifier);; + TType blockNameType(EbtReference, blockType, identifier); + TVariable* blockNameVar = new TVariable(&identifier, blockNameType, true); + if (! symbolTable.insert(*blockNameVar)) { + error(loc, "block name cannot redefine a non-block name", blockName->c_str(), ""); + } + return; + } + + if (! symbol) { + error(loc, "identifier not previously declared", identifier.c_str(), ""); + return; + } + if (symbol->getAsFunction()) { + error(loc, "cannot re-qualify a function name", identifier.c_str(), ""); + return; + } + + if (qualifier.isAuxiliary() || + qualifier.isMemory() || + qualifier.isInterpolation() || + qualifier.hasLayout() || + qualifier.storage != EvqTemporary || + qualifier.precision != EpqNone) { + error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), ""); + return; + } + + // For read-only built-ins, add a new symbol for holding the modified qualifier. + // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block) + if (symbol->isReadOnly()) + symbol = symbolTable.copyUp(symbol); + + if (qualifier.invariant) { + if (intermediate.inIoAccessed(identifier)) + error(loc, "cannot change qualification after use", "invariant", ""); + symbol->getWritableType().getQualifier().invariant = true; + invariantCheck(loc, symbol->getType().getQualifier()); + } else if (qualifier.isNoContraction()) { + if (intermediate.inIoAccessed(identifier)) + error(loc, "cannot change qualification after use", "precise", ""); + symbol->getWritableType().getQualifier().setNoContraction(); + } else if (qualifier.specConstant) { + symbol->getWritableType().getQualifier().makeSpecConstant(); + if (qualifier.hasSpecConstantId()) + symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId; + } else + warn(loc, "unknown requalification", "", ""); +} + +void TParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers) +{ + for (unsigned int i = 0; i < identifiers.size(); ++i) + addQualifierToExisting(loc, qualifier, *identifiers[i]); +} + +// Make sure 'invariant' isn't being applied to a non-allowed object. +void TParseContext::invariantCheck(const TSourceLoc& loc, const TQualifier& qualifier) +{ + if (! qualifier.invariant) + return; + + bool pipeOut = qualifier.isPipeOutput(); + bool pipeIn = qualifier.isPipeInput(); + if (version >= 300 || (!isEsProfile() && version >= 420)) { + if (! pipeOut) + error(loc, "can only apply to an output", "invariant", ""); + } else { + if ((language == EShLangVertex && pipeIn) || (! pipeOut && ! pipeIn)) + error(loc, "can only apply to an output, or to an input in a non-vertex stage\n", "invariant", ""); + } +} + +// +// Updating default qualifier for the case of a declaration with just a qualifier, +// no type, block, or identifier. +// +void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType) +{ +#ifndef GLSLANG_WEB + if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) { + assert(language == EShLangTessControl || language == EShLangGeometry || language == EShLangMeshNV); + const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices"; + + if (publicType.qualifier.storage != EvqVaryingOut) + error(loc, "can only apply to 'out'", id, ""); + if (! intermediate.setVertices(publicType.shaderQualifiers.vertices)) + error(loc, "cannot change previously set layout value", id, ""); + + if (language == EShLangTessControl) + checkIoArraysConsistency(loc); + } + if (publicType.shaderQualifiers.primitives != TQualifier::layoutNotSet) { + assert(language == EShLangMeshNV); + const char* id = "max_primitives"; + + if (publicType.qualifier.storage != EvqVaryingOut) + error(loc, "can only apply to 'out'", id, ""); + if (! intermediate.setPrimitives(publicType.shaderQualifiers.primitives)) + error(loc, "cannot change previously set layout value", id, ""); + } + if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) { + if (publicType.qualifier.storage != EvqVaryingIn) + error(loc, "can only apply to 'in'", "invocations", ""); + if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations)) + error(loc, "cannot change previously set layout value", "invocations", ""); + } + if (publicType.shaderQualifiers.geometry != ElgNone) { + if (publicType.qualifier.storage == EvqVaryingIn) { + switch (publicType.shaderQualifiers.geometry) { + case ElgPoints: + case ElgLines: + case ElgLinesAdjacency: + case ElgTriangles: + case ElgTrianglesAdjacency: + case ElgQuads: + case ElgIsolines: + if (language == EShLangMeshNV) { + error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + break; + } + if (intermediate.setInputPrimitive(publicType.shaderQualifiers.geometry)) { + if (language == EShLangGeometry) + checkIoArraysConsistency(loc); + } else + error(loc, "cannot change previously set input primitive", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + break; + default: + error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + } + } else if (publicType.qualifier.storage == EvqVaryingOut) { + switch (publicType.shaderQualifiers.geometry) { + case ElgLines: + case ElgTriangles: + if (language != EShLangMeshNV) { + error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + break; + } + // Fall through + case ElgPoints: + case ElgLineStrip: + case ElgTriangleStrip: + if (! intermediate.setOutputPrimitive(publicType.shaderQualifiers.geometry)) + error(loc, "cannot change previously set output primitive", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + break; + default: + error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + } + } else + error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), GetStorageQualifierString(publicType.qualifier.storage)); + } + if (publicType.shaderQualifiers.spacing != EvsNone) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (! intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing)) + error(loc, "cannot change previously set vertex spacing", TQualifier::getVertexSpacingString(publicType.shaderQualifiers.spacing), ""); + } else + error(loc, "can only apply to 'in'", TQualifier::getVertexSpacingString(publicType.shaderQualifiers.spacing), ""); + } + if (publicType.shaderQualifiers.order != EvoNone) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (! intermediate.setVertexOrder(publicType.shaderQualifiers.order)) + error(loc, "cannot change previously set vertex order", TQualifier::getVertexOrderString(publicType.shaderQualifiers.order), ""); + } else + error(loc, "can only apply to 'in'", TQualifier::getVertexOrderString(publicType.shaderQualifiers.order), ""); + } + if (publicType.shaderQualifiers.pointMode) { + if (publicType.qualifier.storage == EvqVaryingIn) + intermediate.setPointMode(); + else + error(loc, "can only apply to 'in'", "point_mode", ""); + } +#endif + for (int i = 0; i < 3; ++i) { + if (publicType.shaderQualifiers.localSizeNotDefault[i]) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (! intermediate.setLocalSize(i, publicType.shaderQualifiers.localSize[i])) + error(loc, "cannot change previously set size", "local_size", ""); + else { + int max = 0; + if (language == EShLangCompute) { + switch (i) { + case 0: max = resources.maxComputeWorkGroupSizeX; break; + case 1: max = resources.maxComputeWorkGroupSizeY; break; + case 2: max = resources.maxComputeWorkGroupSizeZ; break; + default: break; + } + if (intermediate.getLocalSize(i) > (unsigned int)max) + error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", ""); + } +#ifndef GLSLANG_WEB + else if (language == EShLangMeshNV) { + switch (i) { + case 0: max = resources.maxMeshWorkGroupSizeX_NV; break; + case 1: max = resources.maxMeshWorkGroupSizeY_NV; break; + case 2: max = resources.maxMeshWorkGroupSizeZ_NV; break; + default: break; + } + if (intermediate.getLocalSize(i) > (unsigned int)max) + error(loc, "too large; see gl_MaxMeshWorkGroupSizeNV", "local_size", ""); + } else if (language == EShLangTaskNV) { + switch (i) { + case 0: max = resources.maxTaskWorkGroupSizeX_NV; break; + case 1: max = resources.maxTaskWorkGroupSizeY_NV; break; + case 2: max = resources.maxTaskWorkGroupSizeZ_NV; break; + default: break; + } + if (intermediate.getLocalSize(i) > (unsigned int)max) + error(loc, "too large; see gl_MaxTaskWorkGroupSizeNV", "local_size", ""); + } +#endif + else { + assert(0); + } + + // Fix the existing constant gl_WorkGroupSize with this new information. + TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); + if (workGroupSize != nullptr) + workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i)); + } + } else + error(loc, "can only apply to 'in'", "local_size", ""); + } + if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (! intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i])) + error(loc, "cannot change previously set size", "local_size", ""); + } else + error(loc, "can only apply to 'in'", "local_size id", ""); + // Set the workgroup built-in variable as a specialization constant + TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); + if (workGroupSize != nullptr) + workGroupSize->getWritableType().getQualifier().specConstant = true; + } + } + +#ifndef GLSLANG_WEB + if (publicType.shaderQualifiers.earlyFragmentTests) { + if (publicType.qualifier.storage == EvqVaryingIn) + intermediate.setEarlyFragmentTests(); + else + error(loc, "can only apply to 'in'", "early_fragment_tests", ""); + } + if (publicType.shaderQualifiers.postDepthCoverage) { + if (publicType.qualifier.storage == EvqVaryingIn) + intermediate.setPostDepthCoverage(); + else + error(loc, "can only apply to 'in'", "post_coverage_coverage", ""); + } + if (publicType.shaderQualifiers.hasBlendEquation()) { + if (publicType.qualifier.storage != EvqVaryingOut) + error(loc, "can only apply to 'out'", "blend equation", ""); + } + if (publicType.shaderQualifiers.interlockOrdering) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (!intermediate.setInterlockOrdering(publicType.shaderQualifiers.interlockOrdering)) + error(loc, "cannot change previously set fragment shader interlock ordering", TQualifier::getInterlockOrderingString(publicType.shaderQualifiers.interlockOrdering), ""); + } + else + error(loc, "can only apply to 'in'", TQualifier::getInterlockOrderingString(publicType.shaderQualifiers.interlockOrdering), ""); + } + + if (publicType.shaderQualifiers.layoutDerivativeGroupQuads && + publicType.shaderQualifiers.layoutDerivativeGroupLinear) { + error(loc, "cannot be both specified", "derivative_group_quadsNV and derivative_group_linearNV", ""); + } + + if (publicType.shaderQualifiers.layoutDerivativeGroupQuads) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if ((intermediate.getLocalSize(0) & 1) || + (intermediate.getLocalSize(1) & 1)) + error(loc, "requires local_size_x and local_size_y to be multiple of two", "derivative_group_quadsNV", ""); + else + intermediate.setLayoutDerivativeMode(LayoutDerivativeGroupQuads); + } + else + error(loc, "can only apply to 'in'", "derivative_group_quadsNV", ""); + } + if (publicType.shaderQualifiers.layoutDerivativeGroupLinear) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if((intermediate.getLocalSize(0) * + intermediate.getLocalSize(1) * + intermediate.getLocalSize(2)) % 4 != 0) + error(loc, "requires total group size to be multiple of four", "derivative_group_linearNV", ""); + else + intermediate.setLayoutDerivativeMode(LayoutDerivativeGroupLinear); + } + else + error(loc, "can only apply to 'in'", "derivative_group_linearNV", ""); + } + // Check mesh out array sizes, once all the necessary out qualifiers are defined. + if ((language == EShLangMeshNV) && + (intermediate.getVertices() != TQualifier::layoutNotSet) && + (intermediate.getPrimitives() != TQualifier::layoutNotSet) && + (intermediate.getOutputPrimitive() != ElgNone)) + { + checkIoArraysConsistency(loc); + } +#endif + const TQualifier& qualifier = publicType.qualifier; + + if (qualifier.isAuxiliary() || + qualifier.isMemory() || + qualifier.isInterpolation() || + qualifier.precision != EpqNone) + error(loc, "cannot use auxiliary, memory, interpolation, or precision qualifier in a default qualifier declaration (declaration with no type)", "qualifier", ""); + + // "The offset qualifier can only be used on block members of blocks..." + // "The align qualifier can only be used on blocks or block members..." + if (qualifier.hasOffset() || + qualifier.hasAlign()) + error(loc, "cannot use offset or align qualifiers in a default qualifier declaration (declaration with no type)", "layout qualifier", ""); + + layoutQualifierCheck(loc, qualifier); + + switch (qualifier.storage) { + case EvqUniform: + if (qualifier.hasMatrix()) + globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix; + if (qualifier.hasPacking()) + globalUniformDefaults.layoutPacking = qualifier.layoutPacking; + break; + case EvqBuffer: + if (qualifier.hasMatrix()) + globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix; + if (qualifier.hasPacking()) + globalBufferDefaults.layoutPacking = qualifier.layoutPacking; + break; + case EvqVaryingIn: + break; + case EvqVaryingOut: +#ifndef GLSLANG_WEB + if (qualifier.hasStream()) + globalOutputDefaults.layoutStream = qualifier.layoutStream; + if (qualifier.hasXfbBuffer()) + globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer; + if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) { + if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride)) + error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", qualifier.layoutXfbBuffer); + } +#endif + break; + default: + error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", ""); + return; + } + + if (qualifier.hasBinding()) + error(loc, "cannot declare a default, include a type or full declaration", "binding", ""); + if (qualifier.hasAnyLocation()) + error(loc, "cannot declare a default, use a full declaration", "location/component/index", ""); + if (qualifier.hasXfbOffset()) + error(loc, "cannot declare a default, use a full declaration", "xfb_offset", ""); + if (qualifier.isPushConstant()) + error(loc, "cannot declare a default, can only be used on a block", "push_constant", ""); + if (qualifier.hasBufferReference()) + error(loc, "cannot declare a default, can only be used on a block", "buffer_reference", ""); + if (qualifier.hasSpecConstantId()) + error(loc, "cannot declare a default, can only be used on a scalar", "constant_id", ""); + if (qualifier.isShaderRecord()) + error(loc, "cannot declare a default, can only be used on a block", "shaderRecordNV", ""); +} + +// +// Take the sequence of statements that has been built up since the last case/default, +// put it on the list of top-level nodes for the current (inner-most) switch statement, +// and follow that by the case/default we are on now. (See switch topology comment on +// TIntermSwitch.) +// +void TParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode) +{ + TIntermSequence* switchSequence = switchSequenceStack.back(); + + if (statements) { + if (switchSequence->size() == 0) + error(statements->getLoc(), "cannot have statements before first case/default label", "switch", ""); + statements->setOperator(EOpSequence); + switchSequence->push_back(statements); + } + if (branchNode) { + // check all previous cases for the same label (or both are 'default') + for (unsigned int s = 0; s < switchSequence->size(); ++s) { + TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode(); + if (prevBranch) { + TIntermTyped* prevExpression = prevBranch->getExpression(); + TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression(); + if (prevExpression == nullptr && newExpression == nullptr) + error(branchNode->getLoc(), "duplicate label", "default", ""); + else if (prevExpression != nullptr && + newExpression != nullptr && + prevExpression->getAsConstantUnion() && + newExpression->getAsConstantUnion() && + prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() == + newExpression->getAsConstantUnion()->getConstArray()[0].getIConst()) + error(branchNode->getLoc(), "duplicated value", "case", ""); + } + } + switchSequence->push_back(branchNode); + } +} + +// +// Turn the top-level node sequence built up of wrapupSwitchSubsequence9) +// into a switch node. +// +TIntermNode* TParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression, TIntermAggregate* lastStatements) +{ + profileRequires(loc, EEsProfile, 300, nullptr, "switch statements"); + profileRequires(loc, ENoProfile, 130, nullptr, "switch statements"); + + wrapupSwitchSubsequence(lastStatements, nullptr); + + if (expression == nullptr || + (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) || + expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector()) + error(loc, "condition must be a scalar integer expression", "switch", ""); + + // If there is nothing to do, drop the switch but still execute the expression + TIntermSequence* switchSequence = switchSequenceStack.back(); + if (switchSequence->size() == 0) + return expression; + + if (lastStatements == nullptr) { + // This was originally an ERRROR, because early versions of the specification said + // "it is an error to have no statement between a label and the end of the switch statement." + // The specifications were updated to remove this (being ill-defined what a "statement" was), + // so, this became a warning. However, 3.0 tests still check for the error. + if (isEsProfile() && version <= 300 && ! relaxedErrors()) + error(loc, "last case/default label not followed by statements", "switch", ""); + else + warn(loc, "last case/default label not followed by statements", "switch", ""); + + // emulate a break for error recovery + lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc)); + lastStatements->setOperator(EOpSequence); + switchSequence->push_back(lastStatements); + } + + TIntermAggregate* body = new TIntermAggregate(EOpSequence); + body->getSequence() = *switchSequenceStack.back(); + body->setLoc(loc); + + TIntermSwitch* switchNode = new TIntermSwitch(expression, body); + switchNode->setLoc(loc); + + return switchNode; +} + +} // end namespace glslang + diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ParseHelper.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ParseHelper.h new file mode 100644 index 000000000..8831ba82b --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ParseHelper.h @@ -0,0 +1,526 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// This header defines a two-level parse-helper hierarchy, derived from +// TParseVersions: +// - TParseContextBase: sharable across multiple parsers +// - TParseContext: GLSL specific helper +// + +#ifndef _PARSER_HELPER_INCLUDED_ +#define _PARSER_HELPER_INCLUDED_ + +#include +#include + +#include "parseVersions.h" +#include "../Include/ShHandle.h" +#include "SymbolTable.h" +#include "localintermediate.h" +#include "Scan.h" +#include "attribute.h" + +namespace glslang { + +struct TPragma { + TPragma(bool o, bool d) : optimize(o), debug(d) { } + bool optimize; + bool debug; + TPragmaTable pragmaTable; +}; + +class TScanContext; +class TPpContext; + +typedef std::set TIdSetType; + +// +// Sharable code (as well as what's in TParseVersions) across +// parse helpers. +// +class TParseContextBase : public TParseVersions { +public: + TParseContextBase(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, int version, + EProfile profile, const SpvVersion& spvVersion, EShLanguage language, + TInfoSink& infoSink, bool forwardCompatible, EShMessages messages, + const TString* entryPoint = nullptr) + : TParseVersions(interm, version, profile, spvVersion, language, infoSink, forwardCompatible, messages), + scopeMangler("::"), + symbolTable(symbolTable), + statementNestingLevel(0), loopNestingLevel(0), structNestingLevel(0), controlFlowNestingLevel(0), + postEntryPointReturn(false), + contextPragma(true, false), + beginInvocationInterlockCount(0), endInvocationInterlockCount(0), + parsingBuiltins(parsingBuiltins), scanContext(nullptr), ppContext(nullptr), + limits(resources.limits), + globalUniformBlock(nullptr), + globalUniformBinding(TQualifier::layoutBindingEnd), + globalUniformSet(TQualifier::layoutSetEnd) + { + if (entryPoint != nullptr) + sourceEntryPointName = *entryPoint; + } + virtual ~TParseContextBase() { } + +#if !defined(GLSLANG_WEB) || defined(GLSLANG_WEB_DEVEL) + virtual void C_DECL error(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...); + virtual void C_DECL warn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...); + virtual void C_DECL ppError(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...); + virtual void C_DECL ppWarn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...); +#endif + + virtual void setLimits(const TBuiltInResource&) = 0; + + void checkIndex(const TSourceLoc&, const TType&, int& index); + + EShLanguage getLanguage() const { return language; } + void setScanContext(TScanContext* c) { scanContext = c; } + TScanContext* getScanContext() const { return scanContext; } + void setPpContext(TPpContext* c) { ppContext = c; } + TPpContext* getPpContext() const { return ppContext; } + + virtual void setLineCallback(const std::function& func) { lineCallback = func; } + virtual void setExtensionCallback(const std::function& func) { extensionCallback = func; } + virtual void setVersionCallback(const std::function& func) { versionCallback = func; } + virtual void setPragmaCallback(const std::function&)>& func) { pragmaCallback = func; } + virtual void setErrorCallback(const std::function& func) { errorCallback = func; } + + virtual void reservedPpErrorCheck(const TSourceLoc&, const char* name, const char* op) = 0; + virtual bool lineContinuationCheck(const TSourceLoc&, bool endOfComment) = 0; + virtual bool lineDirectiveShouldSetNextLine() const = 0; + virtual void handlePragma(const TSourceLoc&, const TVector&) = 0; + + virtual bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) = 0; + + virtual void notifyVersion(int line, int version, const char* type_string) + { + if (versionCallback) + versionCallback(line, version, type_string); + } + virtual void notifyErrorDirective(int line, const char* error_message) + { + if (errorCallback) + errorCallback(line, error_message); + } + virtual void notifyLineDirective(int curLineNo, int newLineNo, bool hasSource, int sourceNum, const char* sourceName) + { + if (lineCallback) + lineCallback(curLineNo, newLineNo, hasSource, sourceNum, sourceName); + } + virtual void notifyExtensionDirective(int line, const char* extension, const char* behavior) + { + if (extensionCallback) + extensionCallback(line, extension, behavior); + } + +#ifdef ENABLE_HLSL + // Manage the global uniform block (default uniforms in GLSL, $Global in HLSL) + virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr); +#endif + + // Potentially rename shader entry point function + void renameShaderFunction(TString*& name) const + { + // Replace the entry point name given in the shader with the real entry point name, + // if there is a substitution. + if (name != nullptr && *name == sourceEntryPointName && intermediate.getEntryPointName().size() > 0) + name = NewPoolTString(intermediate.getEntryPointName().c_str()); + } + + virtual bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*); + virtual void rValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*); + + const char* const scopeMangler; + + // Basic parsing state, easily accessible to the grammar + + TSymbolTable& symbolTable; // symbol table that goes with the current language, version, and profile + int statementNestingLevel; // 0 if outside all flow control or compound statements + int loopNestingLevel; // 0 if outside all loops + int structNestingLevel; // 0 if outside blocks and structures + int controlFlowNestingLevel; // 0 if outside all flow control + const TType* currentFunctionType; // the return type of the function that's currently being parsed + bool functionReturnsValue; // true if a non-void function has a return + // if inside a function, true if the function is the entry point and this is after a return statement + bool postEntryPointReturn; + // case, node, case, case, node, ...; ensure only one node between cases; stack of them for nesting + TList switchSequenceStack; + // the statementNestingLevel the current switch statement is at, which must match the level of its case statements + TList switchLevel; + struct TPragma contextPragma; + int beginInvocationInterlockCount; + int endInvocationInterlockCount; + +protected: + TParseContextBase(TParseContextBase&); + TParseContextBase& operator=(TParseContextBase&); + + const bool parsingBuiltins; // true if parsing built-in symbols/functions + TVector linkageSymbols; // will be transferred to 'linkage', after all editing is done, order preserving + TScanContext* scanContext; + TPpContext* ppContext; + TBuiltInResource resources; + TLimits& limits; + TString sourceEntryPointName; + + // These, if set, will be called when a line, pragma ... is preprocessed. + // They will be called with any parameters to the original directive. + std::function lineCallback; + std::function&)> pragmaCallback; + std::function versionCallback; + std::function extensionCallback; + std::function errorCallback; + + // see implementation for detail + const TFunction* selectFunction(const TVector, const TFunction&, + std::function, + std::function, + /* output */ bool& tie); + + virtual void parseSwizzleSelector(const TSourceLoc&, const TString&, int size, + TSwizzleSelectors&); + + // Manage the global uniform block (default uniforms in GLSL, $Global in HLSL) + TVariable* globalUniformBlock; // the actual block, inserted into the symbol table + unsigned int globalUniformBinding; // the block's binding number + unsigned int globalUniformSet; // the block's set number + int firstNewMember; // the index of the first member not yet inserted into the symbol table + // override this to set the language-specific name + virtual const char* getGlobalUniformBlockName() const { return ""; } + virtual void setUniformBlockDefaults(TType&) const { } + virtual void finalizeGlobalUniformBlockLayout(TVariable&) { } + virtual void outputMessage(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, TPrefixType prefix, + va_list args); + virtual void trackLinkage(TSymbol& symbol); + virtual void makeEditable(TSymbol*&); + virtual TVariable* getEditableVariable(const char* name); + virtual void finish(); +}; + +// +// Manage the state for when to respect precision qualifiers and when to warn about +// the defaults being different than might be expected. +// +class TPrecisionManager { +public: + TPrecisionManager() : obey(false), warn(false), explicitIntDefault(false), explicitFloatDefault(false){ } + virtual ~TPrecisionManager() {} + + void respectPrecisionQualifiers() { obey = true; } + bool respectingPrecisionQualifiers() const { return obey; } + bool shouldWarnAboutDefaults() const { return warn; } + void defaultWarningGiven() { warn = false; } + void warnAboutDefaults() { warn = true; } + void explicitIntDefaultSeen() + { + explicitIntDefault = true; + if (explicitFloatDefault) + warn = false; + } + void explicitFloatDefaultSeen() + { + explicitFloatDefault = true; + if (explicitIntDefault) + warn = false; + } + +protected: + bool obey; // respect precision qualifiers + bool warn; // need to give a warning about the defaults + bool explicitIntDefault; // user set the default for int/uint + bool explicitFloatDefault; // user set the default for float +}; + +// +// GLSL-specific parse helper. Should have GLSL in the name, but that's +// too big of a change for comparing branches at the moment, and perhaps +// impacts downstream consumers as well. +// +class TParseContext : public TParseContextBase { +public: + TParseContext(TSymbolTable&, TIntermediate&, bool parsingBuiltins, int version, EProfile, const SpvVersion& spvVersion, EShLanguage, TInfoSink&, + bool forwardCompatible = false, EShMessages messages = EShMsgDefault, + const TString* entryPoint = nullptr); + virtual ~TParseContext(); + + bool obeyPrecisionQualifiers() const { return precisionManager.respectingPrecisionQualifiers(); } + void setPrecisionDefaults(); + + void setLimits(const TBuiltInResource&) override; + bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) override; + void parserError(const char* s); // for bison's yyerror + + void reservedErrorCheck(const TSourceLoc&, const TString&); + void reservedPpErrorCheck(const TSourceLoc&, const char* name, const char* op) override; + bool lineContinuationCheck(const TSourceLoc&, bool endOfComment) override; + bool lineDirectiveShouldSetNextLine() const override; + bool builtInName(const TString&); + + void handlePragma(const TSourceLoc&, const TVector&) override; + TIntermTyped* handleVariable(const TSourceLoc&, TSymbol* symbol, const TString* string); + TIntermTyped* handleBracketDereference(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index); + void handleIndexLimits(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index); + +#ifndef GLSLANG_WEB + void makeEditable(TSymbol*&) override; + void ioArrayCheck(const TSourceLoc&, const TType&, const TString& identifier); +#endif + bool isIoResizeArray(const TType&) const; + void fixIoArraySize(const TSourceLoc&, TType&); + void handleIoResizeArrayAccess(const TSourceLoc&, TIntermTyped* base); + void checkIoArraysConsistency(const TSourceLoc&, bool tailOnly = false); + int getIoArrayImplicitSize(const TQualifier&, TString* featureString = nullptr) const; + void checkIoArrayConsistency(const TSourceLoc&, int requiredSize, const char* feature, TType&, const TString&); + + TIntermTyped* handleBinaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right); + TIntermTyped* handleUnaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* childNode); + TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field); + TIntermTyped* handleDotSwizzle(const TSourceLoc&, TIntermTyped* base, const TString& field); + void blockMemberExtensionCheck(const TSourceLoc&, const TIntermTyped* base, int member, const TString& memberName); + TFunction* handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype); + TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&); + TIntermTyped* handleFunctionCall(const TSourceLoc&, TFunction*, TIntermNode*); + TIntermTyped* handleBuiltInFunctionCall(TSourceLoc, TIntermNode* arguments, const TFunction& function); + void computeBuiltinPrecisions(TIntermTyped&, const TFunction&); + TIntermNode* handleReturnValue(const TSourceLoc&, TIntermTyped*); + void checkLocation(const TSourceLoc&, TOperator); + TIntermTyped* handleLengthMethod(const TSourceLoc&, TFunction*, TIntermNode*); + void addInputArgumentConversions(const TFunction&, TIntermNode*&) const; + TIntermTyped* addOutputArgumentConversions(const TFunction&, TIntermAggregate&) const; + void builtInOpCheck(const TSourceLoc&, const TFunction&, TIntermOperator&); + void nonOpBuiltInCheck(const TSourceLoc&, const TFunction&, TIntermAggregate&); + void userFunctionCallCheck(const TSourceLoc&, TIntermAggregate&); + void samplerConstructorLocationCheck(const TSourceLoc&, const char* token, TIntermNode*); + TFunction* handleConstructorCall(const TSourceLoc&, const TPublicType&); + void handlePrecisionQualifier(const TSourceLoc&, TQualifier&, TPrecisionQualifier); + void checkPrecisionQualifier(const TSourceLoc&, TPrecisionQualifier); + void memorySemanticsCheck(const TSourceLoc&, const TFunction&, const TIntermOperator& callNode); + + void assignError(const TSourceLoc&, const char* op, TString left, TString right); + void unaryOpError(const TSourceLoc&, const char* op, TString operand); + void binaryOpError(const TSourceLoc&, const char* op, TString left, TString right); + void variableCheck(TIntermTyped*& nodePtr); + bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override; + void rValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override; + void constantValueCheck(TIntermTyped* node, const char* token); + void integerCheck(const TIntermTyped* node, const char* token); + void globalCheck(const TSourceLoc&, const char* token); + bool constructorError(const TSourceLoc&, TIntermNode*, TFunction&, TOperator, TType&); + bool constructorTextureSamplerError(const TSourceLoc&, const TFunction&); + void arraySizeCheck(const TSourceLoc&, TIntermTyped* expr, TArraySize&, const char *sizeType); + bool arrayQualifierError(const TSourceLoc&, const TQualifier&); + bool arrayError(const TSourceLoc&, const TType&); + void arraySizeRequiredCheck(const TSourceLoc&, const TArraySizes&); + void structArrayCheck(const TSourceLoc&, const TType& structure); + void arraySizesCheck(const TSourceLoc&, const TQualifier&, TArraySizes*, const TIntermTyped* initializer, bool lastMember); + void arrayOfArrayVersionCheck(const TSourceLoc&, const TArraySizes*); + bool voidErrorCheck(const TSourceLoc&, const TString&, TBasicType); + void boolCheck(const TSourceLoc&, const TIntermTyped*); + void boolCheck(const TSourceLoc&, const TPublicType&); + void samplerCheck(const TSourceLoc&, const TType&, const TString& identifier, TIntermTyped* initializer); + void atomicUintCheck(const TSourceLoc&, const TType&, const TString& identifier); + void accStructCheck(const TSourceLoc & loc, const TType & type, const TString & identifier); + void transparentOpaqueCheck(const TSourceLoc&, const TType&, const TString& identifier); + void memberQualifierCheck(glslang::TPublicType&); + void globalQualifierFixCheck(const TSourceLoc&, TQualifier&); + void globalQualifierTypeCheck(const TSourceLoc&, const TQualifier&, const TPublicType&); + bool structQualifierErrorCheck(const TSourceLoc&, const TPublicType& pType); + void mergeQualifiers(const TSourceLoc&, TQualifier& dst, const TQualifier& src, bool force); + void setDefaultPrecision(const TSourceLoc&, TPublicType&, TPrecisionQualifier); + int computeSamplerTypeIndex(TSampler&); + TPrecisionQualifier getDefaultPrecision(TPublicType&); + void precisionQualifierCheck(const TSourceLoc&, TBasicType, TQualifier&); + void parameterTypeCheck(const TSourceLoc&, TStorageQualifier qualifier, const TType& type); + bool containsFieldWithBasicType(const TType& type ,TBasicType basicType); + TSymbol* redeclareBuiltinVariable(const TSourceLoc&, const TString&, const TQualifier&, const TShaderQualifiers&); + void redeclareBuiltinBlock(const TSourceLoc&, TTypeList& typeList, const TString& blockName, const TString* instanceName, TArraySizes* arraySizes); + void paramCheckFixStorage(const TSourceLoc&, const TStorageQualifier&, TType& type); + void paramCheckFix(const TSourceLoc&, const TQualifier&, TType& type); + void nestedBlockCheck(const TSourceLoc&); + void nestedStructCheck(const TSourceLoc&); + void arrayObjectCheck(const TSourceLoc&, const TType&, const char* op); + void opaqueCheck(const TSourceLoc&, const TType&, const char* op); + void referenceCheck(const TSourceLoc&, const TType&, const char* op); + void storage16BitAssignmentCheck(const TSourceLoc&, const TType&, const char* op); + void specializationCheck(const TSourceLoc&, const TType&, const char* op); + void structTypeCheck(const TSourceLoc&, TPublicType&); + void inductiveLoopCheck(const TSourceLoc&, TIntermNode* init, TIntermLoop* loop); + void arrayLimitCheck(const TSourceLoc&, const TString&, int size); + void limitCheck(const TSourceLoc&, int value, const char* limit, const char* feature); + + void inductiveLoopBodyCheck(TIntermNode*, int loopIndexId, TSymbolTable&); + void constantIndexExpressionCheck(TIntermNode*); + + void setLayoutQualifier(const TSourceLoc&, TPublicType&, TString&); + void setLayoutQualifier(const TSourceLoc&, TPublicType&, TString&, const TIntermTyped*); + void mergeObjectLayoutQualifiers(TQualifier& dest, const TQualifier& src, bool inheritOnly); + void layoutObjectCheck(const TSourceLoc&, const TSymbol&); + void layoutMemberLocationArrayCheck(const TSourceLoc&, bool memberWithLocation, TArraySizes* arraySizes); + void layoutTypeCheck(const TSourceLoc&, const TType&); + void layoutQualifierCheck(const TSourceLoc&, const TQualifier&); + void checkNoShaderLayouts(const TSourceLoc&, const TShaderQualifiers&); + void fixOffset(const TSourceLoc&, TSymbol&); + + const TFunction* findFunction(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + const TFunction* findFunctionExact(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + const TFunction* findFunction120(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + const TFunction* findFunction400(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + const TFunction* findFunctionExplicitTypes(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + void declareTypeDefaults(const TSourceLoc&, const TPublicType&); + TIntermNode* declareVariable(const TSourceLoc&, TString& identifier, const TPublicType&, TArraySizes* typeArray = 0, TIntermTyped* initializer = 0); + TIntermTyped* addConstructor(const TSourceLoc&, TIntermNode*, const TType&); + TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&); + TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset); + void inheritMemoryQualifiers(const TQualifier& from, TQualifier& to); + void declareBlock(const TSourceLoc&, TTypeList& typeList, const TString* instanceName = 0, TArraySizes* arraySizes = 0); + void blockStageIoCheck(const TSourceLoc&, const TQualifier&); + void blockQualifierCheck(const TSourceLoc&, const TQualifier&, bool instanceName); + void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation); + void fixXfbOffsets(TQualifier&, TTypeList&); + void fixBlockUniformOffsets(TQualifier&, TTypeList&); + void addQualifierToExisting(const TSourceLoc&, TQualifier, const TString& identifier); + void addQualifierToExisting(const TSourceLoc&, TQualifier, TIdentifierList&); + void invariantCheck(const TSourceLoc&, const TQualifier&); + void updateStandaloneQualifierDefaults(const TSourceLoc&, const TPublicType&); + void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode); + TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body); + +#ifndef GLSLANG_WEB + TAttributeType attributeFromName(const TString& name) const; + TAttributes* makeAttributes(const TString& identifier) const; + TAttributes* makeAttributes(const TString& identifier, TIntermNode* node) const; + TAttributes* mergeAttributes(TAttributes*, TAttributes*) const; + + // Determine selection control from attributes + void handleSelectionAttributes(const TAttributes& attributes, TIntermNode*); + void handleSwitchAttributes(const TAttributes& attributes, TIntermNode*); + // Determine loop control from attributes + void handleLoopAttributes(const TAttributes& attributes, TIntermNode*); +#endif + + void checkAndResizeMeshViewDim(const TSourceLoc&, TType&, bool isBlockMember); + +protected: + void nonInitConstCheck(const TSourceLoc&, TString& identifier, TType& type); + void inheritGlobalDefaults(TQualifier& dst) const; + TVariable* makeInternalVariable(const char* name, const TType&) const; + TVariable* declareNonArray(const TSourceLoc&, const TString& identifier, const TType&); + void declareArray(const TSourceLoc&, const TString& identifier, const TType&, TSymbol*&); + void checkRuntimeSizable(const TSourceLoc&, const TIntermTyped&); + bool isRuntimeLength(const TIntermTyped&) const; + TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable); + TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer); +#ifndef GLSLANG_WEB + void finish() override; +#endif + +public: + // + // Generally, bison productions, the scanner, and the PP need read/write access to these; just give them direct access + // + + // Current state of parsing + bool inMain; // if inside a function, true if the function is main + const TString* blockName; + TQualifier currentBlockQualifier; + TPrecisionQualifier defaultPrecision[EbtNumTypes]; + TBuiltInResource resources; + TLimits& limits; + +protected: + TParseContext(TParseContext&); + TParseContext& operator=(TParseContext&); + + static const int maxSamplerIndex = EsdNumDims * (EbtNumTypes * (2 * 2 * 2 * 2 * 2 * 2)); // see computeSamplerTypeIndex() + TPrecisionQualifier defaultSamplerPrecision[maxSamplerIndex]; + TPrecisionManager precisionManager; + TQualifier globalBufferDefaults; + TQualifier globalUniformDefaults; + TQualifier globalInputDefaults; + TQualifier globalOutputDefaults; + TString currentCaller; // name of last function body entered (not valid when at global scope) +#ifndef GLSLANG_WEB + int* atomicUintOffsets; // to become an array of the right size to hold an offset per binding point + bool anyIndexLimits; + TIdSetType inductiveLoopIds; + TVector needsIndexLimitationChecking; + + // + // Geometry shader input arrays: + // - array sizing is based on input primitive and/or explicit size + // + // Tessellation control output arrays: + // - array sizing is based on output layout(vertices=...) and/or explicit size + // + // Both: + // - array sizing is retroactive + // - built-in block redeclarations interact with this + // + // Design: + // - use a per-context "resize-list", a list of symbols whose array sizes + // can be fixed + // + // - the resize-list starts empty at beginning of user-shader compilation, it does + // not have built-ins in it + // + // - on built-in array use: copyUp() symbol and add it to the resize-list + // + // - on user array declaration: add it to the resize-list + // + // - on block redeclaration: copyUp() symbol and add it to the resize-list + // * note, that appropriately gives an error if redeclaring a block that + // was already used and hence already copied-up + // + // - on seeing a layout declaration that sizes the array, fix everything in the + // resize-list, giving errors for mismatch + // + // - on seeing an array size declaration, give errors on mismatch between it and previous + // array-sizing declarations + // + TVector ioArraySymbolResizeList; +#endif +}; + +} // end namespace glslang + +#endif // _PARSER_HELPER_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/PoolAlloc.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/PoolAlloc.cpp new file mode 100644 index 000000000..84c40f4e7 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/PoolAlloc.cpp @@ -0,0 +1,315 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/Common.h" +#include "../Include/PoolAlloc.h" + +#include "../Include/InitializeGlobals.h" +#include "../OSDependent/osinclude.h" + +namespace glslang { + +// Process-wide TLS index +OS_TLSIndex PoolIndex; + +// Return the thread-specific current pool. +TPoolAllocator& GetThreadPoolAllocator() +{ + return *static_cast(OS_GetTLSValue(PoolIndex)); +} + +// Set the thread-specific current pool. +void SetThreadPoolAllocator(TPoolAllocator* poolAllocator) +{ + OS_SetTLSValue(PoolIndex, poolAllocator); +} + +// Process-wide set up of the TLS pool storage. +bool InitializePoolIndex() +{ + // Allocate a TLS index. + if ((PoolIndex = OS_AllocTLSIndex()) == OS_INVALID_TLS_INDEX) + return false; + + return true; +} + +// +// Implement the functionality of the TPoolAllocator class, which +// is documented in PoolAlloc.h. +// +TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) : + pageSize(growthIncrement), + alignment(allocationAlignment), + freeList(nullptr), + inUseList(nullptr), + numCalls(0) +{ + // + // Don't allow page sizes we know are smaller than all common + // OS page sizes. + // + if (pageSize < 4*1024) + pageSize = 4*1024; + + // + // A large currentPageOffset indicates a new page needs to + // be obtained to allocate memory. + // + currentPageOffset = pageSize; + + // + // Adjust alignment to be at least pointer aligned and + // power of 2. + // + size_t minAlign = sizeof(void*); + alignment &= ~(minAlign - 1); + if (alignment < minAlign) + alignment = minAlign; + size_t a = 1; + while (a < alignment) + a <<= 1; + alignment = a; + alignmentMask = a - 1; + + // + // Align header skip + // + headerSkip = minAlign; + if (headerSkip < sizeof(tHeader)) { + headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask; + } + + push(); +} + +TPoolAllocator::~TPoolAllocator() +{ + while (inUseList) { + tHeader* next = inUseList->nextPage; + inUseList->~tHeader(); + delete [] reinterpret_cast(inUseList); + inUseList = next; + } + + // + // Always delete the free list memory - it can't be being + // (correctly) referenced, whether the pool allocator was + // global or not. We should not check the guard blocks + // here, because we did it already when the block was + // placed into the free list. + // + while (freeList) { + tHeader* next = freeList->nextPage; + delete [] reinterpret_cast(freeList); + freeList = next; + } +} + +const unsigned char TAllocation::guardBlockBeginVal = 0xfb; +const unsigned char TAllocation::guardBlockEndVal = 0xfe; +const unsigned char TAllocation::userDataFill = 0xcd; + +# ifdef GUARD_BLOCKS + const size_t TAllocation::guardBlockSize = 16; +# else + const size_t TAllocation::guardBlockSize = 0; +# endif + +// +// Check a single guard block for damage +// +#ifdef GUARD_BLOCKS +void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const +#else +void TAllocation::checkGuardBlock(unsigned char*, unsigned char, const char*) const +#endif +{ +#ifdef GUARD_BLOCKS + for (size_t x = 0; x < guardBlockSize; x++) { + if (blockMem[x] != val) { + const int maxSize = 80; + char assertMsg[maxSize]; + + // We don't print the assert message. It's here just to be helpful. + snprintf(assertMsg, maxSize, "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n", + locText, size, data()); + assert(0 && "PoolAlloc: Damage in guard block"); + } + } +#else + assert(guardBlockSize == 0); +#endif +} + +void TPoolAllocator::push() +{ + tAllocState state = { currentPageOffset, inUseList }; + + stack.push_back(state); + + // + // Indicate there is no current page to allocate from. + // + currentPageOffset = pageSize; +} + +// +// Do a mass-deallocation of all the individual allocations +// that have occurred since the last push(), or since the +// last pop(), or since the object's creation. +// +// The deallocated pages are saved for future allocations. +// +void TPoolAllocator::pop() +{ + if (stack.size() < 1) + return; + + tHeader* page = stack.back().page; + currentPageOffset = stack.back().offset; + + while (inUseList != page) { + tHeader* nextInUse = inUseList->nextPage; + size_t pageCount = inUseList->pageCount; + + // This technically ends the lifetime of the header as C++ object, + // but we will still control the memory and reuse it. + inUseList->~tHeader(); // currently, just a debug allocation checker + + if (pageCount > 1) { + delete [] reinterpret_cast(inUseList); + } else { + inUseList->nextPage = freeList; + freeList = inUseList; + } + inUseList = nextInUse; + } + + stack.pop_back(); +} + +// +// Do a mass-deallocation of all the individual allocations +// that have occurred. +// +void TPoolAllocator::popAll() +{ + while (stack.size() > 0) + pop(); +} + +void* TPoolAllocator::allocate(size_t numBytes) +{ + // If we are using guard blocks, all allocations are bracketed by + // them: [guardblock][allocation][guardblock]. numBytes is how + // much memory the caller asked for. allocationSize is the total + // size including guard blocks. In release build, + // guardBlockSize=0 and this all gets optimized away. + size_t allocationSize = TAllocation::allocationSize(numBytes); + + // + // Just keep some interesting statistics. + // + ++numCalls; + totalBytes += numBytes; + + // + // Do the allocation, most likely case first, for efficiency. + // This step could be moved to be inline sometime. + // + if (currentPageOffset + allocationSize <= pageSize) { + // + // Safe to allocate from currentPageOffset. + // + unsigned char* memory = reinterpret_cast(inUseList) + currentPageOffset; + currentPageOffset += allocationSize; + currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask; + + return initializeAllocation(inUseList, memory, numBytes); + } + + if (allocationSize + headerSkip > pageSize) { + // + // Do a multi-page allocation. Don't mix these with the others. + // The OS is efficient and allocating and free-ing multiple pages. + // + size_t numBytesToAlloc = allocationSize + headerSkip; + tHeader* memory = reinterpret_cast(::new char[numBytesToAlloc]); + if (memory == 0) + return 0; + + // Use placement-new to initialize header + new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize); + inUseList = memory; + + currentPageOffset = pageSize; // make next allocation come from a new page + + // No guard blocks for multi-page allocations (yet) + return reinterpret_cast(reinterpret_cast(memory) + headerSkip); + } + + // + // Need a simple page to allocate from. + // + tHeader* memory; + if (freeList) { + memory = freeList; + freeList = freeList->nextPage; + } else { + memory = reinterpret_cast(::new char[pageSize]); + if (memory == 0) + return 0; + } + + // Use placement-new to initialize header + new(memory) tHeader(inUseList, 1); + inUseList = memory; + + unsigned char* ret = reinterpret_cast(inUseList) + headerSkip; + currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask; + + return initializeAllocation(inUseList, ret, numBytes); +} + +// +// Check all allocations in a list for damage by calling check on each. +// +void TAllocation::checkAllocList() const +{ + for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc) + alloc->check(); +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/RemoveTree.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/RemoveTree.cpp new file mode 100644 index 000000000..1d33bfd20 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/RemoveTree.cpp @@ -0,0 +1,118 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/intermediate.h" +#include "RemoveTree.h" + +namespace glslang { + +// +// Code to recursively delete the intermediate tree. +// +struct TRemoveTraverser : TIntermTraverser { + TRemoveTraverser() : TIntermTraverser(false, false, true, false) {} + + virtual void visitSymbol(TIntermSymbol* node) + { + delete node; + } + + virtual bool visitBinary(TVisit /* visit*/ , TIntermBinary* node) + { + delete node; + + return true; + } + + virtual bool visitUnary(TVisit /* visit */, TIntermUnary* node) + { + delete node; + + return true; + } + + virtual bool visitAggregate(TVisit /* visit*/ , TIntermAggregate* node) + { + delete node; + + return true; + } + + virtual bool visitSelection(TVisit /* visit*/ , TIntermSelection* node) + { + delete node; + + return true; + } + + virtual bool visitSwitch(TVisit /* visit*/ , TIntermSwitch* node) + { + delete node; + + return true; + } + + virtual void visitConstantUnion(TIntermConstantUnion* node) + { + delete node; + } + + virtual bool visitLoop(TVisit /* visit*/ , TIntermLoop* node) + { + delete node; + + return true; + } + + virtual bool visitBranch(TVisit /* visit*/ , TIntermBranch* node) + { + delete node; + + return true; + } +}; + +// +// Entry point. +// +void RemoveAllTreeNodes(TIntermNode* root) +{ + TRemoveTraverser it; + + root->traverse(&it); +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/RemoveTree.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/RemoveTree.h new file mode 100644 index 000000000..1ed015626 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/RemoveTree.h @@ -0,0 +1,41 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +namespace glslang { + +void RemoveAllTreeNodes(TIntermNode*); + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Scan.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Scan.cpp new file mode 100644 index 000000000..b251c47a4 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Scan.cpp @@ -0,0 +1,1833 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2020 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// GLSL scanning, leveraging the scanning done by the preprocessor. +// + +#include +#include +#include + +#include "../Include/Types.h" +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "attribute.h" +#include "glslang_tab.cpp.h" +#include "ScanContext.h" +#include "Scan.h" + +// preprocessor includes +#include "preprocessor/PpContext.h" +#include "preprocessor/PpTokens.h" + +// Required to avoid missing prototype warnings for some compilers +int yylex(YYSTYPE*, glslang::TParseContext&); + +namespace glslang { + +// read past any white space +void TInputScanner::consumeWhiteSpace(bool& foundNonSpaceTab) +{ + int c = peek(); // don't accidentally consume anything other than whitespace + while (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + if (c == '\r' || c == '\n') + foundNonSpaceTab = true; + get(); + c = peek(); + } +} + +// return true if a comment was actually consumed +bool TInputScanner::consumeComment() +{ + if (peek() != '/') + return false; + + get(); // consume the '/' + int c = peek(); + if (c == '/') { + + // a '//' style comment + get(); // consume the second '/' + c = get(); + do { + while (c != EndOfInput && c != '\\' && c != '\r' && c != '\n') + c = get(); + + if (c == EndOfInput || c == '\r' || c == '\n') { + while (c == '\r' || c == '\n') + c = get(); + + // we reached the end of the comment + break; + } else { + // it's a '\', so we need to keep going, after skipping what's escaped + + // read the skipped character + c = get(); + + // if it's a two-character newline, skip both characters + if (c == '\r' && peek() == '\n') + get(); + c = get(); + } + } while (true); + + // put back the last non-comment character + if (c != EndOfInput) + unget(); + + return true; + } else if (c == '*') { + + // a '/*' style comment + get(); // consume the '*' + c = get(); + do { + while (c != EndOfInput && c != '*') + c = get(); + if (c == '*') { + c = get(); + if (c == '/') + break; // end of comment + // not end of comment + } else // end of input + break; + } while (true); + + return true; + } else { + // it's not a comment, put the '/' back + unget(); + + return false; + } +} + +// skip whitespace, then skip a comment, rinse, repeat +void TInputScanner::consumeWhitespaceComment(bool& foundNonSpaceTab) +{ + do { + consumeWhiteSpace(foundNonSpaceTab); + + // if not starting a comment now, then done + int c = peek(); + if (c != '/' || c == EndOfInput) + return; + + // skip potential comment + foundNonSpaceTab = true; + if (! consumeComment()) + return; + + } while (true); +} + +// Returns true if there was non-white space (e.g., a comment, newline) before the #version +// or no #version was found; otherwise, returns false. There is no error case, it always +// succeeds, but will leave version == 0 if no #version was found. +// +// Sets notFirstToken based on whether tokens (beyond white space and comments) +// appeared before the #version. +// +// N.B. does not attempt to leave input in any particular known state. The assumption +// is that scanning will start anew, following the rules for the chosen version/profile, +// and with a corresponding parsing context. +// +bool TInputScanner::scanVersion(int& version, EProfile& profile, bool& notFirstToken) +{ + // This function doesn't have to get all the semantics correct, + // just find the #version if there is a correct one present. + // The preprocessor will have the responsibility of getting all the semantics right. + + bool versionNotFirst = false; // means not first WRT comments and white space, nothing more + notFirstToken = false; // means not first WRT to real tokens + version = 0; // means not found + profile = ENoProfile; + + bool foundNonSpaceTab = false; + bool lookingInMiddle = false; + int c; + do { + if (lookingInMiddle) { + notFirstToken = true; + // make forward progress by finishing off the current line plus extra new lines + if (peek() != '\n' && peek() != '\r') { + do { + c = get(); + } while (c != EndOfInput && c != '\n' && c != '\r'); + } + while (peek() == '\n' || peek() == '\r') + get(); + if (peek() == EndOfInput) + return true; + } + lookingInMiddle = true; + + // Nominal start, skipping the desktop allowed comments and white space, but tracking if + // something else was found for ES: + consumeWhitespaceComment(foundNonSpaceTab); + if (foundNonSpaceTab) + versionNotFirst = true; + + // "#" + if (get() != '#') { + versionNotFirst = true; + continue; + } + + // whitespace + do { + c = get(); + } while (c == ' ' || c == '\t'); + + // "version" + if ( c != 'v' || + get() != 'e' || + get() != 'r' || + get() != 's' || + get() != 'i' || + get() != 'o' || + get() != 'n') { + versionNotFirst = true; + continue; + } + + // whitespace + do { + c = get(); + } while (c == ' ' || c == '\t'); + + // version number + while (c >= '0' && c <= '9') { + version = 10 * version + (c - '0'); + c = get(); + } + if (version == 0) { + versionNotFirst = true; + continue; + } + + // whitespace + while (c == ' ' || c == '\t') + c = get(); + + // profile + const int maxProfileLength = 13; // not including any 0 + char profileString[maxProfileLength]; + int profileLength; + for (profileLength = 0; profileLength < maxProfileLength; ++profileLength) { + if (c == EndOfInput || c == ' ' || c == '\t' || c == '\n' || c == '\r') + break; + profileString[profileLength] = (char)c; + c = get(); + } + if (c != EndOfInput && c != ' ' && c != '\t' && c != '\n' && c != '\r') { + versionNotFirst = true; + continue; + } + + if (profileLength == 2 && strncmp(profileString, "es", profileLength) == 0) + profile = EEsProfile; + else if (profileLength == 4 && strncmp(profileString, "core", profileLength) == 0) + profile = ECoreProfile; + else if (profileLength == 13 && strncmp(profileString, "compatibility", profileLength) == 0) + profile = ECompatibilityProfile; + + return versionNotFirst; + } while (true); +} + +// Fill this in when doing glslang-level scanning, to hand back to the parser. +class TParserToken { +public: + explicit TParserToken(YYSTYPE& b) : sType(b) { } + + YYSTYPE& sType; +protected: + TParserToken(TParserToken&); + TParserToken& operator=(TParserToken&); +}; + +} // end namespace glslang + +// This is the function the glslang parser (i.e., bison) calls to get its next token +int yylex(YYSTYPE* glslangTokenDesc, glslang::TParseContext& parseContext) +{ + glslang::TParserToken token(*glslangTokenDesc); + + return parseContext.getScanContext()->tokenize(parseContext.getPpContext(), token); +} + +namespace { + +struct str_eq +{ + bool operator()(const char* lhs, const char* rhs) const + { + return strcmp(lhs, rhs) == 0; + } +}; + +struct str_hash +{ + size_t operator()(const char* str) const + { + // djb2 + unsigned long hash = 5381; + int c; + + while ((c = *str++) != 0) + hash = ((hash << 5) + hash) + c; + + return hash; + } +}; + +// A single global usable by all threads, by all versions, by all languages. +// After a single process-level initialization, this is read only and thread safe +std::unordered_map* KeywordMap = nullptr; +#ifndef GLSLANG_WEB +std::unordered_set* ReservedSet = nullptr; +#endif + +}; + +namespace glslang { + +void TScanContext::fillInKeywordMap() +{ + if (KeywordMap != nullptr) { + // this is really an error, as this should called only once per process + // but, the only risk is if two threads called simultaneously + return; + } + KeywordMap = new std::unordered_map; + + (*KeywordMap)["const"] = CONST; + (*KeywordMap)["uniform"] = UNIFORM; + (*KeywordMap)["buffer"] = BUFFER; + (*KeywordMap)["in"] = IN; + (*KeywordMap)["out"] = OUT; + (*KeywordMap)["smooth"] = SMOOTH; + (*KeywordMap)["flat"] = FLAT; + (*KeywordMap)["centroid"] = CENTROID; + (*KeywordMap)["invariant"] = INVARIANT; + (*KeywordMap)["packed"] = PACKED; + (*KeywordMap)["resource"] = RESOURCE; + (*KeywordMap)["inout"] = INOUT; + (*KeywordMap)["struct"] = STRUCT; + (*KeywordMap)["break"] = BREAK; + (*KeywordMap)["continue"] = CONTINUE; + (*KeywordMap)["do"] = DO; + (*KeywordMap)["for"] = FOR; + (*KeywordMap)["while"] = WHILE; + (*KeywordMap)["switch"] = SWITCH; + (*KeywordMap)["case"] = CASE; + (*KeywordMap)["default"] = DEFAULT; + (*KeywordMap)["if"] = IF; + (*KeywordMap)["else"] = ELSE; + (*KeywordMap)["discard"] = DISCARD; + (*KeywordMap)["return"] = RETURN; + (*KeywordMap)["void"] = VOID; + (*KeywordMap)["bool"] = BOOL; + (*KeywordMap)["float"] = FLOAT; + (*KeywordMap)["int"] = INT; + (*KeywordMap)["bvec2"] = BVEC2; + (*KeywordMap)["bvec3"] = BVEC3; + (*KeywordMap)["bvec4"] = BVEC4; + (*KeywordMap)["vec2"] = VEC2; + (*KeywordMap)["vec3"] = VEC3; + (*KeywordMap)["vec4"] = VEC4; + (*KeywordMap)["ivec2"] = IVEC2; + (*KeywordMap)["ivec3"] = IVEC3; + (*KeywordMap)["ivec4"] = IVEC4; + (*KeywordMap)["mat2"] = MAT2; + (*KeywordMap)["mat3"] = MAT3; + (*KeywordMap)["mat4"] = MAT4; + (*KeywordMap)["true"] = BOOLCONSTANT; + (*KeywordMap)["false"] = BOOLCONSTANT; + (*KeywordMap)["layout"] = LAYOUT; + (*KeywordMap)["shared"] = SHARED; + (*KeywordMap)["highp"] = HIGH_PRECISION; + (*KeywordMap)["mediump"] = MEDIUM_PRECISION; + (*KeywordMap)["lowp"] = LOW_PRECISION; + (*KeywordMap)["superp"] = SUPERP; + (*KeywordMap)["precision"] = PRECISION; + (*KeywordMap)["mat2x2"] = MAT2X2; + (*KeywordMap)["mat2x3"] = MAT2X3; + (*KeywordMap)["mat2x4"] = MAT2X4; + (*KeywordMap)["mat3x2"] = MAT3X2; + (*KeywordMap)["mat3x3"] = MAT3X3; + (*KeywordMap)["mat3x4"] = MAT3X4; + (*KeywordMap)["mat4x2"] = MAT4X2; + (*KeywordMap)["mat4x3"] = MAT4X3; + (*KeywordMap)["mat4x4"] = MAT4X4; + (*KeywordMap)["uint"] = UINT; + (*KeywordMap)["uvec2"] = UVEC2; + (*KeywordMap)["uvec3"] = UVEC3; + (*KeywordMap)["uvec4"] = UVEC4; + +#ifndef GLSLANG_WEB + (*KeywordMap)["nonuniformEXT"] = NONUNIFORM; + (*KeywordMap)["demote"] = DEMOTE; + (*KeywordMap)["attribute"] = ATTRIBUTE; + (*KeywordMap)["varying"] = VARYING; + (*KeywordMap)["noperspective"] = NOPERSPECTIVE; + (*KeywordMap)["coherent"] = COHERENT; + (*KeywordMap)["devicecoherent"] = DEVICECOHERENT; + (*KeywordMap)["queuefamilycoherent"] = QUEUEFAMILYCOHERENT; + (*KeywordMap)["workgroupcoherent"] = WORKGROUPCOHERENT; + (*KeywordMap)["subgroupcoherent"] = SUBGROUPCOHERENT; + (*KeywordMap)["shadercallcoherent"] = SHADERCALLCOHERENT; + (*KeywordMap)["nonprivate"] = NONPRIVATE; + (*KeywordMap)["restrict"] = RESTRICT; + (*KeywordMap)["readonly"] = READONLY; + (*KeywordMap)["writeonly"] = WRITEONLY; + (*KeywordMap)["atomic_uint"] = ATOMIC_UINT; + (*KeywordMap)["volatile"] = VOLATILE; + (*KeywordMap)["patch"] = PATCH; + (*KeywordMap)["sample"] = SAMPLE; + (*KeywordMap)["subroutine"] = SUBROUTINE; + (*KeywordMap)["dmat2"] = DMAT2; + (*KeywordMap)["dmat3"] = DMAT3; + (*KeywordMap)["dmat4"] = DMAT4; + (*KeywordMap)["dmat2x2"] = DMAT2X2; + (*KeywordMap)["dmat2x3"] = DMAT2X3; + (*KeywordMap)["dmat2x4"] = DMAT2X4; + (*KeywordMap)["dmat3x2"] = DMAT3X2; + (*KeywordMap)["dmat3x3"] = DMAT3X3; + (*KeywordMap)["dmat3x4"] = DMAT3X4; + (*KeywordMap)["dmat4x2"] = DMAT4X2; + (*KeywordMap)["dmat4x3"] = DMAT4X3; + (*KeywordMap)["dmat4x4"] = DMAT4X4; + (*KeywordMap)["image1D"] = IMAGE1D; + (*KeywordMap)["iimage1D"] = IIMAGE1D; + (*KeywordMap)["uimage1D"] = UIMAGE1D; + (*KeywordMap)["image2D"] = IMAGE2D; + (*KeywordMap)["iimage2D"] = IIMAGE2D; + (*KeywordMap)["uimage2D"] = UIMAGE2D; + (*KeywordMap)["image3D"] = IMAGE3D; + (*KeywordMap)["iimage3D"] = IIMAGE3D; + (*KeywordMap)["uimage3D"] = UIMAGE3D; + (*KeywordMap)["image2DRect"] = IMAGE2DRECT; + (*KeywordMap)["iimage2DRect"] = IIMAGE2DRECT; + (*KeywordMap)["uimage2DRect"] = UIMAGE2DRECT; + (*KeywordMap)["imageCube"] = IMAGECUBE; + (*KeywordMap)["iimageCube"] = IIMAGECUBE; + (*KeywordMap)["uimageCube"] = UIMAGECUBE; + (*KeywordMap)["imageBuffer"] = IMAGEBUFFER; + (*KeywordMap)["iimageBuffer"] = IIMAGEBUFFER; + (*KeywordMap)["uimageBuffer"] = UIMAGEBUFFER; + (*KeywordMap)["image1DArray"] = IMAGE1DARRAY; + (*KeywordMap)["iimage1DArray"] = IIMAGE1DARRAY; + (*KeywordMap)["uimage1DArray"] = UIMAGE1DARRAY; + (*KeywordMap)["image2DArray"] = IMAGE2DARRAY; + (*KeywordMap)["iimage2DArray"] = IIMAGE2DARRAY; + (*KeywordMap)["uimage2DArray"] = UIMAGE2DARRAY; + (*KeywordMap)["imageCubeArray"] = IMAGECUBEARRAY; + (*KeywordMap)["iimageCubeArray"] = IIMAGECUBEARRAY; + (*KeywordMap)["uimageCubeArray"] = UIMAGECUBEARRAY; + (*KeywordMap)["image2DMS"] = IMAGE2DMS; + (*KeywordMap)["iimage2DMS"] = IIMAGE2DMS; + (*KeywordMap)["uimage2DMS"] = UIMAGE2DMS; + (*KeywordMap)["image2DMSArray"] = IMAGE2DMSARRAY; + (*KeywordMap)["iimage2DMSArray"] = IIMAGE2DMSARRAY; + (*KeywordMap)["uimage2DMSArray"] = UIMAGE2DMSARRAY; + (*KeywordMap)["double"] = DOUBLE; + (*KeywordMap)["dvec2"] = DVEC2; + (*KeywordMap)["dvec3"] = DVEC3; + (*KeywordMap)["dvec4"] = DVEC4; + (*KeywordMap)["int64_t"] = INT64_T; + (*KeywordMap)["uint64_t"] = UINT64_T; + (*KeywordMap)["i64vec2"] = I64VEC2; + (*KeywordMap)["i64vec3"] = I64VEC3; + (*KeywordMap)["i64vec4"] = I64VEC4; + (*KeywordMap)["u64vec2"] = U64VEC2; + (*KeywordMap)["u64vec3"] = U64VEC3; + (*KeywordMap)["u64vec4"] = U64VEC4; + + // GL_EXT_shader_explicit_arithmetic_types + (*KeywordMap)["int8_t"] = INT8_T; + (*KeywordMap)["i8vec2"] = I8VEC2; + (*KeywordMap)["i8vec3"] = I8VEC3; + (*KeywordMap)["i8vec4"] = I8VEC4; + (*KeywordMap)["uint8_t"] = UINT8_T; + (*KeywordMap)["u8vec2"] = U8VEC2; + (*KeywordMap)["u8vec3"] = U8VEC3; + (*KeywordMap)["u8vec4"] = U8VEC4; + + (*KeywordMap)["int16_t"] = INT16_T; + (*KeywordMap)["i16vec2"] = I16VEC2; + (*KeywordMap)["i16vec3"] = I16VEC3; + (*KeywordMap)["i16vec4"] = I16VEC4; + (*KeywordMap)["uint16_t"] = UINT16_T; + (*KeywordMap)["u16vec2"] = U16VEC2; + (*KeywordMap)["u16vec3"] = U16VEC3; + (*KeywordMap)["u16vec4"] = U16VEC4; + + (*KeywordMap)["int32_t"] = INT32_T; + (*KeywordMap)["i32vec2"] = I32VEC2; + (*KeywordMap)["i32vec3"] = I32VEC3; + (*KeywordMap)["i32vec4"] = I32VEC4; + (*KeywordMap)["uint32_t"] = UINT32_T; + (*KeywordMap)["u32vec2"] = U32VEC2; + (*KeywordMap)["u32vec3"] = U32VEC3; + (*KeywordMap)["u32vec4"] = U32VEC4; + + (*KeywordMap)["float16_t"] = FLOAT16_T; + (*KeywordMap)["f16vec2"] = F16VEC2; + (*KeywordMap)["f16vec3"] = F16VEC3; + (*KeywordMap)["f16vec4"] = F16VEC4; + (*KeywordMap)["f16mat2"] = F16MAT2; + (*KeywordMap)["f16mat3"] = F16MAT3; + (*KeywordMap)["f16mat4"] = F16MAT4; + (*KeywordMap)["f16mat2x2"] = F16MAT2X2; + (*KeywordMap)["f16mat2x3"] = F16MAT2X3; + (*KeywordMap)["f16mat2x4"] = F16MAT2X4; + (*KeywordMap)["f16mat3x2"] = F16MAT3X2; + (*KeywordMap)["f16mat3x3"] = F16MAT3X3; + (*KeywordMap)["f16mat3x4"] = F16MAT3X4; + (*KeywordMap)["f16mat4x2"] = F16MAT4X2; + (*KeywordMap)["f16mat4x3"] = F16MAT4X3; + (*KeywordMap)["f16mat4x4"] = F16MAT4X4; + + (*KeywordMap)["float32_t"] = FLOAT32_T; + (*KeywordMap)["f32vec2"] = F32VEC2; + (*KeywordMap)["f32vec3"] = F32VEC3; + (*KeywordMap)["f32vec4"] = F32VEC4; + (*KeywordMap)["f32mat2"] = F32MAT2; + (*KeywordMap)["f32mat3"] = F32MAT3; + (*KeywordMap)["f32mat4"] = F32MAT4; + (*KeywordMap)["f32mat2x2"] = F32MAT2X2; + (*KeywordMap)["f32mat2x3"] = F32MAT2X3; + (*KeywordMap)["f32mat2x4"] = F32MAT2X4; + (*KeywordMap)["f32mat3x2"] = F32MAT3X2; + (*KeywordMap)["f32mat3x3"] = F32MAT3X3; + (*KeywordMap)["f32mat3x4"] = F32MAT3X4; + (*KeywordMap)["f32mat4x2"] = F32MAT4X2; + (*KeywordMap)["f32mat4x3"] = F32MAT4X3; + (*KeywordMap)["f32mat4x4"] = F32MAT4X4; + (*KeywordMap)["float64_t"] = FLOAT64_T; + (*KeywordMap)["f64vec2"] = F64VEC2; + (*KeywordMap)["f64vec3"] = F64VEC3; + (*KeywordMap)["f64vec4"] = F64VEC4; + (*KeywordMap)["f64mat2"] = F64MAT2; + (*KeywordMap)["f64mat3"] = F64MAT3; + (*KeywordMap)["f64mat4"] = F64MAT4; + (*KeywordMap)["f64mat2x2"] = F64MAT2X2; + (*KeywordMap)["f64mat2x3"] = F64MAT2X3; + (*KeywordMap)["f64mat2x4"] = F64MAT2X4; + (*KeywordMap)["f64mat3x2"] = F64MAT3X2; + (*KeywordMap)["f64mat3x3"] = F64MAT3X3; + (*KeywordMap)["f64mat3x4"] = F64MAT3X4; + (*KeywordMap)["f64mat4x2"] = F64MAT4X2; + (*KeywordMap)["f64mat4x3"] = F64MAT4X3; + (*KeywordMap)["f64mat4x4"] = F64MAT4X4; +#endif + + (*KeywordMap)["sampler2D"] = SAMPLER2D; + (*KeywordMap)["samplerCube"] = SAMPLERCUBE; + (*KeywordMap)["samplerCubeShadow"] = SAMPLERCUBESHADOW; + (*KeywordMap)["sampler2DArray"] = SAMPLER2DARRAY; + (*KeywordMap)["sampler2DArrayShadow"] = SAMPLER2DARRAYSHADOW; + (*KeywordMap)["isampler2D"] = ISAMPLER2D; + (*KeywordMap)["isampler3D"] = ISAMPLER3D; + (*KeywordMap)["isamplerCube"] = ISAMPLERCUBE; + (*KeywordMap)["isampler2DArray"] = ISAMPLER2DARRAY; + (*KeywordMap)["usampler2D"] = USAMPLER2D; + (*KeywordMap)["usampler3D"] = USAMPLER3D; + (*KeywordMap)["usamplerCube"] = USAMPLERCUBE; + (*KeywordMap)["usampler2DArray"] = USAMPLER2DARRAY; + (*KeywordMap)["sampler3D"] = SAMPLER3D; + (*KeywordMap)["sampler2DShadow"] = SAMPLER2DSHADOW; + (*KeywordMap)["samplerVideo"] = SAMPLERVIDEO; + + (*KeywordMap)["texture2D"] = TEXTURE2D; + (*KeywordMap)["textureCube"] = TEXTURECUBE; + (*KeywordMap)["texture2DArray"] = TEXTURE2DARRAY; + (*KeywordMap)["itexture2D"] = ITEXTURE2D; + (*KeywordMap)["itexture3D"] = ITEXTURE3D; + (*KeywordMap)["itextureCube"] = ITEXTURECUBE; + (*KeywordMap)["itexture2DArray"] = ITEXTURE2DARRAY; + (*KeywordMap)["utexture2D"] = UTEXTURE2D; + (*KeywordMap)["utexture3D"] = UTEXTURE3D; + (*KeywordMap)["utextureCube"] = UTEXTURECUBE; + (*KeywordMap)["utexture2DArray"] = UTEXTURE2DARRAY; + (*KeywordMap)["texture3D"] = TEXTURE3D; + + (*KeywordMap)["sampler"] = SAMPLER; + (*KeywordMap)["samplerShadow"] = SAMPLERSHADOW; + +#ifndef GLSLANG_WEB + (*KeywordMap)["textureCubeArray"] = TEXTURECUBEARRAY; + (*KeywordMap)["itextureCubeArray"] = ITEXTURECUBEARRAY; + (*KeywordMap)["utextureCubeArray"] = UTEXTURECUBEARRAY; + (*KeywordMap)["samplerCubeArray"] = SAMPLERCUBEARRAY; + (*KeywordMap)["samplerCubeArrayShadow"] = SAMPLERCUBEARRAYSHADOW; + (*KeywordMap)["isamplerCubeArray"] = ISAMPLERCUBEARRAY; + (*KeywordMap)["usamplerCubeArray"] = USAMPLERCUBEARRAY; + (*KeywordMap)["sampler1DArrayShadow"] = SAMPLER1DARRAYSHADOW; + (*KeywordMap)["isampler1DArray"] = ISAMPLER1DARRAY; + (*KeywordMap)["usampler1D"] = USAMPLER1D; + (*KeywordMap)["isampler1D"] = ISAMPLER1D; + (*KeywordMap)["usampler1DArray"] = USAMPLER1DARRAY; + (*KeywordMap)["samplerBuffer"] = SAMPLERBUFFER; + (*KeywordMap)["isampler2DRect"] = ISAMPLER2DRECT; + (*KeywordMap)["usampler2DRect"] = USAMPLER2DRECT; + (*KeywordMap)["isamplerBuffer"] = ISAMPLERBUFFER; + (*KeywordMap)["usamplerBuffer"] = USAMPLERBUFFER; + (*KeywordMap)["sampler2DMS"] = SAMPLER2DMS; + (*KeywordMap)["isampler2DMS"] = ISAMPLER2DMS; + (*KeywordMap)["usampler2DMS"] = USAMPLER2DMS; + (*KeywordMap)["sampler2DMSArray"] = SAMPLER2DMSARRAY; + (*KeywordMap)["isampler2DMSArray"] = ISAMPLER2DMSARRAY; + (*KeywordMap)["usampler2DMSArray"] = USAMPLER2DMSARRAY; + (*KeywordMap)["sampler1D"] = SAMPLER1D; + (*KeywordMap)["sampler1DShadow"] = SAMPLER1DSHADOW; + (*KeywordMap)["sampler2DRect"] = SAMPLER2DRECT; + (*KeywordMap)["sampler2DRectShadow"] = SAMPLER2DRECTSHADOW; + (*KeywordMap)["sampler1DArray"] = SAMPLER1DARRAY; + + (*KeywordMap)["samplerExternalOES"] = SAMPLEREXTERNALOES; // GL_OES_EGL_image_external + (*KeywordMap)["samplerVideo"] = SAMPLERVIDEO; // K extension + + (*KeywordMap)["__samplerExternal2DY2YEXT"] = SAMPLEREXTERNAL2DY2YEXT; // GL_EXT_YUV_target + + (*KeywordMap)["itexture1DArray"] = ITEXTURE1DARRAY; + (*KeywordMap)["utexture1D"] = UTEXTURE1D; + (*KeywordMap)["itexture1D"] = ITEXTURE1D; + (*KeywordMap)["utexture1DArray"] = UTEXTURE1DARRAY; + (*KeywordMap)["textureBuffer"] = TEXTUREBUFFER; + (*KeywordMap)["itexture2DRect"] = ITEXTURE2DRECT; + (*KeywordMap)["utexture2DRect"] = UTEXTURE2DRECT; + (*KeywordMap)["itextureBuffer"] = ITEXTUREBUFFER; + (*KeywordMap)["utextureBuffer"] = UTEXTUREBUFFER; + (*KeywordMap)["texture2DMS"] = TEXTURE2DMS; + (*KeywordMap)["itexture2DMS"] = ITEXTURE2DMS; + (*KeywordMap)["utexture2DMS"] = UTEXTURE2DMS; + (*KeywordMap)["texture2DMSArray"] = TEXTURE2DMSARRAY; + (*KeywordMap)["itexture2DMSArray"] = ITEXTURE2DMSARRAY; + (*KeywordMap)["utexture2DMSArray"] = UTEXTURE2DMSARRAY; + (*KeywordMap)["texture1D"] = TEXTURE1D; + (*KeywordMap)["texture2DRect"] = TEXTURE2DRECT; + (*KeywordMap)["texture1DArray"] = TEXTURE1DARRAY; + + (*KeywordMap)["subpassInput"] = SUBPASSINPUT; + (*KeywordMap)["subpassInputMS"] = SUBPASSINPUTMS; + (*KeywordMap)["isubpassInput"] = ISUBPASSINPUT; + (*KeywordMap)["isubpassInputMS"] = ISUBPASSINPUTMS; + (*KeywordMap)["usubpassInput"] = USUBPASSINPUT; + (*KeywordMap)["usubpassInputMS"] = USUBPASSINPUTMS; + + (*KeywordMap)["f16sampler1D"] = F16SAMPLER1D; + (*KeywordMap)["f16sampler2D"] = F16SAMPLER2D; + (*KeywordMap)["f16sampler3D"] = F16SAMPLER3D; + (*KeywordMap)["f16sampler2DRect"] = F16SAMPLER2DRECT; + (*KeywordMap)["f16samplerCube"] = F16SAMPLERCUBE; + (*KeywordMap)["f16sampler1DArray"] = F16SAMPLER1DARRAY; + (*KeywordMap)["f16sampler2DArray"] = F16SAMPLER2DARRAY; + (*KeywordMap)["f16samplerCubeArray"] = F16SAMPLERCUBEARRAY; + (*KeywordMap)["f16samplerBuffer"] = F16SAMPLERBUFFER; + (*KeywordMap)["f16sampler2DMS"] = F16SAMPLER2DMS; + (*KeywordMap)["f16sampler2DMSArray"] = F16SAMPLER2DMSARRAY; + (*KeywordMap)["f16sampler1DShadow"] = F16SAMPLER1DSHADOW; + (*KeywordMap)["f16sampler2DShadow"] = F16SAMPLER2DSHADOW; + (*KeywordMap)["f16sampler2DRectShadow"] = F16SAMPLER2DRECTSHADOW; + (*KeywordMap)["f16samplerCubeShadow"] = F16SAMPLERCUBESHADOW; + (*KeywordMap)["f16sampler1DArrayShadow"] = F16SAMPLER1DARRAYSHADOW; + (*KeywordMap)["f16sampler2DArrayShadow"] = F16SAMPLER2DARRAYSHADOW; + (*KeywordMap)["f16samplerCubeArrayShadow"] = F16SAMPLERCUBEARRAYSHADOW; + + (*KeywordMap)["f16image1D"] = F16IMAGE1D; + (*KeywordMap)["f16image2D"] = F16IMAGE2D; + (*KeywordMap)["f16image3D"] = F16IMAGE3D; + (*KeywordMap)["f16image2DRect"] = F16IMAGE2DRECT; + (*KeywordMap)["f16imageCube"] = F16IMAGECUBE; + (*KeywordMap)["f16image1DArray"] = F16IMAGE1DARRAY; + (*KeywordMap)["f16image2DArray"] = F16IMAGE2DARRAY; + (*KeywordMap)["f16imageCubeArray"] = F16IMAGECUBEARRAY; + (*KeywordMap)["f16imageBuffer"] = F16IMAGEBUFFER; + (*KeywordMap)["f16image2DMS"] = F16IMAGE2DMS; + (*KeywordMap)["f16image2DMSArray"] = F16IMAGE2DMSARRAY; + + (*KeywordMap)["f16texture1D"] = F16TEXTURE1D; + (*KeywordMap)["f16texture2D"] = F16TEXTURE2D; + (*KeywordMap)["f16texture3D"] = F16TEXTURE3D; + (*KeywordMap)["f16texture2DRect"] = F16TEXTURE2DRECT; + (*KeywordMap)["f16textureCube"] = F16TEXTURECUBE; + (*KeywordMap)["f16texture1DArray"] = F16TEXTURE1DARRAY; + (*KeywordMap)["f16texture2DArray"] = F16TEXTURE2DARRAY; + (*KeywordMap)["f16textureCubeArray"] = F16TEXTURECUBEARRAY; + (*KeywordMap)["f16textureBuffer"] = F16TEXTUREBUFFER; + (*KeywordMap)["f16texture2DMS"] = F16TEXTURE2DMS; + (*KeywordMap)["f16texture2DMSArray"] = F16TEXTURE2DMSARRAY; + + (*KeywordMap)["f16subpassInput"] = F16SUBPASSINPUT; + (*KeywordMap)["f16subpassInputMS"] = F16SUBPASSINPUTMS; + (*KeywordMap)["__explicitInterpAMD"] = EXPLICITINTERPAMD; + (*KeywordMap)["pervertexNV"] = PERVERTEXNV; + (*KeywordMap)["precise"] = PRECISE; + + (*KeywordMap)["rayPayloadNV"] = PAYLOADNV; + (*KeywordMap)["rayPayloadEXT"] = PAYLOADEXT; + (*KeywordMap)["rayPayloadInNV"] = PAYLOADINNV; + (*KeywordMap)["rayPayloadInEXT"] = PAYLOADINEXT; + (*KeywordMap)["hitAttributeNV"] = HITATTRNV; + (*KeywordMap)["hitAttributeEXT"] = HITATTREXT; + (*KeywordMap)["callableDataNV"] = CALLDATANV; + (*KeywordMap)["callableDataEXT"] = CALLDATAEXT; + (*KeywordMap)["callableDataInNV"] = CALLDATAINNV; + (*KeywordMap)["callableDataInEXT"] = CALLDATAINEXT; + (*KeywordMap)["accelerationStructureNV"] = ACCSTRUCTNV; + (*KeywordMap)["accelerationStructureEXT"] = ACCSTRUCTEXT; + (*KeywordMap)["rayQueryEXT"] = RAYQUERYEXT; + (*KeywordMap)["perprimitiveNV"] = PERPRIMITIVENV; + (*KeywordMap)["perviewNV"] = PERVIEWNV; + (*KeywordMap)["taskNV"] = PERTASKNV; + + (*KeywordMap)["fcoopmatNV"] = FCOOPMATNV; + (*KeywordMap)["icoopmatNV"] = ICOOPMATNV; + (*KeywordMap)["ucoopmatNV"] = UCOOPMATNV; + + ReservedSet = new std::unordered_set; + + ReservedSet->insert("common"); + ReservedSet->insert("partition"); + ReservedSet->insert("active"); + ReservedSet->insert("asm"); + ReservedSet->insert("class"); + ReservedSet->insert("union"); + ReservedSet->insert("enum"); + ReservedSet->insert("typedef"); + ReservedSet->insert("template"); + ReservedSet->insert("this"); + ReservedSet->insert("goto"); + ReservedSet->insert("inline"); + ReservedSet->insert("noinline"); + ReservedSet->insert("public"); + ReservedSet->insert("static"); + ReservedSet->insert("extern"); + ReservedSet->insert("external"); + ReservedSet->insert("interface"); + ReservedSet->insert("long"); + ReservedSet->insert("short"); + ReservedSet->insert("half"); + ReservedSet->insert("fixed"); + ReservedSet->insert("unsigned"); + ReservedSet->insert("input"); + ReservedSet->insert("output"); + ReservedSet->insert("hvec2"); + ReservedSet->insert("hvec3"); + ReservedSet->insert("hvec4"); + ReservedSet->insert("fvec2"); + ReservedSet->insert("fvec3"); + ReservedSet->insert("fvec4"); + ReservedSet->insert("sampler3DRect"); + ReservedSet->insert("filter"); + ReservedSet->insert("sizeof"); + ReservedSet->insert("cast"); + ReservedSet->insert("namespace"); + ReservedSet->insert("using"); +#endif +} + +void TScanContext::deleteKeywordMap() +{ + delete KeywordMap; + KeywordMap = nullptr; +#ifndef GLSLANG_WEB + delete ReservedSet; + ReservedSet = nullptr; +#endif +} + +// Called by yylex to get the next token. +// Returning 0 implies end of input. +int TScanContext::tokenize(TPpContext* pp, TParserToken& token) +{ + do { + parserToken = &token; + TPpToken ppToken; + int token = pp->tokenize(ppToken); + if (token == EndOfInput) + return 0; + + tokenText = ppToken.name; + loc = ppToken.loc; + parserToken->sType.lex.loc = loc; + switch (token) { + case ';': afterType = false; afterBuffer = false; return SEMICOLON; + case ',': afterType = false; return COMMA; + case ':': return COLON; + case '=': afterType = false; return EQUAL; + case '(': afterType = false; return LEFT_PAREN; + case ')': afterType = false; return RIGHT_PAREN; + case '.': field = true; return DOT; + case '!': return BANG; + case '-': return DASH; + case '~': return TILDE; + case '+': return PLUS; + case '*': return STAR; + case '/': return SLASH; + case '%': return PERCENT; + case '<': return LEFT_ANGLE; + case '>': return RIGHT_ANGLE; + case '|': return VERTICAL_BAR; + case '^': return CARET; + case '&': return AMPERSAND; + case '?': return QUESTION; + case '[': return LEFT_BRACKET; + case ']': return RIGHT_BRACKET; + case '{': afterStruct = false; afterBuffer = false; return LEFT_BRACE; + case '}': return RIGHT_BRACE; + case '\\': + parseContext.error(loc, "illegal use of escape character", "\\", ""); + break; + + case PPAtomAddAssign: return ADD_ASSIGN; + case PPAtomSubAssign: return SUB_ASSIGN; + case PPAtomMulAssign: return MUL_ASSIGN; + case PPAtomDivAssign: return DIV_ASSIGN; + case PPAtomModAssign: return MOD_ASSIGN; + + case PpAtomRight: return RIGHT_OP; + case PpAtomLeft: return LEFT_OP; + + case PpAtomRightAssign: return RIGHT_ASSIGN; + case PpAtomLeftAssign: return LEFT_ASSIGN; + case PpAtomAndAssign: return AND_ASSIGN; + case PpAtomOrAssign: return OR_ASSIGN; + case PpAtomXorAssign: return XOR_ASSIGN; + + case PpAtomAnd: return AND_OP; + case PpAtomOr: return OR_OP; + case PpAtomXor: return XOR_OP; + + case PpAtomEQ: return EQ_OP; + case PpAtomGE: return GE_OP; + case PpAtomNE: return NE_OP; + case PpAtomLE: return LE_OP; + + case PpAtomDecrement: return DEC_OP; + case PpAtomIncrement: return INC_OP; + + case PpAtomColonColon: + parseContext.error(loc, "not supported", "::", ""); + break; + + case PpAtomConstString: parserToken->sType.lex.string = NewPoolTString(tokenText); return STRING_LITERAL; + case PpAtomConstInt: parserToken->sType.lex.i = ppToken.ival; return INTCONSTANT; + case PpAtomConstUint: parserToken->sType.lex.i = ppToken.ival; return UINTCONSTANT; + case PpAtomConstFloat: parserToken->sType.lex.d = ppToken.dval; return FLOATCONSTANT; +#ifndef GLSLANG_WEB + case PpAtomConstInt16: parserToken->sType.lex.i = ppToken.ival; return INT16CONSTANT; + case PpAtomConstUint16: parserToken->sType.lex.i = ppToken.ival; return UINT16CONSTANT; + case PpAtomConstInt64: parserToken->sType.lex.i64 = ppToken.i64val; return INT64CONSTANT; + case PpAtomConstUint64: parserToken->sType.lex.i64 = ppToken.i64val; return UINT64CONSTANT; + case PpAtomConstDouble: parserToken->sType.lex.d = ppToken.dval; return DOUBLECONSTANT; + case PpAtomConstFloat16: parserToken->sType.lex.d = ppToken.dval; return FLOAT16CONSTANT; +#endif + case PpAtomIdentifier: + { + int token = tokenizeIdentifier(); + field = false; + return token; + } + + case EndOfInput: return 0; + + default: + char buf[2]; + buf[0] = (char)token; + buf[1] = 0; + parseContext.error(loc, "unexpected token", buf, ""); + break; + } + } while (true); +} + +int TScanContext::tokenizeIdentifier() +{ +#ifndef GLSLANG_WEB + if (ReservedSet->find(tokenText) != ReservedSet->end()) + return reservedWord(); +#endif + + auto it = KeywordMap->find(tokenText); + if (it == KeywordMap->end()) { + // Should have an identifier of some sort + return identifierOrType(); + } + keyword = it->second; + + switch (keyword) { + case CONST: + case UNIFORM: + case IN: + case OUT: + case INOUT: + case BREAK: + case CONTINUE: + case DO: + case FOR: + case WHILE: + case IF: + case ELSE: + case DISCARD: + case RETURN: + case CASE: + return keyword; + + case BUFFER: + afterBuffer = true; + if ((parseContext.isEsProfile() && parseContext.version < 310) || + (!parseContext.isEsProfile() && (parseContext.version < 430 && + !parseContext.extensionTurnedOn(E_GL_ARB_shader_storage_buffer_object)))) + return identifierOrType(); + return keyword; + + case STRUCT: + afterStruct = true; + return keyword; + + case SWITCH: + case DEFAULT: + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 130)) + reservedWord(); + return keyword; + + case VOID: + case BOOL: + case FLOAT: + case INT: + case BVEC2: + case BVEC3: + case BVEC4: + case VEC2: + case VEC3: + case VEC4: + case IVEC2: + case IVEC3: + case IVEC4: + case MAT2: + case MAT3: + case MAT4: + case SAMPLER2D: + case SAMPLERCUBE: + afterType = true; + return keyword; + + case BOOLCONSTANT: + if (strcmp("true", tokenText) == 0) + parserToken->sType.lex.b = true; + else + parserToken->sType.lex.b = false; + return keyword; + + case SMOOTH: + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 130)) + return identifierOrType(); + return keyword; + case FLAT: + if (parseContext.isEsProfile() && parseContext.version < 300) + reservedWord(); + else if (!parseContext.isEsProfile() && parseContext.version < 130) + return identifierOrType(); + return keyword; + case CENTROID: + if (parseContext.version < 120) + return identifierOrType(); + return keyword; + case INVARIANT: + if (!parseContext.isEsProfile() && parseContext.version < 120) + return identifierOrType(); + return keyword; + case PACKED: + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 330)) + return reservedWord(); + return identifierOrType(); + + case RESOURCE: + { + bool reserved = (parseContext.isEsProfile() && parseContext.version >= 300) || + (!parseContext.isEsProfile() && parseContext.version >= 420); + return identifierOrReserved(reserved); + } + case SUPERP: + { + bool reserved = parseContext.isEsProfile() || parseContext.version >= 130; + return identifierOrReserved(reserved); + } + +#ifndef GLSLANG_WEB + case NOPERSPECTIVE: + if (parseContext.extensionTurnedOn(E_GL_NV_shader_noperspective_interpolation)) + return keyword; + return es30ReservedFromGLSL(130); + + case NONUNIFORM: + if (parseContext.extensionTurnedOn(E_GL_EXT_nonuniform_qualifier)) + return keyword; + else + return identifierOrType(); + case ATTRIBUTE: + case VARYING: + if (parseContext.isEsProfile() && parseContext.version >= 300) + reservedWord(); + return keyword; + case PAYLOADNV: + case PAYLOADINNV: + case HITATTRNV: + case CALLDATANV: + case CALLDATAINNV: + case ACCSTRUCTNV: + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_NV_ray_tracing)) + return keyword; + return identifierOrType(); + case PAYLOADEXT: + case PAYLOADINEXT: + case HITATTREXT: + case CALLDATAEXT: + case CALLDATAINEXT: + case ACCSTRUCTEXT: + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_ray_tracing) || + parseContext.extensionTurnedOn(E_GL_EXT_ray_query)) + return keyword; + return identifierOrType(); + case RAYQUERYEXT: + if (parseContext.symbolTable.atBuiltInLevel() || + (!parseContext.isEsProfile() && parseContext.version >= 460 + && parseContext.extensionTurnedOn(E_GL_EXT_ray_query))) + return keyword; + return identifierOrType(); + case ATOMIC_UINT: + if ((parseContext.isEsProfile() && parseContext.version >= 310) || + parseContext.extensionTurnedOn(E_GL_ARB_shader_atomic_counters)) + return keyword; + return es30ReservedFromGLSL(420); + + case COHERENT: + case DEVICECOHERENT: + case QUEUEFAMILYCOHERENT: + case WORKGROUPCOHERENT: + case SUBGROUPCOHERENT: + case SHADERCALLCOHERENT: + case NONPRIVATE: + case RESTRICT: + case READONLY: + case WRITEONLY: + if (parseContext.isEsProfile() && parseContext.version >= 310) + return keyword; + return es30ReservedFromGLSL(parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store) ? 130 : 420); + case VOLATILE: + if (parseContext.isEsProfile() && parseContext.version >= 310) + return keyword; + if (! parseContext.symbolTable.atBuiltInLevel() && (parseContext.isEsProfile() || + (parseContext.version < 420 && ! parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store)))) + reservedWord(); + return keyword; + case PATCH: + if (parseContext.symbolTable.atBuiltInLevel() || + (parseContext.isEsProfile() && + (parseContext.version >= 320 || + parseContext.extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader))) || + (!parseContext.isEsProfile() && parseContext.extensionTurnedOn(E_GL_ARB_tessellation_shader))) + return keyword; + + return es30ReservedFromGLSL(400); + + case SAMPLE: + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(1, &E_GL_OES_shader_multisample_interpolation)) + return keyword; + return es30ReservedFromGLSL(400); + + case SUBROUTINE: + return es30ReservedFromGLSL(400); +#endif + case SHARED: + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 140)) + return identifierOrType(); + return keyword; + case LAYOUT: + { + const int numLayoutExts = 2; + const char* layoutExts[numLayoutExts] = { E_GL_ARB_shading_language_420pack, + E_GL_ARB_explicit_attrib_location }; + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 140 && + ! parseContext.extensionsTurnedOn(numLayoutExts, layoutExts))) + return identifierOrType(); + return keyword; + } + + case HIGH_PRECISION: + case MEDIUM_PRECISION: + case LOW_PRECISION: + case PRECISION: + return precisionKeyword(); + + case MAT2X2: + case MAT2X3: + case MAT2X4: + case MAT3X2: + case MAT3X3: + case MAT3X4: + case MAT4X2: + case MAT4X3: + case MAT4X4: + return matNxM(); + +#ifndef GLSLANG_WEB + case DMAT2: + case DMAT3: + case DMAT4: + case DMAT2X2: + case DMAT2X3: + case DMAT2X4: + case DMAT3X2: + case DMAT3X3: + case DMAT3X4: + case DMAT4X2: + case DMAT4X3: + case DMAT4X4: + return dMat(); + + case IMAGE1D: + case IIMAGE1D: + case UIMAGE1D: + case IMAGE1DARRAY: + case IIMAGE1DARRAY: + case UIMAGE1DARRAY: + case IMAGE2DRECT: + case IIMAGE2DRECT: + case UIMAGE2DRECT: + afterType = true; + return firstGenerationImage(false); + + case IMAGEBUFFER: + case IIMAGEBUFFER: + case UIMAGEBUFFER: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer)) + return keyword; + return firstGenerationImage(false); + + case IMAGE2D: + case IIMAGE2D: + case UIMAGE2D: + case IMAGE3D: + case IIMAGE3D: + case UIMAGE3D: + case IMAGECUBE: + case IIMAGECUBE: + case UIMAGECUBE: + case IMAGE2DARRAY: + case IIMAGE2DARRAY: + case UIMAGE2DARRAY: + afterType = true; + return firstGenerationImage(true); + + case IMAGECUBEARRAY: + case IIMAGECUBEARRAY: + case UIMAGECUBEARRAY: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_cube_map_array, AEP_texture_cube_map_array)) + return keyword; + return secondGenerationImage(); + + case IMAGE2DMS: + case IIMAGE2DMS: + case UIMAGE2DMS: + case IMAGE2DMSARRAY: + case IIMAGE2DMSARRAY: + case UIMAGE2DMSARRAY: + afterType = true; + return secondGenerationImage(); + + case DOUBLE: + case DVEC2: + case DVEC3: + case DVEC4: + afterType = true; + if (parseContext.isEsProfile() || parseContext.version < 150 || + (!parseContext.symbolTable.atBuiltInLevel() && + (parseContext.version < 400 && !parseContext.extensionTurnedOn(E_GL_ARB_gpu_shader_fp64) && + (parseContext.version < 410 && !parseContext.extensionTurnedOn(E_GL_ARB_vertex_attrib_64bit))))) + reservedWord(); + return keyword; + + case INT64_T: + case UINT64_T: + case I64VEC2: + case I64VEC3: + case I64VEC4: + case U64VEC2: + case U64VEC3: + case U64VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_ARB_gpu_shader_int64) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int64)) + return keyword; + return identifierOrType(); + + case INT8_T: + case UINT8_T: + case I8VEC2: + case I8VEC3: + case I8VEC4: + case U8VEC2: + case U8VEC3: + case U8VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_8bit_storage) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int8)) + return keyword; + return identifierOrType(); + + case INT16_T: + case UINT16_T: + case I16VEC2: + case I16VEC3: + case I16VEC4: + case U16VEC2: + case U16VEC3: + case U16VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_int16) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_16bit_storage) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int16)) + return keyword; + return identifierOrType(); + case INT32_T: + case UINT32_T: + case I32VEC2: + case I32VEC3: + case I32VEC4: + case U32VEC2: + case U32VEC3: + case U32VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int32)) + return keyword; + return identifierOrType(); + case FLOAT32_T: + case F32VEC2: + case F32VEC3: + case F32VEC4: + case F32MAT2: + case F32MAT3: + case F32MAT4: + case F32MAT2X2: + case F32MAT2X3: + case F32MAT2X4: + case F32MAT3X2: + case F32MAT3X3: + case F32MAT3X4: + case F32MAT4X2: + case F32MAT4X3: + case F32MAT4X4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float32)) + return keyword; + return identifierOrType(); + + case FLOAT64_T: + case F64VEC2: + case F64VEC3: + case F64VEC4: + case F64MAT2: + case F64MAT3: + case F64MAT4: + case F64MAT2X2: + case F64MAT2X3: + case F64MAT2X4: + case F64MAT3X2: + case F64MAT3X3: + case F64MAT3X4: + case F64MAT4X2: + case F64MAT4X3: + case F64MAT4X4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float64)) + return keyword; + return identifierOrType(); + + case FLOAT16_T: + case F16VEC2: + case F16VEC3: + case F16VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_half_float) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_16bit_storage) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float16)) + return keyword; + + return identifierOrType(); + + case F16MAT2: + case F16MAT3: + case F16MAT4: + case F16MAT2X2: + case F16MAT2X3: + case F16MAT2X4: + case F16MAT3X2: + case F16MAT3X3: + case F16MAT3X4: + case F16MAT4X2: + case F16MAT4X3: + case F16MAT4X4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_half_float) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float16)) + return keyword; + + return identifierOrType(); + + case SAMPLERCUBEARRAY: + case SAMPLERCUBEARRAYSHADOW: + case ISAMPLERCUBEARRAY: + case USAMPLERCUBEARRAY: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_cube_map_array, AEP_texture_cube_map_array)) + return keyword; + if (parseContext.isEsProfile() || (parseContext.version < 400 && ! parseContext.extensionTurnedOn(E_GL_ARB_texture_cube_map_array))) + reservedWord(); + return keyword; + + case TEXTURECUBEARRAY: + case ITEXTURECUBEARRAY: + case UTEXTURECUBEARRAY: + if (parseContext.spvVersion.vulkan > 0) + return keyword; + else + return identifierOrType(); +#endif + + case UINT: + case UVEC2: + case UVEC3: + case UVEC4: + case SAMPLERCUBESHADOW: + case SAMPLER2DARRAY: + case SAMPLER2DARRAYSHADOW: + case ISAMPLER2D: + case ISAMPLER3D: + case ISAMPLERCUBE: + case ISAMPLER2DARRAY: + case USAMPLER2D: + case USAMPLER3D: + case USAMPLERCUBE: + case USAMPLER2DARRAY: + afterType = true; + return nonreservedKeyword(300, 130); + + case SAMPLER3D: + afterType = true; + if (parseContext.isEsProfile() && parseContext.version < 300) { + if (!parseContext.extensionTurnedOn(E_GL_OES_texture_3D)) + reservedWord(); + } + return keyword; + + case SAMPLER2DSHADOW: + afterType = true; + if (parseContext.isEsProfile() && parseContext.version < 300) { + if (!parseContext.extensionTurnedOn(E_GL_EXT_shadow_samplers)) + reservedWord(); + } + return keyword; + + case TEXTURE2D: + case TEXTURECUBE: + case TEXTURE2DARRAY: + case ITEXTURE2D: + case ITEXTURE3D: + case ITEXTURECUBE: + case ITEXTURE2DARRAY: + case UTEXTURE2D: + case UTEXTURE3D: + case UTEXTURECUBE: + case UTEXTURE2DARRAY: + case TEXTURE3D: + case SAMPLER: + case SAMPLERSHADOW: + if (parseContext.spvVersion.vulkan > 0) + return keyword; + else + return identifierOrType(); + +#ifndef GLSLANG_WEB + case ISAMPLER1D: + case ISAMPLER1DARRAY: + case SAMPLER1DARRAYSHADOW: + case USAMPLER1D: + case USAMPLER1DARRAY: + afterType = true; + return es30ReservedFromGLSL(130); + case ISAMPLER2DRECT: + case USAMPLER2DRECT: + afterType = true; + return es30ReservedFromGLSL(140); + + case SAMPLERBUFFER: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer)) + return keyword; + return es30ReservedFromGLSL(130); + + case ISAMPLERBUFFER: + case USAMPLERBUFFER: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer)) + return keyword; + return es30ReservedFromGLSL(140); + + case SAMPLER2DMS: + case ISAMPLER2DMS: + case USAMPLER2DMS: + afterType = true; + if (parseContext.isEsProfile() && parseContext.version >= 310) + return keyword; + if (!parseContext.isEsProfile() && (parseContext.version > 140 || + (parseContext.version == 140 && parseContext.extensionsTurnedOn(1, &E_GL_ARB_texture_multisample)))) + return keyword; + return es30ReservedFromGLSL(150); + + case SAMPLER2DMSARRAY: + case ISAMPLER2DMSARRAY: + case USAMPLER2DMSARRAY: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(1, &E_GL_OES_texture_storage_multisample_2d_array)) + return keyword; + if (!parseContext.isEsProfile() && (parseContext.version > 140 || + (parseContext.version == 140 && parseContext.extensionsTurnedOn(1, &E_GL_ARB_texture_multisample)))) + return keyword; + return es30ReservedFromGLSL(150); + + case SAMPLER1D: + case SAMPLER1DSHADOW: + afterType = true; + if (parseContext.isEsProfile()) + reservedWord(); + return keyword; + + case SAMPLER2DRECT: + case SAMPLER2DRECTSHADOW: + afterType = true; + if (parseContext.isEsProfile()) + reservedWord(); + else if (parseContext.version < 140 && ! parseContext.symbolTable.atBuiltInLevel() && ! parseContext.extensionTurnedOn(E_GL_ARB_texture_rectangle)) { + if (parseContext.relaxedErrors()) + parseContext.requireExtensions(loc, 1, &E_GL_ARB_texture_rectangle, "texture-rectangle sampler keyword"); + else + reservedWord(); + } + return keyword; + + case SAMPLER1DARRAY: + afterType = true; + if (parseContext.isEsProfile() && parseContext.version == 300) + reservedWord(); + else if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 130)) + return identifierOrType(); + return keyword; + + case SAMPLEREXTERNALOES: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_OES_EGL_image_external) || + parseContext.extensionTurnedOn(E_GL_OES_EGL_image_external_essl3)) + return keyword; + return identifierOrType(); + + case SAMPLEREXTERNAL2DY2YEXT: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_YUV_target)) + return keyword; + return identifierOrType(); + + case SAMPLERVIDEO: + afterType = true; + return keyword; + + case ITEXTURE1DARRAY: + case UTEXTURE1D: + case ITEXTURE1D: + case UTEXTURE1DARRAY: + case TEXTUREBUFFER: + case ITEXTURE2DRECT: + case UTEXTURE2DRECT: + case ITEXTUREBUFFER: + case UTEXTUREBUFFER: + case TEXTURE2DMS: + case ITEXTURE2DMS: + case UTEXTURE2DMS: + case TEXTURE2DMSARRAY: + case ITEXTURE2DMSARRAY: + case UTEXTURE2DMSARRAY: + case TEXTURE1D: + case TEXTURE2DRECT: + case TEXTURE1DARRAY: + if (parseContext.spvVersion.vulkan > 0) + return keyword; + else + return identifierOrType(); + + case SUBPASSINPUT: + case SUBPASSINPUTMS: + case ISUBPASSINPUT: + case ISUBPASSINPUTMS: + case USUBPASSINPUT: + case USUBPASSINPUTMS: + if (parseContext.spvVersion.vulkan > 0) + return keyword; + else + return identifierOrType(); + + case F16SAMPLER1D: + case F16SAMPLER2D: + case F16SAMPLER3D: + case F16SAMPLER2DRECT: + case F16SAMPLERCUBE: + case F16SAMPLER1DARRAY: + case F16SAMPLER2DARRAY: + case F16SAMPLERCUBEARRAY: + case F16SAMPLERBUFFER: + case F16SAMPLER2DMS: + case F16SAMPLER2DMSARRAY: + case F16SAMPLER1DSHADOW: + case F16SAMPLER2DSHADOW: + case F16SAMPLER1DARRAYSHADOW: + case F16SAMPLER2DARRAYSHADOW: + case F16SAMPLER2DRECTSHADOW: + case F16SAMPLERCUBESHADOW: + case F16SAMPLERCUBEARRAYSHADOW: + + case F16IMAGE1D: + case F16IMAGE2D: + case F16IMAGE3D: + case F16IMAGE2DRECT: + case F16IMAGECUBE: + case F16IMAGE1DARRAY: + case F16IMAGE2DARRAY: + case F16IMAGECUBEARRAY: + case F16IMAGEBUFFER: + case F16IMAGE2DMS: + case F16IMAGE2DMSARRAY: + + case F16TEXTURE1D: + case F16TEXTURE2D: + case F16TEXTURE3D: + case F16TEXTURE2DRECT: + case F16TEXTURECUBE: + case F16TEXTURE1DARRAY: + case F16TEXTURE2DARRAY: + case F16TEXTURECUBEARRAY: + case F16TEXTUREBUFFER: + case F16TEXTURE2DMS: + case F16TEXTURE2DMSARRAY: + + case F16SUBPASSINPUT: + case F16SUBPASSINPUTMS: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_half_float_fetch)) + return keyword; + return identifierOrType(); + + case EXPLICITINTERPAMD: + if (parseContext.extensionTurnedOn(E_GL_AMD_shader_explicit_vertex_parameter)) + return keyword; + return identifierOrType(); + + case PERVERTEXNV: + if ((!parseContext.isEsProfile() && parseContext.version >= 450) || + parseContext.extensionTurnedOn(E_GL_NV_fragment_shader_barycentric)) + return keyword; + return identifierOrType(); + + case PRECISE: + if ((parseContext.isEsProfile() && + (parseContext.version >= 320 || parseContext.extensionsTurnedOn(Num_AEP_gpu_shader5, AEP_gpu_shader5))) || + (!parseContext.isEsProfile() && parseContext.version >= 400)) + return keyword; + if (parseContext.isEsProfile() && parseContext.version == 310) { + reservedWord(); + return keyword; + } + return identifierOrType(); + + case PERPRIMITIVENV: + case PERVIEWNV: + case PERTASKNV: + if ((!parseContext.isEsProfile() && parseContext.version >= 450) || + (parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionTurnedOn(E_GL_NV_mesh_shader)) + return keyword; + return identifierOrType(); + + case FCOOPMATNV: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_NV_cooperative_matrix)) + return keyword; + return identifierOrType(); + + case UCOOPMATNV: + case ICOOPMATNV: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_NV_integer_cooperative_matrix)) + return keyword; + return identifierOrType(); + + case DEMOTE: + if (parseContext.extensionTurnedOn(E_GL_EXT_demote_to_helper_invocation)) + return keyword; + else + return identifierOrType(); +#endif + + default: + parseContext.infoSink.info.message(EPrefixInternalError, "Unknown glslang keyword", loc); + return 0; + } +} + +int TScanContext::identifierOrType() +{ + parserToken->sType.lex.string = NewPoolTString(tokenText); + if (field) + return IDENTIFIER; + + parserToken->sType.lex.symbol = parseContext.symbolTable.find(*parserToken->sType.lex.string); + if ((afterType == false && afterStruct == false) && parserToken->sType.lex.symbol != nullptr) { + if (const TVariable* variable = parserToken->sType.lex.symbol->getAsVariable()) { + if (variable->isUserType() && + // treat redeclaration of forward-declared buffer/uniform reference as an identifier + !(variable->getType().isReference() && afterBuffer)) { + afterType = true; + + return TYPE_NAME; + } + } + } + + return IDENTIFIER; +} + +// Give an error for use of a reserved symbol. +// However, allow built-in declarations to use reserved words, to allow +// extension support before the extension is enabled. +int TScanContext::reservedWord() +{ + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.error(loc, "Reserved word.", tokenText, "", ""); + + return 0; +} + +int TScanContext::identifierOrReserved(bool reserved) +{ + if (reserved) { + reservedWord(); + + return 0; + } + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future reserved keyword", tokenText, ""); + + return identifierOrType(); +} + +// For keywords that suddenly showed up on non-ES (not previously reserved) +// but then got reserved by ES 3.0. +int TScanContext::es30ReservedFromGLSL(int version) +{ + if (parseContext.symbolTable.atBuiltInLevel()) + return keyword; + + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < version)) { + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "future reserved word in ES 300 and keyword in GLSL", tokenText, ""); + + return identifierOrType(); + } else if (parseContext.isEsProfile() && parseContext.version >= 300) + reservedWord(); + + return keyword; +} + +// For a keyword that was never reserved, until it suddenly +// showed up, both in an es version and a non-ES version. +int TScanContext::nonreservedKeyword(int esVersion, int nonEsVersion) +{ + if ((parseContext.isEsProfile() && parseContext.version < esVersion) || + (!parseContext.isEsProfile() && parseContext.version < nonEsVersion)) { + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future keyword", tokenText, ""); + + return identifierOrType(); + } + + return keyword; +} + +int TScanContext::precisionKeyword() +{ + if (parseContext.isEsProfile() || parseContext.version >= 130) + return keyword; + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using ES precision qualifier keyword", tokenText, ""); + + return identifierOrType(); +} + +int TScanContext::matNxM() +{ + afterType = true; + + if (parseContext.version > 110) + return keyword; + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future non-square matrix type keyword", tokenText, ""); + + return identifierOrType(); +} + +int TScanContext::dMat() +{ + afterType = true; + + if (parseContext.isEsProfile() && parseContext.version >= 300) { + reservedWord(); + + return keyword; + } + + if (!parseContext.isEsProfile() && (parseContext.version >= 400 || + parseContext.symbolTable.atBuiltInLevel() || + (parseContext.version >= 150 && parseContext.extensionTurnedOn(E_GL_ARB_gpu_shader_fp64)) || + (parseContext.version >= 150 && parseContext.extensionTurnedOn(E_GL_ARB_vertex_attrib_64bit) + && parseContext.language == EShLangVertex))) + return keyword; + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future type keyword", tokenText, ""); + + return identifierOrType(); +} + +int TScanContext::firstGenerationImage(bool inEs310) +{ + if (parseContext.symbolTable.atBuiltInLevel() || + (!parseContext.isEsProfile() && (parseContext.version >= 420 || + parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store))) || + (inEs310 && parseContext.isEsProfile() && parseContext.version >= 310)) + return keyword; + + if ((parseContext.isEsProfile() && parseContext.version >= 300) || + (!parseContext.isEsProfile() && parseContext.version >= 130)) { + reservedWord(); + + return keyword; + } + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future type keyword", tokenText, ""); + + return identifierOrType(); +} + +int TScanContext::secondGenerationImage() +{ + if (parseContext.isEsProfile() && parseContext.version >= 310) { + reservedWord(); + return keyword; + } + + if (parseContext.symbolTable.atBuiltInLevel() || + (!parseContext.isEsProfile() && + (parseContext.version >= 420 || parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store)))) + return keyword; + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future type keyword", tokenText, ""); + + return identifierOrType(); +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Scan.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Scan.h new file mode 100644 index 000000000..24b75cf7c --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Scan.h @@ -0,0 +1,276 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef _GLSLANG_SCAN_INCLUDED_ +#define _GLSLANG_SCAN_INCLUDED_ + +#include "Versions.h" + +namespace glslang { + +// Use a global end-of-input character, so no translation is needed across +// layers of encapsulation. Characters are all 8 bit, and positive, so there is +// no aliasing of character 255 onto -1, for example. +const int EndOfInput = -1; + +// +// A character scanner that seamlessly, on read-only strings, reads across an +// array of strings without assuming null termination. +// +class TInputScanner { +public: + TInputScanner(int n, const char* const s[], size_t L[], const char* const* names = nullptr, + int b = 0, int f = 0, bool single = false) : + numSources(n), + // up to this point, common usage is "char*", but now we need positive 8-bit characters + sources(reinterpret_cast(s)), + lengths(L), currentSource(0), currentChar(0), stringBias(b), finale(f), singleLogical(single), + endOfFileReached(false) + { + loc = new TSourceLoc[numSources]; + for (int i = 0; i < numSources; ++i) { + loc[i].init(i - stringBias); + } + if (names != nullptr) { + for (int i = 0; i < numSources; ++i) + loc[i].name = names[i] != nullptr ? NewPoolTString(names[i]) : nullptr; + } + loc[currentSource].line = 1; + logicalSourceLoc.init(1); + logicalSourceLoc.name = loc[0].name; + } + + virtual ~TInputScanner() + { + delete [] loc; + } + + // retrieve the next character and advance one character + int get() + { + int ret = peek(); + if (ret == EndOfInput) + return ret; + ++loc[currentSource].column; + ++logicalSourceLoc.column; + if (ret == '\n') { + ++loc[currentSource].line; + ++logicalSourceLoc.line; + logicalSourceLoc.column = 0; + loc[currentSource].column = 0; + } + advance(); + + return ret; + } + + // retrieve the next character, no advance + int peek() + { + if (currentSource >= numSources) { + endOfFileReached = true; + return EndOfInput; + } + // Make sure we do not read off the end of a string. + // N.B. Sources can have a length of 0. + int sourceToRead = currentSource; + size_t charToRead = currentChar; + while(charToRead >= lengths[sourceToRead]) { + charToRead = 0; + sourceToRead += 1; + if (sourceToRead >= numSources) { + return EndOfInput; + } + } + + // Here, we care about making negative valued characters positive + return sources[sourceToRead][charToRead]; + } + + // go back one character + void unget() + { + // Do not roll back once we've reached the end of the file. + if (endOfFileReached) + return; + + if (currentChar > 0) { + --currentChar; + --loc[currentSource].column; + --logicalSourceLoc.column; + if (loc[currentSource].column < 0) { + // We've moved back past a new line. Find the + // previous newline (or start of the file) to compute + // the column count on the now current line. + size_t chIndex = currentChar; + while (chIndex > 0) { + if (sources[currentSource][chIndex] == '\n') { + break; + } + --chIndex; + } + logicalSourceLoc.column = (int)(currentChar - chIndex); + loc[currentSource].column = (int)(currentChar - chIndex); + } + } else { + do { + --currentSource; + } while (currentSource > 0 && lengths[currentSource] == 0); + if (lengths[currentSource] == 0) { + // set to 0 if we've backed up to the start of an empty string + currentChar = 0; + } else + currentChar = lengths[currentSource] - 1; + } + if (peek() == '\n') { + --loc[currentSource].line; + --logicalSourceLoc.line; + } + } + + // for #line override + void setLine(int newLine) + { + logicalSourceLoc.line = newLine; + loc[getLastValidSourceIndex()].line = newLine; + } + + // for #line override in filename based parsing + void setFile(const char* filename) + { + TString* fn_tstr = NewPoolTString(filename); + logicalSourceLoc.name = fn_tstr; + loc[getLastValidSourceIndex()].name = fn_tstr; + } + + void setFile(const char* filename, int i) + { + TString* fn_tstr = NewPoolTString(filename); + if (i == getLastValidSourceIndex()) { + logicalSourceLoc.name = fn_tstr; + } + loc[i].name = fn_tstr; + } + + void setString(int newString) + { + logicalSourceLoc.string = newString; + loc[getLastValidSourceIndex()].string = newString; + logicalSourceLoc.name = nullptr; + loc[getLastValidSourceIndex()].name = nullptr; + } + + // for #include content indentation + void setColumn(int col) + { + logicalSourceLoc.column = col; + loc[getLastValidSourceIndex()].column = col; + } + + void setEndOfInput() + { + endOfFileReached = true; + currentSource = numSources; + } + + bool atEndOfInput() const { return endOfFileReached; } + + const TSourceLoc& getSourceLoc() const + { + if (singleLogical) { + return logicalSourceLoc; + } else { + return loc[std::max(0, std::min(currentSource, numSources - finale - 1))]; + } + } + // Returns the index (starting from 0) of the most recent valid source string we are reading from. + int getLastValidSourceIndex() const { return std::min(currentSource, numSources - 1); } + + void consumeWhiteSpace(bool& foundNonSpaceTab); + bool consumeComment(); + void consumeWhitespaceComment(bool& foundNonSpaceTab); + bool scanVersion(int& version, EProfile& profile, bool& notFirstToken); + +protected: + + // advance one character + void advance() + { + ++currentChar; + if (currentChar >= lengths[currentSource]) { + ++currentSource; + if (currentSource < numSources) { + loc[currentSource].string = loc[currentSource - 1].string + 1; + loc[currentSource].line = 1; + loc[currentSource].column = 0; + } + while (currentSource < numSources && lengths[currentSource] == 0) { + ++currentSource; + if (currentSource < numSources) { + loc[currentSource].string = loc[currentSource - 1].string + 1; + loc[currentSource].line = 1; + loc[currentSource].column = 0; + } + } + currentChar = 0; + } + } + + int numSources; // number of strings in source + const unsigned char* const *sources; // array of strings; must be converted to positive values on use, to avoid aliasing with -1 as EndOfInput + const size_t *lengths; // length of each string + int currentSource; + size_t currentChar; + + // This is for reporting what string/line an error occurred on, and can be overridden by #line. + // It remembers the last state of each source string as it is left for the next one, so unget() + // can restore that state. + TSourceLoc* loc; // an array + + int stringBias; // the first string that is the user's string number 0 + int finale; // number of internal strings after user's last string + + TSourceLoc logicalSourceLoc; + bool singleLogical; // treats the strings as a single logical string. + // locations will be reported from the first string. + + // Set to true once peek() returns EndOfFile, so that we won't roll back + // once we've reached EndOfFile. + bool endOfFileReached; +}; + +} // end namespace glslang + +#endif // _GLSLANG_SCAN_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ScanContext.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ScanContext.h new file mode 100644 index 000000000..74b2b3c74 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ScanContext.h @@ -0,0 +1,93 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// This holds context specific to the GLSL scanner, which +// sits between the preprocessor scanner and parser. +// + +#pragma once + +#include "ParseHelper.h" + +namespace glslang { + +class TPpContext; +class TPpToken; +class TParserToken; + +class TScanContext { +public: + explicit TScanContext(TParseContextBase& pc) : + parseContext(pc), + afterType(false), afterStruct(false), + field(false), afterBuffer(false) { } + virtual ~TScanContext() { } + + static void fillInKeywordMap(); + static void deleteKeywordMap(); + + int tokenize(TPpContext*, TParserToken&); + +protected: + TScanContext(TScanContext&); + TScanContext& operator=(TScanContext&); + + int tokenizeIdentifier(); + int identifierOrType(); + int reservedWord(); + int identifierOrReserved(bool reserved); + int es30ReservedFromGLSL(int version); + int nonreservedKeyword(int esVersion, int nonEsVersion); + int precisionKeyword(); + int matNxM(); + int dMat(); + int firstGenerationImage(bool inEs310); + int secondGenerationImage(); + + TParseContextBase& parseContext; + bool afterType; // true if we've recognized a type, so can only be looking for an identifier + bool afterStruct; // true if we've recognized the STRUCT keyword, so can only be looking for an identifier + bool field; // true if we're on a field, right after a '.' + bool afterBuffer; // true if we've recognized the BUFFER keyword + TSourceLoc loc; + TParserToken* parserToken; + TPpToken* ppToken; + + const char* tokenText; + int keyword; +}; + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ShaderLang.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ShaderLang.cpp new file mode 100644 index 000000000..a1392dbde --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/ShaderLang.cpp @@ -0,0 +1,2118 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013-2016 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Implement the top-level of interface to the compiler/linker, +// as defined in ShaderLang.h +// This is the platform independent interface between an OGL driver +// and the shading language compiler/linker. +// +#include +#include +#include +#include +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "Scan.h" +#include "ScanContext.h" + +#ifdef ENABLE_HLSL +#include "../../hlsl/hlslParseHelper.h" +#include "../../hlsl/hlslParseables.h" +#include "../../hlsl/hlslScanContext.h" +#endif + +#include "../Include/ShHandle.h" +#include "../../OGLCompilersDLL/InitializeDll.h" + +#include "preprocessor/PpContext.h" + +#define SH_EXPORTING +#include "../Public/ShaderLang.h" +#include "reflection.h" +#include "iomapper.h" +#include "Initialize.h" + +// TODO: this really shouldn't be here, it is only because of the trial addition +// of printing pre-processed tokens, which requires knowing the string literal +// token to print ", but none of that seems appropriate for this file. +#include "preprocessor/PpTokens.h" + +namespace { // anonymous namespace for file-local functions and symbols + +// Total number of successful initializers of glslang: a refcount +// Shared global; access should be protected by a global mutex/critical section. +int NumberOfClients = 0; + +using namespace glslang; + +// Create a language specific version of parseables. +TBuiltInParseables* CreateBuiltInParseables(TInfoSink& infoSink, EShSource source) +{ + switch (source) { + case EShSourceGlsl: return new TBuiltIns(); // GLSL builtIns +#ifdef ENABLE_HLSL + case EShSourceHlsl: return new TBuiltInParseablesHlsl(); // HLSL intrinsics +#endif + + default: + infoSink.info.message(EPrefixInternalError, "Unable to determine source language"); + return nullptr; + } +} + +// Create a language specific version of a parse context. +TParseContextBase* CreateParseContext(TSymbolTable& symbolTable, TIntermediate& intermediate, + int version, EProfile profile, EShSource source, + EShLanguage language, TInfoSink& infoSink, + SpvVersion spvVersion, bool forwardCompatible, EShMessages messages, + bool parsingBuiltIns, std::string sourceEntryPointName = "") +{ + switch (source) { + case EShSourceGlsl: { + if (sourceEntryPointName.size() == 0) + intermediate.setEntryPointName("main"); + TString entryPoint = sourceEntryPointName.c_str(); + return new TParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion, + language, infoSink, forwardCompatible, messages, &entryPoint); + } +#ifdef ENABLE_HLSL + case EShSourceHlsl: + return new HlslParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion, + language, infoSink, sourceEntryPointName.c_str(), forwardCompatible, messages); +#endif + default: + infoSink.info.message(EPrefixInternalError, "Unable to determine source language"); + return nullptr; + } +} + +// Local mapping functions for making arrays of symbol tables.... + +const int VersionCount = 17; // index range in MapVersionToIndex + +int MapVersionToIndex(int version) +{ + int index = 0; + + switch (version) { + case 100: index = 0; break; + case 110: index = 1; break; + case 120: index = 2; break; + case 130: index = 3; break; + case 140: index = 4; break; + case 150: index = 5; break; + case 300: index = 6; break; + case 330: index = 7; break; + case 400: index = 8; break; + case 410: index = 9; break; + case 420: index = 10; break; + case 430: index = 11; break; + case 440: index = 12; break; + case 310: index = 13; break; + case 450: index = 14; break; + case 500: index = 0; break; // HLSL + case 320: index = 15; break; + case 460: index = 16; break; + default: assert(0); break; + } + + assert(index < VersionCount); + + return index; +} + +const int SpvVersionCount = 3; // index range in MapSpvVersionToIndex + +int MapSpvVersionToIndex(const SpvVersion& spvVersion) +{ + int index = 0; + + if (spvVersion.openGl > 0) + index = 1; + else if (spvVersion.vulkan > 0) + index = 2; + + assert(index < SpvVersionCount); + + return index; +} + +const int ProfileCount = 4; // index range in MapProfileToIndex + +int MapProfileToIndex(EProfile profile) +{ + int index = 0; + + switch (profile) { + case ENoProfile: index = 0; break; + case ECoreProfile: index = 1; break; + case ECompatibilityProfile: index = 2; break; + case EEsProfile: index = 3; break; + default: break; + } + + assert(index < ProfileCount); + + return index; +} + +const int SourceCount = 2; + +int MapSourceToIndex(EShSource source) +{ + int index = 0; + + switch (source) { + case EShSourceGlsl: index = 0; break; + case EShSourceHlsl: index = 1; break; + default: break; + } + + assert(index < SourceCount); + + return index; +} + +// only one of these needed for non-ES; ES needs 2 for different precision defaults of built-ins +enum EPrecisionClass { + EPcGeneral, + EPcFragment, + EPcCount +}; + +// A process-global symbol table per version per profile for built-ins common +// to multiple stages (languages), and a process-global symbol table per version +// per profile per stage for built-ins unique to each stage. They will be sparsely +// populated, so they will only be generated as needed. +// +// Each has a different set of built-ins, and we want to preserve that from +// compile to compile. +// +TSymbolTable* CommonSymbolTable[VersionCount][SpvVersionCount][ProfileCount][SourceCount][EPcCount] = {}; +TSymbolTable* SharedSymbolTables[VersionCount][SpvVersionCount][ProfileCount][SourceCount][EShLangCount] = {}; + +TPoolAllocator* PerProcessGPA = nullptr; + +// +// Parse and add to the given symbol table the content of the given shader string. +// +bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, + EShSource source, TInfoSink& infoSink, TSymbolTable& symbolTable) +{ + TIntermediate intermediate(language, version, profile); + + intermediate.setSource(source); + + std::unique_ptr parseContext(CreateParseContext(symbolTable, intermediate, version, profile, source, + language, infoSink, spvVersion, true, EShMsgDefault, + true)); + + TShader::ForbidIncluder includer; + TPpContext ppContext(*parseContext, "", includer); + TScanContext scanContext(*parseContext); + parseContext->setScanContext(&scanContext); + parseContext->setPpContext(&ppContext); + + // + // Push the symbol table to give it an initial scope. This + // push should not have a corresponding pop, so that built-ins + // are preserved, and the test for an empty table fails. + // + + symbolTable.push(); + + const char* builtInShaders[2]; + size_t builtInLengths[2]; + builtInShaders[0] = builtIns.c_str(); + builtInLengths[0] = builtIns.size(); + + if (builtInLengths[0] == 0) + return true; + + TInputScanner input(1, builtInShaders, builtInLengths); + if (! parseContext->parseShaderStrings(ppContext, input) != 0) { + infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins"); + printf("Unable to parse built-ins\n%s\n", infoSink.info.c_str()); + printf("%s\n", builtInShaders[0]); + + return false; + } + + return true; +} + +int CommonIndex(EProfile profile, EShLanguage language) +{ + return (profile == EEsProfile && language == EShLangFragment) ? EPcFragment : EPcGeneral; +} + +// +// To initialize per-stage shared tables, with the common table already complete. +// +void InitializeStageSymbolTable(TBuiltInParseables& builtInParseables, int version, EProfile profile, const SpvVersion& spvVersion, + EShLanguage language, EShSource source, TInfoSink& infoSink, TSymbolTable** commonTable, + TSymbolTable** symbolTables) +{ +#ifdef GLSLANG_WEB + profile = EEsProfile; + version = 310; +#endif + + (*symbolTables[language]).adoptLevels(*commonTable[CommonIndex(profile, language)]); + InitializeSymbolTable(builtInParseables.getStageString(language), version, profile, spvVersion, language, source, + infoSink, *symbolTables[language]); + builtInParseables.identifyBuiltIns(version, profile, spvVersion, language, *symbolTables[language]); + if (profile == EEsProfile && version >= 300) + (*symbolTables[language]).setNoBuiltInRedeclarations(); + if (version == 110) + (*symbolTables[language]).setSeparateNameSpaces(); +} + +// +// Initialize the full set of shareable symbol tables; +// The common (cross-stage) and those shareable per-stage. +// +bool InitializeSymbolTables(TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables, int version, EProfile profile, const SpvVersion& spvVersion, EShSource source) +{ +#ifdef GLSLANG_WEB + profile = EEsProfile; + version = 310; +#endif + + std::unique_ptr builtInParseables(CreateBuiltInParseables(infoSink, source)); + + if (builtInParseables == nullptr) + return false; + + builtInParseables->initialize(version, profile, spvVersion); + + // do the common tables + InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangVertex, source, + infoSink, *commonTable[EPcGeneral]); + if (profile == EEsProfile) + InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangFragment, source, + infoSink, *commonTable[EPcFragment]); + + // do the per-stage tables + + // always have vertex and fragment + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangVertex, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangFragment, source, + infoSink, commonTable, symbolTables); + +#ifndef GLSLANG_WEB + // check for tessellation + if ((profile != EEsProfile && version >= 150) || + (profile == EEsProfile && version >= 310)) { + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessControl, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessEvaluation, source, + infoSink, commonTable, symbolTables); + } + + // check for geometry + if ((profile != EEsProfile && version >= 150) || + (profile == EEsProfile && version >= 310)) + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangGeometry, source, + infoSink, commonTable, symbolTables); +#endif + + // check for compute + if ((profile != EEsProfile && version >= 420) || + (profile == EEsProfile && version >= 310)) + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCompute, source, + infoSink, commonTable, symbolTables); + + // check for ray tracing stages + if (profile != EEsProfile && version >= 450) { + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangRayGen, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangIntersect, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangAnyHit, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangClosestHit, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMiss, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCallable, source, + infoSink, commonTable, symbolTables); + } + + // check for mesh + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMeshNV, source, + infoSink, commonTable, symbolTables); + + // check for task + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTaskNV, source, + infoSink, commonTable, symbolTables); + + return true; +} + +bool AddContextSpecificSymbols(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable& symbolTable, int version, + EProfile profile, const SpvVersion& spvVersion, EShLanguage language, EShSource source) +{ + std::unique_ptr builtInParseables(CreateBuiltInParseables(infoSink, source)); + + if (builtInParseables == nullptr) + return false; + + builtInParseables->initialize(*resources, version, profile, spvVersion, language); + InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, language, source, infoSink, symbolTable); + builtInParseables->identifyBuiltIns(version, profile, spvVersion, language, symbolTable, *resources); + + return true; +} + +// +// To do this on the fly, we want to leave the current state of our thread's +// pool allocator intact, so: +// - Switch to a new pool for parsing the built-ins +// - Do the parsing, which builds the symbol table, using the new pool +// - Switch to the process-global pool to save a copy of the resulting symbol table +// - Free up the new pool used to parse the built-ins +// - Switch back to the original thread's pool +// +// This only gets done the first time any thread needs a particular symbol table +// (lazy evaluation). +// +void SetupBuiltinSymbolTable(int version, EProfile profile, const SpvVersion& spvVersion, EShSource source) +{ + TInfoSink infoSink; + + // Make sure only one thread tries to do this at a time + glslang::GetGlobalLock(); + + // See if it's already been done for this version/profile combination + int versionIndex = MapVersionToIndex(version); + int spvVersionIndex = MapSpvVersionToIndex(spvVersion); + int profileIndex = MapProfileToIndex(profile); + int sourceIndex = MapSourceToIndex(source); + if (CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][EPcGeneral]) { + glslang::ReleaseGlobalLock(); + + return; + } + + // Switch to a new pool + TPoolAllocator& previousAllocator = GetThreadPoolAllocator(); + TPoolAllocator* builtInPoolAllocator = new TPoolAllocator; + SetThreadPoolAllocator(builtInPoolAllocator); + + // Dynamically allocate the local symbol tables so we can control when they are deallocated WRT when the pool is popped. + TSymbolTable* commonTable[EPcCount]; + TSymbolTable* stageTables[EShLangCount]; + for (int precClass = 0; precClass < EPcCount; ++precClass) + commonTable[precClass] = new TSymbolTable; + for (int stage = 0; stage < EShLangCount; ++stage) + stageTables[stage] = new TSymbolTable; + + // Generate the local symbol tables using the new pool + InitializeSymbolTables(infoSink, commonTable, stageTables, version, profile, spvVersion, source); + + // Switch to the process-global pool + SetThreadPoolAllocator(PerProcessGPA); + + // Copy the local symbol tables from the new pool to the global tables using the process-global pool + for (int precClass = 0; precClass < EPcCount; ++precClass) { + if (! commonTable[precClass]->isEmpty()) { + CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass] = new TSymbolTable; + CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->copyTable(*commonTable[precClass]); + CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->readOnly(); + } + } + for (int stage = 0; stage < EShLangCount; ++stage) { + if (! stageTables[stage]->isEmpty()) { + SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage] = new TSymbolTable; + SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->adoptLevels(*CommonSymbolTable + [versionIndex][spvVersionIndex][profileIndex][sourceIndex][CommonIndex(profile, (EShLanguage)stage)]); + SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->copyTable(*stageTables[stage]); + SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->readOnly(); + } + } + + // Clean up the local tables before deleting the pool they used. + for (int precClass = 0; precClass < EPcCount; ++precClass) + delete commonTable[precClass]; + for (int stage = 0; stage < EShLangCount; ++stage) + delete stageTables[stage]; + + delete builtInPoolAllocator; + SetThreadPoolAllocator(&previousAllocator); + + glslang::ReleaseGlobalLock(); +} + +// Function to Print all builtins +void DumpBuiltinSymbolTable(TInfoSink& infoSink, const TSymbolTable& symbolTable) +{ +#ifndef GLSLANG_WEB + infoSink.debug << "BuiltinSymbolTable {\n"; + + symbolTable.dump(infoSink, true); + + infoSink.debug << "}\n"; +#endif +} + +// Return true if the shader was correctly specified for version/profile/stage. +bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNotFirst, int defaultVersion, + EShSource source, int& version, EProfile& profile, const SpvVersion& spvVersion) +{ + const int FirstProfileVersion = 150; + bool correct = true; + + if (source == EShSourceHlsl) { + version = 500; // shader model; currently a characteristic of glslang, not the input + profile = ECoreProfile; // allow doubles in prototype parsing + return correct; + } + + // Get a version... + if (version == 0) { + version = defaultVersion; + // infoSink.info.message(EPrefixWarning, "#version: statement missing; use #version on first line of shader"); + } + + // Get a good profile... + if (profile == ENoProfile) { + if (version == 300 || version == 310 || version == 320) { + correct = false; + infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 require specifying the 'es' profile"); + profile = EEsProfile; + } else if (version == 100) + profile = EEsProfile; + else if (version >= FirstProfileVersion) + profile = ECoreProfile; + else + profile = ENoProfile; + } else { + // a profile was provided... + if (version < 150) { + correct = false; + infoSink.info.message(EPrefixError, "#version: versions before 150 do not allow a profile token"); + if (version == 100) + profile = EEsProfile; + else + profile = ENoProfile; + } else if (version == 300 || version == 310 || version == 320) { + if (profile != EEsProfile) { + correct = false; + infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 support only the es profile"); + } + profile = EEsProfile; + } else { + if (profile == EEsProfile) { + correct = false; + infoSink.info.message(EPrefixError, "#version: only version 300, 310, and 320 support the es profile"); + if (version >= FirstProfileVersion) + profile = ECoreProfile; + else + profile = ENoProfile; + } + // else: typical desktop case... e.g., "#version 410 core" + } + } + + // Fix version... + switch (version) { + // ES versions + case 100: break; + case 300: break; + case 310: break; + case 320: break; + + // desktop versions + case 110: break; + case 120: break; + case 130: break; + case 140: break; + case 150: break; + case 330: break; + case 400: break; + case 410: break; + case 420: break; + case 430: break; + case 440: break; + case 450: break; + case 460: break; + + // unknown version + default: + correct = false; + infoSink.info.message(EPrefixError, "version not supported"); + if (profile == EEsProfile) + version = 310; + else { + version = 450; + profile = ECoreProfile; + } + break; + } + +#ifndef GLSLANG_WEB + // Correct for stage type... + switch (stage) { + case EShLangGeometry: + if ((profile == EEsProfile && version < 310) || + (profile != EEsProfile && version < 150)) { + correct = false; + infoSink.info.message(EPrefixError, "#version: geometry shaders require es profile with version 310 or non-es profile with version 150 or above"); + version = (profile == EEsProfile) ? 310 : 150; + if (profile == EEsProfile || profile == ENoProfile) + profile = ECoreProfile; + } + break; + case EShLangTessControl: + case EShLangTessEvaluation: + if ((profile == EEsProfile && version < 310) || + (profile != EEsProfile && version < 150)) { + correct = false; + infoSink.info.message(EPrefixError, "#version: tessellation shaders require es profile with version 310 or non-es profile with version 150 or above"); + version = (profile == EEsProfile) ? 310 : 400; // 150 supports the extension, correction is to 400 which does not + if (profile == EEsProfile || profile == ENoProfile) + profile = ECoreProfile; + } + break; + case EShLangCompute: + if ((profile == EEsProfile && version < 310) || + (profile != EEsProfile && version < 420)) { + correct = false; + infoSink.info.message(EPrefixError, "#version: compute shaders require es profile with version 310 or above, or non-es profile with version 420 or above"); + version = profile == EEsProfile ? 310 : 420; + } + break; + case EShLangRayGen: + case EShLangIntersect: + case EShLangAnyHit: + case EShLangClosestHit: + case EShLangMiss: + case EShLangCallable: + if (profile == EEsProfile || version < 460) { + correct = false; + infoSink.info.message(EPrefixError, "#version: ray tracing shaders require non-es profile with version 460 or above"); + version = 460; + } + break; + case EShLangMeshNV: + case EShLangTaskNV: + if ((profile == EEsProfile && version < 320) || + (profile != EEsProfile && version < 450)) { + correct = false; + infoSink.info.message(EPrefixError, "#version: mesh/task shaders require es profile with version 320 or above, or non-es profile with version 450 or above"); + version = profile == EEsProfile ? 320 : 450; + } + default: + break; + } + + if (profile == EEsProfile && version >= 300 && versionNotFirst) { + correct = false; + infoSink.info.message(EPrefixError, "#version: statement must appear first in es-profile shader; before comments or newlines"); + } + + // Check for SPIR-V compatibility + if (spvVersion.spv != 0) { + switch (profile) { + case EEsProfile: + if (version < 310) { + correct = false; + infoSink.info.message(EPrefixError, "#version: ES shaders for SPIR-V require version 310 or higher"); + version = 310; + } + break; + case ECompatibilityProfile: + infoSink.info.message(EPrefixError, "#version: compilation for SPIR-V does not support the compatibility profile"); + break; + default: + if (spvVersion.vulkan > 0 && version < 140) { + correct = false; + infoSink.info.message(EPrefixError, "#version: Desktop shaders for Vulkan SPIR-V require version 140 or higher"); + version = 140; + } + if (spvVersion.openGl >= 100 && version < 330) { + correct = false; + infoSink.info.message(EPrefixError, "#version: Desktop shaders for OpenGL SPIR-V require version 330 or higher"); + version = 330; + } + break; + } + } +#endif + + return correct; +} + +// There are multiple paths in for setting environment stuff. +// TEnvironment takes precedence, for what it sets, so sort all this out. +// Ideally, the internal code could be made to use TEnvironment, but for +// now, translate it to the historically used parameters. +void TranslateEnvironment(const TEnvironment* environment, EShMessages& messages, EShSource& source, + EShLanguage& stage, SpvVersion& spvVersion) +{ + // Set up environmental defaults, first ignoring 'environment'. + if (messages & EShMsgSpvRules) + spvVersion.spv = EShTargetSpv_1_0; + if (messages & EShMsgVulkanRules) { + spvVersion.vulkan = EShTargetVulkan_1_0; + spvVersion.vulkanGlsl = 100; + } else if (spvVersion.spv != 0) + spvVersion.openGl = 100; + + // Now, override, based on any content set in 'environment'. + // 'environment' must be cleared to ESh*None settings when items + // are not being set. + if (environment != nullptr) { + // input language + if (environment->input.languageFamily != EShSourceNone) { + stage = environment->input.stage; + switch (environment->input.dialect) { + case EShClientNone: + break; + case EShClientVulkan: + spvVersion.vulkanGlsl = environment->input.dialectVersion; + break; + case EShClientOpenGL: + spvVersion.openGl = environment->input.dialectVersion; + break; + case EShClientCount: + assert(0); + break; + } + switch (environment->input.languageFamily) { + case EShSourceNone: + break; + case EShSourceGlsl: + source = EShSourceGlsl; + messages = static_cast(messages & ~EShMsgReadHlsl); + break; + case EShSourceHlsl: + source = EShSourceHlsl; + messages = static_cast(messages | EShMsgReadHlsl); + break; + case EShSourceCount: + assert(0); + break; + } + } + + // client + switch (environment->client.client) { + case EShClientVulkan: + spvVersion.vulkan = environment->client.version; + break; + default: + break; + } + + // generated code + switch (environment->target.language) { + case EshTargetSpv: + spvVersion.spv = environment->target.version; + break; + default: + break; + } + } +} + +// Most processes are recorded when set in the intermediate representation, +// These are the few that are not. +void RecordProcesses(TIntermediate& intermediate, EShMessages messages, const std::string& sourceEntryPointName) +{ + if ((messages & EShMsgRelaxedErrors) != 0) + intermediate.addProcess("relaxed-errors"); + if ((messages & EShMsgSuppressWarnings) != 0) + intermediate.addProcess("suppress-warnings"); + if ((messages & EShMsgKeepUncalled) != 0) + intermediate.addProcess("keep-uncalled"); + if (sourceEntryPointName.size() > 0) { + intermediate.addProcess("source-entrypoint"); + intermediate.addProcessArgument(sourceEntryPointName); + } +} + +// This is the common setup and cleanup code for PreprocessDeferred and +// CompileDeferred. +// It takes any callable with a signature of +// bool (TParseContextBase& parseContext, TPpContext& ppContext, +// TInputScanner& input, bool versionWillBeError, +// TSymbolTable& , TIntermediate& , +// EShOptimizationLevel , EShMessages ); +// Which returns false if a failure was detected and true otherwise. +// +template +bool ProcessDeferred( + TCompiler* compiler, + const char* const shaderStrings[], + const int numStrings, + const int* inputLengths, + const char* const stringNames[], + const char* customPreamble, + const EShOptimizationLevel optLevel, + const TBuiltInResource* resources, + int defaultVersion, // use 100 for ES environment, 110 for desktop; this is the GLSL version, not SPIR-V or Vulkan + EProfile defaultProfile, + // set version/profile to defaultVersion/defaultProfile regardless of the #version + // directive in the source code + bool forceDefaultVersionAndProfile, + bool forwardCompatible, // give errors for use of deprecated features + EShMessages messages, // warnings/errors/AST; things to print out + TIntermediate& intermediate, // returned tree, etc. + ProcessingContext& processingContext, + bool requireNonempty, + TShader::Includer& includer, + const std::string sourceEntryPointName = "", + const TEnvironment* environment = nullptr) // optional way of fully setting all versions, overriding the above +{ + // This must be undone (.pop()) by the caller, after it finishes consuming the created tree. + GetThreadPoolAllocator().push(); + + if (numStrings == 0) + return true; + + // Move to length-based strings, rather than null-terminated strings. + // Also, add strings to include the preamble and to ensure the shader is not null, + // which lets the grammar accept what was a null (post preprocessing) shader. + // + // Shader will look like + // string 0: system preamble + // string 1: custom preamble + // string 2...numStrings+1: user's shader + // string numStrings+2: "int;" + const int numPre = 2; + const int numPost = requireNonempty? 1 : 0; + const int numTotal = numPre + numStrings + numPost; + std::unique_ptr lengths(new size_t[numTotal]); + std::unique_ptr strings(new const char*[numTotal]); + std::unique_ptr names(new const char*[numTotal]); + for (int s = 0; s < numStrings; ++s) { + strings[s + numPre] = shaderStrings[s]; + if (inputLengths == nullptr || inputLengths[s] < 0) + lengths[s + numPre] = strlen(shaderStrings[s]); + else + lengths[s + numPre] = inputLengths[s]; + } + if (stringNames != nullptr) { + for (int s = 0; s < numStrings; ++s) + names[s + numPre] = stringNames[s]; + } else { + for (int s = 0; s < numStrings; ++s) + names[s + numPre] = nullptr; + } + + // Get all the stages, languages, clients, and other environment + // stuff sorted out. + EShSource sourceGuess = (messages & EShMsgReadHlsl) != 0 ? EShSourceHlsl : EShSourceGlsl; + SpvVersion spvVersion; + EShLanguage stage = compiler->getLanguage(); + TranslateEnvironment(environment, messages, sourceGuess, stage, spvVersion); +#ifdef ENABLE_HLSL + EShSource source = sourceGuess; + if (environment != nullptr && environment->target.hlslFunctionality1) + intermediate.setHlslFunctionality1(); +#else + const EShSource source = EShSourceGlsl; +#endif + // First, without using the preprocessor or parser, find the #version, so we know what + // symbol tables, processing rules, etc. to set up. This does not need the extra strings + // outlined above, just the user shader, after the system and user preambles. + glslang::TInputScanner userInput(numStrings, &strings[numPre], &lengths[numPre]); + int version = 0; + EProfile profile = ENoProfile; + bool versionNotFirstToken = false; + bool versionNotFirst = (source == EShSourceHlsl) + ? true + : userInput.scanVersion(version, profile, versionNotFirstToken); + bool versionNotFound = version == 0; + if (forceDefaultVersionAndProfile && source == EShSourceGlsl) { +#ifndef GLSLANG_WEB + if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound && + (version != defaultVersion || profile != defaultProfile)) { + compiler->infoSink.info << "Warning, (version, profile) forced to be (" + << defaultVersion << ", " << ProfileName(defaultProfile) + << "), while in source code it is (" + << version << ", " << ProfileName(profile) << ")\n"; + } +#endif + if (versionNotFound) { + versionNotFirstToken = false; + versionNotFirst = false; + versionNotFound = false; + } + version = defaultVersion; + profile = defaultProfile; + } + + bool goodVersion = DeduceVersionProfile(compiler->infoSink, stage, + versionNotFirst, defaultVersion, source, version, profile, spvVersion); +#ifdef GLSLANG_WEB + profile = EEsProfile; + version = 310; +#endif + + bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst)); +#ifndef GLSLANG_WEB + bool warnVersionNotFirst = false; + if (! versionWillBeError && versionNotFirstToken) { + if (messages & EShMsgRelaxedErrors) + warnVersionNotFirst = true; + else + versionWillBeError = true; + } +#endif + + intermediate.setSource(source); + intermediate.setVersion(version); + intermediate.setProfile(profile); + intermediate.setSpv(spvVersion); + RecordProcesses(intermediate, messages, sourceEntryPointName); + if (spvVersion.vulkan > 0) + intermediate.setOriginUpperLeft(); +#ifdef ENABLE_HLSL + if ((messages & EShMsgHlslOffsets) || source == EShSourceHlsl) + intermediate.setHlslOffsets(); +#endif + if (messages & EShMsgDebugInfo) { + intermediate.setSourceFile(names[numPre]); + for (int s = 0; s < numStrings; ++s) { + // The string may not be null-terminated, so make sure we provide + // the length along with the string. + intermediate.addSourceText(strings[numPre + s], lengths[numPre + s]); + } + } + SetupBuiltinSymbolTable(version, profile, spvVersion, source); + + TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)] + [MapSpvVersionToIndex(spvVersion)] + [MapProfileToIndex(profile)] + [MapSourceToIndex(source)] + [stage]; + + // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool. + std::unique_ptr symbolTable(new TSymbolTable); + if (cachedTable) + symbolTable->adoptLevels(*cachedTable); + + // Add built-in symbols that are potentially context dependent; + // they get popped again further down. + if (! AddContextSpecificSymbols(resources, compiler->infoSink, *symbolTable, version, profile, spvVersion, + stage, source)) { + return false; + } + + if (messages & EShMsgBuiltinSymbolTable) + DumpBuiltinSymbolTable(compiler->infoSink, *symbolTable); + + // + // Now we can process the full shader under proper symbols and rules. + // + + std::unique_ptr parseContext(CreateParseContext(*symbolTable, intermediate, version, profile, source, + stage, compiler->infoSink, + spvVersion, forwardCompatible, messages, false, sourceEntryPointName)); + TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer); + + // only GLSL (bison triggered, really) needs an externally set scan context + glslang::TScanContext scanContext(*parseContext); + if (source == EShSourceGlsl) + parseContext->setScanContext(&scanContext); + + parseContext->setPpContext(&ppContext); + parseContext->setLimits(*resources); + if (! goodVersion) + parseContext->addError(); +#ifndef GLSLANG_WEB + if (warnVersionNotFirst) { + TSourceLoc loc; + loc.init(); + parseContext->warn(loc, "Illegal to have non-comment, non-whitespace tokens before #version", "#version", ""); + } +#endif + + parseContext->initializeExtensionBehavior(); + + // Fill in the strings as outlined above. + std::string preamble; + parseContext->getPreamble(preamble); + strings[0] = preamble.c_str(); + lengths[0] = strlen(strings[0]); + names[0] = nullptr; + strings[1] = customPreamble; + lengths[1] = strlen(strings[1]); + names[1] = nullptr; + assert(2 == numPre); + if (requireNonempty) { + const int postIndex = numStrings + numPre; + strings[postIndex] = "\n int;"; + lengths[postIndex] = strlen(strings[numStrings + numPre]); + names[postIndex] = nullptr; + } + TInputScanner fullInput(numStrings + numPre + numPost, strings.get(), lengths.get(), names.get(), numPre, numPost); + + // Push a new symbol allocation scope that will get used for the shader's globals. + symbolTable->push(); + + bool success = processingContext(*parseContext, ppContext, fullInput, + versionWillBeError, *symbolTable, + intermediate, optLevel, messages); + return success; +} + +#ifndef GLSLANG_WEB + +// Responsible for keeping track of the most recent source string and line in +// the preprocessor and outputting newlines appropriately if the source string +// or line changes. +class SourceLineSynchronizer { +public: + SourceLineSynchronizer(const std::function& lastSourceIndex, + std::string* output) + : getLastSourceIndex(lastSourceIndex), output(output), lastSource(-1), lastLine(0) {} +// SourceLineSynchronizer(const SourceLineSynchronizer&) = delete; +// SourceLineSynchronizer& operator=(const SourceLineSynchronizer&) = delete; + + // Sets the internally tracked source string index to that of the most + // recently read token. If we switched to a new source string, returns + // true and inserts a newline. Otherwise, returns false and outputs nothing. + bool syncToMostRecentString() { + if (getLastSourceIndex() != lastSource) { + // After switching to a new source string, we need to reset lastLine + // because line number resets every time a new source string is + // used. We also need to output a newline to separate the output + // from the previous source string (if there is one). + if (lastSource != -1 || lastLine != 0) + *output += '\n'; + lastSource = getLastSourceIndex(); + lastLine = -1; + return true; + } + return false; + } + + // Calls syncToMostRecentString() and then sets the internally tracked line + // number to tokenLine. If we switched to a new line, returns true and inserts + // newlines appropriately. Otherwise, returns false and outputs nothing. + bool syncToLine(int tokenLine) { + syncToMostRecentString(); + const bool newLineStarted = lastLine < tokenLine; + for (; lastLine < tokenLine; ++lastLine) { + if (lastLine > 0) *output += '\n'; + } + return newLineStarted; + } + + // Sets the internally tracked line number to newLineNum. + void setLineNum(int newLineNum) { lastLine = newLineNum; } + +private: + SourceLineSynchronizer& operator=(const SourceLineSynchronizer&); + + // A function for getting the index of the last valid source string we've + // read tokens from. + const std::function getLastSourceIndex; + // output string for newlines. + std::string* output; + // lastSource is the source string index (starting from 0) of the last token + // processed. It is tracked in order for newlines to be inserted when a new + // source string starts. -1 means we haven't started processing any source + // string. + int lastSource; + // lastLine is the line number (starting from 1) of the last token processed. + // It is tracked in order for newlines to be inserted when a token appears + // on a new line. 0 means we haven't started processing any line in the + // current source string. + int lastLine; +}; + +// DoPreprocessing is a valid ProcessingContext template argument, +// which only performs the preprocessing step of compilation. +// It places the result in the "string" argument to its constructor. +// +// This is not an officially supported or fully working path. +struct DoPreprocessing { + explicit DoPreprocessing(std::string* string): outputString(string) {} + bool operator()(TParseContextBase& parseContext, TPpContext& ppContext, + TInputScanner& input, bool versionWillBeError, + TSymbolTable&, TIntermediate&, + EShOptimizationLevel, EShMessages) + { + // This is a list of tokens that do not require a space before or after. + static const std::string unNeededSpaceTokens = ";()[]"; + static const std::string noSpaceBeforeTokens = ","; + glslang::TPpToken ppToken; + + parseContext.setScanner(&input); + ppContext.setInput(input, versionWillBeError); + + std::string outputBuffer; + SourceLineSynchronizer lineSync( + std::bind(&TInputScanner::getLastValidSourceIndex, &input), &outputBuffer); + + parseContext.setExtensionCallback([&lineSync, &outputBuffer]( + int line, const char* extension, const char* behavior) { + lineSync.syncToLine(line); + outputBuffer += "#extension "; + outputBuffer += extension; + outputBuffer += " : "; + outputBuffer += behavior; + }); + + parseContext.setLineCallback([&lineSync, &outputBuffer, &parseContext]( + int curLineNum, int newLineNum, bool hasSource, int sourceNum, const char* sourceName) { + // SourceNum is the number of the source-string that is being parsed. + lineSync.syncToLine(curLineNum); + outputBuffer += "#line "; + outputBuffer += std::to_string(newLineNum); + if (hasSource) { + outputBuffer += ' '; + if (sourceName != nullptr) { + outputBuffer += '\"'; + outputBuffer += sourceName; + outputBuffer += '\"'; + } else { + outputBuffer += std::to_string(sourceNum); + } + } + if (parseContext.lineDirectiveShouldSetNextLine()) { + // newLineNum is the new line number for the line following the #line + // directive. So the new line number for the current line is + newLineNum -= 1; + } + outputBuffer += '\n'; + // And we are at the next line of the #line directive now. + lineSync.setLineNum(newLineNum + 1); + }); + + parseContext.setVersionCallback( + [&lineSync, &outputBuffer](int line, int version, const char* str) { + lineSync.syncToLine(line); + outputBuffer += "#version "; + outputBuffer += std::to_string(version); + if (str) { + outputBuffer += ' '; + outputBuffer += str; + } + }); + + parseContext.setPragmaCallback([&lineSync, &outputBuffer]( + int line, const glslang::TVector& ops) { + lineSync.syncToLine(line); + outputBuffer += "#pragma "; + for(size_t i = 0; i < ops.size(); ++i) { + outputBuffer += ops[i].c_str(); + } + }); + + parseContext.setErrorCallback([&lineSync, &outputBuffer]( + int line, const char* errorMessage) { + lineSync.syncToLine(line); + outputBuffer += "#error "; + outputBuffer += errorMessage; + }); + + int lastToken = EndOfInput; // lastToken records the last token processed. + do { + int token = ppContext.tokenize(ppToken); + if (token == EndOfInput) + break; + + bool isNewString = lineSync.syncToMostRecentString(); + bool isNewLine = lineSync.syncToLine(ppToken.loc.line); + + if (isNewLine) { + // Don't emit whitespace onto empty lines. + // Copy any whitespace characters at the start of a line + // from the input to the output. + outputBuffer += std::string(ppToken.loc.column - 1, ' '); + } + + // Output a space in between tokens, but not at the start of a line, + // and also not around special tokens. This helps with readability + // and consistency. + if (!isNewString && !isNewLine && lastToken != EndOfInput && + (unNeededSpaceTokens.find((char)token) == std::string::npos) && + (unNeededSpaceTokens.find((char)lastToken) == std::string::npos) && + (noSpaceBeforeTokens.find((char)token) == std::string::npos)) { + outputBuffer += ' '; + } + lastToken = token; + if (token == PpAtomConstString) + outputBuffer += "\""; + outputBuffer += ppToken.name; + if (token == PpAtomConstString) + outputBuffer += "\""; + } while (true); + outputBuffer += '\n'; + *outputString = std::move(outputBuffer); + + bool success = true; + if (parseContext.getNumErrors() > 0) { + success = false; + parseContext.infoSink.info.prefix(EPrefixError); + parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors. No code generated.\n\n"; + } + return success; + } + std::string* outputString; +}; + +#endif + +// DoFullParse is a valid ProcessingConext template argument for fully +// parsing the shader. It populates the "intermediate" with the AST. +struct DoFullParse{ + bool operator()(TParseContextBase& parseContext, TPpContext& ppContext, + TInputScanner& fullInput, bool versionWillBeError, + TSymbolTable&, TIntermediate& intermediate, + EShOptimizationLevel optLevel, EShMessages messages) + { + bool success = true; + // Parse the full shader. + if (! parseContext.parseShaderStrings(ppContext, fullInput, versionWillBeError)) + success = false; + + if (success && intermediate.getTreeRoot()) { + if (optLevel == EShOptNoGeneration) + parseContext.infoSink.info.message(EPrefixNone, "No errors. No code generation or linking was requested."); + else + success = intermediate.postProcess(intermediate.getTreeRoot(), parseContext.getLanguage()); + } else if (! success) { + parseContext.infoSink.info.prefix(EPrefixError); + parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors. No code generated.\n\n"; + } + + if (messages & EShMsgAST) + intermediate.output(parseContext.infoSink, true); + + return success; + } +}; + +#ifndef GLSLANG_WEB +// Take a single compilation unit, and run the preprocessor on it. +// Return: True if there were no issues found in preprocessing, +// False if during preprocessing any unknown version, pragmas or +// extensions were found. +// +// NOTE: Doing just preprocessing to obtain a correct preprocessed shader string +// is not an officially supported or fully working path. +bool PreprocessDeferred( + TCompiler* compiler, + const char* const shaderStrings[], + const int numStrings, + const int* inputLengths, + const char* const stringNames[], + const char* preamble, + const EShOptimizationLevel optLevel, + const TBuiltInResource* resources, + int defaultVersion, // use 100 for ES environment, 110 for desktop + EProfile defaultProfile, + bool forceDefaultVersionAndProfile, + bool forwardCompatible, // give errors for use of deprecated features + EShMessages messages, // warnings/errors/AST; things to print out + TShader::Includer& includer, + TIntermediate& intermediate, // returned tree, etc. + std::string* outputString) +{ + DoPreprocessing parser(outputString); + return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, + preamble, optLevel, resources, defaultVersion, + defaultProfile, forceDefaultVersionAndProfile, + forwardCompatible, messages, intermediate, parser, + false, includer); +} +#endif + +// +// do a partial compile on the given strings for a single compilation unit +// for a potential deferred link into a single stage (and deferred full compile of that +// stage through machine-dependent compilation). +// +// all preprocessing, parsing, semantic checks, etc. for a single compilation unit +// are done here. +// +// return: the tree and other information is filled into the intermediate argument, +// and true is returned by the function for success. +// +bool CompileDeferred( + TCompiler* compiler, + const char* const shaderStrings[], + const int numStrings, + const int* inputLengths, + const char* const stringNames[], + const char* preamble, + const EShOptimizationLevel optLevel, + const TBuiltInResource* resources, + int defaultVersion, // use 100 for ES environment, 110 for desktop + EProfile defaultProfile, + bool forceDefaultVersionAndProfile, + bool forwardCompatible, // give errors for use of deprecated features + EShMessages messages, // warnings/errors/AST; things to print out + TIntermediate& intermediate,// returned tree, etc. + TShader::Includer& includer, + const std::string sourceEntryPointName = "", + TEnvironment* environment = nullptr) +{ + DoFullParse parser; + return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, + preamble, optLevel, resources, defaultVersion, + defaultProfile, forceDefaultVersionAndProfile, + forwardCompatible, messages, intermediate, parser, + true, includer, sourceEntryPointName, environment); +} + +} // end anonymous namespace for local functions + +// +// ShInitialize() should be called exactly once per process, not per thread. +// +int ShInitialize() +{ + glslang::InitGlobalLock(); + + if (! InitProcess()) + return 0; + + glslang::GetGlobalLock(); + ++NumberOfClients; + glslang::ReleaseGlobalLock(); + + if (PerProcessGPA == nullptr) + PerProcessGPA = new TPoolAllocator(); + + glslang::TScanContext::fillInKeywordMap(); +#ifdef ENABLE_HLSL + glslang::HlslScanContext::fillInKeywordMap(); +#endif + + return 1; +} + +// +// Driver calls these to create and destroy compiler/linker +// objects. +// + +ShHandle ShConstructCompiler(const EShLanguage language, int debugOptions) +{ + if (!InitThread()) + return 0; + + TShHandleBase* base = static_cast(ConstructCompiler(language, debugOptions)); + + return reinterpret_cast(base); +} + +ShHandle ShConstructLinker(const EShExecutable executable, int debugOptions) +{ + if (!InitThread()) + return 0; + + TShHandleBase* base = static_cast(ConstructLinker(executable, debugOptions)); + + return reinterpret_cast(base); +} + +ShHandle ShConstructUniformMap() +{ + if (!InitThread()) + return 0; + + TShHandleBase* base = static_cast(ConstructUniformMap()); + + return reinterpret_cast(base); +} + +void ShDestruct(ShHandle handle) +{ + if (handle == 0) + return; + + TShHandleBase* base = static_cast(handle); + + if (base->getAsCompiler()) + DeleteCompiler(base->getAsCompiler()); + else if (base->getAsLinker()) + DeleteLinker(base->getAsLinker()); + else if (base->getAsUniformMap()) + DeleteUniformMap(base->getAsUniformMap()); +} + +// +// Cleanup symbol tables +// +int ShFinalize() +{ + glslang::GetGlobalLock(); + --NumberOfClients; + assert(NumberOfClients >= 0); + bool finalize = NumberOfClients == 0; + glslang::ReleaseGlobalLock(); + if (! finalize) + return 1; + + for (int version = 0; version < VersionCount; ++version) { + for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) { + for (int p = 0; p < ProfileCount; ++p) { + for (int source = 0; source < SourceCount; ++source) { + for (int stage = 0; stage < EShLangCount; ++stage) { + delete SharedSymbolTables[version][spvVersion][p][source][stage]; + SharedSymbolTables[version][spvVersion][p][source][stage] = 0; + } + } + } + } + } + + for (int version = 0; version < VersionCount; ++version) { + for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) { + for (int p = 0; p < ProfileCount; ++p) { + for (int source = 0; source < SourceCount; ++source) { + for (int pc = 0; pc < EPcCount; ++pc) { + delete CommonSymbolTable[version][spvVersion][p][source][pc]; + CommonSymbolTable[version][spvVersion][p][source][pc] = 0; + } + } + } + } + } + + if (PerProcessGPA != nullptr) { + delete PerProcessGPA; + PerProcessGPA = nullptr; + } + + glslang::TScanContext::deleteKeywordMap(); +#ifdef ENABLE_HLSL + glslang::HlslScanContext::deleteKeywordMap(); +#endif + + return 1; +} + +// +// Do a full compile on the given strings for a single compilation unit +// forming a complete stage. The result of the machine dependent compilation +// is left in the provided compile object. +// +// Return: The return value is really boolean, indicating +// success (1) or failure (0). +// +int ShCompile( + const ShHandle handle, + const char* const shaderStrings[], + const int numStrings, + const int* inputLengths, + const EShOptimizationLevel optLevel, + const TBuiltInResource* resources, + int /*debugOptions*/, + int defaultVersion, // use 100 for ES environment, 110 for desktop + bool forwardCompatible, // give errors for use of deprecated features + EShMessages messages // warnings/errors/AST; things to print out + ) +{ + // Map the generic handle to the C++ object + if (handle == 0) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + TCompiler* compiler = base->getAsCompiler(); + if (compiler == 0) + return 0; + + SetThreadPoolAllocator(compiler->getPool()); + + compiler->infoSink.info.erase(); + compiler->infoSink.debug.erase(); + + TIntermediate intermediate(compiler->getLanguage()); + TShader::ForbidIncluder includer; + bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, nullptr, + "", optLevel, resources, defaultVersion, ENoProfile, false, + forwardCompatible, messages, intermediate, includer); + + // + // Call the machine dependent compiler + // + if (success && intermediate.getTreeRoot() && optLevel != EShOptNoGeneration) + success = compiler->compile(intermediate.getTreeRoot(), intermediate.getVersion(), intermediate.getProfile()); + + intermediate.removeTree(); + + // Throw away all the temporary memory used by the compilation process. + // The push was done in the CompileDeferred() call above. + GetThreadPoolAllocator().pop(); + + return success ? 1 : 0; +} + +// +// Link the given compile objects. +// +// Return: The return value of is really boolean, indicating +// success or failure. +// +int ShLinkExt( + const ShHandle linkHandle, + const ShHandle compHandles[], + const int numHandles) +{ + if (linkHandle == 0 || numHandles == 0) + return 0; + + THandleList cObjects; + + for (int i = 0; i < numHandles; ++i) { + if (compHandles[i] == 0) + return 0; + TShHandleBase* base = reinterpret_cast(compHandles[i]); + if (base->getAsLinker()) { + cObjects.push_back(base->getAsLinker()); + } + if (base->getAsCompiler()) + cObjects.push_back(base->getAsCompiler()); + + if (cObjects[i] == 0) + return 0; + } + + TShHandleBase* base = reinterpret_cast(linkHandle); + TLinker* linker = static_cast(base->getAsLinker()); + + SetThreadPoolAllocator(linker->getPool()); + + if (linker == 0) + return 0; + + linker->infoSink.info.erase(); + + for (int i = 0; i < numHandles; ++i) { + if (cObjects[i]->getAsCompiler()) { + if (! cObjects[i]->getAsCompiler()->linkable()) { + linker->infoSink.info.message(EPrefixError, "Not all shaders have valid object code."); + return 0; + } + } + } + + bool ret = linker->link(cObjects); + + return ret ? 1 : 0; +} + +// +// ShSetEncrpytionMethod is a place-holder for specifying +// how source code is encrypted. +// +void ShSetEncryptionMethod(ShHandle handle) +{ + if (handle == 0) + return; +} + +// +// Return any compiler/linker/uniformmap log of messages for the application. +// +const char* ShGetInfoLog(const ShHandle handle) +{ + if (handle == 0) + return 0; + + TShHandleBase* base = static_cast(handle); + TInfoSink* infoSink; + + if (base->getAsCompiler()) + infoSink = &(base->getAsCompiler()->getInfoSink()); + else if (base->getAsLinker()) + infoSink = &(base->getAsLinker()->getInfoSink()); + else + return 0; + + infoSink->info << infoSink->debug.c_str(); + return infoSink->info.c_str(); +} + +// +// Return the resulting binary code from the link process. Structure +// is machine dependent. +// +const void* ShGetExecutable(const ShHandle handle) +{ + if (handle == 0) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + + TLinker* linker = static_cast(base->getAsLinker()); + if (linker == 0) + return 0; + + return linker->getObjectCode(); +} + +// +// Let the linker know where the application said it's attributes are bound. +// The linker does not use these values, they are remapped by the ICD or +// hardware. It just needs them to know what's aliased. +// +// Return: The return value of is really boolean, indicating +// success or failure. +// +int ShSetVirtualAttributeBindings(const ShHandle handle, const ShBindingTable* table) +{ + if (handle == 0) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + TLinker* linker = static_cast(base->getAsLinker()); + + if (linker == 0) + return 0; + + linker->setAppAttributeBindings(table); + + return 1; +} + +// +// Let the linker know where the predefined attributes have to live. +// +int ShSetFixedAttributeBindings(const ShHandle handle, const ShBindingTable* table) +{ + if (handle == 0) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + TLinker* linker = static_cast(base->getAsLinker()); + + if (linker == 0) + return 0; + + linker->setFixedAttributeBindings(table); + return 1; +} + +// +// Some attribute locations are off-limits to the linker... +// +int ShExcludeAttributes(const ShHandle handle, int *attributes, int count) +{ + if (handle == 0) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + TLinker* linker = static_cast(base->getAsLinker()); + if (linker == 0) + return 0; + + linker->setExcludedAttributes(attributes, count); + + return 1; +} + +// +// Return the index for OpenGL to use for knowing where a uniform lives. +// +// Return: The return value of is really boolean, indicating +// success or failure. +// +int ShGetUniformLocation(const ShHandle handle, const char* name) +{ + if (handle == 0) + return -1; + + TShHandleBase* base = reinterpret_cast(handle); + TUniformMap* uniformMap= base->getAsUniformMap(); + if (uniformMap == 0) + return -1; + + return uniformMap->getLocation(name); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Deferred-Lowering C++ Interface +// ----------------------------------- +// +// Below is a new alternate C++ interface that might potentially replace the above +// opaque handle-based interface. +// +// See more detailed comment in ShaderLang.h +// + +namespace glslang { + +#include "../Include/revision.h" + +#define QUOTE(s) #s +#define STR(n) QUOTE(n) + +const char* GetEsslVersionString() +{ + return "OpenGL ES GLSL 3.20 glslang Khronos. " STR(GLSLANG_MINOR_VERSION) "." STR(GLSLANG_PATCH_LEVEL); +} + +const char* GetGlslVersionString() +{ + return "4.60 glslang Khronos. " STR(GLSLANG_MINOR_VERSION) "." STR(GLSLANG_PATCH_LEVEL); +} + +int GetKhronosToolId() +{ + return 8; +} + +bool InitializeProcess() +{ + return ShInitialize() != 0; +} + +void FinalizeProcess() +{ + ShFinalize(); +} + +class TDeferredCompiler : public TCompiler { +public: + TDeferredCompiler(EShLanguage s, TInfoSink& i) : TCompiler(s, i) { } + virtual bool compile(TIntermNode*, int = 0, EProfile = ENoProfile) { return true; } +}; + +TShader::TShader(EShLanguage s) + : stage(s), lengths(nullptr), stringNames(nullptr), preamble("") +{ + pool = new TPoolAllocator; + infoSink = new TInfoSink; + compiler = new TDeferredCompiler(stage, *infoSink); + intermediate = new TIntermediate(s); + + // clear environment (avoid constructors in them for use in a C interface) + environment.input.languageFamily = EShSourceNone; + environment.input.dialect = EShClientNone; + environment.client.client = EShClientNone; + environment.target.language = EShTargetNone; + environment.target.hlslFunctionality1 = false; +} + +TShader::~TShader() +{ + delete infoSink; + delete compiler; + delete intermediate; + delete pool; +} + +void TShader::setStrings(const char* const* s, int n) +{ + strings = s; + numStrings = n; + lengths = nullptr; +} + +void TShader::setStringsWithLengths(const char* const* s, const int* l, int n) +{ + strings = s; + numStrings = n; + lengths = l; +} + +void TShader::setStringsWithLengthsAndNames( + const char* const* s, const int* l, const char* const* names, int n) +{ + strings = s; + numStrings = n; + lengths = l; + stringNames = names; +} + +void TShader::setEntryPoint(const char* entryPoint) +{ + intermediate->setEntryPointName(entryPoint); +} + +void TShader::setSourceEntryPoint(const char* name) +{ + sourceEntryPointName = name; +} + +// Log initial settings and transforms. +// See comment for class TProcesses. +void TShader::addProcesses(const std::vector& p) +{ + intermediate->addProcesses(p); +} + +void TShader::setInvertY(bool invert) { intermediate->setInvertY(invert); } +void TShader::setNanMinMaxClamp(bool useNonNan) { intermediate->setNanMinMaxClamp(useNonNan); } + +#ifndef GLSLANG_WEB + +// Set binding base for given resource type +void TShader::setShiftBinding(TResourceType res, unsigned int base) { + intermediate->setShiftBinding(res, base); +} + +// Set binding base for given resource type for a given binding set. +void TShader::setShiftBindingForSet(TResourceType res, unsigned int base, unsigned int set) { + intermediate->setShiftBindingForSet(res, base, set); +} + +// Set binding base for sampler types +void TShader::setShiftSamplerBinding(unsigned int base) { setShiftBinding(EResSampler, base); } +// Set binding base for texture types (SRV) +void TShader::setShiftTextureBinding(unsigned int base) { setShiftBinding(EResTexture, base); } +// Set binding base for image types +void TShader::setShiftImageBinding(unsigned int base) { setShiftBinding(EResImage, base); } +// Set binding base for uniform buffer objects (CBV) +void TShader::setShiftUboBinding(unsigned int base) { setShiftBinding(EResUbo, base); } +// Synonym for setShiftUboBinding, to match HLSL language. +void TShader::setShiftCbufferBinding(unsigned int base) { setShiftBinding(EResUbo, base); } +// Set binding base for UAV (unordered access view) +void TShader::setShiftUavBinding(unsigned int base) { setShiftBinding(EResUav, base); } +// Set binding base for SSBOs +void TShader::setShiftSsboBinding(unsigned int base) { setShiftBinding(EResSsbo, base); } +// Enables binding automapping using TIoMapper +void TShader::setAutoMapBindings(bool map) { intermediate->setAutoMapBindings(map); } +// Enables position.Y output negation in vertex shader + +// Fragile: currently within one stage: simple auto-assignment of location +void TShader::setAutoMapLocations(bool map) { intermediate->setAutoMapLocations(map); } +void TShader::addUniformLocationOverride(const char* name, int loc) +{ + intermediate->addUniformLocationOverride(name, loc); +} +void TShader::setUniformLocationBase(int base) +{ + intermediate->setUniformLocationBase(base); +} +void TShader::setNoStorageFormat(bool useUnknownFormat) { intermediate->setNoStorageFormat(useUnknownFormat); } +void TShader::setResourceSetBinding(const std::vector& base) { intermediate->setResourceSetBinding(base); } +void TShader::setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { intermediate->setTextureSamplerTransformMode(mode); } +#endif + +#ifdef ENABLE_HLSL +// See comment above TDefaultHlslIoMapper in iomapper.cpp: +void TShader::setHlslIoMapping(bool hlslIoMap) { intermediate->setHlslIoMapping(hlslIoMap); } +void TShader::setFlattenUniformArrays(bool flatten) { intermediate->setFlattenUniformArrays(flatten); } +#endif + +// +// Turn the shader strings into a parse tree in the TIntermediate. +// +// Returns true for success. +// +bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages messages, Includer& includer) +{ + if (! InitThread()) + return false; + SetThreadPoolAllocator(pool); + + if (! preamble) + preamble = ""; + + return CompileDeferred(compiler, strings, numStrings, lengths, stringNames, + preamble, EShOptNone, builtInResources, defaultVersion, + defaultProfile, forceDefaultVersionAndProfile, + forwardCompatible, messages, *intermediate, includer, sourceEntryPointName, + &environment); +} + +#ifndef GLSLANG_WEB +// Fill in a string with the result of preprocessing ShaderStrings +// Returns true if all extensions, pragmas and version strings were valid. +// +// NOTE: Doing just preprocessing to obtain a correct preprocessed shader string +// is not an officially supported or fully working path. +bool TShader::preprocess(const TBuiltInResource* builtInResources, + int defaultVersion, EProfile defaultProfile, + bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages message, + std::string* output_string, + Includer& includer) +{ + if (! InitThread()) + return false; + SetThreadPoolAllocator(pool); + + if (! preamble) + preamble = ""; + + return PreprocessDeferred(compiler, strings, numStrings, lengths, stringNames, preamble, + EShOptNone, builtInResources, defaultVersion, + defaultProfile, forceDefaultVersionAndProfile, + forwardCompatible, message, includer, *intermediate, output_string); +} +#endif + +const char* TShader::getInfoLog() +{ + return infoSink->info.c_str(); +} + +const char* TShader::getInfoDebugLog() +{ + return infoSink->debug.c_str(); +} + +TProgram::TProgram() : +#ifndef GLSLANG_WEB + reflection(0), +#endif + linked(false) +{ + pool = new TPoolAllocator; + infoSink = new TInfoSink; + for (int s = 0; s < EShLangCount; ++s) { + intermediate[s] = 0; + newedIntermediate[s] = false; + } +} + +TProgram::~TProgram() +{ + delete infoSink; +#ifndef GLSLANG_WEB + delete reflection; +#endif + + for (int s = 0; s < EShLangCount; ++s) + if (newedIntermediate[s]) + delete intermediate[s]; + + delete pool; +} + +// +// Merge the compilation units within each stage into a single TIntermediate. +// All starting compilation units need to be the result of calling TShader::parse(). +// +// Return true for success. +// +bool TProgram::link(EShMessages messages) +{ + if (linked) + return false; + linked = true; + + bool error = false; + + SetThreadPoolAllocator(pool); + + for (int s = 0; s < EShLangCount; ++s) { + if (! linkStage((EShLanguage)s, messages)) + error = true; + } + + // TODO: Link: cross-stage error checking + + return ! error; +} + +// +// Merge the compilation units within the given stage into a single TIntermediate. +// +// Return true for success. +// +bool TProgram::linkStage(EShLanguage stage, EShMessages messages) +{ + if (stages[stage].size() == 0) + return true; + +#ifndef GLSLANG_WEB + int numEsShaders = 0, numNonEsShaders = 0; + for (auto it = stages[stage].begin(); it != stages[stage].end(); ++it) { + if ((*it)->intermediate->getProfile() == EEsProfile) { + numEsShaders++; + } else { + numNonEsShaders++; + } + } + + if (numEsShaders > 0 && numNonEsShaders > 0) { + infoSink->info.message(EPrefixError, "Cannot mix ES profile with non-ES profile shaders"); + return false; + } else if (numEsShaders > 1) { + infoSink->info.message(EPrefixError, "Cannot attach multiple ES shaders of the same type to a single program"); + return false; + } + + // + // Be efficient for the common single compilation unit per stage case, + // reusing it's TIntermediate instead of merging into a new one. + // + TIntermediate *firstIntermediate = stages[stage].front()->intermediate; + if (stages[stage].size() == 1) + intermediate[stage] = firstIntermediate; + else { + intermediate[stage] = new TIntermediate(stage, + firstIntermediate->getVersion(), + firstIntermediate->getProfile()); + + + // The new TIntermediate must use the same origin as the original TIntermediates. + // Otherwise linking will fail due to different coordinate systems. + if (firstIntermediate->getOriginUpperLeft()) { + intermediate[stage]->setOriginUpperLeft(); + } + intermediate[stage]->setSpv(firstIntermediate->getSpv()); + + newedIntermediate[stage] = true; + } + + if (messages & EShMsgAST) + infoSink->info << "\nLinked " << StageName(stage) << " stage:\n\n"; + + if (stages[stage].size() > 1) { + std::list::const_iterator it; + for (it = stages[stage].begin(); it != stages[stage].end(); ++it) + intermediate[stage]->merge(*infoSink, *(*it)->intermediate); + } +#else + intermediate[stage] = stages[stage].front()->intermediate; +#endif + intermediate[stage]->finalCheck(*infoSink, (messages & EShMsgKeepUncalled) != 0); + + if (messages & EShMsgAST) + intermediate[stage]->output(*infoSink, true); + + return intermediate[stage]->getNumErrors() == 0; +} + +const char* TProgram::getInfoLog() +{ + return infoSink->info.c_str(); +} + +const char* TProgram::getInfoDebugLog() +{ + return infoSink->debug.c_str(); +} + +#ifndef GLSLANG_WEB + +// +// Reflection implementation. +// + +bool TProgram::buildReflection(int opts) +{ + if (! linked || reflection != nullptr) + return false; + + int firstStage = EShLangVertex, lastStage = EShLangFragment; + + if (opts & EShReflectionIntermediateIO) { + // if we're reflecting intermediate I/O, determine the first and last stage linked and use those as the + // boundaries for which stages generate pipeline inputs/outputs + firstStage = EShLangCount; + lastStage = 0; + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s]) { + firstStage = std::min(firstStage, s); + lastStage = std::max(lastStage, s); + } + } + } + + reflection = new TReflection((EShReflectionOptions)opts, (EShLanguage)firstStage, (EShLanguage)lastStage); + + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s]) { + if (! reflection->addStage((EShLanguage)s, *intermediate[s])) + return false; + } + } + + return true; +} + +unsigned TProgram::getLocalSize(int dim) const { return reflection->getLocalSize(dim); } +int TProgram::getReflectionIndex(const char* name) const { return reflection->getIndex(name); } +int TProgram::getReflectionPipeIOIndex(const char* name, const bool inOrOut) const + { return reflection->getPipeIOIndex(name, inOrOut); } + +int TProgram::getNumUniformVariables() const { return reflection->getNumUniforms(); } +const TObjectReflection& TProgram::getUniform(int index) const { return reflection->getUniform(index); } +int TProgram::getNumUniformBlocks() const { return reflection->getNumUniformBlocks(); } +const TObjectReflection& TProgram::getUniformBlock(int index) const { return reflection->getUniformBlock(index); } +int TProgram::getNumPipeInputs() const { return reflection->getNumPipeInputs(); } +const TObjectReflection& TProgram::getPipeInput(int index) const { return reflection->getPipeInput(index); } +int TProgram::getNumPipeOutputs() const { return reflection->getNumPipeOutputs(); } +const TObjectReflection& TProgram::getPipeOutput(int index) const { return reflection->getPipeOutput(index); } +int TProgram::getNumBufferVariables() const { return reflection->getNumBufferVariables(); } +const TObjectReflection& TProgram::getBufferVariable(int index) const { return reflection->getBufferVariable(index); } +int TProgram::getNumBufferBlocks() const { return reflection->getNumStorageBuffers(); } +const TObjectReflection& TProgram::getBufferBlock(int index) const { return reflection->getStorageBufferBlock(index); } +int TProgram::getNumAtomicCounters() const { return reflection->getNumAtomicCounters(); } +const TObjectReflection& TProgram::getAtomicCounter(int index) const { return reflection->getAtomicCounter(index); } +void TProgram::dumpReflection() { if (reflection != nullptr) reflection->dump(); } + +// +// I/O mapping implementation. +// +bool TProgram::mapIO(TIoMapResolver* pResolver, TIoMapper* pIoMapper) +{ + if (! linked) + return false; + TIoMapper* ioMapper = nullptr; + TIoMapper defaultIOMapper; + if (pIoMapper == nullptr) + ioMapper = &defaultIOMapper; + else + ioMapper = pIoMapper; + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s]) { + if (! ioMapper->addStage((EShLanguage)s, *intermediate[s], *infoSink, pResolver)) + return false; + } + } + + return ioMapper->doMap(pResolver, *infoSink); +} + +#endif // GLSLANG_WEB + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/SymbolTable.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/SymbolTable.cpp new file mode 100644 index 000000000..2363c8d0a --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/SymbolTable.cpp @@ -0,0 +1,448 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Symbol table for parsing. Most functionality and main ideas +// are documented in the header file. +// + +#include "SymbolTable.h" + +namespace glslang { + +// +// TType helper function needs a place to live. +// + +// +// Recursively generate mangled names. +// +void TType::buildMangledName(TString& mangledName) const +{ + if (isMatrix()) + mangledName += 'm'; + else if (isVector()) + mangledName += 'v'; + + switch (basicType) { + case EbtFloat: mangledName += 'f'; break; + case EbtInt: mangledName += 'i'; break; + case EbtUint: mangledName += 'u'; break; + case EbtBool: mangledName += 'b'; break; +#ifndef GLSLANG_WEB + case EbtDouble: mangledName += 'd'; break; + case EbtFloat16: mangledName += "f16"; break; + case EbtInt8: mangledName += "i8"; break; + case EbtUint8: mangledName += "u8"; break; + case EbtInt16: mangledName += "i16"; break; + case EbtUint16: mangledName += "u16"; break; + case EbtInt64: mangledName += "i64"; break; + case EbtUint64: mangledName += "u64"; break; + case EbtAtomicUint: mangledName += "au"; break; + case EbtAccStruct: mangledName += "as"; break; + case EbtRayQuery: mangledName += "rq"; break; +#endif + case EbtSampler: + switch (sampler.type) { +#ifndef GLSLANG_WEB + case EbtFloat16: mangledName += "f16"; break; +#endif + case EbtInt: mangledName += "i"; break; + case EbtUint: mangledName += "u"; break; + default: break; // some compilers want this + } + if (sampler.isImageClass()) + mangledName += "I"; // a normal image or subpass + else if (sampler.isPureSampler()) + mangledName += "p"; // a "pure" sampler + else if (!sampler.isCombined()) + mangledName += "t"; // a "pure" texture + else + mangledName += "s"; // traditional combined sampler + if (sampler.isArrayed()) + mangledName += "A"; + if (sampler.isShadow()) + mangledName += "S"; + if (sampler.isExternal()) + mangledName += "E"; + if (sampler.isYuv()) + mangledName += "Y"; + if (sampler.external) + mangledName += "V"; + switch (sampler.dim) { + case Esd2D: mangledName += "2"; break; + case Esd3D: mangledName += "3"; break; + case EsdCube: mangledName += "C"; break; +#ifndef GLSLANG_WEB + case Esd1D: mangledName += "1"; break; + case EsdRect: mangledName += "R2"; break; + case EsdBuffer: mangledName += "B"; break; + case EsdSubpass: mangledName += "P"; break; +#endif + default: break; // some compilers want this + } + +#ifdef ENABLE_HLSL + if (sampler.hasReturnStruct()) { + // Name mangle for sampler return struct uses struct table index. + mangledName += "-tx-struct"; + + char text[16]; // plenty enough space for the small integers. + snprintf(text, sizeof(text), "%u-", sampler.getStructReturnIndex()); + mangledName += text; + } else { + switch (sampler.getVectorSize()) { + case 1: mangledName += "1"; break; + case 2: mangledName += "2"; break; + case 3: mangledName += "3"; break; + case 4: break; // default to prior name mangle behavior + } + } +#endif + + if (sampler.isMultiSample()) + mangledName += "M"; + break; + case EbtStruct: + case EbtBlock: + if (basicType == EbtStruct) + mangledName += "struct-"; + else + mangledName += "block-"; + if (typeName) + mangledName += *typeName; + for (unsigned int i = 0; i < structure->size(); ++i) { + mangledName += '-'; + (*structure)[i].type->buildMangledName(mangledName); + } + default: + break; + } + + if (getVectorSize() > 0) + mangledName += static_cast('0' + getVectorSize()); + else { + mangledName += static_cast('0' + getMatrixCols()); + mangledName += static_cast('0' + getMatrixRows()); + } + + if (arraySizes) { + const int maxSize = 11; + char buf[maxSize]; + for (int i = 0; i < arraySizes->getNumDims(); ++i) { + if (arraySizes->getDimNode(i)) { + if (arraySizes->getDimNode(i)->getAsSymbolNode()) + snprintf(buf, maxSize, "s%d", arraySizes->getDimNode(i)->getAsSymbolNode()->getId()); + else + snprintf(buf, maxSize, "s%p", arraySizes->getDimNode(i)); + } else + snprintf(buf, maxSize, "%d", arraySizes->getDimSize(i)); + mangledName += '['; + mangledName += buf; + mangledName += ']'; + } + } +} + +#ifndef GLSLANG_WEB + +// +// Dump functions. +// + +void TSymbol::dumpExtensions(TInfoSink& infoSink) const +{ + int numExtensions = getNumExtensions(); + if (numExtensions) { + infoSink.debug << " <"; + + for (int i = 0; i < numExtensions; i++) + infoSink.debug << getExtensions()[i] << ","; + + infoSink.debug << ">"; + } +} + +void TVariable::dump(TInfoSink& infoSink, bool complete) const +{ + if (complete) { + infoSink.debug << getName().c_str() << ": " << type.getCompleteString(); + dumpExtensions(infoSink); + } else { + infoSink.debug << getName().c_str() << ": " << type.getStorageQualifierString() << " " + << type.getBasicTypeString(); + + if (type.isArray()) + infoSink.debug << "[0]"; + } + + infoSink.debug << "\n"; +} + +void TFunction::dump(TInfoSink& infoSink, bool complete) const +{ + if (complete) { + infoSink.debug << getName().c_str() << ": " << returnType.getCompleteString() << " " << getName().c_str() + << "("; + + int numParams = getParamCount(); + for (int i = 0; i < numParams; i++) { + const TParameter ¶m = parameters[i]; + infoSink.debug << param.type->getCompleteString() << " " + << (param.type->isStruct() ? "of " + param.type->getTypeName() + " " : "") + << (param.name ? *param.name : "") << (i < numParams - 1 ? "," : ""); + } + + infoSink.debug << ")"; + dumpExtensions(infoSink); + } else { + infoSink.debug << getName().c_str() << ": " << returnType.getBasicTypeString() << " " + << getMangledName().c_str() << "n"; + } + + infoSink.debug << "\n"; +} + +void TAnonMember::dump(TInfoSink& TInfoSink, bool) const +{ + TInfoSink.debug << "anonymous member " << getMemberNumber() << " of " << getAnonContainer().getName().c_str() + << "\n"; +} + +void TSymbolTableLevel::dump(TInfoSink& infoSink, bool complete) const +{ + tLevel::const_iterator it; + for (it = level.begin(); it != level.end(); ++it) + (*it).second->dump(infoSink, complete); +} + +void TSymbolTable::dump(TInfoSink& infoSink, bool complete) const +{ + for (int level = currentLevel(); level >= 0; --level) { + infoSink.debug << "LEVEL " << level << "\n"; + table[level]->dump(infoSink, complete); + } +} + +#endif + +// +// Functions have buried pointers to delete. +// +TFunction::~TFunction() +{ + for (TParamList::iterator i = parameters.begin(); i != parameters.end(); ++i) + delete (*i).type; +} + +// +// Symbol table levels are a map of pointers to symbols that have to be deleted. +// +TSymbolTableLevel::~TSymbolTableLevel() +{ + for (tLevel::iterator it = level.begin(); it != level.end(); ++it) + delete (*it).second; + + delete [] defaultPrecision; +} + +// +// Change all function entries in the table with the non-mangled name +// to be related to the provided built-in operation. +// +void TSymbolTableLevel::relateToOperator(const char* name, TOperator op) +{ + tLevel::const_iterator candidate = level.lower_bound(name); + while (candidate != level.end()) { + const TString& candidateName = (*candidate).first; + TString::size_type parenAt = candidateName.find_first_of('('); + if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) { + TFunction* function = (*candidate).second->getAsFunction(); + function->relateToOperator(op); + } else + break; + ++candidate; + } +} + +// Make all function overloads of the given name require an extension(s). +// Should only be used for a version/profile that actually needs the extension(s). +void TSymbolTableLevel::setFunctionExtensions(const char* name, int num, const char* const extensions[]) +{ + tLevel::const_iterator candidate = level.lower_bound(name); + while (candidate != level.end()) { + const TString& candidateName = (*candidate).first; + TString::size_type parenAt = candidateName.find_first_of('('); + if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) { + TSymbol* symbol = candidate->second; + symbol->setExtensions(num, extensions); + } else + break; + ++candidate; + } +} + +// +// Make all symbols in this table level read only. +// +void TSymbolTableLevel::readOnly() +{ + for (tLevel::iterator it = level.begin(); it != level.end(); ++it) + (*it).second->makeReadOnly(); +} + +// +// Copy a symbol, but the copy is writable; call readOnly() afterward if that's not desired. +// +TSymbol::TSymbol(const TSymbol& copyOf) +{ + name = NewPoolTString(copyOf.name->c_str()); + uniqueId = copyOf.uniqueId; + writable = true; +} + +TVariable::TVariable(const TVariable& copyOf) : TSymbol(copyOf) +{ + type.deepCopy(copyOf.type); + userType = copyOf.userType; + + // we don't support specialization-constant subtrees in cloned tables, only extensions + constSubtree = nullptr; + extensions = nullptr; + memberExtensions = nullptr; + if (copyOf.getNumExtensions() > 0) + setExtensions(copyOf.getNumExtensions(), copyOf.getExtensions()); + if (copyOf.hasMemberExtensions()) { + for (int m = 0; m < (int)copyOf.type.getStruct()->size(); ++m) { + if (copyOf.getNumMemberExtensions(m) > 0) + setMemberExtensions(m, copyOf.getNumMemberExtensions(m), copyOf.getMemberExtensions(m)); + } + } + + if (! copyOf.constArray.empty()) { + assert(! copyOf.type.isStruct()); + TConstUnionArray newArray(copyOf.constArray, 0, copyOf.constArray.size()); + constArray = newArray; + } +} + +TVariable* TVariable::clone() const +{ + TVariable *variable = new TVariable(*this); + + return variable; +} + +TFunction::TFunction(const TFunction& copyOf) : TSymbol(copyOf) +{ + for (unsigned int i = 0; i < copyOf.parameters.size(); ++i) { + TParameter param; + parameters.push_back(param); + parameters.back().copyParam(copyOf.parameters[i]); + } + + extensions = nullptr; + if (copyOf.getNumExtensions() > 0) + setExtensions(copyOf.getNumExtensions(), copyOf.getExtensions()); + returnType.deepCopy(copyOf.returnType); + mangledName = copyOf.mangledName; + op = copyOf.op; + defined = copyOf.defined; + prototyped = copyOf.prototyped; + implicitThis = copyOf.implicitThis; + illegalImplicitThis = copyOf.illegalImplicitThis; + defaultParamCount = copyOf.defaultParamCount; +} + +TFunction* TFunction::clone() const +{ + TFunction *function = new TFunction(*this); + + return function; +} + +TAnonMember* TAnonMember::clone() const +{ + // Anonymous members of a given block should be cloned at a higher level, + // where they can all be assured to still end up pointing to a single + // copy of the original container. + assert(0); + + return 0; +} + +TSymbolTableLevel* TSymbolTableLevel::clone() const +{ + TSymbolTableLevel *symTableLevel = new TSymbolTableLevel(); + symTableLevel->anonId = anonId; + symTableLevel->thisLevel = thisLevel; + std::vector containerCopied(anonId, false); + tLevel::const_iterator iter; + for (iter = level.begin(); iter != level.end(); ++iter) { + const TAnonMember* anon = iter->second->getAsAnonMember(); + if (anon) { + // Insert all the anonymous members of this same container at once, + // avoid inserting the remaining members in the future, once this has been done, + // allowing them to all be part of the same new container. + if (! containerCopied[anon->getAnonId()]) { + TVariable* container = anon->getAnonContainer().clone(); + container->changeName(NewPoolTString("")); + // insert the container and all its members + symTableLevel->insert(*container, false); + containerCopied[anon->getAnonId()] = true; + } + } else + symTableLevel->insert(*iter->second->clone(), false); + } + + return symTableLevel; +} + +void TSymbolTable::copyTable(const TSymbolTable& copyOf) +{ + assert(adoptedLevels == copyOf.adoptedLevels); + + uniqueId = copyOf.uniqueId; + noBuiltInRedeclarations = copyOf.noBuiltInRedeclarations; + separateNameSpaces = copyOf.separateNameSpaces; + for (unsigned int i = copyOf.adoptedLevels; i < copyOf.table.size(); ++i) + table.push_back(copyOf.table[i]->clone()); +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/SymbolTable.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/SymbolTable.h new file mode 100644 index 000000000..40ca3da53 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/SymbolTable.h @@ -0,0 +1,885 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _SYMBOL_TABLE_INCLUDED_ +#define _SYMBOL_TABLE_INCLUDED_ + +// +// Symbol table for parsing. Has these design characteristics: +// +// * Same symbol table can be used to compile many shaders, to preserve +// effort of creating and loading with the large numbers of built-in +// symbols. +// +// --> This requires a copy mechanism, so initial pools used to create +// the shared information can be popped. Done through "clone" +// methods. +// +// * Name mangling will be used to give each function a unique name +// so that symbol table lookups are never ambiguous. This allows +// a simpler symbol table structure. +// +// * Pushing and popping of scope, so symbol table will really be a stack +// of symbol tables. Searched from the top, with new inserts going into +// the top. +// +// * Constants: Compile time constant symbols will keep their values +// in the symbol table. The parser can substitute constants at parse +// time, including doing constant folding and constant propagation. +// +// * No temporaries: Temporaries made from operations (+, --, .xy, etc.) +// are tracked in the intermediate representation, not the symbol table. +// + +#include "../Include/Common.h" +#include "../Include/intermediate.h" +#include "../Include/InfoSink.h" + +namespace glslang { + +// +// Symbol base class. (Can build functions or variables out of these...) +// + +class TVariable; +class TFunction; +class TAnonMember; + +typedef TVector TExtensionList; + +class TSymbol { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + explicit TSymbol(const TString *n) : name(n), extensions(0), writable(true) { } + virtual TSymbol* clone() const = 0; + virtual ~TSymbol() { } // rely on all symbol owned memory coming from the pool + + virtual const TString& getName() const { return *name; } + virtual void changeName(const TString* newName) { name = newName; } + virtual void addPrefix(const char* prefix) + { + TString newName(prefix); + newName.append(*name); + changeName(NewPoolTString(newName.c_str())); + } + virtual const TString& getMangledName() const { return getName(); } + virtual TFunction* getAsFunction() { return 0; } + virtual const TFunction* getAsFunction() const { return 0; } + virtual TVariable* getAsVariable() { return 0; } + virtual const TVariable* getAsVariable() const { return 0; } + virtual const TAnonMember* getAsAnonMember() const { return 0; } + virtual const TType& getType() const = 0; + virtual TType& getWritableType() = 0; + virtual void setUniqueId(int id) { uniqueId = id; } + virtual int getUniqueId() const { return uniqueId; } + virtual void setExtensions(int numExts, const char* const exts[]) + { + assert(extensions == 0); + assert(numExts > 0); + extensions = NewPoolObject(extensions); + for (int e = 0; e < numExts; ++e) + extensions->push_back(exts[e]); + } + virtual int getNumExtensions() const { return extensions == nullptr ? 0 : (int)extensions->size(); } + virtual const char** getExtensions() const { return extensions->data(); } + +#ifndef GLSLANG_WEB + virtual void dump(TInfoSink& infoSink, bool complete = false) const = 0; + void dumpExtensions(TInfoSink& infoSink) const; +#endif + + virtual bool isReadOnly() const { return ! writable; } + virtual void makeReadOnly() { writable = false; } + +protected: + explicit TSymbol(const TSymbol&); + TSymbol& operator=(const TSymbol&); + + const TString *name; + unsigned int uniqueId; // For cross-scope comparing during code generation + + // For tracking what extensions must be present + // (don't use if correct version/profile is present). + TExtensionList* extensions; // an array of pointers to existing constant char strings + + // + // N.B.: Non-const functions that will be generally used should assert on this, + // to avoid overwriting shared symbol-table information. + // + bool writable; +}; + +// +// Variable class, meaning a symbol that's not a function. +// +// There could be a separate class hierarchy for Constant variables; +// Only one of int, bool, or float, (or none) is correct for +// any particular use, but it's easy to do this way, and doesn't +// seem worth having separate classes, and "getConst" can't simply return +// different values for different types polymorphically, so this is +// just simple and pragmatic. +// +class TVariable : public TSymbol { +public: + TVariable(const TString *name, const TType& t, bool uT = false ) + : TSymbol(name), + userType(uT), + constSubtree(nullptr), + memberExtensions(nullptr), + anonId(-1) + { type.shallowCopy(t); } + virtual TVariable* clone() const; + virtual ~TVariable() { } + + virtual TVariable* getAsVariable() { return this; } + virtual const TVariable* getAsVariable() const { return this; } + virtual const TType& getType() const { return type; } + virtual TType& getWritableType() { assert(writable); return type; } + virtual bool isUserType() const { return userType; } + virtual const TConstUnionArray& getConstArray() const { return constArray; } + virtual TConstUnionArray& getWritableConstArray() { assert(writable); return constArray; } + virtual void setConstArray(const TConstUnionArray& array) { constArray = array; } + virtual void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; } + virtual TIntermTyped* getConstSubtree() const { return constSubtree; } + virtual void setAnonId(int i) { anonId = i; } + virtual int getAnonId() const { return anonId; } + + virtual void setMemberExtensions(int member, int numExts, const char* const exts[]) + { + assert(type.isStruct()); + assert(numExts > 0); + if (memberExtensions == nullptr) { + memberExtensions = NewPoolObject(memberExtensions); + memberExtensions->resize(type.getStruct()->size()); + } + for (int e = 0; e < numExts; ++e) + (*memberExtensions)[member].push_back(exts[e]); + } + virtual bool hasMemberExtensions() const { return memberExtensions != nullptr; } + virtual int getNumMemberExtensions(int member) const + { + return memberExtensions == nullptr ? 0 : (int)(*memberExtensions)[member].size(); + } + virtual const char** getMemberExtensions(int member) const { return (*memberExtensions)[member].data(); } + +#ifndef GLSLANG_WEB + virtual void dump(TInfoSink& infoSink, bool complete = false) const; +#endif + +protected: + explicit TVariable(const TVariable&); + TVariable& operator=(const TVariable&); + + TType type; + bool userType; + + // we are assuming that Pool Allocator will free the memory allocated to unionArray + // when this object is destroyed + + TConstUnionArray constArray; // for compile-time constant value + TIntermTyped* constSubtree; // for specialization constant computation + TVector* memberExtensions; // per-member extension list, allocated only when needed + int anonId; // the ID used for anonymous blocks: TODO: see if uniqueId could serve a dual purpose +}; + +// +// The function sub-class of symbols and the parser will need to +// share this definition of a function parameter. +// +struct TParameter { + TString *name; + TType* type; + TIntermTyped* defaultValue; + void copyParam(const TParameter& param) + { + if (param.name) + name = NewPoolTString(param.name->c_str()); + else + name = 0; + type = param.type->clone(); + defaultValue = param.defaultValue; + } + TBuiltInVariable getDeclaredBuiltIn() const { return type->getQualifier().declaredBuiltIn; } +}; + +// +// The function sub-class of a symbol. +// +class TFunction : public TSymbol { +public: + explicit TFunction(TOperator o) : + TSymbol(0), + op(o), + defined(false), prototyped(false), implicitThis(false), illegalImplicitThis(false), defaultParamCount(0) { } + TFunction(const TString *name, const TType& retType, TOperator tOp = EOpNull) : + TSymbol(name), + mangledName(*name + '('), + op(tOp), + defined(false), prototyped(false), implicitThis(false), illegalImplicitThis(false), defaultParamCount(0) + { + returnType.shallowCopy(retType); + declaredBuiltIn = retType.getQualifier().builtIn; + } + virtual TFunction* clone() const override; + virtual ~TFunction(); + + virtual TFunction* getAsFunction() override { return this; } + virtual const TFunction* getAsFunction() const override { return this; } + + // Install 'p' as the (non-'this') last parameter. + // Non-'this' parameters are reflected in both the list of parameters and the + // mangled name. + virtual void addParameter(TParameter& p) + { + assert(writable); + parameters.push_back(p); + p.type->appendMangledName(mangledName); + + if (p.defaultValue != nullptr) + defaultParamCount++; + } + + // Install 'this' as the first parameter. + // 'this' is reflected in the list of parameters, but not the mangled name. + virtual void addThisParameter(TType& type, const char* name) + { + TParameter p = { NewPoolTString(name), new TType, nullptr }; + p.type->shallowCopy(type); + parameters.insert(parameters.begin(), p); + } + + virtual void addPrefix(const char* prefix) override + { + TSymbol::addPrefix(prefix); + mangledName.insert(0, prefix); + } + + virtual void removePrefix(const TString& prefix) + { + assert(mangledName.compare(0, prefix.size(), prefix) == 0); + mangledName.erase(0, prefix.size()); + } + + virtual const TString& getMangledName() const override { return mangledName; } + virtual const TType& getType() const override { return returnType; } + virtual TBuiltInVariable getDeclaredBuiltInType() const { return declaredBuiltIn; } + virtual TType& getWritableType() override { return returnType; } + virtual void relateToOperator(TOperator o) { assert(writable); op = o; } + virtual TOperator getBuiltInOp() const { return op; } + virtual void setDefined() { assert(writable); defined = true; } + virtual bool isDefined() const { return defined; } + virtual void setPrototyped() { assert(writable); prototyped = true; } + virtual bool isPrototyped() const { return prototyped; } + virtual void setImplicitThis() { assert(writable); implicitThis = true; } + virtual bool hasImplicitThis() const { return implicitThis; } + virtual void setIllegalImplicitThis() { assert(writable); illegalImplicitThis = true; } + virtual bool hasIllegalImplicitThis() const { return illegalImplicitThis; } + + // Return total number of parameters + virtual int getParamCount() const { return static_cast(parameters.size()); } + // Return number of parameters with default values. + virtual int getDefaultParamCount() const { return defaultParamCount; } + // Return number of fixed parameters (without default values) + virtual int getFixedParamCount() const { return getParamCount() - getDefaultParamCount(); } + + virtual TParameter& operator[](int i) { assert(writable); return parameters[i]; } + virtual const TParameter& operator[](int i) const { return parameters[i]; } + +#ifndef GLSLANG_WEB + virtual void dump(TInfoSink& infoSink, bool complete = false) const override; +#endif + +protected: + explicit TFunction(const TFunction&); + TFunction& operator=(const TFunction&); + + typedef TVector TParamList; + TParamList parameters; + TType returnType; + TBuiltInVariable declaredBuiltIn; + + TString mangledName; + TOperator op; + bool defined; + bool prototyped; + bool implicitThis; // True if this function is allowed to see all members of 'this' + bool illegalImplicitThis; // True if this function is not supposed to have access to dynamic members of 'this', + // even if it finds member variables in the symbol table. + // This is important for a static member function that has member variables in scope, + // but is not allowed to use them, or see hidden symbols instead. + int defaultParamCount; +}; + +// +// Members of anonymous blocks are a kind of TSymbol. They are not hidden in +// the symbol table behind a container; rather they are visible and point to +// their anonymous container. (The anonymous container is found through the +// member, not the other way around.) +// +class TAnonMember : public TSymbol { +public: + TAnonMember(const TString* n, unsigned int m, TVariable& a, int an) : TSymbol(n), anonContainer(a), memberNumber(m), anonId(an) { } + virtual TAnonMember* clone() const override; + virtual ~TAnonMember() { } + + virtual const TAnonMember* getAsAnonMember() const override { return this; } + virtual const TVariable& getAnonContainer() const { return anonContainer; } + virtual unsigned int getMemberNumber() const { return memberNumber; } + + virtual const TType& getType() const override + { + const TTypeList& types = *anonContainer.getType().getStruct(); + return *types[memberNumber].type; + } + + virtual TType& getWritableType() override + { + assert(writable); + const TTypeList& types = *anonContainer.getType().getStruct(); + return *types[memberNumber].type; + } + + virtual void setExtensions(int numExts, const char* const exts[]) override + { + anonContainer.setMemberExtensions(memberNumber, numExts, exts); + } + virtual int getNumExtensions() const override { return anonContainer.getNumMemberExtensions(memberNumber); } + virtual const char** getExtensions() const override { return anonContainer.getMemberExtensions(memberNumber); } + + virtual int getAnonId() const { return anonId; } +#ifndef GLSLANG_WEB + virtual void dump(TInfoSink& infoSink, bool complete = false) const override; +#endif + +protected: + explicit TAnonMember(const TAnonMember&); + TAnonMember& operator=(const TAnonMember&); + + TVariable& anonContainer; + unsigned int memberNumber; + int anonId; +}; + +class TSymbolTableLevel { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + TSymbolTableLevel() : defaultPrecision(0), anonId(0), thisLevel(false) { } + ~TSymbolTableLevel(); + + bool insert(TSymbol& symbol, bool separateNameSpaces) + { + // + // returning true means symbol was added to the table with no semantic errors + // + const TString& name = symbol.getName(); + if (name == "") { + symbol.getAsVariable()->setAnonId(anonId++); + // An empty name means an anonymous container, exposing its members to the external scope. + // Give it a name and insert its members in the symbol table, pointing to the container. + char buf[20]; + snprintf(buf, 20, "%s%d", AnonymousPrefix, symbol.getAsVariable()->getAnonId()); + symbol.changeName(NewPoolTString(buf)); + + return insertAnonymousMembers(symbol, 0); + } else { + // Check for redefinition errors: + // - STL itself will tell us if there is a direct name collision, with name mangling, at this level + // - additionally, check for function-redefining-variable name collisions + const TString& insertName = symbol.getMangledName(); + if (symbol.getAsFunction()) { + // make sure there isn't a variable of this name + if (! separateNameSpaces && level.find(name) != level.end()) + return false; + + // insert, and whatever happens is okay + level.insert(tLevelPair(insertName, &symbol)); + + return true; + } else + return level.insert(tLevelPair(insertName, &symbol)).second; + } + } + + // Add more members to an already inserted aggregate object + bool amend(TSymbol& symbol, int firstNewMember) + { + // See insert() for comments on basic explanation of insert. + // This operates similarly, but more simply. + // Only supporting amend of anonymous blocks so far. + if (IsAnonymous(symbol.getName())) + return insertAnonymousMembers(symbol, firstNewMember); + else + return false; + } + + bool insertAnonymousMembers(TSymbol& symbol, int firstMember) + { + const TTypeList& types = *symbol.getAsVariable()->getType().getStruct(); + for (unsigned int m = firstMember; m < types.size(); ++m) { + TAnonMember* member = new TAnonMember(&types[m].type->getFieldName(), m, *symbol.getAsVariable(), symbol.getAsVariable()->getAnonId()); + if (! level.insert(tLevelPair(member->getMangledName(), member)).second) + return false; + } + + return true; + } + + TSymbol* find(const TString& name) const + { + tLevel::const_iterator it = level.find(name); + if (it == level.end()) + return 0; + else + return (*it).second; + } + + void findFunctionNameList(const TString& name, TVector& list) + { + size_t parenAt = name.find_first_of('('); + TString base(name, 0, parenAt + 1); + + tLevel::const_iterator begin = level.lower_bound(base); + base[parenAt] = ')'; // assume ')' is lexically after '(' + tLevel::const_iterator end = level.upper_bound(base); + for (tLevel::const_iterator it = begin; it != end; ++it) + list.push_back(it->second->getAsFunction()); + } + + // See if there is already a function in the table having the given non-function-style name. + bool hasFunctionName(const TString& name) const + { + tLevel::const_iterator candidate = level.lower_bound(name); + if (candidate != level.end()) { + const TString& candidateName = (*candidate).first; + TString::size_type parenAt = candidateName.find_first_of('('); + if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) + + return true; + } + + return false; + } + + // See if there is a variable at this level having the given non-function-style name. + // Return true if name is found, and set variable to true if the name was a variable. + bool findFunctionVariableName(const TString& name, bool& variable) const + { + tLevel::const_iterator candidate = level.lower_bound(name); + if (candidate != level.end()) { + const TString& candidateName = (*candidate).first; + TString::size_type parenAt = candidateName.find_first_of('('); + if (parenAt == candidateName.npos) { + // not a mangled name + if (candidateName == name) { + // found a variable name match + variable = true; + return true; + } + } else { + // a mangled name + if (candidateName.compare(0, parenAt, name) == 0) { + // found a function name match + variable = false; + return true; + } + } + } + + return false; + } + + // Use this to do a lazy 'push' of precision defaults the first time + // a precision statement is seen in a new scope. Leave it at 0 for + // when no push was needed. Thus, it is not the current defaults, + // it is what to restore the defaults to when popping a level. + void setPreviousDefaultPrecisions(const TPrecisionQualifier *p) + { + // can call multiple times at one scope, will only latch on first call, + // as we're tracking the previous scope's values, not the current values + if (defaultPrecision != 0) + return; + + defaultPrecision = new TPrecisionQualifier[EbtNumTypes]; + for (int t = 0; t < EbtNumTypes; ++t) + defaultPrecision[t] = p[t]; + } + + void getPreviousDefaultPrecisions(TPrecisionQualifier *p) + { + // can be called for table level pops that didn't set the + // defaults + if (defaultPrecision == 0 || p == 0) + return; + + for (int t = 0; t < EbtNumTypes; ++t) + p[t] = defaultPrecision[t]; + } + + void relateToOperator(const char* name, TOperator op); + void setFunctionExtensions(const char* name, int num, const char* const extensions[]); +#ifndef GLSLANG_WEB + void dump(TInfoSink& infoSink, bool complete = false) const; +#endif + TSymbolTableLevel* clone() const; + void readOnly(); + + void setThisLevel() { thisLevel = true; } + bool isThisLevel() const { return thisLevel; } + +protected: + explicit TSymbolTableLevel(TSymbolTableLevel&); + TSymbolTableLevel& operator=(TSymbolTableLevel&); + + typedef std::map, pool_allocator > > tLevel; + typedef const tLevel::value_type tLevelPair; + typedef std::pair tInsertResult; + + tLevel level; // named mappings + TPrecisionQualifier *defaultPrecision; + int anonId; + bool thisLevel; // True if this level of the symbol table is a structure scope containing member function + // that are supposed to see anonymous access to member variables. +}; + +class TSymbolTable { +public: + TSymbolTable() : uniqueId(0), noBuiltInRedeclarations(false), separateNameSpaces(false), adoptedLevels(0) + { + // + // This symbol table cannot be used until push() is called. + // + } + ~TSymbolTable() + { + // this can be called explicitly; safest to code it so it can be called multiple times + + // don't deallocate levels passed in from elsewhere + while (table.size() > adoptedLevels) + pop(0); + } + + void adoptLevels(TSymbolTable& symTable) + { + for (unsigned int level = 0; level < symTable.table.size(); ++level) { + table.push_back(symTable.table[level]); + ++adoptedLevels; + } + uniqueId = symTable.uniqueId; + noBuiltInRedeclarations = symTable.noBuiltInRedeclarations; + separateNameSpaces = symTable.separateNameSpaces; + } + + // + // While level adopting is generic, the methods below enact a the following + // convention for levels: + // 0: common built-ins shared across all stages, all compiles, only one copy for all symbol tables + // 1: per-stage built-ins, shared across all compiles, but a different copy per stage + // 2: built-ins specific to a compile, like resources that are context-dependent, or redeclared built-ins + // 3: user-shader globals + // +protected: + static const int globalLevel = 3; + bool isSharedLevel(int level) { return level <= 1; } // exclude all per-compile levels + bool isBuiltInLevel(int level) { return level <= 2; } // exclude user globals + bool isGlobalLevel(int level) { return level <= globalLevel; } // include user globals +public: + bool isEmpty() { return table.size() == 0; } + bool atBuiltInLevel() { return isBuiltInLevel(currentLevel()); } + bool atGlobalLevel() { return isGlobalLevel(currentLevel()); } + + void setNoBuiltInRedeclarations() { noBuiltInRedeclarations = true; } + void setSeparateNameSpaces() { separateNameSpaces = true; } + + void push() + { + table.push_back(new TSymbolTableLevel); + } + + // Make a new symbol-table level to represent the scope introduced by a structure + // containing member functions, such that the member functions can find anonymous + // references to member variables. + // + // 'thisSymbol' should have a name of "" to trigger anonymous structure-member + // symbol finds. + void pushThis(TSymbol& thisSymbol) + { + assert(thisSymbol.getName().size() == 0); + table.push_back(new TSymbolTableLevel); + table.back()->setThisLevel(); + insert(thisSymbol); + } + + void pop(TPrecisionQualifier *p) + { + table[currentLevel()]->getPreviousDefaultPrecisions(p); + delete table.back(); + table.pop_back(); + } + + // + // Insert a visible symbol into the symbol table so it can + // be found later by name. + // + // Returns false if the was a name collision. + // + bool insert(TSymbol& symbol) + { + symbol.setUniqueId(++uniqueId); + + // make sure there isn't a function of this variable name + if (! separateNameSpaces && ! symbol.getAsFunction() && table[currentLevel()]->hasFunctionName(symbol.getName())) + return false; + + // check for not overloading or redefining a built-in function + if (noBuiltInRedeclarations) { + if (atGlobalLevel() && currentLevel() > 0) { + if (table[0]->hasFunctionName(symbol.getName())) + return false; + if (currentLevel() > 1 && table[1]->hasFunctionName(symbol.getName())) + return false; + } + } + + return table[currentLevel()]->insert(symbol, separateNameSpaces); + } + + // Add more members to an already inserted aggregate object + bool amend(TSymbol& symbol, int firstNewMember) + { + // See insert() for comments on basic explanation of insert. + // This operates similarly, but more simply. + return table[currentLevel()]->amend(symbol, firstNewMember); + } + + // + // To allocate an internal temporary, which will need to be uniquely + // identified by the consumer of the AST, but never need to + // found by doing a symbol table search by name, hence allowed an + // arbitrary name in the symbol with no worry of collision. + // + void makeInternalVariable(TSymbol& symbol) + { + symbol.setUniqueId(++uniqueId); + } + + // + // Copy a variable or anonymous member's structure from a shared level so that + // it can be added (soon after return) to the symbol table where it can be + // modified without impacting other users of the shared table. + // + TSymbol* copyUpDeferredInsert(TSymbol* shared) + { + if (shared->getAsVariable()) { + TSymbol* copy = shared->clone(); + copy->setUniqueId(shared->getUniqueId()); + return copy; + } else { + const TAnonMember* anon = shared->getAsAnonMember(); + assert(anon); + TVariable* container = anon->getAnonContainer().clone(); + container->changeName(NewPoolTString("")); + container->setUniqueId(anon->getAnonContainer().getUniqueId()); + return container; + } + } + + TSymbol* copyUp(TSymbol* shared) + { + TSymbol* copy = copyUpDeferredInsert(shared); + table[globalLevel]->insert(*copy, separateNameSpaces); + if (shared->getAsVariable()) + return copy; + else { + // return the copy of the anonymous member + return table[globalLevel]->find(shared->getName()); + } + } + + // Normal find of a symbol, that can optionally say whether the symbol was found + // at a built-in level or the current top-scope level. + TSymbol* find(const TString& name, bool* builtIn = 0, bool* currentScope = 0, int* thisDepthP = 0) + { + int level = currentLevel(); + TSymbol* symbol; + int thisDepth = 0; + do { + if (table[level]->isThisLevel()) + ++thisDepth; + symbol = table[level]->find(name); + --level; + } while (symbol == nullptr && level >= 0); + level++; + if (builtIn) + *builtIn = isBuiltInLevel(level); + if (currentScope) + *currentScope = isGlobalLevel(currentLevel()) || level == currentLevel(); // consider shared levels as "current scope" WRT user globals + if (thisDepthP != nullptr) { + if (! table[level]->isThisLevel()) + thisDepth = 0; + *thisDepthP = thisDepth; + } + + return symbol; + } + + // Find of a symbol that returns how many layers deep of nested + // structures-with-member-functions ('this' scopes) deep the symbol was + // found in. + TSymbol* find(const TString& name, int& thisDepth) + { + int level = currentLevel(); + TSymbol* symbol; + thisDepth = 0; + do { + if (table[level]->isThisLevel()) + ++thisDepth; + symbol = table[level]->find(name); + --level; + } while (symbol == 0 && level >= 0); + + if (! table[level + 1]->isThisLevel()) + thisDepth = 0; + + return symbol; + } + + bool isFunctionNameVariable(const TString& name) const + { + if (separateNameSpaces) + return false; + + int level = currentLevel(); + do { + bool variable; + bool found = table[level]->findFunctionVariableName(name, variable); + if (found) + return variable; + --level; + } while (level >= 0); + + return false; + } + + void findFunctionNameList(const TString& name, TVector& list, bool& builtIn) + { + // For user levels, return the set found in the first scope with a match + builtIn = false; + int level = currentLevel(); + do { + table[level]->findFunctionNameList(name, list); + --level; + } while (list.empty() && level >= globalLevel); + + if (! list.empty()) + return; + + // Gather across all built-in levels; they don't hide each other + builtIn = true; + do { + table[level]->findFunctionNameList(name, list); + --level; + } while (level >= 0); + } + + void relateToOperator(const char* name, TOperator op) + { + for (unsigned int level = 0; level < table.size(); ++level) + table[level]->relateToOperator(name, op); + } + + void setFunctionExtensions(const char* name, int num, const char* const extensions[]) + { + for (unsigned int level = 0; level < table.size(); ++level) + table[level]->setFunctionExtensions(name, num, extensions); + } + + void setVariableExtensions(const char* name, int numExts, const char* const extensions[]) + { + TSymbol* symbol = find(TString(name)); + if (symbol == nullptr) + return; + + symbol->setExtensions(numExts, extensions); + } + + void setVariableExtensions(const char* blockName, const char* name, int numExts, const char* const extensions[]) + { + TSymbol* symbol = find(TString(blockName)); + if (symbol == nullptr) + return; + TVariable* variable = symbol->getAsVariable(); + assert(variable != nullptr); + + const TTypeList& structure = *variable->getAsVariable()->getType().getStruct(); + for (int member = 0; member < (int)structure.size(); ++member) { + if (structure[member].type->getFieldName().compare(name) == 0) { + variable->setMemberExtensions(member, numExts, extensions); + return; + } + } + } + + int getMaxSymbolId() { return uniqueId; } +#ifndef GLSLANG_WEB + void dump(TInfoSink& infoSink, bool complete = false) const; +#endif + void copyTable(const TSymbolTable& copyOf); + + void setPreviousDefaultPrecisions(TPrecisionQualifier *p) { table[currentLevel()]->setPreviousDefaultPrecisions(p); } + + void readOnly() + { + for (unsigned int level = 0; level < table.size(); ++level) + table[level]->readOnly(); + } + +protected: + TSymbolTable(TSymbolTable&); + TSymbolTable& operator=(TSymbolTableLevel&); + + int currentLevel() const { return static_cast(table.size()) - 1; } + + std::vector table; + int uniqueId; // for unique identification in code generation + bool noBuiltInRedeclarations; + bool separateNameSpaces; + unsigned int adoptedLevels; +}; + +} // end namespace glslang + +#endif // _SYMBOL_TABLE_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Versions.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Versions.cpp new file mode 100644 index 000000000..be2d9cef8 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Versions.cpp @@ -0,0 +1,1205 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2020 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Help manage multiple profiles, versions, extensions etc. +// +// These don't return error codes, as the presumption is parsing will +// always continue as if the tested feature were enabled, and thus there +// is no error recovery needed. +// + +// +// HOW TO add a feature enabled by an extension. +// +// To add a new hypothetical "Feature F" to the front end, where an extension +// "XXX_extension_X" can be used to enable the feature, do the following. +// +// OVERVIEW: Specific features are what are error-checked for, not +// extensions: A specific Feature F might be enabled by an extension, or a +// particular version in a particular profile, or a stage, or combinations, etc. +// +// The basic mechanism is to use the following to "declare" all the things that +// enable/disable Feature F, in a code path that implements Feature F: +// +// requireProfile() +// profileRequires() +// requireStage() +// checkDeprecated() +// requireNotRemoved() +// requireExtensions() +// +// Typically, only the first two calls are needed. They go into a code path that +// implements Feature F, and will log the proper error/warning messages. Parsing +// will then always continue as if the tested feature was enabled. +// +// There is typically no if-testing or conditional parsing, just insertion of the calls above. +// However, if symbols specific to the extension are added (step 5), they will +// only be added under tests that the minimum version and profile are present. +// +// 1) Add a symbol name for the extension string at the bottom of Versions.h: +// +// const char* const XXX_extension_X = "XXX_extension_X"; +// +// 2) Add extension initialization to TParseVersions::initializeExtensionBehavior(), +// the first function below: +// +// extensionBehavior[XXX_extension_X] = EBhDisable; +// +// 3) Add any preprocessor directives etc. in the next function, TParseVersions::getPreamble(): +// +// "#define XXX_extension_X 1\n" +// +// The new-line is important, as that ends preprocess tokens. +// +// 4) Insert a profile check in the feature's path (unless all profiles support the feature, +// for some version level). That is, call requireProfile() to constrain the profiles, e.g.: +// +// // ... in a path specific to Feature F... +// requireProfile(loc, +// ECoreProfile | ECompatibilityProfile, +// "Feature F"); +// +// 5) For each profile that supports the feature, insert version/extension checks: +// +// The mostly likely scenario is that Feature F can only be used with a +// particular profile if XXX_extension_X is present or the version is +// high enough that the core specification already incorporated it. +// +// // following the requireProfile() call... +// profileRequires(loc, +// ECoreProfile | ECompatibilityProfile, +// 420, // 0 if no version incorporated the feature into the core spec. +// XXX_extension_X, // can be a list of extensions that all add the feature +// "Feature F Description"); +// +// This allows the feature if either A) one of the extensions is enabled or +// B) the version is high enough. If no version yet incorporates the feature +// into core, pass in 0. +// +// This can be called multiple times, if different profiles support the +// feature starting at different version numbers or with different +// extensions. +// +// This must be called for each profile allowed by the initial call to requireProfile(). +// +// Profiles are all masks, which can be "or"-ed together. +// +// ENoProfile +// ECoreProfile +// ECompatibilityProfile +// EEsProfile +// +// The ENoProfile profile is only for desktop, before profiles showed up in version 150; +// All other #version with no profile default to either es or core, and so have profiles. +// +// You can select all but a particular profile using ~. The following basically means "desktop": +// +// ~EEsProfile +// +// 6) If built-in symbols are added by the extension, add them in Initialize.cpp: Their use +// will be automatically error checked against the extensions enabled at that moment. +// see the comment at the top of Initialize.cpp for where to put them. Establish them at +// the earliest release that supports the extension. Then, tag them with the +// set of extensions that both enable them and are necessary, given the version of the symbol +// table. (There is a different symbol table for each version.) +// + +#include "parseVersions.h" +#include "localintermediate.h" + +namespace glslang { + +#ifndef GLSLANG_WEB + +// +// Initialize all extensions, almost always to 'disable', as once their features +// are incorporated into a core version, their features are supported through allowing that +// core version, not through a pseudo-enablement of the extension. +// +void TParseVersions::initializeExtensionBehavior() +{ + extensionBehavior[E_GL_OES_texture_3D] = EBhDisable; + extensionBehavior[E_GL_OES_standard_derivatives] = EBhDisable; + extensionBehavior[E_GL_EXT_frag_depth] = EBhDisable; + extensionBehavior[E_GL_OES_EGL_image_external] = EBhDisable; + extensionBehavior[E_GL_OES_EGL_image_external_essl3] = EBhDisable; + extensionBehavior[E_GL_EXT_YUV_target] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_texture_lod] = EBhDisable; + extensionBehavior[E_GL_EXT_shadow_samplers] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_rectangle] = EBhDisable; + extensionBehavior[E_GL_3DL_array_objects] = EBhDisable; + extensionBehavior[E_GL_ARB_shading_language_420pack] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_gather] = EBhDisable; + extensionBehavior[E_GL_ARB_gpu_shader5] = EBhDisablePartial; + extensionBehavior[E_GL_ARB_separate_shader_objects] = EBhDisable; + extensionBehavior[E_GL_ARB_compute_shader] = EBhDisable; + extensionBehavior[E_GL_ARB_tessellation_shader] = EBhDisable; + extensionBehavior[E_GL_ARB_enhanced_layouts] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_cube_map_array] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_multisample] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_texture_lod] = EBhDisable; + extensionBehavior[E_GL_ARB_explicit_attrib_location] = EBhDisable; + extensionBehavior[E_GL_ARB_explicit_uniform_location] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_image_load_store] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_atomic_counters] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_draw_parameters] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_group_vote] = EBhDisable; + extensionBehavior[E_GL_ARB_derivative_control] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_texture_image_samples] = EBhDisable; + extensionBehavior[E_GL_ARB_viewport_array] = EBhDisable; + extensionBehavior[E_GL_ARB_gpu_shader_int64] = EBhDisable; + extensionBehavior[E_GL_ARB_gpu_shader_fp64] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_ballot] = EBhDisable; + extensionBehavior[E_GL_ARB_sparse_texture2] = EBhDisable; + extensionBehavior[E_GL_ARB_sparse_texture_clamp] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_stencil_export] = EBhDisable; +// extensionBehavior[E_GL_ARB_cull_distance] = EBhDisable; // present for 4.5, but need extension control over block members + extensionBehavior[E_GL_ARB_post_depth_coverage] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_viewport_layer_array] = EBhDisable; + extensionBehavior[E_GL_ARB_fragment_shader_interlock] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_clock] = EBhDisable; + extensionBehavior[E_GL_ARB_uniform_buffer_object] = EBhDisable; + extensionBehavior[E_GL_ARB_sample_shading] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_bit_encoding] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_image_size] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_storage_buffer_object] = EBhDisable; + extensionBehavior[E_GL_ARB_shading_language_packing] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_query_lod] = EBhDisable; + extensionBehavior[E_GL_ARB_vertex_attrib_64bit] = EBhDisable; + + extensionBehavior[E_GL_KHR_shader_subgroup_basic] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_vote] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_arithmetic] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_ballot] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_shuffle] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_shuffle_relative] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_clustered] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_quad] = EBhDisable; + extensionBehavior[E_GL_KHR_memory_scope_semantics] = EBhDisable; + + extensionBehavior[E_GL_EXT_shader_atomic_int64] = EBhDisable; + + extensionBehavior[E_GL_EXT_shader_non_constant_global_initializers] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_image_load_formatted] = EBhDisable; + extensionBehavior[E_GL_EXT_post_depth_coverage] = EBhDisable; + extensionBehavior[E_GL_EXT_control_flow_attributes] = EBhDisable; + extensionBehavior[E_GL_EXT_nonuniform_qualifier] = EBhDisable; + extensionBehavior[E_GL_EXT_samplerless_texture_functions] = EBhDisable; + extensionBehavior[E_GL_EXT_scalar_block_layout] = EBhDisable; + extensionBehavior[E_GL_EXT_fragment_invocation_density] = EBhDisable; + extensionBehavior[E_GL_EXT_buffer_reference] = EBhDisable; + extensionBehavior[E_GL_EXT_buffer_reference2] = EBhDisable; + extensionBehavior[E_GL_EXT_buffer_reference_uvec2] = EBhDisable; + extensionBehavior[E_GL_EXT_demote_to_helper_invocation] = EBhDisable; + extensionBehavior[E_GL_EXT_debug_printf] = EBhDisable; + + extensionBehavior[E_GL_EXT_shader_16bit_storage] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_8bit_storage] = EBhDisable; + + // #line and #include + extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhEnable; + extensionBehavior[E_GL_GOOGLE_include_directive] = EBhEnable; + + extensionBehavior[E_GL_AMD_shader_ballot] = EBhDisable; + extensionBehavior[E_GL_AMD_shader_trinary_minmax] = EBhDisable; + extensionBehavior[E_GL_AMD_shader_explicit_vertex_parameter] = EBhDisable; + extensionBehavior[E_GL_AMD_gcn_shader] = EBhDisable; + extensionBehavior[E_GL_AMD_gpu_shader_half_float] = EBhDisable; + extensionBehavior[E_GL_AMD_texture_gather_bias_lod] = EBhDisable; + extensionBehavior[E_GL_AMD_gpu_shader_int16] = EBhDisable; + extensionBehavior[E_GL_AMD_shader_image_load_store_lod] = EBhDisable; + extensionBehavior[E_GL_AMD_shader_fragment_mask] = EBhDisable; + extensionBehavior[E_GL_AMD_gpu_shader_half_float_fetch] = EBhDisable; + + extensionBehavior[E_GL_INTEL_shader_integer_functions2] = EBhDisable; + + extensionBehavior[E_GL_NV_sample_mask_override_coverage] = EBhDisable; + extensionBehavior[E_SPV_NV_geometry_shader_passthrough] = EBhDisable; + extensionBehavior[E_GL_NV_viewport_array2] = EBhDisable; + extensionBehavior[E_GL_NV_stereo_view_rendering] = EBhDisable; + extensionBehavior[E_GL_NVX_multiview_per_view_attributes] = EBhDisable; + extensionBehavior[E_GL_NV_shader_atomic_int64] = EBhDisable; + extensionBehavior[E_GL_NV_conservative_raster_underestimation] = EBhDisable; + extensionBehavior[E_GL_NV_shader_noperspective_interpolation] = EBhDisable; + extensionBehavior[E_GL_NV_shader_subgroup_partitioned] = EBhDisable; + extensionBehavior[E_GL_NV_shading_rate_image] = EBhDisable; + extensionBehavior[E_GL_NV_ray_tracing] = EBhDisable; + extensionBehavior[E_GL_NV_fragment_shader_barycentric] = EBhDisable; + extensionBehavior[E_GL_NV_compute_shader_derivatives] = EBhDisable; + extensionBehavior[E_GL_NV_shader_texture_footprint] = EBhDisable; + extensionBehavior[E_GL_NV_mesh_shader] = EBhDisable; + + extensionBehavior[E_GL_NV_cooperative_matrix] = EBhDisable; + extensionBehavior[E_GL_NV_shader_sm_builtins] = EBhDisable; + extensionBehavior[E_GL_NV_integer_cooperative_matrix] = EBhDisable; + + // AEP + extensionBehavior[E_GL_ANDROID_extension_pack_es31a] = EBhDisable; + extensionBehavior[E_GL_KHR_blend_equation_advanced] = EBhDisable; + extensionBehavior[E_GL_OES_sample_variables] = EBhDisable; + extensionBehavior[E_GL_OES_shader_image_atomic] = EBhDisable; + extensionBehavior[E_GL_OES_shader_multisample_interpolation] = EBhDisable; + extensionBehavior[E_GL_OES_texture_storage_multisample_2d_array] = EBhDisable; + extensionBehavior[E_GL_EXT_geometry_shader] = EBhDisable; + extensionBehavior[E_GL_EXT_geometry_point_size] = EBhDisable; + extensionBehavior[E_GL_EXT_gpu_shader5] = EBhDisable; + extensionBehavior[E_GL_EXT_primitive_bounding_box] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_io_blocks] = EBhDisable; + extensionBehavior[E_GL_EXT_tessellation_shader] = EBhDisable; + extensionBehavior[E_GL_EXT_tessellation_point_size] = EBhDisable; + extensionBehavior[E_GL_EXT_texture_buffer] = EBhDisable; + extensionBehavior[E_GL_EXT_texture_cube_map_array] = EBhDisable; + + // OES matching AEP + extensionBehavior[E_GL_OES_geometry_shader] = EBhDisable; + extensionBehavior[E_GL_OES_geometry_point_size] = EBhDisable; + extensionBehavior[E_GL_OES_gpu_shader5] = EBhDisable; + extensionBehavior[E_GL_OES_primitive_bounding_box] = EBhDisable; + extensionBehavior[E_GL_OES_shader_io_blocks] = EBhDisable; + extensionBehavior[E_GL_OES_tessellation_shader] = EBhDisable; + extensionBehavior[E_GL_OES_tessellation_point_size] = EBhDisable; + extensionBehavior[E_GL_OES_texture_buffer] = EBhDisable; + extensionBehavior[E_GL_OES_texture_cube_map_array] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_integer_mix] = EBhDisable; + + // EXT extensions + extensionBehavior[E_GL_EXT_device_group] = EBhDisable; + extensionBehavior[E_GL_EXT_multiview] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_realtime_clock] = EBhDisable; + extensionBehavior[E_GL_EXT_ray_tracing] = EBhDisable; + extensionBehavior[E_GL_EXT_ray_query] = EBhDisable; + extensionBehavior[E_GL_EXT_ray_flags_primitive_culling] = EBhDisable; + extensionBehavior[E_GL_EXT_blend_func_extended] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_implicit_conversions] = EBhDisable; + + // OVR extensions + extensionBehavior[E_GL_OVR_multiview] = EBhDisable; + extensionBehavior[E_GL_OVR_multiview2] = EBhDisable; + + // explicit types + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int8] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int16] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int32] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int64] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_float16] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_float32] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_float64] = EBhDisable; + + // subgroup extended types + extensionBehavior[E_GL_EXT_shader_subgroup_extended_types_int8] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_subgroup_extended_types_int16] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_subgroup_extended_types_int64] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_subgroup_extended_types_float16] = EBhDisable; +} +#endif // GLSLANG_WEB + +// Get code that is not part of a shared symbol table, is specific to this shader, +// or needed by the preprocessor (which does not use a shared symbol table). +void TParseVersions::getPreamble(std::string& preamble) +{ + if (isEsProfile()) { + preamble = + "#define GL_ES 1\n" + "#define GL_FRAGMENT_PRECISION_HIGH 1\n" +#ifdef GLSLANG_WEB + ; +#else + "#define GL_OES_texture_3D 1\n" + "#define GL_OES_standard_derivatives 1\n" + "#define GL_EXT_frag_depth 1\n" + "#define GL_OES_EGL_image_external 1\n" + "#define GL_OES_EGL_image_external_essl3 1\n" + "#define GL_EXT_YUV_target 1\n" + "#define GL_EXT_shader_texture_lod 1\n" + "#define GL_EXT_shadow_samplers 1\n" + + // AEP + "#define GL_ANDROID_extension_pack_es31a 1\n" + "#define GL_OES_sample_variables 1\n" + "#define GL_OES_shader_image_atomic 1\n" + "#define GL_OES_shader_multisample_interpolation 1\n" + "#define GL_OES_texture_storage_multisample_2d_array 1\n" + "#define GL_EXT_geometry_shader 1\n" + "#define GL_EXT_geometry_point_size 1\n" + "#define GL_EXT_gpu_shader5 1\n" + "#define GL_EXT_primitive_bounding_box 1\n" + "#define GL_EXT_shader_io_blocks 1\n" + "#define GL_EXT_tessellation_shader 1\n" + "#define GL_EXT_tessellation_point_size 1\n" + "#define GL_EXT_texture_buffer 1\n" + "#define GL_EXT_texture_cube_map_array 1\n" + "#define GL_EXT_shader_implicit_conversions 1\n" + "#define GL_EXT_shader_integer_mix 1\n" + "#define GL_EXT_blend_func_extended 1\n" + + // OES matching AEP + "#define GL_OES_geometry_shader 1\n" + "#define GL_OES_geometry_point_size 1\n" + "#define GL_OES_gpu_shader5 1\n" + "#define GL_OES_primitive_bounding_box 1\n" + "#define GL_OES_shader_io_blocks 1\n" + "#define GL_OES_tessellation_shader 1\n" + "#define GL_OES_tessellation_point_size 1\n" + "#define GL_OES_texture_buffer 1\n" + "#define GL_OES_texture_cube_map_array 1\n" + "#define GL_EXT_shader_non_constant_global_initializers 1\n" + ; + + if (isEsProfile() && version >= 300) { + preamble += "#define GL_NV_shader_noperspective_interpolation 1\n"; + } + + } else { + preamble = + "#define GL_FRAGMENT_PRECISION_HIGH 1\n" + "#define GL_ARB_texture_rectangle 1\n" + "#define GL_ARB_shading_language_420pack 1\n" + "#define GL_ARB_texture_gather 1\n" + "#define GL_ARB_gpu_shader5 1\n" + "#define GL_ARB_separate_shader_objects 1\n" + "#define GL_ARB_compute_shader 1\n" + "#define GL_ARB_tessellation_shader 1\n" + "#define GL_ARB_enhanced_layouts 1\n" + "#define GL_ARB_texture_cube_map_array 1\n" + "#define GL_ARB_texture_multisample 1\n" + "#define GL_ARB_shader_texture_lod 1\n" + "#define GL_ARB_explicit_attrib_location 1\n" + "#define GL_ARB_explicit_uniform_location 1\n" + "#define GL_ARB_shader_image_load_store 1\n" + "#define GL_ARB_shader_atomic_counters 1\n" + "#define GL_ARB_shader_draw_parameters 1\n" + "#define GL_ARB_shader_group_vote 1\n" + "#define GL_ARB_derivative_control 1\n" + "#define GL_ARB_shader_texture_image_samples 1\n" + "#define GL_ARB_viewport_array 1\n" + "#define GL_ARB_gpu_shader_int64 1\n" + "#define GL_ARB_gpu_shader_fp64 1\n" + "#define GL_ARB_shader_ballot 1\n" + "#define GL_ARB_sparse_texture2 1\n" + "#define GL_ARB_sparse_texture_clamp 1\n" + "#define GL_ARB_shader_stencil_export 1\n" + "#define GL_ARB_sample_shading 1\n" + "#define GL_ARB_shader_image_size 1\n" + "#define GL_ARB_shading_language_packing 1\n" +// "#define GL_ARB_cull_distance 1\n" // present for 4.5, but need extension control over block members + "#define GL_ARB_post_depth_coverage 1\n" + "#define GL_ARB_fragment_shader_interlock 1\n" + "#define GL_ARB_uniform_buffer_object 1\n" + "#define GL_ARB_shader_bit_encoding 1\n" + "#define GL_ARB_shader_storage_buffer_object 1\n" + "#define GL_ARB_texture_query_lod 1\n" + "#define GL_ARB_vertex_attrib_64bit 1\n" + "#define GL_EXT_shader_non_constant_global_initializers 1\n" + "#define GL_EXT_shader_image_load_formatted 1\n" + "#define GL_EXT_post_depth_coverage 1\n" + "#define GL_EXT_control_flow_attributes 1\n" + "#define GL_EXT_nonuniform_qualifier 1\n" + "#define GL_EXT_shader_16bit_storage 1\n" + "#define GL_EXT_shader_8bit_storage 1\n" + "#define GL_EXT_samplerless_texture_functions 1\n" + "#define GL_EXT_scalar_block_layout 1\n" + "#define GL_EXT_fragment_invocation_density 1\n" + "#define GL_EXT_buffer_reference 1\n" + "#define GL_EXT_buffer_reference2 1\n" + "#define GL_EXT_buffer_reference_uvec2 1\n" + "#define GL_EXT_demote_to_helper_invocation 1\n" + "#define GL_EXT_debug_printf 1\n" + + // GL_KHR_shader_subgroup + "#define GL_KHR_shader_subgroup_basic 1\n" + "#define GL_KHR_shader_subgroup_vote 1\n" + "#define GL_KHR_shader_subgroup_arithmetic 1\n" + "#define GL_KHR_shader_subgroup_ballot 1\n" + "#define GL_KHR_shader_subgroup_shuffle 1\n" + "#define GL_KHR_shader_subgroup_shuffle_relative 1\n" + "#define GL_KHR_shader_subgroup_clustered 1\n" + "#define GL_KHR_shader_subgroup_quad 1\n" + + "#define E_GL_EXT_shader_atomic_int64 1\n" + "#define E_GL_EXT_shader_realtime_clock 1\n" + "#define E_GL_EXT_ray_tracing 1\n" + "#define E_GL_EXT_ray_query 1\n" + "#define E_GL_EXT_ray_flags_primitive_culling 1\n" + + "#define GL_AMD_shader_ballot 1\n" + "#define GL_AMD_shader_trinary_minmax 1\n" + "#define GL_AMD_shader_explicit_vertex_parameter 1\n" + "#define GL_AMD_gcn_shader 1\n" + "#define GL_AMD_gpu_shader_half_float 1\n" + "#define GL_AMD_texture_gather_bias_lod 1\n" + "#define GL_AMD_gpu_shader_int16 1\n" + "#define GL_AMD_shader_image_load_store_lod 1\n" + "#define GL_AMD_shader_fragment_mask 1\n" + "#define GL_AMD_gpu_shader_half_float_fetch 1\n" + + "#define GL_INTEL_shader_integer_functions2 1\n" + + "#define GL_NV_sample_mask_override_coverage 1\n" + "#define GL_NV_geometry_shader_passthrough 1\n" + "#define GL_NV_viewport_array2 1\n" + "#define GL_NV_shader_atomic_int64 1\n" + "#define GL_NV_conservative_raster_underestimation 1\n" + "#define GL_NV_shader_subgroup_partitioned 1\n" + "#define GL_NV_shading_rate_image 1\n" + "#define GL_NV_ray_tracing 1\n" + "#define GL_NV_fragment_shader_barycentric 1\n" + "#define GL_NV_compute_shader_derivatives 1\n" + "#define GL_NV_shader_texture_footprint 1\n" + "#define GL_NV_mesh_shader 1\n" + "#define GL_NV_cooperative_matrix 1\n" + "#define GL_NV_integer_cooperative_matrix 1\n" + + "#define GL_EXT_shader_explicit_arithmetic_types 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_int8 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_int16 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_int32 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_int64 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_float16 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_float32 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_float64 1\n" + + "#define GL_EXT_shader_subgroup_extended_types_int8 1\n" + "#define GL_EXT_shader_subgroup_extended_types_int16 1\n" + "#define GL_EXT_shader_subgroup_extended_types_int64 1\n" + "#define GL_EXT_shader_subgroup_extended_types_float16 1\n" + ; + + if (version >= 150) { + // define GL_core_profile and GL_compatibility_profile + preamble += "#define GL_core_profile 1\n"; + + if (profile == ECompatibilityProfile) + preamble += "#define GL_compatibility_profile 1\n"; + } +#endif // GLSLANG_WEB + } + +#ifndef GLSLANG_WEB + if ((!isEsProfile() && version >= 140) || + (isEsProfile() && version >= 310)) { + preamble += + "#define GL_EXT_device_group 1\n" + "#define GL_EXT_multiview 1\n" + "#define GL_NV_shader_sm_builtins 1\n" + ; + } + + if (version >= 300 /* both ES and non-ES */) { + preamble += + "#define GL_OVR_multiview 1\n" + "#define GL_OVR_multiview2 1\n" + ; + } + + // #line and #include + preamble += + "#define GL_GOOGLE_cpp_style_line_directive 1\n" + "#define GL_GOOGLE_include_directive 1\n" + "#define GL_KHR_blend_equation_advanced 1\n" + ; +#endif + + // #define VULKAN XXXX + const int numberBufSize = 12; + char numberBuf[numberBufSize]; + if (spvVersion.vulkanGlsl > 0) { + preamble += "#define VULKAN "; + snprintf(numberBuf, numberBufSize, "%d", spvVersion.vulkanGlsl); + preamble += numberBuf; + preamble += "\n"; + } + +#ifndef GLSLANG_WEB + // #define GL_SPIRV XXXX + if (spvVersion.openGl > 0) { + preamble += "#define GL_SPIRV "; + snprintf(numberBuf, numberBufSize, "%d", spvVersion.openGl); + preamble += numberBuf; + preamble += "\n"; + } +#endif +} + +// +// Map from stage enum to externally readable text name. +// +const char* StageName(EShLanguage stage) +{ + switch(stage) { + case EShLangVertex: return "vertex"; + case EShLangFragment: return "fragment"; + case EShLangCompute: return "compute"; +#ifndef GLSLANG_WEB + case EShLangTessControl: return "tessellation control"; + case EShLangTessEvaluation: return "tessellation evaluation"; + case EShLangGeometry: return "geometry"; + case EShLangRayGen: return "ray-generation"; + case EShLangIntersect: return "intersection"; + case EShLangAnyHit: return "any-hit"; + case EShLangClosestHit: return "closest-hit"; + case EShLangMiss: return "miss"; + case EShLangCallable: return "callable"; + case EShLangMeshNV: return "mesh"; + case EShLangTaskNV: return "task"; +#endif + default: return "unknown stage"; + } +} + +// +// When to use requireStage() +// +// If only some stages support a feature. +// +// Operation: If the current stage is not present, give an error message. +// +void TParseVersions::requireStage(const TSourceLoc& loc, EShLanguageMask languageMask, const char* featureDesc) +{ + if (((1 << language) & languageMask) == 0) + error(loc, "not supported in this stage:", featureDesc, StageName(language)); +} + +// If only one stage supports a feature, this can be called. But, all supporting stages +// must be specified with one call. +void TParseVersions::requireStage(const TSourceLoc& loc, EShLanguage stage, const char* featureDesc) +{ + requireStage(loc, static_cast(1 << stage), featureDesc); +} + +#ifndef GLSLANG_WEB +// +// When to use requireProfile(): +// +// Use if only some profiles support a feature. However, if within a profile the feature +// is version or extension specific, follow this call with calls to profileRequires(). +// +// Operation: If the current profile is not one of the profileMask, +// give an error message. +// +void TParseVersions::requireProfile(const TSourceLoc& loc, int profileMask, const char* featureDesc) +{ + if (! (profile & profileMask)) + error(loc, "not supported with this profile:", featureDesc, ProfileName(profile)); +} + +// +// When to use profileRequires(): +// +// If a set of profiles have the same requirements for what version or extensions +// are needed to support a feature. +// +// It must be called for each profile that needs protection. Use requireProfile() first +// to reduce that set of profiles. +// +// Operation: Will issue warnings/errors based on the current profile, version, and extension +// behaviors. It only checks extensions when the current profile is one of the profileMask. +// +// A minVersion of 0 means no version of the profileMask support this in core, +// the extension must be present. +// + +// entry point that takes multiple extensions +void TParseVersions::profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, int numExtensions, + const char* const extensions[], const char* featureDesc) +{ + if (profile & profileMask) { + bool okay = minVersion > 0 && version >= minVersion; +#ifndef GLSLANG_WEB + for (int i = 0; i < numExtensions; ++i) { + switch (getExtensionBehavior(extensions[i])) { + case EBhWarn: + infoSink.info.message(EPrefixWarning, ("extension " + TString(extensions[i]) + " is being used for " + featureDesc).c_str(), loc); + // fall through + case EBhRequire: + case EBhEnable: + okay = true; + break; + default: break; // some compilers want this + } + } +#endif + if (! okay) + error(loc, "not supported for this version or the enabled extensions", featureDesc, ""); + } +} + +// entry point for the above that takes a single extension +void TParseVersions::profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, const char* extension, + const char* featureDesc) +{ + profileRequires(loc, profileMask, minVersion, extension ? 1 : 0, &extension, featureDesc); +} + +void TParseVersions::unimplemented(const TSourceLoc& loc, const char* featureDesc) +{ + error(loc, "feature not yet implemented", featureDesc, ""); +} + +// +// Within a set of profiles, see if a feature is deprecated and give an error or warning based on whether +// a future compatibility context is being use. +// +void TParseVersions::checkDeprecated(const TSourceLoc& loc, int profileMask, int depVersion, const char* featureDesc) +{ + if (profile & profileMask) { + if (version >= depVersion) { + if (forwardCompatible) + error(loc, "deprecated, may be removed in future release", featureDesc, ""); + else if (! suppressWarnings()) + infoSink.info.message(EPrefixWarning, (TString(featureDesc) + " deprecated in version " + + String(depVersion) + "; may be removed in future release").c_str(), loc); + } + } +} + +// +// Within a set of profiles, see if a feature has now been removed and if so, give an error. +// The version argument is the first version no longer having the feature. +// +void TParseVersions::requireNotRemoved(const TSourceLoc& loc, int profileMask, int removedVersion, const char* featureDesc) +{ + if (profile & profileMask) { + if (version >= removedVersion) { + const int maxSize = 60; + char buf[maxSize]; + snprintf(buf, maxSize, "%s profile; removed in version %d", ProfileName(profile), removedVersion); + error(loc, "no longer supported in", featureDesc, buf); + } + } +} + +// Returns true if at least one of the extensions in the extensions parameter is requested. Otherwise, returns false. +// Warns appropriately if the requested behavior of an extension is "warn". +bool TParseVersions::checkExtensionsRequested(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) +{ + // First, see if any of the extensions are enabled + for (int i = 0; i < numExtensions; ++i) { + TExtensionBehavior behavior = getExtensionBehavior(extensions[i]); + if (behavior == EBhEnable || behavior == EBhRequire) + return true; + } + + // See if any extensions want to give a warning on use; give warnings for all such extensions + bool warned = false; + for (int i = 0; i < numExtensions; ++i) { + TExtensionBehavior behavior = getExtensionBehavior(extensions[i]); + if (behavior == EBhDisable && relaxedErrors()) { + infoSink.info.message(EPrefixWarning, "The following extension must be enabled to use this feature:", loc); + behavior = EBhWarn; + } + if (behavior == EBhWarn) { + infoSink.info.message(EPrefixWarning, ("extension " + TString(extensions[i]) + " is being used for " + featureDesc).c_str(), loc); + warned = true; + } + } + if (warned) + return true; + return false; +} + +// +// Use when there are no profile/version to check, it's just an error if one of the +// extensions is not present. +// +void TParseVersions::requireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) +{ + if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc)) + return; + + // If we get this far, give errors explaining what extensions are needed + if (numExtensions == 1) + error(loc, "required extension not requested:", featureDesc, extensions[0]); + else { + error(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); + for (int i = 0; i < numExtensions; ++i) + infoSink.info.message(EPrefixNone, extensions[i]); + } +} + +// +// Use by preprocessor when there are no profile/version to check, it's just an error if one of the +// extensions is not present. +// +void TParseVersions::ppRequireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) +{ + if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc)) + return; + + // If we get this far, give errors explaining what extensions are needed + if (numExtensions == 1) + ppError(loc, "required extension not requested:", featureDesc, extensions[0]); + else { + ppError(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); + for (int i = 0; i < numExtensions; ++i) + infoSink.info.message(EPrefixNone, extensions[i]); + } +} + +TExtensionBehavior TParseVersions::getExtensionBehavior(const char* extension) +{ + auto iter = extensionBehavior.find(TString(extension)); + if (iter == extensionBehavior.end()) + return EBhMissing; + else + return iter->second; +} + +// Returns true if the given extension is set to enable, require, or warn. +bool TParseVersions::extensionTurnedOn(const char* const extension) +{ + switch (getExtensionBehavior(extension)) { + case EBhEnable: + case EBhRequire: + case EBhWarn: + return true; + default: + break; + } + return false; +} +// See if any of the extensions are set to enable, require, or warn. +bool TParseVersions::extensionsTurnedOn(int numExtensions, const char* const extensions[]) +{ + for (int i = 0; i < numExtensions; ++i) { + if (extensionTurnedOn(extensions[i])) + return true; + } + return false; +} + +// +// Change the current state of an extension's behavior. +// +void TParseVersions::updateExtensionBehavior(int line, const char* extension, const char* behaviorString) +{ + // Translate from text string of extension's behavior to an enum. + TExtensionBehavior behavior = EBhDisable; + if (! strcmp("require", behaviorString)) + behavior = EBhRequire; + else if (! strcmp("enable", behaviorString)) + behavior = EBhEnable; + else if (! strcmp("disable", behaviorString)) + behavior = EBhDisable; + else if (! strcmp("warn", behaviorString)) + behavior = EBhWarn; + else { + error(getCurrentLoc(), "behavior not supported:", "#extension", behaviorString); + return; + } + + // check if extension is used with correct shader stage + checkExtensionStage(getCurrentLoc(), extension); + + // update the requested extension + updateExtensionBehavior(extension, behavior); + + // see if need to propagate to implicitly modified things + if (strcmp(extension, "GL_ANDROID_extension_pack_es31a") == 0) { + // to everything in AEP + updateExtensionBehavior(line, "GL_KHR_blend_equation_advanced", behaviorString); + updateExtensionBehavior(line, "GL_OES_sample_variables", behaviorString); + updateExtensionBehavior(line, "GL_OES_shader_image_atomic", behaviorString); + updateExtensionBehavior(line, "GL_OES_shader_multisample_interpolation", behaviorString); + updateExtensionBehavior(line, "GL_OES_texture_storage_multisample_2d_array", behaviorString); + updateExtensionBehavior(line, "GL_EXT_geometry_shader", behaviorString); + updateExtensionBehavior(line, "GL_EXT_gpu_shader5", behaviorString); + updateExtensionBehavior(line, "GL_EXT_primitive_bounding_box", behaviorString); + updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString); + updateExtensionBehavior(line, "GL_EXT_tessellation_shader", behaviorString); + updateExtensionBehavior(line, "GL_EXT_texture_buffer", behaviorString); + updateExtensionBehavior(line, "GL_EXT_texture_cube_map_array", behaviorString); + } + // geometry to io_blocks + else if (strcmp(extension, "GL_EXT_geometry_shader") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString); + else if (strcmp(extension, "GL_OES_geometry_shader") == 0) + updateExtensionBehavior(line, "GL_OES_shader_io_blocks", behaviorString); + // tessellation to io_blocks + else if (strcmp(extension, "GL_EXT_tessellation_shader") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString); + else if (strcmp(extension, "GL_OES_tessellation_shader") == 0) + updateExtensionBehavior(line, "GL_OES_shader_io_blocks", behaviorString); + else if (strcmp(extension, "GL_GOOGLE_include_directive") == 0) + updateExtensionBehavior(line, "GL_GOOGLE_cpp_style_line_directive", behaviorString); + // subgroup_* to subgroup_basic + else if (strcmp(extension, "GL_KHR_shader_subgroup_vote") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_arithmetic") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_ballot") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_shuffle") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_shuffle_relative") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_clustered") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_quad") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_NV_shader_subgroup_partitioned") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_EXT_buffer_reference2") == 0 || + strcmp(extension, "GL_EXT_buffer_reference_uvec2") == 0) + updateExtensionBehavior(line, "GL_EXT_buffer_reference", behaviorString); + else if (strcmp(extension, "GL_NV_integer_cooperative_matrix") == 0) + updateExtensionBehavior(line, "GL_NV_cooperative_matrix", behaviorString); + // subgroup extended types to explicit types + else if (strcmp(extension, "GL_EXT_shader_subgroup_extended_types_int8") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_explicit_arithmetic_types_int8", behaviorString); + else if (strcmp(extension, "GL_EXT_shader_subgroup_extended_types_int16") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_explicit_arithmetic_types_int16", behaviorString); + else if (strcmp(extension, "GL_EXT_shader_subgroup_extended_types_int64") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_explicit_arithmetic_types_int64", behaviorString); + else if (strcmp(extension, "GL_EXT_shader_subgroup_extended_types_float16") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_explicit_arithmetic_types_float16", behaviorString); +} + +void TParseVersions::updateExtensionBehavior(const char* extension, TExtensionBehavior behavior) +{ + // Update the current behavior + if (strcmp(extension, "all") == 0) { + // special case for the 'all' extension; apply it to every extension present + if (behavior == EBhRequire || behavior == EBhEnable) { + error(getCurrentLoc(), "extension 'all' cannot have 'require' or 'enable' behavior", "#extension", ""); + return; + } else { + for (auto iter = extensionBehavior.begin(); iter != extensionBehavior.end(); ++iter) + iter->second = behavior; + } + } else { + // Do the update for this single extension + auto iter = extensionBehavior.find(TString(extension)); + if (iter == extensionBehavior.end()) { + switch (behavior) { + case EBhRequire: + error(getCurrentLoc(), "extension not supported:", "#extension", extension); + break; + case EBhEnable: + case EBhWarn: + case EBhDisable: + warn(getCurrentLoc(), "extension not supported:", "#extension", extension); + break; + default: + assert(0 && "unexpected behavior"); + } + + return; + } else { + if (iter->second == EBhDisablePartial) + warn(getCurrentLoc(), "extension is only partially supported:", "#extension", extension); + if (behavior == EBhEnable || behavior == EBhRequire || behavior == EBhDisable) + intermediate.updateRequestedExtension(extension, behavior); + iter->second = behavior; + } + } +} + +// Check if extension is used with correct shader stage. +void TParseVersions::checkExtensionStage(const TSourceLoc& loc, const char * const extension) +{ + // GL_NV_mesh_shader extension is only allowed in task/mesh shaders + if (strcmp(extension, "GL_NV_mesh_shader") == 0) { + requireStage(loc, (EShLanguageMask)(EShLangTaskNVMask | EShLangMeshNVMask | EShLangFragmentMask), + "#extension GL_NV_mesh_shader"); + profileRequires(loc, ECoreProfile, 450, 0, "#extension GL_NV_mesh_shader"); + profileRequires(loc, EEsProfile, 320, 0, "#extension GL_NV_mesh_shader"); + } +} + +// Call for any operation needing full GLSL integer data-type support. +void TParseVersions::fullIntegerCheck(const TSourceLoc& loc, const char* op) +{ + profileRequires(loc, ENoProfile, 130, nullptr, op); + profileRequires(loc, EEsProfile, 300, nullptr, op); +} + +// Call for any operation needing GLSL double data-type support. +void TParseVersions::doubleCheck(const TSourceLoc& loc, const char* op) +{ + + //requireProfile(loc, ECoreProfile | ECompatibilityProfile, op); + if (language == EShLangVertex) { + const char* const f64_Extensions[] = {E_GL_ARB_gpu_shader_fp64, E_GL_ARB_vertex_attrib_64bit}; + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, 2, f64_Extensions, op); + } else + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, E_GL_ARB_gpu_shader_fp64, op); +} + +// Call for any operation needing GLSL float16 data-type support. +void TParseVersions::float16Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = { + E_GL_AMD_gpu_shader_half_float, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +bool TParseVersions::float16Arithmetic() +{ + const char* const extensions[] = { + E_GL_AMD_gpu_shader_half_float, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float16}; + return extensionsTurnedOn(sizeof(extensions)/sizeof(extensions[0]), extensions); +} + +bool TParseVersions::int16Arithmetic() +{ + const char* const extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16}; + return extensionsTurnedOn(sizeof(extensions)/sizeof(extensions[0]), extensions); +} + +bool TParseVersions::int8Arithmetic() +{ + const char* const extensions[] = { + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int8}; + return extensionsTurnedOn(sizeof(extensions)/sizeof(extensions[0]), extensions); +} + +void TParseVersions::requireFloat16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) +{ + TString combined; + combined = op; + combined += ": "; + combined += featureDesc; + + const char* const extensions[] = { + E_GL_AMD_gpu_shader_half_float, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, combined.c_str()); +} + +void TParseVersions::requireInt16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) +{ + TString combined; + combined = op; + combined += ": "; + combined += featureDesc; + + const char* const extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, combined.c_str()); +} + +void TParseVersions::requireInt8Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) +{ + TString combined; + combined = op; + combined += ": "; + combined += featureDesc; + + const char* const extensions[] = { + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int8}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, combined.c_str()); +} + +void TParseVersions::float16ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = { + E_GL_AMD_gpu_shader_half_float, + E_GL_EXT_shader_16bit_storage, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +// Call for any operation needing GLSL float32 data-type support. +void TParseVersions::explicitFloat32Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float32}; + requireExtensions(loc, 2, extensions, op); + } +} + +// Call for any operation needing GLSL float64 data-type support. +void TParseVersions::explicitFloat64Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float64}; + requireExtensions(loc, 2, extensions, op); + requireProfile(loc, ECoreProfile | ECompatibilityProfile, op); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, nullptr, op); + } +} + +// Call for any operation needing GLSL explicit int8 data-type support. +void TParseVersions::explicitInt8Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int8}; + requireExtensions(loc, 2, extensions, op); + } +} + +// Call for any operation needing GLSL float16 opaque-type support +void TParseVersions::float16OpaqueCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + requireExtensions(loc, 1, &E_GL_AMD_gpu_shader_half_float_fetch, op); + requireProfile(loc, ECoreProfile | ECompatibilityProfile, op); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, nullptr, op); + } +} + +// Call for any operation needing GLSL explicit int16 data-type support. +void TParseVersions::explicitInt16Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +void TParseVersions::int16ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_16bit_storage, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +void TParseVersions::int8ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[] = { + E_GL_EXT_shader_8bit_storage, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int8}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +// Call for any operation needing GLSL explicit int32 data-type support. +void TParseVersions::explicitInt32Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int32}; + requireExtensions(loc, 2, extensions, op); + } +} + +// Call for any operation needing GLSL 64-bit integer data-type support. +void TParseVersions::int64Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[3] = {E_GL_ARB_gpu_shader_int64, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int64}; + requireExtensions(loc, 3, extensions, op); + requireProfile(loc, ECoreProfile | ECompatibilityProfile, op); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, nullptr, op); + } +} + +void TParseVersions::fcoopmatCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = {E_GL_NV_cooperative_matrix}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +void TParseVersions::intcoopmatCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = {E_GL_NV_integer_cooperative_matrix}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} +#endif // GLSLANG_WEB +// Call for any operation removed because SPIR-V is in use. +void TParseVersions::spvRemoved(const TSourceLoc& loc, const char* op) +{ + if (spvVersion.spv != 0) + error(loc, "not allowed when generating SPIR-V", op, ""); +} + +// Call for any operation removed because Vulkan SPIR-V is being generated. +void TParseVersions::vulkanRemoved(const TSourceLoc& loc, const char* op) +{ + if (spvVersion.vulkan > 0) + error(loc, "not allowed when using GLSL for Vulkan", op, ""); +} + +// Call for any operation that requires Vulkan. +void TParseVersions::requireVulkan(const TSourceLoc& loc, const char* op) +{ +#ifndef GLSLANG_WEB + if (spvVersion.vulkan == 0) + error(loc, "only allowed when using GLSL for Vulkan", op, ""); +#endif +} + +// Call for any operation that requires SPIR-V. +void TParseVersions::requireSpv(const TSourceLoc& loc, const char* op) +{ +#ifndef GLSLANG_WEB + if (spvVersion.spv == 0) + error(loc, "only allowed when generating SPIR-V", op, ""); +#endif +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Versions.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Versions.h new file mode 100644 index 000000000..8667411e9 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/Versions.h @@ -0,0 +1,331 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef _VERSIONS_INCLUDED_ +#define _VERSIONS_INCLUDED_ + +#define LAST_ELEMENT_MARKER(x) x + +// +// Help manage multiple profiles, versions, extensions etc. +// + +// +// Profiles are set up for masking operations, so queries can be done on multiple +// profiles at the same time. +// +// Don't maintain an ordinal set of enums (0,1,2,3...) to avoid all possible +// defects from mixing the two different forms. +// +typedef enum { + EBadProfile = 0, + ENoProfile = (1 << 0), // only for desktop, before profiles showed up + ECoreProfile = (1 << 1), + ECompatibilityProfile = (1 << 2), + EEsProfile = (1 << 3), + LAST_ELEMENT_MARKER(EProfileCount), +} EProfile; + +namespace glslang { + +// +// Map from profile enum to externally readable text name. +// +inline const char* ProfileName(EProfile profile) +{ + switch (profile) { + case ENoProfile: return "none"; + case ECoreProfile: return "core"; + case ECompatibilityProfile: return "compatibility"; + case EEsProfile: return "es"; + default: return "unknown profile"; + } +} + +// +// What source rules, validation rules, target language, etc. are needed or +// desired for SPIR-V? +// +// 0 means a target or rule set is not enabled (ignore rules from that entity). +// Non-0 means to apply semantic rules arising from that version of its rule set. +// The union of all requested rule sets will be applied. +// +struct SpvVersion { + SpvVersion() : spv(0), vulkanGlsl(0), vulkan(0), openGl(0) {} + unsigned int spv; // the version of SPIR-V to target, as defined by "word 1" of the SPIR-V binary header + int vulkanGlsl; // the version of GLSL semantics for Vulkan, from GL_KHR_vulkan_glsl, for "#define VULKAN XXX" + int vulkan; // the version of Vulkan, for which SPIR-V execution environment rules to use + int openGl; // the version of GLSL semantics for OpenGL, from GL_ARB_gl_spirv, for "#define GL_SPIRV XXX" +}; + +// +// The behaviors from the GLSL "#extension extension_name : behavior" +// +typedef enum { + EBhMissing = 0, + EBhRequire, + EBhEnable, + EBhWarn, + EBhDisable, + EBhDisablePartial // use as initial state of an extension that is only partially implemented +} TExtensionBehavior; + +// +// Symbolic names for extensions. Strings may be directly used when calling the +// functions, but better to have the compiler do spelling checks. +// +const char* const E_GL_OES_texture_3D = "GL_OES_texture_3D"; +const char* const E_GL_OES_standard_derivatives = "GL_OES_standard_derivatives"; +const char* const E_GL_EXT_frag_depth = "GL_EXT_frag_depth"; +const char* const E_GL_OES_EGL_image_external = "GL_OES_EGL_image_external"; +const char* const E_GL_OES_EGL_image_external_essl3 = "GL_OES_EGL_image_external_essl3"; +const char* const E_GL_EXT_YUV_target = "GL_EXT_YUV_target"; +const char* const E_GL_EXT_shader_texture_lod = "GL_EXT_shader_texture_lod"; +const char* const E_GL_EXT_shadow_samplers = "GL_EXT_shadow_samplers"; + +const char* const E_GL_ARB_texture_rectangle = "GL_ARB_texture_rectangle"; +const char* const E_GL_3DL_array_objects = "GL_3DL_array_objects"; +const char* const E_GL_ARB_shading_language_420pack = "GL_ARB_shading_language_420pack"; +const char* const E_GL_ARB_texture_gather = "GL_ARB_texture_gather"; +const char* const E_GL_ARB_gpu_shader5 = "GL_ARB_gpu_shader5"; +const char* const E_GL_ARB_separate_shader_objects = "GL_ARB_separate_shader_objects"; +const char* const E_GL_ARB_compute_shader = "GL_ARB_compute_shader"; +const char* const E_GL_ARB_tessellation_shader = "GL_ARB_tessellation_shader"; +const char* const E_GL_ARB_enhanced_layouts = "GL_ARB_enhanced_layouts"; +const char* const E_GL_ARB_texture_cube_map_array = "GL_ARB_texture_cube_map_array"; +const char* const E_GL_ARB_texture_multisample = "GL_ARB_texture_multisample"; +const char* const E_GL_ARB_shader_texture_lod = "GL_ARB_shader_texture_lod"; +const char* const E_GL_ARB_explicit_attrib_location = "GL_ARB_explicit_attrib_location"; +const char* const E_GL_ARB_explicit_uniform_location = "GL_ARB_explicit_uniform_location"; +const char* const E_GL_ARB_shader_image_load_store = "GL_ARB_shader_image_load_store"; +const char* const E_GL_ARB_shader_atomic_counters = "GL_ARB_shader_atomic_counters"; +const char* const E_GL_ARB_shader_draw_parameters = "GL_ARB_shader_draw_parameters"; +const char* const E_GL_ARB_shader_group_vote = "GL_ARB_shader_group_vote"; +const char* const E_GL_ARB_derivative_control = "GL_ARB_derivative_control"; +const char* const E_GL_ARB_shader_texture_image_samples = "GL_ARB_shader_texture_image_samples"; +const char* const E_GL_ARB_viewport_array = "GL_ARB_viewport_array"; +const char* const E_GL_ARB_gpu_shader_int64 = "GL_ARB_gpu_shader_int64"; +const char* const E_GL_ARB_gpu_shader_fp64 = "GL_ARB_gpu_shader_fp64"; +const char* const E_GL_ARB_shader_ballot = "GL_ARB_shader_ballot"; +const char* const E_GL_ARB_sparse_texture2 = "GL_ARB_sparse_texture2"; +const char* const E_GL_ARB_sparse_texture_clamp = "GL_ARB_sparse_texture_clamp"; +const char* const E_GL_ARB_shader_stencil_export = "GL_ARB_shader_stencil_export"; +// const char* const E_GL_ARB_cull_distance = "GL_ARB_cull_distance"; // present for 4.5, but need extension control over block members +const char* const E_GL_ARB_post_depth_coverage = "GL_ARB_post_depth_coverage"; +const char* const E_GL_ARB_shader_viewport_layer_array = "GL_ARB_shader_viewport_layer_array"; +const char* const E_GL_ARB_fragment_shader_interlock = "GL_ARB_fragment_shader_interlock"; +const char* const E_GL_ARB_shader_clock = "GL_ARB_shader_clock"; +const char* const E_GL_ARB_uniform_buffer_object = "GL_ARB_uniform_buffer_object"; +const char* const E_GL_ARB_sample_shading = "GL_ARB_sample_shading"; +const char* const E_GL_ARB_shader_bit_encoding = "GL_ARB_shader_bit_encoding"; +const char* const E_GL_ARB_shader_image_size = "GL_ARB_shader_image_size"; +const char* const E_GL_ARB_shader_storage_buffer_object = "GL_ARB_shader_storage_buffer_object"; +const char* const E_GL_ARB_shading_language_packing = "GL_ARB_shading_language_packing"; +const char* const E_GL_ARB_texture_query_lod = "GL_ARB_texture_query_lod"; +const char* const E_GL_ARB_vertex_attrib_64bit = "GL_ARB_vertex_attrib_64bit"; + +const char* const E_GL_KHR_shader_subgroup_basic = "GL_KHR_shader_subgroup_basic"; +const char* const E_GL_KHR_shader_subgroup_vote = "GL_KHR_shader_subgroup_vote"; +const char* const E_GL_KHR_shader_subgroup_arithmetic = "GL_KHR_shader_subgroup_arithmetic"; +const char* const E_GL_KHR_shader_subgroup_ballot = "GL_KHR_shader_subgroup_ballot"; +const char* const E_GL_KHR_shader_subgroup_shuffle = "GL_KHR_shader_subgroup_shuffle"; +const char* const E_GL_KHR_shader_subgroup_shuffle_relative = "GL_KHR_shader_subgroup_shuffle_relative"; +const char* const E_GL_KHR_shader_subgroup_clustered = "GL_KHR_shader_subgroup_clustered"; +const char* const E_GL_KHR_shader_subgroup_quad = "GL_KHR_shader_subgroup_quad"; +const char* const E_GL_KHR_memory_scope_semantics = "GL_KHR_memory_scope_semantics"; + +const char* const E_GL_EXT_shader_atomic_int64 = "GL_EXT_shader_atomic_int64"; + +const char* const E_GL_EXT_shader_non_constant_global_initializers = "GL_EXT_shader_non_constant_global_initializers"; +const char* const E_GL_EXT_shader_image_load_formatted = "GL_EXT_shader_image_load_formatted"; + +const char* const E_GL_EXT_shader_16bit_storage = "GL_EXT_shader_16bit_storage"; +const char* const E_GL_EXT_shader_8bit_storage = "GL_EXT_shader_8bit_storage"; + + +// EXT extensions +const char* const E_GL_EXT_device_group = "GL_EXT_device_group"; +const char* const E_GL_EXT_multiview = "GL_EXT_multiview"; +const char* const E_GL_EXT_post_depth_coverage = "GL_EXT_post_depth_coverage"; +const char* const E_GL_EXT_control_flow_attributes = "GL_EXT_control_flow_attributes"; +const char* const E_GL_EXT_nonuniform_qualifier = "GL_EXT_nonuniform_qualifier"; +const char* const E_GL_EXT_samplerless_texture_functions = "GL_EXT_samplerless_texture_functions"; +const char* const E_GL_EXT_scalar_block_layout = "GL_EXT_scalar_block_layout"; +const char* const E_GL_EXT_fragment_invocation_density = "GL_EXT_fragment_invocation_density"; +const char* const E_GL_EXT_buffer_reference = "GL_EXT_buffer_reference"; +const char* const E_GL_EXT_buffer_reference2 = "GL_EXT_buffer_reference2"; +const char* const E_GL_EXT_buffer_reference_uvec2 = "GL_EXT_buffer_reference_uvec2"; +const char* const E_GL_EXT_demote_to_helper_invocation = "GL_EXT_demote_to_helper_invocation"; +const char* const E_GL_EXT_shader_realtime_clock = "GL_EXT_shader_realtime_clock"; +const char* const E_GL_EXT_debug_printf = "GL_EXT_debug_printf"; +const char* const E_GL_EXT_ray_tracing = "GL_EXT_ray_tracing"; +const char* const E_GL_EXT_ray_query = "GL_EXT_ray_query"; +const char* const E_GL_EXT_ray_flags_primitive_culling = "GL_EXT_ray_flags_primitive_culling"; +const char* const E_GL_EXT_blend_func_extended = "GL_EXT_blend_func_extended"; +const char* const E_GL_EXT_shader_implicit_conversions = "GL_EXT_shader_implicit_conversions"; + +// Arrays of extensions for the above viewportEXTs duplications + +const char* const post_depth_coverageEXTs[] = { E_GL_ARB_post_depth_coverage, E_GL_EXT_post_depth_coverage }; +const int Num_post_depth_coverageEXTs = sizeof(post_depth_coverageEXTs) / sizeof(post_depth_coverageEXTs[0]); + +// OVR extensions +const char* const E_GL_OVR_multiview = "GL_OVR_multiview"; +const char* const E_GL_OVR_multiview2 = "GL_OVR_multiview2"; + +const char* const OVR_multiview_EXTs[] = { E_GL_OVR_multiview, E_GL_OVR_multiview2 }; +const int Num_OVR_multiview_EXTs = sizeof(OVR_multiview_EXTs) / sizeof(OVR_multiview_EXTs[0]); + +// #line and #include +const char* const E_GL_GOOGLE_cpp_style_line_directive = "GL_GOOGLE_cpp_style_line_directive"; +const char* const E_GL_GOOGLE_include_directive = "GL_GOOGLE_include_directive"; + +const char* const E_GL_AMD_shader_ballot = "GL_AMD_shader_ballot"; +const char* const E_GL_AMD_shader_trinary_minmax = "GL_AMD_shader_trinary_minmax"; +const char* const E_GL_AMD_shader_explicit_vertex_parameter = "GL_AMD_shader_explicit_vertex_parameter"; +const char* const E_GL_AMD_gcn_shader = "GL_AMD_gcn_shader"; +const char* const E_GL_AMD_gpu_shader_half_float = "GL_AMD_gpu_shader_half_float"; +const char* const E_GL_AMD_texture_gather_bias_lod = "GL_AMD_texture_gather_bias_lod"; +const char* const E_GL_AMD_gpu_shader_int16 = "GL_AMD_gpu_shader_int16"; +const char* const E_GL_AMD_shader_image_load_store_lod = "GL_AMD_shader_image_load_store_lod"; +const char* const E_GL_AMD_shader_fragment_mask = "GL_AMD_shader_fragment_mask"; +const char* const E_GL_AMD_gpu_shader_half_float_fetch = "GL_AMD_gpu_shader_half_float_fetch"; + +const char* const E_GL_INTEL_shader_integer_functions2 = "GL_INTEL_shader_integer_functions2"; + +const char* const E_GL_NV_sample_mask_override_coverage = "GL_NV_sample_mask_override_coverage"; +const char* const E_SPV_NV_geometry_shader_passthrough = "GL_NV_geometry_shader_passthrough"; +const char* const E_GL_NV_viewport_array2 = "GL_NV_viewport_array2"; +const char* const E_GL_NV_stereo_view_rendering = "GL_NV_stereo_view_rendering"; +const char* const E_GL_NVX_multiview_per_view_attributes = "GL_NVX_multiview_per_view_attributes"; +const char* const E_GL_NV_shader_atomic_int64 = "GL_NV_shader_atomic_int64"; +const char* const E_GL_NV_conservative_raster_underestimation = "GL_NV_conservative_raster_underestimation"; +const char* const E_GL_NV_shader_noperspective_interpolation = "GL_NV_shader_noperspective_interpolation"; +const char* const E_GL_NV_shader_subgroup_partitioned = "GL_NV_shader_subgroup_partitioned"; +const char* const E_GL_NV_shading_rate_image = "GL_NV_shading_rate_image"; +const char* const E_GL_NV_ray_tracing = "GL_NV_ray_tracing"; +const char* const E_GL_NV_fragment_shader_barycentric = "GL_NV_fragment_shader_barycentric"; +const char* const E_GL_NV_compute_shader_derivatives = "GL_NV_compute_shader_derivatives"; +const char* const E_GL_NV_shader_texture_footprint = "GL_NV_shader_texture_footprint"; +const char* const E_GL_NV_mesh_shader = "GL_NV_mesh_shader"; + +// Arrays of extensions for the above viewportEXTs duplications + +const char* const viewportEXTs[] = { E_GL_ARB_shader_viewport_layer_array, E_GL_NV_viewport_array2 }; +const int Num_viewportEXTs = sizeof(viewportEXTs) / sizeof(viewportEXTs[0]); + +const char* const E_GL_NV_cooperative_matrix = "GL_NV_cooperative_matrix"; +const char* const E_GL_NV_shader_sm_builtins = "GL_NV_shader_sm_builtins"; +const char* const E_GL_NV_integer_cooperative_matrix = "GL_NV_integer_cooperative_matrix"; + +// AEP +const char* const E_GL_ANDROID_extension_pack_es31a = "GL_ANDROID_extension_pack_es31a"; +const char* const E_GL_KHR_blend_equation_advanced = "GL_KHR_blend_equation_advanced"; +const char* const E_GL_OES_sample_variables = "GL_OES_sample_variables"; +const char* const E_GL_OES_shader_image_atomic = "GL_OES_shader_image_atomic"; +const char* const E_GL_OES_shader_multisample_interpolation = "GL_OES_shader_multisample_interpolation"; +const char* const E_GL_OES_texture_storage_multisample_2d_array = "GL_OES_texture_storage_multisample_2d_array"; +const char* const E_GL_EXT_geometry_shader = "GL_EXT_geometry_shader"; +const char* const E_GL_EXT_geometry_point_size = "GL_EXT_geometry_point_size"; +const char* const E_GL_EXT_gpu_shader5 = "GL_EXT_gpu_shader5"; +const char* const E_GL_EXT_primitive_bounding_box = "GL_EXT_primitive_bounding_box"; +const char* const E_GL_EXT_shader_io_blocks = "GL_EXT_shader_io_blocks"; +const char* const E_GL_EXT_tessellation_shader = "GL_EXT_tessellation_shader"; +const char* const E_GL_EXT_tessellation_point_size = "GL_EXT_tessellation_point_size"; +const char* const E_GL_EXT_texture_buffer = "GL_EXT_texture_buffer"; +const char* const E_GL_EXT_texture_cube_map_array = "GL_EXT_texture_cube_map_array"; +const char* const E_GL_EXT_shader_integer_mix = "GL_EXT_shader_integer_mix"; + +// OES matching AEP +const char* const E_GL_OES_geometry_shader = "GL_OES_geometry_shader"; +const char* const E_GL_OES_geometry_point_size = "GL_OES_geometry_point_size"; +const char* const E_GL_OES_gpu_shader5 = "GL_OES_gpu_shader5"; +const char* const E_GL_OES_primitive_bounding_box = "GL_OES_primitive_bounding_box"; +const char* const E_GL_OES_shader_io_blocks = "GL_OES_shader_io_blocks"; +const char* const E_GL_OES_tessellation_shader = "GL_OES_tessellation_shader"; +const char* const E_GL_OES_tessellation_point_size = "GL_OES_tessellation_point_size"; +const char* const E_GL_OES_texture_buffer = "GL_OES_texture_buffer"; +const char* const E_GL_OES_texture_cube_map_array = "GL_OES_texture_cube_map_array"; + +// EXT +const char* const E_GL_EXT_shader_explicit_arithmetic_types = "GL_EXT_shader_explicit_arithmetic_types"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_int8 = "GL_EXT_shader_explicit_arithmetic_types_int8"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_int16 = "GL_EXT_shader_explicit_arithmetic_types_int16"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_int32 = "GL_EXT_shader_explicit_arithmetic_types_int32"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_int64 = "GL_EXT_shader_explicit_arithmetic_types_int64"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_float16 = "GL_EXT_shader_explicit_arithmetic_types_float16"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_float32 = "GL_EXT_shader_explicit_arithmetic_types_float32"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_float64 = "GL_EXT_shader_explicit_arithmetic_types_float64"; + +const char* const E_GL_EXT_shader_subgroup_extended_types_int8 = "GL_EXT_shader_subgroup_extended_types_int8"; +const char* const E_GL_EXT_shader_subgroup_extended_types_int16 = "GL_EXT_shader_subgroup_extended_types_int16"; +const char* const E_GL_EXT_shader_subgroup_extended_types_int64 = "GL_EXT_shader_subgroup_extended_types_int64"; +const char* const E_GL_EXT_shader_subgroup_extended_types_float16 = "GL_EXT_shader_subgroup_extended_types_float16"; + +// Arrays of extensions for the above AEP duplications + +const char* const AEP_geometry_shader[] = { E_GL_EXT_geometry_shader, E_GL_OES_geometry_shader }; +const int Num_AEP_geometry_shader = sizeof(AEP_geometry_shader)/sizeof(AEP_geometry_shader[0]); + +const char* const AEP_geometry_point_size[] = { E_GL_EXT_geometry_point_size, E_GL_OES_geometry_point_size }; +const int Num_AEP_geometry_point_size = sizeof(AEP_geometry_point_size)/sizeof(AEP_geometry_point_size[0]); + +const char* const AEP_gpu_shader5[] = { E_GL_EXT_gpu_shader5, E_GL_OES_gpu_shader5 }; +const int Num_AEP_gpu_shader5 = sizeof(AEP_gpu_shader5)/sizeof(AEP_gpu_shader5[0]); + +const char* const AEP_primitive_bounding_box[] = { E_GL_EXT_primitive_bounding_box, E_GL_OES_primitive_bounding_box }; +const int Num_AEP_primitive_bounding_box = sizeof(AEP_primitive_bounding_box)/sizeof(AEP_primitive_bounding_box[0]); + +const char* const AEP_shader_io_blocks[] = { E_GL_EXT_shader_io_blocks, E_GL_OES_shader_io_blocks }; +const int Num_AEP_shader_io_blocks = sizeof(AEP_shader_io_blocks)/sizeof(AEP_shader_io_blocks[0]); + +const char* const AEP_tessellation_shader[] = { E_GL_EXT_tessellation_shader, E_GL_OES_tessellation_shader }; +const int Num_AEP_tessellation_shader = sizeof(AEP_tessellation_shader)/sizeof(AEP_tessellation_shader[0]); + +const char* const AEP_tessellation_point_size[] = { E_GL_EXT_tessellation_point_size, E_GL_OES_tessellation_point_size }; +const int Num_AEP_tessellation_point_size = sizeof(AEP_tessellation_point_size)/sizeof(AEP_tessellation_point_size[0]); + +const char* const AEP_texture_buffer[] = { E_GL_EXT_texture_buffer, E_GL_OES_texture_buffer }; +const int Num_AEP_texture_buffer = sizeof(AEP_texture_buffer)/sizeof(AEP_texture_buffer[0]); + +const char* const AEP_texture_cube_map_array[] = { E_GL_EXT_texture_cube_map_array, E_GL_OES_texture_cube_map_array }; +const int Num_AEP_texture_cube_map_array = sizeof(AEP_texture_cube_map_array)/sizeof(AEP_texture_cube_map_array[0]); + +} // end namespace glslang + +#endif // _VERSIONS_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/attribute.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/attribute.cpp new file mode 100644 index 000000000..958551834 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/attribute.cpp @@ -0,0 +1,346 @@ +// +// Copyright (C) 2017 LunarG, Inc. +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef GLSLANG_WEB + +#include "attribute.h" +#include "../Include/intermediate.h" +#include "ParseHelper.h" + +namespace glslang { + +// extract integers out of attribute arguments stored in attribute aggregate +bool TAttributeArgs::getInt(int& value, int argNum) const +{ + const TConstUnion* intConst = getConstUnion(EbtInt, argNum); + + if (intConst == nullptr) + return false; + + value = intConst->getIConst(); + return true; +} + + +// extract strings out of attribute arguments stored in attribute aggregate. +// convert to lower case if converToLower is true (for case-insensitive compare convenience) +bool TAttributeArgs::getString(TString& value, int argNum, bool convertToLower) const +{ + const TConstUnion* stringConst = getConstUnion(EbtString, argNum); + + if (stringConst == nullptr) + return false; + + value = *stringConst->getSConst(); + + // Convenience. + if (convertToLower) + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + + return true; +} + +// How many arguments were supplied? +int TAttributeArgs::size() const +{ + return args == nullptr ? 0 : (int)args->getSequence().size(); +} + +// Helper to get attribute const union. Returns nullptr on failure. +const TConstUnion* TAttributeArgs::getConstUnion(TBasicType basicType, int argNum) const +{ + if (args == nullptr) + return nullptr; + + if (argNum >= (int)args->getSequence().size()) + return nullptr; + + if (args->getSequence()[argNum]->getAsConstantUnion() == nullptr) + return nullptr; + + const TConstUnion* constVal = &args->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0]; + if (constVal == nullptr || constVal->getType() != basicType) + return nullptr; + + return constVal; +} + +// Implementation of TParseContext parts of attributes +TAttributeType TParseContext::attributeFromName(const TString& name) const +{ + if (name == "branch" || name == "dont_flatten") + return EatBranch; + else if (name == "flatten") + return EatFlatten; + else if (name == "unroll") + return EatUnroll; + else if (name == "loop" || name == "dont_unroll") + return EatLoop; + else if (name == "dependency_infinite") + return EatDependencyInfinite; + else if (name == "dependency_length") + return EatDependencyLength; + else if (name == "min_iterations") + return EatMinIterations; + else if (name == "max_iterations") + return EatMaxIterations; + else if (name == "iteration_multiple") + return EatIterationMultiple; + else if (name == "peel_count") + return EatPeelCount; + else if (name == "partial_count") + return EatPartialCount; + else + return EatNone; +} + +// Make an initial leaf for the grammar from a no-argument attribute +TAttributes* TParseContext::makeAttributes(const TString& identifier) const +{ + TAttributes *attributes = nullptr; + attributes = NewPoolObject(attributes); + TAttributeArgs args = { attributeFromName(identifier), nullptr }; + attributes->push_back(args); + return attributes; +} + +// Make an initial leaf for the grammar from a one-argument attribute +TAttributes* TParseContext::makeAttributes(const TString& identifier, TIntermNode* node) const +{ + TAttributes *attributes = nullptr; + attributes = NewPoolObject(attributes); + + // for now, node is always a simple single expression, but other code expects + // a list, so make it so + TIntermAggregate* agg = intermediate.makeAggregate(node); + TAttributeArgs args = { attributeFromName(identifier), agg }; + attributes->push_back(args); + return attributes; +} + +// Merge two sets of attributes into a single set. +// The second argument is destructively consumed. +TAttributes* TParseContext::mergeAttributes(TAttributes* attr1, TAttributes* attr2) const +{ + attr1->splice(attr1->end(), *attr2); + return attr1; +} + +// +// Selection attributes +// +void TParseContext::handleSelectionAttributes(const TAttributes& attributes, TIntermNode* node) +{ + TIntermSelection* selection = node->getAsSelectionNode(); + if (selection == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + if (it->size() > 0) { + warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", ""); + continue; + } + + switch (it->name) { + case EatFlatten: + selection->setFlatten(); + break; + case EatBranch: + selection->setDontFlatten(); + break; + default: + warn(node->getLoc(), "attribute does not apply to a selection", "", ""); + break; + } + } +} + +// +// Switch attributes +// +void TParseContext::handleSwitchAttributes(const TAttributes& attributes, TIntermNode* node) +{ + TIntermSwitch* selection = node->getAsSwitchNode(); + if (selection == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + if (it->size() > 0) { + warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", ""); + continue; + } + + switch (it->name) { + case EatFlatten: + selection->setFlatten(); + break; + case EatBranch: + selection->setDontFlatten(); + break; + default: + warn(node->getLoc(), "attribute does not apply to a switch", "", ""); + break; + } + } +} + +// +// Loop attributes +// +void TParseContext::handleLoopAttributes(const TAttributes& attributes, TIntermNode* node) +{ + TIntermLoop* loop = node->getAsLoopNode(); + if (loop == nullptr) { + // the actual loop might be part of a sequence + TIntermAggregate* agg = node->getAsAggregate(); + if (agg == nullptr) + return; + for (auto it = agg->getSequence().begin(); it != agg->getSequence().end(); ++it) { + loop = (*it)->getAsLoopNode(); + if (loop != nullptr) + break; + } + if (loop == nullptr) + return; + } + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + + const auto noArgument = [&](const char* feature) { + if (it->size() > 0) { + warn(node->getLoc(), "expected no arguments", feature, ""); + return false; + } + return true; + }; + + const auto positiveSignedArgument = [&](const char* feature, int& value) { + if (it->size() == 1 && it->getInt(value)) { + if (value <= 0) { + error(node->getLoc(), "must be positive", feature, ""); + return false; + } + } else { + warn(node->getLoc(), "expected a single integer argument", feature, ""); + return false; + } + return true; + }; + + const auto unsignedArgument = [&](const char* feature, unsigned int& uiValue) { + int value; + if (!(it->size() == 1 && it->getInt(value))) { + warn(node->getLoc(), "expected a single integer argument", feature, ""); + return false; + } + uiValue = (unsigned int)value; + return true; + }; + + const auto positiveUnsignedArgument = [&](const char* feature, unsigned int& uiValue) { + int value; + if (it->size() == 1 && it->getInt(value)) { + if (value == 0) { + error(node->getLoc(), "must be greater than or equal to 1", feature, ""); + return false; + } + } else { + warn(node->getLoc(), "expected a single integer argument", feature, ""); + return false; + } + uiValue = (unsigned int)value; + return true; + }; + + const auto spirv14 = [&](const char* feature) { + if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4) + warn(node->getLoc(), "attribute requires a SPIR-V 1.4 target-env", feature, ""); + }; + + int value = 0; + unsigned uiValue = 0; + switch (it->name) { + case EatUnroll: + if (noArgument("unroll")) + loop->setUnroll(); + break; + case EatLoop: + if (noArgument("dont_unroll")) + loop->setDontUnroll(); + break; + case EatDependencyInfinite: + if (noArgument("dependency_infinite")) + loop->setLoopDependency(TIntermLoop::dependencyInfinite); + break; + case EatDependencyLength: + if (positiveSignedArgument("dependency_length", value)) + loop->setLoopDependency(value); + break; + case EatMinIterations: + spirv14("min_iterations"); + if (unsignedArgument("min_iterations", uiValue)) + loop->setMinIterations(uiValue); + break; + case EatMaxIterations: + spirv14("max_iterations"); + if (unsignedArgument("max_iterations", uiValue)) + loop->setMaxIterations(uiValue); + break; + case EatIterationMultiple: + spirv14("iteration_multiple"); + if (positiveUnsignedArgument("iteration_multiple", uiValue)) + loop->setIterationMultiple(uiValue); + break; + case EatPeelCount: + spirv14("peel_count"); + if (unsignedArgument("peel_count", uiValue)) + loop->setPeelCount(uiValue); + break; + case EatPartialCount: + spirv14("partial_count"); + if (unsignedArgument("partial_count", uiValue)) + loop->setPartialCount(uiValue); + break; + default: + warn(node->getLoc(), "attribute does not apply to a loop", "", ""); + break; + } + } +} + +} // end namespace glslang + +#endif // GLSLANG_WEB diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/attribute.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/attribute.h new file mode 100644 index 000000000..38a943d28 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/attribute.h @@ -0,0 +1,149 @@ +// +// Copyright (C) 2017 LunarG, Inc. +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _ATTRIBUTE_INCLUDED_ +#define _ATTRIBUTE_INCLUDED_ + +#include "../Include/Common.h" +#include "../Include/ConstantUnion.h" + +namespace glslang { + + enum TAttributeType { + EatNone, + EatAllow_uav_condition, + EatBranch, + EatCall, + EatDomain, + EatEarlyDepthStencil, + EatFastOpt, + EatFlatten, + EatForceCase, + EatInstance, + EatMaxTessFactor, + EatNumThreads, + EatMaxVertexCount, + EatOutputControlPoints, + EatOutputTopology, + EatPartitioning, + EatPatchConstantFunc, + EatPatchSize, + EatUnroll, + EatLoop, + EatBinding, + EatGlobalBinding, + EatLocation, + EatInputAttachment, + EatBuiltIn, + EatPushConstant, + EatConstantId, + EatDependencyInfinite, + EatDependencyLength, + EatMinIterations, + EatMaxIterations, + EatIterationMultiple, + EatPeelCount, + EatPartialCount, + EatFormatRgba32f, + EatFormatRgba16f, + EatFormatR32f, + EatFormatRgba8, + EatFormatRgba8Snorm, + EatFormatRg32f, + EatFormatRg16f, + EatFormatR11fG11fB10f, + EatFormatR16f, + EatFormatRgba16, + EatFormatRgb10A2, + EatFormatRg16, + EatFormatRg8, + EatFormatR16, + EatFormatR8, + EatFormatRgba16Snorm, + EatFormatRg16Snorm, + EatFormatRg8Snorm, + EatFormatR16Snorm, + EatFormatR8Snorm, + EatFormatRgba32i, + EatFormatRgba16i, + EatFormatRgba8i, + EatFormatR32i, + EatFormatRg32i, + EatFormatRg16i, + EatFormatRg8i, + EatFormatR16i, + EatFormatR8i, + EatFormatRgba32ui, + EatFormatRgba16ui, + EatFormatRgba8ui, + EatFormatR32ui, + EatFormatRgb10a2ui, + EatFormatRg32ui, + EatFormatRg16ui, + EatFormatRg8ui, + EatFormatR16ui, + EatFormatR8ui, + EatFormatUnknown, + EatNonWritable, + EatNonReadable + }; + + class TIntermAggregate; + + struct TAttributeArgs { + TAttributeType name; + const TIntermAggregate* args; + + // Obtain attribute as integer + // Return false if it cannot be obtained + bool getInt(int& value, int argNum = 0) const; + + // Obtain attribute as string, with optional to-lower transform + // Return false if it cannot be obtained + bool getString(TString& value, int argNum = 0, bool convertToLower = true) const; + + // How many arguments were provided to the attribute? + int size() const; + + protected: + const TConstUnion* getConstUnion(TBasicType basicType, int argNum) const; + }; + + typedef TList TAttributes; + +} // end namespace glslang + +#endif // _ATTRIBUTE_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/gl_types.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/gl_types.h new file mode 100644 index 000000000..b6f613bce --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/gl_types.h @@ -0,0 +1,210 @@ +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#pragma once + +#define GL_FLOAT 0x1406 +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 + +#define GL_DOUBLE 0x140A +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE + +#define GL_INT 0x1404 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 + +#define GL_UNSIGNED_INT 0x1405 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 + +#define GL_INT64_ARB 0x140E +#define GL_INT64_VEC2_ARB 0x8FE9 +#define GL_INT64_VEC3_ARB 0x8FEA +#define GL_INT64_VEC4_ARB 0x8FEB + +#define GL_UNSIGNED_INT64_ARB 0x140F +#define GL_UNSIGNED_INT64_VEC2_ARB 0x8FE5 +#define GL_UNSIGNED_INT64_VEC3_ARB 0x8FE6 +#define GL_UNSIGNED_INT64_VEC4_ARB 0x8FE7 + +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 + +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A + +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E + +// Those constants are borrowed from extension NV_gpu_shader5 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB + +#define GL_FLOAT16_MAT2_AMD 0x91C5 +#define GL_FLOAT16_MAT3_AMD 0x91C6 +#define GL_FLOAT16_MAT4_AMD 0x91C7 +#define GL_FLOAT16_MAT2x3_AMD 0x91C8 +#define GL_FLOAT16_MAT2x4_AMD 0x91C9 +#define GL_FLOAT16_MAT3x2_AMD 0x91CA +#define GL_FLOAT16_MAT3x4_AMD 0x91CB +#define GL_FLOAT16_MAT4x2_AMD 0x91CC +#define GL_FLOAT16_MAT4x3_AMD 0x91CD + +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D + +#define GL_FLOAT16_SAMPLER_1D_AMD 0x91CE +#define GL_FLOAT16_SAMPLER_2D_AMD 0x91CF +#define GL_FLOAT16_SAMPLER_3D_AMD 0x91D0 +#define GL_FLOAT16_SAMPLER_CUBE_AMD 0x91D1 +#define GL_FLOAT16_SAMPLER_2D_RECT_AMD 0x91D2 +#define GL_FLOAT16_SAMPLER_1D_ARRAY_AMD 0x91D3 +#define GL_FLOAT16_SAMPLER_2D_ARRAY_AMD 0x91D4 +#define GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_AMD 0x91D5 +#define GL_FLOAT16_SAMPLER_BUFFER_AMD 0x91D6 +#define GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_AMD 0x91D7 +#define GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_ARRAY_AMD 0x91D8 + +#define GL_FLOAT16_SAMPLER_1D_SHADOW_AMD 0x91D9 +#define GL_FLOAT16_SAMPLER_2D_SHADOW_AMD 0x91DA +#define GL_FLOAT16_SAMPLER_2D_RECT_SHADOW_AMD 0x91DB +#define GL_FLOAT16_SAMPLER_1D_ARRAY_SHADOW_AMD 0x91DC +#define GL_FLOAT16_SAMPLER_2D_ARRAY_SHADOW_AMD 0x91DD +#define GL_FLOAT16_SAMPLER_CUBE_SHADOW_AMD 0x91DE +#define GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_SHADOW_AMD 0x91DF + +#define GL_FLOAT16_IMAGE_1D_AMD 0x91E0 +#define GL_FLOAT16_IMAGE_2D_AMD 0x91E1 +#define GL_FLOAT16_IMAGE_3D_AMD 0x91E2 +#define GL_FLOAT16_IMAGE_2D_RECT_AMD 0x91E3 +#define GL_FLOAT16_IMAGE_CUBE_AMD 0x91E4 +#define GL_FLOAT16_IMAGE_1D_ARRAY_AMD 0x91E5 +#define GL_FLOAT16_IMAGE_2D_ARRAY_AMD 0x91E6 +#define GL_FLOAT16_IMAGE_CUBE_MAP_ARRAY_AMD 0x91E7 +#define GL_FLOAT16_IMAGE_BUFFER_AMD 0x91E8 +#define GL_FLOAT16_IMAGE_2D_MULTISAMPLE_AMD 0x91E9 +#define GL_FLOAT16_IMAGE_2D_MULTISAMPLE_ARRAY_AMD 0x91EA + +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E + +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A + +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C + +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/glslang.y b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/glslang.y new file mode 100644 index 000000000..2a782fd53 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/glslang.y @@ -0,0 +1,3894 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2019 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Do not edit the .y file, only edit the .m4 file. +// The .y bison file is not a source file, it is a derivative of the .m4 file. +// The m4 file needs to be processed by m4 to generate the .y bison file. +// +// Code sandwiched between a pair: +// +// GLSLANG_WEB_EXCLUDE_ON +// ... +// ... +// ... +// GLSLANG_WEB_EXCLUDE_OFF +// +// Will be excluded from the grammar when m4 is executed as: +// +// m4 -P -DGLSLANG_WEB +// +// It will be included when m4 is executed as: +// +// m4 -P +// + + + + +/** + * This is bison grammar and productions for parsing all versions of the + * GLSL shading languages. + */ +%{ + +/* Based on: +ANSI C Yacc grammar + +In 1985, Jeff Lee published his Yacc grammar (which is accompanied by a +matching Lex specification) for the April 30, 1985 draft version of the +ANSI C standard. Tom Stockfisch reposted it to net.sources in 1987; that +original, as mentioned in the answer to question 17.25 of the comp.lang.c +FAQ, can be ftp'ed from ftp.uu.net, file usenet/net.sources/ansi.c.grammar.Z. + +I intend to keep this version as close to the current C Standard grammar as +possible; please let me know if you discover discrepancies. + +Jutta Degener, 1995 +*/ + +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "../Public/ShaderLang.h" +#include "attribute.h" + +using namespace glslang; + +%} + +%define parse.error verbose + +%union { + struct { + glslang::TSourceLoc loc; + union { + glslang::TString *string; + int i; + unsigned int u; + long long i64; + unsigned long long u64; + bool b; + double d; + }; + glslang::TSymbol* symbol; + } lex; + struct { + glslang::TSourceLoc loc; + glslang::TOperator op; + union { + TIntermNode* intermNode; + glslang::TIntermNodePair nodePair; + glslang::TIntermTyped* intermTypedNode; + glslang::TAttributes* attributes; + }; + union { + glslang::TPublicType type; + glslang::TFunction* function; + glslang::TParameter param; + glslang::TTypeLoc typeLine; + glslang::TTypeList* typeList; + glslang::TArraySizes* arraySizes; + glslang::TIdentifierList* identifierList; + }; + glslang::TArraySizes* typeParameters; + } interm; +} + +%{ + +/* windows only pragma */ +#ifdef _MSC_VER + #pragma warning(disable : 4065) + #pragma warning(disable : 4127) + #pragma warning(disable : 4244) +#endif + +#define parseContext (*pParseContext) +#define yyerror(context, msg) context->parserError(msg) + +extern int yylex(YYSTYPE*, TParseContext&); + +%} + +%parse-param {glslang::TParseContext* pParseContext} +%lex-param {parseContext} +%pure-parser // enable thread safety +%expect 1 // One shift reduce conflict because of if | else + +%token CONST BOOL INT UINT FLOAT +%token BVEC2 BVEC3 BVEC4 +%token IVEC2 IVEC3 IVEC4 +%token UVEC2 UVEC3 UVEC4 +%token VEC2 VEC3 VEC4 +%token MAT2 MAT3 MAT4 +%token MAT2X2 MAT2X3 MAT2X4 +%token MAT3X2 MAT3X3 MAT3X4 +%token MAT4X2 MAT4X3 MAT4X4 + +// combined image/sampler +%token SAMPLER2D SAMPLER3D SAMPLERCUBE SAMPLER2DSHADOW +%token SAMPLERCUBESHADOW SAMPLER2DARRAY +%token SAMPLER2DARRAYSHADOW ISAMPLER2D ISAMPLER3D ISAMPLERCUBE +%token ISAMPLER2DARRAY USAMPLER2D USAMPLER3D +%token USAMPLERCUBE USAMPLER2DARRAY + +// separate image/sampler +%token SAMPLER SAMPLERSHADOW +%token TEXTURE2D TEXTURE3D TEXTURECUBE TEXTURE2DARRAY +%token ITEXTURE2D ITEXTURE3D ITEXTURECUBE ITEXTURE2DARRAY +%token UTEXTURE2D UTEXTURE3D UTEXTURECUBE UTEXTURE2DARRAY + + + +%token ATTRIBUTE VARYING +%token FLOAT16_T FLOAT32_T DOUBLE FLOAT64_T +%token INT64_T UINT64_T INT32_T UINT32_T INT16_T UINT16_T INT8_T UINT8_T +%token I64VEC2 I64VEC3 I64VEC4 +%token U64VEC2 U64VEC3 U64VEC4 +%token I32VEC2 I32VEC3 I32VEC4 +%token U32VEC2 U32VEC3 U32VEC4 +%token I16VEC2 I16VEC3 I16VEC4 +%token U16VEC2 U16VEC3 U16VEC4 +%token I8VEC2 I8VEC3 I8VEC4 +%token U8VEC2 U8VEC3 U8VEC4 +%token DVEC2 DVEC3 DVEC4 DMAT2 DMAT3 DMAT4 +%token F16VEC2 F16VEC3 F16VEC4 F16MAT2 F16MAT3 F16MAT4 +%token F32VEC2 F32VEC3 F32VEC4 F32MAT2 F32MAT3 F32MAT4 +%token F64VEC2 F64VEC3 F64VEC4 F64MAT2 F64MAT3 F64MAT4 +%token DMAT2X2 DMAT2X3 DMAT2X4 +%token DMAT3X2 DMAT3X3 DMAT3X4 +%token DMAT4X2 DMAT4X3 DMAT4X4 +%token F16MAT2X2 F16MAT2X3 F16MAT2X4 +%token F16MAT3X2 F16MAT3X3 F16MAT3X4 +%token F16MAT4X2 F16MAT4X3 F16MAT4X4 +%token F32MAT2X2 F32MAT2X3 F32MAT2X4 +%token F32MAT3X2 F32MAT3X3 F32MAT3X4 +%token F32MAT4X2 F32MAT4X3 F32MAT4X4 +%token F64MAT2X2 F64MAT2X3 F64MAT2X4 +%token F64MAT3X2 F64MAT3X3 F64MAT3X4 +%token F64MAT4X2 F64MAT4X3 F64MAT4X4 +%token ATOMIC_UINT +%token ACCSTRUCTNV +%token ACCSTRUCTEXT +%token RAYQUERYEXT +%token FCOOPMATNV ICOOPMATNV UCOOPMATNV + +// combined image/sampler +%token SAMPLERCUBEARRAY SAMPLERCUBEARRAYSHADOW +%token ISAMPLERCUBEARRAY USAMPLERCUBEARRAY +%token SAMPLER1D SAMPLER1DARRAY SAMPLER1DARRAYSHADOW ISAMPLER1D SAMPLER1DSHADOW +%token SAMPLER2DRECT SAMPLER2DRECTSHADOW ISAMPLER2DRECT USAMPLER2DRECT +%token SAMPLERBUFFER ISAMPLERBUFFER USAMPLERBUFFER +%token SAMPLER2DMS ISAMPLER2DMS USAMPLER2DMS +%token SAMPLER2DMSARRAY ISAMPLER2DMSARRAY USAMPLER2DMSARRAY +%token SAMPLEREXTERNALOES +%token SAMPLEREXTERNAL2DY2YEXT +%token SAMPLERVIDEO +%token ISAMPLER1DARRAY USAMPLER1D USAMPLER1DARRAY +%token F16SAMPLER1D F16SAMPLER2D F16SAMPLER3D F16SAMPLER2DRECT F16SAMPLERCUBE +%token F16SAMPLER1DARRAY F16SAMPLER2DARRAY F16SAMPLERCUBEARRAY +%token F16SAMPLERBUFFER F16SAMPLER2DMS F16SAMPLER2DMSARRAY +%token F16SAMPLER1DSHADOW F16SAMPLER2DSHADOW F16SAMPLER1DARRAYSHADOW F16SAMPLER2DARRAYSHADOW +%token F16SAMPLER2DRECTSHADOW F16SAMPLERCUBESHADOW F16SAMPLERCUBEARRAYSHADOW + +// images +%token IMAGE1D IIMAGE1D UIMAGE1D IMAGE2D IIMAGE2D +%token UIMAGE2D IMAGE3D IIMAGE3D UIMAGE3D +%token IMAGE2DRECT IIMAGE2DRECT UIMAGE2DRECT +%token IMAGECUBE IIMAGECUBE UIMAGECUBE +%token IMAGEBUFFER IIMAGEBUFFER UIMAGEBUFFER +%token IMAGE1DARRAY IIMAGE1DARRAY UIMAGE1DARRAY +%token IMAGE2DARRAY IIMAGE2DARRAY UIMAGE2DARRAY +%token IMAGECUBEARRAY IIMAGECUBEARRAY UIMAGECUBEARRAY +%token IMAGE2DMS IIMAGE2DMS UIMAGE2DMS +%token IMAGE2DMSARRAY IIMAGE2DMSARRAY UIMAGE2DMSARRAY + +%token F16IMAGE1D F16IMAGE2D F16IMAGE3D F16IMAGE2DRECT +%token F16IMAGECUBE F16IMAGE1DARRAY F16IMAGE2DARRAY F16IMAGECUBEARRAY +%token F16IMAGEBUFFER F16IMAGE2DMS F16IMAGE2DMSARRAY + +// texture without sampler +%token TEXTURECUBEARRAY ITEXTURECUBEARRAY UTEXTURECUBEARRAY +%token TEXTURE1D ITEXTURE1D UTEXTURE1D +%token TEXTURE1DARRAY ITEXTURE1DARRAY UTEXTURE1DARRAY +%token TEXTURE2DRECT ITEXTURE2DRECT UTEXTURE2DRECT +%token TEXTUREBUFFER ITEXTUREBUFFER UTEXTUREBUFFER +%token TEXTURE2DMS ITEXTURE2DMS UTEXTURE2DMS +%token TEXTURE2DMSARRAY ITEXTURE2DMSARRAY UTEXTURE2DMSARRAY + +%token F16TEXTURE1D F16TEXTURE2D F16TEXTURE3D F16TEXTURE2DRECT F16TEXTURECUBE +%token F16TEXTURE1DARRAY F16TEXTURE2DARRAY F16TEXTURECUBEARRAY +%token F16TEXTUREBUFFER F16TEXTURE2DMS F16TEXTURE2DMSARRAY + +// input attachments +%token SUBPASSINPUT SUBPASSINPUTMS ISUBPASSINPUT ISUBPASSINPUTMS USUBPASSINPUT USUBPASSINPUTMS +%token F16SUBPASSINPUT F16SUBPASSINPUTMS + + + +%token LEFT_OP RIGHT_OP +%token INC_OP DEC_OP LE_OP GE_OP EQ_OP NE_OP +%token AND_OP OR_OP XOR_OP MUL_ASSIGN DIV_ASSIGN ADD_ASSIGN +%token MOD_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN +%token SUB_ASSIGN +%token STRING_LITERAL + +%token LEFT_PAREN RIGHT_PAREN LEFT_BRACKET RIGHT_BRACKET LEFT_BRACE RIGHT_BRACE DOT +%token COMMA COLON EQUAL SEMICOLON BANG DASH TILDE PLUS STAR SLASH PERCENT +%token LEFT_ANGLE RIGHT_ANGLE VERTICAL_BAR CARET AMPERSAND QUESTION + +%token INVARIANT +%token HIGH_PRECISION MEDIUM_PRECISION LOW_PRECISION PRECISION +%token PACKED RESOURCE SUPERP + +%token FLOATCONSTANT INTCONSTANT UINTCONSTANT BOOLCONSTANT +%token IDENTIFIER TYPE_NAME +%token CENTROID IN OUT INOUT +%token STRUCT VOID WHILE +%token BREAK CONTINUE DO ELSE FOR IF DISCARD RETURN SWITCH CASE DEFAULT +%token UNIFORM SHARED BUFFER +%token FLAT SMOOTH LAYOUT + + +%token DOUBLECONSTANT INT16CONSTANT UINT16CONSTANT FLOAT16CONSTANT INT32CONSTANT UINT32CONSTANT +%token INT64CONSTANT UINT64CONSTANT +%token SUBROUTINE DEMOTE +%token PAYLOADNV PAYLOADINNV HITATTRNV CALLDATANV CALLDATAINNV +%token PAYLOADEXT PAYLOADINEXT HITATTREXT CALLDATAEXT CALLDATAINEXT +%token PATCH SAMPLE NONUNIFORM +%token COHERENT VOLATILE RESTRICT READONLY WRITEONLY DEVICECOHERENT QUEUEFAMILYCOHERENT WORKGROUPCOHERENT +%token SUBGROUPCOHERENT NONPRIVATE SHADERCALLCOHERENT +%token NOPERSPECTIVE EXPLICITINTERPAMD PERVERTEXNV PERPRIMITIVENV PERVIEWNV PERTASKNV +%token PRECISE + + +%type assignment_operator unary_operator +%type variable_identifier primary_expression postfix_expression +%type expression integer_expression assignment_expression +%type unary_expression multiplicative_expression additive_expression +%type relational_expression equality_expression +%type conditional_expression constant_expression +%type logical_or_expression logical_xor_expression logical_and_expression +%type shift_expression and_expression exclusive_or_expression inclusive_or_expression +%type function_call initializer condition conditionopt + +%type translation_unit function_definition +%type statement simple_statement +%type statement_list switch_statement_list compound_statement +%type declaration_statement selection_statement selection_statement_nonattributed expression_statement +%type switch_statement switch_statement_nonattributed case_label +%type declaration external_declaration +%type for_init_statement compound_statement_no_new_scope +%type selection_rest_statement for_rest_statement +%type iteration_statement iteration_statement_nonattributed jump_statement statement_no_new_scope statement_scoped +%type single_declaration init_declarator_list + +%type parameter_declaration parameter_declarator parameter_type_specifier + +%type array_specifier +%type invariant_qualifier interpolation_qualifier storage_qualifier precision_qualifier +%type layout_qualifier layout_qualifier_id_list layout_qualifier_id + +%type type_parameter_specifier +%type type_parameter_specifier_opt +%type type_parameter_specifier_list + +%type type_qualifier fully_specified_type type_specifier +%type single_type_qualifier +%type type_specifier_nonarray +%type struct_specifier +%type struct_declarator +%type struct_declarator_list struct_declaration struct_declaration_list +%type block_structure +%type function_header function_declarator +%type function_header_with_parameters +%type function_call_header_with_parameters function_call_header_no_parameters function_call_generic function_prototype +%type function_call_or_method function_identifier function_call_header + +%type identifier_list + + +%type precise_qualifier non_uniform_qualifier +%type type_name_list +%type attribute attribute_list single_attribute +%type demote_statement +%type initializer_list + + +%start translation_unit +%% + +variable_identifier + : IDENTIFIER { + $$ = parseContext.handleVariable($1.loc, $1.symbol, $1.string); + } + ; + +primary_expression + : variable_identifier { + $$ = $1; + } + | LEFT_PAREN expression RIGHT_PAREN { + $$ = $2; + if ($$->getAsConstantUnion()) + $$->getAsConstantUnion()->setExpression(); + } + | FLOATCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtFloat, $1.loc, true); + } + | INTCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.i, $1.loc, true); + } + | UINTCONSTANT { + parseContext.fullIntegerCheck($1.loc, "unsigned literal"); + $$ = parseContext.intermediate.addConstantUnion($1.u, $1.loc, true); + } + | BOOLCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.b, $1.loc, true); + } + + | STRING_LITERAL { + $$ = parseContext.intermediate.addConstantUnion($1.string, $1.loc, true); + } + | INT32CONSTANT { + parseContext.explicitInt32Check($1.loc, "32-bit signed literal"); + $$ = parseContext.intermediate.addConstantUnion($1.i, $1.loc, true); + } + | UINT32CONSTANT { + parseContext.explicitInt32Check($1.loc, "32-bit signed literal"); + $$ = parseContext.intermediate.addConstantUnion($1.u, $1.loc, true); + } + | INT64CONSTANT { + parseContext.int64Check($1.loc, "64-bit integer literal"); + $$ = parseContext.intermediate.addConstantUnion($1.i64, $1.loc, true); + } + | UINT64CONSTANT { + parseContext.int64Check($1.loc, "64-bit unsigned integer literal"); + $$ = parseContext.intermediate.addConstantUnion($1.u64, $1.loc, true); + } + | INT16CONSTANT { + parseContext.explicitInt16Check($1.loc, "16-bit integer literal"); + $$ = parseContext.intermediate.addConstantUnion((short)$1.i, $1.loc, true); + } + | UINT16CONSTANT { + parseContext.explicitInt16Check($1.loc, "16-bit unsigned integer literal"); + $$ = parseContext.intermediate.addConstantUnion((unsigned short)$1.u, $1.loc, true); + } + | DOUBLECONSTANT { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double literal"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double literal"); + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtDouble, $1.loc, true); + } + | FLOAT16CONSTANT { + parseContext.float16Check($1.loc, "half float literal"); + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtFloat16, $1.loc, true); + } + + ; + +postfix_expression + : primary_expression { + $$ = $1; + } + | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET { + $$ = parseContext.handleBracketDereference($2.loc, $1, $3); + } + | function_call { + $$ = $1; + } + | postfix_expression DOT IDENTIFIER { + $$ = parseContext.handleDotDereference($3.loc, $1, *$3.string); + } + | postfix_expression INC_OP { + parseContext.variableCheck($1); + parseContext.lValueErrorCheck($2.loc, "++", $1); + $$ = parseContext.handleUnaryMath($2.loc, "++", EOpPostIncrement, $1); + } + | postfix_expression DEC_OP { + parseContext.variableCheck($1); + parseContext.lValueErrorCheck($2.loc, "--", $1); + $$ = parseContext.handleUnaryMath($2.loc, "--", EOpPostDecrement, $1); + } + ; + +integer_expression + : expression { + parseContext.integerCheck($1, "[]"); + $$ = $1; + } + ; + +function_call + : function_call_or_method { + $$ = parseContext.handleFunctionCall($1.loc, $1.function, $1.intermNode); + delete $1.function; + } + ; + +function_call_or_method + : function_call_generic { + $$ = $1; + } + ; + +function_call_generic + : function_call_header_with_parameters RIGHT_PAREN { + $$ = $1; + $$.loc = $2.loc; + } + | function_call_header_no_parameters RIGHT_PAREN { + $$ = $1; + $$.loc = $2.loc; + } + ; + +function_call_header_no_parameters + : function_call_header VOID { + $$ = $1; + } + | function_call_header { + $$ = $1; + } + ; + +function_call_header_with_parameters + : function_call_header assignment_expression { + TParameter param = { 0, new TType }; + param.type->shallowCopy($2->getType()); + $1.function->addParameter(param); + $$.function = $1.function; + $$.intermNode = $2; + } + | function_call_header_with_parameters COMMA assignment_expression { + TParameter param = { 0, new TType }; + param.type->shallowCopy($3->getType()); + $1.function->addParameter(param); + $$.function = $1.function; + $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, $3, $2.loc); + } + ; + +function_call_header + : function_identifier LEFT_PAREN { + $$ = $1; + } + ; + +// Grammar Note: Constructors look like functions, but are recognized as types. + +function_identifier + : type_specifier { + // Constructor + $$.intermNode = 0; + $$.function = parseContext.handleConstructorCall($1.loc, $1); + } + | postfix_expression { + // + // Should be a method or subroutine call, but we haven't recognized the arguments yet. + // + $$.function = 0; + $$.intermNode = 0; + + TIntermMethod* method = $1->getAsMethodNode(); + if (method) { + $$.function = new TFunction(&method->getMethodName(), TType(EbtInt), EOpArrayLength); + $$.intermNode = method->getObject(); + } else { + TIntermSymbol* symbol = $1->getAsSymbolNode(); + if (symbol) { + parseContext.reservedErrorCheck(symbol->getLoc(), symbol->getName()); + TFunction *function = new TFunction(&symbol->getName(), TType(EbtVoid)); + $$.function = function; + } else + parseContext.error($1->getLoc(), "function call, method, or subroutine call expected", "", ""); + } + + if ($$.function == 0) { + // error recover + TString* empty = NewPoolTString(""); + $$.function = new TFunction(empty, TType(EbtVoid), EOpNull); + } + } + + | non_uniform_qualifier { + // Constructor + $$.intermNode = 0; + $$.function = parseContext.handleConstructorCall($1.loc, $1); + } + + ; + +unary_expression + : postfix_expression { + parseContext.variableCheck($1); + $$ = $1; + if (TIntermMethod* method = $1->getAsMethodNode()) + parseContext.error($1->getLoc(), "incomplete method syntax", method->getMethodName().c_str(), ""); + } + | INC_OP unary_expression { + parseContext.lValueErrorCheck($1.loc, "++", $2); + $$ = parseContext.handleUnaryMath($1.loc, "++", EOpPreIncrement, $2); + } + | DEC_OP unary_expression { + parseContext.lValueErrorCheck($1.loc, "--", $2); + $$ = parseContext.handleUnaryMath($1.loc, "--", EOpPreDecrement, $2); + } + | unary_operator unary_expression { + if ($1.op != EOpNull) { + char errorOp[2] = {0, 0}; + switch($1.op) { + case EOpNegative: errorOp[0] = '-'; break; + case EOpLogicalNot: errorOp[0] = '!'; break; + case EOpBitwiseNot: errorOp[0] = '~'; break; + default: break; // some compilers want this + } + $$ = parseContext.handleUnaryMath($1.loc, errorOp, $1.op, $2); + } else { + $$ = $2; + if ($$->getAsConstantUnion()) + $$->getAsConstantUnion()->setExpression(); + } + } + ; +// Grammar Note: No traditional style type casts. + +unary_operator + : PLUS { $$.loc = $1.loc; $$.op = EOpNull; } + | DASH { $$.loc = $1.loc; $$.op = EOpNegative; } + | BANG { $$.loc = $1.loc; $$.op = EOpLogicalNot; } + | TILDE { $$.loc = $1.loc; $$.op = EOpBitwiseNot; + parseContext.fullIntegerCheck($1.loc, "bitwise not"); } + ; +// Grammar Note: No '*' or '&' unary ops. Pointers are not supported. + +multiplicative_expression + : unary_expression { $$ = $1; } + | multiplicative_expression STAR unary_expression { + $$ = parseContext.handleBinaryMath($2.loc, "*", EOpMul, $1, $3); + if ($$ == 0) + $$ = $1; + } + | multiplicative_expression SLASH unary_expression { + $$ = parseContext.handleBinaryMath($2.loc, "/", EOpDiv, $1, $3); + if ($$ == 0) + $$ = $1; + } + | multiplicative_expression PERCENT unary_expression { + parseContext.fullIntegerCheck($2.loc, "%"); + $$ = parseContext.handleBinaryMath($2.loc, "%", EOpMod, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +additive_expression + : multiplicative_expression { $$ = $1; } + | additive_expression PLUS multiplicative_expression { + $$ = parseContext.handleBinaryMath($2.loc, "+", EOpAdd, $1, $3); + if ($$ == 0) + $$ = $1; + } + | additive_expression DASH multiplicative_expression { + $$ = parseContext.handleBinaryMath($2.loc, "-", EOpSub, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +shift_expression + : additive_expression { $$ = $1; } + | shift_expression LEFT_OP additive_expression { + parseContext.fullIntegerCheck($2.loc, "bit shift left"); + $$ = parseContext.handleBinaryMath($2.loc, "<<", EOpLeftShift, $1, $3); + if ($$ == 0) + $$ = $1; + } + | shift_expression RIGHT_OP additive_expression { + parseContext.fullIntegerCheck($2.loc, "bit shift right"); + $$ = parseContext.handleBinaryMath($2.loc, ">>", EOpRightShift, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +relational_expression + : shift_expression { $$ = $1; } + | relational_expression LEFT_ANGLE shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, "<", EOpLessThan, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | relational_expression RIGHT_ANGLE shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, ">", EOpGreaterThan, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | relational_expression LE_OP shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, "<=", EOpLessThanEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | relational_expression GE_OP shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, ">=", EOpGreaterThanEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +equality_expression + : relational_expression { $$ = $1; } + | equality_expression EQ_OP relational_expression { + parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison"); + parseContext.opaqueCheck($2.loc, $1->getType(), "=="); + parseContext.specializationCheck($2.loc, $1->getType(), "=="); + parseContext.referenceCheck($2.loc, $1->getType(), "=="); + $$ = parseContext.handleBinaryMath($2.loc, "==", EOpEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | equality_expression NE_OP relational_expression { + parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison"); + parseContext.opaqueCheck($2.loc, $1->getType(), "!="); + parseContext.specializationCheck($2.loc, $1->getType(), "!="); + parseContext.referenceCheck($2.loc, $1->getType(), "!="); + $$ = parseContext.handleBinaryMath($2.loc, "!=", EOpNotEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +and_expression + : equality_expression { $$ = $1; } + | and_expression AMPERSAND equality_expression { + parseContext.fullIntegerCheck($2.loc, "bitwise and"); + $$ = parseContext.handleBinaryMath($2.loc, "&", EOpAnd, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +exclusive_or_expression + : and_expression { $$ = $1; } + | exclusive_or_expression CARET and_expression { + parseContext.fullIntegerCheck($2.loc, "bitwise exclusive or"); + $$ = parseContext.handleBinaryMath($2.loc, "^", EOpExclusiveOr, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +inclusive_or_expression + : exclusive_or_expression { $$ = $1; } + | inclusive_or_expression VERTICAL_BAR exclusive_or_expression { + parseContext.fullIntegerCheck($2.loc, "bitwise inclusive or"); + $$ = parseContext.handleBinaryMath($2.loc, "|", EOpInclusiveOr, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +logical_and_expression + : inclusive_or_expression { $$ = $1; } + | logical_and_expression AND_OP inclusive_or_expression { + $$ = parseContext.handleBinaryMath($2.loc, "&&", EOpLogicalAnd, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +logical_xor_expression + : logical_and_expression { $$ = $1; } + | logical_xor_expression XOR_OP logical_and_expression { + $$ = parseContext.handleBinaryMath($2.loc, "^^", EOpLogicalXor, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +logical_or_expression + : logical_xor_expression { $$ = $1; } + | logical_or_expression OR_OP logical_xor_expression { + $$ = parseContext.handleBinaryMath($2.loc, "||", EOpLogicalOr, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +conditional_expression + : logical_or_expression { $$ = $1; } + | logical_or_expression QUESTION { + ++parseContext.controlFlowNestingLevel; + } + expression COLON assignment_expression { + --parseContext.controlFlowNestingLevel; + parseContext.boolCheck($2.loc, $1); + parseContext.rValueErrorCheck($2.loc, "?", $1); + parseContext.rValueErrorCheck($5.loc, ":", $4); + parseContext.rValueErrorCheck($5.loc, ":", $6); + $$ = parseContext.intermediate.addSelection($1, $4, $6, $2.loc); + if ($$ == 0) { + parseContext.binaryOpError($2.loc, ":", $4->getCompleteString(), $6->getCompleteString()); + $$ = $6; + } + } + ; + +assignment_expression + : conditional_expression { $$ = $1; } + | unary_expression assignment_operator assignment_expression { + parseContext.arrayObjectCheck($2.loc, $1->getType(), "array assignment"); + parseContext.opaqueCheck($2.loc, $1->getType(), "="); + parseContext.storage16BitAssignmentCheck($2.loc, $1->getType(), "="); + parseContext.specializationCheck($2.loc, $1->getType(), "="); + parseContext.lValueErrorCheck($2.loc, "assign", $1); + parseContext.rValueErrorCheck($2.loc, "assign", $3); + $$ = parseContext.intermediate.addAssign($2.op, $1, $3, $2.loc); + if ($$ == 0) { + parseContext.assignError($2.loc, "assign", $1->getCompleteString(), $3->getCompleteString()); + $$ = $1; + } + } + ; + +assignment_operator + : EQUAL { + $$.loc = $1.loc; + $$.op = EOpAssign; + } + | MUL_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpMulAssign; + } + | DIV_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpDivAssign; + } + | MOD_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "%="); + $$.loc = $1.loc; + $$.op = EOpModAssign; + } + | ADD_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpAddAssign; + } + | SUB_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpSubAssign; + } + | LEFT_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bit-shift left assign"); + $$.loc = $1.loc; $$.op = EOpLeftShiftAssign; + } + | RIGHT_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bit-shift right assign"); + $$.loc = $1.loc; $$.op = EOpRightShiftAssign; + } + | AND_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bitwise-and assign"); + $$.loc = $1.loc; $$.op = EOpAndAssign; + } + | XOR_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bitwise-xor assign"); + $$.loc = $1.loc; $$.op = EOpExclusiveOrAssign; + } + | OR_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bitwise-or assign"); + $$.loc = $1.loc; $$.op = EOpInclusiveOrAssign; + } + ; + +expression + : assignment_expression { + $$ = $1; + } + | expression COMMA assignment_expression { + parseContext.samplerConstructorLocationCheck($2.loc, ",", $3); + $$ = parseContext.intermediate.addComma($1, $3, $2.loc); + if ($$ == 0) { + parseContext.binaryOpError($2.loc, ",", $1->getCompleteString(), $3->getCompleteString()); + $$ = $3; + } + } + ; + +constant_expression + : conditional_expression { + parseContext.constantValueCheck($1, ""); + $$ = $1; + } + ; + +declaration + : function_prototype SEMICOLON { + parseContext.handleFunctionDeclarator($1.loc, *$1.function, true /* prototype */); + $$ = 0; + // TODO: 4.0 functionality: subroutines: make the identifier a user type for this signature + } + | init_declarator_list SEMICOLON { + if ($1.intermNode && $1.intermNode->getAsAggregate()) + $1.intermNode->getAsAggregate()->setOperator(EOpSequence); + $$ = $1.intermNode; + } + | PRECISION precision_qualifier type_specifier SEMICOLON { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "precision statement"); + // lazy setting of the previous scope's defaults, has effect only the first time it is called in a particular scope + parseContext.symbolTable.setPreviousDefaultPrecisions(&parseContext.defaultPrecision[0]); + parseContext.setDefaultPrecision($1.loc, $3, $2.qualifier.precision); + $$ = 0; + } + | block_structure SEMICOLON { + parseContext.declareBlock($1.loc, *$1.typeList); + $$ = 0; + } + | block_structure IDENTIFIER SEMICOLON { + parseContext.declareBlock($1.loc, *$1.typeList, $2.string); + $$ = 0; + } + | block_structure IDENTIFIER array_specifier SEMICOLON { + parseContext.declareBlock($1.loc, *$1.typeList, $2.string, $3.arraySizes); + $$ = 0; + } + | type_qualifier SEMICOLON { + parseContext.globalQualifierFixCheck($1.loc, $1.qualifier); + parseContext.updateStandaloneQualifierDefaults($1.loc, $1); + $$ = 0; + } + | type_qualifier IDENTIFIER SEMICOLON { + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.addQualifierToExisting($1.loc, $1.qualifier, *$2.string); + $$ = 0; + } + | type_qualifier IDENTIFIER identifier_list SEMICOLON { + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + $3->push_back($2.string); + parseContext.addQualifierToExisting($1.loc, $1.qualifier, *$3); + $$ = 0; + } + ; + +block_structure + : type_qualifier IDENTIFIER LEFT_BRACE { parseContext.nestedBlockCheck($1.loc); } struct_declaration_list RIGHT_BRACE { + --parseContext.structNestingLevel; + parseContext.blockName = $2.string; + parseContext.globalQualifierFixCheck($1.loc, $1.qualifier); + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.currentBlockQualifier = $1.qualifier; + $$.loc = $1.loc; + $$.typeList = $5; + } + +identifier_list + : COMMA IDENTIFIER { + $$ = new TIdentifierList; + $$->push_back($2.string); + } + | identifier_list COMMA IDENTIFIER { + $$ = $1; + $$->push_back($3.string); + } + ; + +function_prototype + : function_declarator RIGHT_PAREN { + $$.function = $1; + $$.loc = $2.loc; + } + ; + +function_declarator + : function_header { + $$ = $1; + } + | function_header_with_parameters { + $$ = $1; + } + ; + + +function_header_with_parameters + : function_header parameter_declaration { + // Add the parameter + $$ = $1; + if ($2.param.type->getBasicType() != EbtVoid) + $1->addParameter($2.param); + else + delete $2.param.type; + } + | function_header_with_parameters COMMA parameter_declaration { + // + // Only first parameter of one-parameter functions can be void + // The check for named parameters not being void is done in parameter_declarator + // + if ($3.param.type->getBasicType() == EbtVoid) { + // + // This parameter > first is void + // + parseContext.error($2.loc, "cannot be an argument type except for '(void)'", "void", ""); + delete $3.param.type; + } else { + // Add the parameter + $$ = $1; + $1->addParameter($3.param); + } + } + ; + +function_header + : fully_specified_type IDENTIFIER LEFT_PAREN { + if ($1.qualifier.storage != EvqGlobal && $1.qualifier.storage != EvqTemporary) { + parseContext.error($2.loc, "no qualifiers allowed for function return", + GetStorageQualifierString($1.qualifier.storage), ""); + } + if ($1.arraySizes) + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + + // Add the function as a prototype after parsing it (we do not support recursion) + TFunction *function; + TType type($1); + + // Potentially rename shader entry point function. No-op most of the time. + parseContext.renameShaderFunction($2.string); + + // Make the function + function = new TFunction($2.string, type); + $$ = function; + } + ; + +parameter_declarator + // Type + name + : type_specifier IDENTIFIER { + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + if ($1.basicType == EbtVoid) { + parseContext.error($2.loc, "illegal use of type 'void'", $2.string->c_str(), ""); + } + parseContext.reservedErrorCheck($2.loc, *$2.string); + + TParameter param = {$2.string, new TType($1)}; + $$.loc = $2.loc; + $$.param = param; + } + | type_specifier IDENTIFIER array_specifier { + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + TType* type = new TType($1); + type->transferArraySizes($3.arraySizes); + type->copyArrayInnerSizes($1.arraySizes); + + parseContext.arrayOfArrayVersionCheck($2.loc, type->getArraySizes()); + parseContext.arraySizeRequiredCheck($3.loc, *$3.arraySizes); + parseContext.reservedErrorCheck($2.loc, *$2.string); + + TParameter param = { $2.string, type }; + + $$.loc = $2.loc; + $$.param = param; + } + ; + +parameter_declaration + // + // With name + // + : type_qualifier parameter_declarator { + $$ = $2; + if ($1.qualifier.precision != EpqNone) + $$.param.type->getQualifier().precision = $1.qualifier.precision; + parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier()); + + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.parameterTypeCheck($2.loc, $1.qualifier.storage, *$$.param.type); + parseContext.paramCheckFix($1.loc, $1.qualifier, *$$.param.type); + + } + | parameter_declarator { + $$ = $1; + + parseContext.parameterTypeCheck($1.loc, EvqIn, *$1.param.type); + parseContext.paramCheckFixStorage($1.loc, EvqTemporary, *$$.param.type); + parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier()); + } + // + // Without name + // + | type_qualifier parameter_type_specifier { + $$ = $2; + if ($1.qualifier.precision != EpqNone) + $$.param.type->getQualifier().precision = $1.qualifier.precision; + parseContext.precisionQualifierCheck($1.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier()); + + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.parameterTypeCheck($2.loc, $1.qualifier.storage, *$$.param.type); + parseContext.paramCheckFix($1.loc, $1.qualifier, *$$.param.type); + } + | parameter_type_specifier { + $$ = $1; + + parseContext.parameterTypeCheck($1.loc, EvqIn, *$1.param.type); + parseContext.paramCheckFixStorage($1.loc, EvqTemporary, *$$.param.type); + parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier()); + } + ; + +parameter_type_specifier + : type_specifier { + TParameter param = { 0, new TType($1) }; + $$.param = param; + if ($1.arraySizes) + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + ; + +init_declarator_list + : single_declaration { + $$ = $1; + } + | init_declarator_list COMMA IDENTIFIER { + $$ = $1; + parseContext.declareVariable($3.loc, *$3.string, $1.type); + } + | init_declarator_list COMMA IDENTIFIER array_specifier { + $$ = $1; + parseContext.declareVariable($3.loc, *$3.string, $1.type, $4.arraySizes); + } + | init_declarator_list COMMA IDENTIFIER array_specifier EQUAL initializer { + $$.type = $1.type; + TIntermNode* initNode = parseContext.declareVariable($3.loc, *$3.string, $1.type, $4.arraySizes, $6); + $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, initNode, $5.loc); + } + | init_declarator_list COMMA IDENTIFIER EQUAL initializer { + $$.type = $1.type; + TIntermNode* initNode = parseContext.declareVariable($3.loc, *$3.string, $1.type, 0, $5); + $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, initNode, $4.loc); + } + ; + +single_declaration + : fully_specified_type { + $$.type = $1; + $$.intermNode = 0; + + parseContext.declareTypeDefaults($$.loc, $$.type); + + } + | fully_specified_type IDENTIFIER { + $$.type = $1; + $$.intermNode = 0; + parseContext.declareVariable($2.loc, *$2.string, $1); + } + | fully_specified_type IDENTIFIER array_specifier { + $$.type = $1; + $$.intermNode = 0; + parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes); + } + | fully_specified_type IDENTIFIER array_specifier EQUAL initializer { + $$.type = $1; + TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes, $5); + $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $4.loc); + } + | fully_specified_type IDENTIFIER EQUAL initializer { + $$.type = $1; + TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, 0, $4); + $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $3.loc); + } + +// Grammar Note: No 'enum', or 'typedef'. + +fully_specified_type + : type_specifier { + $$ = $1; + + parseContext.globalQualifierTypeCheck($1.loc, $1.qualifier, $$); + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + } + parseContext.precisionQualifierCheck($$.loc, $$.basicType, $$.qualifier); + } + | type_qualifier type_specifier { + parseContext.globalQualifierFixCheck($1.loc, $1.qualifier); + parseContext.globalQualifierTypeCheck($1.loc, $1.qualifier, $2); + + if ($2.arraySizes) { + parseContext.profileRequires($2.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($2.loc, EEsProfile, 300, 0, "arrayed type"); + } + + if ($2.arraySizes && parseContext.arrayQualifierError($2.loc, $1.qualifier)) + $2.arraySizes = nullptr; + + parseContext.checkNoShaderLayouts($2.loc, $1.shaderQualifiers); + $2.shaderQualifiers.merge($1.shaderQualifiers); + parseContext.mergeQualifiers($2.loc, $2.qualifier, $1.qualifier, true); + parseContext.precisionQualifierCheck($2.loc, $2.basicType, $2.qualifier); + + $$ = $2; + + if (! $$.qualifier.isInterpolation() && + ((parseContext.language == EShLangVertex && $$.qualifier.storage == EvqVaryingOut) || + (parseContext.language == EShLangFragment && $$.qualifier.storage == EvqVaryingIn))) + $$.qualifier.smooth = true; + } + ; + +invariant_qualifier + : INVARIANT { + parseContext.globalCheck($1.loc, "invariant"); + parseContext.profileRequires($$.loc, ENoProfile, 120, 0, "invariant"); + $$.init($1.loc); + $$.qualifier.invariant = true; + } + ; + +interpolation_qualifier + : SMOOTH { + parseContext.globalCheck($1.loc, "smooth"); + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "smooth"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "smooth"); + $$.init($1.loc); + $$.qualifier.smooth = true; + } + | FLAT { + parseContext.globalCheck($1.loc, "flat"); + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "flat"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "flat"); + $$.init($1.loc); + $$.qualifier.flat = true; + } + + | NOPERSPECTIVE { + parseContext.globalCheck($1.loc, "noperspective"); + parseContext.profileRequires($1.loc, EEsProfile, 0, E_GL_NV_shader_noperspective_interpolation, "noperspective"); + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "noperspective"); + $$.init($1.loc); + $$.qualifier.nopersp = true; + } + | EXPLICITINTERPAMD { + parseContext.globalCheck($1.loc, "__explicitInterpAMD"); + parseContext.profileRequires($1.loc, ECoreProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + parseContext.profileRequires($1.loc, ECompatibilityProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + $$.init($1.loc); + $$.qualifier.explicitInterp = true; + } + | PERVERTEXNV { + parseContext.globalCheck($1.loc, "pervertexNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires($1.loc, ECompatibilityProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires($1.loc, EEsProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + $$.init($1.loc); + $$.qualifier.pervertexNV = true; + } + | PERPRIMITIVENV { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "perprimitiveNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangFragmentMask | EShLangMeshNVMask), "perprimitiveNV"); + // Fragment shader stage doesn't check for extension. So we explicitly add below extension check. + if (parseContext.language == EShLangFragment) + parseContext.requireExtensions($1.loc, 1, &E_GL_NV_mesh_shader, "perprimitiveNV"); + $$.init($1.loc); + $$.qualifier.perPrimitiveNV = true; + } + | PERVIEWNV { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "perviewNV"); + parseContext.requireStage($1.loc, EShLangMeshNV, "perviewNV"); + $$.init($1.loc); + $$.qualifier.perViewNV = true; + } + | PERTASKNV { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "taskNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangTaskNVMask | EShLangMeshNVMask), "taskNV"); + $$.init($1.loc); + $$.qualifier.perTaskNV = true; + } + + ; + +layout_qualifier + : LAYOUT LEFT_PAREN layout_qualifier_id_list RIGHT_PAREN { + $$ = $3; + } + ; + +layout_qualifier_id_list + : layout_qualifier_id { + $$ = $1; + } + | layout_qualifier_id_list COMMA layout_qualifier_id { + $$ = $1; + $$.shaderQualifiers.merge($3.shaderQualifiers); + parseContext.mergeObjectLayoutQualifiers($$.qualifier, $3.qualifier, false); + } + +layout_qualifier_id + : IDENTIFIER { + $$.init($1.loc); + parseContext.setLayoutQualifier($1.loc, $$, *$1.string); + } + | IDENTIFIER EQUAL constant_expression { + $$.init($1.loc); + parseContext.setLayoutQualifier($1.loc, $$, *$1.string, $3); + } + | SHARED { // because "shared" is both an identifier and a keyword + $$.init($1.loc); + TString strShared("shared"); + parseContext.setLayoutQualifier($1.loc, $$, strShared); + } + ; + + +precise_qualifier + : PRECISE { + parseContext.profileRequires($$.loc, ECoreProfile | ECompatibilityProfile, 400, E_GL_ARB_gpu_shader5, "precise"); + parseContext.profileRequires($1.loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, "precise"); + $$.init($1.loc); + $$.qualifier.noContraction = true; + } + ; + + +type_qualifier + : single_type_qualifier { + $$ = $1; + } + | type_qualifier single_type_qualifier { + $$ = $1; + if ($$.basicType == EbtVoid) + $$.basicType = $2.basicType; + + $$.shaderQualifiers.merge($2.shaderQualifiers); + parseContext.mergeQualifiers($$.loc, $$.qualifier, $2.qualifier, false); + } + ; + +single_type_qualifier + : storage_qualifier { + $$ = $1; + } + | layout_qualifier { + $$ = $1; + } + | precision_qualifier { + parseContext.checkPrecisionQualifier($1.loc, $1.qualifier.precision); + $$ = $1; + } + | interpolation_qualifier { + // allow inheritance of storage qualifier from block declaration + $$ = $1; + } + | invariant_qualifier { + // allow inheritance of storage qualifier from block declaration + $$ = $1; + } + + | precise_qualifier { + // allow inheritance of storage qualifier from block declaration + $$ = $1; + } + | non_uniform_qualifier { + $$ = $1; + } + + ; + +storage_qualifier + : CONST { + $$.init($1.loc); + $$.qualifier.storage = EvqConst; // will later turn into EvqConstReadOnly, if the initializer is not constant + } + | INOUT { + parseContext.globalCheck($1.loc, "inout"); + $$.init($1.loc); + $$.qualifier.storage = EvqInOut; + } + | IN { + parseContext.globalCheck($1.loc, "in"); + $$.init($1.loc); + // whether this is a parameter "in" or a pipeline "in" will get sorted out a bit later + $$.qualifier.storage = EvqIn; + } + | OUT { + parseContext.globalCheck($1.loc, "out"); + $$.init($1.loc); + // whether this is a parameter "out" or a pipeline "out" will get sorted out a bit later + $$.qualifier.storage = EvqOut; + } + | CENTROID { + parseContext.profileRequires($1.loc, ENoProfile, 120, 0, "centroid"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "centroid"); + parseContext.globalCheck($1.loc, "centroid"); + $$.init($1.loc); + $$.qualifier.centroid = true; + } + | UNIFORM { + parseContext.globalCheck($1.loc, "uniform"); + $$.init($1.loc); + $$.qualifier.storage = EvqUniform; + } + | SHARED { + parseContext.globalCheck($1.loc, "shared"); + parseContext.profileRequires($1.loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_compute_shader, "shared"); + parseContext.profileRequires($1.loc, EEsProfile, 310, 0, "shared"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangComputeMask | EShLangMeshNVMask | EShLangTaskNVMask), "shared"); + $$.init($1.loc); + $$.qualifier.storage = EvqShared; + } + | BUFFER { + parseContext.globalCheck($1.loc, "buffer"); + $$.init($1.loc); + $$.qualifier.storage = EvqBuffer; + } + + | ATTRIBUTE { + parseContext.requireStage($1.loc, EShLangVertex, "attribute"); + parseContext.checkDeprecated($1.loc, ECoreProfile, 130, "attribute"); + parseContext.checkDeprecated($1.loc, ENoProfile, 130, "attribute"); + parseContext.requireNotRemoved($1.loc, ECoreProfile, 420, "attribute"); + parseContext.requireNotRemoved($1.loc, EEsProfile, 300, "attribute"); + + parseContext.globalCheck($1.loc, "attribute"); + + $$.init($1.loc); + $$.qualifier.storage = EvqVaryingIn; + } + | VARYING { + parseContext.checkDeprecated($1.loc, ENoProfile, 130, "varying"); + parseContext.checkDeprecated($1.loc, ECoreProfile, 130, "varying"); + parseContext.requireNotRemoved($1.loc, ECoreProfile, 420, "varying"); + parseContext.requireNotRemoved($1.loc, EEsProfile, 300, "varying"); + + parseContext.globalCheck($1.loc, "varying"); + + $$.init($1.loc); + if (parseContext.language == EShLangVertex) + $$.qualifier.storage = EvqVaryingOut; + else + $$.qualifier.storage = EvqVaryingIn; + } + | PATCH { + parseContext.globalCheck($1.loc, "patch"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangTessControlMask | EShLangTessEvaluationMask), "patch"); + $$.init($1.loc); + $$.qualifier.patch = true; + } + | SAMPLE { + parseContext.globalCheck($1.loc, "sample"); + $$.init($1.loc); + $$.qualifier.sample = true; + } + | HITATTRNV { + parseContext.globalCheck($1.loc, "hitAttributeNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "hitAttributeNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqHitAttr; + } + | HITATTREXT { + parseContext.globalCheck($1.loc, "hitAttributeEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "hitAttributeNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqHitAttr; + } + | PAYLOADNV { + parseContext.globalCheck($1.loc, "rayPayloadNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayload; + } + | PAYLOADEXT { + parseContext.globalCheck($1.loc, "rayPayloadEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayload; + } + | PAYLOADINNV { + parseContext.globalCheck($1.loc, "rayPayloadInNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadInNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayloadIn; + } + | PAYLOADINEXT { + parseContext.globalCheck($1.loc, "rayPayloadInEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadInEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayloadIn; + } + | CALLDATANV { + parseContext.globalCheck($1.loc, "callableDataNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableData; + } + | CALLDATAEXT { + parseContext.globalCheck($1.loc, "callableDataEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableData; + } + | CALLDATAINNV { + parseContext.globalCheck($1.loc, "callableDataInNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataInNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableDataIn; + } + | CALLDATAINEXT { + parseContext.globalCheck($1.loc, "callableDataInEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataInEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableDataIn; + } + | COHERENT { + $$.init($1.loc); + $$.qualifier.coherent = true; + } + | DEVICECOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "devicecoherent"); + $$.qualifier.devicecoherent = true; + } + | QUEUEFAMILYCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "queuefamilycoherent"); + $$.qualifier.queuefamilycoherent = true; + } + | WORKGROUPCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "workgroupcoherent"); + $$.qualifier.workgroupcoherent = true; + } + | SUBGROUPCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "subgroupcoherent"); + $$.qualifier.subgroupcoherent = true; + } + | NONPRIVATE { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "nonprivate"); + $$.qualifier.nonprivate = true; + } + | SHADERCALLCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_ray_tracing, "shadercallcoherent"); + $$.qualifier.shadercallcoherent = true; + } + | VOLATILE { + $$.init($1.loc); + $$.qualifier.volatil = true; + } + | RESTRICT { + $$.init($1.loc); + $$.qualifier.restrict = true; + } + | READONLY { + $$.init($1.loc); + $$.qualifier.readonly = true; + } + | WRITEONLY { + $$.init($1.loc); + $$.qualifier.writeonly = true; + } + | SUBROUTINE { + parseContext.spvRemoved($1.loc, "subroutine"); + parseContext.globalCheck($1.loc, "subroutine"); + parseContext.unimplemented($1.loc, "subroutine"); + $$.init($1.loc); + } + | SUBROUTINE LEFT_PAREN type_name_list RIGHT_PAREN { + parseContext.spvRemoved($1.loc, "subroutine"); + parseContext.globalCheck($1.loc, "subroutine"); + parseContext.unimplemented($1.loc, "subroutine"); + $$.init($1.loc); + } + + ; + + +non_uniform_qualifier + : NONUNIFORM { + $$.init($1.loc); + $$.qualifier.nonUniform = true; + } + ; + +type_name_list + : IDENTIFIER { + // TODO + } + | type_name_list COMMA IDENTIFIER { + // TODO: 4.0 semantics: subroutines + // 1) make sure each identifier is a type declared earlier with SUBROUTINE + // 2) save all of the identifiers for future comparison with the declared function + } + ; + + +type_specifier + : type_specifier_nonarray type_parameter_specifier_opt { + $$ = $1; + $$.qualifier.precision = parseContext.getDefaultPrecision($$); + $$.typeParameters = $2; + } + | type_specifier_nonarray type_parameter_specifier_opt array_specifier { + parseContext.arrayOfArrayVersionCheck($3.loc, $3.arraySizes); + $$ = $1; + $$.qualifier.precision = parseContext.getDefaultPrecision($$); + $$.typeParameters = $2; + $$.arraySizes = $3.arraySizes; + } + ; + +array_specifier + : LEFT_BRACKET RIGHT_BRACKET { + $$.loc = $1.loc; + $$.arraySizes = new TArraySizes; + $$.arraySizes->addInnerSize(); + } + | LEFT_BRACKET conditional_expression RIGHT_BRACKET { + $$.loc = $1.loc; + $$.arraySizes = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck($2->getLoc(), $2, size, "array size"); + $$.arraySizes->addInnerSize(size); + } + | array_specifier LEFT_BRACKET RIGHT_BRACKET { + $$ = $1; + $$.arraySizes->addInnerSize(); + } + | array_specifier LEFT_BRACKET conditional_expression RIGHT_BRACKET { + $$ = $1; + + TArraySize size; + parseContext.arraySizeCheck($3->getLoc(), $3, size, "array size"); + $$.arraySizes->addInnerSize(size); + } + ; + +type_parameter_specifier_opt + : type_parameter_specifier { + $$ = $1; + } + | /* May be null */ { + $$ = 0; + } + ; + +type_parameter_specifier + : LEFT_ANGLE type_parameter_specifier_list RIGHT_ANGLE { + $$ = $2; + } + ; + +type_parameter_specifier_list + : unary_expression { + $$ = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck($1->getLoc(), $1, size, "type parameter"); + $$->addInnerSize(size); + } + | type_parameter_specifier_list COMMA unary_expression { + $$ = $1; + + TArraySize size; + parseContext.arraySizeCheck($3->getLoc(), $3, size, "type parameter"); + $$->addInnerSize(size); + } + ; + +type_specifier_nonarray + : VOID { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtVoid; + } + | FLOAT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + } + | INT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + } + | UINT { + parseContext.fullIntegerCheck($1.loc, "unsigned integer"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + } + | BOOL { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + } + | VEC2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(2); + } + | VEC3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(3); + } + | VEC4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(4); + } + | BVEC2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + $$.setVector(2); + } + | BVEC3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + $$.setVector(3); + } + | BVEC4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + $$.setVector(4); + } + | IVEC2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(2); + } + | IVEC3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(3); + } + | IVEC4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(4); + } + | UVEC2 { + parseContext.fullIntegerCheck($1.loc, "unsigned integer vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(2); + } + | UVEC3 { + parseContext.fullIntegerCheck($1.loc, "unsigned integer vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(3); + } + | UVEC4 { + parseContext.fullIntegerCheck($1.loc, "unsigned integer vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(4); + } + | MAT2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | MAT3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | MAT4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + | MAT2X2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | MAT2X3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 3); + } + | MAT2X4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 4); + } + | MAT3X2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 2); + } + | MAT3X3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | MAT3X4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 4); + } + | MAT4X2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 2); + } + | MAT4X3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 3); + } + | MAT4X4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + + | DOUBLE { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + } + | FLOAT16_T { + parseContext.float16ScalarVectorCheck($1.loc, "float16_t", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + } + | FLOAT32_T { + parseContext.explicitFloat32Check($1.loc, "float32_t", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + } + | FLOAT64_T { + parseContext.explicitFloat64Check($1.loc, "float64_t", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + } + | INT8_T { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + } + | UINT8_T { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + } + | INT16_T { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + } + | UINT16_T { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + } + | INT32_T { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + } + | UINT32_T { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + } + | INT64_T { + parseContext.int64Check($1.loc, "64-bit integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + } + | UINT64_T { + parseContext.int64Check($1.loc, "64-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + } + | DVEC2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(2); + } + | DVEC3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(3); + } + | DVEC4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(4); + } + | F16VEC2 { + parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setVector(2); + } + | F16VEC3 { + parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setVector(3); + } + | F16VEC4 { + parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setVector(4); + } + | F32VEC2 { + parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(2); + } + | F32VEC3 { + parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(3); + } + | F32VEC4 { + parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(4); + } + | F64VEC2 { + parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(2); + } + | F64VEC3 { + parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(3); + } + | F64VEC4 { + parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(4); + } + | I8VEC2 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + $$.setVector(2); + } + | I8VEC3 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + $$.setVector(3); + } + | I8VEC4 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + $$.setVector(4); + } + | I16VEC2 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + $$.setVector(2); + } + | I16VEC3 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + $$.setVector(3); + } + | I16VEC4 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + $$.setVector(4); + } + | I32VEC2 { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(2); + } + | I32VEC3 { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(3); + } + | I32VEC4 { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(4); + } + | I64VEC2 { + parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + $$.setVector(2); + } + | I64VEC3 { + parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + $$.setVector(3); + } + | I64VEC4 { + parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + $$.setVector(4); + } + | U8VEC2 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + $$.setVector(2); + } + | U8VEC3 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + $$.setVector(3); + } + | U8VEC4 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + $$.setVector(4); + } + | U16VEC2 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + $$.setVector(2); + } + | U16VEC3 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + $$.setVector(3); + } + | U16VEC4 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + $$.setVector(4); + } + | U32VEC2 { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(2); + } + | U32VEC3 { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(3); + } + | U32VEC4 { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(4); + } + | U64VEC2 { + parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + $$.setVector(2); + } + | U64VEC3 { + parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + $$.setVector(3); + } + | U64VEC4 { + parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + $$.setVector(4); + } + | DMAT2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | DMAT3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | DMAT4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | DMAT2X2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | DMAT2X3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 3); + } + | DMAT2X4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 4); + } + | DMAT3X2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 2); + } + | DMAT3X3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | DMAT3X4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 4); + } + | DMAT4X2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 2); + } + | DMAT4X3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 3); + } + | DMAT4X4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | F16MAT2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 2); + } + | F16MAT3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 3); + } + | F16MAT4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 4); + } + | F16MAT2X2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 2); + } + | F16MAT2X3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 3); + } + | F16MAT2X4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 4); + } + | F16MAT3X2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 2); + } + | F16MAT3X3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 3); + } + | F16MAT3X4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 4); + } + | F16MAT4X2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 2); + } + | F16MAT4X3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 3); + } + | F16MAT4X4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 4); + } + | F32MAT2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | F32MAT3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | F32MAT4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + | F32MAT2X2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | F32MAT2X3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 3); + } + | F32MAT2X4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 4); + } + | F32MAT3X2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 2); + } + | F32MAT3X3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | F32MAT3X4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 4); + } + | F32MAT4X2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 2); + } + | F32MAT4X3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 3); + } + | F32MAT4X4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + | F64MAT2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | F64MAT3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | F64MAT4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | F64MAT2X2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | F64MAT2X3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 3); + } + | F64MAT2X4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 4); + } + | F64MAT3X2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 2); + } + | F64MAT3X3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | F64MAT3X4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 4); + } + | F64MAT4X2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 2); + } + | F64MAT4X3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 3); + } + | F64MAT4X4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | ACCSTRUCTNV { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtAccStruct; + } + | ACCSTRUCTEXT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtAccStruct; + } + | RAYQUERYEXT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtRayQuery; + } + | ATOMIC_UINT { + parseContext.vulkanRemoved($1.loc, "atomic counter types"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtAtomicUint; + } + | SAMPLER1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D); + } + + | SAMPLER2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D); + } + | SAMPLER3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd3D); + } + | SAMPLERCUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube); + } + | SAMPLER2DSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, false, true); + } + | SAMPLERCUBESHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube, false, true); + } + | SAMPLER2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, true); + } + | SAMPLER2DARRAYSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, true, true); + } + + | SAMPLER1DSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D, false, true); + } + | SAMPLER1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D, true); + } + | SAMPLER1DARRAYSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D, true, true); + } + | SAMPLERCUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube, true); + } + | SAMPLERCUBEARRAYSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube, true, true); + } + | F16SAMPLER1D { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D); + } + | F16SAMPLER2D { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D); + } + | F16SAMPLER3D { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd3D); + } + | F16SAMPLERCUBE { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube); + } + | F16SAMPLER1DSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D, false, true); + } + | F16SAMPLER2DSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, false, true); + } + | F16SAMPLERCUBESHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube, false, true); + } + | F16SAMPLER1DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D, true); + } + | F16SAMPLER2DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, true); + } + | F16SAMPLER1DARRAYSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D, true, true); + } + | F16SAMPLER2DARRAYSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, true, true); + } + | F16SAMPLERCUBEARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube, true); + } + | F16SAMPLERCUBEARRAYSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube, true, true); + } + | ISAMPLER1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd1D); + } + + | ISAMPLER2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D); + } + | ISAMPLER3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd3D); + } + | ISAMPLERCUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdCube); + } + | ISAMPLER2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D, true); + } + | USAMPLER2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D); + } + | USAMPLER3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd3D); + } + | USAMPLERCUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdCube); + } + + | ISAMPLER1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd1D, true); + } + | ISAMPLERCUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdCube, true); + } + | USAMPLER1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd1D); + } + | USAMPLER1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd1D, true); + } + | USAMPLERCUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdCube, true); + } + | TEXTURECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdCube, true); + } + | ITEXTURECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdCube, true); + } + | UTEXTURECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdCube, true); + } + + | USAMPLER2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D, true); + } + | TEXTURE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D); + } + | TEXTURE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd3D); + } + | TEXTURE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D, true); + } + | TEXTURECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdCube); + } + | ITEXTURE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D); + } + | ITEXTURE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd3D); + } + | ITEXTURECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdCube); + } + | ITEXTURE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D, true); + } + | UTEXTURE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D); + } + | UTEXTURE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd3D); + } + | UTEXTURECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdCube); + } + | UTEXTURE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D, true); + } + | SAMPLER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setPureSampler(false); + } + | SAMPLERSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setPureSampler(true); + } + + | SAMPLER2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdRect); + } + | SAMPLER2DRECTSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdRect, false, true); + } + | F16SAMPLER2DRECT { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdRect); + } + | F16SAMPLER2DRECTSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdRect, false, true); + } + | ISAMPLER2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdRect); + } + | USAMPLER2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdRect); + } + | SAMPLERBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdBuffer); + } + | F16SAMPLERBUFFER { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdBuffer); + } + | ISAMPLERBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdBuffer); + } + | USAMPLERBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdBuffer); + } + | SAMPLER2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, false, false, true); + } + | F16SAMPLER2DMS { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, false, false, true); + } + | ISAMPLER2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D, false, false, true); + } + | USAMPLER2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D, false, false, true); + } + | SAMPLER2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, true, false, true); + } + | F16SAMPLER2DMSARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, true, false, true); + } + | ISAMPLER2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D, true, false, true); + } + | USAMPLER2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D, true, false, true); + } + | TEXTURE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd1D); + } + | F16TEXTURE1D { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd1D); + } + | F16TEXTURE2D { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D); + } + | F16TEXTURE3D { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd3D); + } + | F16TEXTURECUBE { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdCube); + } + | TEXTURE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd1D, true); + } + | F16TEXTURE1DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd1D, true); + } + | F16TEXTURE2DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D, true); + } + | F16TEXTURECUBEARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdCube, true); + } + | ITEXTURE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd1D); + } + | ITEXTURE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd1D, true); + } + | UTEXTURE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd1D); + } + | UTEXTURE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd1D, true); + } + | TEXTURE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdRect); + } + | F16TEXTURE2DRECT { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdRect); + } + | ITEXTURE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdRect); + } + | UTEXTURE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdRect); + } + | TEXTUREBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdBuffer); + } + | F16TEXTUREBUFFER { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdBuffer); + } + | ITEXTUREBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdBuffer); + } + | UTEXTUREBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdBuffer); + } + | TEXTURE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D, false, false, true); + } + | F16TEXTURE2DMS { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D, false, false, true); + } + | ITEXTURE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D, false, false, true); + } + | UTEXTURE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D, false, false, true); + } + | TEXTURE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D, true, false, true); + } + | F16TEXTURE2DMSARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D, true, false, true); + } + | ITEXTURE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D, true, false, true); + } + | UTEXTURE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D, true, false, true); + } + | IMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd1D); + } + | F16IMAGE1D { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd1D); + } + | IIMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd1D); + } + | UIMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd1D); + } + | IMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D); + } + | F16IMAGE2D { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D); + } + | IIMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D); + } + | UIMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D); + } + | IMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd3D); + } + | F16IMAGE3D { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd3D); + } + | IIMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd3D); + } + | UIMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd3D); + } + | IMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdRect); + } + | F16IMAGE2DRECT { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdRect); + } + | IIMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdRect); + } + | UIMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdRect); + } + | IMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdCube); + } + | F16IMAGECUBE { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdCube); + } + | IIMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdCube); + } + | UIMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdCube); + } + | IMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdBuffer); + } + | F16IMAGEBUFFER { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdBuffer); + } + | IIMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdBuffer); + } + | UIMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdBuffer); + } + | IMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd1D, true); + } + | F16IMAGE1DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd1D, true); + } + | IIMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd1D, true); + } + | UIMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd1D, true); + } + | IMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D, true); + } + | F16IMAGE2DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D, true); + } + | IIMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D, true); + } + | UIMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D, true); + } + | IMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdCube, true); + } + | F16IMAGECUBEARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdCube, true); + } + | IIMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdCube, true); + } + | UIMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdCube, true); + } + | IMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D, false, false, true); + } + | F16IMAGE2DMS { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D, false, false, true); + } + | IIMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D, false, false, true); + } + | UIMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D, false, false, true); + } + | IMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D, true, false, true); + } + | F16IMAGE2DMSARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D, true, false, true); + } + | IIMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D, true, false, true); + } + | UIMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D, true, false, true); + } + | SAMPLEREXTERNALOES { // GL_OES_EGL_image_external + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D); + $$.sampler.external = true; + } + | SAMPLEREXTERNAL2DY2YEXT { // GL_EXT_YUV_target + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D); + $$.sampler.yuv = true; + } + | SAMPLERVIDEO { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D); + $$.sampler.video = true; + } + | SUBPASSINPUT { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat); + } + | SUBPASSINPUTMS { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat, true); + } + | F16SUBPASSINPUT { + parseContext.float16OpaqueCheck($1.loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat16); + } + | F16SUBPASSINPUTMS { + parseContext.float16OpaqueCheck($1.loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat16, true); + } + | ISUBPASSINPUT { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtInt); + } + | ISUBPASSINPUTMS { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtInt, true); + } + | USUBPASSINPUT { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtUint); + } + | USUBPASSINPUTMS { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtUint, true); + } + | FCOOPMATNV { + parseContext.fcoopmatCheck($1.loc, "fcoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.coopmat = true; + } + | ICOOPMATNV { + parseContext.intcoopmatCheck($1.loc, "icoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.coopmat = true; + } + | UCOOPMATNV { + parseContext.intcoopmatCheck($1.loc, "ucoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.coopmat = true; + } + + | struct_specifier { + $$ = $1; + $$.qualifier.storage = parseContext.symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; + parseContext.structTypeCheck($$.loc, $$); + } + | TYPE_NAME { + // + // This is for user defined type names. The lexical phase looked up the + // type. + // + if (const TVariable* variable = ($1.symbol)->getAsVariable()) { + const TType& structure = variable->getType(); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtStruct; + $$.userDef = &structure; + } else + parseContext.error($1.loc, "expected type name", $1.string->c_str(), ""); + } + ; + +precision_qualifier + : HIGH_PRECISION { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "highp precision qualifier"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqHigh); + } + | MEDIUM_PRECISION { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "mediump precision qualifier"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqMedium); + } + | LOW_PRECISION { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "lowp precision qualifier"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqLow); + } + ; + +struct_specifier + : STRUCT IDENTIFIER LEFT_BRACE { parseContext.nestedStructCheck($1.loc); } struct_declaration_list RIGHT_BRACE { + TType* structure = new TType($5, *$2.string); + parseContext.structArrayCheck($2.loc, *structure); + TVariable* userTypeDef = new TVariable($2.string, *structure, true); + if (! parseContext.symbolTable.insert(*userTypeDef)) + parseContext.error($2.loc, "redefinition", $2.string->c_str(), "struct"); + $$.init($1.loc); + $$.basicType = EbtStruct; + $$.userDef = structure; + --parseContext.structNestingLevel; + } + | STRUCT LEFT_BRACE { parseContext.nestedStructCheck($1.loc); } struct_declaration_list RIGHT_BRACE { + TType* structure = new TType($4, TString("")); + $$.init($1.loc); + $$.basicType = EbtStruct; + $$.userDef = structure; + --parseContext.structNestingLevel; + } + ; + +struct_declaration_list + : struct_declaration { + $$ = $1; + } + | struct_declaration_list struct_declaration { + $$ = $1; + for (unsigned int i = 0; i < $2->size(); ++i) { + for (unsigned int j = 0; j < $$->size(); ++j) { + if ((*$$)[j].type->getFieldName() == (*$2)[i].type->getFieldName()) + parseContext.error((*$2)[i].loc, "duplicate member name:", "", (*$2)[i].type->getFieldName().c_str()); + } + $$->push_back((*$2)[i]); + } + } + ; + +struct_declaration + : type_specifier struct_declarator_list SEMICOLON { + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + + $$ = $2; + + parseContext.voidErrorCheck($1.loc, (*$2)[0].type->getFieldName(), $1.basicType); + parseContext.precisionQualifierCheck($1.loc, $1.basicType, $1.qualifier); + + for (unsigned int i = 0; i < $$->size(); ++i) { + TType type($1); + type.setFieldName((*$$)[i].type->getFieldName()); + type.transferArraySizes((*$$)[i].type->getArraySizes()); + type.copyArrayInnerSizes($1.arraySizes); + parseContext.arrayOfArrayVersionCheck((*$$)[i].loc, type.getArraySizes()); + (*$$)[i].type->shallowCopy(type); + } + } + | type_qualifier type_specifier struct_declarator_list SEMICOLON { + if ($2.arraySizes) { + parseContext.profileRequires($2.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($2.loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck($2.loc, *$2.arraySizes); + } + + $$ = $3; + + parseContext.memberQualifierCheck($1); + parseContext.voidErrorCheck($2.loc, (*$3)[0].type->getFieldName(), $2.basicType); + parseContext.mergeQualifiers($2.loc, $2.qualifier, $1.qualifier, true); + parseContext.precisionQualifierCheck($2.loc, $2.basicType, $2.qualifier); + + for (unsigned int i = 0; i < $$->size(); ++i) { + TType type($2); + type.setFieldName((*$$)[i].type->getFieldName()); + type.transferArraySizes((*$$)[i].type->getArraySizes()); + type.copyArrayInnerSizes($2.arraySizes); + parseContext.arrayOfArrayVersionCheck((*$$)[i].loc, type.getArraySizes()); + (*$$)[i].type->shallowCopy(type); + } + } + ; + +struct_declarator_list + : struct_declarator { + $$ = new TTypeList; + $$->push_back($1); + } + | struct_declarator_list COMMA struct_declarator { + $$->push_back($3); + } + ; + +struct_declarator + : IDENTIFIER { + $$.type = new TType(EbtVoid); + $$.loc = $1.loc; + $$.type->setFieldName(*$1.string); + } + | IDENTIFIER array_specifier { + parseContext.arrayOfArrayVersionCheck($1.loc, $2.arraySizes); + + $$.type = new TType(EbtVoid); + $$.loc = $1.loc; + $$.type->setFieldName(*$1.string); + $$.type->transferArraySizes($2.arraySizes); + } + ; + +initializer + : assignment_expression { + $$ = $1; + } + + | LEFT_BRACE initializer_list RIGHT_BRACE { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile($1.loc, ~EEsProfile, initFeature); + parseContext.profileRequires($1.loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + $$ = $2; + } + | LEFT_BRACE initializer_list COMMA RIGHT_BRACE { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile($1.loc, ~EEsProfile, initFeature); + parseContext.profileRequires($1.loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + $$ = $2; + } + + ; + + +initializer_list + : initializer { + $$ = parseContext.intermediate.growAggregate(0, $1, $1->getLoc()); + } + | initializer_list COMMA initializer { + $$ = parseContext.intermediate.growAggregate($1, $3); + } + ; + + +declaration_statement + : declaration { $$ = $1; } + ; + +statement + : compound_statement { $$ = $1; } + | simple_statement { $$ = $1; } + ; + +// Grammar Note: labeled statements for switch statements only; 'goto' is not supported. + +simple_statement + : declaration_statement { $$ = $1; } + | expression_statement { $$ = $1; } + | selection_statement { $$ = $1; } + | switch_statement { $$ = $1; } + | case_label { $$ = $1; } + | iteration_statement { $$ = $1; } + | jump_statement { $$ = $1; } + + | demote_statement { $$ = $1; } + + ; + + +demote_statement + : DEMOTE SEMICOLON { + parseContext.requireStage($1.loc, EShLangFragment, "demote"); + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_demote_to_helper_invocation, "demote"); + $$ = parseContext.intermediate.addBranch(EOpDemote, $1.loc); + } + ; + + +compound_statement + : LEFT_BRACE RIGHT_BRACE { $$ = 0; } + | LEFT_BRACE { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + } + statement_list { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + } + RIGHT_BRACE { + if ($3 && $3->getAsAggregate()) + $3->getAsAggregate()->setOperator(EOpSequence); + $$ = $3; + } + ; + +statement_no_new_scope + : compound_statement_no_new_scope { $$ = $1; } + | simple_statement { $$ = $1; } + ; + +statement_scoped + : { + ++parseContext.controlFlowNestingLevel; + } + compound_statement { + --parseContext.controlFlowNestingLevel; + $$ = $2; + } + | { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + simple_statement { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + $$ = $2; + } + +compound_statement_no_new_scope + // Statement that doesn't create a new scope, for selection_statement, iteration_statement + : LEFT_BRACE RIGHT_BRACE { + $$ = 0; + } + | LEFT_BRACE statement_list RIGHT_BRACE { + if ($2 && $2->getAsAggregate()) + $2->getAsAggregate()->setOperator(EOpSequence); + $$ = $2; + } + ; + +statement_list + : statement { + $$ = parseContext.intermediate.makeAggregate($1); + if ($1 && $1->getAsBranchNode() && ($1->getAsBranchNode()->getFlowOp() == EOpCase || + $1->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence(0, $1); + $$ = 0; // start a fresh subsequence for what's after this case + } + } + | statement_list statement { + if ($2 && $2->getAsBranchNode() && ($2->getAsBranchNode()->getFlowOp() == EOpCase || + $2->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence($1 ? $1->getAsAggregate() : 0, $2); + $$ = 0; // start a fresh subsequence for what's after this case + } else + $$ = parseContext.intermediate.growAggregate($1, $2); + } + ; + +expression_statement + : SEMICOLON { $$ = 0; } + | expression SEMICOLON { $$ = static_cast($1); } + ; + +selection_statement + : selection_statement_nonattributed { + $$ = $1; + } + + | attribute selection_statement_nonattributed { + parseContext.handleSelectionAttributes(*$1, $2); + $$ = $2; + } + + +selection_statement_nonattributed + : IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement { + parseContext.boolCheck($1.loc, $3); + $$ = parseContext.intermediate.addSelection($3, $5, $1.loc); + } + ; + +selection_rest_statement + : statement_scoped ELSE statement_scoped { + $$.node1 = $1; + $$.node2 = $3; + } + | statement_scoped { + $$.node1 = $1; + $$.node2 = 0; + } + ; + +condition + // In 1996 c++ draft, conditions can include single declarations + : expression { + $$ = $1; + parseContext.boolCheck($1->getLoc(), $1); + } + | fully_specified_type IDENTIFIER EQUAL initializer { + parseContext.boolCheck($2.loc, $1); + + TType type($1); + TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, 0, $4); + if (initNode) + $$ = initNode->getAsTyped(); + else + $$ = 0; + } + ; + +switch_statement + : switch_statement_nonattributed { + $$ = $1; + } + + | attribute switch_statement_nonattributed { + parseContext.handleSwitchAttributes(*$1, $2); + $$ = $2; + } + + +switch_statement_nonattributed + : SWITCH LEFT_PAREN expression RIGHT_PAREN { + // start new switch sequence on the switch stack + ++parseContext.controlFlowNestingLevel; + ++parseContext.statementNestingLevel; + parseContext.switchSequenceStack.push_back(new TIntermSequence); + parseContext.switchLevel.push_back(parseContext.statementNestingLevel); + parseContext.symbolTable.push(); + } + LEFT_BRACE switch_statement_list RIGHT_BRACE { + $$ = parseContext.addSwitch($1.loc, $3, $7 ? $7->getAsAggregate() : 0); + delete parseContext.switchSequenceStack.back(); + parseContext.switchSequenceStack.pop_back(); + parseContext.switchLevel.pop_back(); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + ; + +switch_statement_list + : /* nothing */ { + $$ = 0; + } + | statement_list { + $$ = $1; + } + ; + +case_label + : CASE expression COLON { + $$ = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error($1.loc, "cannot appear outside switch statement", "case", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error($1.loc, "cannot be nested inside control flow", "case", ""); + else { + parseContext.constantValueCheck($2, "case"); + parseContext.integerCheck($2, "case"); + $$ = parseContext.intermediate.addBranch(EOpCase, $2, $1.loc); + } + } + | DEFAULT COLON { + $$ = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error($1.loc, "cannot appear outside switch statement", "default", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error($1.loc, "cannot be nested inside control flow", "default", ""); + else + $$ = parseContext.intermediate.addBranch(EOpDefault, $1.loc); + } + ; + +iteration_statement + : iteration_statement_nonattributed { + $$ = $1; + } + + | attribute iteration_statement_nonattributed { + parseContext.handleLoopAttributes(*$1, $2); + $$ = $2; + } + + +iteration_statement_nonattributed + : WHILE LEFT_PAREN { + if (! parseContext.limits.whileLoops) + parseContext.error($1.loc, "while loops not available", "limitation", ""); + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + condition RIGHT_PAREN statement_no_new_scope { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + $$ = parseContext.intermediate.addLoop($6, $4, 0, true, $1.loc); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + | DO { + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + statement WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON { + if (! parseContext.limits.whileLoops) + parseContext.error($1.loc, "do-while loops not available", "limitation", ""); + + parseContext.boolCheck($8.loc, $6); + + $$ = parseContext.intermediate.addLoop($3, $6, 0, false, $4.loc); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + | FOR LEFT_PAREN { + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + $$ = parseContext.intermediate.makeAggregate($4, $2.loc); + TIntermLoop* forLoop = parseContext.intermediate.addLoop($7, reinterpret_cast($5.node1), reinterpret_cast($5.node2), true, $1.loc); + if (! parseContext.limits.nonInductiveForLoops) + parseContext.inductiveLoopCheck($1.loc, $4, forLoop); + $$ = parseContext.intermediate.growAggregate($$, forLoop, $1.loc); + $$->getAsAggregate()->setOperator(EOpSequence); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + ; + +for_init_statement + : expression_statement { + $$ = $1; + } + | declaration_statement { + $$ = $1; + } + ; + +conditionopt + : condition { + $$ = $1; + } + | /* May be null */ { + $$ = 0; + } + ; + +for_rest_statement + : conditionopt SEMICOLON { + $$.node1 = $1; + $$.node2 = 0; + } + | conditionopt SEMICOLON expression { + $$.node1 = $1; + $$.node2 = $3; + } + ; + +jump_statement + : CONTINUE SEMICOLON { + if (parseContext.loopNestingLevel <= 0) + parseContext.error($1.loc, "continue statement only allowed in loops", "", ""); + $$ = parseContext.intermediate.addBranch(EOpContinue, $1.loc); + } + | BREAK SEMICOLON { + if (parseContext.loopNestingLevel + parseContext.switchSequenceStack.size() <= 0) + parseContext.error($1.loc, "break statement only allowed in switch and loops", "", ""); + $$ = parseContext.intermediate.addBranch(EOpBreak, $1.loc); + } + | RETURN SEMICOLON { + $$ = parseContext.intermediate.addBranch(EOpReturn, $1.loc); + if (parseContext.currentFunctionType->getBasicType() != EbtVoid) + parseContext.error($1.loc, "non-void function must return a value", "return", ""); + if (parseContext.inMain) + parseContext.postEntryPointReturn = true; + } + | RETURN expression SEMICOLON { + $$ = parseContext.handleReturnValue($1.loc, $2); + } + | DISCARD SEMICOLON { + parseContext.requireStage($1.loc, EShLangFragment, "discard"); + $$ = parseContext.intermediate.addBranch(EOpKill, $1.loc); + } + ; + +// Grammar Note: No 'goto'. Gotos are not supported. + +translation_unit + : external_declaration { + $$ = $1; + parseContext.intermediate.setTreeRoot($$); + } + | translation_unit external_declaration { + if ($2 != nullptr) { + $$ = parseContext.intermediate.growAggregate($1, $2); + parseContext.intermediate.setTreeRoot($$); + } + } + ; + +external_declaration + : function_definition { + $$ = $1; + } + | declaration { + $$ = $1; + } + + | SEMICOLON { + parseContext.requireProfile($1.loc, ~EEsProfile, "extraneous semicolon"); + parseContext.profileRequires($1.loc, ~EEsProfile, 460, nullptr, "extraneous semicolon"); + $$ = nullptr; + } + + ; + +function_definition + : function_prototype { + $1.function = parseContext.handleFunctionDeclarator($1.loc, *$1.function, false /* not prototype */); + $1.intermNode = parseContext.handleFunctionDefinition($1.loc, *$1.function); + } + compound_statement_no_new_scope { + // May be best done as post process phase on intermediate code + if (parseContext.currentFunctionType->getBasicType() != EbtVoid && ! parseContext.functionReturnsValue) + parseContext.error($1.loc, "function does not return a value:", "", $1.function->getName().c_str()); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + $$ = parseContext.intermediate.growAggregate($1.intermNode, $3); + parseContext.intermediate.setAggregateOperator($$, EOpFunction, $1.function->getType(), $1.loc); + $$->getAsAggregate()->setName($1.function->getMangledName().c_str()); + + // store the pragma information for debug and optimize and other vendor specific + // information. This information can be queried from the parse tree + $$->getAsAggregate()->setOptimize(parseContext.contextPragma.optimize); + $$->getAsAggregate()->setDebug(parseContext.contextPragma.debug); + $$->getAsAggregate()->setPragmaTable(parseContext.contextPragma.pragmaTable); + } + ; + + +attribute + : LEFT_BRACKET LEFT_BRACKET attribute_list RIGHT_BRACKET RIGHT_BRACKET { + $$ = $3; + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_control_flow_attributes, "attribute"); + } + +attribute_list + : single_attribute { + $$ = $1; + } + | attribute_list COMMA single_attribute { + $$ = parseContext.mergeAttributes($1, $3); + } + +single_attribute + : IDENTIFIER { + $$ = parseContext.makeAttributes(*$1.string); + } + | IDENTIFIER LEFT_PAREN constant_expression RIGHT_PAREN { + $$ = parseContext.makeAttributes(*$1.string, $3); + } + + +%% diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/glslang_tab.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/glslang_tab.cpp new file mode 100644 index 000000000..9914cd32b --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/glslang_tab.cpp @@ -0,0 +1,10716 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.5.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* First part of user prologue. */ +#line 69 "MachineIndependent/glslang.y" + + +/* Based on: +ANSI C Yacc grammar + +In 1985, Jeff Lee published his Yacc grammar (which is accompanied by a +matching Lex specification) for the April 30, 1985 draft version of the +ANSI C standard. Tom Stockfisch reposted it to net.sources in 1987; that +original, as mentioned in the answer to question 17.25 of the comp.lang.c +FAQ, can be ftp'ed from ftp.uu.net, file usenet/net.sources/ansi.c.grammar.Z. + +I intend to keep this version as close to the current C Standard grammar as +possible; please let me know if you discover discrepancies. + +Jutta Degener, 1995 +*/ + +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "../Public/ShaderLang.h" +#include "attribute.h" + +using namespace glslang; + + +#line 96 "MachineIndependent/glslang_tab.cpp" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED +# define YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + CONST = 258, + BOOL = 259, + INT = 260, + UINT = 261, + FLOAT = 262, + BVEC2 = 263, + BVEC3 = 264, + BVEC4 = 265, + IVEC2 = 266, + IVEC3 = 267, + IVEC4 = 268, + UVEC2 = 269, + UVEC3 = 270, + UVEC4 = 271, + VEC2 = 272, + VEC3 = 273, + VEC4 = 274, + MAT2 = 275, + MAT3 = 276, + MAT4 = 277, + MAT2X2 = 278, + MAT2X3 = 279, + MAT2X4 = 280, + MAT3X2 = 281, + MAT3X3 = 282, + MAT3X4 = 283, + MAT4X2 = 284, + MAT4X3 = 285, + MAT4X4 = 286, + SAMPLER2D = 287, + SAMPLER3D = 288, + SAMPLERCUBE = 289, + SAMPLER2DSHADOW = 290, + SAMPLERCUBESHADOW = 291, + SAMPLER2DARRAY = 292, + SAMPLER2DARRAYSHADOW = 293, + ISAMPLER2D = 294, + ISAMPLER3D = 295, + ISAMPLERCUBE = 296, + ISAMPLER2DARRAY = 297, + USAMPLER2D = 298, + USAMPLER3D = 299, + USAMPLERCUBE = 300, + USAMPLER2DARRAY = 301, + SAMPLER = 302, + SAMPLERSHADOW = 303, + TEXTURE2D = 304, + TEXTURE3D = 305, + TEXTURECUBE = 306, + TEXTURE2DARRAY = 307, + ITEXTURE2D = 308, + ITEXTURE3D = 309, + ITEXTURECUBE = 310, + ITEXTURE2DARRAY = 311, + UTEXTURE2D = 312, + UTEXTURE3D = 313, + UTEXTURECUBE = 314, + UTEXTURE2DARRAY = 315, + ATTRIBUTE = 316, + VARYING = 317, + FLOAT16_T = 318, + FLOAT32_T = 319, + DOUBLE = 320, + FLOAT64_T = 321, + INT64_T = 322, + UINT64_T = 323, + INT32_T = 324, + UINT32_T = 325, + INT16_T = 326, + UINT16_T = 327, + INT8_T = 328, + UINT8_T = 329, + I64VEC2 = 330, + I64VEC3 = 331, + I64VEC4 = 332, + U64VEC2 = 333, + U64VEC3 = 334, + U64VEC4 = 335, + I32VEC2 = 336, + I32VEC3 = 337, + I32VEC4 = 338, + U32VEC2 = 339, + U32VEC3 = 340, + U32VEC4 = 341, + I16VEC2 = 342, + I16VEC3 = 343, + I16VEC4 = 344, + U16VEC2 = 345, + U16VEC3 = 346, + U16VEC4 = 347, + I8VEC2 = 348, + I8VEC3 = 349, + I8VEC4 = 350, + U8VEC2 = 351, + U8VEC3 = 352, + U8VEC4 = 353, + DVEC2 = 354, + DVEC3 = 355, + DVEC4 = 356, + DMAT2 = 357, + DMAT3 = 358, + DMAT4 = 359, + F16VEC2 = 360, + F16VEC3 = 361, + F16VEC4 = 362, + F16MAT2 = 363, + F16MAT3 = 364, + F16MAT4 = 365, + F32VEC2 = 366, + F32VEC3 = 367, + F32VEC4 = 368, + F32MAT2 = 369, + F32MAT3 = 370, + F32MAT4 = 371, + F64VEC2 = 372, + F64VEC3 = 373, + F64VEC4 = 374, + F64MAT2 = 375, + F64MAT3 = 376, + F64MAT4 = 377, + DMAT2X2 = 378, + DMAT2X3 = 379, + DMAT2X4 = 380, + DMAT3X2 = 381, + DMAT3X3 = 382, + DMAT3X4 = 383, + DMAT4X2 = 384, + DMAT4X3 = 385, + DMAT4X4 = 386, + F16MAT2X2 = 387, + F16MAT2X3 = 388, + F16MAT2X4 = 389, + F16MAT3X2 = 390, + F16MAT3X3 = 391, + F16MAT3X4 = 392, + F16MAT4X2 = 393, + F16MAT4X3 = 394, + F16MAT4X4 = 395, + F32MAT2X2 = 396, + F32MAT2X3 = 397, + F32MAT2X4 = 398, + F32MAT3X2 = 399, + F32MAT3X3 = 400, + F32MAT3X4 = 401, + F32MAT4X2 = 402, + F32MAT4X3 = 403, + F32MAT4X4 = 404, + F64MAT2X2 = 405, + F64MAT2X3 = 406, + F64MAT2X4 = 407, + F64MAT3X2 = 408, + F64MAT3X3 = 409, + F64MAT3X4 = 410, + F64MAT4X2 = 411, + F64MAT4X3 = 412, + F64MAT4X4 = 413, + ATOMIC_UINT = 414, + ACCSTRUCTNV = 415, + ACCSTRUCTEXT = 416, + RAYQUERYEXT = 417, + FCOOPMATNV = 418, + ICOOPMATNV = 419, + UCOOPMATNV = 420, + SAMPLERCUBEARRAY = 421, + SAMPLERCUBEARRAYSHADOW = 422, + ISAMPLERCUBEARRAY = 423, + USAMPLERCUBEARRAY = 424, + SAMPLER1D = 425, + SAMPLER1DARRAY = 426, + SAMPLER1DARRAYSHADOW = 427, + ISAMPLER1D = 428, + SAMPLER1DSHADOW = 429, + SAMPLER2DRECT = 430, + SAMPLER2DRECTSHADOW = 431, + ISAMPLER2DRECT = 432, + USAMPLER2DRECT = 433, + SAMPLERBUFFER = 434, + ISAMPLERBUFFER = 435, + USAMPLERBUFFER = 436, + SAMPLER2DMS = 437, + ISAMPLER2DMS = 438, + USAMPLER2DMS = 439, + SAMPLER2DMSARRAY = 440, + ISAMPLER2DMSARRAY = 441, + USAMPLER2DMSARRAY = 442, + SAMPLEREXTERNALOES = 443, + SAMPLEREXTERNAL2DY2YEXT = 444, + SAMPLERVIDEO = 445, + ISAMPLER1DARRAY = 446, + USAMPLER1D = 447, + USAMPLER1DARRAY = 448, + F16SAMPLER1D = 449, + F16SAMPLER2D = 450, + F16SAMPLER3D = 451, + F16SAMPLER2DRECT = 452, + F16SAMPLERCUBE = 453, + F16SAMPLER1DARRAY = 454, + F16SAMPLER2DARRAY = 455, + F16SAMPLERCUBEARRAY = 456, + F16SAMPLERBUFFER = 457, + F16SAMPLER2DMS = 458, + F16SAMPLER2DMSARRAY = 459, + F16SAMPLER1DSHADOW = 460, + F16SAMPLER2DSHADOW = 461, + F16SAMPLER1DARRAYSHADOW = 462, + F16SAMPLER2DARRAYSHADOW = 463, + F16SAMPLER2DRECTSHADOW = 464, + F16SAMPLERCUBESHADOW = 465, + F16SAMPLERCUBEARRAYSHADOW = 466, + IMAGE1D = 467, + IIMAGE1D = 468, + UIMAGE1D = 469, + IMAGE2D = 470, + IIMAGE2D = 471, + UIMAGE2D = 472, + IMAGE3D = 473, + IIMAGE3D = 474, + UIMAGE3D = 475, + IMAGE2DRECT = 476, + IIMAGE2DRECT = 477, + UIMAGE2DRECT = 478, + IMAGECUBE = 479, + IIMAGECUBE = 480, + UIMAGECUBE = 481, + IMAGEBUFFER = 482, + IIMAGEBUFFER = 483, + UIMAGEBUFFER = 484, + IMAGE1DARRAY = 485, + IIMAGE1DARRAY = 486, + UIMAGE1DARRAY = 487, + IMAGE2DARRAY = 488, + IIMAGE2DARRAY = 489, + UIMAGE2DARRAY = 490, + IMAGECUBEARRAY = 491, + IIMAGECUBEARRAY = 492, + UIMAGECUBEARRAY = 493, + IMAGE2DMS = 494, + IIMAGE2DMS = 495, + UIMAGE2DMS = 496, + IMAGE2DMSARRAY = 497, + IIMAGE2DMSARRAY = 498, + UIMAGE2DMSARRAY = 499, + F16IMAGE1D = 500, + F16IMAGE2D = 501, + F16IMAGE3D = 502, + F16IMAGE2DRECT = 503, + F16IMAGECUBE = 504, + F16IMAGE1DARRAY = 505, + F16IMAGE2DARRAY = 506, + F16IMAGECUBEARRAY = 507, + F16IMAGEBUFFER = 508, + F16IMAGE2DMS = 509, + F16IMAGE2DMSARRAY = 510, + TEXTURECUBEARRAY = 511, + ITEXTURECUBEARRAY = 512, + UTEXTURECUBEARRAY = 513, + TEXTURE1D = 514, + ITEXTURE1D = 515, + UTEXTURE1D = 516, + TEXTURE1DARRAY = 517, + ITEXTURE1DARRAY = 518, + UTEXTURE1DARRAY = 519, + TEXTURE2DRECT = 520, + ITEXTURE2DRECT = 521, + UTEXTURE2DRECT = 522, + TEXTUREBUFFER = 523, + ITEXTUREBUFFER = 524, + UTEXTUREBUFFER = 525, + TEXTURE2DMS = 526, + ITEXTURE2DMS = 527, + UTEXTURE2DMS = 528, + TEXTURE2DMSARRAY = 529, + ITEXTURE2DMSARRAY = 530, + UTEXTURE2DMSARRAY = 531, + F16TEXTURE1D = 532, + F16TEXTURE2D = 533, + F16TEXTURE3D = 534, + F16TEXTURE2DRECT = 535, + F16TEXTURECUBE = 536, + F16TEXTURE1DARRAY = 537, + F16TEXTURE2DARRAY = 538, + F16TEXTURECUBEARRAY = 539, + F16TEXTUREBUFFER = 540, + F16TEXTURE2DMS = 541, + F16TEXTURE2DMSARRAY = 542, + SUBPASSINPUT = 543, + SUBPASSINPUTMS = 544, + ISUBPASSINPUT = 545, + ISUBPASSINPUTMS = 546, + USUBPASSINPUT = 547, + USUBPASSINPUTMS = 548, + F16SUBPASSINPUT = 549, + F16SUBPASSINPUTMS = 550, + LEFT_OP = 551, + RIGHT_OP = 552, + INC_OP = 553, + DEC_OP = 554, + LE_OP = 555, + GE_OP = 556, + EQ_OP = 557, + NE_OP = 558, + AND_OP = 559, + OR_OP = 560, + XOR_OP = 561, + MUL_ASSIGN = 562, + DIV_ASSIGN = 563, + ADD_ASSIGN = 564, + MOD_ASSIGN = 565, + LEFT_ASSIGN = 566, + RIGHT_ASSIGN = 567, + AND_ASSIGN = 568, + XOR_ASSIGN = 569, + OR_ASSIGN = 570, + SUB_ASSIGN = 571, + STRING_LITERAL = 572, + LEFT_PAREN = 573, + RIGHT_PAREN = 574, + LEFT_BRACKET = 575, + RIGHT_BRACKET = 576, + LEFT_BRACE = 577, + RIGHT_BRACE = 578, + DOT = 579, + COMMA = 580, + COLON = 581, + EQUAL = 582, + SEMICOLON = 583, + BANG = 584, + DASH = 585, + TILDE = 586, + PLUS = 587, + STAR = 588, + SLASH = 589, + PERCENT = 590, + LEFT_ANGLE = 591, + RIGHT_ANGLE = 592, + VERTICAL_BAR = 593, + CARET = 594, + AMPERSAND = 595, + QUESTION = 596, + INVARIANT = 597, + HIGH_PRECISION = 598, + MEDIUM_PRECISION = 599, + LOW_PRECISION = 600, + PRECISION = 601, + PACKED = 602, + RESOURCE = 603, + SUPERP = 604, + FLOATCONSTANT = 605, + INTCONSTANT = 606, + UINTCONSTANT = 607, + BOOLCONSTANT = 608, + IDENTIFIER = 609, + TYPE_NAME = 610, + CENTROID = 611, + IN = 612, + OUT = 613, + INOUT = 614, + STRUCT = 615, + VOID = 616, + WHILE = 617, + BREAK = 618, + CONTINUE = 619, + DO = 620, + ELSE = 621, + FOR = 622, + IF = 623, + DISCARD = 624, + RETURN = 625, + SWITCH = 626, + CASE = 627, + DEFAULT = 628, + UNIFORM = 629, + SHARED = 630, + BUFFER = 631, + FLAT = 632, + SMOOTH = 633, + LAYOUT = 634, + DOUBLECONSTANT = 635, + INT16CONSTANT = 636, + UINT16CONSTANT = 637, + FLOAT16CONSTANT = 638, + INT32CONSTANT = 639, + UINT32CONSTANT = 640, + INT64CONSTANT = 641, + UINT64CONSTANT = 642, + SUBROUTINE = 643, + DEMOTE = 644, + PAYLOADNV = 645, + PAYLOADINNV = 646, + HITATTRNV = 647, + CALLDATANV = 648, + CALLDATAINNV = 649, + PAYLOADEXT = 650, + PAYLOADINEXT = 651, + HITATTREXT = 652, + CALLDATAEXT = 653, + CALLDATAINEXT = 654, + PATCH = 655, + SAMPLE = 656, + NONUNIFORM = 657, + COHERENT = 658, + VOLATILE = 659, + RESTRICT = 660, + READONLY = 661, + WRITEONLY = 662, + DEVICECOHERENT = 663, + QUEUEFAMILYCOHERENT = 664, + WORKGROUPCOHERENT = 665, + SUBGROUPCOHERENT = 666, + NONPRIVATE = 667, + SHADERCALLCOHERENT = 668, + NOPERSPECTIVE = 669, + EXPLICITINTERPAMD = 670, + PERVERTEXNV = 671, + PERPRIMITIVENV = 672, + PERVIEWNV = 673, + PERTASKNV = 674, + PRECISE = 675 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 97 "MachineIndependent/glslang.y" + + struct { + glslang::TSourceLoc loc; + union { + glslang::TString *string; + int i; + unsigned int u; + long long i64; + unsigned long long u64; + bool b; + double d; + }; + glslang::TSymbol* symbol; + } lex; + struct { + glslang::TSourceLoc loc; + glslang::TOperator op; + union { + TIntermNode* intermNode; + glslang::TIntermNodePair nodePair; + glslang::TIntermTyped* intermTypedNode; + glslang::TAttributes* attributes; + }; + union { + glslang::TPublicType type; + glslang::TFunction* function; + glslang::TParameter param; + glslang::TTypeLoc typeLine; + glslang::TTypeList* typeList; + glslang::TArraySizes* arraySizes; + glslang::TIdentifierList* identifierList; + }; + glslang::TArraySizes* typeParameters; + } interm; + +#line 605 "MachineIndependent/glslang_tab.cpp" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int yyparse (glslang::TParseContext* pParseContext); + +#endif /* !YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED */ + +/* Second part of user prologue. */ +#line 133 "MachineIndependent/glslang.y" + + +/* windows only pragma */ +#ifdef _MSC_VER + #pragma warning(disable : 4065) + #pragma warning(disable : 4127) + #pragma warning(disable : 4244) +#endif + +#define parseContext (*pParseContext) +#define yyerror(context, msg) context->parserError(msg) + +extern int yylex(YYSTYPE*, TParseContext&); + + +#line 636 "MachineIndependent/glslang_tab.cpp" + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + +/* Stored state numbers (used for stacks). */ +typedef yytype_int16 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 395 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 9572 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 421 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 111 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 592 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 737 + +#define YYUNDEFTOK 2 +#define YYMAXUTOK 675 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int16 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, + 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, + 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, + 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, + 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, + 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 416, 417, 418, 419, 420 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 358, 358, 364, 367, 372, 375, 378, 382, 386, + 389, 393, 397, 401, 405, 409, 413, 419, 427, 430, + 433, 436, 439, 444, 452, 459, 466, 472, 476, 483, + 486, 492, 499, 509, 517, 522, 550, 559, 565, 569, + 573, 593, 594, 595, 596, 602, 603, 608, 613, 622, + 623, 628, 636, 637, 643, 652, 653, 658, 663, 668, + 676, 677, 686, 698, 699, 708, 709, 718, 719, 728, + 729, 737, 738, 746, 747, 755, 756, 756, 774, 775, + 791, 795, 799, 803, 808, 812, 816, 820, 824, 828, + 832, 839, 842, 853, 860, 865, 870, 877, 881, 885, + 889, 894, 899, 908, 908, 919, 923, 930, 937, 940, + 947, 955, 975, 998, 1013, 1038, 1049, 1059, 1069, 1079, + 1088, 1091, 1095, 1099, 1104, 1112, 1119, 1124, 1129, 1134, + 1143, 1153, 1180, 1189, 1196, 1204, 1211, 1218, 1226, 1236, + 1243, 1254, 1260, 1263, 1270, 1274, 1278, 1287, 1297, 1300, + 1311, 1314, 1317, 1321, 1325, 1330, 1334, 1341, 1345, 1350, + 1356, 1362, 1369, 1374, 1382, 1388, 1400, 1414, 1420, 1425, + 1433, 1441, 1449, 1457, 1465, 1473, 1481, 1489, 1496, 1503, + 1507, 1512, 1517, 1522, 1527, 1532, 1537, 1541, 1545, 1549, + 1553, 1559, 1570, 1577, 1580, 1589, 1594, 1604, 1609, 1617, + 1621, 1631, 1634, 1640, 1646, 1653, 1663, 1667, 1671, 1675, + 1680, 1684, 1689, 1694, 1699, 1704, 1709, 1714, 1719, 1724, + 1729, 1735, 1741, 1747, 1752, 1757, 1762, 1767, 1772, 1777, + 1782, 1787, 1792, 1797, 1802, 1808, 1815, 1820, 1825, 1830, + 1835, 1840, 1845, 1850, 1855, 1860, 1865, 1870, 1878, 1886, + 1894, 1900, 1906, 1912, 1918, 1924, 1930, 1936, 1942, 1948, + 1954, 1960, 1966, 1972, 1978, 1984, 1990, 1996, 2002, 2008, + 2014, 2020, 2026, 2032, 2038, 2044, 2050, 2056, 2062, 2068, + 2074, 2080, 2086, 2092, 2100, 2108, 2116, 2124, 2132, 2140, + 2148, 2156, 2164, 2172, 2180, 2188, 2194, 2200, 2206, 2212, + 2218, 2224, 2230, 2236, 2242, 2248, 2254, 2260, 2266, 2272, + 2278, 2284, 2290, 2296, 2302, 2308, 2314, 2320, 2326, 2332, + 2338, 2344, 2350, 2356, 2362, 2368, 2374, 2380, 2386, 2392, + 2398, 2404, 2408, 2412, 2416, 2421, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2463, 2468, 2473, 2478, 2483, 2488, 2494, + 2500, 2506, 2512, 2518, 2524, 2530, 2536, 2542, 2548, 2554, + 2560, 2566, 2572, 2577, 2582, 2587, 2592, 2597, 2602, 2608, + 2613, 2618, 2623, 2628, 2633, 2638, 2643, 2649, 2654, 2659, + 2664, 2669, 2674, 2679, 2684, 2689, 2694, 2699, 2704, 2709, + 2714, 2719, 2725, 2730, 2735, 2741, 2747, 2752, 2757, 2762, + 2768, 2773, 2778, 2783, 2789, 2794, 2799, 2804, 2810, 2815, + 2820, 2825, 2831, 2837, 2843, 2849, 2854, 2860, 2866, 2872, + 2877, 2882, 2887, 2892, 2897, 2903, 2908, 2913, 2918, 2924, + 2929, 2934, 2939, 2945, 2950, 2955, 2960, 2966, 2971, 2976, + 2981, 2987, 2992, 2997, 3002, 3008, 3013, 3018, 3023, 3029, + 3034, 3039, 3044, 3050, 3055, 3060, 3065, 3071, 3076, 3081, + 3086, 3092, 3097, 3102, 3107, 3113, 3118, 3123, 3128, 3134, + 3139, 3144, 3149, 3155, 3160, 3165, 3170, 3176, 3181, 3186, + 3191, 3197, 3202, 3207, 3213, 3219, 3225, 3231, 3237, 3244, + 3251, 3257, 3263, 3269, 3275, 3281, 3287, 3294, 3299, 3315, + 3320, 3325, 3333, 3333, 3344, 3344, 3354, 3357, 3370, 3392, + 3419, 3423, 3429, 3434, 3445, 3449, 3455, 3466, 3469, 3476, + 3480, 3481, 3487, 3488, 3489, 3490, 3491, 3492, 3493, 3495, + 3501, 3510, 3511, 3515, 3511, 3527, 3528, 3532, 3532, 3539, + 3539, 3553, 3556, 3564, 3572, 3583, 3584, 3588, 3592, 3599, + 3606, 3610, 3618, 3622, 3635, 3639, 3646, 3646, 3666, 3669, + 3675, 3687, 3699, 3703, 3710, 3710, 3725, 3725, 3741, 3741, + 3762, 3765, 3771, 3774, 3780, 3784, 3791, 3796, 3801, 3808, + 3811, 3820, 3824, 3833, 3836, 3840, 3849, 3849, 3872, 3878, + 3881, 3886, 3889 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "CONST", "BOOL", "INT", "UINT", "FLOAT", + "BVEC2", "BVEC3", "BVEC4", "IVEC2", "IVEC3", "IVEC4", "UVEC2", "UVEC3", + "UVEC4", "VEC2", "VEC3", "VEC4", "MAT2", "MAT3", "MAT4", "MAT2X2", + "MAT2X3", "MAT2X4", "MAT3X2", "MAT3X3", "MAT3X4", "MAT4X2", "MAT4X3", + "MAT4X4", "SAMPLER2D", "SAMPLER3D", "SAMPLERCUBE", "SAMPLER2DSHADOW", + "SAMPLERCUBESHADOW", "SAMPLER2DARRAY", "SAMPLER2DARRAYSHADOW", + "ISAMPLER2D", "ISAMPLER3D", "ISAMPLERCUBE", "ISAMPLER2DARRAY", + "USAMPLER2D", "USAMPLER3D", "USAMPLERCUBE", "USAMPLER2DARRAY", "SAMPLER", + "SAMPLERSHADOW", "TEXTURE2D", "TEXTURE3D", "TEXTURECUBE", + "TEXTURE2DARRAY", "ITEXTURE2D", "ITEXTURE3D", "ITEXTURECUBE", + "ITEXTURE2DARRAY", "UTEXTURE2D", "UTEXTURE3D", "UTEXTURECUBE", + "UTEXTURE2DARRAY", "ATTRIBUTE", "VARYING", "FLOAT16_T", "FLOAT32_T", + "DOUBLE", "FLOAT64_T", "INT64_T", "UINT64_T", "INT32_T", "UINT32_T", + "INT16_T", "UINT16_T", "INT8_T", "UINT8_T", "I64VEC2", "I64VEC3", + "I64VEC4", "U64VEC2", "U64VEC3", "U64VEC4", "I32VEC2", "I32VEC3", + "I32VEC4", "U32VEC2", "U32VEC3", "U32VEC4", "I16VEC2", "I16VEC3", + "I16VEC4", "U16VEC2", "U16VEC3", "U16VEC4", "I8VEC2", "I8VEC3", "I8VEC4", + "U8VEC2", "U8VEC3", "U8VEC4", "DVEC2", "DVEC3", "DVEC4", "DMAT2", + "DMAT3", "DMAT4", "F16VEC2", "F16VEC3", "F16VEC4", "F16MAT2", "F16MAT3", + "F16MAT4", "F32VEC2", "F32VEC3", "F32VEC4", "F32MAT2", "F32MAT3", + "F32MAT4", "F64VEC2", "F64VEC3", "F64VEC4", "F64MAT2", "F64MAT3", + "F64MAT4", "DMAT2X2", "DMAT2X3", "DMAT2X4", "DMAT3X2", "DMAT3X3", + "DMAT3X4", "DMAT4X2", "DMAT4X3", "DMAT4X4", "F16MAT2X2", "F16MAT2X3", + "F16MAT2X4", "F16MAT3X2", "F16MAT3X3", "F16MAT3X4", "F16MAT4X2", + "F16MAT4X3", "F16MAT4X4", "F32MAT2X2", "F32MAT2X3", "F32MAT2X4", + "F32MAT3X2", "F32MAT3X3", "F32MAT3X4", "F32MAT4X2", "F32MAT4X3", + "F32MAT4X4", "F64MAT2X2", "F64MAT2X3", "F64MAT2X4", "F64MAT3X2", + "F64MAT3X3", "F64MAT3X4", "F64MAT4X2", "F64MAT4X3", "F64MAT4X4", + "ATOMIC_UINT", "ACCSTRUCTNV", "ACCSTRUCTEXT", "RAYQUERYEXT", + "FCOOPMATNV", "ICOOPMATNV", "UCOOPMATNV", "SAMPLERCUBEARRAY", + "SAMPLERCUBEARRAYSHADOW", "ISAMPLERCUBEARRAY", "USAMPLERCUBEARRAY", + "SAMPLER1D", "SAMPLER1DARRAY", "SAMPLER1DARRAYSHADOW", "ISAMPLER1D", + "SAMPLER1DSHADOW", "SAMPLER2DRECT", "SAMPLER2DRECTSHADOW", + "ISAMPLER2DRECT", "USAMPLER2DRECT", "SAMPLERBUFFER", "ISAMPLERBUFFER", + "USAMPLERBUFFER", "SAMPLER2DMS", "ISAMPLER2DMS", "USAMPLER2DMS", + "SAMPLER2DMSARRAY", "ISAMPLER2DMSARRAY", "USAMPLER2DMSARRAY", + "SAMPLEREXTERNALOES", "SAMPLEREXTERNAL2DY2YEXT", "SAMPLERVIDEO", + "ISAMPLER1DARRAY", "USAMPLER1D", "USAMPLER1DARRAY", "F16SAMPLER1D", + "F16SAMPLER2D", "F16SAMPLER3D", "F16SAMPLER2DRECT", "F16SAMPLERCUBE", + "F16SAMPLER1DARRAY", "F16SAMPLER2DARRAY", "F16SAMPLERCUBEARRAY", + "F16SAMPLERBUFFER", "F16SAMPLER2DMS", "F16SAMPLER2DMSARRAY", + "F16SAMPLER1DSHADOW", "F16SAMPLER2DSHADOW", "F16SAMPLER1DARRAYSHADOW", + "F16SAMPLER2DARRAYSHADOW", "F16SAMPLER2DRECTSHADOW", + "F16SAMPLERCUBESHADOW", "F16SAMPLERCUBEARRAYSHADOW", "IMAGE1D", + "IIMAGE1D", "UIMAGE1D", "IMAGE2D", "IIMAGE2D", "UIMAGE2D", "IMAGE3D", + "IIMAGE3D", "UIMAGE3D", "IMAGE2DRECT", "IIMAGE2DRECT", "UIMAGE2DRECT", + "IMAGECUBE", "IIMAGECUBE", "UIMAGECUBE", "IMAGEBUFFER", "IIMAGEBUFFER", + "UIMAGEBUFFER", "IMAGE1DARRAY", "IIMAGE1DARRAY", "UIMAGE1DARRAY", + "IMAGE2DARRAY", "IIMAGE2DARRAY", "UIMAGE2DARRAY", "IMAGECUBEARRAY", + "IIMAGECUBEARRAY", "UIMAGECUBEARRAY", "IMAGE2DMS", "IIMAGE2DMS", + "UIMAGE2DMS", "IMAGE2DMSARRAY", "IIMAGE2DMSARRAY", "UIMAGE2DMSARRAY", + "F16IMAGE1D", "F16IMAGE2D", "F16IMAGE3D", "F16IMAGE2DRECT", + "F16IMAGECUBE", "F16IMAGE1DARRAY", "F16IMAGE2DARRAY", + "F16IMAGECUBEARRAY", "F16IMAGEBUFFER", "F16IMAGE2DMS", + "F16IMAGE2DMSARRAY", "TEXTURECUBEARRAY", "ITEXTURECUBEARRAY", + "UTEXTURECUBEARRAY", "TEXTURE1D", "ITEXTURE1D", "UTEXTURE1D", + "TEXTURE1DARRAY", "ITEXTURE1DARRAY", "UTEXTURE1DARRAY", "TEXTURE2DRECT", + "ITEXTURE2DRECT", "UTEXTURE2DRECT", "TEXTUREBUFFER", "ITEXTUREBUFFER", + "UTEXTUREBUFFER", "TEXTURE2DMS", "ITEXTURE2DMS", "UTEXTURE2DMS", + "TEXTURE2DMSARRAY", "ITEXTURE2DMSARRAY", "UTEXTURE2DMSARRAY", + "F16TEXTURE1D", "F16TEXTURE2D", "F16TEXTURE3D", "F16TEXTURE2DRECT", + "F16TEXTURECUBE", "F16TEXTURE1DARRAY", "F16TEXTURE2DARRAY", + "F16TEXTURECUBEARRAY", "F16TEXTUREBUFFER", "F16TEXTURE2DMS", + "F16TEXTURE2DMSARRAY", "SUBPASSINPUT", "SUBPASSINPUTMS", "ISUBPASSINPUT", + "ISUBPASSINPUTMS", "USUBPASSINPUT", "USUBPASSINPUTMS", "F16SUBPASSINPUT", + "F16SUBPASSINPUTMS", "LEFT_OP", "RIGHT_OP", "INC_OP", "DEC_OP", "LE_OP", + "GE_OP", "EQ_OP", "NE_OP", "AND_OP", "OR_OP", "XOR_OP", "MUL_ASSIGN", + "DIV_ASSIGN", "ADD_ASSIGN", "MOD_ASSIGN", "LEFT_ASSIGN", "RIGHT_ASSIGN", + "AND_ASSIGN", "XOR_ASSIGN", "OR_ASSIGN", "SUB_ASSIGN", "STRING_LITERAL", + "LEFT_PAREN", "RIGHT_PAREN", "LEFT_BRACKET", "RIGHT_BRACKET", + "LEFT_BRACE", "RIGHT_BRACE", "DOT", "COMMA", "COLON", "EQUAL", + "SEMICOLON", "BANG", "DASH", "TILDE", "PLUS", "STAR", "SLASH", "PERCENT", + "LEFT_ANGLE", "RIGHT_ANGLE", "VERTICAL_BAR", "CARET", "AMPERSAND", + "QUESTION", "INVARIANT", "HIGH_PRECISION", "MEDIUM_PRECISION", + "LOW_PRECISION", "PRECISION", "PACKED", "RESOURCE", "SUPERP", + "FLOATCONSTANT", "INTCONSTANT", "UINTCONSTANT", "BOOLCONSTANT", + "IDENTIFIER", "TYPE_NAME", "CENTROID", "IN", "OUT", "INOUT", "STRUCT", + "VOID", "WHILE", "BREAK", "CONTINUE", "DO", "ELSE", "FOR", "IF", + "DISCARD", "RETURN", "SWITCH", "CASE", "DEFAULT", "UNIFORM", "SHARED", + "BUFFER", "FLAT", "SMOOTH", "LAYOUT", "DOUBLECONSTANT", "INT16CONSTANT", + "UINT16CONSTANT", "FLOAT16CONSTANT", "INT32CONSTANT", "UINT32CONSTANT", + "INT64CONSTANT", "UINT64CONSTANT", "SUBROUTINE", "DEMOTE", "PAYLOADNV", + "PAYLOADINNV", "HITATTRNV", "CALLDATANV", "CALLDATAINNV", "PAYLOADEXT", + "PAYLOADINEXT", "HITATTREXT", "CALLDATAEXT", "CALLDATAINEXT", "PATCH", + "SAMPLE", "NONUNIFORM", "COHERENT", "VOLATILE", "RESTRICT", "READONLY", + "WRITEONLY", "DEVICECOHERENT", "QUEUEFAMILYCOHERENT", + "WORKGROUPCOHERENT", "SUBGROUPCOHERENT", "NONPRIVATE", + "SHADERCALLCOHERENT", "NOPERSPECTIVE", "EXPLICITINTERPAMD", + "PERVERTEXNV", "PERPRIMITIVENV", "PERVIEWNV", "PERTASKNV", "PRECISE", + "$accept", "variable_identifier", "primary_expression", + "postfix_expression", "integer_expression", "function_call", + "function_call_or_method", "function_call_generic", + "function_call_header_no_parameters", + "function_call_header_with_parameters", "function_call_header", + "function_identifier", "unary_expression", "unary_operator", + "multiplicative_expression", "additive_expression", "shift_expression", + "relational_expression", "equality_expression", "and_expression", + "exclusive_or_expression", "inclusive_or_expression", + "logical_and_expression", "logical_xor_expression", + "logical_or_expression", "conditional_expression", "$@1", + "assignment_expression", "assignment_operator", "expression", + "constant_expression", "declaration", "block_structure", "$@2", + "identifier_list", "function_prototype", "function_declarator", + "function_header_with_parameters", "function_header", + "parameter_declarator", "parameter_declaration", + "parameter_type_specifier", "init_declarator_list", "single_declaration", + "fully_specified_type", "invariant_qualifier", "interpolation_qualifier", + "layout_qualifier", "layout_qualifier_id_list", "layout_qualifier_id", + "precise_qualifier", "type_qualifier", "single_type_qualifier", + "storage_qualifier", "non_uniform_qualifier", "type_name_list", + "type_specifier", "array_specifier", "type_parameter_specifier_opt", + "type_parameter_specifier", "type_parameter_specifier_list", + "type_specifier_nonarray", "precision_qualifier", "struct_specifier", + "$@3", "$@4", "struct_declaration_list", "struct_declaration", + "struct_declarator_list", "struct_declarator", "initializer", + "initializer_list", "declaration_statement", "statement", + "simple_statement", "demote_statement", "compound_statement", "$@5", + "$@6", "statement_no_new_scope", "statement_scoped", "$@7", "$@8", + "compound_statement_no_new_scope", "statement_list", + "expression_statement", "selection_statement", + "selection_statement_nonattributed", "selection_rest_statement", + "condition", "switch_statement", "switch_statement_nonattributed", "$@9", + "switch_statement_list", "case_label", "iteration_statement", + "iteration_statement_nonattributed", "$@10", "$@11", "$@12", + "for_init_statement", "conditionopt", "for_rest_statement", + "jump_statement", "translation_unit", "external_declaration", + "function_definition", "$@13", "attribute", "attribute_list", + "single_attribute", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, + 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, + 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, + 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, + 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, + 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, + 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, + 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, + 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, + 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, + 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, + 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, + 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, + 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, + 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, + 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, + 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, + 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, + 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, + 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, + 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, + 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, + 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, + 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, + 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, + 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, + 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, + 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, + 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, + 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, + 675 +}; +# endif + +#define YYPACT_NINF (-458) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-538) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 4084, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, 132, + -458, -458, -458, -458, -458, -1, -458, -458, -458, -458, + -458, -458, -302, -299, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, 11, -250, 17, 30, + 6174, 20, -458, 28, -458, -458, -458, -458, 4502, -458, + -458, -458, -458, 50, -458, -458, 740, -458, -458, 16, + -458, 81, -29, 69, -458, -314, -458, 111, -458, 6174, + -458, -458, -458, 6174, 103, 106, -458, -315, -458, 72, + -458, -458, 8586, 142, -458, -458, -458, 136, 6174, -458, + 144, -458, 53, -458, -458, 76, 6990, -458, -313, 1158, + -458, -458, -458, -458, 142, -310, -458, 7389, -309, -458, + 119, -458, 65, 8586, 8586, -458, 8586, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, -458, 36, -458, -458, -458, + 171, 85, 8985, 173, -458, 8586, -458, -458, -324, 174, + -458, 6174, 139, 4920, -458, 6174, 8586, -458, -29, -458, + 141, -458, -458, 145, 99, 35, 26, 71, 156, 159, + 161, 196, 195, 23, 181, 7788, -458, 183, 182, -458, + -458, 186, 179, 180, -458, 191, 192, 187, 8187, 193, + 8586, 188, 189, 127, -458, -458, 96, -458, -250, 200, + 201, -458, -458, -458, -458, -458, 1576, -458, -458, -458, + -458, -458, -458, -458, -458, -458, -24, 174, 7389, 13, + 7389, -458, -458, 7389, 6174, -458, 166, -458, -458, -458, + 86, -458, -458, 8586, 168, -458, -458, 8586, 205, -458, + -458, -458, 8586, -458, 139, 142, 124, -458, -458, -458, + 5338, -458, -458, -458, -458, 8586, 8586, 8586, 8586, 8586, + 8586, 8586, 8586, 8586, 8586, 8586, 8586, 8586, 8586, 8586, + 8586, 8586, 8586, 8586, -458, -458, -458, 206, 172, -458, + 1994, -458, -458, -458, 1994, -458, 8586, -458, -458, 130, + 8586, 125, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, -458, -458, -458, 8586, 8586, -458, -458, -458, + -458, -458, -458, -458, 7389, -458, 94, -458, 5756, -458, + -458, 207, 204, -458, -458, -458, 131, 174, 139, -458, + -458, -458, -458, -458, 145, 145, 99, 99, 35, 35, + 35, 35, 26, 26, 71, 156, 159, 161, 196, 195, + 8586, -458, 212, 60, -458, 1994, 3666, 169, 3248, 87, + -458, 89, -458, -458, -458, -458, -458, 6591, -458, -458, + -458, -458, 143, 8586, 211, 172, 210, 204, 184, 6174, + 217, 219, -458, -458, 3666, 218, -458, -458, -458, 8586, + 220, -458, -458, -458, 214, 2412, 8586, -458, 216, 223, + 185, 224, 2830, -458, 225, -458, -458, 7389, -458, -458, + -458, 97, 8586, 2412, 218, -458, -458, 1994, -458, 222, + 204, -458, -458, 1994, 229, -458, -458 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int16 yydefact[] = +{ + 0, 157, 210, 208, 209, 207, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 211, 212, 213, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 336, 337, 338, 339, 340, 341, 342, 362, 363, 364, + 365, 366, 367, 368, 377, 390, 391, 378, 379, 381, + 380, 382, 383, 384, 385, 386, 387, 388, 389, 165, + 166, 236, 237, 235, 238, 245, 246, 243, 244, 241, + 242, 239, 240, 268, 269, 270, 280, 281, 282, 265, + 266, 267, 277, 278, 279, 262, 263, 264, 274, 275, + 276, 259, 260, 261, 271, 272, 273, 247, 248, 249, + 283, 284, 285, 250, 251, 252, 295, 296, 297, 253, + 254, 255, 307, 308, 309, 256, 257, 258, 319, 320, + 321, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 310, + 311, 312, 313, 314, 315, 316, 317, 318, 322, 323, + 324, 325, 326, 327, 328, 329, 330, 334, 331, 332, + 333, 494, 495, 496, 346, 347, 370, 373, 335, 344, + 345, 361, 343, 392, 393, 396, 397, 398, 400, 401, + 402, 404, 405, 406, 408, 409, 483, 484, 485, 369, + 371, 372, 348, 349, 350, 394, 351, 355, 356, 359, + 399, 403, 407, 352, 353, 357, 358, 395, 354, 360, + 439, 441, 442, 443, 445, 446, 447, 449, 450, 451, + 453, 454, 455, 457, 458, 459, 461, 462, 463, 465, + 466, 467, 469, 470, 471, 473, 474, 475, 477, 478, + 479, 481, 482, 440, 444, 448, 452, 456, 464, 468, + 472, 460, 476, 480, 374, 375, 376, 410, 419, 421, + 415, 420, 422, 423, 425, 426, 427, 429, 430, 431, + 433, 434, 435, 437, 438, 411, 412, 413, 424, 414, + 416, 417, 418, 428, 432, 436, 486, 487, 490, 491, + 492, 493, 488, 489, 585, 132, 499, 500, 501, 0, + 498, 161, 159, 160, 158, 0, 206, 162, 163, 164, + 134, 133, 0, 190, 171, 173, 169, 175, 177, 172, + 174, 170, 176, 178, 167, 168, 192, 179, 186, 187, + 188, 189, 180, 181, 182, 183, 184, 185, 135, 136, + 137, 138, 139, 140, 147, 584, 0, 586, 0, 109, + 108, 0, 120, 125, 154, 153, 151, 155, 0, 148, + 150, 156, 130, 202, 152, 497, 0, 581, 583, 0, + 504, 0, 0, 0, 97, 0, 94, 0, 107, 0, + 116, 110, 118, 0, 119, 0, 95, 126, 100, 0, + 149, 131, 0, 195, 201, 1, 582, 0, 0, 502, + 144, 146, 0, 142, 193, 0, 0, 98, 0, 0, + 587, 111, 115, 117, 113, 121, 112, 0, 127, 103, + 0, 101, 0, 0, 0, 9, 0, 43, 42, 44, + 41, 5, 6, 7, 8, 2, 16, 14, 15, 17, + 10, 11, 12, 13, 3, 18, 37, 20, 25, 26, + 0, 0, 30, 0, 204, 0, 36, 34, 0, 196, + 96, 0, 0, 0, 506, 0, 0, 141, 0, 191, + 0, 197, 45, 49, 52, 55, 60, 63, 65, 67, + 69, 71, 73, 75, 0, 0, 99, 0, 532, 541, + 545, 0, 0, 0, 566, 0, 0, 0, 0, 0, + 0, 0, 0, 45, 78, 91, 0, 519, 0, 156, + 130, 522, 543, 521, 529, 520, 0, 523, 524, 547, + 525, 554, 526, 527, 562, 528, 0, 114, 0, 122, + 0, 514, 129, 0, 0, 105, 0, 102, 38, 39, + 0, 22, 23, 0, 0, 28, 27, 0, 206, 31, + 33, 40, 0, 203, 0, 512, 0, 510, 505, 507, + 0, 93, 145, 143, 194, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 76, 198, 199, 0, 0, 531, + 0, 564, 577, 576, 0, 568, 0, 580, 578, 0, + 0, 0, 561, 530, 81, 82, 84, 83, 86, 87, + 88, 89, 90, 85, 80, 0, 0, 546, 542, 544, + 548, 555, 563, 124, 0, 517, 0, 128, 0, 106, + 4, 0, 24, 21, 32, 205, 0, 513, 0, 508, + 503, 46, 47, 48, 51, 50, 53, 54, 58, 59, + 56, 57, 61, 62, 64, 66, 68, 70, 72, 74, + 0, 200, 591, 0, 589, 533, 0, 0, 0, 0, + 579, 0, 560, 79, 92, 123, 515, 0, 104, 19, + 509, 511, 0, 0, 0, 0, 0, 552, 0, 0, + 0, 0, 571, 570, 573, 539, 556, 516, 518, 0, + 0, 588, 590, 534, 0, 0, 0, 572, 0, 0, + 551, 0, 0, 549, 0, 77, 592, 0, 536, 565, + 535, 0, 574, 0, 539, 538, 540, 558, 553, 0, + 575, 569, 550, 559, 0, 567, 557 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -458, -458, -458, -458, -458, -458, -458, -458, -458, -458, + -458, -458, 8889, -458, -87, -84, -127, -93, -33, -31, + -27, -25, -28, -26, -458, -86, -458, -103, -458, -111, + -125, 2, -458, -458, -458, 4, -458, -458, -458, 176, + 194, 178, -458, -458, -337, -458, -458, -458, -458, 95, + -458, -37, -46, -458, 9, -458, 0, -63, -458, -458, + -458, -458, 263, -458, -458, -458, -457, -140, 10, -73, + -211, -458, -102, -198, -321, -458, -144, -458, -458, -155, + -154, -458, -458, 198, -274, -97, -458, 46, -458, -118, + -458, 51, -458, -458, -458, -458, 52, -458, -458, -458, + -458, -458, -458, -458, -458, 213, -458, -458, -458, -458, + -105 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 444, 445, 446, 631, 447, 448, 449, 450, 451, + 452, 453, 503, 455, 473, 474, 475, 476, 477, 478, + 479, 480, 481, 482, 483, 504, 660, 505, 615, 506, + 562, 507, 346, 534, 422, 508, 348, 349, 350, 380, + 381, 382, 351, 352, 353, 354, 355, 356, 402, 403, + 357, 358, 359, 360, 456, 405, 457, 408, 393, 394, + 458, 363, 364, 365, 465, 398, 463, 464, 556, 557, + 532, 626, 511, 512, 513, 514, 515, 590, 686, 719, + 710, 711, 712, 720, 516, 517, 518, 519, 713, 690, + 520, 521, 714, 734, 522, 523, 524, 666, 594, 668, + 694, 708, 709, 525, 366, 367, 368, 377, 526, 663, + 664 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 362, 552, 345, 416, 347, 406, 406, 485, 560, 361, + 406, 485, 417, 553, 407, 486, 372, 528, 533, 373, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 628, 376, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 390, 383, 531, 540, 665, 623, 619, 625, + 484, 370, 627, 559, 418, 400, 572, 573, 583, 688, + 459, 570, 571, 485, 541, 542, 378, 390, 491, 374, + 624, 494, 383, 495, 496, 385, 401, 499, 386, 549, + 384, 527, 529, 371, -35, 379, 543, 688, 391, 361, + 544, 461, 574, 575, 584, 375, 362, 361, 345, 397, + 347, 300, 467, 576, 577, 361, 305, 306, 468, 384, + 561, 684, 387, 384, 718, 685, 392, 599, 361, 601, + 536, 726, 361, 537, 419, 469, 667, 420, 462, 587, + 421, 470, 718, 399, 546, 630, 695, 361, 696, 510, + 547, 616, 616, 675, 616, 390, 729, 676, 509, 677, + 559, 616, 616, 404, 617, 531, 461, 531, 461, 568, + 531, 569, 632, 409, 604, 605, 606, 607, 608, 609, + 610, 611, 612, 613, 634, 648, 649, 650, 651, 638, + 616, 672, 639, 733, 614, 616, 638, 414, 670, 680, + 415, 554, 406, 462, 460, 462, 698, 619, 616, 699, + 361, 466, 361, 535, 361, 296, 297, 298, 565, 566, + 567, 644, 645, 652, 653, 669, 646, 647, 559, 671, + 545, 550, 637, 555, 485, 564, 578, 461, 579, 580, + 581, 582, 585, 588, 591, 589, 728, 592, 593, 595, + 596, 600, 673, 674, 602, 597, 510, 603, -36, -34, + 629, 531, 633, 461, -29, 509, 662, 661, 679, 616, + 683, 691, 701, 703, 462, 619, 705, 706, 704, 716, + -537, 717, 723, 361, 722, 654, 488, 727, 655, 682, + 735, 724, 736, 656, 658, 687, 657, 659, 700, 412, + 462, 413, 369, 563, 636, 681, 692, 725, 731, 361, + 732, 693, 620, 411, 531, 410, 707, 621, 622, 396, + 702, 0, 0, 687, 0, 0, 0, 0, 0, 0, + 510, 461, 0, 0, 510, 721, 715, 561, 0, 509, + 0, 0, 0, 509, 0, 0, 0, 0, 0, 0, + 0, 730, 0, 0, 531, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 462, 689, + 0, 0, 0, 0, 0, 0, 0, 361, 0, 0, + 0, 0, 0, 390, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 689, 0, 0, + 0, 0, 0, 0, 0, 510, 510, 0, 510, 0, + 0, 0, 0, 0, 509, 509, 0, 509, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 391, + 0, 0, 0, 0, 510, 0, 0, 0, 361, 0, + 0, 0, 0, 509, 0, 510, 0, 0, 0, 0, + 0, 0, 510, 0, 509, 0, 0, 0, 0, 0, + 0, 509, 0, 510, 0, 0, 0, 510, 0, 0, + 0, 0, 509, 510, 0, 0, 509, 0, 0, 0, + 395, 0, 509, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 294, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 295, 296, 297, 298, 299, 0, 0, 0, + 0, 0, 0, 0, 0, 300, 301, 302, 303, 304, + 305, 306, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 307, 308, 309, 310, 311, 312, + 0, 0, 0, 0, 0, 0, 0, 0, 313, 0, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, + 344, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 0, 0, 423, 424, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 425, 426, 0, 487, 0, + 488, 489, 0, 0, 0, 0, 490, 427, 428, 429, + 430, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 295, 296, 297, 298, 299, 0, 0, 0, 431, 432, + 433, 434, 435, 300, 301, 302, 303, 304, 305, 306, + 491, 492, 493, 494, 0, 495, 496, 497, 498, 499, + 500, 501, 307, 308, 309, 310, 311, 312, 436, 437, + 438, 439, 440, 441, 442, 443, 313, 502, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, + 336, 337, 338, 339, 340, 341, 342, 343, 344, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 0, 0, 423, 424, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 425, 426, 0, 487, 0, 488, 618, + 0, 0, 0, 0, 490, 427, 428, 429, 430, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 295, 296, + 297, 298, 299, 0, 0, 0, 431, 432, 433, 434, + 435, 300, 301, 302, 303, 304, 305, 306, 491, 492, + 493, 494, 0, 495, 496, 497, 498, 499, 500, 501, + 307, 308, 309, 310, 311, 312, 436, 437, 438, 439, + 440, 441, 442, 443, 313, 502, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, + 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, + 338, 339, 340, 341, 342, 343, 344, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 0, 0, 423, 424, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 425, 426, 0, 487, 0, 488, 0, 0, 0, + 0, 0, 490, 427, 428, 429, 430, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 295, 296, 297, 298, + 299, 0, 0, 0, 431, 432, 433, 434, 435, 300, + 301, 302, 303, 304, 305, 306, 491, 492, 493, 494, + 0, 495, 496, 497, 498, 499, 500, 501, 307, 308, + 309, 310, 311, 312, 436, 437, 438, 439, 440, 441, + 442, 443, 313, 502, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 0, 0, + 423, 424, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 425, + 426, 0, 487, 0, 409, 0, 0, 0, 0, 0, + 490, 427, 428, 429, 430, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 295, 296, 297, 298, 299, 0, + 0, 0, 431, 432, 433, 434, 435, 300, 301, 302, + 303, 304, 305, 306, 491, 492, 493, 494, 0, 495, + 496, 497, 498, 499, 500, 501, 307, 308, 309, 310, + 311, 312, 436, 437, 438, 439, 440, 441, 442, 443, + 313, 502, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, + 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, + 342, 343, 344, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 0, 0, 423, 424, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 425, 426, 0, + 487, 0, 0, 0, 0, 0, 0, 0, 490, 427, + 428, 429, 430, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 295, 296, 297, 298, 299, 0, 0, 0, + 431, 432, 433, 434, 435, 300, 301, 302, 303, 304, + 305, 306, 491, 492, 493, 494, 0, 495, 496, 497, + 498, 499, 500, 501, 307, 308, 309, 310, 311, 312, + 436, 437, 438, 439, 440, 441, 442, 443, 313, 502, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, + 344, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 0, 0, 423, 424, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 425, 426, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 490, 427, 428, 429, + 430, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 295, 296, 297, 298, 299, 0, 0, 0, 431, 432, + 433, 434, 435, 300, 301, 302, 303, 304, 305, 306, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 307, 308, 309, 310, 311, 312, 436, 437, + 438, 439, 440, 441, 442, 443, 313, 0, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, + 336, 337, 338, 339, 340, 341, 342, 343, 344, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 0, 0, 423, 424, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 425, 426, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 427, 428, 429, 430, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 295, 296, + 297, 298, 0, 0, 0, 0, 431, 432, 433, 434, + 435, 300, 301, 302, 303, 304, 305, 306, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 307, 308, 309, 310, 311, 312, 436, 437, 438, 439, + 440, 441, 442, 443, 313, 0, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, + 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, + 338, 339, 340, 341, 342, 343, 344, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 294, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 295, 296, 297, 298, + 299, 0, 0, 0, 0, 0, 0, 0, 0, 300, + 301, 302, 303, 304, 305, 306, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 307, 308, + 309, 310, 311, 312, 0, 0, 0, 0, 0, 0, + 0, 0, 313, 0, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 388, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 295, 296, 297, 298, 0, 0, + 0, 0, 0, 0, 0, 0, 389, 300, 301, 302, + 303, 304, 305, 306, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 307, 308, 309, 310, + 311, 312, 0, 0, 0, 0, 0, 0, 0, 0, + 313, 0, 314, 315, 316, 317, 318, 319, 320, 321, + 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, + 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, + 342, 343, 344, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 558, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 295, 296, 297, 298, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 300, 301, 302, 303, 304, + 305, 306, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 307, 308, 309, 310, 311, 312, + 0, 0, 0, 0, 0, 0, 0, 0, 313, 0, + 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, + 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, + 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, + 344, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 640, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 295, 296, 297, 298, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 300, 301, 302, 303, 304, 305, 306, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 307, 308, 309, 310, 311, 312, 0, 0, + 0, 0, 0, 0, 0, 0, 313, 0, 314, 315, + 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, + 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, + 336, 337, 338, 339, 340, 341, 342, 343, 344, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 678, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 295, 296, + 297, 298, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 300, 301, 302, 303, 304, 305, 306, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 307, 308, 309, 310, 311, 312, 0, 0, 0, 0, + 0, 0, 0, 0, 313, 0, 314, 315, 316, 317, + 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, + 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, + 338, 339, 340, 341, 342, 343, 344, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 295, 296, 297, 298, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 300, + 301, 302, 303, 304, 305, 306, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 307, 308, + 309, 310, 311, 312, 0, 0, 0, 0, 0, 0, + 0, 0, 313, 0, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, + 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 0, 0, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 0, 0, 423, + 424, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 425, 426, + 0, 0, 0, 530, 697, 0, 0, 0, 0, 0, + 427, 428, 429, 430, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 431, 432, 433, 434, 435, 300, 0, 0, 0, + 0, 305, 306, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 436, 437, 438, 439, 440, 441, 442, 443, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 326, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 0, 0, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 0, 0, 423, 424, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 425, 426, 0, + 0, 471, 0, 0, 0, 0, 0, 0, 0, 427, + 428, 429, 430, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 431, 432, 433, 434, 435, 300, 0, 0, 0, 0, + 305, 306, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 436, 437, 438, 439, 440, 441, 442, 443, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 326, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 0, 0, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, + 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, + 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, + 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, + 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, + 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, + 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, + 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, + 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, + 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, + 289, 290, 291, 292, 293, 0, 0, 423, 424, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 425, 426, 0, 0, + 0, 530, 0, 0, 0, 0, 0, 0, 427, 428, + 429, 430, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 431, + 432, 433, 434, 435, 300, 0, 0, 0, 0, 305, + 306, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 436, + 437, 438, 439, 440, 441, 442, 443, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 326, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 0, + 0, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 0, 0, 423, 424, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 425, 426, 0, 0, 586, + 0, 0, 0, 0, 0, 0, 0, 427, 428, 429, + 430, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 431, 432, + 433, 434, 435, 300, 0, 0, 0, 0, 305, 306, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 436, 437, + 438, 439, 440, 441, 442, 443, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 326, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 0, 0, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, + 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, + 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, + 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, + 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, + 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, + 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 0, 0, 423, 424, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 425, 426, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 598, 427, 428, 429, 430, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 431, 432, 433, + 434, 435, 300, 0, 0, 0, 0, 305, 306, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 436, 437, 438, + 439, 440, 441, 442, 443, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 326, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 0, 0, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 0, 0, 423, 424, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 425, 426, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 427, 428, 429, 430, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 431, 432, 433, 434, + 435, 300, 0, 0, 0, 0, 305, 306, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 436, 437, 438, 439, + 440, 441, 442, 443, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 326, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 0, 0, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, + 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, + 293, 454, 0, 423, 424, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 472, 0, 0, 0, 0, + 0, 0, 425, 426, 0, 0, 0, 0, 0, 0, + 0, 0, 538, 539, 427, 428, 429, 430, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 431, 432, 433, 434, 435, + 300, 0, 0, 0, 551, 305, 548, 0, 0, 0, + 0, 0, 0, 0, 0, 472, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 436, 437, 438, 439, 440, + 441, 442, 443, 0, 472, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 326, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 635, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 641, 642, 643, 472, 472, 472, + 472, 472, 472, 472, 472, 472, 472, 472, 472, 472, + 472, 472, 472, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 472 +}; + +static const yytype_int16 yycheck[] = +{ + 0, 325, 0, 318, 0, 320, 320, 320, 465, 0, + 320, 320, 327, 337, 328, 328, 318, 327, 327, 318, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 534, 328, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 358, 350, 417, 426, 590, 528, 516, 530, + 406, 322, 533, 463, 387, 354, 300, 301, 305, 666, + 393, 296, 297, 320, 298, 299, 319, 383, 362, 328, + 327, 365, 379, 367, 368, 325, 375, 371, 328, 452, + 350, 414, 415, 354, 318, 325, 320, 694, 358, 350, + 324, 398, 336, 337, 341, 354, 366, 358, 366, 369, + 366, 355, 319, 302, 303, 366, 360, 361, 325, 379, + 466, 321, 354, 383, 705, 325, 336, 498, 379, 500, + 325, 712, 383, 328, 322, 319, 594, 325, 398, 485, + 328, 325, 723, 322, 319, 319, 319, 398, 319, 409, + 325, 325, 325, 624, 325, 461, 319, 323, 409, 325, + 560, 325, 325, 354, 328, 528, 463, 530, 465, 330, + 533, 332, 543, 322, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 547, 572, 573, 574, 575, 325, + 325, 326, 328, 727, 327, 325, 325, 354, 328, 328, + 354, 461, 320, 463, 328, 465, 677, 665, 325, 326, + 461, 327, 463, 354, 465, 343, 344, 345, 333, 334, + 335, 568, 569, 576, 577, 596, 570, 571, 628, 600, + 319, 318, 555, 354, 320, 354, 340, 534, 339, 338, + 304, 306, 321, 320, 318, 323, 717, 328, 328, 318, + 318, 318, 615, 616, 326, 328, 516, 328, 318, 318, + 354, 624, 354, 560, 319, 516, 354, 321, 321, 325, + 318, 362, 321, 323, 534, 733, 319, 318, 354, 319, + 322, 327, 319, 534, 328, 578, 322, 322, 579, 660, + 328, 366, 323, 580, 582, 666, 581, 583, 683, 383, + 560, 383, 299, 468, 554, 638, 668, 711, 723, 560, + 724, 668, 526, 379, 677, 377, 694, 526, 526, 366, + 685, -1, -1, 694, -1, -1, -1, -1, -1, -1, + 590, 628, -1, -1, 594, 706, 699, 683, -1, 590, + -1, -1, -1, 594, -1, -1, -1, -1, -1, -1, + -1, 722, -1, -1, 717, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 628, 666, + -1, -1, -1, -1, -1, -1, -1, 628, -1, -1, + -1, -1, -1, 689, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 694, -1, -1, + -1, -1, -1, -1, -1, 665, 666, -1, 668, -1, + -1, -1, -1, -1, 665, 666, -1, 668, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 689, + -1, -1, -1, -1, 694, -1, -1, -1, 689, -1, + -1, -1, -1, 694, -1, 705, -1, -1, -1, -1, + -1, -1, 712, -1, 705, -1, -1, -1, -1, -1, + -1, 712, -1, 723, -1, -1, -1, 727, -1, -1, + -1, -1, 723, 733, -1, -1, 727, -1, -1, -1, + 0, -1, 733, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 328, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 342, 343, 344, 345, 346, -1, -1, -1, + -1, -1, -1, -1, -1, 355, 356, 357, 358, 359, + 360, 361, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 374, 375, 376, 377, 378, 379, + -1, -1, -1, -1, -1, -1, -1, -1, 388, -1, + 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, + 420, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, -1, -1, 298, 299, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 317, 318, -1, 320, -1, + 322, 323, -1, -1, -1, -1, 328, 329, 330, 331, + 332, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 342, 343, 344, 345, 346, -1, -1, -1, 350, 351, + 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, + 362, 363, 364, 365, -1, 367, 368, 369, 370, 371, + 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, + 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, + 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, + 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, + 412, 413, 414, 415, 416, 417, 418, 419, 420, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, -1, -1, 298, 299, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 317, 318, -1, 320, -1, 322, 323, + -1, -1, -1, -1, 328, 329, 330, 331, 332, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 342, 343, + 344, 345, 346, -1, -1, -1, 350, 351, 352, 353, + 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, + 364, 365, -1, 367, 368, 369, 370, 371, 372, 373, + 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, + 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, + 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, + 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, + 414, 415, 416, 417, 418, 419, 420, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + -1, -1, 298, 299, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 317, 318, -1, 320, -1, 322, -1, -1, -1, + -1, -1, 328, 329, 330, 331, 332, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 342, 343, 344, 345, + 346, -1, -1, -1, 350, 351, 352, 353, 354, 355, + 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, + -1, 367, 368, 369, 370, 371, 372, 373, 374, 375, + 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, + 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, + 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, + 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, + 416, 417, 418, 419, 420, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 295, -1, -1, + 298, 299, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 317, + 318, -1, 320, -1, 322, -1, -1, -1, -1, -1, + 328, 329, 330, 331, 332, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 342, 343, 344, 345, 346, -1, + -1, -1, 350, 351, 352, 353, 354, 355, 356, 357, + 358, 359, 360, 361, 362, 363, 364, 365, -1, 367, + 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, + 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, + 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, + 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, + 418, 419, 420, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, -1, -1, 298, 299, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 317, 318, -1, + 320, -1, -1, -1, -1, -1, -1, -1, 328, 329, + 330, 331, 332, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 342, 343, 344, 345, 346, -1, -1, -1, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, -1, 367, 368, 369, + 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, + 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, + 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, + 420, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, -1, -1, 298, 299, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 317, 318, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 328, 329, 330, 331, + 332, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 342, 343, 344, 345, 346, -1, -1, -1, 350, 351, + 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 374, 375, 376, 377, 378, 379, 380, 381, + 382, 383, 384, 385, 386, 387, 388, -1, 390, 391, + 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, + 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, + 412, 413, 414, 415, 416, 417, 418, 419, 420, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, -1, -1, 298, 299, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 317, 318, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 329, 330, 331, 332, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 342, 343, + 344, 345, -1, -1, -1, -1, 350, 351, 352, 353, + 354, 355, 356, 357, 358, 359, 360, 361, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, + 384, 385, 386, 387, 388, -1, 390, 391, 392, 393, + 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, + 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, + 414, 415, 416, 417, 418, 419, 420, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 328, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 342, 343, 344, 345, + 346, -1, -1, -1, -1, -1, -1, -1, -1, 355, + 356, 357, 358, 359, 360, 361, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 374, 375, + 376, 377, 378, 379, -1, -1, -1, -1, -1, -1, + -1, -1, 388, -1, 390, 391, 392, 393, 394, 395, + 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, + 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, + 416, 417, 418, 419, 420, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 295, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 328, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 342, 343, 344, 345, -1, -1, + -1, -1, -1, -1, -1, -1, 354, 355, 356, 357, + 358, 359, 360, 361, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 374, 375, 376, 377, + 378, 379, -1, -1, -1, -1, -1, -1, -1, -1, + 388, -1, 390, 391, 392, 393, 394, 395, 396, 397, + 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, + 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, + 418, 419, 420, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 323, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 342, 343, 344, 345, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 355, 356, 357, 358, 359, + 360, 361, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 374, 375, 376, 377, 378, 379, + -1, -1, -1, -1, -1, -1, -1, -1, 388, -1, + 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, + 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, + 420, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 323, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 342, 343, 344, 345, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 355, 356, 357, 358, 359, 360, 361, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 374, 375, 376, 377, 378, 379, -1, -1, + -1, -1, -1, -1, -1, -1, 388, -1, 390, 391, + 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, + 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, + 412, 413, 414, 415, 416, 417, 418, 419, 420, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 323, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 342, 343, + 344, 345, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 355, 356, 357, 358, 359, 360, 361, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 374, 375, 376, 377, 378, 379, -1, -1, -1, -1, + -1, -1, -1, -1, 388, -1, 390, 391, 392, 393, + 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, + 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, + 414, 415, 416, 417, 418, 419, 420, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 342, 343, 344, 345, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 355, + 356, 357, 358, 359, 360, 361, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 374, 375, + 376, 377, 378, 379, -1, -1, -1, -1, -1, -1, + -1, -1, 388, -1, 390, 391, 392, 393, 394, 395, + 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, + 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, + 416, 417, 418, 419, 420, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, -1, -1, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, + 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, + 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, + 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, + 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, + 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, + 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, + 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, + 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, + 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, + 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, + 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, + 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, + 289, 290, 291, 292, 293, 294, 295, -1, -1, 298, + 299, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 317, 318, + -1, -1, -1, 322, 323, -1, -1, -1, -1, -1, + 329, 330, 331, 332, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 350, 351, 352, 353, 354, 355, -1, -1, -1, + -1, 360, 361, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 380, 381, 382, 383, 384, 385, 386, 387, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 402, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, -1, -1, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, -1, -1, 298, 299, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 317, 318, -1, + -1, 321, -1, -1, -1, -1, -1, -1, -1, 329, + 330, 331, 332, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 350, 351, 352, 353, 354, 355, -1, -1, -1, -1, + 360, 361, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 380, 381, 382, 383, 384, 385, 386, 387, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 402, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + -1, -1, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, + 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, + 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, + 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, + 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, + 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, + 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 294, 295, -1, -1, 298, 299, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 317, 318, -1, -1, + -1, 322, -1, -1, -1, -1, -1, -1, 329, 330, + 331, 332, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 350, + 351, 352, 353, 354, 355, -1, -1, -1, -1, 360, + 361, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 380, + 381, 382, 383, 384, 385, 386, 387, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 402, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, -1, + -1, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, -1, -1, 298, 299, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 317, 318, -1, -1, 321, + -1, -1, -1, -1, -1, -1, -1, 329, 330, 331, + 332, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 350, 351, + 352, 353, 354, 355, -1, -1, -1, -1, 360, 361, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 380, 381, + 382, 383, 384, 385, 386, 387, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 402, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, -1, -1, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, + 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, + 293, 294, 295, -1, -1, 298, 299, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 317, 318, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 328, 329, 330, 331, 332, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 350, 351, 352, + 353, 354, 355, -1, -1, -1, -1, 360, 361, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 380, 381, 382, + 383, 384, 385, 386, 387, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 402, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, -1, -1, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, -1, -1, 298, 299, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 317, 318, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 329, 330, 331, 332, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 350, 351, 352, 353, + 354, 355, -1, -1, -1, -1, 360, 361, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 380, 381, 382, 383, + 384, 385, 386, 387, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 402, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, -1, -1, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 392, -1, 298, 299, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 406, -1, -1, -1, -1, + -1, -1, 317, 318, -1, -1, -1, -1, -1, -1, + -1, -1, 423, 424, 329, 330, 331, 332, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 350, 351, 352, 353, 354, + 355, -1, -1, -1, 455, 360, 361, -1, -1, -1, + -1, -1, -1, -1, -1, 466, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 380, 381, 382, 383, 384, + 385, 386, 387, -1, 485, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 402, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 552, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 565, 566, 567, 568, 569, 570, + 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, + 581, 582, 583, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 683 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int16 yystos[] = +{ + 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 328, 342, 343, 344, 345, 346, + 355, 356, 357, 358, 359, 360, 361, 374, 375, 376, + 377, 378, 379, 388, 390, 391, 392, 393, 394, 395, + 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, + 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, + 416, 417, 418, 419, 420, 452, 453, 456, 457, 458, + 459, 463, 464, 465, 466, 467, 468, 471, 472, 473, + 474, 475, 477, 482, 483, 484, 525, 526, 527, 483, + 322, 354, 318, 318, 328, 354, 328, 528, 319, 325, + 460, 461, 462, 472, 477, 325, 328, 354, 328, 354, + 473, 477, 336, 479, 480, 0, 526, 477, 486, 322, + 354, 375, 469, 470, 354, 476, 320, 328, 478, 322, + 504, 461, 460, 462, 354, 354, 318, 327, 478, 322, + 325, 328, 455, 298, 299, 317, 318, 329, 330, 331, + 332, 350, 351, 352, 353, 354, 380, 381, 382, 383, + 384, 385, 386, 387, 422, 423, 424, 426, 427, 428, + 429, 430, 431, 432, 433, 434, 475, 477, 481, 478, + 328, 472, 477, 487, 488, 485, 327, 319, 325, 319, + 325, 321, 433, 435, 436, 437, 438, 439, 440, 441, + 442, 443, 444, 445, 446, 320, 328, 320, 322, 323, + 328, 362, 363, 364, 365, 367, 368, 369, 370, 371, + 372, 373, 389, 433, 446, 448, 450, 452, 456, 475, + 477, 493, 494, 495, 496, 497, 505, 506, 507, 508, + 511, 512, 515, 516, 517, 524, 529, 478, 327, 478, + 322, 448, 491, 327, 454, 354, 325, 328, 433, 433, + 450, 298, 299, 320, 324, 319, 319, 325, 361, 448, + 318, 433, 325, 337, 477, 354, 489, 490, 323, 488, + 487, 446, 451, 470, 354, 333, 334, 335, 330, 332, + 296, 297, 300, 301, 336, 337, 302, 303, 340, 339, + 338, 304, 306, 305, 341, 321, 321, 446, 320, 323, + 498, 318, 328, 328, 519, 318, 318, 328, 328, 450, + 318, 450, 326, 328, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 327, 449, 325, 328, 323, 494, + 508, 512, 517, 491, 327, 491, 492, 491, 487, 354, + 319, 425, 450, 354, 448, 433, 489, 478, 325, 328, + 323, 433, 433, 433, 435, 435, 436, 436, 437, 437, + 437, 437, 438, 438, 439, 440, 441, 442, 443, 444, + 447, 321, 354, 530, 531, 505, 518, 494, 520, 450, + 328, 450, 326, 448, 448, 491, 323, 325, 323, 321, + 328, 490, 450, 318, 321, 325, 499, 450, 465, 472, + 510, 362, 493, 506, 521, 319, 319, 323, 491, 326, + 451, 321, 531, 323, 354, 319, 318, 510, 522, 523, + 501, 502, 503, 509, 513, 448, 319, 327, 495, 500, + 504, 450, 328, 319, 366, 497, 495, 322, 491, 319, + 450, 500, 501, 505, 514, 328, 323 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int16 yyr1[] = +{ + 0, 421, 422, 423, 423, 423, 423, 423, 423, 423, + 423, 423, 423, 423, 423, 423, 423, 423, 424, 424, + 424, 424, 424, 424, 425, 426, 427, 428, 428, 429, + 429, 430, 430, 431, 432, 432, 432, 433, 433, 433, + 433, 434, 434, 434, 434, 435, 435, 435, 435, 436, + 436, 436, 437, 437, 437, 438, 438, 438, 438, 438, + 439, 439, 439, 440, 440, 441, 441, 442, 442, 443, + 443, 444, 444, 445, 445, 446, 447, 446, 448, 448, + 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, + 449, 450, 450, 451, 452, 452, 452, 452, 452, 452, + 452, 452, 452, 454, 453, 455, 455, 456, 457, 457, + 458, 458, 459, 460, 460, 461, 461, 461, 461, 462, + 463, 463, 463, 463, 463, 464, 464, 464, 464, 464, + 465, 465, 466, 467, 467, 467, 467, 467, 467, 467, + 467, 468, 469, 469, 470, 470, 470, 471, 472, 472, + 473, 473, 473, 473, 473, 473, 473, 474, 474, 474, + 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, + 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, + 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, + 474, 474, 475, 476, 476, 477, 477, 478, 478, 478, + 478, 479, 479, 480, 481, 481, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, + 482, 482, 482, 482, 482, 482, 482, 482, 482, 483, + 483, 483, 485, 484, 486, 484, 487, 487, 488, 488, + 489, 489, 490, 490, 491, 491, 491, 492, 492, 493, + 494, 494, 495, 495, 495, 495, 495, 495, 495, 495, + 496, 497, 498, 499, 497, 500, 500, 502, 501, 503, + 501, 504, 504, 505, 505, 506, 506, 507, 507, 508, + 509, 509, 510, 510, 511, 511, 513, 512, 514, 514, + 515, 515, 516, 516, 518, 517, 519, 517, 520, 517, + 521, 521, 522, 522, 523, 523, 524, 524, 524, 524, + 524, 525, 525, 526, 526, 526, 528, 527, 529, 530, + 530, 531, 531 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 1, 3, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 1, 3, 2, 2, 1, 1, 1, 2, 2, 2, + 1, 2, 3, 2, 1, 1, 1, 1, 2, 2, + 2, 1, 1, 1, 1, 1, 3, 3, 3, 1, + 3, 3, 1, 3, 3, 1, 3, 3, 3, 3, + 1, 3, 3, 1, 3, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 3, 1, 0, 6, 1, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 1, 2, 2, 4, 2, 3, 4, + 2, 3, 4, 0, 6, 2, 3, 2, 1, 1, + 2, 3, 3, 2, 3, 2, 1, 2, 1, 1, + 1, 3, 4, 6, 5, 1, 2, 3, 5, 4, + 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4, 1, 3, 1, 3, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4, 1, 1, 3, 2, 3, 2, 3, 3, + 4, 1, 0, 3, 1, 3, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 6, 0, 5, 1, 2, 3, 4, + 1, 3, 1, 2, 1, 3, 4, 1, 3, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 0, 0, 5, 1, 1, 0, 2, 0, + 2, 2, 3, 1, 2, 1, 2, 1, 2, 5, + 3, 1, 1, 4, 1, 2, 0, 8, 0, 1, + 3, 2, 1, 2, 0, 6, 0, 8, 0, 7, + 1, 1, 1, 0, 2, 3, 2, 2, 2, 3, + 2, 1, 2, 1, 1, 1, 0, 3, 5, 1, + 3, 1, 4 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (pParseContext, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, pParseContext); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, glslang::TParseContext* pParseContext) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (pParseContext); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyo, yytoknum[yytype], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, int yytype, YYSTYPE const * const yyvaluep, glslang::TParseContext* pParseContext) +{ + YYFPRINTF (yyo, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + yy_symbol_value_print (yyo, yytype, yyvaluep, pParseContext); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, int yyrule, glslang::TParseContext* pParseContext) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[+yyssp[yyi + 1 - yynrhs]], + &yyvsp[(yyi + 1) - (yynrhs)] + , pParseContext); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, pParseContext); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + yy_state_t *yyssp, int yytoken) +{ + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Actual size of YYARG. */ + int yycount = 0; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[+*yyssp]; + YYPTRDIFF_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + yysize = yysize0; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + /* Don't count the "%s"s in the final size, but reserve room for + the terminator. */ + YYPTRDIFF_T yysize1 = yysize + (yystrlen (yyformat) - 2 * yycount) + 1; + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return 2; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, glslang::TParseContext* pParseContext) +{ + YYUSE (yyvaluep); + YYUSE (pParseContext); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (glslang::TParseContext* pParseContext) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs; + + yy_state_fast_t yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss; + yy_state_t *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYPTRDIFF_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, parseContext); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 358 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleVariable((yyvsp[0].lex).loc, (yyvsp[0].lex).symbol, (yyvsp[0].lex).string); + } +#line 4334 "MachineIndependent/glslang_tab.cpp" + break; + + case 3: +#line 364 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 4342 "MachineIndependent/glslang_tab.cpp" + break; + + case 4: +#line 367 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[-1].interm.intermTypedNode); + if ((yyval.interm.intermTypedNode)->getAsConstantUnion()) + (yyval.interm.intermTypedNode)->getAsConstantUnion()->setExpression(); + } +#line 4352 "MachineIndependent/glslang_tab.cpp" + break; + + case 5: +#line 372 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtFloat, (yyvsp[0].lex).loc, true); + } +#line 4360 "MachineIndependent/glslang_tab.cpp" + break; + + case 6: +#line 375 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i, (yyvsp[0].lex).loc, true); + } +#line 4368 "MachineIndependent/glslang_tab.cpp" + break; + + case 7: +#line 378 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u, (yyvsp[0].lex).loc, true); + } +#line 4377 "MachineIndependent/glslang_tab.cpp" + break; + + case 8: +#line 382 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).b, (yyvsp[0].lex).loc, true); + } +#line 4385 "MachineIndependent/glslang_tab.cpp" + break; + + case 9: +#line 386 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).string, (yyvsp[0].lex).loc, true); + } +#line 4393 "MachineIndependent/glslang_tab.cpp" + break; + + case 10: +#line 389 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i, (yyvsp[0].lex).loc, true); + } +#line 4402 "MachineIndependent/glslang_tab.cpp" + break; + + case 11: +#line 393 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u, (yyvsp[0].lex).loc, true); + } +#line 4411 "MachineIndependent/glslang_tab.cpp" + break; + + case 12: +#line 397 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i64, (yyvsp[0].lex).loc, true); + } +#line 4420 "MachineIndependent/glslang_tab.cpp" + break; + + case 13: +#line 401 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u64, (yyvsp[0].lex).loc, true); + } +#line 4429 "MachineIndependent/glslang_tab.cpp" + break; + + case 14: +#line 405 "MachineIndependent/glslang.y" + { + parseContext.explicitInt16Check((yyvsp[0].lex).loc, "16-bit integer literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((short)(yyvsp[0].lex).i, (yyvsp[0].lex).loc, true); + } +#line 4438 "MachineIndependent/glslang_tab.cpp" + break; + + case 15: +#line 409 "MachineIndependent/glslang.y" + { + parseContext.explicitInt16Check((yyvsp[0].lex).loc, "16-bit unsigned integer literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((unsigned short)(yyvsp[0].lex).u, (yyvsp[0].lex).loc, true); + } +#line 4447 "MachineIndependent/glslang_tab.cpp" + break; + + case 16: +#line 413 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double literal"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtDouble, (yyvsp[0].lex).loc, true); + } +#line 4458 "MachineIndependent/glslang_tab.cpp" + break; + + case 17: +#line 419 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtFloat16, (yyvsp[0].lex).loc, true); + } +#line 4467 "MachineIndependent/glslang_tab.cpp" + break; + + case 18: +#line 427 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 4475 "MachineIndependent/glslang_tab.cpp" + break; + + case 19: +#line 430 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBracketDereference((yyvsp[-2].lex).loc, (yyvsp[-3].interm.intermTypedNode), (yyvsp[-1].interm.intermTypedNode)); + } +#line 4483 "MachineIndependent/glslang_tab.cpp" + break; + + case 20: +#line 433 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 4491 "MachineIndependent/glslang_tab.cpp" + break; + + case 21: +#line 436 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleDotDereference((yyvsp[0].lex).loc, (yyvsp[-2].interm.intermTypedNode), *(yyvsp[0].lex).string); + } +#line 4499 "MachineIndependent/glslang_tab.cpp" + break; + + case 22: +#line 439 "MachineIndependent/glslang.y" + { + parseContext.variableCheck((yyvsp[-1].interm.intermTypedNode)); + parseContext.lValueErrorCheck((yyvsp[0].lex).loc, "++", (yyvsp[-1].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[0].lex).loc, "++", EOpPostIncrement, (yyvsp[-1].interm.intermTypedNode)); + } +#line 4509 "MachineIndependent/glslang_tab.cpp" + break; + + case 23: +#line 444 "MachineIndependent/glslang.y" + { + parseContext.variableCheck((yyvsp[-1].interm.intermTypedNode)); + parseContext.lValueErrorCheck((yyvsp[0].lex).loc, "--", (yyvsp[-1].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[0].lex).loc, "--", EOpPostDecrement, (yyvsp[-1].interm.intermTypedNode)); + } +#line 4519 "MachineIndependent/glslang_tab.cpp" + break; + + case 24: +#line 452 "MachineIndependent/glslang.y" + { + parseContext.integerCheck((yyvsp[0].interm.intermTypedNode), "[]"); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 4528 "MachineIndependent/glslang_tab.cpp" + break; + + case 25: +#line 459 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleFunctionCall((yyvsp[0].interm).loc, (yyvsp[0].interm).function, (yyvsp[0].interm).intermNode); + delete (yyvsp[0].interm).function; + } +#line 4537 "MachineIndependent/glslang_tab.cpp" + break; + + case 26: +#line 466 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + } +#line 4545 "MachineIndependent/glslang_tab.cpp" + break; + + case 27: +#line 472 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-1].interm); + (yyval.interm).loc = (yyvsp[0].lex).loc; + } +#line 4554 "MachineIndependent/glslang_tab.cpp" + break; + + case 28: +#line 476 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-1].interm); + (yyval.interm).loc = (yyvsp[0].lex).loc; + } +#line 4563 "MachineIndependent/glslang_tab.cpp" + break; + + case 29: +#line 483 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-1].interm); + } +#line 4571 "MachineIndependent/glslang_tab.cpp" + break; + + case 30: +#line 486 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + } +#line 4579 "MachineIndependent/glslang_tab.cpp" + break; + + case 31: +#line 492 "MachineIndependent/glslang.y" + { + TParameter param = { 0, new TType }; + param.type->shallowCopy((yyvsp[0].interm.intermTypedNode)->getType()); + (yyvsp[-1].interm).function->addParameter(param); + (yyval.interm).function = (yyvsp[-1].interm).function; + (yyval.interm).intermNode = (yyvsp[0].interm.intermTypedNode); + } +#line 4591 "MachineIndependent/glslang_tab.cpp" + break; + + case 32: +#line 499 "MachineIndependent/glslang.y" + { + TParameter param = { 0, new TType }; + param.type->shallowCopy((yyvsp[0].interm.intermTypedNode)->getType()); + (yyvsp[-2].interm).function->addParameter(param); + (yyval.interm).function = (yyvsp[-2].interm).function; + (yyval.interm).intermNode = parseContext.intermediate.growAggregate((yyvsp[-2].interm).intermNode, (yyvsp[0].interm.intermTypedNode), (yyvsp[-1].lex).loc); + } +#line 4603 "MachineIndependent/glslang_tab.cpp" + break; + + case 33: +#line 509 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-1].interm); + } +#line 4611 "MachineIndependent/glslang_tab.cpp" + break; + + case 34: +#line 517 "MachineIndependent/glslang.y" + { + // Constructor + (yyval.interm).intermNode = 0; + (yyval.interm).function = parseContext.handleConstructorCall((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type)); + } +#line 4621 "MachineIndependent/glslang_tab.cpp" + break; + + case 35: +#line 522 "MachineIndependent/glslang.y" + { + // + // Should be a method or subroutine call, but we haven't recognized the arguments yet. + // + (yyval.interm).function = 0; + (yyval.interm).intermNode = 0; + + TIntermMethod* method = (yyvsp[0].interm.intermTypedNode)->getAsMethodNode(); + if (method) { + (yyval.interm).function = new TFunction(&method->getMethodName(), TType(EbtInt), EOpArrayLength); + (yyval.interm).intermNode = method->getObject(); + } else { + TIntermSymbol* symbol = (yyvsp[0].interm.intermTypedNode)->getAsSymbolNode(); + if (symbol) { + parseContext.reservedErrorCheck(symbol->getLoc(), symbol->getName()); + TFunction *function = new TFunction(&symbol->getName(), TType(EbtVoid)); + (yyval.interm).function = function; + } else + parseContext.error((yyvsp[0].interm.intermTypedNode)->getLoc(), "function call, method, or subroutine call expected", "", ""); + } + + if ((yyval.interm).function == 0) { + // error recover + TString* empty = NewPoolTString(""); + (yyval.interm).function = new TFunction(empty, TType(EbtVoid), EOpNull); + } + } +#line 4653 "MachineIndependent/glslang_tab.cpp" + break; + + case 36: +#line 550 "MachineIndependent/glslang.y" + { + // Constructor + (yyval.interm).intermNode = 0; + (yyval.interm).function = parseContext.handleConstructorCall((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type)); + } +#line 4663 "MachineIndependent/glslang_tab.cpp" + break; + + case 37: +#line 559 "MachineIndependent/glslang.y" + { + parseContext.variableCheck((yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + if (TIntermMethod* method = (yyvsp[0].interm.intermTypedNode)->getAsMethodNode()) + parseContext.error((yyvsp[0].interm.intermTypedNode)->getLoc(), "incomplete method syntax", method->getMethodName().c_str(), ""); + } +#line 4674 "MachineIndependent/glslang_tab.cpp" + break; + + case 38: +#line 565 "MachineIndependent/glslang.y" + { + parseContext.lValueErrorCheck((yyvsp[-1].lex).loc, "++", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[-1].lex).loc, "++", EOpPreIncrement, (yyvsp[0].interm.intermTypedNode)); + } +#line 4683 "MachineIndependent/glslang_tab.cpp" + break; + + case 39: +#line 569 "MachineIndependent/glslang.y" + { + parseContext.lValueErrorCheck((yyvsp[-1].lex).loc, "--", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[-1].lex).loc, "--", EOpPreDecrement, (yyvsp[0].interm.intermTypedNode)); + } +#line 4692 "MachineIndependent/glslang_tab.cpp" + break; + + case 40: +#line 573 "MachineIndependent/glslang.y" + { + if ((yyvsp[-1].interm).op != EOpNull) { + char errorOp[2] = {0, 0}; + switch((yyvsp[-1].interm).op) { + case EOpNegative: errorOp[0] = '-'; break; + case EOpLogicalNot: errorOp[0] = '!'; break; + case EOpBitwiseNot: errorOp[0] = '~'; break; + default: break; // some compilers want this + } + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[-1].interm).loc, errorOp, (yyvsp[-1].interm).op, (yyvsp[0].interm.intermTypedNode)); + } else { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + if ((yyval.interm.intermTypedNode)->getAsConstantUnion()) + (yyval.interm.intermTypedNode)->getAsConstantUnion()->setExpression(); + } + } +#line 4713 "MachineIndependent/glslang_tab.cpp" + break; + + case 41: +#line 593 "MachineIndependent/glslang.y" + { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpNull; } +#line 4719 "MachineIndependent/glslang_tab.cpp" + break; + + case 42: +#line 594 "MachineIndependent/glslang.y" + { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpNegative; } +#line 4725 "MachineIndependent/glslang_tab.cpp" + break; + + case 43: +#line 595 "MachineIndependent/glslang.y" + { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpLogicalNot; } +#line 4731 "MachineIndependent/glslang_tab.cpp" + break; + + case 44: +#line 596 "MachineIndependent/glslang.y" + { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpBitwiseNot; + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise not"); } +#line 4738 "MachineIndependent/glslang_tab.cpp" + break; + + case 45: +#line 602 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 4744 "MachineIndependent/glslang_tab.cpp" + break; + + case 46: +#line 603 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "*", EOpMul, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 4754 "MachineIndependent/glslang_tab.cpp" + break; + + case 47: +#line 608 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "/", EOpDiv, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 4764 "MachineIndependent/glslang_tab.cpp" + break; + + case 48: +#line 613 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "%"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "%", EOpMod, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 4775 "MachineIndependent/glslang_tab.cpp" + break; + + case 49: +#line 622 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 4781 "MachineIndependent/glslang_tab.cpp" + break; + + case 50: +#line 623 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "+", EOpAdd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 4791 "MachineIndependent/glslang_tab.cpp" + break; + + case 51: +#line 628 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "-", EOpSub, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 4801 "MachineIndependent/glslang_tab.cpp" + break; + + case 52: +#line 636 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 4807 "MachineIndependent/glslang_tab.cpp" + break; + + case 53: +#line 637 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bit shift left"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "<<", EOpLeftShift, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 4818 "MachineIndependent/glslang_tab.cpp" + break; + + case 54: +#line 643 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bit shift right"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, ">>", EOpRightShift, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 4829 "MachineIndependent/glslang_tab.cpp" + break; + + case 55: +#line 652 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 4835 "MachineIndependent/glslang_tab.cpp" + break; + + case 56: +#line 653 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "<", EOpLessThan, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 4845 "MachineIndependent/glslang_tab.cpp" + break; + + case 57: +#line 658 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, ">", EOpGreaterThan, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 4855 "MachineIndependent/glslang_tab.cpp" + break; + + case 58: +#line 663 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "<=", EOpLessThanEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 4865 "MachineIndependent/glslang_tab.cpp" + break; + + case 59: +#line 668 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, ">=", EOpGreaterThanEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 4875 "MachineIndependent/glslang_tab.cpp" + break; + + case 60: +#line 676 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 4881 "MachineIndependent/glslang_tab.cpp" + break; + + case 61: +#line 677 "MachineIndependent/glslang.y" + { + parseContext.arrayObjectCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "array comparison"); + parseContext.opaqueCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "=="); + parseContext.specializationCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "=="); + parseContext.referenceCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "=="); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "==", EOpEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 4895 "MachineIndependent/glslang_tab.cpp" + break; + + case 62: +#line 686 "MachineIndependent/glslang.y" + { + parseContext.arrayObjectCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "array comparison"); + parseContext.opaqueCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "!="); + parseContext.specializationCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "!="); + parseContext.referenceCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "!="); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "!=", EOpNotEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 4909 "MachineIndependent/glslang_tab.cpp" + break; + + case 63: +#line 698 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 4915 "MachineIndependent/glslang_tab.cpp" + break; + + case 64: +#line 699 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bitwise and"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "&", EOpAnd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 4926 "MachineIndependent/glslang_tab.cpp" + break; + + case 65: +#line 708 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 4932 "MachineIndependent/glslang_tab.cpp" + break; + + case 66: +#line 709 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bitwise exclusive or"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "^", EOpExclusiveOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 4943 "MachineIndependent/glslang_tab.cpp" + break; + + case 67: +#line 718 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 4949 "MachineIndependent/glslang_tab.cpp" + break; + + case 68: +#line 719 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bitwise inclusive or"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "|", EOpInclusiveOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 4960 "MachineIndependent/glslang_tab.cpp" + break; + + case 69: +#line 728 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 4966 "MachineIndependent/glslang_tab.cpp" + break; + + case 70: +#line 729 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "&&", EOpLogicalAnd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 4976 "MachineIndependent/glslang_tab.cpp" + break; + + case 71: +#line 737 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 4982 "MachineIndependent/glslang_tab.cpp" + break; + + case 72: +#line 738 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "^^", EOpLogicalXor, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 4992 "MachineIndependent/glslang_tab.cpp" + break; + + case 73: +#line 746 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 4998 "MachineIndependent/glslang_tab.cpp" + break; + + case 74: +#line 747 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "||", EOpLogicalOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5008 "MachineIndependent/glslang_tab.cpp" + break; + + case 75: +#line 755 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5014 "MachineIndependent/glslang_tab.cpp" + break; + + case 76: +#line 756 "MachineIndependent/glslang.y" + { + ++parseContext.controlFlowNestingLevel; + } +#line 5022 "MachineIndependent/glslang_tab.cpp" + break; + + case 77: +#line 759 "MachineIndependent/glslang.y" + { + --parseContext.controlFlowNestingLevel; + parseContext.boolCheck((yyvsp[-4].lex).loc, (yyvsp[-5].interm.intermTypedNode)); + parseContext.rValueErrorCheck((yyvsp[-4].lex).loc, "?", (yyvsp[-5].interm.intermTypedNode)); + parseContext.rValueErrorCheck((yyvsp[-1].lex).loc, ":", (yyvsp[-2].interm.intermTypedNode)); + parseContext.rValueErrorCheck((yyvsp[-1].lex).loc, ":", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addSelection((yyvsp[-5].interm.intermTypedNode), (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yyvsp[-4].lex).loc); + if ((yyval.interm.intermTypedNode) == 0) { + parseContext.binaryOpError((yyvsp[-4].lex).loc, ":", (yyvsp[-2].interm.intermTypedNode)->getCompleteString(), (yyvsp[0].interm.intermTypedNode)->getCompleteString()); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } + } +#line 5039 "MachineIndependent/glslang_tab.cpp" + break; + + case 78: +#line 774 "MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5045 "MachineIndependent/glslang_tab.cpp" + break; + + case 79: +#line 775 "MachineIndependent/glslang.y" + { + parseContext.arrayObjectCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "array assignment"); + parseContext.opaqueCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "="); + parseContext.storage16BitAssignmentCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "="); + parseContext.specializationCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "="); + parseContext.lValueErrorCheck((yyvsp[-1].interm).loc, "assign", (yyvsp[-2].interm.intermTypedNode)); + parseContext.rValueErrorCheck((yyvsp[-1].interm).loc, "assign", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addAssign((yyvsp[-1].interm).op, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yyvsp[-1].interm).loc); + if ((yyval.interm.intermTypedNode) == 0) { + parseContext.assignError((yyvsp[-1].interm).loc, "assign", (yyvsp[-2].interm.intermTypedNode)->getCompleteString(), (yyvsp[0].interm.intermTypedNode)->getCompleteString()); + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } + } +#line 5063 "MachineIndependent/glslang_tab.cpp" + break; + + case 80: +#line 791 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpAssign; + } +#line 5072 "MachineIndependent/glslang_tab.cpp" + break; + + case 81: +#line 795 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpMulAssign; + } +#line 5081 "MachineIndependent/glslang_tab.cpp" + break; + + case 82: +#line 799 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpDivAssign; + } +#line 5090 "MachineIndependent/glslang_tab.cpp" + break; + + case 83: +#line 803 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "%="); + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpModAssign; + } +#line 5100 "MachineIndependent/glslang_tab.cpp" + break; + + case 84: +#line 808 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpAddAssign; + } +#line 5109 "MachineIndependent/glslang_tab.cpp" + break; + + case 85: +#line 812 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpSubAssign; + } +#line 5118 "MachineIndependent/glslang_tab.cpp" + break; + + case 86: +#line 816 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bit-shift left assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpLeftShiftAssign; + } +#line 5127 "MachineIndependent/glslang_tab.cpp" + break; + + case 87: +#line 820 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bit-shift right assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpRightShiftAssign; + } +#line 5136 "MachineIndependent/glslang_tab.cpp" + break; + + case 88: +#line 824 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise-and assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpAndAssign; + } +#line 5145 "MachineIndependent/glslang_tab.cpp" + break; + + case 89: +#line 828 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise-xor assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpExclusiveOrAssign; + } +#line 5154 "MachineIndependent/glslang_tab.cpp" + break; + + case 90: +#line 832 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise-or assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpInclusiveOrAssign; + } +#line 5163 "MachineIndependent/glslang_tab.cpp" + break; + + case 91: +#line 839 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 5171 "MachineIndependent/glslang_tab.cpp" + break; + + case 92: +#line 842 "MachineIndependent/glslang.y" + { + parseContext.samplerConstructorLocationCheck((yyvsp[-1].lex).loc, ",", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addComma((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yyvsp[-1].lex).loc); + if ((yyval.interm.intermTypedNode) == 0) { + parseContext.binaryOpError((yyvsp[-1].lex).loc, ",", (yyvsp[-2].interm.intermTypedNode)->getCompleteString(), (yyvsp[0].interm.intermTypedNode)->getCompleteString()); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } + } +#line 5184 "MachineIndependent/glslang_tab.cpp" + break; + + case 93: +#line 853 "MachineIndependent/glslang.y" + { + parseContext.constantValueCheck((yyvsp[0].interm.intermTypedNode), ""); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 5193 "MachineIndependent/glslang_tab.cpp" + break; + + case 94: +#line 860 "MachineIndependent/glslang.y" + { + parseContext.handleFunctionDeclarator((yyvsp[-1].interm).loc, *(yyvsp[-1].interm).function, true /* prototype */); + (yyval.interm.intermNode) = 0; + // TODO: 4.0 functionality: subroutines: make the identifier a user type for this signature + } +#line 5203 "MachineIndependent/glslang_tab.cpp" + break; + + case 95: +#line 865 "MachineIndependent/glslang.y" + { + if ((yyvsp[-1].interm).intermNode && (yyvsp[-1].interm).intermNode->getAsAggregate()) + (yyvsp[-1].interm).intermNode->getAsAggregate()->setOperator(EOpSequence); + (yyval.interm.intermNode) = (yyvsp[-1].interm).intermNode; + } +#line 5213 "MachineIndependent/glslang_tab.cpp" + break; + + case 96: +#line 870 "MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[-3].lex).loc, ENoProfile, 130, 0, "precision statement"); + // lazy setting of the previous scope's defaults, has effect only the first time it is called in a particular scope + parseContext.symbolTable.setPreviousDefaultPrecisions(&parseContext.defaultPrecision[0]); + parseContext.setDefaultPrecision((yyvsp[-3].lex).loc, (yyvsp[-1].interm.type), (yyvsp[-2].interm.type).qualifier.precision); + (yyval.interm.intermNode) = 0; + } +#line 5225 "MachineIndependent/glslang_tab.cpp" + break; + + case 97: +#line 877 "MachineIndependent/glslang.y" + { + parseContext.declareBlock((yyvsp[-1].interm).loc, *(yyvsp[-1].interm).typeList); + (yyval.interm.intermNode) = 0; + } +#line 5234 "MachineIndependent/glslang_tab.cpp" + break; + + case 98: +#line 881 "MachineIndependent/glslang.y" + { + parseContext.declareBlock((yyvsp[-2].interm).loc, *(yyvsp[-2].interm).typeList, (yyvsp[-1].lex).string); + (yyval.interm.intermNode) = 0; + } +#line 5243 "MachineIndependent/glslang_tab.cpp" + break; + + case 99: +#line 885 "MachineIndependent/glslang.y" + { + parseContext.declareBlock((yyvsp[-3].interm).loc, *(yyvsp[-3].interm).typeList, (yyvsp[-2].lex).string, (yyvsp[-1].interm).arraySizes); + (yyval.interm.intermNode) = 0; + } +#line 5252 "MachineIndependent/glslang_tab.cpp" + break; + + case 100: +#line 889 "MachineIndependent/glslang.y" + { + parseContext.globalQualifierFixCheck((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier); + parseContext.updateStandaloneQualifierDefaults((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type)); + (yyval.interm.intermNode) = 0; + } +#line 5262 "MachineIndependent/glslang_tab.cpp" + break; + + case 101: +#line 894 "MachineIndependent/glslang.y" + { + parseContext.checkNoShaderLayouts((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).shaderQualifiers); + parseContext.addQualifierToExisting((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).qualifier, *(yyvsp[-1].lex).string); + (yyval.interm.intermNode) = 0; + } +#line 5272 "MachineIndependent/glslang_tab.cpp" + break; + + case 102: +#line 899 "MachineIndependent/glslang.y" + { + parseContext.checkNoShaderLayouts((yyvsp[-3].interm.type).loc, (yyvsp[-3].interm.type).shaderQualifiers); + (yyvsp[-1].interm.identifierList)->push_back((yyvsp[-2].lex).string); + parseContext.addQualifierToExisting((yyvsp[-3].interm.type).loc, (yyvsp[-3].interm.type).qualifier, *(yyvsp[-1].interm.identifierList)); + (yyval.interm.intermNode) = 0; + } +#line 5283 "MachineIndependent/glslang_tab.cpp" + break; + + case 103: +#line 908 "MachineIndependent/glslang.y" + { parseContext.nestedBlockCheck((yyvsp[-2].interm.type).loc); } +#line 5289 "MachineIndependent/glslang_tab.cpp" + break; + + case 104: +#line 908 "MachineIndependent/glslang.y" + { + --parseContext.structNestingLevel; + parseContext.blockName = (yyvsp[-4].lex).string; + parseContext.globalQualifierFixCheck((yyvsp[-5].interm.type).loc, (yyvsp[-5].interm.type).qualifier); + parseContext.checkNoShaderLayouts((yyvsp[-5].interm.type).loc, (yyvsp[-5].interm.type).shaderQualifiers); + parseContext.currentBlockQualifier = (yyvsp[-5].interm.type).qualifier; + (yyval.interm).loc = (yyvsp[-5].interm.type).loc; + (yyval.interm).typeList = (yyvsp[-1].interm.typeList); + } +#line 5303 "MachineIndependent/glslang_tab.cpp" + break; + + case 105: +#line 919 "MachineIndependent/glslang.y" + { + (yyval.interm.identifierList) = new TIdentifierList; + (yyval.interm.identifierList)->push_back((yyvsp[0].lex).string); + } +#line 5312 "MachineIndependent/glslang_tab.cpp" + break; + + case 106: +#line 923 "MachineIndependent/glslang.y" + { + (yyval.interm.identifierList) = (yyvsp[-2].interm.identifierList); + (yyval.interm.identifierList)->push_back((yyvsp[0].lex).string); + } +#line 5321 "MachineIndependent/glslang_tab.cpp" + break; + + case 107: +#line 930 "MachineIndependent/glslang.y" + { + (yyval.interm).function = (yyvsp[-1].interm.function); + (yyval.interm).loc = (yyvsp[0].lex).loc; + } +#line 5330 "MachineIndependent/glslang_tab.cpp" + break; + + case 108: +#line 937 "MachineIndependent/glslang.y" + { + (yyval.interm.function) = (yyvsp[0].interm.function); + } +#line 5338 "MachineIndependent/glslang_tab.cpp" + break; + + case 109: +#line 940 "MachineIndependent/glslang.y" + { + (yyval.interm.function) = (yyvsp[0].interm.function); + } +#line 5346 "MachineIndependent/glslang_tab.cpp" + break; + + case 110: +#line 947 "MachineIndependent/glslang.y" + { + // Add the parameter + (yyval.interm.function) = (yyvsp[-1].interm.function); + if ((yyvsp[0].interm).param.type->getBasicType() != EbtVoid) + (yyvsp[-1].interm.function)->addParameter((yyvsp[0].interm).param); + else + delete (yyvsp[0].interm).param.type; + } +#line 5359 "MachineIndependent/glslang_tab.cpp" + break; + + case 111: +#line 955 "MachineIndependent/glslang.y" + { + // + // Only first parameter of one-parameter functions can be void + // The check for named parameters not being void is done in parameter_declarator + // + if ((yyvsp[0].interm).param.type->getBasicType() == EbtVoid) { + // + // This parameter > first is void + // + parseContext.error((yyvsp[-1].lex).loc, "cannot be an argument type except for '(void)'", "void", ""); + delete (yyvsp[0].interm).param.type; + } else { + // Add the parameter + (yyval.interm.function) = (yyvsp[-2].interm.function); + (yyvsp[-2].interm.function)->addParameter((yyvsp[0].interm).param); + } + } +#line 5381 "MachineIndependent/glslang_tab.cpp" + break; + + case 112: +#line 975 "MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.type).qualifier.storage != EvqGlobal && (yyvsp[-2].interm.type).qualifier.storage != EvqTemporary) { + parseContext.error((yyvsp[-1].lex).loc, "no qualifiers allowed for function return", + GetStorageQualifierString((yyvsp[-2].interm.type).qualifier.storage), ""); + } + if ((yyvsp[-2].interm.type).arraySizes) + parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes); + + // Add the function as a prototype after parsing it (we do not support recursion) + TFunction *function; + TType type((yyvsp[-2].interm.type)); + + // Potentially rename shader entry point function. No-op most of the time. + parseContext.renameShaderFunction((yyvsp[-1].lex).string); + + // Make the function + function = new TFunction((yyvsp[-1].lex).string, type); + (yyval.interm.function) = function; + } +#line 5405 "MachineIndependent/glslang_tab.cpp" + break; + + case 113: +#line 998 "MachineIndependent/glslang.y" + { + if ((yyvsp[-1].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[-1].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[-1].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck((yyvsp[-1].interm.type).loc, *(yyvsp[-1].interm.type).arraySizes); + } + if ((yyvsp[-1].interm.type).basicType == EbtVoid) { + parseContext.error((yyvsp[0].lex).loc, "illegal use of type 'void'", (yyvsp[0].lex).string->c_str(), ""); + } + parseContext.reservedErrorCheck((yyvsp[0].lex).loc, *(yyvsp[0].lex).string); + + TParameter param = {(yyvsp[0].lex).string, new TType((yyvsp[-1].interm.type))}; + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).param = param; + } +#line 5425 "MachineIndependent/glslang_tab.cpp" + break; + + case 114: +#line 1013 "MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[-2].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[-2].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes); + } + TType* type = new TType((yyvsp[-2].interm.type)); + type->transferArraySizes((yyvsp[0].interm).arraySizes); + type->copyArrayInnerSizes((yyvsp[-2].interm.type).arraySizes); + + parseContext.arrayOfArrayVersionCheck((yyvsp[-1].lex).loc, type->getArraySizes()); + parseContext.arraySizeRequiredCheck((yyvsp[0].interm).loc, *(yyvsp[0].interm).arraySizes); + parseContext.reservedErrorCheck((yyvsp[-1].lex).loc, *(yyvsp[-1].lex).string); + + TParameter param = { (yyvsp[-1].lex).string, type }; + + (yyval.interm).loc = (yyvsp[-1].lex).loc; + (yyval.interm).param = param; + } +#line 5449 "MachineIndependent/glslang_tab.cpp" + break; + + case 115: +#line 1038 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + if ((yyvsp[-1].interm.type).qualifier.precision != EpqNone) + (yyval.interm).param.type->getQualifier().precision = (yyvsp[-1].interm.type).qualifier.precision; + parseContext.precisionQualifierCheck((yyval.interm).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier()); + + parseContext.checkNoShaderLayouts((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).shaderQualifiers); + parseContext.parameterTypeCheck((yyvsp[0].interm).loc, (yyvsp[-1].interm.type).qualifier.storage, *(yyval.interm).param.type); + parseContext.paramCheckFix((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier, *(yyval.interm).param.type); + + } +#line 5465 "MachineIndependent/glslang_tab.cpp" + break; + + case 116: +#line 1049 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + + parseContext.parameterTypeCheck((yyvsp[0].interm).loc, EvqIn, *(yyvsp[0].interm).param.type); + parseContext.paramCheckFixStorage((yyvsp[0].interm).loc, EvqTemporary, *(yyval.interm).param.type); + parseContext.precisionQualifierCheck((yyval.interm).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier()); + } +#line 5477 "MachineIndependent/glslang_tab.cpp" + break; + + case 117: +#line 1059 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + if ((yyvsp[-1].interm.type).qualifier.precision != EpqNone) + (yyval.interm).param.type->getQualifier().precision = (yyvsp[-1].interm.type).qualifier.precision; + parseContext.precisionQualifierCheck((yyvsp[-1].interm.type).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier()); + + parseContext.checkNoShaderLayouts((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).shaderQualifiers); + parseContext.parameterTypeCheck((yyvsp[0].interm).loc, (yyvsp[-1].interm.type).qualifier.storage, *(yyval.interm).param.type); + parseContext.paramCheckFix((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier, *(yyval.interm).param.type); + } +#line 5492 "MachineIndependent/glslang_tab.cpp" + break; + + case 118: +#line 1069 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + + parseContext.parameterTypeCheck((yyvsp[0].interm).loc, EvqIn, *(yyvsp[0].interm).param.type); + parseContext.paramCheckFixStorage((yyvsp[0].interm).loc, EvqTemporary, *(yyval.interm).param.type); + parseContext.precisionQualifierCheck((yyval.interm).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier()); + } +#line 5504 "MachineIndependent/glslang_tab.cpp" + break; + + case 119: +#line 1079 "MachineIndependent/glslang.y" + { + TParameter param = { 0, new TType((yyvsp[0].interm.type)) }; + (yyval.interm).param = param; + if ((yyvsp[0].interm.type).arraySizes) + parseContext.arraySizeRequiredCheck((yyvsp[0].interm.type).loc, *(yyvsp[0].interm.type).arraySizes); + } +#line 5515 "MachineIndependent/glslang_tab.cpp" + break; + + case 120: +#line 1088 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + } +#line 5523 "MachineIndependent/glslang_tab.cpp" + break; + + case 121: +#line 1091 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-2].interm); + parseContext.declareVariable((yyvsp[0].lex).loc, *(yyvsp[0].lex).string, (yyvsp[-2].interm).type); + } +#line 5532 "MachineIndependent/glslang_tab.cpp" + break; + + case 122: +#line 1095 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-3].interm); + parseContext.declareVariable((yyvsp[-1].lex).loc, *(yyvsp[-1].lex).string, (yyvsp[-3].interm).type, (yyvsp[0].interm).arraySizes); + } +#line 5541 "MachineIndependent/glslang_tab.cpp" + break; + + case 123: +#line 1099 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-5].interm).type; + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-3].lex).loc, *(yyvsp[-3].lex).string, (yyvsp[-5].interm).type, (yyvsp[-2].interm).arraySizes, (yyvsp[0].interm.intermTypedNode)); + (yyval.interm).intermNode = parseContext.intermediate.growAggregate((yyvsp[-5].interm).intermNode, initNode, (yyvsp[-1].lex).loc); + } +#line 5551 "MachineIndependent/glslang_tab.cpp" + break; + + case 124: +#line 1104 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-4].interm).type; + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-2].lex).loc, *(yyvsp[-2].lex).string, (yyvsp[-4].interm).type, 0, (yyvsp[0].interm.intermTypedNode)); + (yyval.interm).intermNode = parseContext.intermediate.growAggregate((yyvsp[-4].interm).intermNode, initNode, (yyvsp[-1].lex).loc); + } +#line 5561 "MachineIndependent/glslang_tab.cpp" + break; + + case 125: +#line 1112 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[0].interm.type); + (yyval.interm).intermNode = 0; + + parseContext.declareTypeDefaults((yyval.interm).loc, (yyval.interm).type); + + } +#line 5573 "MachineIndependent/glslang_tab.cpp" + break; + + case 126: +#line 1119 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-1].interm.type); + (yyval.interm).intermNode = 0; + parseContext.declareVariable((yyvsp[0].lex).loc, *(yyvsp[0].lex).string, (yyvsp[-1].interm.type)); + } +#line 5583 "MachineIndependent/glslang_tab.cpp" + break; + + case 127: +#line 1124 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-2].interm.type); + (yyval.interm).intermNode = 0; + parseContext.declareVariable((yyvsp[-1].lex).loc, *(yyvsp[-1].lex).string, (yyvsp[-2].interm.type), (yyvsp[0].interm).arraySizes); + } +#line 5593 "MachineIndependent/glslang_tab.cpp" + break; + + case 128: +#line 1129 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-4].interm.type); + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-3].lex).loc, *(yyvsp[-3].lex).string, (yyvsp[-4].interm.type), (yyvsp[-2].interm).arraySizes, (yyvsp[0].interm.intermTypedNode)); + (yyval.interm).intermNode = parseContext.intermediate.growAggregate(0, initNode, (yyvsp[-1].lex).loc); + } +#line 5603 "MachineIndependent/glslang_tab.cpp" + break; + + case 129: +#line 1134 "MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-3].interm.type); + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-2].lex).loc, *(yyvsp[-2].lex).string, (yyvsp[-3].interm.type), 0, (yyvsp[0].interm.intermTypedNode)); + (yyval.interm).intermNode = parseContext.intermediate.growAggregate(0, initNode, (yyvsp[-1].lex).loc); + } +#line 5613 "MachineIndependent/glslang_tab.cpp" + break; + + case 130: +#line 1143 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + + parseContext.globalQualifierTypeCheck((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).qualifier, (yyval.interm.type)); + if ((yyvsp[0].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[0].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[0].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + } + parseContext.precisionQualifierCheck((yyval.interm.type).loc, (yyval.interm.type).basicType, (yyval.interm.type).qualifier); + } +#line 5628 "MachineIndependent/glslang_tab.cpp" + break; + + case 131: +#line 1153 "MachineIndependent/glslang.y" + { + parseContext.globalQualifierFixCheck((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier); + parseContext.globalQualifierTypeCheck((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier, (yyvsp[0].interm.type)); + + if ((yyvsp[0].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[0].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[0].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + } + + if ((yyvsp[0].interm.type).arraySizes && parseContext.arrayQualifierError((yyvsp[0].interm.type).loc, (yyvsp[-1].interm.type).qualifier)) + (yyvsp[0].interm.type).arraySizes = nullptr; + + parseContext.checkNoShaderLayouts((yyvsp[0].interm.type).loc, (yyvsp[-1].interm.type).shaderQualifiers); + (yyvsp[0].interm.type).shaderQualifiers.merge((yyvsp[-1].interm.type).shaderQualifiers); + parseContext.mergeQualifiers((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).qualifier, (yyvsp[-1].interm.type).qualifier, true); + parseContext.precisionQualifierCheck((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).basicType, (yyvsp[0].interm.type).qualifier); + + (yyval.interm.type) = (yyvsp[0].interm.type); + + if (! (yyval.interm.type).qualifier.isInterpolation() && + ((parseContext.language == EShLangVertex && (yyval.interm.type).qualifier.storage == EvqVaryingOut) || + (parseContext.language == EShLangFragment && (yyval.interm.type).qualifier.storage == EvqVaryingIn))) + (yyval.interm.type).qualifier.smooth = true; + } +#line 5657 "MachineIndependent/glslang_tab.cpp" + break; + + case 132: +#line 1180 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "invariant"); + parseContext.profileRequires((yyval.interm.type).loc, ENoProfile, 120, 0, "invariant"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.invariant = true; + } +#line 5668 "MachineIndependent/glslang_tab.cpp" + break; + + case 133: +#line 1189 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "smooth"); + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "smooth"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 300, 0, "smooth"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.smooth = true; + } +#line 5680 "MachineIndependent/glslang_tab.cpp" + break; + + case 134: +#line 1196 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "flat"); + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "flat"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 300, 0, "flat"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.flat = true; + } +#line 5692 "MachineIndependent/glslang_tab.cpp" + break; + + case 135: +#line 1204 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "noperspective"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 0, E_GL_NV_shader_noperspective_interpolation, "noperspective"); + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "noperspective"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.nopersp = true; + } +#line 5704 "MachineIndependent/glslang_tab.cpp" + break; + + case 136: +#line 1211 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "__explicitInterpAMD"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECompatibilityProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.explicitInterp = true; + } +#line 5716 "MachineIndependent/glslang_tab.cpp" + break; + + case 137: +#line 1218 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "pervertexNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECompatibilityProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.pervertexNV = true; + } +#line 5729 "MachineIndependent/glslang_tab.cpp" + break; + + case 138: +#line 1226 "MachineIndependent/glslang.y" + { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck((yyvsp[0].lex).loc, "perprimitiveNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangFragmentMask | EShLangMeshNVMask), "perprimitiveNV"); + // Fragment shader stage doesn't check for extension. So we explicitly add below extension check. + if (parseContext.language == EShLangFragment) + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_NV_mesh_shader, "perprimitiveNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.perPrimitiveNV = true; + } +#line 5744 "MachineIndependent/glslang_tab.cpp" + break; + + case 139: +#line 1236 "MachineIndependent/glslang.y" + { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck((yyvsp[0].lex).loc, "perviewNV"); + parseContext.requireStage((yyvsp[0].lex).loc, EShLangMeshNV, "perviewNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.perViewNV = true; + } +#line 5756 "MachineIndependent/glslang_tab.cpp" + break; + + case 140: +#line 1243 "MachineIndependent/glslang.y" + { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck((yyvsp[0].lex).loc, "taskNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangTaskNVMask | EShLangMeshNVMask), "taskNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.perTaskNV = true; + } +#line 5768 "MachineIndependent/glslang_tab.cpp" + break; + + case 141: +#line 1254 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[-1].interm.type); + } +#line 5776 "MachineIndependent/glslang_tab.cpp" + break; + + case 142: +#line 1260 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 5784 "MachineIndependent/glslang_tab.cpp" + break; + + case 143: +#line 1263 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[-2].interm.type); + (yyval.interm.type).shaderQualifiers.merge((yyvsp[0].interm.type).shaderQualifiers); + parseContext.mergeObjectLayoutQualifiers((yyval.interm.type).qualifier, (yyvsp[0].interm.type).qualifier, false); + } +#line 5794 "MachineIndependent/glslang_tab.cpp" + break; + + case 144: +#line 1270 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.setLayoutQualifier((yyvsp[0].lex).loc, (yyval.interm.type), *(yyvsp[0].lex).string); + } +#line 5803 "MachineIndependent/glslang_tab.cpp" + break; + + case 145: +#line 1274 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-2].lex).loc); + parseContext.setLayoutQualifier((yyvsp[-2].lex).loc, (yyval.interm.type), *(yyvsp[-2].lex).string, (yyvsp[0].interm.intermTypedNode)); + } +#line 5812 "MachineIndependent/glslang_tab.cpp" + break; + + case 146: +#line 1278 "MachineIndependent/glslang.y" + { // because "shared" is both an identifier and a keyword + (yyval.interm.type).init((yyvsp[0].lex).loc); + TString strShared("shared"); + parseContext.setLayoutQualifier((yyvsp[0].lex).loc, (yyval.interm.type), strShared); + } +#line 5822 "MachineIndependent/glslang_tab.cpp" + break; + + case 147: +#line 1287 "MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyval.interm.type).loc, ECoreProfile | ECompatibilityProfile, 400, E_GL_ARB_gpu_shader5, "precise"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, "precise"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.noContraction = true; + } +#line 5833 "MachineIndependent/glslang_tab.cpp" + break; + + case 148: +#line 1297 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 5841 "MachineIndependent/glslang_tab.cpp" + break; + + case 149: +#line 1300 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[-1].interm.type); + if ((yyval.interm.type).basicType == EbtVoid) + (yyval.interm.type).basicType = (yyvsp[0].interm.type).basicType; + + (yyval.interm.type).shaderQualifiers.merge((yyvsp[0].interm.type).shaderQualifiers); + parseContext.mergeQualifiers((yyval.interm.type).loc, (yyval.interm.type).qualifier, (yyvsp[0].interm.type).qualifier, false); + } +#line 5854 "MachineIndependent/glslang_tab.cpp" + break; + + case 150: +#line 1311 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 5862 "MachineIndependent/glslang_tab.cpp" + break; + + case 151: +#line 1314 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 5870 "MachineIndependent/glslang_tab.cpp" + break; + + case 152: +#line 1317 "MachineIndependent/glslang.y" + { + parseContext.checkPrecisionQualifier((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).qualifier.precision); + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 5879 "MachineIndependent/glslang_tab.cpp" + break; + + case 153: +#line 1321 "MachineIndependent/glslang.y" + { + // allow inheritance of storage qualifier from block declaration + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 5888 "MachineIndependent/glslang_tab.cpp" + break; + + case 154: +#line 1325 "MachineIndependent/glslang.y" + { + // allow inheritance of storage qualifier from block declaration + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 5897 "MachineIndependent/glslang_tab.cpp" + break; + + case 155: +#line 1330 "MachineIndependent/glslang.y" + { + // allow inheritance of storage qualifier from block declaration + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 5906 "MachineIndependent/glslang_tab.cpp" + break; + + case 156: +#line 1334 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 5914 "MachineIndependent/glslang_tab.cpp" + break; + + case 157: +#line 1341 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqConst; // will later turn into EvqConstReadOnly, if the initializer is not constant + } +#line 5923 "MachineIndependent/glslang_tab.cpp" + break; + + case 158: +#line 1345 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "inout"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqInOut; + } +#line 5933 "MachineIndependent/glslang_tab.cpp" + break; + + case 159: +#line 1350 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "in"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + // whether this is a parameter "in" or a pipeline "in" will get sorted out a bit later + (yyval.interm.type).qualifier.storage = EvqIn; + } +#line 5944 "MachineIndependent/glslang_tab.cpp" + break; + + case 160: +#line 1356 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "out"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + // whether this is a parameter "out" or a pipeline "out" will get sorted out a bit later + (yyval.interm.type).qualifier.storage = EvqOut; + } +#line 5955 "MachineIndependent/glslang_tab.cpp" + break; + + case 161: +#line 1362 "MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 120, 0, "centroid"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 300, 0, "centroid"); + parseContext.globalCheck((yyvsp[0].lex).loc, "centroid"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.centroid = true; + } +#line 5967 "MachineIndependent/glslang_tab.cpp" + break; + + case 162: +#line 1369 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "uniform"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqUniform; + } +#line 5977 "MachineIndependent/glslang_tab.cpp" + break; + + case 163: +#line 1374 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "shared"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_compute_shader, "shared"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 310, 0, "shared"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangComputeMask | EShLangMeshNVMask | EShLangTaskNVMask), "shared"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqShared; + } +#line 5990 "MachineIndependent/glslang_tab.cpp" + break; + + case 164: +#line 1382 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "buffer"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqBuffer; + } +#line 6000 "MachineIndependent/glslang_tab.cpp" + break; + + case 165: +#line 1388 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangVertex, "attribute"); + parseContext.checkDeprecated((yyvsp[0].lex).loc, ECoreProfile, 130, "attribute"); + parseContext.checkDeprecated((yyvsp[0].lex).loc, ENoProfile, 130, "attribute"); + parseContext.requireNotRemoved((yyvsp[0].lex).loc, ECoreProfile, 420, "attribute"); + parseContext.requireNotRemoved((yyvsp[0].lex).loc, EEsProfile, 300, "attribute"); + + parseContext.globalCheck((yyvsp[0].lex).loc, "attribute"); + + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqVaryingIn; + } +#line 6017 "MachineIndependent/glslang_tab.cpp" + break; + + case 166: +#line 1400 "MachineIndependent/glslang.y" + { + parseContext.checkDeprecated((yyvsp[0].lex).loc, ENoProfile, 130, "varying"); + parseContext.checkDeprecated((yyvsp[0].lex).loc, ECoreProfile, 130, "varying"); + parseContext.requireNotRemoved((yyvsp[0].lex).loc, ECoreProfile, 420, "varying"); + parseContext.requireNotRemoved((yyvsp[0].lex).loc, EEsProfile, 300, "varying"); + + parseContext.globalCheck((yyvsp[0].lex).loc, "varying"); + + (yyval.interm.type).init((yyvsp[0].lex).loc); + if (parseContext.language == EShLangVertex) + (yyval.interm.type).qualifier.storage = EvqVaryingOut; + else + (yyval.interm.type).qualifier.storage = EvqVaryingIn; + } +#line 6036 "MachineIndependent/glslang_tab.cpp" + break; + + case 167: +#line 1414 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "patch"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangTessControlMask | EShLangTessEvaluationMask), "patch"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.patch = true; + } +#line 6047 "MachineIndependent/glslang_tab.cpp" + break; + + case 168: +#line 1420 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "sample"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.sample = true; + } +#line 6057 "MachineIndependent/glslang_tab.cpp" + break; + + case 169: +#line 1425 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "hitAttributeNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "hitAttributeNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqHitAttr; + } +#line 6070 "MachineIndependent/glslang_tab.cpp" + break; + + case 170: +#line 1433 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "hitAttributeEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "hitAttributeNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqHitAttr; + } +#line 6083 "MachineIndependent/glslang_tab.cpp" + break; + + case 171: +#line 1441 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqPayload; + } +#line 6096 "MachineIndependent/glslang_tab.cpp" + break; + + case 172: +#line 1449 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqPayload; + } +#line 6109 "MachineIndependent/glslang_tab.cpp" + break; + + case 173: +#line 1457 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadInNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadInNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqPayloadIn; + } +#line 6122 "MachineIndependent/glslang_tab.cpp" + break; + + case 174: +#line 1465 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadInEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadInEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqPayloadIn; + } +#line 6135 "MachineIndependent/glslang_tab.cpp" + break; + + case 175: +#line 1473 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqCallableData; + } +#line 6148 "MachineIndependent/glslang_tab.cpp" + break; + + case 176: +#line 1481 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqCallableData; + } +#line 6161 "MachineIndependent/glslang_tab.cpp" + break; + + case 177: +#line 1489 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataInNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataInNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqCallableDataIn; + } +#line 6173 "MachineIndependent/glslang_tab.cpp" + break; + + case 178: +#line 1496 "MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataInEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataInEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqCallableDataIn; + } +#line 6185 "MachineIndependent/glslang_tab.cpp" + break; + + case 179: +#line 1503 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.coherent = true; + } +#line 6194 "MachineIndependent/glslang_tab.cpp" + break; + + case 180: +#line 1507 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "devicecoherent"); + (yyval.interm.type).qualifier.devicecoherent = true; + } +#line 6204 "MachineIndependent/glslang_tab.cpp" + break; + + case 181: +#line 1512 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "queuefamilycoherent"); + (yyval.interm.type).qualifier.queuefamilycoherent = true; + } +#line 6214 "MachineIndependent/glslang_tab.cpp" + break; + + case 182: +#line 1517 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "workgroupcoherent"); + (yyval.interm.type).qualifier.workgroupcoherent = true; + } +#line 6224 "MachineIndependent/glslang_tab.cpp" + break; + + case 183: +#line 1522 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "subgroupcoherent"); + (yyval.interm.type).qualifier.subgroupcoherent = true; + } +#line 6234 "MachineIndependent/glslang_tab.cpp" + break; + + case 184: +#line 1527 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "nonprivate"); + (yyval.interm.type).qualifier.nonprivate = true; + } +#line 6244 "MachineIndependent/glslang_tab.cpp" + break; + + case 185: +#line 1532 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_EXT_ray_tracing, "shadercallcoherent"); + (yyval.interm.type).qualifier.shadercallcoherent = true; + } +#line 6254 "MachineIndependent/glslang_tab.cpp" + break; + + case 186: +#line 1537 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.volatil = true; + } +#line 6263 "MachineIndependent/glslang_tab.cpp" + break; + + case 187: +#line 1541 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.restrict = true; + } +#line 6272 "MachineIndependent/glslang_tab.cpp" + break; + + case 188: +#line 1545 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.readonly = true; + } +#line 6281 "MachineIndependent/glslang_tab.cpp" + break; + + case 189: +#line 1549 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.writeonly = true; + } +#line 6290 "MachineIndependent/glslang_tab.cpp" + break; + + case 190: +#line 1553 "MachineIndependent/glslang.y" + { + parseContext.spvRemoved((yyvsp[0].lex).loc, "subroutine"); + parseContext.globalCheck((yyvsp[0].lex).loc, "subroutine"); + parseContext.unimplemented((yyvsp[0].lex).loc, "subroutine"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + } +#line 6301 "MachineIndependent/glslang_tab.cpp" + break; + + case 191: +#line 1559 "MachineIndependent/glslang.y" + { + parseContext.spvRemoved((yyvsp[-3].lex).loc, "subroutine"); + parseContext.globalCheck((yyvsp[-3].lex).loc, "subroutine"); + parseContext.unimplemented((yyvsp[-3].lex).loc, "subroutine"); + (yyval.interm.type).init((yyvsp[-3].lex).loc); + } +#line 6312 "MachineIndependent/glslang_tab.cpp" + break; + + case 192: +#line 1570 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.nonUniform = true; + } +#line 6321 "MachineIndependent/glslang_tab.cpp" + break; + + case 193: +#line 1577 "MachineIndependent/glslang.y" + { + // TODO + } +#line 6329 "MachineIndependent/glslang_tab.cpp" + break; + + case 194: +#line 1580 "MachineIndependent/glslang.y" + { + // TODO: 4.0 semantics: subroutines + // 1) make sure each identifier is a type declared earlier with SUBROUTINE + // 2) save all of the identifiers for future comparison with the declared function + } +#line 6339 "MachineIndependent/glslang_tab.cpp" + break; + + case 195: +#line 1589 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[-1].interm.type); + (yyval.interm.type).qualifier.precision = parseContext.getDefaultPrecision((yyval.interm.type)); + (yyval.interm.type).typeParameters = (yyvsp[0].interm.typeParameters); + } +#line 6349 "MachineIndependent/glslang_tab.cpp" + break; + + case 196: +#line 1594 "MachineIndependent/glslang.y" + { + parseContext.arrayOfArrayVersionCheck((yyvsp[0].interm).loc, (yyvsp[0].interm).arraySizes); + (yyval.interm.type) = (yyvsp[-2].interm.type); + (yyval.interm.type).qualifier.precision = parseContext.getDefaultPrecision((yyval.interm.type)); + (yyval.interm.type).typeParameters = (yyvsp[-1].interm.typeParameters); + (yyval.interm.type).arraySizes = (yyvsp[0].interm).arraySizes; + } +#line 6361 "MachineIndependent/glslang_tab.cpp" + break; + + case 197: +#line 1604 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[-1].lex).loc; + (yyval.interm).arraySizes = new TArraySizes; + (yyval.interm).arraySizes->addInnerSize(); + } +#line 6371 "MachineIndependent/glslang_tab.cpp" + break; + + case 198: +#line 1609 "MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[-2].lex).loc; + (yyval.interm).arraySizes = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck((yyvsp[-1].interm.intermTypedNode)->getLoc(), (yyvsp[-1].interm.intermTypedNode), size, "array size"); + (yyval.interm).arraySizes->addInnerSize(size); + } +#line 6384 "MachineIndependent/glslang_tab.cpp" + break; + + case 199: +#line 1617 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-2].interm); + (yyval.interm).arraySizes->addInnerSize(); + } +#line 6393 "MachineIndependent/glslang_tab.cpp" + break; + + case 200: +#line 1621 "MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-3].interm); + + TArraySize size; + parseContext.arraySizeCheck((yyvsp[-1].interm.intermTypedNode)->getLoc(), (yyvsp[-1].interm.intermTypedNode), size, "array size"); + (yyval.interm).arraySizes->addInnerSize(size); + } +#line 6405 "MachineIndependent/glslang_tab.cpp" + break; + + case 201: +#line 1631 "MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = (yyvsp[0].interm.typeParameters); + } +#line 6413 "MachineIndependent/glslang_tab.cpp" + break; + + case 202: +#line 1634 "MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = 0; + } +#line 6421 "MachineIndependent/glslang_tab.cpp" + break; + + case 203: +#line 1640 "MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = (yyvsp[-1].interm.typeParameters); + } +#line 6429 "MachineIndependent/glslang_tab.cpp" + break; + + case 204: +#line 1646 "MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck((yyvsp[0].interm.intermTypedNode)->getLoc(), (yyvsp[0].interm.intermTypedNode), size, "type parameter"); + (yyval.interm.typeParameters)->addInnerSize(size); + } +#line 6441 "MachineIndependent/glslang_tab.cpp" + break; + + case 205: +#line 1653 "MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = (yyvsp[-2].interm.typeParameters); + + TArraySize size; + parseContext.arraySizeCheck((yyvsp[0].interm.intermTypedNode)->getLoc(), (yyvsp[0].interm.intermTypedNode), size, "type parameter"); + (yyval.interm.typeParameters)->addInnerSize(size); + } +#line 6453 "MachineIndependent/glslang_tab.cpp" + break; + + case 206: +#line 1663 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtVoid; + } +#line 6462 "MachineIndependent/glslang_tab.cpp" + break; + + case 207: +#line 1667 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + } +#line 6471 "MachineIndependent/glslang_tab.cpp" + break; + + case 208: +#line 1671 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + } +#line 6480 "MachineIndependent/glslang_tab.cpp" + break; + + case 209: +#line 1675 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + } +#line 6490 "MachineIndependent/glslang_tab.cpp" + break; + + case 210: +#line 1680 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBool; + } +#line 6499 "MachineIndependent/glslang_tab.cpp" + break; + + case 211: +#line 1684 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(2); + } +#line 6509 "MachineIndependent/glslang_tab.cpp" + break; + + case 212: +#line 1689 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(3); + } +#line 6519 "MachineIndependent/glslang_tab.cpp" + break; + + case 213: +#line 1694 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(4); + } +#line 6529 "MachineIndependent/glslang_tab.cpp" + break; + + case 214: +#line 1699 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBool; + (yyval.interm.type).setVector(2); + } +#line 6539 "MachineIndependent/glslang_tab.cpp" + break; + + case 215: +#line 1704 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBool; + (yyval.interm.type).setVector(3); + } +#line 6549 "MachineIndependent/glslang_tab.cpp" + break; + + case 216: +#line 1709 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBool; + (yyval.interm.type).setVector(4); + } +#line 6559 "MachineIndependent/glslang_tab.cpp" + break; + + case 217: +#line 1714 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(2); + } +#line 6569 "MachineIndependent/glslang_tab.cpp" + break; + + case 218: +#line 1719 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(3); + } +#line 6579 "MachineIndependent/glslang_tab.cpp" + break; + + case 219: +#line 1724 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(4); + } +#line 6589 "MachineIndependent/glslang_tab.cpp" + break; + + case 220: +#line 1729 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(2); + } +#line 6600 "MachineIndependent/glslang_tab.cpp" + break; + + case 221: +#line 1735 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(3); + } +#line 6611 "MachineIndependent/glslang_tab.cpp" + break; + + case 222: +#line 1741 "MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(4); + } +#line 6622 "MachineIndependent/glslang_tab.cpp" + break; + + case 223: +#line 1747 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 2); + } +#line 6632 "MachineIndependent/glslang_tab.cpp" + break; + + case 224: +#line 1752 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 3); + } +#line 6642 "MachineIndependent/glslang_tab.cpp" + break; + + case 225: +#line 1757 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 4); + } +#line 6652 "MachineIndependent/glslang_tab.cpp" + break; + + case 226: +#line 1762 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 2); + } +#line 6662 "MachineIndependent/glslang_tab.cpp" + break; + + case 227: +#line 1767 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 3); + } +#line 6672 "MachineIndependent/glslang_tab.cpp" + break; + + case 228: +#line 1772 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 4); + } +#line 6682 "MachineIndependent/glslang_tab.cpp" + break; + + case 229: +#line 1777 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 2); + } +#line 6692 "MachineIndependent/glslang_tab.cpp" + break; + + case 230: +#line 1782 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 3); + } +#line 6702 "MachineIndependent/glslang_tab.cpp" + break; + + case 231: +#line 1787 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 4); + } +#line 6712 "MachineIndependent/glslang_tab.cpp" + break; + + case 232: +#line 1792 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 2); + } +#line 6722 "MachineIndependent/glslang_tab.cpp" + break; + + case 233: +#line 1797 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 3); + } +#line 6732 "MachineIndependent/glslang_tab.cpp" + break; + + case 234: +#line 1802 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 4); + } +#line 6742 "MachineIndependent/glslang_tab.cpp" + break; + + case 235: +#line 1808 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + } +#line 6754 "MachineIndependent/glslang_tab.cpp" + break; + + case 236: +#line 1815 "MachineIndependent/glslang.y" + { + parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "float16_t", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + } +#line 6764 "MachineIndependent/glslang_tab.cpp" + break; + + case 237: +#line 1820 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + } +#line 6774 "MachineIndependent/glslang_tab.cpp" + break; + + case 238: +#line 1825 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + } +#line 6784 "MachineIndependent/glslang_tab.cpp" + break; + + case 239: +#line 1830 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt8; + } +#line 6794 "MachineIndependent/glslang_tab.cpp" + break; + + case 240: +#line 1835 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint8; + } +#line 6804 "MachineIndependent/glslang_tab.cpp" + break; + + case 241: +#line 1840 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt16; + } +#line 6814 "MachineIndependent/glslang_tab.cpp" + break; + + case 242: +#line 1845 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint16; + } +#line 6824 "MachineIndependent/glslang_tab.cpp" + break; + + case 243: +#line 1850 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + } +#line 6834 "MachineIndependent/glslang_tab.cpp" + break; + + case 244: +#line 1855 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + } +#line 6844 "MachineIndependent/glslang_tab.cpp" + break; + + case 245: +#line 1860 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt64; + } +#line 6854 "MachineIndependent/glslang_tab.cpp" + break; + + case 246: +#line 1865 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint64; + } +#line 6864 "MachineIndependent/glslang_tab.cpp" + break; + + case 247: +#line 1870 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(2); + } +#line 6877 "MachineIndependent/glslang_tab.cpp" + break; + + case 248: +#line 1878 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(3); + } +#line 6890 "MachineIndependent/glslang_tab.cpp" + break; + + case 249: +#line 1886 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(4); + } +#line 6903 "MachineIndependent/glslang_tab.cpp" + break; + + case 250: +#line 1894 "MachineIndependent/glslang.y" + { + parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setVector(2); + } +#line 6914 "MachineIndependent/glslang_tab.cpp" + break; + + case 251: +#line 1900 "MachineIndependent/glslang.y" + { + parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setVector(3); + } +#line 6925 "MachineIndependent/glslang_tab.cpp" + break; + + case 252: +#line 1906 "MachineIndependent/glslang.y" + { + parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setVector(4); + } +#line 6936 "MachineIndependent/glslang_tab.cpp" + break; + + case 253: +#line 1912 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(2); + } +#line 6947 "MachineIndependent/glslang_tab.cpp" + break; + + case 254: +#line 1918 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(3); + } +#line 6958 "MachineIndependent/glslang_tab.cpp" + break; + + case 255: +#line 1924 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(4); + } +#line 6969 "MachineIndependent/glslang_tab.cpp" + break; + + case 256: +#line 1930 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(2); + } +#line 6980 "MachineIndependent/glslang_tab.cpp" + break; + + case 257: +#line 1936 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(3); + } +#line 6991 "MachineIndependent/glslang_tab.cpp" + break; + + case 258: +#line 1942 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(4); + } +#line 7002 "MachineIndependent/glslang_tab.cpp" + break; + + case 259: +#line 1948 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt8; + (yyval.interm.type).setVector(2); + } +#line 7013 "MachineIndependent/glslang_tab.cpp" + break; + + case 260: +#line 1954 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt8; + (yyval.interm.type).setVector(3); + } +#line 7024 "MachineIndependent/glslang_tab.cpp" + break; + + case 261: +#line 1960 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt8; + (yyval.interm.type).setVector(4); + } +#line 7035 "MachineIndependent/glslang_tab.cpp" + break; + + case 262: +#line 1966 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt16; + (yyval.interm.type).setVector(2); + } +#line 7046 "MachineIndependent/glslang_tab.cpp" + break; + + case 263: +#line 1972 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt16; + (yyval.interm.type).setVector(3); + } +#line 7057 "MachineIndependent/glslang_tab.cpp" + break; + + case 264: +#line 1978 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt16; + (yyval.interm.type).setVector(4); + } +#line 7068 "MachineIndependent/glslang_tab.cpp" + break; + + case 265: +#line 1984 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(2); + } +#line 7079 "MachineIndependent/glslang_tab.cpp" + break; + + case 266: +#line 1990 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(3); + } +#line 7090 "MachineIndependent/glslang_tab.cpp" + break; + + case 267: +#line 1996 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(4); + } +#line 7101 "MachineIndependent/glslang_tab.cpp" + break; + + case 268: +#line 2002 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt64; + (yyval.interm.type).setVector(2); + } +#line 7112 "MachineIndependent/glslang_tab.cpp" + break; + + case 269: +#line 2008 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt64; + (yyval.interm.type).setVector(3); + } +#line 7123 "MachineIndependent/glslang_tab.cpp" + break; + + case 270: +#line 2014 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt64; + (yyval.interm.type).setVector(4); + } +#line 7134 "MachineIndependent/glslang_tab.cpp" + break; + + case 271: +#line 2020 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint8; + (yyval.interm.type).setVector(2); + } +#line 7145 "MachineIndependent/glslang_tab.cpp" + break; + + case 272: +#line 2026 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint8; + (yyval.interm.type).setVector(3); + } +#line 7156 "MachineIndependent/glslang_tab.cpp" + break; + + case 273: +#line 2032 "MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint8; + (yyval.interm.type).setVector(4); + } +#line 7167 "MachineIndependent/glslang_tab.cpp" + break; + + case 274: +#line 2038 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint16; + (yyval.interm.type).setVector(2); + } +#line 7178 "MachineIndependent/glslang_tab.cpp" + break; + + case 275: +#line 2044 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint16; + (yyval.interm.type).setVector(3); + } +#line 7189 "MachineIndependent/glslang_tab.cpp" + break; + + case 276: +#line 2050 "MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint16; + (yyval.interm.type).setVector(4); + } +#line 7200 "MachineIndependent/glslang_tab.cpp" + break; + + case 277: +#line 2056 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(2); + } +#line 7211 "MachineIndependent/glslang_tab.cpp" + break; + + case 278: +#line 2062 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(3); + } +#line 7222 "MachineIndependent/glslang_tab.cpp" + break; + + case 279: +#line 2068 "MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(4); + } +#line 7233 "MachineIndependent/glslang_tab.cpp" + break; + + case 280: +#line 2074 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint64; + (yyval.interm.type).setVector(2); + } +#line 7244 "MachineIndependent/glslang_tab.cpp" + break; + + case 281: +#line 2080 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint64; + (yyval.interm.type).setVector(3); + } +#line 7255 "MachineIndependent/glslang_tab.cpp" + break; + + case 282: +#line 2086 "MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint64; + (yyval.interm.type).setVector(4); + } +#line 7266 "MachineIndependent/glslang_tab.cpp" + break; + + case 283: +#line 2092 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7279 "MachineIndependent/glslang_tab.cpp" + break; + + case 284: +#line 2100 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7292 "MachineIndependent/glslang_tab.cpp" + break; + + case 285: +#line 2108 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7305 "MachineIndependent/glslang_tab.cpp" + break; + + case 286: +#line 2116 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7318 "MachineIndependent/glslang_tab.cpp" + break; + + case 287: +#line 2124 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 3); + } +#line 7331 "MachineIndependent/glslang_tab.cpp" + break; + + case 288: +#line 2132 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 4); + } +#line 7344 "MachineIndependent/glslang_tab.cpp" + break; + + case 289: +#line 2140 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 2); + } +#line 7357 "MachineIndependent/glslang_tab.cpp" + break; + + case 290: +#line 2148 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7370 "MachineIndependent/glslang_tab.cpp" + break; + + case 291: +#line 2156 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 4); + } +#line 7383 "MachineIndependent/glslang_tab.cpp" + break; + + case 292: +#line 2164 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 2); + } +#line 7396 "MachineIndependent/glslang_tab.cpp" + break; + + case 293: +#line 2172 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 3); + } +#line 7409 "MachineIndependent/glslang_tab.cpp" + break; + + case 294: +#line 2180 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7422 "MachineIndependent/glslang_tab.cpp" + break; + + case 295: +#line 2188 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7433 "MachineIndependent/glslang_tab.cpp" + break; + + case 296: +#line 2194 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7444 "MachineIndependent/glslang_tab.cpp" + break; + + case 297: +#line 2200 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7455 "MachineIndependent/glslang_tab.cpp" + break; + + case 298: +#line 2206 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7466 "MachineIndependent/glslang_tab.cpp" + break; + + case 299: +#line 2212 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(2, 3); + } +#line 7477 "MachineIndependent/glslang_tab.cpp" + break; + + case 300: +#line 2218 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(2, 4); + } +#line 7488 "MachineIndependent/glslang_tab.cpp" + break; + + case 301: +#line 2224 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(3, 2); + } +#line 7499 "MachineIndependent/glslang_tab.cpp" + break; + + case 302: +#line 2230 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7510 "MachineIndependent/glslang_tab.cpp" + break; + + case 303: +#line 2236 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(3, 4); + } +#line 7521 "MachineIndependent/glslang_tab.cpp" + break; + + case 304: +#line 2242 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(4, 2); + } +#line 7532 "MachineIndependent/glslang_tab.cpp" + break; + + case 305: +#line 2248 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(4, 3); + } +#line 7543 "MachineIndependent/glslang_tab.cpp" + break; + + case 306: +#line 2254 "MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7554 "MachineIndependent/glslang_tab.cpp" + break; + + case 307: +#line 2260 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7565 "MachineIndependent/glslang_tab.cpp" + break; + + case 308: +#line 2266 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7576 "MachineIndependent/glslang_tab.cpp" + break; + + case 309: +#line 2272 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7587 "MachineIndependent/glslang_tab.cpp" + break; + + case 310: +#line 2278 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7598 "MachineIndependent/glslang_tab.cpp" + break; + + case 311: +#line 2284 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 3); + } +#line 7609 "MachineIndependent/glslang_tab.cpp" + break; + + case 312: +#line 2290 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 4); + } +#line 7620 "MachineIndependent/glslang_tab.cpp" + break; + + case 313: +#line 2296 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 2); + } +#line 7631 "MachineIndependent/glslang_tab.cpp" + break; + + case 314: +#line 2302 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7642 "MachineIndependent/glslang_tab.cpp" + break; + + case 315: +#line 2308 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 4); + } +#line 7653 "MachineIndependent/glslang_tab.cpp" + break; + + case 316: +#line 2314 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 2); + } +#line 7664 "MachineIndependent/glslang_tab.cpp" + break; + + case 317: +#line 2320 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 3); + } +#line 7675 "MachineIndependent/glslang_tab.cpp" + break; + + case 318: +#line 2326 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7686 "MachineIndependent/glslang_tab.cpp" + break; + + case 319: +#line 2332 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7697 "MachineIndependent/glslang_tab.cpp" + break; + + case 320: +#line 2338 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7708 "MachineIndependent/glslang_tab.cpp" + break; + + case 321: +#line 2344 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7719 "MachineIndependent/glslang_tab.cpp" + break; + + case 322: +#line 2350 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7730 "MachineIndependent/glslang_tab.cpp" + break; + + case 323: +#line 2356 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 3); + } +#line 7741 "MachineIndependent/glslang_tab.cpp" + break; + + case 324: +#line 2362 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 4); + } +#line 7752 "MachineIndependent/glslang_tab.cpp" + break; + + case 325: +#line 2368 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 2); + } +#line 7763 "MachineIndependent/glslang_tab.cpp" + break; + + case 326: +#line 2374 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7774 "MachineIndependent/glslang_tab.cpp" + break; + + case 327: +#line 2380 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 4); + } +#line 7785 "MachineIndependent/glslang_tab.cpp" + break; + + case 328: +#line 2386 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 2); + } +#line 7796 "MachineIndependent/glslang_tab.cpp" + break; + + case 329: +#line 2392 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 3); + } +#line 7807 "MachineIndependent/glslang_tab.cpp" + break; + + case 330: +#line 2398 "MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7818 "MachineIndependent/glslang_tab.cpp" + break; + + case 331: +#line 2404 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtAccStruct; + } +#line 7827 "MachineIndependent/glslang_tab.cpp" + break; + + case 332: +#line 2408 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtAccStruct; + } +#line 7836 "MachineIndependent/glslang_tab.cpp" + break; + + case 333: +#line 2412 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtRayQuery; + } +#line 7845 "MachineIndependent/glslang_tab.cpp" + break; + + case 334: +#line 2416 "MachineIndependent/glslang.y" + { + parseContext.vulkanRemoved((yyvsp[0].lex).loc, "atomic counter types"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtAtomicUint; + } +#line 7855 "MachineIndependent/glslang_tab.cpp" + break; + + case 335: +#line 2421 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd1D); + } +#line 7865 "MachineIndependent/glslang_tab.cpp" + break; + + case 336: +#line 2427 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D); + } +#line 7875 "MachineIndependent/glslang_tab.cpp" + break; + + case 337: +#line 2432 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd3D); + } +#line 7885 "MachineIndependent/glslang_tab.cpp" + break; + + case 338: +#line 2437 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdCube); + } +#line 7895 "MachineIndependent/glslang_tab.cpp" + break; + + case 339: +#line 2442 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, false, true); + } +#line 7905 "MachineIndependent/glslang_tab.cpp" + break; + + case 340: +#line 2447 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdCube, false, true); + } +#line 7915 "MachineIndependent/glslang_tab.cpp" + break; + + case 341: +#line 2452 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, true); + } +#line 7925 "MachineIndependent/glslang_tab.cpp" + break; + + case 342: +#line 2457 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, true, true); + } +#line 7935 "MachineIndependent/glslang_tab.cpp" + break; + + case 343: +#line 2463 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd1D, false, true); + } +#line 7945 "MachineIndependent/glslang_tab.cpp" + break; + + case 344: +#line 2468 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd1D, true); + } +#line 7955 "MachineIndependent/glslang_tab.cpp" + break; + + case 345: +#line 2473 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd1D, true, true); + } +#line 7965 "MachineIndependent/glslang_tab.cpp" + break; + + case 346: +#line 2478 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdCube, true); + } +#line 7975 "MachineIndependent/glslang_tab.cpp" + break; + + case 347: +#line 2483 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdCube, true, true); + } +#line 7985 "MachineIndependent/glslang_tab.cpp" + break; + + case 348: +#line 2488 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd1D); + } +#line 7996 "MachineIndependent/glslang_tab.cpp" + break; + + case 349: +#line 2494 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D); + } +#line 8007 "MachineIndependent/glslang_tab.cpp" + break; + + case 350: +#line 2500 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd3D); + } +#line 8018 "MachineIndependent/glslang_tab.cpp" + break; + + case 351: +#line 2506 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdCube); + } +#line 8029 "MachineIndependent/glslang_tab.cpp" + break; + + case 352: +#line 2512 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd1D, false, true); + } +#line 8040 "MachineIndependent/glslang_tab.cpp" + break; + + case 353: +#line 2518 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, false, true); + } +#line 8051 "MachineIndependent/glslang_tab.cpp" + break; + + case 354: +#line 2524 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdCube, false, true); + } +#line 8062 "MachineIndependent/glslang_tab.cpp" + break; + + case 355: +#line 2530 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd1D, true); + } +#line 8073 "MachineIndependent/glslang_tab.cpp" + break; + + case 356: +#line 2536 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, true); + } +#line 8084 "MachineIndependent/glslang_tab.cpp" + break; + + case 357: +#line 2542 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd1D, true, true); + } +#line 8095 "MachineIndependent/glslang_tab.cpp" + break; + + case 358: +#line 2548 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, true, true); + } +#line 8106 "MachineIndependent/glslang_tab.cpp" + break; + + case 359: +#line 2554 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdCube, true); + } +#line 8117 "MachineIndependent/glslang_tab.cpp" + break; + + case 360: +#line 2560 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdCube, true, true); + } +#line 8128 "MachineIndependent/glslang_tab.cpp" + break; + + case 361: +#line 2566 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd1D); + } +#line 8138 "MachineIndependent/glslang_tab.cpp" + break; + + case 362: +#line 2572 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd2D); + } +#line 8148 "MachineIndependent/glslang_tab.cpp" + break; + + case 363: +#line 2577 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd3D); + } +#line 8158 "MachineIndependent/glslang_tab.cpp" + break; + + case 364: +#line 2582 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, EsdCube); + } +#line 8168 "MachineIndependent/glslang_tab.cpp" + break; + + case 365: +#line 2587 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd2D, true); + } +#line 8178 "MachineIndependent/glslang_tab.cpp" + break; + + case 366: +#line 2592 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd2D); + } +#line 8188 "MachineIndependent/glslang_tab.cpp" + break; + + case 367: +#line 2597 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd3D); + } +#line 8198 "MachineIndependent/glslang_tab.cpp" + break; + + case 368: +#line 2602 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, EsdCube); + } +#line 8208 "MachineIndependent/glslang_tab.cpp" + break; + + case 369: +#line 2608 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd1D, true); + } +#line 8218 "MachineIndependent/glslang_tab.cpp" + break; + + case 370: +#line 2613 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, EsdCube, true); + } +#line 8228 "MachineIndependent/glslang_tab.cpp" + break; + + case 371: +#line 2618 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd1D); + } +#line 8238 "MachineIndependent/glslang_tab.cpp" + break; + + case 372: +#line 2623 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd1D, true); + } +#line 8248 "MachineIndependent/glslang_tab.cpp" + break; + + case 373: +#line 2628 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, EsdCube, true); + } +#line 8258 "MachineIndependent/glslang_tab.cpp" + break; + + case 374: +#line 2633 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, EsdCube, true); + } +#line 8268 "MachineIndependent/glslang_tab.cpp" + break; + + case 375: +#line 2638 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, EsdCube, true); + } +#line 8278 "MachineIndependent/glslang_tab.cpp" + break; + + case 376: +#line 2643 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, EsdCube, true); + } +#line 8288 "MachineIndependent/glslang_tab.cpp" + break; + + case 377: +#line 2649 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd2D, true); + } +#line 8298 "MachineIndependent/glslang_tab.cpp" + break; + + case 378: +#line 2654 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D); + } +#line 8308 "MachineIndependent/glslang_tab.cpp" + break; + + case 379: +#line 2659 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd3D); + } +#line 8318 "MachineIndependent/glslang_tab.cpp" + break; + + case 380: +#line 2664 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D, true); + } +#line 8328 "MachineIndependent/glslang_tab.cpp" + break; + + case 381: +#line 2669 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, EsdCube); + } +#line 8338 "MachineIndependent/glslang_tab.cpp" + break; + + case 382: +#line 2674 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D); + } +#line 8348 "MachineIndependent/glslang_tab.cpp" + break; + + case 383: +#line 2679 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd3D); + } +#line 8358 "MachineIndependent/glslang_tab.cpp" + break; + + case 384: +#line 2684 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, EsdCube); + } +#line 8368 "MachineIndependent/glslang_tab.cpp" + break; + + case 385: +#line 2689 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D, true); + } +#line 8378 "MachineIndependent/glslang_tab.cpp" + break; + + case 386: +#line 2694 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D); + } +#line 8388 "MachineIndependent/glslang_tab.cpp" + break; + + case 387: +#line 2699 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd3D); + } +#line 8398 "MachineIndependent/glslang_tab.cpp" + break; + + case 388: +#line 2704 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, EsdCube); + } +#line 8408 "MachineIndependent/glslang_tab.cpp" + break; + + case 389: +#line 2709 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D, true); + } +#line 8418 "MachineIndependent/glslang_tab.cpp" + break; + + case 390: +#line 2714 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setPureSampler(false); + } +#line 8428 "MachineIndependent/glslang_tab.cpp" + break; + + case 391: +#line 2719 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setPureSampler(true); + } +#line 8438 "MachineIndependent/glslang_tab.cpp" + break; + + case 392: +#line 2725 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdRect); + } +#line 8448 "MachineIndependent/glslang_tab.cpp" + break; + + case 393: +#line 2730 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdRect, false, true); + } +#line 8458 "MachineIndependent/glslang_tab.cpp" + break; + + case 394: +#line 2735 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdRect); + } +#line 8469 "MachineIndependent/glslang_tab.cpp" + break; + + case 395: +#line 2741 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdRect, false, true); + } +#line 8480 "MachineIndependent/glslang_tab.cpp" + break; + + case 396: +#line 2747 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, EsdRect); + } +#line 8490 "MachineIndependent/glslang_tab.cpp" + break; + + case 397: +#line 2752 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, EsdRect); + } +#line 8500 "MachineIndependent/glslang_tab.cpp" + break; + + case 398: +#line 2757 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdBuffer); + } +#line 8510 "MachineIndependent/glslang_tab.cpp" + break; + + case 399: +#line 2762 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdBuffer); + } +#line 8521 "MachineIndependent/glslang_tab.cpp" + break; + + case 400: +#line 2768 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, EsdBuffer); + } +#line 8531 "MachineIndependent/glslang_tab.cpp" + break; + + case 401: +#line 2773 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, EsdBuffer); + } +#line 8541 "MachineIndependent/glslang_tab.cpp" + break; + + case 402: +#line 2778 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, false, false, true); + } +#line 8551 "MachineIndependent/glslang_tab.cpp" + break; + + case 403: +#line 2783 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, false, false, true); + } +#line 8562 "MachineIndependent/glslang_tab.cpp" + break; + + case 404: +#line 2789 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd2D, false, false, true); + } +#line 8572 "MachineIndependent/glslang_tab.cpp" + break; + + case 405: +#line 2794 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd2D, false, false, true); + } +#line 8582 "MachineIndependent/glslang_tab.cpp" + break; + + case 406: +#line 2799 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, true, false, true); + } +#line 8592 "MachineIndependent/glslang_tab.cpp" + break; + + case 407: +#line 2804 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, true, false, true); + } +#line 8603 "MachineIndependent/glslang_tab.cpp" + break; + + case 408: +#line 2810 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd2D, true, false, true); + } +#line 8613 "MachineIndependent/glslang_tab.cpp" + break; + + case 409: +#line 2815 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd2D, true, false, true); + } +#line 8623 "MachineIndependent/glslang_tab.cpp" + break; + + case 410: +#line 2820 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd1D); + } +#line 8633 "MachineIndependent/glslang_tab.cpp" + break; + + case 411: +#line 2825 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd1D); + } +#line 8644 "MachineIndependent/glslang_tab.cpp" + break; + + case 412: +#line 2831 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D); + } +#line 8655 "MachineIndependent/glslang_tab.cpp" + break; + + case 413: +#line 2837 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd3D); + } +#line 8666 "MachineIndependent/glslang_tab.cpp" + break; + + case 414: +#line 2843 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdCube); + } +#line 8677 "MachineIndependent/glslang_tab.cpp" + break; + + case 415: +#line 2849 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd1D, true); + } +#line 8687 "MachineIndependent/glslang_tab.cpp" + break; + + case 416: +#line 2854 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd1D, true); + } +#line 8698 "MachineIndependent/glslang_tab.cpp" + break; + + case 417: +#line 2860 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D, true); + } +#line 8709 "MachineIndependent/glslang_tab.cpp" + break; + + case 418: +#line 2866 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdCube, true); + } +#line 8720 "MachineIndependent/glslang_tab.cpp" + break; + + case 419: +#line 2872 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd1D); + } +#line 8730 "MachineIndependent/glslang_tab.cpp" + break; + + case 420: +#line 2877 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd1D, true); + } +#line 8740 "MachineIndependent/glslang_tab.cpp" + break; + + case 421: +#line 2882 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd1D); + } +#line 8750 "MachineIndependent/glslang_tab.cpp" + break; + + case 422: +#line 2887 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd1D, true); + } +#line 8760 "MachineIndependent/glslang_tab.cpp" + break; + + case 423: +#line 2892 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, EsdRect); + } +#line 8770 "MachineIndependent/glslang_tab.cpp" + break; + + case 424: +#line 2897 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdRect); + } +#line 8781 "MachineIndependent/glslang_tab.cpp" + break; + + case 425: +#line 2903 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, EsdRect); + } +#line 8791 "MachineIndependent/glslang_tab.cpp" + break; + + case 426: +#line 2908 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, EsdRect); + } +#line 8801 "MachineIndependent/glslang_tab.cpp" + break; + + case 427: +#line 2913 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, EsdBuffer); + } +#line 8811 "MachineIndependent/glslang_tab.cpp" + break; + + case 428: +#line 2918 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdBuffer); + } +#line 8822 "MachineIndependent/glslang_tab.cpp" + break; + + case 429: +#line 2924 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, EsdBuffer); + } +#line 8832 "MachineIndependent/glslang_tab.cpp" + break; + + case 430: +#line 2929 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, EsdBuffer); + } +#line 8842 "MachineIndependent/glslang_tab.cpp" + break; + + case 431: +#line 2934 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D, false, false, true); + } +#line 8852 "MachineIndependent/glslang_tab.cpp" + break; + + case 432: +#line 2939 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D, false, false, true); + } +#line 8863 "MachineIndependent/glslang_tab.cpp" + break; + + case 433: +#line 2945 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D, false, false, true); + } +#line 8873 "MachineIndependent/glslang_tab.cpp" + break; + + case 434: +#line 2950 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D, false, false, true); + } +#line 8883 "MachineIndependent/glslang_tab.cpp" + break; + + case 435: +#line 2955 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D, true, false, true); + } +#line 8893 "MachineIndependent/glslang_tab.cpp" + break; + + case 436: +#line 2960 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D, true, false, true); + } +#line 8904 "MachineIndependent/glslang_tab.cpp" + break; + + case 437: +#line 2966 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D, true, false, true); + } +#line 8914 "MachineIndependent/glslang_tab.cpp" + break; + + case 438: +#line 2971 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D, true, false, true); + } +#line 8924 "MachineIndependent/glslang_tab.cpp" + break; + + case 439: +#line 2976 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd1D); + } +#line 8934 "MachineIndependent/glslang_tab.cpp" + break; + + case 440: +#line 2981 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd1D); + } +#line 8945 "MachineIndependent/glslang_tab.cpp" + break; + + case 441: +#line 2987 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd1D); + } +#line 8955 "MachineIndependent/glslang_tab.cpp" + break; + + case 442: +#line 2992 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd1D); + } +#line 8965 "MachineIndependent/glslang_tab.cpp" + break; + + case 443: +#line 2997 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D); + } +#line 8975 "MachineIndependent/glslang_tab.cpp" + break; + + case 444: +#line 3002 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D); + } +#line 8986 "MachineIndependent/glslang_tab.cpp" + break; + + case 445: +#line 3008 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd2D); + } +#line 8996 "MachineIndependent/glslang_tab.cpp" + break; + + case 446: +#line 3013 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd2D); + } +#line 9006 "MachineIndependent/glslang_tab.cpp" + break; + + case 447: +#line 3018 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd3D); + } +#line 9016 "MachineIndependent/glslang_tab.cpp" + break; + + case 448: +#line 3023 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd3D); + } +#line 9027 "MachineIndependent/glslang_tab.cpp" + break; + + case 449: +#line 3029 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd3D); + } +#line 9037 "MachineIndependent/glslang_tab.cpp" + break; + + case 450: +#line 3034 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd3D); + } +#line 9047 "MachineIndependent/glslang_tab.cpp" + break; + + case 451: +#line 3039 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, EsdRect); + } +#line 9057 "MachineIndependent/glslang_tab.cpp" + break; + + case 452: +#line 3044 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, EsdRect); + } +#line 9068 "MachineIndependent/glslang_tab.cpp" + break; + + case 453: +#line 3050 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, EsdRect); + } +#line 9078 "MachineIndependent/glslang_tab.cpp" + break; + + case 454: +#line 3055 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, EsdRect); + } +#line 9088 "MachineIndependent/glslang_tab.cpp" + break; + + case 455: +#line 3060 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, EsdCube); + } +#line 9098 "MachineIndependent/glslang_tab.cpp" + break; + + case 456: +#line 3065 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, EsdCube); + } +#line 9109 "MachineIndependent/glslang_tab.cpp" + break; + + case 457: +#line 3071 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, EsdCube); + } +#line 9119 "MachineIndependent/glslang_tab.cpp" + break; + + case 458: +#line 3076 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, EsdCube); + } +#line 9129 "MachineIndependent/glslang_tab.cpp" + break; + + case 459: +#line 3081 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, EsdBuffer); + } +#line 9139 "MachineIndependent/glslang_tab.cpp" + break; + + case 460: +#line 3086 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, EsdBuffer); + } +#line 9150 "MachineIndependent/glslang_tab.cpp" + break; + + case 461: +#line 3092 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, EsdBuffer); + } +#line 9160 "MachineIndependent/glslang_tab.cpp" + break; + + case 462: +#line 3097 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, EsdBuffer); + } +#line 9170 "MachineIndependent/glslang_tab.cpp" + break; + + case 463: +#line 3102 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd1D, true); + } +#line 9180 "MachineIndependent/glslang_tab.cpp" + break; + + case 464: +#line 3107 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd1D, true); + } +#line 9191 "MachineIndependent/glslang_tab.cpp" + break; + + case 465: +#line 3113 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd1D, true); + } +#line 9201 "MachineIndependent/glslang_tab.cpp" + break; + + case 466: +#line 3118 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd1D, true); + } +#line 9211 "MachineIndependent/glslang_tab.cpp" + break; + + case 467: +#line 3123 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D, true); + } +#line 9221 "MachineIndependent/glslang_tab.cpp" + break; + + case 468: +#line 3128 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D, true); + } +#line 9232 "MachineIndependent/glslang_tab.cpp" + break; + + case 469: +#line 3134 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd2D, true); + } +#line 9242 "MachineIndependent/glslang_tab.cpp" + break; + + case 470: +#line 3139 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd2D, true); + } +#line 9252 "MachineIndependent/glslang_tab.cpp" + break; + + case 471: +#line 3144 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, EsdCube, true); + } +#line 9262 "MachineIndependent/glslang_tab.cpp" + break; + + case 472: +#line 3149 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, EsdCube, true); + } +#line 9273 "MachineIndependent/glslang_tab.cpp" + break; + + case 473: +#line 3155 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, EsdCube, true); + } +#line 9283 "MachineIndependent/glslang_tab.cpp" + break; + + case 474: +#line 3160 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, EsdCube, true); + } +#line 9293 "MachineIndependent/glslang_tab.cpp" + break; + + case 475: +#line 3165 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D, false, false, true); + } +#line 9303 "MachineIndependent/glslang_tab.cpp" + break; + + case 476: +#line 3170 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D, false, false, true); + } +#line 9314 "MachineIndependent/glslang_tab.cpp" + break; + + case 477: +#line 3176 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd2D, false, false, true); + } +#line 9324 "MachineIndependent/glslang_tab.cpp" + break; + + case 478: +#line 3181 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd2D, false, false, true); + } +#line 9334 "MachineIndependent/glslang_tab.cpp" + break; + + case 479: +#line 3186 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D, true, false, true); + } +#line 9344 "MachineIndependent/glslang_tab.cpp" + break; + + case 480: +#line 3191 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D, true, false, true); + } +#line 9355 "MachineIndependent/glslang_tab.cpp" + break; + + case 481: +#line 3197 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd2D, true, false, true); + } +#line 9365 "MachineIndependent/glslang_tab.cpp" + break; + + case 482: +#line 3202 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd2D, true, false, true); + } +#line 9375 "MachineIndependent/glslang_tab.cpp" + break; + + case 483: +#line 3207 "MachineIndependent/glslang.y" + { // GL_OES_EGL_image_external + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D); + (yyval.interm.type).sampler.external = true; + } +#line 9386 "MachineIndependent/glslang_tab.cpp" + break; + + case 484: +#line 3213 "MachineIndependent/glslang.y" + { // GL_EXT_YUV_target + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D); + (yyval.interm.type).sampler.yuv = true; + } +#line 9397 "MachineIndependent/glslang_tab.cpp" + break; + + case 485: +#line 3219 "MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D); + (yyval.interm.type).sampler.video = true; + } +#line 9408 "MachineIndependent/glslang_tab.cpp" + break; + + case 486: +#line 3225 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtFloat); + } +#line 9419 "MachineIndependent/glslang_tab.cpp" + break; + + case 487: +#line 3231 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtFloat, true); + } +#line 9430 "MachineIndependent/glslang_tab.cpp" + break; + + case 488: +#line 3237 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtFloat16); + } +#line 9442 "MachineIndependent/glslang_tab.cpp" + break; + + case 489: +#line 3244 "MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtFloat16, true); + } +#line 9454 "MachineIndependent/glslang_tab.cpp" + break; + + case 490: +#line 3251 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtInt); + } +#line 9465 "MachineIndependent/glslang_tab.cpp" + break; + + case 491: +#line 3257 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtInt, true); + } +#line 9476 "MachineIndependent/glslang_tab.cpp" + break; + + case 492: +#line 3263 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtUint); + } +#line 9487 "MachineIndependent/glslang_tab.cpp" + break; + + case 493: +#line 3269 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtUint, true); + } +#line 9498 "MachineIndependent/glslang_tab.cpp" + break; + + case 494: +#line 3275 "MachineIndependent/glslang.y" + { + parseContext.fcoopmatCheck((yyvsp[0].lex).loc, "fcoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).coopmat = true; + } +#line 9509 "MachineIndependent/glslang_tab.cpp" + break; + + case 495: +#line 3281 "MachineIndependent/glslang.y" + { + parseContext.intcoopmatCheck((yyvsp[0].lex).loc, "icoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).coopmat = true; + } +#line 9520 "MachineIndependent/glslang_tab.cpp" + break; + + case 496: +#line 3287 "MachineIndependent/glslang.y" + { + parseContext.intcoopmatCheck((yyvsp[0].lex).loc, "ucoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).coopmat = true; + } +#line 9531 "MachineIndependent/glslang_tab.cpp" + break; + + case 497: +#line 3294 "MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + (yyval.interm.type).qualifier.storage = parseContext.symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; + parseContext.structTypeCheck((yyval.interm.type).loc, (yyval.interm.type)); + } +#line 9541 "MachineIndependent/glslang_tab.cpp" + break; + + case 498: +#line 3299 "MachineIndependent/glslang.y" + { + // + // This is for user defined type names. The lexical phase looked up the + // type. + // + if (const TVariable* variable = ((yyvsp[0].lex).symbol)->getAsVariable()) { + const TType& structure = variable->getType(); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtStruct; + (yyval.interm.type).userDef = &structure; + } else + parseContext.error((yyvsp[0].lex).loc, "expected type name", (yyvsp[0].lex).string->c_str(), ""); + } +#line 9559 "MachineIndependent/glslang_tab.cpp" + break; + + case 499: +#line 3315 "MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "highp precision qualifier"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier((yyvsp[0].lex).loc, (yyval.interm.type).qualifier, EpqHigh); + } +#line 9569 "MachineIndependent/glslang_tab.cpp" + break; + + case 500: +#line 3320 "MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "mediump precision qualifier"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier((yyvsp[0].lex).loc, (yyval.interm.type).qualifier, EpqMedium); + } +#line 9579 "MachineIndependent/glslang_tab.cpp" + break; + + case 501: +#line 3325 "MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "lowp precision qualifier"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier((yyvsp[0].lex).loc, (yyval.interm.type).qualifier, EpqLow); + } +#line 9589 "MachineIndependent/glslang_tab.cpp" + break; + + case 502: +#line 3333 "MachineIndependent/glslang.y" + { parseContext.nestedStructCheck((yyvsp[-2].lex).loc); } +#line 9595 "MachineIndependent/glslang_tab.cpp" + break; + + case 503: +#line 3333 "MachineIndependent/glslang.y" + { + TType* structure = new TType((yyvsp[-1].interm.typeList), *(yyvsp[-4].lex).string); + parseContext.structArrayCheck((yyvsp[-4].lex).loc, *structure); + TVariable* userTypeDef = new TVariable((yyvsp[-4].lex).string, *structure, true); + if (! parseContext.symbolTable.insert(*userTypeDef)) + parseContext.error((yyvsp[-4].lex).loc, "redefinition", (yyvsp[-4].lex).string->c_str(), "struct"); + (yyval.interm.type).init((yyvsp[-5].lex).loc); + (yyval.interm.type).basicType = EbtStruct; + (yyval.interm.type).userDef = structure; + --parseContext.structNestingLevel; + } +#line 9611 "MachineIndependent/glslang_tab.cpp" + break; + + case 504: +#line 3344 "MachineIndependent/glslang.y" + { parseContext.nestedStructCheck((yyvsp[-1].lex).loc); } +#line 9617 "MachineIndependent/glslang_tab.cpp" + break; + + case 505: +#line 3344 "MachineIndependent/glslang.y" + { + TType* structure = new TType((yyvsp[-1].interm.typeList), TString("")); + (yyval.interm.type).init((yyvsp[-4].lex).loc); + (yyval.interm.type).basicType = EbtStruct; + (yyval.interm.type).userDef = structure; + --parseContext.structNestingLevel; + } +#line 9629 "MachineIndependent/glslang_tab.cpp" + break; + + case 506: +#line 3354 "MachineIndependent/glslang.y" + { + (yyval.interm.typeList) = (yyvsp[0].interm.typeList); + } +#line 9637 "MachineIndependent/glslang_tab.cpp" + break; + + case 507: +#line 3357 "MachineIndependent/glslang.y" + { + (yyval.interm.typeList) = (yyvsp[-1].interm.typeList); + for (unsigned int i = 0; i < (yyvsp[0].interm.typeList)->size(); ++i) { + for (unsigned int j = 0; j < (yyval.interm.typeList)->size(); ++j) { + if ((*(yyval.interm.typeList))[j].type->getFieldName() == (*(yyvsp[0].interm.typeList))[i].type->getFieldName()) + parseContext.error((*(yyvsp[0].interm.typeList))[i].loc, "duplicate member name:", "", (*(yyvsp[0].interm.typeList))[i].type->getFieldName().c_str()); + } + (yyval.interm.typeList)->push_back((*(yyvsp[0].interm.typeList))[i]); + } + } +#line 9652 "MachineIndependent/glslang_tab.cpp" + break; + + case 508: +#line 3370 "MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[-2].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[-2].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes); + } + + (yyval.interm.typeList) = (yyvsp[-1].interm.typeList); + + parseContext.voidErrorCheck((yyvsp[-2].interm.type).loc, (*(yyvsp[-1].interm.typeList))[0].type->getFieldName(), (yyvsp[-2].interm.type).basicType); + parseContext.precisionQualifierCheck((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).basicType, (yyvsp[-2].interm.type).qualifier); + + for (unsigned int i = 0; i < (yyval.interm.typeList)->size(); ++i) { + TType type((yyvsp[-2].interm.type)); + type.setFieldName((*(yyval.interm.typeList))[i].type->getFieldName()); + type.transferArraySizes((*(yyval.interm.typeList))[i].type->getArraySizes()); + type.copyArrayInnerSizes((yyvsp[-2].interm.type).arraySizes); + parseContext.arrayOfArrayVersionCheck((*(yyval.interm.typeList))[i].loc, type.getArraySizes()); + (*(yyval.interm.typeList))[i].type->shallowCopy(type); + } + } +#line 9679 "MachineIndependent/glslang_tab.cpp" + break; + + case 509: +#line 3392 "MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[-2].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[-2].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes); + } + + (yyval.interm.typeList) = (yyvsp[-1].interm.typeList); + + parseContext.memberQualifierCheck((yyvsp[-3].interm.type)); + parseContext.voidErrorCheck((yyvsp[-2].interm.type).loc, (*(yyvsp[-1].interm.typeList))[0].type->getFieldName(), (yyvsp[-2].interm.type).basicType); + parseContext.mergeQualifiers((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).qualifier, (yyvsp[-3].interm.type).qualifier, true); + parseContext.precisionQualifierCheck((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).basicType, (yyvsp[-2].interm.type).qualifier); + + for (unsigned int i = 0; i < (yyval.interm.typeList)->size(); ++i) { + TType type((yyvsp[-2].interm.type)); + type.setFieldName((*(yyval.interm.typeList))[i].type->getFieldName()); + type.transferArraySizes((*(yyval.interm.typeList))[i].type->getArraySizes()); + type.copyArrayInnerSizes((yyvsp[-2].interm.type).arraySizes); + parseContext.arrayOfArrayVersionCheck((*(yyval.interm.typeList))[i].loc, type.getArraySizes()); + (*(yyval.interm.typeList))[i].type->shallowCopy(type); + } + } +#line 9708 "MachineIndependent/glslang_tab.cpp" + break; + + case 510: +#line 3419 "MachineIndependent/glslang.y" + { + (yyval.interm.typeList) = new TTypeList; + (yyval.interm.typeList)->push_back((yyvsp[0].interm.typeLine)); + } +#line 9717 "MachineIndependent/glslang_tab.cpp" + break; + + case 511: +#line 3423 "MachineIndependent/glslang.y" + { + (yyval.interm.typeList)->push_back((yyvsp[0].interm.typeLine)); + } +#line 9725 "MachineIndependent/glslang_tab.cpp" + break; + + case 512: +#line 3429 "MachineIndependent/glslang.y" + { + (yyval.interm.typeLine).type = new TType(EbtVoid); + (yyval.interm.typeLine).loc = (yyvsp[0].lex).loc; + (yyval.interm.typeLine).type->setFieldName(*(yyvsp[0].lex).string); + } +#line 9735 "MachineIndependent/glslang_tab.cpp" + break; + + case 513: +#line 3434 "MachineIndependent/glslang.y" + { + parseContext.arrayOfArrayVersionCheck((yyvsp[-1].lex).loc, (yyvsp[0].interm).arraySizes); + + (yyval.interm.typeLine).type = new TType(EbtVoid); + (yyval.interm.typeLine).loc = (yyvsp[-1].lex).loc; + (yyval.interm.typeLine).type->setFieldName(*(yyvsp[-1].lex).string); + (yyval.interm.typeLine).type->transferArraySizes((yyvsp[0].interm).arraySizes); + } +#line 9748 "MachineIndependent/glslang_tab.cpp" + break; + + case 514: +#line 3445 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 9756 "MachineIndependent/glslang_tab.cpp" + break; + + case 515: +#line 3449 "MachineIndependent/glslang.y" + { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile((yyvsp[-2].lex).loc, ~EEsProfile, initFeature); + parseContext.profileRequires((yyvsp[-2].lex).loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + (yyval.interm.intermTypedNode) = (yyvsp[-1].interm.intermTypedNode); + } +#line 9767 "MachineIndependent/glslang_tab.cpp" + break; + + case 516: +#line 3455 "MachineIndependent/glslang.y" + { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile((yyvsp[-3].lex).loc, ~EEsProfile, initFeature); + parseContext.profileRequires((yyvsp[-3].lex).loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 9778 "MachineIndependent/glslang_tab.cpp" + break; + + case 517: +#line 3466 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.growAggregate(0, (yyvsp[0].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)->getLoc()); + } +#line 9786 "MachineIndependent/glslang_tab.cpp" + break; + + case 518: +#line 3469 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + } +#line 9794 "MachineIndependent/glslang_tab.cpp" + break; + + case 519: +#line 3476 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 9800 "MachineIndependent/glslang_tab.cpp" + break; + + case 520: +#line 3480 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 9806 "MachineIndependent/glslang_tab.cpp" + break; + + case 521: +#line 3481 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 9812 "MachineIndependent/glslang_tab.cpp" + break; + + case 522: +#line 3487 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 9818 "MachineIndependent/glslang_tab.cpp" + break; + + case 523: +#line 3488 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 9824 "MachineIndependent/glslang_tab.cpp" + break; + + case 524: +#line 3489 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 9830 "MachineIndependent/glslang_tab.cpp" + break; + + case 525: +#line 3490 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 9836 "MachineIndependent/glslang_tab.cpp" + break; + + case 526: +#line 3491 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 9842 "MachineIndependent/glslang_tab.cpp" + break; + + case 527: +#line 3492 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 9848 "MachineIndependent/glslang_tab.cpp" + break; + + case 528: +#line 3493 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 9854 "MachineIndependent/glslang_tab.cpp" + break; + + case 529: +#line 3495 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 9860 "MachineIndependent/glslang_tab.cpp" + break; + + case 530: +#line 3501 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[-1].lex).loc, EShLangFragment, "demote"); + parseContext.requireExtensions((yyvsp[-1].lex).loc, 1, &E_GL_EXT_demote_to_helper_invocation, "demote"); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpDemote, (yyvsp[-1].lex).loc); + } +#line 9870 "MachineIndependent/glslang_tab.cpp" + break; + + case 531: +#line 3510 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = 0; } +#line 9876 "MachineIndependent/glslang_tab.cpp" + break; + + case 532: +#line 3511 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + } +#line 9885 "MachineIndependent/glslang_tab.cpp" + break; + + case 533: +#line 3515 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + } +#line 9894 "MachineIndependent/glslang_tab.cpp" + break; + + case 534: +#line 3519 "MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.intermNode) && (yyvsp[-2].interm.intermNode)->getAsAggregate()) + (yyvsp[-2].interm.intermNode)->getAsAggregate()->setOperator(EOpSequence); + (yyval.interm.intermNode) = (yyvsp[-2].interm.intermNode); + } +#line 9904 "MachineIndependent/glslang_tab.cpp" + break; + + case 535: +#line 3527 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 9910 "MachineIndependent/glslang_tab.cpp" + break; + + case 536: +#line 3528 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 9916 "MachineIndependent/glslang_tab.cpp" + break; + + case 537: +#line 3532 "MachineIndependent/glslang.y" + { + ++parseContext.controlFlowNestingLevel; + } +#line 9924 "MachineIndependent/glslang_tab.cpp" + break; + + case 538: +#line 3535 "MachineIndependent/glslang.y" + { + --parseContext.controlFlowNestingLevel; + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 9933 "MachineIndependent/glslang_tab.cpp" + break; + + case 539: +#line 3539 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } +#line 9943 "MachineIndependent/glslang_tab.cpp" + break; + + case 540: +#line 3544 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 9954 "MachineIndependent/glslang_tab.cpp" + break; + + case 541: +#line 3553 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = 0; + } +#line 9962 "MachineIndependent/glslang_tab.cpp" + break; + + case 542: +#line 3556 "MachineIndependent/glslang.y" + { + if ((yyvsp[-1].interm.intermNode) && (yyvsp[-1].interm.intermNode)->getAsAggregate()) + (yyvsp[-1].interm.intermNode)->getAsAggregate()->setOperator(EOpSequence); + (yyval.interm.intermNode) = (yyvsp[-1].interm.intermNode); + } +#line 9972 "MachineIndependent/glslang_tab.cpp" + break; + + case 543: +#line 3564 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate((yyvsp[0].interm.intermNode)); + if ((yyvsp[0].interm.intermNode) && (yyvsp[0].interm.intermNode)->getAsBranchNode() && ((yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpCase || + (yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence(0, (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = 0; // start a fresh subsequence for what's after this case + } + } +#line 9985 "MachineIndependent/glslang_tab.cpp" + break; + + case 544: +#line 3572 "MachineIndependent/glslang.y" + { + if ((yyvsp[0].interm.intermNode) && (yyvsp[0].interm.intermNode)->getAsBranchNode() && ((yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpCase || + (yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence((yyvsp[-1].interm.intermNode) ? (yyvsp[-1].interm.intermNode)->getAsAggregate() : 0, (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = 0; // start a fresh subsequence for what's after this case + } else + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-1].interm.intermNode), (yyvsp[0].interm.intermNode)); + } +#line 9998 "MachineIndependent/glslang_tab.cpp" + break; + + case 545: +#line 3583 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = 0; } +#line 10004 "MachineIndependent/glslang_tab.cpp" + break; + + case 546: +#line 3584 "MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = static_cast((yyvsp[-1].interm.intermTypedNode)); } +#line 10010 "MachineIndependent/glslang_tab.cpp" + break; + + case 547: +#line 3588 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10018 "MachineIndependent/glslang_tab.cpp" + break; + + case 548: +#line 3592 "MachineIndependent/glslang.y" + { + parseContext.handleSelectionAttributes(*(yyvsp[-1].interm.attributes), (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10027 "MachineIndependent/glslang_tab.cpp" + break; + + case 549: +#line 3599 "MachineIndependent/glslang.y" + { + parseContext.boolCheck((yyvsp[-4].lex).loc, (yyvsp[-2].interm.intermTypedNode)); + (yyval.interm.intermNode) = parseContext.intermediate.addSelection((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.nodePair), (yyvsp[-4].lex).loc); + } +#line 10036 "MachineIndependent/glslang_tab.cpp" + break; + + case 550: +#line 3606 "MachineIndependent/glslang.y" + { + (yyval.interm.nodePair).node1 = (yyvsp[-2].interm.intermNode); + (yyval.interm.nodePair).node2 = (yyvsp[0].interm.intermNode); + } +#line 10045 "MachineIndependent/glslang_tab.cpp" + break; + + case 551: +#line 3610 "MachineIndependent/glslang.y" + { + (yyval.interm.nodePair).node1 = (yyvsp[0].interm.intermNode); + (yyval.interm.nodePair).node2 = 0; + } +#line 10054 "MachineIndependent/glslang_tab.cpp" + break; + + case 552: +#line 3618 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + parseContext.boolCheck((yyvsp[0].interm.intermTypedNode)->getLoc(), (yyvsp[0].interm.intermTypedNode)); + } +#line 10063 "MachineIndependent/glslang_tab.cpp" + break; + + case 553: +#line 3622 "MachineIndependent/glslang.y" + { + parseContext.boolCheck((yyvsp[-2].lex).loc, (yyvsp[-3].interm.type)); + + TType type((yyvsp[-3].interm.type)); + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-2].lex).loc, *(yyvsp[-2].lex).string, (yyvsp[-3].interm.type), 0, (yyvsp[0].interm.intermTypedNode)); + if (initNode) + (yyval.interm.intermTypedNode) = initNode->getAsTyped(); + else + (yyval.interm.intermTypedNode) = 0; + } +#line 10078 "MachineIndependent/glslang_tab.cpp" + break; + + case 554: +#line 3635 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10086 "MachineIndependent/glslang_tab.cpp" + break; + + case 555: +#line 3639 "MachineIndependent/glslang.y" + { + parseContext.handleSwitchAttributes(*(yyvsp[-1].interm.attributes), (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10095 "MachineIndependent/glslang_tab.cpp" + break; + + case 556: +#line 3646 "MachineIndependent/glslang.y" + { + // start new switch sequence on the switch stack + ++parseContext.controlFlowNestingLevel; + ++parseContext.statementNestingLevel; + parseContext.switchSequenceStack.push_back(new TIntermSequence); + parseContext.switchLevel.push_back(parseContext.statementNestingLevel); + parseContext.symbolTable.push(); + } +#line 10108 "MachineIndependent/glslang_tab.cpp" + break; + + case 557: +#line 3654 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.addSwitch((yyvsp[-7].lex).loc, (yyvsp[-5].interm.intermTypedNode), (yyvsp[-1].interm.intermNode) ? (yyvsp[-1].interm.intermNode)->getAsAggregate() : 0); + delete parseContext.switchSequenceStack.back(); + parseContext.switchSequenceStack.pop_back(); + parseContext.switchLevel.pop_back(); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } +#line 10122 "MachineIndependent/glslang_tab.cpp" + break; + + case 558: +#line 3666 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = 0; + } +#line 10130 "MachineIndependent/glslang_tab.cpp" + break; + + case 559: +#line 3669 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10138 "MachineIndependent/glslang_tab.cpp" + break; + + case 560: +#line 3675 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error((yyvsp[-2].lex).loc, "cannot appear outside switch statement", "case", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error((yyvsp[-2].lex).loc, "cannot be nested inside control flow", "case", ""); + else { + parseContext.constantValueCheck((yyvsp[-1].interm.intermTypedNode), "case"); + parseContext.integerCheck((yyvsp[-1].interm.intermTypedNode), "case"); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpCase, (yyvsp[-1].interm.intermTypedNode), (yyvsp[-2].lex).loc); + } + } +#line 10155 "MachineIndependent/glslang_tab.cpp" + break; + + case 561: +#line 3687 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error((yyvsp[-1].lex).loc, "cannot appear outside switch statement", "default", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error((yyvsp[-1].lex).loc, "cannot be nested inside control flow", "default", ""); + else + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpDefault, (yyvsp[-1].lex).loc); + } +#line 10169 "MachineIndependent/glslang_tab.cpp" + break; + + case 562: +#line 3699 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10177 "MachineIndependent/glslang_tab.cpp" + break; + + case 563: +#line 3703 "MachineIndependent/glslang.y" + { + parseContext.handleLoopAttributes(*(yyvsp[-1].interm.attributes), (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10186 "MachineIndependent/glslang_tab.cpp" + break; + + case 564: +#line 3710 "MachineIndependent/glslang.y" + { + if (! parseContext.limits.whileLoops) + parseContext.error((yyvsp[-1].lex).loc, "while loops not available", "limitation", ""); + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } +#line 10199 "MachineIndependent/glslang_tab.cpp" + break; + + case 565: +#line 3718 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + (yyval.interm.intermNode) = parseContext.intermediate.addLoop((yyvsp[0].interm.intermNode), (yyvsp[-2].interm.intermTypedNode), 0, true, (yyvsp[-5].lex).loc); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } +#line 10211 "MachineIndependent/glslang_tab.cpp" + break; + + case 566: +#line 3725 "MachineIndependent/glslang.y" + { + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } +#line 10221 "MachineIndependent/glslang_tab.cpp" + break; + + case 567: +#line 3730 "MachineIndependent/glslang.y" + { + if (! parseContext.limits.whileLoops) + parseContext.error((yyvsp[-7].lex).loc, "do-while loops not available", "limitation", ""); + + parseContext.boolCheck((yyvsp[0].lex).loc, (yyvsp[-2].interm.intermTypedNode)); + + (yyval.interm.intermNode) = parseContext.intermediate.addLoop((yyvsp[-5].interm.intermNode), (yyvsp[-2].interm.intermTypedNode), 0, false, (yyvsp[-4].lex).loc); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } +#line 10237 "MachineIndependent/glslang_tab.cpp" + break; + + case 568: +#line 3741 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } +#line 10248 "MachineIndependent/glslang_tab.cpp" + break; + + case 569: +#line 3747 "MachineIndependent/glslang.y" + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate((yyvsp[-3].interm.intermNode), (yyvsp[-5].lex).loc); + TIntermLoop* forLoop = parseContext.intermediate.addLoop((yyvsp[0].interm.intermNode), reinterpret_cast((yyvsp[-2].interm.nodePair).node1), reinterpret_cast((yyvsp[-2].interm.nodePair).node2), true, (yyvsp[-6].lex).loc); + if (! parseContext.limits.nonInductiveForLoops) + parseContext.inductiveLoopCheck((yyvsp[-6].lex).loc, (yyvsp[-3].interm.intermNode), forLoop); + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyval.interm.intermNode), forLoop, (yyvsp[-6].lex).loc); + (yyval.interm.intermNode)->getAsAggregate()->setOperator(EOpSequence); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } +#line 10265 "MachineIndependent/glslang_tab.cpp" + break; + + case 570: +#line 3762 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10273 "MachineIndependent/glslang_tab.cpp" + break; + + case 571: +#line 3765 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10281 "MachineIndependent/glslang_tab.cpp" + break; + + case 572: +#line 3771 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 10289 "MachineIndependent/glslang_tab.cpp" + break; + + case 573: +#line 3774 "MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = 0; + } +#line 10297 "MachineIndependent/glslang_tab.cpp" + break; + + case 574: +#line 3780 "MachineIndependent/glslang.y" + { + (yyval.interm.nodePair).node1 = (yyvsp[-1].interm.intermTypedNode); + (yyval.interm.nodePair).node2 = 0; + } +#line 10306 "MachineIndependent/glslang_tab.cpp" + break; + + case 575: +#line 3784 "MachineIndependent/glslang.y" + { + (yyval.interm.nodePair).node1 = (yyvsp[-2].interm.intermTypedNode); + (yyval.interm.nodePair).node2 = (yyvsp[0].interm.intermTypedNode); + } +#line 10315 "MachineIndependent/glslang_tab.cpp" + break; + + case 576: +#line 3791 "MachineIndependent/glslang.y" + { + if (parseContext.loopNestingLevel <= 0) + parseContext.error((yyvsp[-1].lex).loc, "continue statement only allowed in loops", "", ""); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpContinue, (yyvsp[-1].lex).loc); + } +#line 10325 "MachineIndependent/glslang_tab.cpp" + break; + + case 577: +#line 3796 "MachineIndependent/glslang.y" + { + if (parseContext.loopNestingLevel + parseContext.switchSequenceStack.size() <= 0) + parseContext.error((yyvsp[-1].lex).loc, "break statement only allowed in switch and loops", "", ""); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpBreak, (yyvsp[-1].lex).loc); + } +#line 10335 "MachineIndependent/glslang_tab.cpp" + break; + + case 578: +#line 3801 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpReturn, (yyvsp[-1].lex).loc); + if (parseContext.currentFunctionType->getBasicType() != EbtVoid) + parseContext.error((yyvsp[-1].lex).loc, "non-void function must return a value", "return", ""); + if (parseContext.inMain) + parseContext.postEntryPointReturn = true; + } +#line 10347 "MachineIndependent/glslang_tab.cpp" + break; + + case 579: +#line 3808 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.handleReturnValue((yyvsp[-2].lex).loc, (yyvsp[-1].interm.intermTypedNode)); + } +#line 10355 "MachineIndependent/glslang_tab.cpp" + break; + + case 580: +#line 3811 "MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[-1].lex).loc, EShLangFragment, "discard"); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpKill, (yyvsp[-1].lex).loc); + } +#line 10364 "MachineIndependent/glslang_tab.cpp" + break; + + case 581: +#line 3820 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + parseContext.intermediate.setTreeRoot((yyval.interm.intermNode)); + } +#line 10373 "MachineIndependent/glslang_tab.cpp" + break; + + case 582: +#line 3824 "MachineIndependent/glslang.y" + { + if ((yyvsp[0].interm.intermNode) != nullptr) { + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-1].interm.intermNode), (yyvsp[0].interm.intermNode)); + parseContext.intermediate.setTreeRoot((yyval.interm.intermNode)); + } + } +#line 10384 "MachineIndependent/glslang_tab.cpp" + break; + + case 583: +#line 3833 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10392 "MachineIndependent/glslang_tab.cpp" + break; + + case 584: +#line 3836 "MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10400 "MachineIndependent/glslang_tab.cpp" + break; + + case 585: +#line 3840 "MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ~EEsProfile, "extraneous semicolon"); + parseContext.profileRequires((yyvsp[0].lex).loc, ~EEsProfile, 460, nullptr, "extraneous semicolon"); + (yyval.interm.intermNode) = nullptr; + } +#line 10410 "MachineIndependent/glslang_tab.cpp" + break; + + case 586: +#line 3849 "MachineIndependent/glslang.y" + { + (yyvsp[0].interm).function = parseContext.handleFunctionDeclarator((yyvsp[0].interm).loc, *(yyvsp[0].interm).function, false /* not prototype */); + (yyvsp[0].interm).intermNode = parseContext.handleFunctionDefinition((yyvsp[0].interm).loc, *(yyvsp[0].interm).function); + } +#line 10419 "MachineIndependent/glslang_tab.cpp" + break; + + case 587: +#line 3853 "MachineIndependent/glslang.y" + { + // May be best done as post process phase on intermediate code + if (parseContext.currentFunctionType->getBasicType() != EbtVoid && ! parseContext.functionReturnsValue) + parseContext.error((yyvsp[-2].interm).loc, "function does not return a value:", "", (yyvsp[-2].interm).function->getName().c_str()); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm).intermNode, (yyvsp[0].interm.intermNode)); + parseContext.intermediate.setAggregateOperator((yyval.interm.intermNode), EOpFunction, (yyvsp[-2].interm).function->getType(), (yyvsp[-2].interm).loc); + (yyval.interm.intermNode)->getAsAggregate()->setName((yyvsp[-2].interm).function->getMangledName().c_str()); + + // store the pragma information for debug and optimize and other vendor specific + // information. This information can be queried from the parse tree + (yyval.interm.intermNode)->getAsAggregate()->setOptimize(parseContext.contextPragma.optimize); + (yyval.interm.intermNode)->getAsAggregate()->setDebug(parseContext.contextPragma.debug); + (yyval.interm.intermNode)->getAsAggregate()->setPragmaTable(parseContext.contextPragma.pragmaTable); + } +#line 10439 "MachineIndependent/glslang_tab.cpp" + break; + + case 588: +#line 3872 "MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = (yyvsp[-2].interm.attributes); + parseContext.requireExtensions((yyvsp[-4].lex).loc, 1, &E_GL_EXT_control_flow_attributes, "attribute"); + } +#line 10448 "MachineIndependent/glslang_tab.cpp" + break; + + case 589: +#line 3878 "MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = (yyvsp[0].interm.attributes); + } +#line 10456 "MachineIndependent/glslang_tab.cpp" + break; + + case 590: +#line 3881 "MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = parseContext.mergeAttributes((yyvsp[-2].interm.attributes), (yyvsp[0].interm.attributes)); + } +#line 10464 "MachineIndependent/glslang_tab.cpp" + break; + + case 591: +#line 3886 "MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = parseContext.makeAttributes(*(yyvsp[0].lex).string); + } +#line 10472 "MachineIndependent/glslang_tab.cpp" + break; + + case 592: +#line 3889 "MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = parseContext.makeAttributes(*(yyvsp[-3].lex).string, (yyvsp[-1].interm.intermTypedNode)); + } +#line 10480 "MachineIndependent/glslang_tab.cpp" + break; + + +#line 10484 "MachineIndependent/glslang_tab.cpp" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (pParseContext, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (pParseContext, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, pParseContext); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp, pParseContext); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (pParseContext, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + + +/*-----------------------------------------------------. +| yyreturn -- parsing is finished, return the result. | +`-----------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, pParseContext); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[+*yyssp], yyvsp, pParseContext); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} +#line 3894 "MachineIndependent/glslang.y" + diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/glslang_tab.cpp.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/glslang_tab.cpp.h new file mode 100644 index 000000000..d48b41a36 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/glslang_tab.cpp.h @@ -0,0 +1,525 @@ +/* A Bison parser, made by GNU Bison 3.5.1. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Undocumented macros, especially those whose name start with YY_, + are private implementation details. Do not rely on them. */ + +#ifndef YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED +# define YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + CONST = 258, + BOOL = 259, + INT = 260, + UINT = 261, + FLOAT = 262, + BVEC2 = 263, + BVEC3 = 264, + BVEC4 = 265, + IVEC2 = 266, + IVEC3 = 267, + IVEC4 = 268, + UVEC2 = 269, + UVEC3 = 270, + UVEC4 = 271, + VEC2 = 272, + VEC3 = 273, + VEC4 = 274, + MAT2 = 275, + MAT3 = 276, + MAT4 = 277, + MAT2X2 = 278, + MAT2X3 = 279, + MAT2X4 = 280, + MAT3X2 = 281, + MAT3X3 = 282, + MAT3X4 = 283, + MAT4X2 = 284, + MAT4X3 = 285, + MAT4X4 = 286, + SAMPLER2D = 287, + SAMPLER3D = 288, + SAMPLERCUBE = 289, + SAMPLER2DSHADOW = 290, + SAMPLERCUBESHADOW = 291, + SAMPLER2DARRAY = 292, + SAMPLER2DARRAYSHADOW = 293, + ISAMPLER2D = 294, + ISAMPLER3D = 295, + ISAMPLERCUBE = 296, + ISAMPLER2DARRAY = 297, + USAMPLER2D = 298, + USAMPLER3D = 299, + USAMPLERCUBE = 300, + USAMPLER2DARRAY = 301, + SAMPLER = 302, + SAMPLERSHADOW = 303, + TEXTURE2D = 304, + TEXTURE3D = 305, + TEXTURECUBE = 306, + TEXTURE2DARRAY = 307, + ITEXTURE2D = 308, + ITEXTURE3D = 309, + ITEXTURECUBE = 310, + ITEXTURE2DARRAY = 311, + UTEXTURE2D = 312, + UTEXTURE3D = 313, + UTEXTURECUBE = 314, + UTEXTURE2DARRAY = 315, + ATTRIBUTE = 316, + VARYING = 317, + FLOAT16_T = 318, + FLOAT32_T = 319, + DOUBLE = 320, + FLOAT64_T = 321, + INT64_T = 322, + UINT64_T = 323, + INT32_T = 324, + UINT32_T = 325, + INT16_T = 326, + UINT16_T = 327, + INT8_T = 328, + UINT8_T = 329, + I64VEC2 = 330, + I64VEC3 = 331, + I64VEC4 = 332, + U64VEC2 = 333, + U64VEC3 = 334, + U64VEC4 = 335, + I32VEC2 = 336, + I32VEC3 = 337, + I32VEC4 = 338, + U32VEC2 = 339, + U32VEC3 = 340, + U32VEC4 = 341, + I16VEC2 = 342, + I16VEC3 = 343, + I16VEC4 = 344, + U16VEC2 = 345, + U16VEC3 = 346, + U16VEC4 = 347, + I8VEC2 = 348, + I8VEC3 = 349, + I8VEC4 = 350, + U8VEC2 = 351, + U8VEC3 = 352, + U8VEC4 = 353, + DVEC2 = 354, + DVEC3 = 355, + DVEC4 = 356, + DMAT2 = 357, + DMAT3 = 358, + DMAT4 = 359, + F16VEC2 = 360, + F16VEC3 = 361, + F16VEC4 = 362, + F16MAT2 = 363, + F16MAT3 = 364, + F16MAT4 = 365, + F32VEC2 = 366, + F32VEC3 = 367, + F32VEC4 = 368, + F32MAT2 = 369, + F32MAT3 = 370, + F32MAT4 = 371, + F64VEC2 = 372, + F64VEC3 = 373, + F64VEC4 = 374, + F64MAT2 = 375, + F64MAT3 = 376, + F64MAT4 = 377, + DMAT2X2 = 378, + DMAT2X3 = 379, + DMAT2X4 = 380, + DMAT3X2 = 381, + DMAT3X3 = 382, + DMAT3X4 = 383, + DMAT4X2 = 384, + DMAT4X3 = 385, + DMAT4X4 = 386, + F16MAT2X2 = 387, + F16MAT2X3 = 388, + F16MAT2X4 = 389, + F16MAT3X2 = 390, + F16MAT3X3 = 391, + F16MAT3X4 = 392, + F16MAT4X2 = 393, + F16MAT4X3 = 394, + F16MAT4X4 = 395, + F32MAT2X2 = 396, + F32MAT2X3 = 397, + F32MAT2X4 = 398, + F32MAT3X2 = 399, + F32MAT3X3 = 400, + F32MAT3X4 = 401, + F32MAT4X2 = 402, + F32MAT4X3 = 403, + F32MAT4X4 = 404, + F64MAT2X2 = 405, + F64MAT2X3 = 406, + F64MAT2X4 = 407, + F64MAT3X2 = 408, + F64MAT3X3 = 409, + F64MAT3X4 = 410, + F64MAT4X2 = 411, + F64MAT4X3 = 412, + F64MAT4X4 = 413, + ATOMIC_UINT = 414, + ACCSTRUCTNV = 415, + ACCSTRUCTEXT = 416, + RAYQUERYEXT = 417, + FCOOPMATNV = 418, + ICOOPMATNV = 419, + UCOOPMATNV = 420, + SAMPLERCUBEARRAY = 421, + SAMPLERCUBEARRAYSHADOW = 422, + ISAMPLERCUBEARRAY = 423, + USAMPLERCUBEARRAY = 424, + SAMPLER1D = 425, + SAMPLER1DARRAY = 426, + SAMPLER1DARRAYSHADOW = 427, + ISAMPLER1D = 428, + SAMPLER1DSHADOW = 429, + SAMPLER2DRECT = 430, + SAMPLER2DRECTSHADOW = 431, + ISAMPLER2DRECT = 432, + USAMPLER2DRECT = 433, + SAMPLERBUFFER = 434, + ISAMPLERBUFFER = 435, + USAMPLERBUFFER = 436, + SAMPLER2DMS = 437, + ISAMPLER2DMS = 438, + USAMPLER2DMS = 439, + SAMPLER2DMSARRAY = 440, + ISAMPLER2DMSARRAY = 441, + USAMPLER2DMSARRAY = 442, + SAMPLEREXTERNALOES = 443, + SAMPLEREXTERNAL2DY2YEXT = 444, + SAMPLERVIDEO = 445, + ISAMPLER1DARRAY = 446, + USAMPLER1D = 447, + USAMPLER1DARRAY = 448, + F16SAMPLER1D = 449, + F16SAMPLER2D = 450, + F16SAMPLER3D = 451, + F16SAMPLER2DRECT = 452, + F16SAMPLERCUBE = 453, + F16SAMPLER1DARRAY = 454, + F16SAMPLER2DARRAY = 455, + F16SAMPLERCUBEARRAY = 456, + F16SAMPLERBUFFER = 457, + F16SAMPLER2DMS = 458, + F16SAMPLER2DMSARRAY = 459, + F16SAMPLER1DSHADOW = 460, + F16SAMPLER2DSHADOW = 461, + F16SAMPLER1DARRAYSHADOW = 462, + F16SAMPLER2DARRAYSHADOW = 463, + F16SAMPLER2DRECTSHADOW = 464, + F16SAMPLERCUBESHADOW = 465, + F16SAMPLERCUBEARRAYSHADOW = 466, + IMAGE1D = 467, + IIMAGE1D = 468, + UIMAGE1D = 469, + IMAGE2D = 470, + IIMAGE2D = 471, + UIMAGE2D = 472, + IMAGE3D = 473, + IIMAGE3D = 474, + UIMAGE3D = 475, + IMAGE2DRECT = 476, + IIMAGE2DRECT = 477, + UIMAGE2DRECT = 478, + IMAGECUBE = 479, + IIMAGECUBE = 480, + UIMAGECUBE = 481, + IMAGEBUFFER = 482, + IIMAGEBUFFER = 483, + UIMAGEBUFFER = 484, + IMAGE1DARRAY = 485, + IIMAGE1DARRAY = 486, + UIMAGE1DARRAY = 487, + IMAGE2DARRAY = 488, + IIMAGE2DARRAY = 489, + UIMAGE2DARRAY = 490, + IMAGECUBEARRAY = 491, + IIMAGECUBEARRAY = 492, + UIMAGECUBEARRAY = 493, + IMAGE2DMS = 494, + IIMAGE2DMS = 495, + UIMAGE2DMS = 496, + IMAGE2DMSARRAY = 497, + IIMAGE2DMSARRAY = 498, + UIMAGE2DMSARRAY = 499, + F16IMAGE1D = 500, + F16IMAGE2D = 501, + F16IMAGE3D = 502, + F16IMAGE2DRECT = 503, + F16IMAGECUBE = 504, + F16IMAGE1DARRAY = 505, + F16IMAGE2DARRAY = 506, + F16IMAGECUBEARRAY = 507, + F16IMAGEBUFFER = 508, + F16IMAGE2DMS = 509, + F16IMAGE2DMSARRAY = 510, + TEXTURECUBEARRAY = 511, + ITEXTURECUBEARRAY = 512, + UTEXTURECUBEARRAY = 513, + TEXTURE1D = 514, + ITEXTURE1D = 515, + UTEXTURE1D = 516, + TEXTURE1DARRAY = 517, + ITEXTURE1DARRAY = 518, + UTEXTURE1DARRAY = 519, + TEXTURE2DRECT = 520, + ITEXTURE2DRECT = 521, + UTEXTURE2DRECT = 522, + TEXTUREBUFFER = 523, + ITEXTUREBUFFER = 524, + UTEXTUREBUFFER = 525, + TEXTURE2DMS = 526, + ITEXTURE2DMS = 527, + UTEXTURE2DMS = 528, + TEXTURE2DMSARRAY = 529, + ITEXTURE2DMSARRAY = 530, + UTEXTURE2DMSARRAY = 531, + F16TEXTURE1D = 532, + F16TEXTURE2D = 533, + F16TEXTURE3D = 534, + F16TEXTURE2DRECT = 535, + F16TEXTURECUBE = 536, + F16TEXTURE1DARRAY = 537, + F16TEXTURE2DARRAY = 538, + F16TEXTURECUBEARRAY = 539, + F16TEXTUREBUFFER = 540, + F16TEXTURE2DMS = 541, + F16TEXTURE2DMSARRAY = 542, + SUBPASSINPUT = 543, + SUBPASSINPUTMS = 544, + ISUBPASSINPUT = 545, + ISUBPASSINPUTMS = 546, + USUBPASSINPUT = 547, + USUBPASSINPUTMS = 548, + F16SUBPASSINPUT = 549, + F16SUBPASSINPUTMS = 550, + LEFT_OP = 551, + RIGHT_OP = 552, + INC_OP = 553, + DEC_OP = 554, + LE_OP = 555, + GE_OP = 556, + EQ_OP = 557, + NE_OP = 558, + AND_OP = 559, + OR_OP = 560, + XOR_OP = 561, + MUL_ASSIGN = 562, + DIV_ASSIGN = 563, + ADD_ASSIGN = 564, + MOD_ASSIGN = 565, + LEFT_ASSIGN = 566, + RIGHT_ASSIGN = 567, + AND_ASSIGN = 568, + XOR_ASSIGN = 569, + OR_ASSIGN = 570, + SUB_ASSIGN = 571, + STRING_LITERAL = 572, + LEFT_PAREN = 573, + RIGHT_PAREN = 574, + LEFT_BRACKET = 575, + RIGHT_BRACKET = 576, + LEFT_BRACE = 577, + RIGHT_BRACE = 578, + DOT = 579, + COMMA = 580, + COLON = 581, + EQUAL = 582, + SEMICOLON = 583, + BANG = 584, + DASH = 585, + TILDE = 586, + PLUS = 587, + STAR = 588, + SLASH = 589, + PERCENT = 590, + LEFT_ANGLE = 591, + RIGHT_ANGLE = 592, + VERTICAL_BAR = 593, + CARET = 594, + AMPERSAND = 595, + QUESTION = 596, + INVARIANT = 597, + HIGH_PRECISION = 598, + MEDIUM_PRECISION = 599, + LOW_PRECISION = 600, + PRECISION = 601, + PACKED = 602, + RESOURCE = 603, + SUPERP = 604, + FLOATCONSTANT = 605, + INTCONSTANT = 606, + UINTCONSTANT = 607, + BOOLCONSTANT = 608, + IDENTIFIER = 609, + TYPE_NAME = 610, + CENTROID = 611, + IN = 612, + OUT = 613, + INOUT = 614, + STRUCT = 615, + VOID = 616, + WHILE = 617, + BREAK = 618, + CONTINUE = 619, + DO = 620, + ELSE = 621, + FOR = 622, + IF = 623, + DISCARD = 624, + RETURN = 625, + SWITCH = 626, + CASE = 627, + DEFAULT = 628, + UNIFORM = 629, + SHARED = 630, + BUFFER = 631, + FLAT = 632, + SMOOTH = 633, + LAYOUT = 634, + DOUBLECONSTANT = 635, + INT16CONSTANT = 636, + UINT16CONSTANT = 637, + FLOAT16CONSTANT = 638, + INT32CONSTANT = 639, + UINT32CONSTANT = 640, + INT64CONSTANT = 641, + UINT64CONSTANT = 642, + SUBROUTINE = 643, + DEMOTE = 644, + PAYLOADNV = 645, + PAYLOADINNV = 646, + HITATTRNV = 647, + CALLDATANV = 648, + CALLDATAINNV = 649, + PAYLOADEXT = 650, + PAYLOADINEXT = 651, + HITATTREXT = 652, + CALLDATAEXT = 653, + CALLDATAINEXT = 654, + PATCH = 655, + SAMPLE = 656, + NONUNIFORM = 657, + COHERENT = 658, + VOLATILE = 659, + RESTRICT = 660, + READONLY = 661, + WRITEONLY = 662, + DEVICECOHERENT = 663, + QUEUEFAMILYCOHERENT = 664, + WORKGROUPCOHERENT = 665, + SUBGROUPCOHERENT = 666, + NONPRIVATE = 667, + SHADERCALLCOHERENT = 668, + NOPERSPECTIVE = 669, + EXPLICITINTERPAMD = 670, + PERVERTEXNV = 671, + PERPRIMITIVENV = 672, + PERVIEWNV = 673, + PERTASKNV = 674, + PRECISE = 675 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 97 "MachineIndependent/glslang.y" + + struct { + glslang::TSourceLoc loc; + union { + glslang::TString *string; + int i; + unsigned int u; + long long i64; + unsigned long long u64; + bool b; + double d; + }; + glslang::TSymbol* symbol; + } lex; + struct { + glslang::TSourceLoc loc; + glslang::TOperator op; + union { + TIntermNode* intermNode; + glslang::TIntermNodePair nodePair; + glslang::TIntermTyped* intermTypedNode; + glslang::TAttributes* attributes; + }; + union { + glslang::TPublicType type; + glslang::TFunction* function; + glslang::TParameter param; + glslang::TTypeLoc typeLine; + glslang::TTypeList* typeList; + glslang::TArraySizes* arraySizes; + glslang::TIdentifierList* identifierList; + }; + glslang::TArraySizes* typeParameters; + } interm; + +#line 514 "MachineIndependent/glslang_tab.cpp.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int yyparse (glslang::TParseContext* pParseContext); + +#endif /* !YY_YY_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED */ diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/intermOut.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/intermOut.cpp new file mode 100644 index 000000000..86edcfe4d --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/intermOut.cpp @@ -0,0 +1,1565 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef GLSLANG_WEB + +#include "localintermediate.h" +#include "../Include/InfoSink.h" + +#ifdef _MSC_VER +#include +#else +#include +#endif +#include + +namespace { + +bool IsInfinity(double x) { +#ifdef _MSC_VER + switch (_fpclass(x)) { + case _FPCLASS_NINF: + case _FPCLASS_PINF: + return true; + default: + return false; + } +#else + return std::isinf(x); +#endif +} + +bool IsNan(double x) { +#ifdef _MSC_VER + switch (_fpclass(x)) { + case _FPCLASS_SNAN: + case _FPCLASS_QNAN: + return true; + default: + return false; + } +#else + return std::isnan(x); +#endif +} + +} + +namespace glslang { + +// +// Two purposes: +// 1. Show an example of how to iterate tree. Functions can +// also directly call Traverse() on children themselves to +// have finer grained control over the process than shown here. +// See the last function for how to get started. +// 2. Print out a text based description of the tree. +// + +// +// Use this class to carry along data from node to node in +// the traversal +// +class TOutputTraverser : public TIntermTraverser { +public: + TOutputTraverser(TInfoSink& i) : infoSink(i), extraOutput(NoExtraOutput) { } + + enum EExtraOutput { + NoExtraOutput, + BinaryDoubleOutput + }; + void setDoubleOutput(EExtraOutput extra) { extraOutput = extra; } + + virtual bool visitBinary(TVisit, TIntermBinary* node); + virtual bool visitUnary(TVisit, TIntermUnary* node); + virtual bool visitAggregate(TVisit, TIntermAggregate* node); + virtual bool visitSelection(TVisit, TIntermSelection* node); + virtual void visitConstantUnion(TIntermConstantUnion* node); + virtual void visitSymbol(TIntermSymbol* node); + virtual bool visitLoop(TVisit, TIntermLoop* node); + virtual bool visitBranch(TVisit, TIntermBranch* node); + virtual bool visitSwitch(TVisit, TIntermSwitch* node); + + TInfoSink& infoSink; +protected: + TOutputTraverser(TOutputTraverser&); + TOutputTraverser& operator=(TOutputTraverser&); + + EExtraOutput extraOutput; +}; + +// +// Helper functions for printing, not part of traversing. +// + +static void OutputTreeText(TInfoSink& infoSink, const TIntermNode* node, const int depth) +{ + int i; + + infoSink.debug << node->getLoc().string << ":"; + if (node->getLoc().line) + infoSink.debug << node->getLoc().line; + else + infoSink.debug << "? "; + + for (i = 0; i < depth; ++i) + infoSink.debug << " "; +} + +// +// The rest of the file are the traversal functions. The last one +// is the one that starts the traversal. +// +// Return true from interior nodes to have the external traversal +// continue on to children. If you process children yourself, +// return false. +// + +bool TOutputTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + switch (node->getOp()) { + case EOpAssign: out.debug << "move second child to first child"; break; + case EOpAddAssign: out.debug << "add second child into first child"; break; + case EOpSubAssign: out.debug << "subtract second child into first child"; break; + case EOpMulAssign: out.debug << "multiply second child into first child"; break; + case EOpVectorTimesMatrixAssign: out.debug << "matrix mult second child into first child"; break; + case EOpVectorTimesScalarAssign: out.debug << "vector scale second child into first child"; break; + case EOpMatrixTimesScalarAssign: out.debug << "matrix scale second child into first child"; break; + case EOpMatrixTimesMatrixAssign: out.debug << "matrix mult second child into first child"; break; + case EOpDivAssign: out.debug << "divide second child into first child"; break; + case EOpModAssign: out.debug << "mod second child into first child"; break; + case EOpAndAssign: out.debug << "and second child into first child"; break; + case EOpInclusiveOrAssign: out.debug << "or second child into first child"; break; + case EOpExclusiveOrAssign: out.debug << "exclusive or second child into first child"; break; + case EOpLeftShiftAssign: out.debug << "left shift second child into first child"; break; + case EOpRightShiftAssign: out.debug << "right shift second child into first child"; break; + + case EOpIndexDirect: out.debug << "direct index"; break; + case EOpIndexIndirect: out.debug << "indirect index"; break; + case EOpIndexDirectStruct: + { + bool reference = node->getLeft()->getType().isReference(); + const TTypeList *members = reference ? node->getLeft()->getType().getReferentType()->getStruct() : node->getLeft()->getType().getStruct(); + out.debug << (*members)[node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst()].type->getFieldName(); + out.debug << ": direct index for structure"; break; + } + case EOpVectorSwizzle: out.debug << "vector swizzle"; break; + case EOpMatrixSwizzle: out.debug << "matrix swizzle"; break; + + case EOpAdd: out.debug << "add"; break; + case EOpSub: out.debug << "subtract"; break; + case EOpMul: out.debug << "component-wise multiply"; break; + case EOpDiv: out.debug << "divide"; break; + case EOpMod: out.debug << "mod"; break; + case EOpRightShift: out.debug << "right-shift"; break; + case EOpLeftShift: out.debug << "left-shift"; break; + case EOpAnd: out.debug << "bitwise and"; break; + case EOpInclusiveOr: out.debug << "inclusive-or"; break; + case EOpExclusiveOr: out.debug << "exclusive-or"; break; + case EOpEqual: out.debug << "Compare Equal"; break; + case EOpNotEqual: out.debug << "Compare Not Equal"; break; + case EOpLessThan: out.debug << "Compare Less Than"; break; + case EOpGreaterThan: out.debug << "Compare Greater Than"; break; + case EOpLessThanEqual: out.debug << "Compare Less Than or Equal"; break; + case EOpGreaterThanEqual: out.debug << "Compare Greater Than or Equal"; break; + case EOpVectorEqual: out.debug << "Equal"; break; + case EOpVectorNotEqual: out.debug << "NotEqual"; break; + + case EOpVectorTimesScalar: out.debug << "vector-scale"; break; + case EOpVectorTimesMatrix: out.debug << "vector-times-matrix"; break; + case EOpMatrixTimesVector: out.debug << "matrix-times-vector"; break; + case EOpMatrixTimesScalar: out.debug << "matrix-scale"; break; + case EOpMatrixTimesMatrix: out.debug << "matrix-multiply"; break; + + case EOpLogicalOr: out.debug << "logical-or"; break; + case EOpLogicalXor: out.debug << "logical-xor"; break; + case EOpLogicalAnd: out.debug << "logical-and"; break; + + case EOpAbsDifference: out.debug << "absoluteDifference"; break; + case EOpAddSaturate: out.debug << "addSaturate"; break; + case EOpSubSaturate: out.debug << "subtractSaturate"; break; + case EOpAverage: out.debug << "average"; break; + case EOpAverageRounded: out.debug << "averageRounded"; break; + case EOpMul32x16: out.debug << "multiply32x16"; break; + + default: out.debug << ""; + } + + out.debug << " (" << node->getCompleteString() << ")"; + + out.debug << "\n"; + + return true; +} + +bool TOutputTraverser::visitUnary(TVisit /* visit */, TIntermUnary* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + switch (node->getOp()) { + case EOpNegative: out.debug << "Negate value"; break; + case EOpVectorLogicalNot: + case EOpLogicalNot: out.debug << "Negate conditional"; break; + case EOpBitwiseNot: out.debug << "Bitwise not"; break; + + case EOpPostIncrement: out.debug << "Post-Increment"; break; + case EOpPostDecrement: out.debug << "Post-Decrement"; break; + case EOpPreIncrement: out.debug << "Pre-Increment"; break; + case EOpPreDecrement: out.debug << "Pre-Decrement"; break; + case EOpCopyObject: out.debug << "copy object"; break; + + // * -> bool + case EOpConvInt8ToBool: out.debug << "Convert int8_t to bool"; break; + case EOpConvUint8ToBool: out.debug << "Convert uint8_t to bool"; break; + case EOpConvInt16ToBool: out.debug << "Convert int16_t to bool"; break; + case EOpConvUint16ToBool: out.debug << "Convert uint16_t to bool";break; + case EOpConvIntToBool: out.debug << "Convert int to bool"; break; + case EOpConvUintToBool: out.debug << "Convert uint to bool"; break; + case EOpConvInt64ToBool: out.debug << "Convert int64 to bool"; break; + case EOpConvUint64ToBool: out.debug << "Convert uint64 to bool"; break; + case EOpConvFloat16ToBool: out.debug << "Convert float16_t to bool"; break; + case EOpConvFloatToBool: out.debug << "Convert float to bool"; break; + case EOpConvDoubleToBool: out.debug << "Convert double to bool"; break; + + // bool -> * + case EOpConvBoolToInt8: out.debug << "Convert bool to int8_t"; break; + case EOpConvBoolToUint8: out.debug << "Convert bool to uint8_t"; break; + case EOpConvBoolToInt16: out.debug << "Convert bool to in16t_t"; break; + case EOpConvBoolToUint16: out.debug << "Convert bool to uint16_t";break; + case EOpConvBoolToInt: out.debug << "Convert bool to int" ; break; + case EOpConvBoolToUint: out.debug << "Convert bool to uint"; break; + case EOpConvBoolToInt64: out.debug << "Convert bool to int64"; break; + case EOpConvBoolToUint64: out.debug << "Convert bool to uint64";break; + case EOpConvBoolToFloat16: out.debug << "Convert bool to float16_t"; break; + case EOpConvBoolToFloat: out.debug << "Convert bool to float"; break; + case EOpConvBoolToDouble: out.debug << "Convert bool to double"; break; + + // int8_t -> (u)int* + case EOpConvInt8ToInt16: out.debug << "Convert int8_t to int16_t";break; + case EOpConvInt8ToInt: out.debug << "Convert int8_t to int"; break; + case EOpConvInt8ToInt64: out.debug << "Convert int8_t to int64"; break; + case EOpConvInt8ToUint8: out.debug << "Convert int8_t to uint8_t";break; + case EOpConvInt8ToUint16: out.debug << "Convert int8_t to uint16_t";break; + case EOpConvInt8ToUint: out.debug << "Convert int8_t to uint"; break; + case EOpConvInt8ToUint64: out.debug << "Convert int8_t to uint64"; break; + + // uint8_t -> (u)int* + case EOpConvUint8ToInt8: out.debug << "Convert uint8_t to int8_t";break; + case EOpConvUint8ToInt16: out.debug << "Convert uint8_t to int16_t";break; + case EOpConvUint8ToInt: out.debug << "Convert uint8_t to int"; break; + case EOpConvUint8ToInt64: out.debug << "Convert uint8_t to int64"; break; + case EOpConvUint8ToUint16: out.debug << "Convert uint8_t to uint16_t";break; + case EOpConvUint8ToUint: out.debug << "Convert uint8_t to uint"; break; + case EOpConvUint8ToUint64: out.debug << "Convert uint8_t to uint64"; break; + + // int8_t -> float* + case EOpConvInt8ToFloat16: out.debug << "Convert int8_t to float16_t";break; + case EOpConvInt8ToFloat: out.debug << "Convert int8_t to float"; break; + case EOpConvInt8ToDouble: out.debug << "Convert int8_t to double"; break; + + // uint8_t -> float* + case EOpConvUint8ToFloat16: out.debug << "Convert uint8_t to float16_t";break; + case EOpConvUint8ToFloat: out.debug << "Convert uint8_t to float"; break; + case EOpConvUint8ToDouble: out.debug << "Convert uint8_t to double"; break; + + // int16_t -> (u)int* + case EOpConvInt16ToInt8: out.debug << "Convert int16_t to int8_t";break; + case EOpConvInt16ToInt: out.debug << "Convert int16_t to int"; break; + case EOpConvInt16ToInt64: out.debug << "Convert int16_t to int64"; break; + case EOpConvInt16ToUint8: out.debug << "Convert int16_t to uint8_t";break; + case EOpConvInt16ToUint16: out.debug << "Convert int16_t to uint16_t";break; + case EOpConvInt16ToUint: out.debug << "Convert int16_t to uint"; break; + case EOpConvInt16ToUint64: out.debug << "Convert int16_t to uint64"; break; + + // int16_t -> float* + case EOpConvInt16ToFloat16: out.debug << "Convert int16_t to float16_t";break; + case EOpConvInt16ToFloat: out.debug << "Convert int16_t to float"; break; + case EOpConvInt16ToDouble: out.debug << "Convert int16_t to double"; break; + + // uint16_t -> (u)int* + case EOpConvUint16ToInt8: out.debug << "Convert uint16_t to int8_t";break; + case EOpConvUint16ToInt16: out.debug << "Convert uint16_t to int16_t";break; + case EOpConvUint16ToInt: out.debug << "Convert uint16_t to int"; break; + case EOpConvUint16ToInt64: out.debug << "Convert uint16_t to int64"; break; + case EOpConvUint16ToUint8: out.debug << "Convert uint16_t to uint8_t";break; + case EOpConvUint16ToUint: out.debug << "Convert uint16_t to uint"; break; + case EOpConvUint16ToUint64: out.debug << "Convert uint16_t to uint64"; break; + + // uint16_t -> float* + case EOpConvUint16ToFloat16: out.debug << "Convert uint16_t to float16_t";break; + case EOpConvUint16ToFloat: out.debug << "Convert uint16_t to float"; break; + case EOpConvUint16ToDouble: out.debug << "Convert uint16_t to double"; break; + + // int32_t -> (u)int* + case EOpConvIntToInt8: out.debug << "Convert int to int8_t";break; + case EOpConvIntToInt16: out.debug << "Convert int to int16_t";break; + case EOpConvIntToInt64: out.debug << "Convert int to int64"; break; + case EOpConvIntToUint8: out.debug << "Convert int to uint8_t";break; + case EOpConvIntToUint16: out.debug << "Convert int to uint16_t";break; + case EOpConvIntToUint: out.debug << "Convert int to uint"; break; + case EOpConvIntToUint64: out.debug << "Convert int to uint64"; break; + + // int32_t -> float* + case EOpConvIntToFloat16: out.debug << "Convert int to float16_t";break; + case EOpConvIntToFloat: out.debug << "Convert int to float"; break; + case EOpConvIntToDouble: out.debug << "Convert int to double"; break; + + // uint32_t -> (u)int* + case EOpConvUintToInt8: out.debug << "Convert uint to int8_t";break; + case EOpConvUintToInt16: out.debug << "Convert uint to int16_t";break; + case EOpConvUintToInt: out.debug << "Convert uint to int";break; + case EOpConvUintToInt64: out.debug << "Convert uint to int64"; break; + case EOpConvUintToUint8: out.debug << "Convert uint to uint8_t";break; + case EOpConvUintToUint16: out.debug << "Convert uint to uint16_t";break; + case EOpConvUintToUint64: out.debug << "Convert uint to uint64"; break; + + // uint32_t -> float* + case EOpConvUintToFloat16: out.debug << "Convert uint to float16_t";break; + case EOpConvUintToFloat: out.debug << "Convert uint to float"; break; + case EOpConvUintToDouble: out.debug << "Convert uint to double"; break; + + // int64 -> (u)int* + case EOpConvInt64ToInt8: out.debug << "Convert int64 to int8_t"; break; + case EOpConvInt64ToInt16: out.debug << "Convert int64 to int16_t"; break; + case EOpConvInt64ToInt: out.debug << "Convert int64 to int"; break; + case EOpConvInt64ToUint8: out.debug << "Convert int64 to uint8_t";break; + case EOpConvInt64ToUint16: out.debug << "Convert int64 to uint16_t";break; + case EOpConvInt64ToUint: out.debug << "Convert int64 to uint"; break; + case EOpConvInt64ToUint64: out.debug << "Convert int64 to uint64"; break; + + // int64 -> float* + case EOpConvInt64ToFloat16: out.debug << "Convert int64 to float16_t";break; + case EOpConvInt64ToFloat: out.debug << "Convert int64 to float"; break; + case EOpConvInt64ToDouble: out.debug << "Convert int64 to double"; break; + + // uint64 -> (u)int* + case EOpConvUint64ToInt8: out.debug << "Convert uint64 to int8_t";break; + case EOpConvUint64ToInt16: out.debug << "Convert uint64 to int16_t";break; + case EOpConvUint64ToInt: out.debug << "Convert uint64 to int"; break; + case EOpConvUint64ToInt64: out.debug << "Convert uint64 to int64"; break; + case EOpConvUint64ToUint8: out.debug << "Convert uint64 to uint8_t";break; + case EOpConvUint64ToUint16: out.debug << "Convert uint64 to uint16"; break; + case EOpConvUint64ToUint: out.debug << "Convert uint64 to uint"; break; + + // uint64 -> float* + case EOpConvUint64ToFloat16: out.debug << "Convert uint64 to float16_t";break; + case EOpConvUint64ToFloat: out.debug << "Convert uint64 to float"; break; + case EOpConvUint64ToDouble: out.debug << "Convert uint64 to double"; break; + + // float16_t -> int* + case EOpConvFloat16ToInt8: out.debug << "Convert float16_t to int8_t"; break; + case EOpConvFloat16ToInt16: out.debug << "Convert float16_t to int16_t"; break; + case EOpConvFloat16ToInt: out.debug << "Convert float16_t to int"; break; + case EOpConvFloat16ToInt64: out.debug << "Convert float16_t to int64"; break; + + // float16_t -> uint* + case EOpConvFloat16ToUint8: out.debug << "Convert float16_t to uint8_t"; break; + case EOpConvFloat16ToUint16: out.debug << "Convert float16_t to uint16_t"; break; + case EOpConvFloat16ToUint: out.debug << "Convert float16_t to uint"; break; + case EOpConvFloat16ToUint64: out.debug << "Convert float16_t to uint64"; break; + + // float16_t -> float* + case EOpConvFloat16ToFloat: out.debug << "Convert float16_t to float"; break; + case EOpConvFloat16ToDouble: out.debug << "Convert float16_t to double"; break; + + // float32 -> float* + case EOpConvFloatToFloat16: out.debug << "Convert float to float16_t"; break; + case EOpConvFloatToDouble: out.debug << "Convert float to double"; break; + + // float32_t -> int* + case EOpConvFloatToInt8: out.debug << "Convert float to int8_t"; break; + case EOpConvFloatToInt16: out.debug << "Convert float to int16_t"; break; + case EOpConvFloatToInt: out.debug << "Convert float to int"; break; + case EOpConvFloatToInt64: out.debug << "Convert float to int64"; break; + + // float32_t -> uint* + case EOpConvFloatToUint8: out.debug << "Convert float to uint8_t"; break; + case EOpConvFloatToUint16: out.debug << "Convert float to uint16_t"; break; + case EOpConvFloatToUint: out.debug << "Convert float to uint"; break; + case EOpConvFloatToUint64: out.debug << "Convert float to uint64"; break; + + // double -> float* + case EOpConvDoubleToFloat16: out.debug << "Convert double to float16_t"; break; + case EOpConvDoubleToFloat: out.debug << "Convert double to float"; break; + + // double -> int* + case EOpConvDoubleToInt8: out.debug << "Convert double to int8_t"; break; + case EOpConvDoubleToInt16: out.debug << "Convert double to int16_t"; break; + case EOpConvDoubleToInt: out.debug << "Convert double to int"; break; + case EOpConvDoubleToInt64: out.debug << "Convert double to int64"; break; + + // float32_t -> uint* + case EOpConvDoubleToUint8: out.debug << "Convert double to uint8_t"; break; + case EOpConvDoubleToUint16: out.debug << "Convert double to uint16_t"; break; + case EOpConvDoubleToUint: out.debug << "Convert double to uint"; break; + case EOpConvDoubleToUint64: out.debug << "Convert double to uint64"; break; + + case EOpConvUint64ToPtr: out.debug << "Convert uint64_t to pointer"; break; + case EOpConvPtrToUint64: out.debug << "Convert pointer to uint64_t"; break; + + case EOpRadians: out.debug << "radians"; break; + case EOpDegrees: out.debug << "degrees"; break; + case EOpSin: out.debug << "sine"; break; + case EOpCos: out.debug << "cosine"; break; + case EOpTan: out.debug << "tangent"; break; + case EOpAsin: out.debug << "arc sine"; break; + case EOpAcos: out.debug << "arc cosine"; break; + case EOpAtan: out.debug << "arc tangent"; break; + case EOpSinh: out.debug << "hyp. sine"; break; + case EOpCosh: out.debug << "hyp. cosine"; break; + case EOpTanh: out.debug << "hyp. tangent"; break; + case EOpAsinh: out.debug << "arc hyp. sine"; break; + case EOpAcosh: out.debug << "arc hyp. cosine"; break; + case EOpAtanh: out.debug << "arc hyp. tangent"; break; + + case EOpExp: out.debug << "exp"; break; + case EOpLog: out.debug << "log"; break; + case EOpExp2: out.debug << "exp2"; break; + case EOpLog2: out.debug << "log2"; break; + case EOpSqrt: out.debug << "sqrt"; break; + case EOpInverseSqrt: out.debug << "inverse sqrt"; break; + + case EOpAbs: out.debug << "Absolute value"; break; + case EOpSign: out.debug << "Sign"; break; + case EOpFloor: out.debug << "Floor"; break; + case EOpTrunc: out.debug << "trunc"; break; + case EOpRound: out.debug << "round"; break; + case EOpRoundEven: out.debug << "roundEven"; break; + case EOpCeil: out.debug << "Ceiling"; break; + case EOpFract: out.debug << "Fraction"; break; + + case EOpIsNan: out.debug << "isnan"; break; + case EOpIsInf: out.debug << "isinf"; break; + + case EOpFloatBitsToInt: out.debug << "floatBitsToInt"; break; + case EOpFloatBitsToUint:out.debug << "floatBitsToUint"; break; + case EOpIntBitsToFloat: out.debug << "intBitsToFloat"; break; + case EOpUintBitsToFloat:out.debug << "uintBitsToFloat"; break; + case EOpDoubleBitsToInt64: out.debug << "doubleBitsToInt64"; break; + case EOpDoubleBitsToUint64: out.debug << "doubleBitsToUint64"; break; + case EOpInt64BitsToDouble: out.debug << "int64BitsToDouble"; break; + case EOpUint64BitsToDouble: out.debug << "uint64BitsToDouble"; break; + case EOpFloat16BitsToInt16: out.debug << "float16BitsToInt16"; break; + case EOpFloat16BitsToUint16: out.debug << "float16BitsToUint16"; break; + case EOpInt16BitsToFloat16: out.debug << "int16BitsToFloat16"; break; + case EOpUint16BitsToFloat16: out.debug << "uint16BitsToFloat16"; break; + + case EOpPackSnorm2x16: out.debug << "packSnorm2x16"; break; + case EOpUnpackSnorm2x16:out.debug << "unpackSnorm2x16"; break; + case EOpPackUnorm2x16: out.debug << "packUnorm2x16"; break; + case EOpUnpackUnorm2x16:out.debug << "unpackUnorm2x16"; break; + case EOpPackHalf2x16: out.debug << "packHalf2x16"; break; + case EOpUnpackHalf2x16: out.debug << "unpackHalf2x16"; break; + case EOpPack16: out.debug << "pack16"; break; + case EOpPack32: out.debug << "pack32"; break; + case EOpPack64: out.debug << "pack64"; break; + case EOpUnpack32: out.debug << "unpack32"; break; + case EOpUnpack16: out.debug << "unpack16"; break; + case EOpUnpack8: out.debug << "unpack8"; break; + + case EOpPackSnorm4x8: out.debug << "PackSnorm4x8"; break; + case EOpUnpackSnorm4x8: out.debug << "UnpackSnorm4x8"; break; + case EOpPackUnorm4x8: out.debug << "PackUnorm4x8"; break; + case EOpUnpackUnorm4x8: out.debug << "UnpackUnorm4x8"; break; + case EOpPackDouble2x32: out.debug << "PackDouble2x32"; break; + case EOpUnpackDouble2x32: out.debug << "UnpackDouble2x32"; break; + + case EOpPackInt2x32: out.debug << "packInt2x32"; break; + case EOpUnpackInt2x32: out.debug << "unpackInt2x32"; break; + case EOpPackUint2x32: out.debug << "packUint2x32"; break; + case EOpUnpackUint2x32: out.debug << "unpackUint2x32"; break; + + case EOpPackInt2x16: out.debug << "packInt2x16"; break; + case EOpUnpackInt2x16: out.debug << "unpackInt2x16"; break; + case EOpPackUint2x16: out.debug << "packUint2x16"; break; + case EOpUnpackUint2x16: out.debug << "unpackUint2x16"; break; + + case EOpPackInt4x16: out.debug << "packInt4x16"; break; + case EOpUnpackInt4x16: out.debug << "unpackInt4x16"; break; + case EOpPackUint4x16: out.debug << "packUint4x16"; break; + case EOpUnpackUint4x16: out.debug << "unpackUint4x16"; break; + case EOpPackFloat2x16: out.debug << "packFloat2x16"; break; + case EOpUnpackFloat2x16: out.debug << "unpackFloat2x16"; break; + + case EOpLength: out.debug << "length"; break; + case EOpNormalize: out.debug << "normalize"; break; + case EOpDPdx: out.debug << "dPdx"; break; + case EOpDPdy: out.debug << "dPdy"; break; + case EOpFwidth: out.debug << "fwidth"; break; + case EOpDPdxFine: out.debug << "dPdxFine"; break; + case EOpDPdyFine: out.debug << "dPdyFine"; break; + case EOpFwidthFine: out.debug << "fwidthFine"; break; + case EOpDPdxCoarse: out.debug << "dPdxCoarse"; break; + case EOpDPdyCoarse: out.debug << "dPdyCoarse"; break; + case EOpFwidthCoarse: out.debug << "fwidthCoarse"; break; + + case EOpInterpolateAtCentroid: out.debug << "interpolateAtCentroid"; break; + + case EOpDeterminant: out.debug << "determinant"; break; + case EOpMatrixInverse: out.debug << "inverse"; break; + case EOpTranspose: out.debug << "transpose"; break; + + case EOpAny: out.debug << "any"; break; + case EOpAll: out.debug << "all"; break; + + case EOpArrayLength: out.debug << "array length"; break; + + case EOpEmitStreamVertex: out.debug << "EmitStreamVertex"; break; + case EOpEndStreamPrimitive: out.debug << "EndStreamPrimitive"; break; + + case EOpAtomicCounterIncrement: out.debug << "AtomicCounterIncrement";break; + case EOpAtomicCounterDecrement: out.debug << "AtomicCounterDecrement";break; + case EOpAtomicCounter: out.debug << "AtomicCounter"; break; + + case EOpTextureQuerySize: out.debug << "textureSize"; break; + case EOpTextureQueryLod: out.debug << "textureQueryLod"; break; + case EOpTextureQueryLevels: out.debug << "textureQueryLevels"; break; + case EOpTextureQuerySamples: out.debug << "textureSamples"; break; + case EOpImageQuerySize: out.debug << "imageQuerySize"; break; + case EOpImageQuerySamples: out.debug << "imageQuerySamples"; break; + case EOpImageLoad: out.debug << "imageLoad"; break; + + case EOpBitFieldReverse: out.debug << "bitFieldReverse"; break; + case EOpBitCount: out.debug << "bitCount"; break; + case EOpFindLSB: out.debug << "findLSB"; break; + case EOpFindMSB: out.debug << "findMSB"; break; + + case EOpCountLeadingZeros: out.debug << "countLeadingZeros"; break; + case EOpCountTrailingZeros: out.debug << "countTrailingZeros"; break; + + case EOpNoise: out.debug << "noise"; break; + + case EOpBallot: out.debug << "ballot"; break; + case EOpReadFirstInvocation: out.debug << "readFirstInvocation"; break; + + case EOpAnyInvocation: out.debug << "anyInvocation"; break; + case EOpAllInvocations: out.debug << "allInvocations"; break; + case EOpAllInvocationsEqual: out.debug << "allInvocationsEqual"; break; + + case EOpSubgroupElect: out.debug << "subgroupElect"; break; + case EOpSubgroupAll: out.debug << "subgroupAll"; break; + case EOpSubgroupAny: out.debug << "subgroupAny"; break; + case EOpSubgroupAllEqual: out.debug << "subgroupAllEqual"; break; + case EOpSubgroupBroadcast: out.debug << "subgroupBroadcast"; break; + case EOpSubgroupBroadcastFirst: out.debug << "subgroupBroadcastFirst"; break; + case EOpSubgroupBallot: out.debug << "subgroupBallot"; break; + case EOpSubgroupInverseBallot: out.debug << "subgroupInverseBallot"; break; + case EOpSubgroupBallotBitExtract: out.debug << "subgroupBallotBitExtract"; break; + case EOpSubgroupBallotBitCount: out.debug << "subgroupBallotBitCount"; break; + case EOpSubgroupBallotInclusiveBitCount: out.debug << "subgroupBallotInclusiveBitCount"; break; + case EOpSubgroupBallotExclusiveBitCount: out.debug << "subgroupBallotExclusiveBitCount"; break; + case EOpSubgroupBallotFindLSB: out.debug << "subgroupBallotFindLSB"; break; + case EOpSubgroupBallotFindMSB: out.debug << "subgroupBallotFindMSB"; break; + case EOpSubgroupShuffle: out.debug << "subgroupShuffle"; break; + case EOpSubgroupShuffleXor: out.debug << "subgroupShuffleXor"; break; + case EOpSubgroupShuffleUp: out.debug << "subgroupShuffleUp"; break; + case EOpSubgroupShuffleDown: out.debug << "subgroupShuffleDown"; break; + case EOpSubgroupAdd: out.debug << "subgroupAdd"; break; + case EOpSubgroupMul: out.debug << "subgroupMul"; break; + case EOpSubgroupMin: out.debug << "subgroupMin"; break; + case EOpSubgroupMax: out.debug << "subgroupMax"; break; + case EOpSubgroupAnd: out.debug << "subgroupAnd"; break; + case EOpSubgroupOr: out.debug << "subgroupOr"; break; + case EOpSubgroupXor: out.debug << "subgroupXor"; break; + case EOpSubgroupInclusiveAdd: out.debug << "subgroupInclusiveAdd"; break; + case EOpSubgroupInclusiveMul: out.debug << "subgroupInclusiveMul"; break; + case EOpSubgroupInclusiveMin: out.debug << "subgroupInclusiveMin"; break; + case EOpSubgroupInclusiveMax: out.debug << "subgroupInclusiveMax"; break; + case EOpSubgroupInclusiveAnd: out.debug << "subgroupInclusiveAnd"; break; + case EOpSubgroupInclusiveOr: out.debug << "subgroupInclusiveOr"; break; + case EOpSubgroupInclusiveXor: out.debug << "subgroupInclusiveXor"; break; + case EOpSubgroupExclusiveAdd: out.debug << "subgroupExclusiveAdd"; break; + case EOpSubgroupExclusiveMul: out.debug << "subgroupExclusiveMul"; break; + case EOpSubgroupExclusiveMin: out.debug << "subgroupExclusiveMin"; break; + case EOpSubgroupExclusiveMax: out.debug << "subgroupExclusiveMax"; break; + case EOpSubgroupExclusiveAnd: out.debug << "subgroupExclusiveAnd"; break; + case EOpSubgroupExclusiveOr: out.debug << "subgroupExclusiveOr"; break; + case EOpSubgroupExclusiveXor: out.debug << "subgroupExclusiveXor"; break; + case EOpSubgroupClusteredAdd: out.debug << "subgroupClusteredAdd"; break; + case EOpSubgroupClusteredMul: out.debug << "subgroupClusteredMul"; break; + case EOpSubgroupClusteredMin: out.debug << "subgroupClusteredMin"; break; + case EOpSubgroupClusteredMax: out.debug << "subgroupClusteredMax"; break; + case EOpSubgroupClusteredAnd: out.debug << "subgroupClusteredAnd"; break; + case EOpSubgroupClusteredOr: out.debug << "subgroupClusteredOr"; break; + case EOpSubgroupClusteredXor: out.debug << "subgroupClusteredXor"; break; + case EOpSubgroupQuadBroadcast: out.debug << "subgroupQuadBroadcast"; break; + case EOpSubgroupQuadSwapHorizontal: out.debug << "subgroupQuadSwapHorizontal"; break; + case EOpSubgroupQuadSwapVertical: out.debug << "subgroupQuadSwapVertical"; break; + case EOpSubgroupQuadSwapDiagonal: out.debug << "subgroupQuadSwapDiagonal"; break; + + case EOpSubgroupPartition: out.debug << "subgroupPartitionNV"; break; + case EOpSubgroupPartitionedAdd: out.debug << "subgroupPartitionedAddNV"; break; + case EOpSubgroupPartitionedMul: out.debug << "subgroupPartitionedMulNV"; break; + case EOpSubgroupPartitionedMin: out.debug << "subgroupPartitionedMinNV"; break; + case EOpSubgroupPartitionedMax: out.debug << "subgroupPartitionedMaxNV"; break; + case EOpSubgroupPartitionedAnd: out.debug << "subgroupPartitionedAndNV"; break; + case EOpSubgroupPartitionedOr: out.debug << "subgroupPartitionedOrNV"; break; + case EOpSubgroupPartitionedXor: out.debug << "subgroupPartitionedXorNV"; break; + case EOpSubgroupPartitionedInclusiveAdd: out.debug << "subgroupPartitionedInclusiveAddNV"; break; + case EOpSubgroupPartitionedInclusiveMul: out.debug << "subgroupPartitionedInclusiveMulNV"; break; + case EOpSubgroupPartitionedInclusiveMin: out.debug << "subgroupPartitionedInclusiveMinNV"; break; + case EOpSubgroupPartitionedInclusiveMax: out.debug << "subgroupPartitionedInclusiveMaxNV"; break; + case EOpSubgroupPartitionedInclusiveAnd: out.debug << "subgroupPartitionedInclusiveAndNV"; break; + case EOpSubgroupPartitionedInclusiveOr: out.debug << "subgroupPartitionedInclusiveOrNV"; break; + case EOpSubgroupPartitionedInclusiveXor: out.debug << "subgroupPartitionedInclusiveXorNV"; break; + case EOpSubgroupPartitionedExclusiveAdd: out.debug << "subgroupPartitionedExclusiveAddNV"; break; + case EOpSubgroupPartitionedExclusiveMul: out.debug << "subgroupPartitionedExclusiveMulNV"; break; + case EOpSubgroupPartitionedExclusiveMin: out.debug << "subgroupPartitionedExclusiveMinNV"; break; + case EOpSubgroupPartitionedExclusiveMax: out.debug << "subgroupPartitionedExclusiveMaxNV"; break; + case EOpSubgroupPartitionedExclusiveAnd: out.debug << "subgroupPartitionedExclusiveAndNV"; break; + case EOpSubgroupPartitionedExclusiveOr: out.debug << "subgroupPartitionedExclusiveOrNV"; break; + case EOpSubgroupPartitionedExclusiveXor: out.debug << "subgroupPartitionedExclusiveXorNV"; break; + + case EOpClip: out.debug << "clip"; break; + case EOpIsFinite: out.debug << "isfinite"; break; + case EOpLog10: out.debug << "log10"; break; + case EOpRcp: out.debug << "rcp"; break; + case EOpSaturate: out.debug << "saturate"; break; + + case EOpSparseTexelsResident: out.debug << "sparseTexelsResident"; break; + + case EOpMinInvocations: out.debug << "minInvocations"; break; + case EOpMaxInvocations: out.debug << "maxInvocations"; break; + case EOpAddInvocations: out.debug << "addInvocations"; break; + case EOpMinInvocationsNonUniform: out.debug << "minInvocationsNonUniform"; break; + case EOpMaxInvocationsNonUniform: out.debug << "maxInvocationsNonUniform"; break; + case EOpAddInvocationsNonUniform: out.debug << "addInvocationsNonUniform"; break; + + case EOpMinInvocationsInclusiveScan: out.debug << "minInvocationsInclusiveScan"; break; + case EOpMaxInvocationsInclusiveScan: out.debug << "maxInvocationsInclusiveScan"; break; + case EOpAddInvocationsInclusiveScan: out.debug << "addInvocationsInclusiveScan"; break; + case EOpMinInvocationsInclusiveScanNonUniform: out.debug << "minInvocationsInclusiveScanNonUniform"; break; + case EOpMaxInvocationsInclusiveScanNonUniform: out.debug << "maxInvocationsInclusiveScanNonUniform"; break; + case EOpAddInvocationsInclusiveScanNonUniform: out.debug << "addInvocationsInclusiveScanNonUniform"; break; + + case EOpMinInvocationsExclusiveScan: out.debug << "minInvocationsExclusiveScan"; break; + case EOpMaxInvocationsExclusiveScan: out.debug << "maxInvocationsExclusiveScan"; break; + case EOpAddInvocationsExclusiveScan: out.debug << "addInvocationsExclusiveScan"; break; + case EOpMinInvocationsExclusiveScanNonUniform: out.debug << "minInvocationsExclusiveScanNonUniform"; break; + case EOpMaxInvocationsExclusiveScanNonUniform: out.debug << "maxInvocationsExclusiveScanNonUniform"; break; + case EOpAddInvocationsExclusiveScanNonUniform: out.debug << "addInvocationsExclusiveScanNonUniform"; break; + + case EOpMbcnt: out.debug << "mbcnt"; break; + + case EOpFragmentMaskFetch: out.debug << "fragmentMaskFetchAMD"; break; + case EOpFragmentFetch: out.debug << "fragmentFetchAMD"; break; + + case EOpCubeFaceIndex: out.debug << "cubeFaceIndex"; break; + case EOpCubeFaceCoord: out.debug << "cubeFaceCoord"; break; + + case EOpSubpassLoad: out.debug << "subpassLoad"; break; + case EOpSubpassLoadMS: out.debug << "subpassLoadMS"; break; + + case EOpConstructReference: out.debug << "Construct reference type"; break; + + default: out.debug.message(EPrefixError, "Bad unary op"); + } + + out.debug << " (" << node->getCompleteString() << ")"; + + out.debug << "\n"; + + return true; +} + +bool TOutputTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node) +{ + TInfoSink& out = infoSink; + + if (node->getOp() == EOpNull) { + out.debug.message(EPrefixError, "node is still EOpNull!"); + return true; + } + + OutputTreeText(out, node, depth); + + switch (node->getOp()) { + case EOpSequence: out.debug << "Sequence\n"; return true; + case EOpLinkerObjects: out.debug << "Linker Objects\n"; return true; + case EOpComma: out.debug << "Comma"; break; + case EOpFunction: out.debug << "Function Definition: " << node->getName(); break; + case EOpFunctionCall: out.debug << "Function Call: " << node->getName(); break; + case EOpParameters: out.debug << "Function Parameters: "; break; + + case EOpConstructFloat: out.debug << "Construct float"; break; + case EOpConstructDouble:out.debug << "Construct double"; break; + + case EOpConstructVec2: out.debug << "Construct vec2"; break; + case EOpConstructVec3: out.debug << "Construct vec3"; break; + case EOpConstructVec4: out.debug << "Construct vec4"; break; + case EOpConstructDVec2: out.debug << "Construct dvec2"; break; + case EOpConstructDVec3: out.debug << "Construct dvec3"; break; + case EOpConstructDVec4: out.debug << "Construct dvec4"; break; + case EOpConstructBool: out.debug << "Construct bool"; break; + case EOpConstructBVec2: out.debug << "Construct bvec2"; break; + case EOpConstructBVec3: out.debug << "Construct bvec3"; break; + case EOpConstructBVec4: out.debug << "Construct bvec4"; break; + case EOpConstructInt8: out.debug << "Construct int8_t"; break; + case EOpConstructI8Vec2: out.debug << "Construct i8vec2"; break; + case EOpConstructI8Vec3: out.debug << "Construct i8vec3"; break; + case EOpConstructI8Vec4: out.debug << "Construct i8vec4"; break; + case EOpConstructInt: out.debug << "Construct int"; break; + case EOpConstructIVec2: out.debug << "Construct ivec2"; break; + case EOpConstructIVec3: out.debug << "Construct ivec3"; break; + case EOpConstructIVec4: out.debug << "Construct ivec4"; break; + case EOpConstructUint8: out.debug << "Construct uint8_t"; break; + case EOpConstructU8Vec2: out.debug << "Construct u8vec2"; break; + case EOpConstructU8Vec3: out.debug << "Construct u8vec3"; break; + case EOpConstructU8Vec4: out.debug << "Construct u8vec4"; break; + case EOpConstructUint: out.debug << "Construct uint"; break; + case EOpConstructUVec2: out.debug << "Construct uvec2"; break; + case EOpConstructUVec3: out.debug << "Construct uvec3"; break; + case EOpConstructUVec4: out.debug << "Construct uvec4"; break; + case EOpConstructInt64: out.debug << "Construct int64"; break; + case EOpConstructI64Vec2: out.debug << "Construct i64vec2"; break; + case EOpConstructI64Vec3: out.debug << "Construct i64vec3"; break; + case EOpConstructI64Vec4: out.debug << "Construct i64vec4"; break; + case EOpConstructUint64: out.debug << "Construct uint64"; break; + case EOpConstructU64Vec2: out.debug << "Construct u64vec2"; break; + case EOpConstructU64Vec3: out.debug << "Construct u64vec3"; break; + case EOpConstructU64Vec4: out.debug << "Construct u64vec4"; break; + case EOpConstructInt16: out.debug << "Construct int16_t"; break; + case EOpConstructI16Vec2: out.debug << "Construct i16vec2"; break; + case EOpConstructI16Vec3: out.debug << "Construct i16vec3"; break; + case EOpConstructI16Vec4: out.debug << "Construct i16vec4"; break; + case EOpConstructUint16: out.debug << "Construct uint16_t"; break; + case EOpConstructU16Vec2: out.debug << "Construct u16vec2"; break; + case EOpConstructU16Vec3: out.debug << "Construct u16vec3"; break; + case EOpConstructU16Vec4: out.debug << "Construct u16vec4"; break; + case EOpConstructMat2x2: out.debug << "Construct mat2"; break; + case EOpConstructMat2x3: out.debug << "Construct mat2x3"; break; + case EOpConstructMat2x4: out.debug << "Construct mat2x4"; break; + case EOpConstructMat3x2: out.debug << "Construct mat3x2"; break; + case EOpConstructMat3x3: out.debug << "Construct mat3"; break; + case EOpConstructMat3x4: out.debug << "Construct mat3x4"; break; + case EOpConstructMat4x2: out.debug << "Construct mat4x2"; break; + case EOpConstructMat4x3: out.debug << "Construct mat4x3"; break; + case EOpConstructMat4x4: out.debug << "Construct mat4"; break; + case EOpConstructDMat2x2: out.debug << "Construct dmat2"; break; + case EOpConstructDMat2x3: out.debug << "Construct dmat2x3"; break; + case EOpConstructDMat2x4: out.debug << "Construct dmat2x4"; break; + case EOpConstructDMat3x2: out.debug << "Construct dmat3x2"; break; + case EOpConstructDMat3x3: out.debug << "Construct dmat3"; break; + case EOpConstructDMat3x4: out.debug << "Construct dmat3x4"; break; + case EOpConstructDMat4x2: out.debug << "Construct dmat4x2"; break; + case EOpConstructDMat4x3: out.debug << "Construct dmat4x3"; break; + case EOpConstructDMat4x4: out.debug << "Construct dmat4"; break; + case EOpConstructIMat2x2: out.debug << "Construct imat2"; break; + case EOpConstructIMat2x3: out.debug << "Construct imat2x3"; break; + case EOpConstructIMat2x4: out.debug << "Construct imat2x4"; break; + case EOpConstructIMat3x2: out.debug << "Construct imat3x2"; break; + case EOpConstructIMat3x3: out.debug << "Construct imat3"; break; + case EOpConstructIMat3x4: out.debug << "Construct imat3x4"; break; + case EOpConstructIMat4x2: out.debug << "Construct imat4x2"; break; + case EOpConstructIMat4x3: out.debug << "Construct imat4x3"; break; + case EOpConstructIMat4x4: out.debug << "Construct imat4"; break; + case EOpConstructUMat2x2: out.debug << "Construct umat2"; break; + case EOpConstructUMat2x3: out.debug << "Construct umat2x3"; break; + case EOpConstructUMat2x4: out.debug << "Construct umat2x4"; break; + case EOpConstructUMat3x2: out.debug << "Construct umat3x2"; break; + case EOpConstructUMat3x3: out.debug << "Construct umat3"; break; + case EOpConstructUMat3x4: out.debug << "Construct umat3x4"; break; + case EOpConstructUMat4x2: out.debug << "Construct umat4x2"; break; + case EOpConstructUMat4x3: out.debug << "Construct umat4x3"; break; + case EOpConstructUMat4x4: out.debug << "Construct umat4"; break; + case EOpConstructBMat2x2: out.debug << "Construct bmat2"; break; + case EOpConstructBMat2x3: out.debug << "Construct bmat2x3"; break; + case EOpConstructBMat2x4: out.debug << "Construct bmat2x4"; break; + case EOpConstructBMat3x2: out.debug << "Construct bmat3x2"; break; + case EOpConstructBMat3x3: out.debug << "Construct bmat3"; break; + case EOpConstructBMat3x4: out.debug << "Construct bmat3x4"; break; + case EOpConstructBMat4x2: out.debug << "Construct bmat4x2"; break; + case EOpConstructBMat4x3: out.debug << "Construct bmat4x3"; break; + case EOpConstructBMat4x4: out.debug << "Construct bmat4"; break; + case EOpConstructFloat16: out.debug << "Construct float16_t"; break; + case EOpConstructF16Vec2: out.debug << "Construct f16vec2"; break; + case EOpConstructF16Vec3: out.debug << "Construct f16vec3"; break; + case EOpConstructF16Vec4: out.debug << "Construct f16vec4"; break; + case EOpConstructF16Mat2x2: out.debug << "Construct f16mat2"; break; + case EOpConstructF16Mat2x3: out.debug << "Construct f16mat2x3"; break; + case EOpConstructF16Mat2x4: out.debug << "Construct f16mat2x4"; break; + case EOpConstructF16Mat3x2: out.debug << "Construct f16mat3x2"; break; + case EOpConstructF16Mat3x3: out.debug << "Construct f16mat3"; break; + case EOpConstructF16Mat3x4: out.debug << "Construct f16mat3x4"; break; + case EOpConstructF16Mat4x2: out.debug << "Construct f16mat4x2"; break; + case EOpConstructF16Mat4x3: out.debug << "Construct f16mat4x3"; break; + case EOpConstructF16Mat4x4: out.debug << "Construct f16mat4"; break; + case EOpConstructStruct: out.debug << "Construct structure"; break; + case EOpConstructTextureSampler: out.debug << "Construct combined texture-sampler"; break; + case EOpConstructReference: out.debug << "Construct reference"; break; + case EOpConstructCooperativeMatrix: out.debug << "Construct cooperative matrix"; break; + + case EOpLessThan: out.debug << "Compare Less Than"; break; + case EOpGreaterThan: out.debug << "Compare Greater Than"; break; + case EOpLessThanEqual: out.debug << "Compare Less Than or Equal"; break; + case EOpGreaterThanEqual: out.debug << "Compare Greater Than or Equal"; break; + case EOpVectorEqual: out.debug << "Equal"; break; + case EOpVectorNotEqual: out.debug << "NotEqual"; break; + + case EOpMod: out.debug << "mod"; break; + case EOpModf: out.debug << "modf"; break; + case EOpPow: out.debug << "pow"; break; + + case EOpAtan: out.debug << "arc tangent"; break; + + case EOpMin: out.debug << "min"; break; + case EOpMax: out.debug << "max"; break; + case EOpClamp: out.debug << "clamp"; break; + case EOpMix: out.debug << "mix"; break; + case EOpStep: out.debug << "step"; break; + case EOpSmoothStep: out.debug << "smoothstep"; break; + + case EOpDistance: out.debug << "distance"; break; + case EOpDot: out.debug << "dot-product"; break; + case EOpCross: out.debug << "cross-product"; break; + case EOpFaceForward: out.debug << "face-forward"; break; + case EOpReflect: out.debug << "reflect"; break; + case EOpRefract: out.debug << "refract"; break; + case EOpMul: out.debug << "component-wise multiply"; break; + case EOpOuterProduct: out.debug << "outer product"; break; + + case EOpEmitVertex: out.debug << "EmitVertex"; break; + case EOpEndPrimitive: out.debug << "EndPrimitive"; break; + + case EOpBarrier: out.debug << "Barrier"; break; + case EOpMemoryBarrier: out.debug << "MemoryBarrier"; break; + case EOpMemoryBarrierAtomicCounter: out.debug << "MemoryBarrierAtomicCounter"; break; + case EOpMemoryBarrierBuffer: out.debug << "MemoryBarrierBuffer"; break; + case EOpMemoryBarrierImage: out.debug << "MemoryBarrierImage"; break; + case EOpMemoryBarrierShared: out.debug << "MemoryBarrierShared"; break; + case EOpGroupMemoryBarrier: out.debug << "GroupMemoryBarrier"; break; + + case EOpReadInvocation: out.debug << "readInvocation"; break; + + case EOpSwizzleInvocations: out.debug << "swizzleInvocations"; break; + case EOpSwizzleInvocationsMasked: out.debug << "swizzleInvocationsMasked"; break; + case EOpWriteInvocation: out.debug << "writeInvocation"; break; + + case EOpMin3: out.debug << "min3"; break; + case EOpMax3: out.debug << "max3"; break; + case EOpMid3: out.debug << "mid3"; break; + case EOpTime: out.debug << "time"; break; + + case EOpAtomicAdd: out.debug << "AtomicAdd"; break; + case EOpAtomicMin: out.debug << "AtomicMin"; break; + case EOpAtomicMax: out.debug << "AtomicMax"; break; + case EOpAtomicAnd: out.debug << "AtomicAnd"; break; + case EOpAtomicOr: out.debug << "AtomicOr"; break; + case EOpAtomicXor: out.debug << "AtomicXor"; break; + case EOpAtomicExchange: out.debug << "AtomicExchange"; break; + case EOpAtomicCompSwap: out.debug << "AtomicCompSwap"; break; + case EOpAtomicLoad: out.debug << "AtomicLoad"; break; + case EOpAtomicStore: out.debug << "AtomicStore"; break; + + case EOpAtomicCounterAdd: out.debug << "AtomicCounterAdd"; break; + case EOpAtomicCounterSubtract: out.debug << "AtomicCounterSubtract"; break; + case EOpAtomicCounterMin: out.debug << "AtomicCounterMin"; break; + case EOpAtomicCounterMax: out.debug << "AtomicCounterMax"; break; + case EOpAtomicCounterAnd: out.debug << "AtomicCounterAnd"; break; + case EOpAtomicCounterOr: out.debug << "AtomicCounterOr"; break; + case EOpAtomicCounterXor: out.debug << "AtomicCounterXor"; break; + case EOpAtomicCounterExchange: out.debug << "AtomicCounterExchange"; break; + case EOpAtomicCounterCompSwap: out.debug << "AtomicCounterCompSwap"; break; + + case EOpImageQuerySize: out.debug << "imageQuerySize"; break; + case EOpImageQuerySamples: out.debug << "imageQuerySamples"; break; + case EOpImageLoad: out.debug << "imageLoad"; break; + case EOpImageStore: out.debug << "imageStore"; break; + case EOpImageAtomicAdd: out.debug << "imageAtomicAdd"; break; + case EOpImageAtomicMin: out.debug << "imageAtomicMin"; break; + case EOpImageAtomicMax: out.debug << "imageAtomicMax"; break; + case EOpImageAtomicAnd: out.debug << "imageAtomicAnd"; break; + case EOpImageAtomicOr: out.debug << "imageAtomicOr"; break; + case EOpImageAtomicXor: out.debug << "imageAtomicXor"; break; + case EOpImageAtomicExchange: out.debug << "imageAtomicExchange"; break; + case EOpImageAtomicCompSwap: out.debug << "imageAtomicCompSwap"; break; + case EOpImageAtomicLoad: out.debug << "imageAtomicLoad"; break; + case EOpImageAtomicStore: out.debug << "imageAtomicStore"; break; + case EOpImageLoadLod: out.debug << "imageLoadLod"; break; + case EOpImageStoreLod: out.debug << "imageStoreLod"; break; + + case EOpTextureQuerySize: out.debug << "textureSize"; break; + case EOpTextureQueryLod: out.debug << "textureQueryLod"; break; + case EOpTextureQueryLevels: out.debug << "textureQueryLevels"; break; + case EOpTextureQuerySamples: out.debug << "textureSamples"; break; + case EOpTexture: out.debug << "texture"; break; + case EOpTextureProj: out.debug << "textureProj"; break; + case EOpTextureLod: out.debug << "textureLod"; break; + case EOpTextureOffset: out.debug << "textureOffset"; break; + case EOpTextureFetch: out.debug << "textureFetch"; break; + case EOpTextureFetchOffset: out.debug << "textureFetchOffset"; break; + case EOpTextureProjOffset: out.debug << "textureProjOffset"; break; + case EOpTextureLodOffset: out.debug << "textureLodOffset"; break; + case EOpTextureProjLod: out.debug << "textureProjLod"; break; + case EOpTextureProjLodOffset: out.debug << "textureProjLodOffset"; break; + case EOpTextureGrad: out.debug << "textureGrad"; break; + case EOpTextureGradOffset: out.debug << "textureGradOffset"; break; + case EOpTextureProjGrad: out.debug << "textureProjGrad"; break; + case EOpTextureProjGradOffset: out.debug << "textureProjGradOffset"; break; + case EOpTextureGather: out.debug << "textureGather"; break; + case EOpTextureGatherOffset: out.debug << "textureGatherOffset"; break; + case EOpTextureGatherOffsets: out.debug << "textureGatherOffsets"; break; + case EOpTextureClamp: out.debug << "textureClamp"; break; + case EOpTextureOffsetClamp: out.debug << "textureOffsetClamp"; break; + case EOpTextureGradClamp: out.debug << "textureGradClamp"; break; + case EOpTextureGradOffsetClamp: out.debug << "textureGradOffsetClamp"; break; + case EOpTextureGatherLod: out.debug << "textureGatherLod"; break; + case EOpTextureGatherLodOffset: out.debug << "textureGatherLodOffset"; break; + case EOpTextureGatherLodOffsets: out.debug << "textureGatherLodOffsets"; break; + + case EOpSparseTexture: out.debug << "sparseTexture"; break; + case EOpSparseTextureOffset: out.debug << "sparseTextureOffset"; break; + case EOpSparseTextureLod: out.debug << "sparseTextureLod"; break; + case EOpSparseTextureLodOffset: out.debug << "sparseTextureLodOffset"; break; + case EOpSparseTextureFetch: out.debug << "sparseTexelFetch"; break; + case EOpSparseTextureFetchOffset: out.debug << "sparseTexelFetchOffset"; break; + case EOpSparseTextureGrad: out.debug << "sparseTextureGrad"; break; + case EOpSparseTextureGradOffset: out.debug << "sparseTextureGradOffset"; break; + case EOpSparseTextureGather: out.debug << "sparseTextureGather"; break; + case EOpSparseTextureGatherOffset: out.debug << "sparseTextureGatherOffset"; break; + case EOpSparseTextureGatherOffsets: out.debug << "sparseTextureGatherOffsets"; break; + case EOpSparseImageLoad: out.debug << "sparseImageLoad"; break; + case EOpSparseTextureClamp: out.debug << "sparseTextureClamp"; break; + case EOpSparseTextureOffsetClamp: out.debug << "sparseTextureOffsetClamp"; break; + case EOpSparseTextureGradClamp: out.debug << "sparseTextureGradClamp"; break; + case EOpSparseTextureGradOffsetClamp: out.debug << "sparseTextureGradOffsetClam"; break; + case EOpSparseTextureGatherLod: out.debug << "sparseTextureGatherLod"; break; + case EOpSparseTextureGatherLodOffset: out.debug << "sparseTextureGatherLodOffset"; break; + case EOpSparseTextureGatherLodOffsets: out.debug << "sparseTextureGatherLodOffsets"; break; + case EOpSparseImageLoadLod: out.debug << "sparseImageLoadLod"; break; + case EOpImageSampleFootprintNV: out.debug << "imageSampleFootprintNV"; break; + case EOpImageSampleFootprintClampNV: out.debug << "imageSampleFootprintClampNV"; break; + case EOpImageSampleFootprintLodNV: out.debug << "imageSampleFootprintLodNV"; break; + case EOpImageSampleFootprintGradNV: out.debug << "imageSampleFootprintGradNV"; break; + case EOpImageSampleFootprintGradClampNV: out.debug << "mageSampleFootprintGradClampNV"; break; + case EOpAddCarry: out.debug << "addCarry"; break; + case EOpSubBorrow: out.debug << "subBorrow"; break; + case EOpUMulExtended: out.debug << "uMulExtended"; break; + case EOpIMulExtended: out.debug << "iMulExtended"; break; + case EOpBitfieldExtract: out.debug << "bitfieldExtract"; break; + case EOpBitfieldInsert: out.debug << "bitfieldInsert"; break; + + case EOpFma: out.debug << "fma"; break; + case EOpFrexp: out.debug << "frexp"; break; + case EOpLdexp: out.debug << "ldexp"; break; + + case EOpInterpolateAtSample: out.debug << "interpolateAtSample"; break; + case EOpInterpolateAtOffset: out.debug << "interpolateAtOffset"; break; + case EOpInterpolateAtVertex: out.debug << "interpolateAtVertex"; break; + + case EOpSinCos: out.debug << "sincos"; break; + case EOpGenMul: out.debug << "mul"; break; + + case EOpAllMemoryBarrierWithGroupSync: out.debug << "AllMemoryBarrierWithGroupSync"; break; + case EOpDeviceMemoryBarrier: out.debug << "DeviceMemoryBarrier"; break; + case EOpDeviceMemoryBarrierWithGroupSync: out.debug << "DeviceMemoryBarrierWithGroupSync"; break; + case EOpWorkgroupMemoryBarrier: out.debug << "WorkgroupMemoryBarrier"; break; + case EOpWorkgroupMemoryBarrierWithGroupSync: out.debug << "WorkgroupMemoryBarrierWithGroupSync"; break; + + case EOpSubgroupBarrier: out.debug << "subgroupBarrier"; break; + case EOpSubgroupMemoryBarrier: out.debug << "subgroupMemoryBarrier"; break; + case EOpSubgroupMemoryBarrierBuffer: out.debug << "subgroupMemoryBarrierBuffer"; break; + case EOpSubgroupMemoryBarrierImage: out.debug << "subgroupMemoryBarrierImage"; break; + case EOpSubgroupMemoryBarrierShared: out.debug << "subgroupMemoryBarrierShared"; break; + case EOpSubgroupElect: out.debug << "subgroupElect"; break; + case EOpSubgroupAll: out.debug << "subgroupAll"; break; + case EOpSubgroupAny: out.debug << "subgroupAny"; break; + case EOpSubgroupAllEqual: out.debug << "subgroupAllEqual"; break; + case EOpSubgroupBroadcast: out.debug << "subgroupBroadcast"; break; + case EOpSubgroupBroadcastFirst: out.debug << "subgroupBroadcastFirst"; break; + case EOpSubgroupBallot: out.debug << "subgroupBallot"; break; + case EOpSubgroupInverseBallot: out.debug << "subgroupInverseBallot"; break; + case EOpSubgroupBallotBitExtract: out.debug << "subgroupBallotBitExtract"; break; + case EOpSubgroupBallotBitCount: out.debug << "subgroupBallotBitCount"; break; + case EOpSubgroupBallotInclusiveBitCount: out.debug << "subgroupBallotInclusiveBitCount"; break; + case EOpSubgroupBallotExclusiveBitCount: out.debug << "subgroupBallotExclusiveBitCount"; break; + case EOpSubgroupBallotFindLSB: out.debug << "subgroupBallotFindLSB"; break; + case EOpSubgroupBallotFindMSB: out.debug << "subgroupBallotFindMSB"; break; + case EOpSubgroupShuffle: out.debug << "subgroupShuffle"; break; + case EOpSubgroupShuffleXor: out.debug << "subgroupShuffleXor"; break; + case EOpSubgroupShuffleUp: out.debug << "subgroupShuffleUp"; break; + case EOpSubgroupShuffleDown: out.debug << "subgroupShuffleDown"; break; + case EOpSubgroupAdd: out.debug << "subgroupAdd"; break; + case EOpSubgroupMul: out.debug << "subgroupMul"; break; + case EOpSubgroupMin: out.debug << "subgroupMin"; break; + case EOpSubgroupMax: out.debug << "subgroupMax"; break; + case EOpSubgroupAnd: out.debug << "subgroupAnd"; break; + case EOpSubgroupOr: out.debug << "subgroupOr"; break; + case EOpSubgroupXor: out.debug << "subgroupXor"; break; + case EOpSubgroupInclusiveAdd: out.debug << "subgroupInclusiveAdd"; break; + case EOpSubgroupInclusiveMul: out.debug << "subgroupInclusiveMul"; break; + case EOpSubgroupInclusiveMin: out.debug << "subgroupInclusiveMin"; break; + case EOpSubgroupInclusiveMax: out.debug << "subgroupInclusiveMax"; break; + case EOpSubgroupInclusiveAnd: out.debug << "subgroupInclusiveAnd"; break; + case EOpSubgroupInclusiveOr: out.debug << "subgroupInclusiveOr"; break; + case EOpSubgroupInclusiveXor: out.debug << "subgroupInclusiveXor"; break; + case EOpSubgroupExclusiveAdd: out.debug << "subgroupExclusiveAdd"; break; + case EOpSubgroupExclusiveMul: out.debug << "subgroupExclusiveMul"; break; + case EOpSubgroupExclusiveMin: out.debug << "subgroupExclusiveMin"; break; + case EOpSubgroupExclusiveMax: out.debug << "subgroupExclusiveMax"; break; + case EOpSubgroupExclusiveAnd: out.debug << "subgroupExclusiveAnd"; break; + case EOpSubgroupExclusiveOr: out.debug << "subgroupExclusiveOr"; break; + case EOpSubgroupExclusiveXor: out.debug << "subgroupExclusiveXor"; break; + case EOpSubgroupClusteredAdd: out.debug << "subgroupClusteredAdd"; break; + case EOpSubgroupClusteredMul: out.debug << "subgroupClusteredMul"; break; + case EOpSubgroupClusteredMin: out.debug << "subgroupClusteredMin"; break; + case EOpSubgroupClusteredMax: out.debug << "subgroupClusteredMax"; break; + case EOpSubgroupClusteredAnd: out.debug << "subgroupClusteredAnd"; break; + case EOpSubgroupClusteredOr: out.debug << "subgroupClusteredOr"; break; + case EOpSubgroupClusteredXor: out.debug << "subgroupClusteredXor"; break; + case EOpSubgroupQuadBroadcast: out.debug << "subgroupQuadBroadcast"; break; + case EOpSubgroupQuadSwapHorizontal: out.debug << "subgroupQuadSwapHorizontal"; break; + case EOpSubgroupQuadSwapVertical: out.debug << "subgroupQuadSwapVertical"; break; + case EOpSubgroupQuadSwapDiagonal: out.debug << "subgroupQuadSwapDiagonal"; break; + + case EOpSubgroupPartition: out.debug << "subgroupPartitionNV"; break; + case EOpSubgroupPartitionedAdd: out.debug << "subgroupPartitionedAddNV"; break; + case EOpSubgroupPartitionedMul: out.debug << "subgroupPartitionedMulNV"; break; + case EOpSubgroupPartitionedMin: out.debug << "subgroupPartitionedMinNV"; break; + case EOpSubgroupPartitionedMax: out.debug << "subgroupPartitionedMaxNV"; break; + case EOpSubgroupPartitionedAnd: out.debug << "subgroupPartitionedAndNV"; break; + case EOpSubgroupPartitionedOr: out.debug << "subgroupPartitionedOrNV"; break; + case EOpSubgroupPartitionedXor: out.debug << "subgroupPartitionedXorNV"; break; + case EOpSubgroupPartitionedInclusiveAdd: out.debug << "subgroupPartitionedInclusiveAddNV"; break; + case EOpSubgroupPartitionedInclusiveMul: out.debug << "subgroupPartitionedInclusiveMulNV"; break; + case EOpSubgroupPartitionedInclusiveMin: out.debug << "subgroupPartitionedInclusiveMinNV"; break; + case EOpSubgroupPartitionedInclusiveMax: out.debug << "subgroupPartitionedInclusiveMaxNV"; break; + case EOpSubgroupPartitionedInclusiveAnd: out.debug << "subgroupPartitionedInclusiveAndNV"; break; + case EOpSubgroupPartitionedInclusiveOr: out.debug << "subgroupPartitionedInclusiveOrNV"; break; + case EOpSubgroupPartitionedInclusiveXor: out.debug << "subgroupPartitionedInclusiveXorNV"; break; + case EOpSubgroupPartitionedExclusiveAdd: out.debug << "subgroupPartitionedExclusiveAddNV"; break; + case EOpSubgroupPartitionedExclusiveMul: out.debug << "subgroupPartitionedExclusiveMulNV"; break; + case EOpSubgroupPartitionedExclusiveMin: out.debug << "subgroupPartitionedExclusiveMinNV"; break; + case EOpSubgroupPartitionedExclusiveMax: out.debug << "subgroupPartitionedExclusiveMaxNV"; break; + case EOpSubgroupPartitionedExclusiveAnd: out.debug << "subgroupPartitionedExclusiveAndNV"; break; + case EOpSubgroupPartitionedExclusiveOr: out.debug << "subgroupPartitionedExclusiveOrNV"; break; + case EOpSubgroupPartitionedExclusiveXor: out.debug << "subgroupPartitionedExclusiveXorNV"; break; + + case EOpSubpassLoad: out.debug << "subpassLoad"; break; + case EOpSubpassLoadMS: out.debug << "subpassLoadMS"; break; + + case EOpTrace: out.debug << "traceNV"; break; + case EOpReportIntersection: out.debug << "reportIntersectionNV"; break; + case EOpIgnoreIntersection: out.debug << "ignoreIntersectionNV"; break; + case EOpTerminateRay: out.debug << "terminateRayNV"; break; + case EOpExecuteCallable: out.debug << "executeCallableNV"; break; + case EOpWritePackedPrimitiveIndices4x8NV: out.debug << "writePackedPrimitiveIndices4x8NV"; break; + + case EOpRayQueryInitialize: out.debug << "rayQueryInitializeEXT"; break; + case EOpRayQueryTerminate: out.debug << "rayQueryTerminateEXT"; break; + case EOpRayQueryGenerateIntersection: out.debug << "rayQueryGenerateIntersectionEXT"; break; + case EOpRayQueryConfirmIntersection: out.debug << "rayQueryConfirmIntersectionEXT"; break; + case EOpRayQueryProceed: out.debug << "rayQueryProceedEXT"; break; + case EOpRayQueryGetIntersectionType: out.debug << "rayQueryGetIntersectionTypeEXT"; break; + case EOpRayQueryGetRayTMin: out.debug << "rayQueryGetRayTMinEXT"; break; + case EOpRayQueryGetRayFlags: out.debug << "rayQueryGetRayFlagsEXT"; break; + case EOpRayQueryGetIntersectionT: out.debug << "rayQueryGetIntersectionTEXT"; break; + case EOpRayQueryGetIntersectionInstanceCustomIndex: out.debug << "rayQueryGetIntersectionInstanceCustomIndexEXT"; break; + case EOpRayQueryGetIntersectionInstanceId: out.debug << "rayQueryGetIntersectionInstanceIdEXT"; break; + case EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: out.debug << "rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT"; break; + case EOpRayQueryGetIntersectionGeometryIndex: out.debug << "rayQueryGetIntersectionGeometryIndexEXT"; break; + case EOpRayQueryGetIntersectionPrimitiveIndex: out.debug << "rayQueryGetIntersectionPrimitiveIndexEXT"; break; + case EOpRayQueryGetIntersectionBarycentrics: out.debug << "rayQueryGetIntersectionBarycentricsEXT"; break; + case EOpRayQueryGetIntersectionFrontFace: out.debug << "rayQueryGetIntersectionFrontFaceEXT"; break; + case EOpRayQueryGetIntersectionCandidateAABBOpaque: out.debug << "rayQueryGetIntersectionCandidateAABBOpaqueEXT"; break; + case EOpRayQueryGetIntersectionObjectRayDirection: out.debug << "rayQueryGetIntersectionObjectRayDirectionEXT"; break; + case EOpRayQueryGetIntersectionObjectRayOrigin: out.debug << "rayQueryGetIntersectionObjectRayOriginEXT"; break; + case EOpRayQueryGetWorldRayDirection: out.debug << "rayQueryGetWorldRayDirectionEXT"; break; + case EOpRayQueryGetWorldRayOrigin: out.debug << "rayQueryGetWorldRayOriginEXT"; break; + case EOpRayQueryGetIntersectionObjectToWorld: out.debug << "rayQueryGetIntersectionObjectToWorldEXT"; break; + case EOpRayQueryGetIntersectionWorldToObject: out.debug << "rayQueryGetIntersectionWorldToObjectEXT"; break; + + case EOpCooperativeMatrixLoad: out.debug << "Load cooperative matrix"; break; + case EOpCooperativeMatrixStore: out.debug << "Store cooperative matrix"; break; + case EOpCooperativeMatrixMulAdd: out.debug << "MulAdd cooperative matrices"; break; + + case EOpIsHelperInvocation: out.debug << "IsHelperInvocation"; break; + case EOpDebugPrintf: out.debug << "Debug printf"; break; + + default: out.debug.message(EPrefixError, "Bad aggregation op"); + } + + if (node->getOp() != EOpSequence && node->getOp() != EOpParameters) + out.debug << " (" << node->getCompleteString() << ")"; + + out.debug << "\n"; + + return true; +} + +bool TOutputTraverser::visitSelection(TVisit /* visit */, TIntermSelection* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + out.debug << "Test condition and select"; + out.debug << " (" << node->getCompleteString() << ")"; + + if (node->getShortCircuit() == false) + out.debug << ": no shortcircuit"; + if (node->getFlatten()) + out.debug << ": Flatten"; + if (node->getDontFlatten()) + out.debug << ": DontFlatten"; + out.debug << "\n"; + + ++depth; + + OutputTreeText(out, node, depth); + out.debug << "Condition\n"; + node->getCondition()->traverse(this); + + OutputTreeText(out, node, depth); + if (node->getTrueBlock()) { + out.debug << "true case\n"; + node->getTrueBlock()->traverse(this); + } else + out.debug << "true case is null\n"; + + if (node->getFalseBlock()) { + OutputTreeText(out, node, depth); + out.debug << "false case\n"; + node->getFalseBlock()->traverse(this); + } + + --depth; + + return false; +} + +// Print infinities and NaNs, and numbers in a portable way. +// Goals: +// - portable (across IEEE 754 platforms) +// - shows all possible IEEE values +// - shows simple numbers in a simple way, e.g., no leading/trailing 0s +// - shows all digits, no premature rounding +static void OutputDouble(TInfoSink& out, double value, TOutputTraverser::EExtraOutput extra) +{ + if (IsInfinity(value)) { + if (value < 0) + out.debug << "-1.#INF"; + else + out.debug << "+1.#INF"; + } else if (IsNan(value)) + out.debug << "1.#IND"; + else { + const int maxSize = 340; + char buf[maxSize]; + const char* format = "%f"; + if (fabs(value) > 0.0 && (fabs(value) < 1e-5 || fabs(value) > 1e12)) + format = "%-.13e"; + int len = snprintf(buf, maxSize, format, value); + assert(len < maxSize); + + // remove a leading zero in the 100s slot in exponent; it is not portable + // pattern: XX...XXXe+0XX or XX...XXXe-0XX + if (len > 5) { + if (buf[len-5] == 'e' && (buf[len-4] == '+' || buf[len-4] == '-') && buf[len-3] == '0') { + buf[len-3] = buf[len-2]; + buf[len-2] = buf[len-1]; + buf[len-1] = '\0'; + } + } + + out.debug << buf; + + switch (extra) { + case TOutputTraverser::BinaryDoubleOutput: + { + uint64_t b; + static_assert(sizeof(b) == sizeof(value), "sizeof(uint64_t) != sizeof(double)"); + memcpy(&b, &value, sizeof(b)); + + out.debug << " : "; + for (size_t i = 0; i < 8 * sizeof(value); ++i, ++b) { + out.debug << ((b & 0x8000000000000000) != 0 ? "1" : "0"); + b <<= 1; + } + break; + } + default: + break; + } + } +} + +static void OutputConstantUnion(TInfoSink& out, const TIntermTyped* node, const TConstUnionArray& constUnion, + TOutputTraverser::EExtraOutput extra, int depth) +{ + int size = node->getType().computeNumComponents(); + + for (int i = 0; i < size; i++) { + OutputTreeText(out, node, depth); + switch (constUnion[i].getType()) { + case EbtBool: + if (constUnion[i].getBConst()) + out.debug << "true"; + else + out.debug << "false"; + + out.debug << " (" << "const bool" << ")"; + + out.debug << "\n"; + break; + case EbtFloat: + case EbtDouble: + case EbtFloat16: + OutputDouble(out, constUnion[i].getDConst(), extra); + out.debug << "\n"; + break; + case EbtInt8: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%d (%s)", constUnion[i].getI8Const(), "const int8_t"); + + out.debug << buf << "\n"; + } + break; + case EbtUint8: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%u (%s)", constUnion[i].getU8Const(), "const uint8_t"); + + out.debug << buf << "\n"; + } + break; + case EbtInt16: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%d (%s)", constUnion[i].getI16Const(), "const int16_t"); + + out.debug << buf << "\n"; + } + break; + case EbtUint16: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%u (%s)", constUnion[i].getU16Const(), "const uint16_t"); + + out.debug << buf << "\n"; + } + break; + case EbtInt: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%d (%s)", constUnion[i].getIConst(), "const int"); + + out.debug << buf << "\n"; + } + break; + case EbtUint: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%u (%s)", constUnion[i].getUConst(), "const uint"); + + out.debug << buf << "\n"; + } + break; + case EbtInt64: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%lld (%s)", constUnion[i].getI64Const(), "const int64_t"); + + out.debug << buf << "\n"; + } + break; + case EbtUint64: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%llu (%s)", constUnion[i].getU64Const(), "const uint64_t"); + + out.debug << buf << "\n"; + } + break; + default: + out.info.message(EPrefixInternalError, "Unknown constant", node->getLoc()); + break; + } + } +} + +void TOutputTraverser::visitConstantUnion(TIntermConstantUnion* node) +{ + OutputTreeText(infoSink, node, depth); + infoSink.debug << "Constant:\n"; + + OutputConstantUnion(infoSink, node, node->getConstArray(), extraOutput, depth + 1); +} + +void TOutputTraverser::visitSymbol(TIntermSymbol* node) +{ + OutputTreeText(infoSink, node, depth); + + infoSink.debug << "'" << node->getName() << "' (" << node->getCompleteString() << ")\n"; + + if (! node->getConstArray().empty()) + OutputConstantUnion(infoSink, node, node->getConstArray(), extraOutput, depth + 1); + else if (node->getConstSubtree()) { + incrementDepth(node); + node->getConstSubtree()->traverse(this); + decrementDepth(); + } +} + +bool TOutputTraverser::visitLoop(TVisit /* visit */, TIntermLoop* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + out.debug << "Loop with condition "; + if (! node->testFirst()) + out.debug << "not "; + out.debug << "tested first"; + + if (node->getUnroll()) + out.debug << ": Unroll"; + if (node->getDontUnroll()) + out.debug << ": DontUnroll"; + if (node->getLoopDependency()) { + out.debug << ": Dependency "; + out.debug << node->getLoopDependency(); + } + out.debug << "\n"; + + ++depth; + + OutputTreeText(infoSink, node, depth); + if (node->getTest()) { + out.debug << "Loop Condition\n"; + node->getTest()->traverse(this); + } else + out.debug << "No loop condition\n"; + + OutputTreeText(infoSink, node, depth); + if (node->getBody()) { + out.debug << "Loop Body\n"; + node->getBody()->traverse(this); + } else + out.debug << "No loop body\n"; + + if (node->getTerminal()) { + OutputTreeText(infoSink, node, depth); + out.debug << "Loop Terminal Expression\n"; + node->getTerminal()->traverse(this); + } + + --depth; + + return false; +} + +bool TOutputTraverser::visitBranch(TVisit /* visit*/, TIntermBranch* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + switch (node->getFlowOp()) { + case EOpKill: out.debug << "Branch: Kill"; break; + case EOpBreak: out.debug << "Branch: Break"; break; + case EOpContinue: out.debug << "Branch: Continue"; break; + case EOpReturn: out.debug << "Branch: Return"; break; + case EOpCase: out.debug << "case: "; break; + case EOpDemote: out.debug << "Demote"; break; + case EOpDefault: out.debug << "default: "; break; + default: out.debug << "Branch: Unknown Branch"; break; + } + + if (node->getExpression()) { + out.debug << " with expression\n"; + ++depth; + node->getExpression()->traverse(this); + --depth; + } else + out.debug << "\n"; + + return false; +} + +bool TOutputTraverser::visitSwitch(TVisit /* visit */, TIntermSwitch* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + out.debug << "switch"; + + if (node->getFlatten()) + out.debug << ": Flatten"; + if (node->getDontFlatten()) + out.debug << ": DontFlatten"; + out.debug << "\n"; + + OutputTreeText(out, node, depth); + out.debug << "condition\n"; + ++depth; + node->getCondition()->traverse(this); + + --depth; + OutputTreeText(out, node, depth); + out.debug << "body\n"; + ++depth; + node->getBody()->traverse(this); + + --depth; + + return false; +} + +// +// This function is the one to call externally to start the traversal. +// Individual functions can be initialized to 0 to skip processing of that +// type of node. It's children will still be processed. +// +void TIntermediate::output(TInfoSink& infoSink, bool tree) +{ + infoSink.debug << "Shader version: " << version << "\n"; + if (requestedExtensions.size() > 0) { + for (auto extIt = requestedExtensions.begin(); extIt != requestedExtensions.end(); ++extIt) + infoSink.debug << "Requested " << extIt->first << "\n"; + } + + if (xfbMode) + infoSink.debug << "in xfb mode\n"; + + switch (language) { + case EShLangVertex: + break; + + case EShLangTessControl: + infoSink.debug << "vertices = " << vertices << "\n"; + + if (inputPrimitive != ElgNone) + infoSink.debug << "input primitive = " << TQualifier::getGeometryString(inputPrimitive) << "\n"; + if (vertexSpacing != EvsNone) + infoSink.debug << "vertex spacing = " << TQualifier::getVertexSpacingString(vertexSpacing) << "\n"; + if (vertexOrder != EvoNone) + infoSink.debug << "triangle order = " << TQualifier::getVertexOrderString(vertexOrder) << "\n"; + break; + + case EShLangTessEvaluation: + infoSink.debug << "input primitive = " << TQualifier::getGeometryString(inputPrimitive) << "\n"; + infoSink.debug << "vertex spacing = " << TQualifier::getVertexSpacingString(vertexSpacing) << "\n"; + infoSink.debug << "triangle order = " << TQualifier::getVertexOrderString(vertexOrder) << "\n"; + if (pointMode) + infoSink.debug << "using point mode\n"; + break; + + case EShLangGeometry: + infoSink.debug << "invocations = " << invocations << "\n"; + infoSink.debug << "max_vertices = " << vertices << "\n"; + infoSink.debug << "input primitive = " << TQualifier::getGeometryString(inputPrimitive) << "\n"; + infoSink.debug << "output primitive = " << TQualifier::getGeometryString(outputPrimitive) << "\n"; + break; + + case EShLangFragment: + if (pixelCenterInteger) + infoSink.debug << "gl_FragCoord pixel center is integer\n"; + if (originUpperLeft) + infoSink.debug << "gl_FragCoord origin is upper left\n"; + if (earlyFragmentTests) + infoSink.debug << "using early_fragment_tests\n"; + if (postDepthCoverage) + infoSink.debug << "using post_depth_coverage\n"; + if (depthLayout != EldNone) + infoSink.debug << "using " << TQualifier::getLayoutDepthString(depthLayout) << "\n"; + if (blendEquations != 0) { + infoSink.debug << "using"; + // blendEquations is a mask, decode it + for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) { + if (blendEquations & (1 << be)) + infoSink.debug << " " << TQualifier::getBlendEquationString(be); + } + infoSink.debug << "\n"; + } + if (interlockOrdering != EioNone) + infoSink.debug << "interlock ordering = " << TQualifier::getInterlockOrderingString(interlockOrdering) << "\n"; + break; + + case EShLangMeshNV: + infoSink.debug << "max_vertices = " << vertices << "\n"; + infoSink.debug << "max_primitives = " << primitives << "\n"; + infoSink.debug << "output primitive = " << TQualifier::getGeometryString(outputPrimitive) << "\n"; + // Fall through + case EShLangTaskNV: + // Fall through + case EShLangCompute: + infoSink.debug << "local_size = (" << localSize[0] << ", " << localSize[1] << ", " << localSize[2] << ")\n"; + { + if (localSizeSpecId[0] != TQualifier::layoutNotSet || + localSizeSpecId[1] != TQualifier::layoutNotSet || + localSizeSpecId[2] != TQualifier::layoutNotSet) { + infoSink.debug << "local_size ids = (" << + localSizeSpecId[0] << ", " << + localSizeSpecId[1] << ", " << + localSizeSpecId[2] << ")\n"; + } + } + break; + + default: + break; + } + + if (treeRoot == 0 || ! tree) + return; + + TOutputTraverser it(infoSink); + if (getBinaryDoubleOutput()) + it.setDoubleOutput(TOutputTraverser::BinaryDoubleOutput); + treeRoot->traverse(&it); +} + +} // end namespace glslang + +#endif // not GLSLANG_WEB diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/iomapper.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/iomapper.cpp new file mode 100644 index 000000000..9dc1da2c9 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/iomapper.cpp @@ -0,0 +1,1267 @@ +// +// Copyright (C) 2016-2017 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef GLSLANG_WEB + +#include "../Include/Common.h" +#include "../Include/InfoSink.h" + +#include "gl_types.h" +#include "iomapper.h" + +// +// Map IO bindings. +// +// High-level algorithm for one stage: +// +// 1. Traverse all code (live+dead) to find the explicitly provided bindings. +// +// 2. Traverse (just) the live code to determine which non-provided bindings +// require auto-numbering. We do not auto-number dead ones. +// +// 3. Traverse all the code to apply the bindings: +// a. explicitly given bindings are offset according to their type +// b. implicit live bindings are auto-numbered into the holes, using +// any open binding slot. +// c. implicit dead bindings are left un-bound. +// + +namespace glslang { + +class TVarGatherTraverser : public TLiveTraverser { +public: + TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList) + : TLiveTraverser(i, traverseDeadCode, true, true, false) + , inputList(inList) + , outputList(outList) + , uniformList(uniformList) + { + } + + virtual void visitSymbol(TIntermSymbol* base) + { + TVarLiveMap* target = nullptr; + if (base->getQualifier().storage == EvqVaryingIn) + target = &inputList; + else if (base->getQualifier().storage == EvqVaryingOut) + target = &outputList; + else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant()) + target = &uniformList; + if (target) { + TVarEntryInfo ent = {base->getId(), base, ! traverseAll}; + ent.stage = intermediate.getStage(); + TVarLiveMap::iterator at = target->find( + ent.symbol->getName()); // std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById()); + if (at != target->end() && at->second.id == ent.id) + at->second.live = at->second.live || ! traverseAll; // update live state + else + (*target)[ent.symbol->getName()] = ent; + } + } + +private: + TVarLiveMap& inputList; + TVarLiveMap& outputList; + TVarLiveMap& uniformList; +}; + +class TVarSetTraverser : public TLiveTraverser +{ +public: + TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& inList, const TVarLiveMap& outList, const TVarLiveMap& uniformList) + : TLiveTraverser(i, true, true, true, false) + , inputList(inList) + , outputList(outList) + , uniformList(uniformList) + { + } + + virtual void visitSymbol(TIntermSymbol* base) { + const TVarLiveMap* source; + if (base->getQualifier().storage == EvqVaryingIn) + source = &inputList; + else if (base->getQualifier().storage == EvqVaryingOut) + source = &outputList; + else if (base->getQualifier().isUniformOrBuffer()) + source = &uniformList; + else + return; + + TVarEntryInfo ent = { base->getId() }; + TVarLiveMap::const_iterator at = source->find(base->getName()); + if (at == source->end()) + return; + + if (at->second.id != ent.id) + return; + + if (at->second.newBinding != -1) + base->getWritableType().getQualifier().layoutBinding = at->second.newBinding; + if (at->second.newSet != -1) + base->getWritableType().getQualifier().layoutSet = at->second.newSet; + if (at->second.newLocation != -1) + base->getWritableType().getQualifier().layoutLocation = at->second.newLocation; + if (at->second.newComponent != -1) + base->getWritableType().getQualifier().layoutComponent = at->second.newComponent; + if (at->second.newIndex != -1) + base->getWritableType().getQualifier().layoutIndex = at->second.newIndex; + } + + private: + const TVarLiveMap& inputList; + const TVarLiveMap& outputList; + const TVarLiveMap& uniformList; +}; + +struct TNotifyUniformAdaptor +{ + EShLanguage stage; + TIoMapResolver& resolver; + inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r) + : stage(s) + , resolver(r) + { + } + + inline void operator()(std::pair& entKey) + { + resolver.notifyBinding(stage, entKey.second); + } + +private: + TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&) = delete; +}; + +struct TNotifyInOutAdaptor +{ + EShLanguage stage; + TIoMapResolver& resolver; + inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r) + : stage(s) + , resolver(r) + { + } + + inline void operator()(std::pair& entKey) + { + resolver.notifyInOut(stage, entKey.second); + } + +private: + TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&) = delete; +}; + +struct TResolverUniformAdaptor { + TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e) + : stage(s) + , resolver(r) + , infoSink(i) + , error(e) + { + } + + inline void operator()(std::pair& entKey) { + TVarEntryInfo& ent = entKey.second; + ent.newLocation = -1; + ent.newComponent = -1; + ent.newBinding = -1; + ent.newSet = -1; + ent.newIndex = -1; + const bool isValid = resolver.validateBinding(stage, ent); + if (isValid) { + resolver.resolveBinding(stage, ent); + resolver.resolveSet(stage, ent); + resolver.resolveUniformLocation(stage, ent); + + if (ent.newBinding != -1) { + if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) { + TString err = "mapped binding out of range: " + entKey.first; + + infoSink.info.message(EPrefixInternalError, err.c_str()); + error = true; + } + } + if (ent.newSet != -1) { + if (ent.newSet >= int(TQualifier::layoutSetEnd)) { + TString err = "mapped set out of range: " + entKey.first; + + infoSink.info.message(EPrefixInternalError, err.c_str()); + error = true; + } + } + } else { + TString errorMsg = "Invalid binding: " + entKey.first; + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + error = true; + } + } + + inline void setStage(EShLanguage s) { stage = s; } + + EShLanguage stage; + TIoMapResolver& resolver; + TInfoSink& infoSink; + bool& error; + +private: + TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&) = delete; +}; + +struct TResolverInOutAdaptor { + TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e) + : stage(s) + , resolver(r) + , infoSink(i) + , error(e) + { + } + + inline void operator()(std::pair& entKey) + { + TVarEntryInfo& ent = entKey.second; + ent.newLocation = -1; + ent.newComponent = -1; + ent.newBinding = -1; + ent.newSet = -1; + ent.newIndex = -1; + const bool isValid = resolver.validateInOut(stage, ent); + if (isValid) { + resolver.resolveInOutLocation(stage, ent); + resolver.resolveInOutComponent(stage, ent); + resolver.resolveInOutIndex(stage, ent); + } else { + TString errorMsg; + if (ent.symbol->getType().getQualifier().semanticName != nullptr) { + errorMsg = "Invalid shader In/Out variable semantic: "; + errorMsg += ent.symbol->getType().getQualifier().semanticName; + } else { + errorMsg = "Invalid shader In/Out variable: "; + errorMsg += ent.symbol->getName(); + } + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + error = true; + } + } + + inline void setStage(EShLanguage s) { stage = s; } + + EShLanguage stage; + TIoMapResolver& resolver; + TInfoSink& infoSink; + bool& error; + +private: + TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&) = delete; +}; + +// The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings + +struct TSymbolValidater +{ + TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount], + TVarLiveMap* uniform[EShLangCount], bool& hadError) + : preStage(EShLangCount) + , currentStage(EShLangCount) + , nextStage(EShLangCount) + , resolver(r) + , infoSink(i) + , hadError(hadError) + { + memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*))); + memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*))); + memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*))); + } + + inline void operator()(std::pair& entKey) { + TVarEntryInfo& ent1 = entKey.second; + TIntermSymbol* base = ent1.symbol; + const TType& type = ent1.symbol->getType(); + const TString& name = entKey.first; + TString mangleName1, mangleName2; + type.appendMangledName(mangleName1); + EShLanguage stage = ent1.stage; + if (currentStage != stage) { + preStage = currentStage; + currentStage = stage; + nextStage = EShLangCount; + for (int i = currentStage + 1; i < EShLangCount; i++) { + if (inVarMaps[i] != nullptr) + nextStage = static_cast(i); + } + } + if (base->getQualifier().storage == EvqVaryingIn) { + // validate stage in; + if (preStage == EShLangCount) + return; + if (outVarMaps[preStage] != nullptr) { + auto ent2 = outVarMaps[preStage]->find(name); + if (ent2 != outVarMaps[preStage]->end()) { + ent2->second.symbol->getType().appendMangledName(mangleName2); + if (mangleName1 == mangleName2) + return; + else { + TString err = "Invalid In/Out variable type : " + entKey.first; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + } + } + return; + } + } else if (base->getQualifier().storage == EvqVaryingOut) { + // validate stage out; + if (nextStage == EShLangCount) + return; + if (outVarMaps[nextStage] != nullptr) { + auto ent2 = inVarMaps[nextStage]->find(name); + if (ent2 != inVarMaps[nextStage]->end()) { + ent2->second.symbol->getType().appendMangledName(mangleName2); + if (mangleName1 == mangleName2) + return; + else { + TString err = "Invalid In/Out variable type : " + entKey.first; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + } + } + return; + } + } else if (base->getQualifier().isUniformOrBuffer() && ! base->getQualifier().isPushConstant()) { + // validate uniform type; + for (int i = 0; i < EShLangCount; i++) { + if (i != currentStage && outVarMaps[i] != nullptr) { + auto ent2 = uniformVarMap[i]->find(name); + if (ent2 != uniformVarMap[i]->end()) { + ent2->second.symbol->getType().appendMangledName(mangleName2); + if (mangleName1 != mangleName2) { + TString err = "Invalid Uniform variable type : " + entKey.first; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + } + mangleName2.clear(); + } + } + } + } + } + TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount]; + // Use for mark pre stage, to get more interface symbol information. + EShLanguage preStage, currentStage, nextStage; + // Use for mark current shader stage for resolver + TIoMapResolver& resolver; + TInfoSink& infoSink; + bool& hadError; + +private: + TSymbolValidater& operator=(TSymbolValidater&) = delete; +}; + +struct TSlotCollector { + TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { } + + inline void operator()(std::pair& entKey) { + resolver.reserverStorageSlot(entKey.second, infoSink); + resolver.reserverResourceSlot(entKey.second, infoSink); + } + TIoMapResolver& resolver; + TInfoSink& infoSink; + +private: + TSlotCollector& operator=(TSlotCollector&) = delete; +}; + +TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate) + : intermediate(intermediate) + , nextUniformLocation(intermediate.getUniformLocationBase()) + , nextInputLocation(0) + , nextOutputLocation(0) +{ + memset(stageMask, false, sizeof(bool) * (EShLangCount + 1)); +} + +int TDefaultIoResolverBase::getBaseBinding(TResourceType res, unsigned int set) const { + return selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set)); +} + +const std::vector& TDefaultIoResolverBase::getResourceSetBinding() const { + return intermediate.getResourceSetBinding(); +} + +bool TDefaultIoResolverBase::doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); } + +bool TDefaultIoResolverBase::doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); } + +TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) { + return std::lower_bound(slots[set].begin(), slots[set].end(), slot); +} + +bool TDefaultIoResolverBase::checkEmpty(int set, int slot) { + TSlotSet::iterator at = findSlot(set, slot); + return ! (at != slots[set].end() && *at == slot); +} + +int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) { + TSlotSet::iterator at = findSlot(set, slot); + // tolerate aliasing, by not double-recording aliases + // (policy about appropriateness of the alias is higher up) + for (int i = 0; i < size; i++) { + if (at == slots[set].end() || *at != slot + i) + at = slots[set].insert(at, slot + i); + ++at; + } + return slot; +} + +int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) { + TSlotSet::iterator at = findSlot(set, base); + if (at == slots[set].end()) + return reserveSlot(set, base, size); + // look for a big enough gap + for (; at != slots[set].end(); ++at) { + if (*at - base >= size) + break; + base = *at + 1; + } + return reserveSlot(set, base, size); +} + +int TDefaultIoResolverBase::resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + if (type.getQualifier().hasSet()) { + return ent.newSet = type.getQualifier().layoutSet; + } + // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN) + if (getResourceSetBinding().size() == 1) { + return ent.newSet = atoi(getResourceSetBinding()[0].c_str()); + } + return ent.newSet = 0; +} + +int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + const char* name = ent.symbol->getName().c_str(); + // kick out of not doing this + if (! doAutoLocationMapping()) { + return ent.newLocation = -1; + } + // no locations added if already present, a built-in variable, a block, or an opaque + if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock || + type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) { + return ent.newLocation = -1; + } + // no locations on blocks of built-in variables + if (type.isStruct()) { + if (type.getStruct()->size() < 1) { + return ent.newLocation = -1; + } + if ((*type.getStruct())[0].type->isBuiltIn()) { + return ent.newLocation = -1; + } + } + int location = intermediate.getUniformLocationOverride(name); + if (location != -1) { + return ent.newLocation = location; + } + location = nextUniformLocation; + nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type); + return ent.newLocation = location; +} + +int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + // kick out of not doing this + if (! doAutoLocationMapping()) { + return ent.newLocation = -1; + } + + // no locations added if already present, or a built-in variable + if (type.getQualifier().hasLocation() || type.isBuiltIn()) { + return ent.newLocation = -1; + } + + // no locations on blocks of built-in variables + if (type.isStruct()) { + if (type.getStruct()->size() < 1) { + return ent.newLocation = -1; + } + if ((*type.getStruct())[0].type->isBuiltIn()) { + return ent.newLocation = -1; + } + } + // point to the right input or output location counter + int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation; + // Placeholder. This does not do proper cross-stage lining up, nor + // work with mixed location/no-location declarations. + int location = nextLocation; + int typeLocationSize; + // Don’t take into account the outer-most array if the stage’s + // interface is automatically an array. + typeLocationSize = computeTypeLocationSize(type, stage); + nextLocation += typeLocationSize; + return ent.newLocation = location; +} + +int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) { + return ent.newComponent = -1; +} + +int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; } + +uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) { + int typeLocationSize; + // Don’t take into account the outer-most array if the stage’s + // interface is automatically an array. + if (type.getQualifier().isArrayedIo(stage)) { + TType elementType(type, 0); + typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage); + } else { + typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage); + } + return typeLocationSize; +} + +//TDefaultGlslIoResolver +TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) { + if (isImageType(type)) { + return EResImage; + } + if (isTextureType(type)) { + return EResTexture; + } + if (isSsboType(type)) { + return EResSsbo; + } + if (isSamplerType(type)) { + return EResSampler; + } + if (isUboType(type)) { + return EResUbo; + } + return EResCount; +} + +TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate) + : TDefaultIoResolverBase(intermediate) + , preStage(EShLangCount) + , currentStage(EShLangCount) +{ } + +int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + const TString& name = IsAnonymous(ent.symbol->getName()) ? + ent.symbol->getType().getTypeName() + : + ent.symbol->getName(); + if (currentStage != stage) { + preStage = currentStage; + currentStage = stage; + } + // kick out of not doing this + if (! doAutoLocationMapping()) { + return ent.newLocation = -1; + } + // expand the location to each element if the symbol is a struct or array + if (type.getQualifier().hasLocation()) { + return ent.newLocation = type.getQualifier().layoutLocation; + } + // no locations added if already present, or a built-in variable + if (type.isBuiltIn()) { + return ent.newLocation = -1; + } + // no locations on blocks of built-in variables + if (type.isStruct()) { + if (type.getStruct()->size() < 1) { + return ent.newLocation = -1; + } + if ((*type.getStruct())[0].type->isBuiltIn()) { + return ent.newLocation = -1; + } + } + int typeLocationSize = computeTypeLocationSize(type, stage); + int location = type.getQualifier().layoutLocation; + bool hasLocation = false; + EShLanguage keyStage(EShLangCount); + TStorageQualifier storage; + storage = EvqInOut; + if (type.getQualifier().isPipeInput()) { + // If this symbol is a input, search pre stage's out + keyStage = preStage; + } + if (type.getQualifier().isPipeOutput()) { + // If this symbol is a output, search next stage's in + keyStage = currentStage; + } + // The in/out in current stage is not declared with location, but it is possible declared + // with explicit location in other stages, find the storageSlotMap firstly to check whether + // the in/out has location + int resourceKey = buildStorageKey(keyStage, storage); + if (! storageSlotMap[resourceKey].empty()) { + TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name); + if (iter != storageSlotMap[resourceKey].end()) { + // If interface resource be found, set it has location and this symbol's new location + // equal the symbol's explicit location declarated in pre or next stage. + // + // vs: out vec4 a; + // fs: layout(..., location = 3,...) in vec4 a; + hasLocation = true; + location = iter->second; + // if we want deal like that: + // vs: layout(location=4) out vec4 a; + // out vec4 b; + // + // fs: in vec4 a; + // layout(location = 4) in vec4 b; + // we need retraverse the map. + } + if (! hasLocation) { + // If interface resource note found, It's mean the location in two stage are both implicit declarat. + // So we should find a new slot for this interface. + // + // vs: out vec4 a; + // fs: in vec4 a; + location = getFreeSlot(resourceKey, 0, typeLocationSize); + storageSlotMap[resourceKey][name] = location; + } + } else { + // the first interface declarated in a program. + TVarSlotMap varSlotMap; + location = getFreeSlot(resourceKey, 0, typeLocationSize); + varSlotMap[name] = location; + storageSlotMap[resourceKey] = varSlotMap; + } + //Update location + return ent.newLocation = location; +} + +int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + const TString& name = IsAnonymous(ent.symbol->getName()) ? + ent.symbol->getType().getTypeName() + : + ent.symbol->getName(); + // kick out of not doing this + if (! doAutoLocationMapping()) { + return ent.newLocation = -1; + } + // expand the location to each element if the symbol is a struct or array + if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) { + return ent.newLocation = type.getQualifier().layoutLocation; + } else { + // no locations added if already present, a built-in variable, a block, or an opaque + if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock || + type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) { + return ent.newLocation = -1; + } + // no locations on blocks of built-in variables + if (type.isStruct()) { + if (type.getStruct()->size() < 1) { + return ent.newLocation = -1; + } + if ((*type.getStruct())[0].type->isBuiltIn()) { + return ent.newLocation = -1; + } + } + } + int location = intermediate.getUniformLocationOverride(name.c_str()); + if (location != -1) { + return ent.newLocation = location; + } + + int size = TIntermediate::computeTypeUniformLocationSize(type); + + // The uniform in current stage is not declared with location, but it is possible declared + // with explicit location in other stages, find the storageSlotMap firstly to check whether + // the uniform has location + bool hasLocation = false; + int resourceKey = buildStorageKey(EShLangCount, EvqUniform); + TVarSlotMap& slotMap = storageSlotMap[resourceKey]; + // Check dose shader program has uniform resource + if (! slotMap.empty()) { + // If uniform resource not empty, try find a same name uniform + TVarSlotMap::iterator iter = slotMap.find(name); + if (iter != slotMap.end()) { + // If uniform resource be found, set it has location and this symbol's new location + // equal the uniform's explicit location declarated in other stage. + // + // vs: uniform vec4 a; + // fs: layout(..., location = 3,...) uniform vec4 a; + hasLocation = true; + location = iter->second; + } + if (! hasLocation) { + // No explicit location declaraten in other stage. + // So we should find a new slot for this uniform. + // + // vs: uniform vec4 a; + // fs: uniform vec4 a; + location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage)); + storageSlotMap[resourceKey][name] = location; + } + } else { + // the first uniform declarated in a program. + TVarSlotMap varSlotMap; + location = getFreeSlot(resourceKey, 0, size); + varSlotMap[name] = location; + storageSlotMap[resourceKey] = varSlotMap; + } + return ent.newLocation = location; +} + +int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + const TString& name = IsAnonymous(ent.symbol->getName()) ? + ent.symbol->getType().getTypeName() + : + ent.symbol->getName(); + // On OpenGL arrays of opaque types take a seperate binding for each element + int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1; + TResourceType resource = getResourceType(type); + // don't need to handle uniform symbol, it will be handled in resolveUniformLocation + if (resource == EResUbo && type.getBasicType() != EbtBlock) { + return ent.newBinding = -1; + } + // There is no 'set' qualifier in OpenGL shading language, each resource has its own + // binding name space, so remap the 'set' to resource type which make each resource + // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS + int set = resource; + if (resource < EResCount) { + if (type.getQualifier().hasBinding()) { + ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings); + return ent.newBinding; + } else if (ent.live && doAutoBindingMapping()) { + // The resource in current stage is not declared with binding, but it is possible declared + // with explicit binding in other stages, find the resourceSlotMap firstly to check whether + // the resource has binding, don't need to allocate if it already has a binding + bool hasBinding = false; + if (! resourceSlotMap[resource].empty()) { + TVarSlotMap::iterator iter = resourceSlotMap[resource].find(name); + if (iter != resourceSlotMap[resource].end()) { + hasBinding = true; + ent.newBinding = iter->second; + } + } + if (! hasBinding) { + TVarSlotMap varSlotMap; + // find free slot, the caller did make sure it passes all vars with binding + // first and now all are passed that do not have a binding and needs one + int binding = getFreeSlot(resource, getBaseBinding(resource, set), numBindings); + varSlotMap[name] = binding; + resourceSlotMap[resource] = varSlotMap; + ent.newBinding = binding; + } + return ent.newBinding; + } + } + return ent.newBinding = -1; +} + +void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) { + // reset stage state + if (stage == EShLangCount) + preStage = currentStage = stage; + // update stage state + else if (currentStage != stage) { + preStage = currentStage; + currentStage = stage; + } +} + +void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) { + // TODO nothing +} + +void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) { + // reset stage state + if (stage == EShLangCount) + preStage = currentStage = stage; + // update stage state + else if (currentStage != stage) { + preStage = currentStage; + currentStage = stage; + } +} + +void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) { + // TODO nothing +} + +void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) { + const TType& type = ent.symbol->getType(); + const TString& name = IsAnonymous(ent.symbol->getName()) ? + ent.symbol->getType().getTypeName() + : + ent.symbol->getName(); + TStorageQualifier storage = type.getQualifier().storage; + EShLanguage stage(EShLangCount); + switch (storage) { + case EvqUniform: + if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) { + // + // Reserve the slots for the uniforms who has explicit location + int storageKey = buildStorageKey(EShLangCount, EvqUniform); + int location = type.getQualifier().layoutLocation; + TVarSlotMap& varSlotMap = storageSlotMap[storageKey]; + TVarSlotMap::iterator iter = varSlotMap.find(name); + if (iter == varSlotMap.end()) { + int numLocations = TIntermediate::computeTypeUniformLocationSize(type); + reserveSlot(storageKey, location, numLocations); + varSlotMap[name] = location; + } else { + // Allocate location by name for OpenGL driver, so the uniform in different + // stages should be declared with the same location + if (iter->second != location) { + TString errorMsg = "Invalid location: " + name; + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + hasError = true; + } + } + } + break; + case EvqVaryingIn: + case EvqVaryingOut: + // + // Reserve the slots for the inout who has explicit location + if (type.getQualifier().hasLocation()) { + stage = storage == EvqVaryingIn ? preStage : stage; + stage = storage == EvqVaryingOut ? currentStage : stage; + int storageKey = buildStorageKey(stage, EvqInOut); + int location = type.getQualifier().layoutLocation; + TVarSlotMap& varSlotMap = storageSlotMap[storageKey]; + TVarSlotMap::iterator iter = varSlotMap.find(name); + if (iter == varSlotMap.end()) { + int numLocations = TIntermediate::computeTypeUniformLocationSize(type); + reserveSlot(storageKey, location, numLocations); + varSlotMap[name] = location; + } else { + // Allocate location by name for OpenGL driver, so the uniform in different + // stages should be declared with the same location + if (iter->second != location) { + TString errorMsg = "Invalid location: " + name; + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + hasError = true; + } + } + } + break; + default: + break; + } +} + +void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) { + const TType& type = ent.symbol->getType(); + const TString& name = IsAnonymous(ent.symbol->getName()) ? + ent.symbol->getType().getTypeName() + : + ent.symbol->getName(); + int resource = getResourceType(type); + if (type.getQualifier().hasBinding()) { + TVarSlotMap& varSlotMap = resourceSlotMap[resource]; + TVarSlotMap::iterator iter = varSlotMap.find(name); + int binding = type.getQualifier().layoutBinding; + if (iter == varSlotMap.end()) { + // Reserve the slots for the ubo, ssbo and opaques who has explicit binding + int numBindings = type.isSizedArray() ? type.getCumulativeArraySize() : 1; + varSlotMap[name] = binding; + reserveSlot(resource, binding, numBindings); + } else { + // Allocate binding by name for OpenGL driver, so the resource in different + // stages should be declared with the same binding + if (iter->second != binding) { + TString errorMsg = "Invalid binding: " + name; + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + hasError = true; + } + } + } +} + +//TDefaultGlslIoResolver end + +/* + * Basic implementation of glslang::TIoMapResolver that replaces the + * previous offset behavior. + * It does the same, uses the offsets for the corresponding uniform + * types. Also respects the EOptionAutoMapBindings flag and binds + * them if needed. + */ +/* + * Default resolver + */ +struct TDefaultIoResolver : public TDefaultIoResolverBase { + TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { } + + bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } + + TResourceType getResourceType(const glslang::TType& type) override { + if (isImageType(type)) { + return EResImage; + } + if (isTextureType(type)) { + return EResTexture; + } + if (isSsboType(type)) { + return EResSsbo; + } + if (isSamplerType(type)) { + return EResSampler; + } + if (isUboType(type)) { + return EResUbo; + } + return EResCount; + } + + int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override { + const TType& type = ent.symbol->getType(); + const int set = getLayoutSet(type); + // On OpenGL arrays of opaque types take a seperate binding for each element + int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1; + TResourceType resource = getResourceType(type); + if (resource < EResCount) { + if (type.getQualifier().hasBinding()) { + return ent.newBinding = reserveSlot( + set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings); + } else if (ent.live && doAutoBindingMapping()) { + // find free slot, the caller did make sure it passes all vars with binding + // first and now all are passed that do not have a binding and needs one + return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set), numBindings); + } + } + return ent.newBinding = -1; + } +}; + +#ifdef ENABLE_HLSL +/******************************************************************************** +The following IO resolver maps types in HLSL register space, as follows: + +t - for shader resource views (SRV) + TEXTURE1D + TEXTURE1DARRAY + TEXTURE2D + TEXTURE2DARRAY + TEXTURE3D + TEXTURECUBE + TEXTURECUBEARRAY + TEXTURE2DMS + TEXTURE2DMSARRAY + STRUCTUREDBUFFER + BYTEADDRESSBUFFER + BUFFER + TBUFFER + +s - for samplers + SAMPLER + SAMPLER1D + SAMPLER2D + SAMPLER3D + SAMPLERCUBE + SAMPLERSTATE + SAMPLERCOMPARISONSTATE + +u - for unordered access views (UAV) + RWBYTEADDRESSBUFFER + RWSTRUCTUREDBUFFER + APPENDSTRUCTUREDBUFFER + CONSUMESTRUCTUREDBUFFER + RWBUFFER + RWTEXTURE1D + RWTEXTURE1DARRAY + RWTEXTURE2D + RWTEXTURE2DARRAY + RWTEXTURE3D + +b - for constant buffer views (CBV) + CBUFFER + CONSTANTBUFFER + ********************************************************************************/ +struct TDefaultHlslIoResolver : public TDefaultIoResolverBase { + TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { } + + bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } + + TResourceType getResourceType(const glslang::TType& type) override { + if (isUavType(type)) { + return EResUav; + } + if (isSrvType(type)) { + return EResTexture; + } + if (isSamplerType(type)) { + return EResSampler; + } + if (isUboType(type)) { + return EResUbo; + } + return EResCount; + } + + int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override { + const TType& type = ent.symbol->getType(); + const int set = getLayoutSet(type); + TResourceType resource = getResourceType(type); + if (resource < EResCount) { + if (type.getQualifier().hasBinding()) { + return ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding); + } else if (ent.live && doAutoBindingMapping()) { + // find free slot, the caller did make sure it passes all vars with binding + // first and now all are passed that do not have a binding and needs one + return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set)); + } + } + return ent.newBinding = -1; + } +}; +#endif + +// Map I/O variables to provided offsets, and make bindings for +// unbound but live variables. +// +// Returns false if the input is too malformed to do this. +bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) { + bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() || + intermediate.getAutoMapLocations(); + // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce + // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true. + for (int res = 0; (res < EResCount && !somethingToDo); ++res) { + somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) || + intermediate.hasShiftBindingForSet(TResourceType(res)); + } + if (! somethingToDo && resolver == nullptr) + return true; + if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) + return false; + TIntermNode* root = intermediate.getTreeRoot(); + if (root == nullptr) + return false; + // if no resolver is provided, use the default resolver with the given shifts and auto map settings + TDefaultIoResolver defaultResolver(intermediate); +#ifdef ENABLE_HLSL + TDefaultHlslIoResolver defaultHlslResolver(intermediate); + if (resolver == nullptr) { + // TODO: use a passed in IO mapper for this + if (intermediate.usingHlslIoMapping()) + resolver = &defaultHlslResolver; + else + resolver = &defaultResolver; + } + resolver->addStage(stage); +#else + resolver = &defaultResolver; +#endif + + TVarLiveMap inVarMap, outVarMap, uniformVarMap; + TVarLiveVector inVector, outVector, uniformVector; + TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap); + TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap); + root->traverse(&iter_binding_all); + iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str()); + while (! iter_binding_live.functions.empty()) { + TIntermNode* function = iter_binding_live.functions.back(); + iter_binding_live.functions.pop_back(); + function->traverse(&iter_binding_live); + } + // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info. + std::for_each(inVarMap.begin(), inVarMap.end(), + [&inVector](TVarLivePair p) { inVector.push_back(p); }); + std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + std::for_each(outVarMap.begin(), outVarMap.end(), + [&outVector](TVarLivePair p) { outVector.push_back(p); }); + std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + std::for_each(uniformVarMap.begin(), uniformVarMap.end(), + [&uniformVector](TVarLivePair p) { uniformVector.push_back(p); }); + std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + bool hadError = false; + TNotifyInOutAdaptor inOutNotify(stage, *resolver); + TNotifyUniformAdaptor uniformNotify(stage, *resolver); + TResolverUniformAdaptor uniformResolve(stage, *resolver, infoSink, hadError); + TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError); + resolver->beginNotifications(stage); + std::for_each(inVector.begin(), inVector.end(), inOutNotify); + std::for_each(outVector.begin(), outVector.end(), inOutNotify); + std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify); + resolver->endNotifications(stage); + resolver->beginResolve(stage); + std::for_each(inVector.begin(), inVector.end(), inOutResolve); + std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) { + auto at = inVarMap.find(p.second.symbol->getName()); + if (at != inVarMap.end()) + at->second = p.second; + }); + std::for_each(outVector.begin(), outVector.end(), inOutResolve); + std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) { + auto at = outVarMap.find(p.second.symbol->getName()); + if (at != outVarMap.end()) + at->second = p.second; + }); + std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve); + std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) { + auto at = uniformVarMap.find(p.second.symbol->getName()); + if (at != uniformVarMap.end()) + at->second = p.second; + }); + resolver->endResolve(stage); + if (!hadError) { + TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap); + root->traverse(&iter_iomap); + } + return !hadError; +} + +// Map I/O variables to provided offsets, and make bindings for +// unbound but live variables. +// +// Returns false if the input is too malformed to do this. +bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) { + + bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() || + intermediate.getAutoMapLocations(); + // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce + // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true. + for (int res = 0; (res < EResCount && !somethingToDo); ++res) { + somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) || + intermediate.hasShiftBindingForSet(TResourceType(res)); + } + if (! somethingToDo && resolver == nullptr) { + return true; + } + if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) { + return false; + } + TIntermNode* root = intermediate.getTreeRoot(); + if (root == nullptr) { + return false; + } + // if no resolver is provided, use the default resolver with the given shifts and auto map settings + TDefaultGlslIoResolver defaultResolver(intermediate); + if (resolver == nullptr) { + resolver = &defaultResolver; + } + resolver->addStage(stage); + inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap(); + TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage], + *uniformVarMap[stage]); + TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage], + *uniformVarMap[stage]); + root->traverse(&iter_binding_all); + iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str()); + while (! iter_binding_live.functions.empty()) { + TIntermNode* function = iter_binding_live.functions.back(); + iter_binding_live.functions.pop_back(); + function->traverse(&iter_binding_live); + } + TNotifyInOutAdaptor inOutNotify(stage, *resolver); + TNotifyUniformAdaptor uniformNotify(stage, *resolver); + // Resolve current stage input symbol location with previous stage output here, + // uniform symbol, ubo, ssbo and opaque symbols are per-program resource, + // will resolve uniform symbol location and ubo/ssbo/opaque binding in doMap() + resolver->beginNotifications(stage); + std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutNotify); + std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutNotify); + std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), uniformNotify); + resolver->endNotifications(stage); + TSlotCollector slotCollector(*resolver, infoSink); + resolver->beginCollect(stage); + std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), slotCollector); + std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), slotCollector); + std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), slotCollector); + resolver->endCollect(stage); + intermediates[stage] = &intermediate; + return !hadError; +} + +bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) { + resolver->endResolve(EShLangCount); + if (!hadError) { + //Resolve uniform location, ubo/ssbo/opaque bindings across stages + TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, infoSink, hadError); + TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError); + TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps, outVarMaps, uniformVarMap, hadError); + TVarLiveVector uniformVector; + resolver->beginResolve(EShLangCount); + for (int stage = EShLangVertex; stage < EShLangCount; stage++) { + if (inVarMaps[stage] != nullptr) { + inOutResolve.setStage(EShLanguage(stage)); + std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), symbolValidater); + std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutResolve); + std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), symbolValidater); + std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutResolve); + } + if (uniformVarMap[stage] != nullptr) { + uniformResolve.setStage(EShLanguage(stage)); + // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info. + std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), + [&uniformVector](TVarLivePair p) { uniformVector.push_back(p); }); + } + } + std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + std::for_each(uniformVector.begin(), uniformVector.end(), symbolValidater); + std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve); + std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + resolver->endResolve(EShLangCount); + for (size_t stage = 0; stage < EShLangCount; stage++) { + if (intermediates[stage] != nullptr) { + // traverse each stage, set new location to each input/output and unifom symbol, set new binding to + // ubo, ssbo and opaque symbols + TVarLiveMap** pUniformVarMap = uniformVarMap; + std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) { + auto at = pUniformVarMap[stage]->find(p.second.symbol->getName()); + if (at != pUniformVarMap[stage]->end()) + at->second = p.second; + }); + TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage], + *uniformVarMap[stage]); + intermediates[stage]->getTreeRoot()->traverse(&iter_iomap); + } + } + return !hadError; + } else { + return false; + } +} + +} // end namespace glslang + +#endif // GLSLANG_WEB diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/iomapper.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/iomapper.h new file mode 100644 index 000000000..e91a15094 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/iomapper.h @@ -0,0 +1,301 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef GLSLANG_WEB + +#ifndef _IOMAPPER_INCLUDED +#define _IOMAPPER_INCLUDED + +#include +#include "LiveTraverser.h" +#include +#include +// +// A reflection database and its interface, consistent with the OpenGL API reflection queries. +// + +class TInfoSink; + +namespace glslang { + +class TIntermediate; +struct TVarEntryInfo { + int id; + TIntermSymbol* symbol; + bool live; + int newBinding; + int newSet; + int newLocation; + int newComponent; + int newIndex; + EShLanguage stage; + struct TOrderById { + inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) { return l.id < r.id; } + }; + + struct TOrderByPriority { + // ordering: + // 1) has both binding and set + // 2) has binding but no set + // 3) has no binding but set + // 4) has no binding and no set + inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) { + const TQualifier& lq = l.symbol->getQualifier(); + const TQualifier& rq = r.symbol->getQualifier(); + + // simple rules: + // has binding gives 2 points + // has set gives 1 point + // who has the most points is more important. + int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0); + int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0); + + if (lPoints == rPoints) + return l.id < r.id; + return lPoints > rPoints; + } + }; +}; + +// Base class for shared TIoMapResolver services, used by several derivations. +struct TDefaultIoResolverBase : public glslang::TIoMapResolver { +public: + TDefaultIoResolverBase(const TIntermediate& intermediate); + typedef std::vector TSlotSet; + typedef std::unordered_map TSlotSetMap; + + // grow the reflection stage by stage + void notifyBinding(EShLanguage, TVarEntryInfo& /*ent*/) override {} + void notifyInOut(EShLanguage, TVarEntryInfo& /*ent*/) override {} + void beginNotifications(EShLanguage) override {} + void endNotifications(EShLanguage) override {} + void beginResolve(EShLanguage) override {} + void endResolve(EShLanguage) override {} + void beginCollect(EShLanguage) override {} + void endCollect(EShLanguage) override {} + void reserverResourceSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {} + void reserverStorageSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {} + int getBaseBinding(TResourceType res, unsigned int set) const; + const std::vector& getResourceSetBinding() const; + virtual TResourceType getResourceType(const glslang::TType& type) = 0; + bool doAutoBindingMapping() const; + bool doAutoLocationMapping() const; + TSlotSet::iterator findSlot(int set, int slot); + bool checkEmpty(int set, int slot); + bool validateInOut(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } + int reserveSlot(int set, int slot, int size = 1); + int getFreeSlot(int set, int base, int size = 1); + int resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + int resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) override; + int resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + int resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + void addStage(EShLanguage stage) override { + if (stage < EShLangCount) + stageMask[stage] = true; + } + uint32_t computeTypeLocationSize(const TType& type, EShLanguage stage); + + TSlotSetMap slots; + bool hasError = false; + +protected: + TDefaultIoResolverBase(TDefaultIoResolverBase&); + TDefaultIoResolverBase& operator=(TDefaultIoResolverBase&); + const TIntermediate& intermediate; + int nextUniformLocation; + int nextInputLocation; + int nextOutputLocation; + bool stageMask[EShLangCount + 1]; + // Return descriptor set specific base if there is one, and the generic base otherwise. + int selectBaseBinding(int base, int descriptorSetBase) const { + return descriptorSetBase != -1 ? descriptorSetBase : base; + } + + static int getLayoutSet(const glslang::TType& type) { + if (type.getQualifier().hasSet()) + return type.getQualifier().layoutSet; + else + return 0; + } + + static bool isSamplerType(const glslang::TType& type) { + return type.getBasicType() == glslang::EbtSampler && type.getSampler().isPureSampler(); + } + + static bool isTextureType(const glslang::TType& type) { + return (type.getBasicType() == glslang::EbtSampler && + (type.getSampler().isTexture() || type.getSampler().isSubpass())); + } + + static bool isUboType(const glslang::TType& type) { + return type.getQualifier().storage == EvqUniform; + } + + static bool isImageType(const glslang::TType& type) { + return type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage(); + } + + static bool isSsboType(const glslang::TType& type) { + return type.getQualifier().storage == EvqBuffer; + } + + // Return true if this is a SRV (shader resource view) type: + static bool isSrvType(const glslang::TType& type) { + return isTextureType(type) || type.getQualifier().storage == EvqBuffer; + } + + // Return true if this is a UAV (unordered access view) type: + static bool isUavType(const glslang::TType& type) { + if (type.getQualifier().isReadOnly()) + return false; + return (type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage()) || + (type.getQualifier().storage == EvqBuffer); + } +}; + +// Defaulf I/O resolver for OpenGL +struct TDefaultGlslIoResolver : public TDefaultIoResolverBase { +public: + typedef std::map TVarSlotMap; // + typedef std::map TSlotMap; // + TDefaultGlslIoResolver(const TIntermediate& intermediate); + bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } + TResourceType getResourceType(const glslang::TType& type) override; + int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) override; + int resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + void beginResolve(EShLanguage /*stage*/) override; + void endResolve(EShLanguage stage) override; + void beginCollect(EShLanguage) override; + void endCollect(EShLanguage) override; + void reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) override; + void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) override; + // in/out symbol and uniform symbol are stored in the same resourceSlotMap, the storage key is used to identify each type of symbol. + // We use stage and storage qualifier to construct a storage key. it can help us identify the same storage resource used in different stage. + // if a resource is a program resource and we don't need know it usage stage, we can use same stage to build storage key. + // Note: both stage and type must less then 0xffff. + int buildStorageKey(EShLanguage stage, TStorageQualifier type) { + assert(static_cast(stage) <= 0x0000ffff && static_cast(type) <= 0x0000ffff); + return (stage << 16) | type; + } + +protected: + // Use for mark pre stage, to get more interface symbol information. + EShLanguage preStage; + // Use for mark current shader stage for resolver + EShLanguage currentStage; + // Slot map for storage resource(location of uniform and interface symbol) It's a program share slot + TSlotMap resourceSlotMap; + // Slot map for other resource(image, ubo, ssbo), It's a program share slot. + TSlotMap storageSlotMap; +}; + +typedef std::map TVarLiveMap; + +// override function "operator=", if a vector being sort, +// when use vc++, the sort function will call : +// pair& operator=(const pair<_Other1, _Other2>& _Right) +// { +// first = _Right.first; +// second = _Right.second; +// return (*this); +// } +// that will make a const type handing on left. +// override this function can avoid a compiler error. +// In the future, if the vc++ compiler can handle such a situation, +// this part of the code will be removed. +struct TVarLivePair : std::pair { + TVarLivePair(const std::pair& _Right) : pair(_Right.first, _Right.second) {} + TVarLivePair& operator=(const TVarLivePair& _Right) { + const_cast(first) = _Right.first; + second = _Right.second; + return (*this); + } + TVarLivePair(const TVarLivePair& src) { *this = src; } +}; +typedef std::vector TVarLiveVector; + +// I/O mapper +class TIoMapper { +public: + TIoMapper() {} + virtual ~TIoMapper() {} + // grow the reflection stage by stage + bool virtual addStage(EShLanguage, TIntermediate&, TInfoSink&, TIoMapResolver*); + bool virtual doMap(TIoMapResolver*, TInfoSink&) { return true; } +}; + +// I/O mapper for OpenGL +class TGlslIoMapper : public TIoMapper { +public: + TGlslIoMapper() { + memset(inVarMaps, 0, sizeof(TVarLiveMap*) * EShLangCount); + memset(outVarMaps, 0, sizeof(TVarLiveMap*) * EShLangCount); + memset(uniformVarMap, 0, sizeof(TVarLiveMap*) * EShLangCount); + memset(intermediates, 0, sizeof(TIntermediate*) * EShLangCount); + } + virtual ~TGlslIoMapper() { + for (size_t stage = 0; stage < EShLangCount; stage++) { + if (inVarMaps[stage] != nullptr) { + delete inVarMaps[stage]; + inVarMaps[stage] = nullptr; + } + if (outVarMaps[stage] != nullptr) { + delete outVarMaps[stage]; + outVarMaps[stage] = nullptr; + } + if (uniformVarMap[stage] != nullptr) { + delete uniformVarMap[stage]; + uniformVarMap[stage] = nullptr; + } + if (intermediates[stage] != nullptr) + intermediates[stage] = nullptr; + } + } + // grow the reflection stage by stage + bool addStage(EShLanguage, TIntermediate&, TInfoSink&, TIoMapResolver*) override; + bool doMap(TIoMapResolver*, TInfoSink&) override; + TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], + *uniformVarMap[EShLangCount]; + TIntermediate* intermediates[EShLangCount]; + bool hadError = false; +}; + +} // end namespace glslang + +#endif // _IOMAPPER_INCLUDED + +#endif // GLSLANG_WEB diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/limits.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/limits.cpp new file mode 100644 index 000000000..51d930034 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/limits.cpp @@ -0,0 +1,200 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Do sub tree walks for +// 1) inductive loop bodies to see if the inductive variable is modified +// 2) array-index expressions to see if they are "constant-index-expression" +// +// These are per Appendix A of ES 2.0: +// +// "Within the body of the loop, the loop index is not statically assigned to nor is it used as the +// argument to a function out or inout parameter." +// +// "The following are constant-index-expressions: +// - Constant expressions +// - Loop indices as defined in section 4 +// - Expressions composed of both of the above" +// +// N.B.: assuming the last rule excludes function calls +// + +#include "ParseHelper.h" + +namespace glslang { + +// +// The inductive loop-body traverser. +// +// Just look at things that might modify the loop index. +// + +class TInductiveTraverser : public TIntermTraverser { +public: + TInductiveTraverser(int id, TSymbolTable& st) + : loopId(id), symbolTable(st), bad(false) { } + + virtual bool visitBinary(TVisit, TIntermBinary* node); + virtual bool visitUnary(TVisit, TIntermUnary* node); + virtual bool visitAggregate(TVisit, TIntermAggregate* node); + + int loopId; // unique ID of the symbol that's the loop inductive variable + TSymbolTable& symbolTable; + bool bad; + TSourceLoc badLoc; + +protected: + TInductiveTraverser(TInductiveTraverser&); + TInductiveTraverser& operator=(TInductiveTraverser&); +}; + +// check binary operations for those modifying the loop index +bool TInductiveTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node) +{ + if (node->modifiesState() && node->getLeft()->getAsSymbolNode() && + node->getLeft()->getAsSymbolNode()->getId() == loopId) { + bad = true; + badLoc = node->getLoc(); + } + + return true; +} + +// check unary operations for those modifying the loop index +bool TInductiveTraverser::visitUnary(TVisit /* visit */, TIntermUnary* node) +{ + if (node->modifiesState() && node->getOperand()->getAsSymbolNode() && + node->getOperand()->getAsSymbolNode()->getId() == loopId) { + bad = true; + badLoc = node->getLoc(); + } + + return true; +} + +// check function calls for arguments modifying the loop index +bool TInductiveTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node) +{ + if (node->getOp() == EOpFunctionCall) { + // see if an out or inout argument is the loop index + const TIntermSequence& args = node->getSequence(); + for (int i = 0; i < (int)args.size(); ++i) { + if (args[i]->getAsSymbolNode() && args[i]->getAsSymbolNode()->getId() == loopId) { + TSymbol* function = symbolTable.find(node->getName()); + const TType* type = (*function->getAsFunction())[i].type; + if (type->getQualifier().storage == EvqOut || + type->getQualifier().storage == EvqInOut) { + bad = true; + badLoc = node->getLoc(); + } + } + } + } + + return true; +} + +// +// External function to call for loop check. +// +void TParseContext::inductiveLoopBodyCheck(TIntermNode* body, int loopId, TSymbolTable& symbolTable) +{ + TInductiveTraverser it(loopId, symbolTable); + + if (body == nullptr) + return; + + body->traverse(&it); + + if (it.bad) + error(it.badLoc, "inductive loop index modified", "limitations", ""); +} + +// +// The "constant-index-expression" tranverser. +// +// Just look at things that can form an index. +// + +class TIndexTraverser : public TIntermTraverser { +public: + TIndexTraverser(const TIdSetType& ids) : inductiveLoopIds(ids), bad(false) { } + virtual void visitSymbol(TIntermSymbol* symbol); + virtual bool visitAggregate(TVisit, TIntermAggregate* node); + const TIdSetType& inductiveLoopIds; + bool bad; + TSourceLoc badLoc; + +protected: + TIndexTraverser(TIndexTraverser&); + TIndexTraverser& operator=(TIndexTraverser&); +}; + +// make sure symbols are inductive-loop indexes +void TIndexTraverser::visitSymbol(TIntermSymbol* symbol) +{ + if (inductiveLoopIds.find(symbol->getId()) == inductiveLoopIds.end()) { + bad = true; + badLoc = symbol->getLoc(); + } +} + +// check for function calls, assuming they are bad; spec. doesn't really say +bool TIndexTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node) +{ + if (node->getOp() == EOpFunctionCall) { + bad = true; + badLoc = node->getLoc(); + } + + return true; +} + +// +// External function to call for loop check. +// +void TParseContext::constantIndexExpressionCheck(TIntermNode* index) +{ +#ifndef GLSLANG_WEB + TIndexTraverser it(inductiveLoopIds); + + index->traverse(&it); + + if (it.bad) + error(it.badLoc, "Non-constant-index-expression", "limitations", ""); +#endif +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/linkValidate.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/linkValidate.cpp new file mode 100755 index 000000000..dcb1cc8cb --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/linkValidate.cpp @@ -0,0 +1,1779 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Do link-time merging and validation of intermediate representations. +// +// Basic model is that during compilation, each compilation unit (shader) is +// compiled into one TIntermediate instance. Then, at link time, multiple +// units for the same stage can be merged together, which can generate errors. +// Then, after all merging, a single instance of TIntermediate represents +// the whole stage. A final error check can be done on the resulting stage, +// even if no merging was done (i.e., the stage was only one compilation unit). +// + +#include "localintermediate.h" +#include "../Include/InfoSink.h" + +namespace glslang { + +// +// Link-time error emitter. +// +void TIntermediate::error(TInfoSink& infoSink, const char* message) +{ +#ifndef GLSLANG_WEB + infoSink.info.prefix(EPrefixError); + infoSink.info << "Linking " << StageName(language) << " stage: " << message << "\n"; +#endif + + ++numErrors; +} + +// Link-time warning. +void TIntermediate::warn(TInfoSink& infoSink, const char* message) +{ +#ifndef GLSLANG_WEB + infoSink.info.prefix(EPrefixWarning); + infoSink.info << "Linking " << StageName(language) << " stage: " << message << "\n"; +#endif +} + +// TODO: 4.4 offset/align: "Two blocks linked together in the same program with the same block +// name must have the exact same set of members qualified with offset and their integral-constant +// expression values must be the same, or a link-time error results." + +// +// Merge the information from 'unit' into 'this' +// +void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit) +{ +#ifndef GLSLANG_WEB + mergeCallGraphs(infoSink, unit); + mergeModes(infoSink, unit); + mergeTrees(infoSink, unit); +#endif +} + +void TIntermediate::mergeCallGraphs(TInfoSink& infoSink, TIntermediate& unit) +{ + if (unit.getNumEntryPoints() > 0) { + if (getNumEntryPoints() > 0) + error(infoSink, "can't handle multiple entry points per stage"); + else { + entryPointName = unit.getEntryPointName(); + entryPointMangledName = unit.getEntryPointMangledName(); + } + } + numEntryPoints += unit.getNumEntryPoints(); + + callGraph.insert(callGraph.end(), unit.callGraph.begin(), unit.callGraph.end()); +} + +#ifndef GLSLANG_WEB + +#define MERGE_MAX(member) member = std::max(member, unit.member) +#define MERGE_TRUE(member) if (unit.member) member = unit.member; + +void TIntermediate::mergeModes(TInfoSink& infoSink, TIntermediate& unit) +{ + if (language != unit.language) + error(infoSink, "stages must match when linking into a single stage"); + + if (getSource() == EShSourceNone) + setSource(unit.getSource()); + if (getSource() != unit.getSource()) + error(infoSink, "can't link compilation units from different source languages"); + + if (treeRoot == nullptr) { + profile = unit.profile; + version = unit.version; + requestedExtensions = unit.requestedExtensions; + } else { + if ((isEsProfile()) != (unit.isEsProfile())) + error(infoSink, "Cannot cross link ES and desktop profiles"); + else if (unit.profile == ECompatibilityProfile) + profile = ECompatibilityProfile; + version = std::max(version, unit.version); + requestedExtensions.insert(unit.requestedExtensions.begin(), unit.requestedExtensions.end()); + } + + MERGE_MAX(spvVersion.spv); + MERGE_MAX(spvVersion.vulkanGlsl); + MERGE_MAX(spvVersion.vulkan); + MERGE_MAX(spvVersion.openGl); + + numErrors += unit.getNumErrors(); + // Only one push_constant is allowed, mergeLinkerObjects() will ensure the push_constant + // is the same for all units. + if (numPushConstants > 1 || unit.numPushConstants > 1) + error(infoSink, "Only one push_constant block is allowed per stage"); + numPushConstants = std::min(numPushConstants + unit.numPushConstants, 1); + + if (unit.invocations != TQualifier::layoutNotSet) { + if (invocations == TQualifier::layoutNotSet) + invocations = unit.invocations; + else if (invocations != unit.invocations) + error(infoSink, "number of invocations must match between compilation units"); + } + + if (vertices == TQualifier::layoutNotSet) + vertices = unit.vertices; + else if (vertices != unit.vertices) { + if (language == EShLangGeometry || language == EShLangMeshNV) + error(infoSink, "Contradictory layout max_vertices values"); + else if (language == EShLangTessControl) + error(infoSink, "Contradictory layout vertices values"); + else + assert(0); + } + if (primitives == TQualifier::layoutNotSet) + primitives = unit.primitives; + else if (primitives != unit.primitives) { + if (language == EShLangMeshNV) + error(infoSink, "Contradictory layout max_primitives values"); + else + assert(0); + } + + if (inputPrimitive == ElgNone) + inputPrimitive = unit.inputPrimitive; + else if (inputPrimitive != unit.inputPrimitive) + error(infoSink, "Contradictory input layout primitives"); + + if (outputPrimitive == ElgNone) + outputPrimitive = unit.outputPrimitive; + else if (outputPrimitive != unit.outputPrimitive) + error(infoSink, "Contradictory output layout primitives"); + + if (originUpperLeft != unit.originUpperLeft || pixelCenterInteger != unit.pixelCenterInteger) + error(infoSink, "gl_FragCoord redeclarations must match across shaders"); + + if (vertexSpacing == EvsNone) + vertexSpacing = unit.vertexSpacing; + else if (vertexSpacing != unit.vertexSpacing) + error(infoSink, "Contradictory input vertex spacing"); + + if (vertexOrder == EvoNone) + vertexOrder = unit.vertexOrder; + else if (vertexOrder != unit.vertexOrder) + error(infoSink, "Contradictory triangle ordering"); + + MERGE_TRUE(pointMode); + + for (int i = 0; i < 3; ++i) { + if (!localSizeNotDefault[i] && unit.localSizeNotDefault[i]) { + localSize[i] = unit.localSize[i]; + localSizeNotDefault[i] = true; + } + else if (localSize[i] != unit.localSize[i]) + error(infoSink, "Contradictory local size"); + + if (localSizeSpecId[i] == TQualifier::layoutNotSet) + localSizeSpecId[i] = unit.localSizeSpecId[i]; + else if (localSizeSpecId[i] != unit.localSizeSpecId[i]) + error(infoSink, "Contradictory local size specialization ids"); + } + + MERGE_TRUE(earlyFragmentTests); + MERGE_TRUE(postDepthCoverage); + + if (depthLayout == EldNone) + depthLayout = unit.depthLayout; + else if (depthLayout != unit.depthLayout) + error(infoSink, "Contradictory depth layouts"); + + MERGE_TRUE(depthReplacing); + MERGE_TRUE(hlslFunctionality1); + + blendEquations |= unit.blendEquations; + + MERGE_TRUE(xfbMode); + + for (size_t b = 0; b < xfbBuffers.size(); ++b) { + if (xfbBuffers[b].stride == TQualifier::layoutXfbStrideEnd) + xfbBuffers[b].stride = unit.xfbBuffers[b].stride; + else if (xfbBuffers[b].stride != unit.xfbBuffers[b].stride) + error(infoSink, "Contradictory xfb_stride"); + xfbBuffers[b].implicitStride = std::max(xfbBuffers[b].implicitStride, unit.xfbBuffers[b].implicitStride); + if (unit.xfbBuffers[b].contains64BitType) + xfbBuffers[b].contains64BitType = true; + if (unit.xfbBuffers[b].contains32BitType) + xfbBuffers[b].contains32BitType = true; + if (unit.xfbBuffers[b].contains16BitType) + xfbBuffers[b].contains16BitType = true; + // TODO: 4.4 link: enhanced layouts: compare ranges + } + + MERGE_TRUE(multiStream); + MERGE_TRUE(layoutOverrideCoverage); + MERGE_TRUE(geoPassthroughEXT); + + for (unsigned int i = 0; i < unit.shiftBinding.size(); ++i) { + if (unit.shiftBinding[i] > 0) + setShiftBinding((TResourceType)i, unit.shiftBinding[i]); + } + + for (unsigned int i = 0; i < unit.shiftBindingForSet.size(); ++i) { + for (auto it = unit.shiftBindingForSet[i].begin(); it != unit.shiftBindingForSet[i].end(); ++it) + setShiftBindingForSet((TResourceType)i, it->second, it->first); + } + + resourceSetBinding.insert(resourceSetBinding.end(), unit.resourceSetBinding.begin(), unit.resourceSetBinding.end()); + + MERGE_TRUE(autoMapBindings); + MERGE_TRUE(autoMapLocations); + MERGE_TRUE(invertY); + MERGE_TRUE(flattenUniformArrays); + MERGE_TRUE(useUnknownFormat); + MERGE_TRUE(hlslOffsets); + MERGE_TRUE(useStorageBuffer); + MERGE_TRUE(hlslIoMapping); + + // TODO: sourceFile + // TODO: sourceText + // TODO: processes + + MERGE_TRUE(needToLegalize); + MERGE_TRUE(binaryDoubleOutput); + MERGE_TRUE(usePhysicalStorageBuffer); +} + +// +// Merge the 'unit' AST into 'this' AST. +// That includes rationalizing the unique IDs, which were set up independently, +// and might have overlaps that are not the same symbol, or might have different +// IDs for what should be the same shared symbol. +// +void TIntermediate::mergeTrees(TInfoSink& infoSink, TIntermediate& unit) +{ + if (unit.treeRoot == nullptr) + return; + + if (treeRoot == nullptr) { + treeRoot = unit.treeRoot; + return; + } + + // Getting this far means we have two existing trees to merge... + numShaderRecordBlocks += unit.numShaderRecordBlocks; + numTaskNVBlocks += unit.numTaskNVBlocks; + + // Get the top-level globals of each unit + TIntermSequence& globals = treeRoot->getAsAggregate()->getSequence(); + TIntermSequence& unitGlobals = unit.treeRoot->getAsAggregate()->getSequence(); + + // Get the linker-object lists + TIntermSequence& linkerObjects = findLinkerObjects()->getSequence(); + const TIntermSequence& unitLinkerObjects = unit.findLinkerObjects()->getSequence(); + + // Map by global name to unique ID to rationalize the same object having + // differing IDs in different trees. + TIdMaps idMaps; + int maxId; + seedIdMap(idMaps, maxId); + remapIds(idMaps, maxId + 1, unit); + + mergeBodies(infoSink, globals, unitGlobals); + mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects); + ioAccessed.insert(unit.ioAccessed.begin(), unit.ioAccessed.end()); +} + +#endif + +static const TString& getNameForIdMap(TIntermSymbol* symbol) +{ + TShaderInterface si = symbol->getType().getShaderInterface(); + if (si == EsiNone) + return symbol->getName(); + else + return symbol->getType().getTypeName(); +} + + + +// Traverser that seeds an ID map with all built-ins, and tracks the +// maximum ID used. +// (It would be nice to put this in a function, but that causes warnings +// on having no bodies for the copy-constructor/operator=.) +class TBuiltInIdTraverser : public TIntermTraverser { +public: + TBuiltInIdTraverser(TIdMaps& idMaps) : idMaps(idMaps), maxId(0) { } + // If it's a built in, add it to the map. + // Track the max ID. + virtual void visitSymbol(TIntermSymbol* symbol) + { + const TQualifier& qualifier = symbol->getType().getQualifier(); + if (qualifier.builtIn != EbvNone) { + TShaderInterface si = symbol->getType().getShaderInterface(); + idMaps[si][getNameForIdMap(symbol)] = symbol->getId(); + } + maxId = std::max(maxId, symbol->getId()); + } + int getMaxId() const { return maxId; } +protected: + TBuiltInIdTraverser(TBuiltInIdTraverser&); + TBuiltInIdTraverser& operator=(TBuiltInIdTraverser&); + TIdMaps& idMaps; + int maxId; +}; + +// Traverser that seeds an ID map with non-builtins. +// (It would be nice to put this in a function, but that causes warnings +// on having no bodies for the copy-constructor/operator=.) +class TUserIdTraverser : public TIntermTraverser { +public: + TUserIdTraverser(TIdMaps& idMaps) : idMaps(idMaps) { } + // If its a non-built-in global, add it to the map. + virtual void visitSymbol(TIntermSymbol* symbol) + { + const TQualifier& qualifier = symbol->getType().getQualifier(); + if (qualifier.builtIn == EbvNone) { + TShaderInterface si = symbol->getType().getShaderInterface(); + idMaps[si][getNameForIdMap(symbol)] = symbol->getId(); + } + } + +protected: + TUserIdTraverser(TUserIdTraverser&); + TUserIdTraverser& operator=(TUserIdTraverser&); + TIdMaps& idMaps; // over biggest id +}; + +// Initialize the the ID map with what we know of 'this' AST. +void TIntermediate::seedIdMap(TIdMaps& idMaps, int& maxId) +{ + // all built-ins everywhere need to align on IDs and contribute to the max ID + TBuiltInIdTraverser builtInIdTraverser(idMaps); + treeRoot->traverse(&builtInIdTraverser); + maxId = builtInIdTraverser.getMaxId(); + + // user variables in the linker object list need to align on ids + TUserIdTraverser userIdTraverser(idMaps); + findLinkerObjects()->traverse(&userIdTraverser); +} + +// Traverser to map an AST ID to what was known from the seeding AST. +// (It would be nice to put this in a function, but that causes warnings +// on having no bodies for the copy-constructor/operator=.) +class TRemapIdTraverser : public TIntermTraverser { +public: + TRemapIdTraverser(const TIdMaps& idMaps, int idShift) : idMaps(idMaps), idShift(idShift) { } + // Do the mapping: + // - if the same symbol, adopt the 'this' ID + // - otherwise, ensure a unique ID by shifting to a new space + virtual void visitSymbol(TIntermSymbol* symbol) + { + const TQualifier& qualifier = symbol->getType().getQualifier(); + bool remapped = false; + if (qualifier.isLinkable() || qualifier.builtIn != EbvNone) { + TShaderInterface si = symbol->getType().getShaderInterface(); + auto it = idMaps[si].find(getNameForIdMap(symbol)); + if (it != idMaps[si].end()) { + symbol->changeId(it->second); + remapped = true; + } + } + if (!remapped) + symbol->changeId(symbol->getId() + idShift); + } +protected: + TRemapIdTraverser(TRemapIdTraverser&); + TRemapIdTraverser& operator=(TRemapIdTraverser&); + const TIdMaps& idMaps; + int idShift; +}; + +void TIntermediate::remapIds(const TIdMaps& idMaps, int idShift, TIntermediate& unit) +{ + // Remap all IDs to either share or be unique, as dictated by the idMap and idShift. + TRemapIdTraverser idTraverser(idMaps, idShift); + unit.getTreeRoot()->traverse(&idTraverser); +} + +// +// Merge the function bodies and global-level initializers from unitGlobals into globals. +// Will error check duplication of function bodies for the same signature. +// +void TIntermediate::mergeBodies(TInfoSink& infoSink, TIntermSequence& globals, const TIntermSequence& unitGlobals) +{ + // TODO: link-time performance: Processing in alphabetical order will be faster + + // Error check the global objects, not including the linker objects + for (unsigned int child = 0; child < globals.size() - 1; ++child) { + for (unsigned int unitChild = 0; unitChild < unitGlobals.size() - 1; ++unitChild) { + TIntermAggregate* body = globals[child]->getAsAggregate(); + TIntermAggregate* unitBody = unitGlobals[unitChild]->getAsAggregate(); + if (body && unitBody && body->getOp() == EOpFunction && unitBody->getOp() == EOpFunction && body->getName() == unitBody->getName()) { + error(infoSink, "Multiple function bodies in multiple compilation units for the same signature in the same stage:"); + infoSink.info << " " << globals[child]->getAsAggregate()->getName() << "\n"; + } + } + } + + // Merge the global objects, just in front of the linker objects + globals.insert(globals.end() - 1, unitGlobals.begin(), unitGlobals.end() - 1); +} + +// +// Merge the linker objects from unitLinkerObjects into linkerObjects. +// Duplication is expected and filtered out, but contradictions are an error. +// +void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects) +{ + // Error check and merge the linker objects (duplicates should not be created) + std::size_t initialNumLinkerObjects = linkerObjects.size(); + for (unsigned int unitLinkObj = 0; unitLinkObj < unitLinkerObjects.size(); ++unitLinkObj) { + bool merge = true; + for (std::size_t linkObj = 0; linkObj < initialNumLinkerObjects; ++linkObj) { + TIntermSymbol* symbol = linkerObjects[linkObj]->getAsSymbolNode(); + TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode(); + assert(symbol && unitSymbol); + + bool isSameSymbol = false; + // If they are both blocks in the same shader interface, + // match by the block-name, not the identifier name. + if (symbol->getType().getBasicType() == EbtBlock && unitSymbol->getType().getBasicType() == EbtBlock) { + if (symbol->getType().getShaderInterface() == unitSymbol->getType().getShaderInterface()) { + isSameSymbol = symbol->getType().getTypeName() == unitSymbol->getType().getTypeName(); + } + } + else if (symbol->getName() == unitSymbol->getName()) + isSameSymbol = true; + + if (isSameSymbol) { + // filter out copy + merge = false; + + // but if one has an initializer and the other does not, update + // the initializer + if (symbol->getConstArray().empty() && ! unitSymbol->getConstArray().empty()) + symbol->setConstArray(unitSymbol->getConstArray()); + + // Similarly for binding + if (! symbol->getQualifier().hasBinding() && unitSymbol->getQualifier().hasBinding()) + symbol->getQualifier().layoutBinding = unitSymbol->getQualifier().layoutBinding; + + // Update implicit array sizes + mergeImplicitArraySizes(symbol->getWritableType(), unitSymbol->getType()); + + // Check for consistent types/qualification/initializers etc. + mergeErrorCheck(infoSink, *symbol, *unitSymbol, false); + } + // If different symbols, verify they arn't push_constant since there can only be one per stage + else if (symbol->getQualifier().isPushConstant() && unitSymbol->getQualifier().isPushConstant()) + error(infoSink, "Only one push_constant block is allowed per stage"); + } + if (merge) + linkerObjects.push_back(unitLinkerObjects[unitLinkObj]); + } +} + +// TODO 4.5 link functionality: cull distance array size checking + +// Recursively merge the implicit array sizes through the objects' respective type trees. +void TIntermediate::mergeImplicitArraySizes(TType& type, const TType& unitType) +{ + if (type.isUnsizedArray()) { + if (unitType.isUnsizedArray()) { + type.updateImplicitArraySize(unitType.getImplicitArraySize()); + if (unitType.isArrayVariablyIndexed()) + type.setArrayVariablyIndexed(); + } else if (unitType.isSizedArray()) + type.changeOuterArraySize(unitType.getOuterArraySize()); + } + + // Type mismatches are caught and reported after this, just be careful for now. + if (! type.isStruct() || ! unitType.isStruct() || type.getStruct()->size() != unitType.getStruct()->size()) + return; + + for (int i = 0; i < (int)type.getStruct()->size(); ++i) + mergeImplicitArraySizes(*(*type.getStruct())[i].type, *(*unitType.getStruct())[i].type); +} + +// +// Compare two global objects from two compilation units and see if they match +// well enough. Rules can be different for intra- vs. cross-stage matching. +// +// This function only does one of intra- or cross-stage matching per call. +// +void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& symbol, const TIntermSymbol& unitSymbol, bool crossStage) +{ +#ifndef GLSLANG_WEB + bool writeTypeComparison = false; + + // Types have to match + if (symbol.getType() != unitSymbol.getType()) { + // but, we make an exception if one is an implicit array and the other is sized + if (! (symbol.getType().isArray() && unitSymbol.getType().isArray() && + symbol.getType().sameElementType(unitSymbol.getType()) && + (symbol.getType().isUnsizedArray() || unitSymbol.getType().isUnsizedArray()))) { + error(infoSink, "Types must match:"); + writeTypeComparison = true; + } + } + + // Qualifiers have to (almost) match + + // Storage... + if (symbol.getQualifier().storage != unitSymbol.getQualifier().storage) { + error(infoSink, "Storage qualifiers must match:"); + writeTypeComparison = true; + } + + // Uniform and buffer blocks must either both have an instance name, or + // must both be anonymous. The names don't need to match though. + if (symbol.getQualifier().isUniformOrBuffer() && + (IsAnonymous(symbol.getName()) != IsAnonymous(unitSymbol.getName()))) { + error(infoSink, "Matched Uniform or Storage blocks must all be anonymous," + " or all be named:"); + writeTypeComparison = true; + } + + if (symbol.getQualifier().storage == unitSymbol.getQualifier().storage && + (IsAnonymous(symbol.getName()) != IsAnonymous(unitSymbol.getName()) || + (!IsAnonymous(symbol.getName()) && symbol.getName() != unitSymbol.getName()))) { + warn(infoSink, "Matched shader interfaces are using different instance names."); + writeTypeComparison = true; + } + + // Precision... + if (symbol.getQualifier().precision != unitSymbol.getQualifier().precision) { + error(infoSink, "Precision qualifiers must match:"); + writeTypeComparison = true; + } + + // Invariance... + if (! crossStage && symbol.getQualifier().invariant != unitSymbol.getQualifier().invariant) { + error(infoSink, "Presence of invariant qualifier must match:"); + writeTypeComparison = true; + } + + // Precise... + if (! crossStage && symbol.getQualifier().isNoContraction() != unitSymbol.getQualifier().isNoContraction()) { + error(infoSink, "Presence of precise qualifier must match:"); + writeTypeComparison = true; + } + + // Auxiliary and interpolation... + if (symbol.getQualifier().centroid != unitSymbol.getQualifier().centroid || + symbol.getQualifier().smooth != unitSymbol.getQualifier().smooth || + symbol.getQualifier().flat != unitSymbol.getQualifier().flat || + symbol.getQualifier().isSample()!= unitSymbol.getQualifier().isSample() || + symbol.getQualifier().isPatch() != unitSymbol.getQualifier().isPatch() || + symbol.getQualifier().isNonPerspective() != unitSymbol.getQualifier().isNonPerspective()) { + error(infoSink, "Interpolation and auxiliary storage qualifiers must match:"); + writeTypeComparison = true; + } + + // Memory... + if (symbol.getQualifier().coherent != unitSymbol.getQualifier().coherent || + symbol.getQualifier().devicecoherent != unitSymbol.getQualifier().devicecoherent || + symbol.getQualifier().queuefamilycoherent != unitSymbol.getQualifier().queuefamilycoherent || + symbol.getQualifier().workgroupcoherent != unitSymbol.getQualifier().workgroupcoherent || + symbol.getQualifier().subgroupcoherent != unitSymbol.getQualifier().subgroupcoherent || + symbol.getQualifier().shadercallcoherent!= unitSymbol.getQualifier().shadercallcoherent || + symbol.getQualifier().nonprivate != unitSymbol.getQualifier().nonprivate || + symbol.getQualifier().volatil != unitSymbol.getQualifier().volatil || + symbol.getQualifier().restrict != unitSymbol.getQualifier().restrict || + symbol.getQualifier().readonly != unitSymbol.getQualifier().readonly || + symbol.getQualifier().writeonly != unitSymbol.getQualifier().writeonly) { + error(infoSink, "Memory qualifiers must match:"); + writeTypeComparison = true; + } + + // Layouts... + // TODO: 4.4 enhanced layouts: Generalize to include offset/align: current spec + // requires separate user-supplied offset from actual computed offset, but + // current implementation only has one offset. + if (symbol.getQualifier().layoutMatrix != unitSymbol.getQualifier().layoutMatrix || + symbol.getQualifier().layoutPacking != unitSymbol.getQualifier().layoutPacking || + symbol.getQualifier().layoutLocation != unitSymbol.getQualifier().layoutLocation || + symbol.getQualifier().layoutComponent != unitSymbol.getQualifier().layoutComponent || + symbol.getQualifier().layoutIndex != unitSymbol.getQualifier().layoutIndex || + symbol.getQualifier().layoutBinding != unitSymbol.getQualifier().layoutBinding || + (symbol.getQualifier().hasBinding() && (symbol.getQualifier().layoutOffset != unitSymbol.getQualifier().layoutOffset))) { + error(infoSink, "Layout qualification must match:"); + writeTypeComparison = true; + } + + // Initializers have to match, if both are present, and if we don't already know the types don't match + if (! writeTypeComparison) { + if (! symbol.getConstArray().empty() && ! unitSymbol.getConstArray().empty()) { + if (symbol.getConstArray() != unitSymbol.getConstArray()) { + error(infoSink, "Initializers must match:"); + infoSink.info << " " << symbol.getName() << "\n"; + } + } + } + + if (writeTypeComparison) { + infoSink.info << " " << symbol.getName() << ": \"" << symbol.getType().getCompleteString() << "\" versus "; + if (symbol.getName() != unitSymbol.getName()) + infoSink.info << unitSymbol.getName() << ": "; + + infoSink.info << "\"" << unitSymbol.getType().getCompleteString() << "\"\n"; + } +#endif +} + +// +// Do final link-time error checking of a complete (merged) intermediate representation. +// (Much error checking was done during merging). +// +// Also, lock in defaults of things not set, including array sizes. +// +void TIntermediate::finalCheck(TInfoSink& infoSink, bool keepUncalled) +{ + if (getTreeRoot() == nullptr) + return; + + if (numEntryPoints < 1) { + if (getSource() == EShSourceGlsl) + error(infoSink, "Missing entry point: Each stage requires one entry point"); + else + warn(infoSink, "Entry point not found"); + } + + // recursion and missing body checking + checkCallGraphCycles(infoSink); + checkCallGraphBodies(infoSink, keepUncalled); + + // overlap/alias/missing I/O, etc. + inOutLocationCheck(infoSink); + +#ifndef GLSLANG_WEB + if (getNumPushConstants() > 1) + error(infoSink, "Only one push_constant block is allowed per stage"); + + // invocations + if (invocations == TQualifier::layoutNotSet) + invocations = 1; + + if (inIoAccessed("gl_ClipDistance") && inIoAccessed("gl_ClipVertex")) + error(infoSink, "Can only use one of gl_ClipDistance or gl_ClipVertex (gl_ClipDistance is preferred)"); + if (inIoAccessed("gl_CullDistance") && inIoAccessed("gl_ClipVertex")) + error(infoSink, "Can only use one of gl_CullDistance or gl_ClipVertex (gl_ClipDistance is preferred)"); + + if (userOutputUsed() && (inIoAccessed("gl_FragColor") || inIoAccessed("gl_FragData"))) + error(infoSink, "Cannot use gl_FragColor or gl_FragData when using user-defined outputs"); + if (inIoAccessed("gl_FragColor") && inIoAccessed("gl_FragData")) + error(infoSink, "Cannot use both gl_FragColor and gl_FragData"); + + for (size_t b = 0; b < xfbBuffers.size(); ++b) { + if (xfbBuffers[b].contains64BitType) + RoundToPow2(xfbBuffers[b].implicitStride, 8); + else if (xfbBuffers[b].contains32BitType) + RoundToPow2(xfbBuffers[b].implicitStride, 4); + else if (xfbBuffers[b].contains16BitType) + RoundToPow2(xfbBuffers[b].implicitStride, 2); + + // "It is a compile-time or link-time error to have + // any xfb_offset that overflows xfb_stride, whether stated on declarations before or after the xfb_stride, or + // in different compilation units. While xfb_stride can be declared multiple times for the same buffer, it is a + // compile-time or link-time error to have different values specified for the stride for the same buffer." + if (xfbBuffers[b].stride != TQualifier::layoutXfbStrideEnd && xfbBuffers[b].implicitStride > xfbBuffers[b].stride) { + error(infoSink, "xfb_stride is too small to hold all buffer entries:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << ", minimum stride needed: " << xfbBuffers[b].implicitStride << "\n"; + } + if (xfbBuffers[b].stride == TQualifier::layoutXfbStrideEnd) + xfbBuffers[b].stride = xfbBuffers[b].implicitStride; + + // "If the buffer is capturing any + // outputs with double-precision or 64-bit integer components, the stride must be a multiple of 8, otherwise it must be a + // multiple of 4, or a compile-time or link-time error results." + if (xfbBuffers[b].contains64BitType && ! IsMultipleOfPow2(xfbBuffers[b].stride, 8)) { + error(infoSink, "xfb_stride must be multiple of 8 for buffer holding a double or 64-bit integer:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n"; + } else if (xfbBuffers[b].contains32BitType && ! IsMultipleOfPow2(xfbBuffers[b].stride, 4)) { + error(infoSink, "xfb_stride must be multiple of 4:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n"; + } + // "If the buffer is capturing any + // outputs with half-precision or 16-bit integer components, the stride must be a multiple of 2" + else if (xfbBuffers[b].contains16BitType && ! IsMultipleOfPow2(xfbBuffers[b].stride, 2)) { + error(infoSink, "xfb_stride must be multiple of 2 for buffer holding a half float or 16-bit integer:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n"; + } + + // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the + // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents." + if (xfbBuffers[b].stride > (unsigned int)(4 * resources.maxTransformFeedbackInterleavedComponents)) { + error(infoSink, "xfb_stride is too large:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", components (1/4 stride) needed are " << xfbBuffers[b].stride/4 << ", gl_MaxTransformFeedbackInterleavedComponents is " << resources.maxTransformFeedbackInterleavedComponents << "\n"; + } + } + + switch (language) { + case EShLangVertex: + break; + case EShLangTessControl: + if (vertices == TQualifier::layoutNotSet) + error(infoSink, "At least one shader must specify an output layout(vertices=...)"); + break; + case EShLangTessEvaluation: + if (getSource() == EShSourceGlsl) { + if (inputPrimitive == ElgNone) + error(infoSink, "At least one shader must specify an input layout primitive"); + if (vertexSpacing == EvsNone) + vertexSpacing = EvsEqual; + if (vertexOrder == EvoNone) + vertexOrder = EvoCcw; + } + break; + case EShLangGeometry: + if (inputPrimitive == ElgNone) + error(infoSink, "At least one shader must specify an input layout primitive"); + if (outputPrimitive == ElgNone) + error(infoSink, "At least one shader must specify an output layout primitive"); + if (vertices == TQualifier::layoutNotSet) + error(infoSink, "At least one shader must specify a layout(max_vertices = value)"); + break; + case EShLangFragment: + // for GL_ARB_post_depth_coverage, EarlyFragmentTest is set automatically in + // ParseHelper.cpp. So if we reach here, this must be GL_EXT_post_depth_coverage + // requiring explicit early_fragment_tests + if (getPostDepthCoverage() && !getEarlyFragmentTests()) + error(infoSink, "post_depth_coverage requires early_fragment_tests"); + break; + case EShLangCompute: + break; + case EShLangRayGen: + case EShLangIntersect: + case EShLangAnyHit: + case EShLangClosestHit: + case EShLangMiss: + case EShLangCallable: + if (numShaderRecordBlocks > 1) + error(infoSink, "Only one shaderRecordNV buffer block is allowed per stage"); + break; + case EShLangMeshNV: + // NV_mesh_shader doesn't allow use of both single-view and per-view builtins. + if (inIoAccessed("gl_Position") && inIoAccessed("gl_PositionPerViewNV")) + error(infoSink, "Can only use one of gl_Position or gl_PositionPerViewNV"); + if (inIoAccessed("gl_ClipDistance") && inIoAccessed("gl_ClipDistancePerViewNV")) + error(infoSink, "Can only use one of gl_ClipDistance or gl_ClipDistancePerViewNV"); + if (inIoAccessed("gl_CullDistance") && inIoAccessed("gl_CullDistancePerViewNV")) + error(infoSink, "Can only use one of gl_CullDistance or gl_CullDistancePerViewNV"); + if (inIoAccessed("gl_Layer") && inIoAccessed("gl_LayerPerViewNV")) + error(infoSink, "Can only use one of gl_Layer or gl_LayerPerViewNV"); + if (inIoAccessed("gl_ViewportMask") && inIoAccessed("gl_ViewportMaskPerViewNV")) + error(infoSink, "Can only use one of gl_ViewportMask or gl_ViewportMaskPerViewNV"); + if (outputPrimitive == ElgNone) + error(infoSink, "At least one shader must specify an output layout primitive"); + if (vertices == TQualifier::layoutNotSet) + error(infoSink, "At least one shader must specify a layout(max_vertices = value)"); + if (primitives == TQualifier::layoutNotSet) + error(infoSink, "At least one shader must specify a layout(max_primitives = value)"); + // fall through + case EShLangTaskNV: + if (numTaskNVBlocks > 1) + error(infoSink, "Only one taskNV interface block is allowed per shader"); + break; + default: + error(infoSink, "Unknown Stage."); + break; + } + + // Process the tree for any node-specific work. + class TFinalLinkTraverser : public TIntermTraverser { + public: + TFinalLinkTraverser() { } + virtual ~TFinalLinkTraverser() { } + + virtual void visitSymbol(TIntermSymbol* symbol) + { + // Implicitly size arrays. + // If an unsized array is left as unsized, it effectively + // becomes run-time sized. + symbol->getWritableType().adoptImplicitArraySizes(false); + } + } finalLinkTraverser; + + treeRoot->traverse(&finalLinkTraverser); +#endif +} + +// +// See if the call graph contains any static recursion, which is disallowed +// by the specification. +// +void TIntermediate::checkCallGraphCycles(TInfoSink& infoSink) +{ + // Clear fields we'll use for this. + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + call->visited = false; + call->currentPath = false; + call->errorGiven = false; + } + + // + // Loop, looking for a new connected subgraph. One subgraph is handled per loop iteration. + // + + TCall* newRoot; + do { + // See if we have unvisited parts of the graph. + newRoot = 0; + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (! call->visited) { + newRoot = &(*call); + break; + } + } + + // If not, we are done. + if (! newRoot) + break; + + // Otherwise, we found a new subgraph, process it: + // See what all can be reached by this new root, and if any of + // that is recursive. This is done by depth-first traversals, seeing + // if a new call is found that was already in the currentPath (a back edge), + // thereby detecting recursion. + std::list stack; + newRoot->currentPath = true; // currentPath will be true iff it is on the stack + stack.push_back(newRoot); + while (! stack.empty()) { + // get a caller + TCall* call = stack.back(); + + // Add to the stack just one callee. + // This algorithm always terminates, because only !visited and !currentPath causes a push + // and all pushes change currentPath to true, and all pops change visited to true. + TGraph::iterator child = callGraph.begin(); + for (; child != callGraph.end(); ++child) { + + // If we already visited this node, its whole subgraph has already been processed, so skip it. + if (child->visited) + continue; + + if (call->callee == child->caller) { + if (child->currentPath) { + // Then, we found a back edge + if (! child->errorGiven) { + error(infoSink, "Recursion detected:"); + infoSink.info << " " << call->callee << " calling " << child->callee << "\n"; + child->errorGiven = true; + recursive = true; + } + } else { + child->currentPath = true; + stack.push_back(&(*child)); + break; + } + } + } + if (child == callGraph.end()) { + // no more callees, we bottomed out, never look at this node again + stack.back()->currentPath = false; + stack.back()->visited = true; + stack.pop_back(); + } + } // end while, meaning nothing left to process in this subtree + + } while (newRoot); // redundant loop check; should always exit via the 'break' above +} + +// +// See which functions are reachable from the entry point and which have bodies. +// Reachable ones with missing bodies are errors. +// Unreachable bodies are dead code. +// +void TIntermediate::checkCallGraphBodies(TInfoSink& infoSink, bool keepUncalled) +{ + // Clear fields we'll use for this. + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + call->visited = false; + call->calleeBodyPosition = -1; + } + + // The top level of the AST includes function definitions (bodies). + // Compare these to function calls in the call graph. + // We'll end up knowing which have bodies, and if so, + // how to map the call-graph node to the location in the AST. + TIntermSequence &functionSequence = getTreeRoot()->getAsAggregate()->getSequence(); + std::vector reachable(functionSequence.size(), true); // so that non-functions are reachable + for (int f = 0; f < (int)functionSequence.size(); ++f) { + glslang::TIntermAggregate* node = functionSequence[f]->getAsAggregate(); + if (node && (node->getOp() == glslang::EOpFunction)) { + if (node->getName().compare(getEntryPointMangledName().c_str()) != 0) + reachable[f] = false; // so that function bodies are unreachable, until proven otherwise + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (call->callee == node->getName()) + call->calleeBodyPosition = f; + } + } + } + + // Start call-graph traversal by visiting the entry point nodes. + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (call->caller.compare(getEntryPointMangledName().c_str()) == 0) + call->visited = true; + } + + // Propagate 'visited' through the call-graph to every part of the graph it + // can reach (seeded with the entry-point setting above). + bool changed; + do { + changed = false; + for (auto call1 = callGraph.begin(); call1 != callGraph.end(); ++call1) { + if (call1->visited) { + for (TGraph::iterator call2 = callGraph.begin(); call2 != callGraph.end(); ++call2) { + if (! call2->visited) { + if (call1->callee == call2->caller) { + changed = true; + call2->visited = true; + } + } + } + } + } + } while (changed); + + // Any call-graph node set to visited but without a callee body is an error. + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (call->visited) { + if (call->calleeBodyPosition == -1) { + error(infoSink, "No function definition (body) found: "); + infoSink.info << " " << call->callee << "\n"; + } else + reachable[call->calleeBodyPosition] = true; + } + } + + // Bodies in the AST not reached by the call graph are dead; + // clear them out, since they can't be reached and also can't + // be translated further due to possibility of being ill defined. + if (! keepUncalled) { + for (int f = 0; f < (int)functionSequence.size(); ++f) { + if (! reachable[f]) + functionSequence[f] = nullptr; + } + functionSequence.erase(std::remove(functionSequence.begin(), functionSequence.end(), nullptr), functionSequence.end()); + } +} + +// +// Satisfy rules for location qualifiers on inputs and outputs +// +void TIntermediate::inOutLocationCheck(TInfoSink& infoSink) +{ + // ES 3.0 requires all outputs to have location qualifiers if there is more than one output + bool fragOutWithNoLocation = false; + int numFragOut = 0; + + // TODO: linker functionality: location collision checking + + TIntermSequence& linkObjects = findLinkerObjects()->getSequence(); + for (size_t i = 0; i < linkObjects.size(); ++i) { + const TType& type = linkObjects[i]->getAsTyped()->getType(); + const TQualifier& qualifier = type.getQualifier(); + if (language == EShLangFragment) { + if (qualifier.storage == EvqVaryingOut && qualifier.builtIn == EbvNone) { + ++numFragOut; + if (!qualifier.hasAnyLocation()) + fragOutWithNoLocation = true; + } + } + } + + if (isEsProfile()) { + if (numFragOut > 1 && fragOutWithNoLocation) + error(infoSink, "when more than one fragment shader output, all must have location qualifiers"); + } +} + +TIntermAggregate* TIntermediate::findLinkerObjects() const +{ + // Get the top-level globals + TIntermSequence& globals = treeRoot->getAsAggregate()->getSequence(); + + // Get the last member of the sequences, expected to be the linker-object lists + assert(globals.back()->getAsAggregate()->getOp() == EOpLinkerObjects); + + return globals.back()->getAsAggregate(); +} + +// See if a variable was both a user-declared output and used. +// Note: the spec discusses writing to one, but this looks at read or write, which +// is more useful, and perhaps the spec should be changed to reflect that. +bool TIntermediate::userOutputUsed() const +{ + const TIntermSequence& linkerObjects = findLinkerObjects()->getSequence(); + + bool found = false; + for (size_t i = 0; i < linkerObjects.size(); ++i) { + const TIntermSymbol& symbolNode = *linkerObjects[i]->getAsSymbolNode(); + if (symbolNode.getQualifier().storage == EvqVaryingOut && + symbolNode.getName().compare(0, 3, "gl_") != 0 && + inIoAccessed(symbolNode.getName())) { + found = true; + break; + } + } + + return found; +} + +// Accumulate locations used for inputs, outputs, and uniforms, and check for collisions +// as the accumulation is done. +// +// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. +// +// typeCollision is set to true if there is no direct collision, but the types in the same location +// are different. +// +int TIntermediate::addUsedLocation(const TQualifier& qualifier, const TType& type, bool& typeCollision) +{ + typeCollision = false; + + int set; + if (qualifier.isPipeInput()) + set = 0; + else if (qualifier.isPipeOutput()) + set = 1; + else if (qualifier.storage == EvqUniform) + set = 2; + else if (qualifier.storage == EvqBuffer) + set = 3; + else + return -1; + + int size; + if (qualifier.isUniformOrBuffer() || qualifier.isTaskMemory()) { + if (type.isSizedArray()) + size = type.getCumulativeArraySize(); + else + size = 1; + } else { + // Strip off the outer array dimension for those having an extra one. + if (type.isArray() && qualifier.isArrayedIo(language)) { + TType elementType(type, 0); + size = computeTypeLocationSize(elementType, language); + } else + size = computeTypeLocationSize(type, language); + } + + // Locations, and components within locations. + // + // Almost always, dealing with components means a single location is involved. + // The exception is a dvec3. From the spec: + // + // "A dvec3 will consume all four components of the first location and components 0 and 1 of + // the second location. This leaves components 2 and 3 available for other component-qualified + // declarations." + // + // That means, without ever mentioning a component, a component range + // for a different location gets specified, if it's not a vertex shader input. (!) + // (A vertex shader input will show using only one location, even for a dvec3/4.) + // + // So, for the case of dvec3, we need two independent ioRanges. + + int collision = -1; // no collision +#ifndef GLSLANG_WEB + if (size == 2 && type.getBasicType() == EbtDouble && type.getVectorSize() == 3 && + (qualifier.isPipeInput() || qualifier.isPipeOutput())) { + // Dealing with dvec3 in/out split across two locations. + // Need two io-ranges. + // The case where the dvec3 doesn't start at component 0 was previously caught as overflow. + + // First range: + TRange locationRange(qualifier.layoutLocation, qualifier.layoutLocation); + TRange componentRange(0, 3); + TIoRange range(locationRange, componentRange, type.getBasicType(), 0); + + // check for collisions + collision = checkLocationRange(set, range, type, typeCollision); + if (collision < 0) { + usedIo[set].push_back(range); + + // Second range: + TRange locationRange2(qualifier.layoutLocation + 1, qualifier.layoutLocation + 1); + TRange componentRange2(0, 1); + TIoRange range2(locationRange2, componentRange2, type.getBasicType(), 0); + + // check for collisions + collision = checkLocationRange(set, range2, type, typeCollision); + if (collision < 0) + usedIo[set].push_back(range2); + } + } else +#endif + { + // Not a dvec3 in/out split across two locations, generic path. + // Need a single IO-range block. + + TRange locationRange(qualifier.layoutLocation, qualifier.layoutLocation + size - 1); + TRange componentRange(0, 3); + if (qualifier.hasComponent() || type.getVectorSize() > 0) { + int consumedComponents = type.getVectorSize() * (type.getBasicType() == EbtDouble ? 2 : 1); + if (qualifier.hasComponent()) + componentRange.start = qualifier.layoutComponent; + componentRange.last = componentRange.start + consumedComponents - 1; + } + + // combine location and component ranges + TIoRange range(locationRange, componentRange, type.getBasicType(), qualifier.hasIndex() ? qualifier.getIndex() : 0); + + // check for collisions, except for vertex inputs on desktop targeting OpenGL + if (! (!isEsProfile() && language == EShLangVertex && qualifier.isPipeInput()) || spvVersion.vulkan > 0) + collision = checkLocationRange(set, range, type, typeCollision); + + if (collision < 0) + usedIo[set].push_back(range); + } + + return collision; +} + +// Compare a new (the passed in) 'range' against the existing set, and see +// if there are any collisions. +// +// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. +// +int TIntermediate::checkLocationRange(int set, const TIoRange& range, const TType& type, bool& typeCollision) +{ + for (size_t r = 0; r < usedIo[set].size(); ++r) { + if (range.overlap(usedIo[set][r])) { + // there is a collision; pick one + return std::max(range.location.start, usedIo[set][r].location.start); + } else if (range.location.overlap(usedIo[set][r].location) && type.getBasicType() != usedIo[set][r].basicType) { + // aliased-type mismatch + typeCollision = true; + return std::max(range.location.start, usedIo[set][r].location.start); + } + } + + return -1; // no collision +} + +// Accumulate bindings and offsets, and check for collisions +// as the accumulation is done. +// +// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. +// +int TIntermediate::addUsedOffsets(int binding, int offset, int numOffsets) +{ + TRange bindingRange(binding, binding); + TRange offsetRange(offset, offset + numOffsets - 1); + TOffsetRange range(bindingRange, offsetRange); + + // check for collisions, except for vertex inputs on desktop + for (size_t r = 0; r < usedAtomics.size(); ++r) { + if (range.overlap(usedAtomics[r])) { + // there is a collision; pick one + return std::max(offset, usedAtomics[r].offset.start); + } + } + + usedAtomics.push_back(range); + + return -1; // no collision +} + +// Accumulate used constant_id values. +// +// Return false is one was already used. +bool TIntermediate::addUsedConstantId(int id) +{ + if (usedConstantId.find(id) != usedConstantId.end()) + return false; + + usedConstantId.insert(id); + + return true; +} + +// Recursively figure out how many locations are used up by an input or output type. +// Return the size of type, as measured by "locations". +int TIntermediate::computeTypeLocationSize(const TType& type, EShLanguage stage) +{ + // "If the declared input is an array of size n and each element takes m locations, it will be assigned m * n + // consecutive locations..." + if (type.isArray()) { + // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness + // TODO: are there valid cases of having an unsized array with a location? If so, running this code too early. + TType elementType(type, 0); + if (type.isSizedArray() && !type.getQualifier().isPerView()) + return type.getOuterArraySize() * computeTypeLocationSize(elementType, stage); + else { +#ifndef GLSLANG_WEB + // unset perViewNV attributes for arrayed per-view outputs: "perviewNV vec4 v[MAX_VIEWS][3];" + elementType.getQualifier().perViewNV = false; +#endif + return computeTypeLocationSize(elementType, stage); + } + } + + // "The locations consumed by block and structure members are determined by applying the rules above + // recursively..." + if (type.isStruct()) { + int size = 0; + for (int member = 0; member < (int)type.getStruct()->size(); ++member) { + TType memberType(type, member); + size += computeTypeLocationSize(memberType, stage); + } + return size; + } + + // ES: "If a shader input is any scalar or vector type, it will consume a single location." + + // Desktop: "If a vertex shader input is any scalar or vector type, it will consume a single location. If a non-vertex + // shader input is a scalar or vector type other than dvec3 or dvec4, it will consume a single location, while + // types dvec3 or dvec4 will consume two consecutive locations. Inputs of type double and dvec2 will + // consume only a single location, in all stages." + if (type.isScalar()) + return 1; + if (type.isVector()) { + if (stage == EShLangVertex && type.getQualifier().isPipeInput()) + return 1; + if (type.getBasicType() == EbtDouble && type.getVectorSize() > 2) + return 2; + else + return 1; + } + + // "If the declared input is an n x m single- or double-precision matrix, ... + // The number of locations assigned for each matrix will be the same as + // for an n-element array of m-component vectors..." + if (type.isMatrix()) { + TType columnType(type, 0); + return type.getMatrixCols() * computeTypeLocationSize(columnType, stage); + } + + assert(0); + return 1; +} + +// Same as computeTypeLocationSize but for uniforms +int TIntermediate::computeTypeUniformLocationSize(const TType& type) +{ + // "Individual elements of a uniform array are assigned + // consecutive locations with the first element taking location + // location." + if (type.isArray()) { + // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness + TType elementType(type, 0); + if (type.isSizedArray()) { + return type.getOuterArraySize() * computeTypeUniformLocationSize(elementType); + } else { + // TODO: are there valid cases of having an implicitly-sized array with a location? If so, running this code too early. + return computeTypeUniformLocationSize(elementType); + } + } + + // "Each subsequent inner-most member or element gets incremental + // locations for the entire structure or array." + if (type.isStruct()) { + int size = 0; + for (int member = 0; member < (int)type.getStruct()->size(); ++member) { + TType memberType(type, member); + size += computeTypeUniformLocationSize(memberType); + } + return size; + } + + return 1; +} + +#ifndef GLSLANG_WEB + +// Accumulate xfb buffer ranges and check for collisions as the accumulation is done. +// +// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. +// +int TIntermediate::addXfbBufferOffset(const TType& type) +{ + const TQualifier& qualifier = type.getQualifier(); + + assert(qualifier.hasXfbOffset() && qualifier.hasXfbBuffer()); + TXfbBuffer& buffer = xfbBuffers[qualifier.layoutXfbBuffer]; + + // compute the range + unsigned int size = computeTypeXfbSize(type, buffer.contains64BitType, buffer.contains32BitType, buffer.contains16BitType); + buffer.implicitStride = std::max(buffer.implicitStride, qualifier.layoutXfbOffset + size); + TRange range(qualifier.layoutXfbOffset, qualifier.layoutXfbOffset + size - 1); + + // check for collisions + for (size_t r = 0; r < buffer.ranges.size(); ++r) { + if (range.overlap(buffer.ranges[r])) { + // there is a collision; pick an example to return + return std::max(range.start, buffer.ranges[r].start); + } + } + + buffer.ranges.push_back(range); + + return -1; // no collision +} + +// Recursively figure out how many bytes of xfb buffer are used by the given type. +// Return the size of type, in bytes. +// Sets contains64BitType to true if the type contains a 64-bit data type. +// Sets contains32BitType to true if the type contains a 32-bit data type. +// Sets contains16BitType to true if the type contains a 16-bit data type. +// N.B. Caller must set contains64BitType, contains32BitType, and contains16BitType to false before calling. +unsigned int TIntermediate::computeTypeXfbSize(const TType& type, bool& contains64BitType, bool& contains32BitType, bool& contains16BitType) const +{ + // "...if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8, + // and the space taken in the buffer will be a multiple of 8. + // ...within the qualified entity, subsequent components are each + // assigned, in order, to the next available offset aligned to a multiple of + // that component's size. Aggregate types are flattened down to the component + // level to get this sequence of components." + + if (type.isArray()) { + // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness + assert(type.isSizedArray()); + TType elementType(type, 0); + return type.getOuterArraySize() * computeTypeXfbSize(elementType, contains64BitType, contains16BitType, contains16BitType); + } + + if (type.isStruct()) { + unsigned int size = 0; + bool structContains64BitType = false; + bool structContains32BitType = false; + bool structContains16BitType = false; + for (int member = 0; member < (int)type.getStruct()->size(); ++member) { + TType memberType(type, member); + // "... if applied to + // an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8, + // and the space taken in the buffer will be a multiple of 8." + bool memberContains64BitType = false; + bool memberContains32BitType = false; + bool memberContains16BitType = false; + int memberSize = computeTypeXfbSize(memberType, memberContains64BitType, memberContains32BitType, memberContains16BitType); + if (memberContains64BitType) { + structContains64BitType = true; + RoundToPow2(size, 8); + } else if (memberContains32BitType) { + structContains32BitType = true; + RoundToPow2(size, 4); + } else if (memberContains16BitType) { + structContains16BitType = true; + RoundToPow2(size, 2); + } + size += memberSize; + } + + if (structContains64BitType) { + contains64BitType = true; + RoundToPow2(size, 8); + } else if (structContains32BitType) { + contains32BitType = true; + RoundToPow2(size, 4); + } else if (structContains16BitType) { + contains16BitType = true; + RoundToPow2(size, 2); + } + return size; + } + + int numComponents; + if (type.isScalar()) + numComponents = 1; + else if (type.isVector()) + numComponents = type.getVectorSize(); + else if (type.isMatrix()) + numComponents = type.getMatrixCols() * type.getMatrixRows(); + else { + assert(0); + numComponents = 1; + } + + if (type.getBasicType() == EbtDouble || type.getBasicType() == EbtInt64 || type.getBasicType() == EbtUint64) { + contains64BitType = true; + return 8 * numComponents; + } else if (type.getBasicType() == EbtFloat16 || type.getBasicType() == EbtInt16 || type.getBasicType() == EbtUint16) { + contains16BitType = true; + return 2 * numComponents; + } else if (type.getBasicType() == EbtInt8 || type.getBasicType() == EbtUint8) + return numComponents; + else { + contains32BitType = true; + return 4 * numComponents; + } +} + +#endif + +const int baseAlignmentVec4Std140 = 16; + +// Return the size and alignment of a component of the given type. +// The size is returned in the 'size' parameter +// Return value is the alignment.. +int TIntermediate::getBaseAlignmentScalar(const TType& type, int& size) +{ +#ifdef GLSLANG_WEB + size = 4; return 4; +#endif + + switch (type.getBasicType()) { + case EbtInt64: + case EbtUint64: + case EbtDouble: size = 8; return 8; + case EbtFloat16: size = 2; return 2; + case EbtInt8: + case EbtUint8: size = 1; return 1; + case EbtInt16: + case EbtUint16: size = 2; return 2; + case EbtReference: size = 8; return 8; + default: size = 4; return 4; + } +} + +// Implement base-alignment and size rules from section 7.6.2.2 Standard Uniform Block Layout +// Operates recursively. +// +// If std140 is true, it does the rounding up to vec4 size required by std140, +// otherwise it does not, yielding std430 rules. +// +// The size is returned in the 'size' parameter +// +// The stride is only non-0 for arrays or matrices, and is the stride of the +// top-level object nested within the type. E.g., for an array of matrices, +// it is the distances needed between matrices, despite the rules saying the +// stride comes from the flattening down to vectors. +// +// Return value is the alignment of the type. +int TIntermediate::getBaseAlignment(const TType& type, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor) +{ + int alignment; + + bool std140 = layoutPacking == glslang::ElpStd140; + // When using the std140 storage layout, structures will be laid out in buffer + // storage with its members stored in monotonically increasing order based on their + // location in the declaration. A structure and each structure member have a base + // offset and a base alignment, from which an aligned offset is computed by rounding + // the base offset up to a multiple of the base alignment. The base offset of the first + // member of a structure is taken from the aligned offset of the structure itself. The + // base offset of all other structure members is derived by taking the offset of the + // last basic machine unit consumed by the previous member and adding one. Each + // structure member is stored in memory at its aligned offset. The members of a top- + // level uniform block are laid out in buffer storage by treating the uniform block as + // a structure with a base offset of zero. + // + // 1. If the member is a scalar consuming N basic machine units, the base alignment is N. + // + // 2. If the member is a two- or four-component vector with components consuming N basic + // machine units, the base alignment is 2N or 4N, respectively. + // + // 3. If the member is a three-component vector with components consuming N + // basic machine units, the base alignment is 4N. + // + // 4. If the member is an array of scalars or vectors, the base alignment and array + // stride are set to match the base alignment of a single array element, according + // to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The + // array may have padding at the end; the base offset of the member following + // the array is rounded up to the next multiple of the base alignment. + // + // 5. If the member is a column-major matrix with C columns and R rows, the + // matrix is stored identically to an array of C column vectors with R + // components each, according to rule (4). + // + // 6. If the member is an array of S column-major matrices with C columns and + // R rows, the matrix is stored identically to a row of S X C column vectors + // with R components each, according to rule (4). + // + // 7. If the member is a row-major matrix with C columns and R rows, the matrix + // is stored identically to an array of R row vectors with C components each, + // according to rule (4). + // + // 8. If the member is an array of S row-major matrices with C columns and R + // rows, the matrix is stored identically to a row of S X R row vectors with C + // components each, according to rule (4). + // + // 9. If the member is a structure, the base alignment of the structure is N , where + // N is the largest base alignment value of any of its members, and rounded + // up to the base alignment of a vec4. The individual members of this substructure + // are then assigned offsets by applying this set of rules recursively, + // where the base offset of the first member of the sub-structure is equal to the + // aligned offset of the structure. The structure may have padding at the end; + // the base offset of the member following the sub-structure is rounded up to + // the next multiple of the base alignment of the structure. + // + // 10. If the member is an array of S structures, the S elements of the array are laid + // out in order, according to rule (9). + // + // Assuming, for rule 10: The stride is the same as the size of an element. + + stride = 0; + int dummyStride; + + // rules 4, 6, 8, and 10 + if (type.isArray()) { + // TODO: perf: this might be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness + TType derefType(type, 0); + alignment = getBaseAlignment(derefType, size, dummyStride, layoutPacking, rowMajor); + if (std140) + alignment = std::max(baseAlignmentVec4Std140, alignment); + RoundToPow2(size, alignment); + stride = size; // uses full matrix size for stride of an array of matrices (not quite what rule 6/8, but what's expected) + // uses the assumption for rule 10 in the comment above + size = stride * type.getOuterArraySize(); + return alignment; + } + + // rule 9 + if (type.getBasicType() == EbtStruct) { + const TTypeList& memberList = *type.getStruct(); + + size = 0; + int maxAlignment = std140 ? baseAlignmentVec4Std140 : 0; + for (size_t m = 0; m < memberList.size(); ++m) { + int memberSize; + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = memberList[m].type->getQualifier().layoutMatrix; + int memberAlignment = getBaseAlignment(*memberList[m].type, memberSize, dummyStride, layoutPacking, + (subMatrixLayout != ElmNone) ? (subMatrixLayout == ElmRowMajor) : rowMajor); + maxAlignment = std::max(maxAlignment, memberAlignment); + RoundToPow2(size, memberAlignment); + size += memberSize; + } + + // The structure may have padding at the end; the base offset of + // the member following the sub-structure is rounded up to the next + // multiple of the base alignment of the structure. + RoundToPow2(size, maxAlignment); + + return maxAlignment; + } + + // rule 1 + if (type.isScalar()) + return getBaseAlignmentScalar(type, size); + + // rules 2 and 3 + if (type.isVector()) { + int scalarAlign = getBaseAlignmentScalar(type, size); + switch (type.getVectorSize()) { + case 1: // HLSL has this, GLSL does not + return scalarAlign; + case 2: + size *= 2; + return 2 * scalarAlign; + default: + size *= type.getVectorSize(); + return 4 * scalarAlign; + } + } + + // rules 5 and 7 + if (type.isMatrix()) { + // rule 5: deref to row, not to column, meaning the size of vector is num columns instead of num rows + TType derefType(type, 0, rowMajor); + + alignment = getBaseAlignment(derefType, size, dummyStride, layoutPacking, rowMajor); + if (std140) + alignment = std::max(baseAlignmentVec4Std140, alignment); + RoundToPow2(size, alignment); + stride = size; // use intra-matrix stride for stride of a just a matrix + if (rowMajor) + size = stride * type.getMatrixRows(); + else + size = stride * type.getMatrixCols(); + + return alignment; + } + + assert(0); // all cases should be covered above + size = baseAlignmentVec4Std140; + return baseAlignmentVec4Std140; +} + +// To aid the basic HLSL rule about crossing vec4 boundaries. +bool TIntermediate::improperStraddle(const TType& type, int size, int offset) +{ + if (! type.isVector() || type.isArray()) + return false; + + return size <= 16 ? offset / 16 != (offset + size - 1) / 16 + : offset % 16 != 0; +} + +int TIntermediate::getScalarAlignment(const TType& type, int& size, int& stride, bool rowMajor) +{ + int alignment; + + stride = 0; + int dummyStride; + + if (type.isArray()) { + TType derefType(type, 0); + alignment = getScalarAlignment(derefType, size, dummyStride, rowMajor); + + stride = size; + RoundToPow2(stride, alignment); + + size = stride * (type.getOuterArraySize() - 1) + size; + return alignment; + } + + if (type.getBasicType() == EbtStruct) { + const TTypeList& memberList = *type.getStruct(); + + size = 0; + int maxAlignment = 0; + for (size_t m = 0; m < memberList.size(); ++m) { + int memberSize; + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = memberList[m].type->getQualifier().layoutMatrix; + int memberAlignment = getScalarAlignment(*memberList[m].type, memberSize, dummyStride, + (subMatrixLayout != ElmNone) ? (subMatrixLayout == ElmRowMajor) : rowMajor); + maxAlignment = std::max(maxAlignment, memberAlignment); + RoundToPow2(size, memberAlignment); + size += memberSize; + } + + return maxAlignment; + } + + if (type.isScalar()) + return getBaseAlignmentScalar(type, size); + + if (type.isVector()) { + int scalarAlign = getBaseAlignmentScalar(type, size); + + size *= type.getVectorSize(); + return scalarAlign; + } + + if (type.isMatrix()) { + TType derefType(type, 0, rowMajor); + + alignment = getScalarAlignment(derefType, size, dummyStride, rowMajor); + + stride = size; // use intra-matrix stride for stride of a just a matrix + if (rowMajor) + size = stride * type.getMatrixRows(); + else + size = stride * type.getMatrixCols(); + + return alignment; + } + + assert(0); // all cases should be covered above + size = 1; + return 1; +} + +int TIntermediate::getMemberAlignment(const TType& type, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor) +{ + if (layoutPacking == glslang::ElpScalar) { + return getScalarAlignment(type, size, stride, rowMajor); + } else { + return getBaseAlignment(type, size, stride, layoutPacking, rowMajor); + } +} + +// shared calculation by getOffset and getOffsets +void TIntermediate::updateOffset(const TType& parentType, const TType& memberType, int& offset, int& memberSize) +{ + int dummyStride; + + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = memberType.getQualifier().layoutMatrix; + int memberAlignment = getMemberAlignment(memberType, memberSize, dummyStride, + parentType.getQualifier().layoutPacking, + subMatrixLayout != ElmNone + ? subMatrixLayout == ElmRowMajor + : parentType.getQualifier().layoutMatrix == ElmRowMajor); + RoundToPow2(offset, memberAlignment); +} + +// Lookup or calculate the offset of a block member, using the recursively +// defined block offset rules. +int TIntermediate::getOffset(const TType& type, int index) +{ + const TTypeList& memberList = *type.getStruct(); + + // Don't calculate offset if one is present, it could be user supplied + // and different than what would be calculated. That is, this is faster, + // but not just an optimization. + if (memberList[index].type->getQualifier().hasOffset()) + return memberList[index].type->getQualifier().layoutOffset; + + int memberSize = 0; + int offset = 0; + for (int m = 0; m <= index; ++m) { + updateOffset(type, *memberList[m].type, offset, memberSize); + + if (m < index) + offset += memberSize; + } + + return offset; +} + +// Calculate the block data size. +// Block arrayness is not taken into account, each element is backed by a separate buffer. +int TIntermediate::getBlockSize(const TType& blockType) +{ + const TTypeList& memberList = *blockType.getStruct(); + int lastIndex = (int)memberList.size() - 1; + int lastOffset = getOffset(blockType, lastIndex); + + int lastMemberSize; + int dummyStride; + getMemberAlignment(*memberList[lastIndex].type, lastMemberSize, dummyStride, + blockType.getQualifier().layoutPacking, + blockType.getQualifier().layoutMatrix == ElmRowMajor); + + return lastOffset + lastMemberSize; +} + +int TIntermediate::computeBufferReferenceTypeSize(const TType& type) +{ + assert(type.isReference()); + int size = getBlockSize(*type.getReferentType()); + + int align = type.getBufferReferenceAlignment(); + + if (align) { + size = (size + align - 1) & ~(align-1); + } + + return size; +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/localintermediate.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/localintermediate.h new file mode 100644 index 000000000..996e347fb --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/localintermediate.h @@ -0,0 +1,1026 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2016 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _LOCAL_INTERMEDIATE_INCLUDED_ +#define _LOCAL_INTERMEDIATE_INCLUDED_ + +#include "../Include/intermediate.h" +#include "../Public/ShaderLang.h" +#include "Versions.h" + +#include +#include +#include +#include +#include + +class TInfoSink; + +namespace glslang { + +struct TMatrixSelector { + int coord1; // stay agnostic about column/row; this is parse order + int coord2; +}; + +typedef int TVectorSelector; + +const int MaxSwizzleSelectors = 4; + +template +class TSwizzleSelectors { +public: + TSwizzleSelectors() : size_(0) { } + + void push_back(selectorType comp) + { + if (size_ < MaxSwizzleSelectors) + components[size_++] = comp; + } + void resize(int s) + { + assert(s <= size_); + size_ = s; + } + int size() const { return size_; } + selectorType operator[](int i) const + { + assert(i < MaxSwizzleSelectors); + return components[i]; + } + +private: + int size_; + selectorType components[MaxSwizzleSelectors]; +}; + +// +// Some helper structures for TIntermediate. Their contents are encapsulated +// by TIntermediate. +// + +// Used for call-graph algorithms for detecting recursion, missing bodies, and dead bodies. +// A "call" is a pair: . +// There can be duplicates. General assumption is the list is small. +struct TCall { + TCall(const TString& pCaller, const TString& pCallee) : caller(pCaller), callee(pCallee) { } + TString caller; + TString callee; + bool visited; + bool currentPath; + bool errorGiven; + int calleeBodyPosition; +}; + +// A generic 1-D range. +struct TRange { + TRange(int start, int last) : start(start), last(last) { } + bool overlap(const TRange& rhs) const + { + return last >= rhs.start && start <= rhs.last; + } + int start; + int last; +}; + +// An IO range is a 3-D rectangle; the set of (location, component, index) triples all lying +// within the same location range, component range, and index value. Locations don't alias unless +// all other dimensions of their range overlap. +struct TIoRange { + TIoRange(TRange location, TRange component, TBasicType basicType, int index) + : location(location), component(component), basicType(basicType), index(index) { } + bool overlap(const TIoRange& rhs) const + { + return location.overlap(rhs.location) && component.overlap(rhs.component) && index == rhs.index; + } + TRange location; + TRange component; + TBasicType basicType; + int index; +}; + +// An offset range is a 2-D rectangle; the set of (binding, offset) pairs all lying +// within the same binding and offset range. +struct TOffsetRange { + TOffsetRange(TRange binding, TRange offset) + : binding(binding), offset(offset) { } + bool overlap(const TOffsetRange& rhs) const + { + return binding.overlap(rhs.binding) && offset.overlap(rhs.offset); + } + TRange binding; + TRange offset; +}; + +#ifndef GLSLANG_WEB +// Things that need to be tracked per xfb buffer. +struct TXfbBuffer { + TXfbBuffer() : stride(TQualifier::layoutXfbStrideEnd), implicitStride(0), contains64BitType(false), + contains32BitType(false), contains16BitType(false) { } + std::vector ranges; // byte offsets that have already been assigned + unsigned int stride; + unsigned int implicitStride; + bool contains64BitType; + bool contains32BitType; + bool contains16BitType; +}; +#endif + +// Track a set of strings describing how the module was processed. +// This includes command line options, transforms, etc., ideally inclusive enough +// to reproduce the steps used to transform the input source to the output. +// E.g., see SPIR-V OpModuleProcessed. +// Each "process" or "transform" uses is expressed in the form: +// process arg0 arg1 arg2 ... +// process arg0 arg1 arg2 ... +// where everything is textual, and there can be zero or more arguments +class TProcesses { +public: + TProcesses() {} + ~TProcesses() {} + + void addProcess(const char* process) + { + processes.push_back(process); + } + void addProcess(const std::string& process) + { + processes.push_back(process); + } + void addArgument(int arg) + { + processes.back().append(" "); + std::string argString = std::to_string(arg); + processes.back().append(argString); + } + void addArgument(const char* arg) + { + processes.back().append(" "); + processes.back().append(arg); + } + void addArgument(const std::string& arg) + { + processes.back().append(" "); + processes.back().append(arg); + } + void addIfNonZero(const char* process, int value) + { + if (value != 0) { + addProcess(process); + addArgument(value); + } + } + + const std::vector& getProcesses() const { return processes; } + +private: + std::vector processes; +}; + +class TSymbolTable; +class TSymbol; +class TVariable; + +// +// Texture and Sampler transformation mode. +// +enum ComputeDerivativeMode { + LayoutDerivativeNone, // default layout as SPV_NV_compute_shader_derivatives not enabled + LayoutDerivativeGroupQuads, // derivative_group_quadsNV + LayoutDerivativeGroupLinear, // derivative_group_linearNV +}; + +class TIdMaps { +public: + TMap& operator[](int i) { return maps[i]; } + const TMap& operator[](int i) const { return maps[i]; } +private: + TMap maps[EsiCount]; +}; + + +// +// Set of helper functions to help parse and build the tree. +// +class TIntermediate { +public: + explicit TIntermediate(EShLanguage l, int v = 0, EProfile p = ENoProfile) : + language(l), + profile(p), version(v), treeRoot(0), + numEntryPoints(0), numErrors(0), numPushConstants(0), recursive(false), + invertY(false), + useStorageBuffer(false), + nanMinMaxClamp(false), + depthReplacing(false) +#ifndef GLSLANG_WEB + , + implicitThisName("@this"), implicitCounterName("@count"), + source(EShSourceNone), + useVulkanMemoryModel(false), + invocations(TQualifier::layoutNotSet), vertices(TQualifier::layoutNotSet), + inputPrimitive(ElgNone), outputPrimitive(ElgNone), + pixelCenterInteger(false), originUpperLeft(false), + vertexSpacing(EvsNone), vertexOrder(EvoNone), interlockOrdering(EioNone), pointMode(false), earlyFragmentTests(false), + postDepthCoverage(false), depthLayout(EldNone), + hlslFunctionality1(false), + blendEquations(0), xfbMode(false), multiStream(false), + layoutOverrideCoverage(false), + geoPassthroughEXT(false), + numShaderRecordBlocks(0), + computeDerivativeMode(LayoutDerivativeNone), + primitives(TQualifier::layoutNotSet), + numTaskNVBlocks(0), + autoMapBindings(false), + autoMapLocations(false), + flattenUniformArrays(false), + useUnknownFormat(false), + hlslOffsets(false), + hlslIoMapping(false), + useVariablePointers(false), + textureSamplerTransformMode(EShTexSampTransKeep), + needToLegalize(false), + binaryDoubleOutput(false), + usePhysicalStorageBuffer(false), + uniformLocationBase(0) +#endif + { + localSize[0] = 1; + localSize[1] = 1; + localSize[2] = 1; + localSizeNotDefault[0] = false; + localSizeNotDefault[1] = false; + localSizeNotDefault[2] = false; + localSizeSpecId[0] = TQualifier::layoutNotSet; + localSizeSpecId[1] = TQualifier::layoutNotSet; + localSizeSpecId[2] = TQualifier::layoutNotSet; +#ifndef GLSLANG_WEB + xfbBuffers.resize(TQualifier::layoutXfbBufferEnd); + shiftBinding.fill(0); +#endif + } + + void setVersion(int v) { version = v; } + int getVersion() const { return version; } + void setProfile(EProfile p) { profile = p; } + EProfile getProfile() const { return profile; } + void setSpv(const SpvVersion& s) + { + spvVersion = s; + + // client processes + if (spvVersion.vulkan > 0) + processes.addProcess("client vulkan100"); + if (spvVersion.openGl > 0) + processes.addProcess("client opengl100"); + + // target SPV + switch (spvVersion.spv) { + case 0: + break; + case EShTargetSpv_1_0: + break; + case EShTargetSpv_1_1: + processes.addProcess("target-env spirv1.1"); + break; + case EShTargetSpv_1_2: + processes.addProcess("target-env spirv1.2"); + break; + case EShTargetSpv_1_3: + processes.addProcess("target-env spirv1.3"); + break; + case EShTargetSpv_1_4: + processes.addProcess("target-env spirv1.4"); + break; + case EShTargetSpv_1_5: + processes.addProcess("target-env spirv1.5"); + break; + default: + processes.addProcess("target-env spirvUnknown"); + break; + } + + // target-environment processes + switch (spvVersion.vulkan) { + case 0: + break; + case EShTargetVulkan_1_0: + processes.addProcess("target-env vulkan1.0"); + break; + case EShTargetVulkan_1_1: + processes.addProcess("target-env vulkan1.1"); + break; + case EShTargetVulkan_1_2: + processes.addProcess("target-env vulkan1.2"); + break; + default: + processes.addProcess("target-env vulkanUnknown"); + break; + } + if (spvVersion.openGl > 0) + processes.addProcess("target-env opengl"); + } + const SpvVersion& getSpv() const { return spvVersion; } + EShLanguage getStage() const { return language; } + void updateRequestedExtension(const char* extension, TExtensionBehavior behavior) { + if(requestedExtensions.find(extension) != requestedExtensions.end()) { + requestedExtensions[extension] = behavior; + } else { + requestedExtensions.insert(std::make_pair(extension, behavior)); + } + } + + const std::map& getRequestedExtensions() const { return requestedExtensions; } + + void setTreeRoot(TIntermNode* r) { treeRoot = r; } + TIntermNode* getTreeRoot() const { return treeRoot; } + void incrementEntryPointCount() { ++numEntryPoints; } + int getNumEntryPoints() const { return numEntryPoints; } + int getNumErrors() const { return numErrors; } + void addPushConstantCount() { ++numPushConstants; } + void setLimits(const TBuiltInResource& r) { resources = r; } + + bool postProcess(TIntermNode*, EShLanguage); + void removeTree(); + + void setEntryPointName(const char* ep) + { + entryPointName = ep; + processes.addProcess("entry-point"); + processes.addArgument(entryPointName); + } + void setEntryPointMangledName(const char* ep) { entryPointMangledName = ep; } + const std::string& getEntryPointName() const { return entryPointName; } + const std::string& getEntryPointMangledName() const { return entryPointMangledName; } + + void setInvertY(bool invert) + { + invertY = invert; + if (invertY) + processes.addProcess("invert-y"); + } + bool getInvertY() const { return invertY; } + +#ifdef ENABLE_HLSL + void setSource(EShSource s) { source = s; } + EShSource getSource() const { return source; } +#else + void setSource(EShSource s) { assert(s == EShSourceGlsl); } + EShSource getSource() const { return EShSourceGlsl; } +#endif + + bool isRecursive() const { return recursive; } + + TIntermSymbol* addSymbol(const TVariable&); + TIntermSymbol* addSymbol(const TVariable&, const TSourceLoc&); + TIntermSymbol* addSymbol(const TType&, const TSourceLoc&); + TIntermSymbol* addSymbol(const TIntermSymbol&); + TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*); + std::tuple addConversion(TOperator op, TIntermTyped* node0, TIntermTyped* node1); + TIntermTyped* addUniShapeConversion(TOperator, const TType&, TIntermTyped*); + TIntermTyped* addConversion(TBasicType convertTo, TIntermTyped* node) const; + void addBiShapeConversion(TOperator, TIntermTyped*& lhsNode, TIntermTyped*& rhsNode); + TIntermTyped* addShapeConversion(const TType&, TIntermTyped*); + TIntermTyped* addBinaryMath(TOperator, TIntermTyped* left, TIntermTyped* right, TSourceLoc); + TIntermTyped* addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc); + TIntermTyped* addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, TSourceLoc); + TIntermTyped* addUnaryMath(TOperator, TIntermTyped* child, TSourceLoc); + TIntermTyped* addBuiltInFunctionCall(const TSourceLoc& line, TOperator, bool unary, TIntermNode*, const TType& returnType); + bool canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op = EOpNull) const; + bool isIntegralPromotion(TBasicType from, TBasicType to) const; + bool isFPPromotion(TBasicType from, TBasicType to) const; + bool isIntegralConversion(TBasicType from, TBasicType to) const; + bool isFPConversion(TBasicType from, TBasicType to) const; + bool isFPIntegralConversion(TBasicType from, TBasicType to) const; + TOperator mapTypeToConstructorOp(const TType&) const; + TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right); + TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc&); + TIntermAggregate* makeAggregate(TIntermNode* node); + TIntermAggregate* makeAggregate(TIntermNode* node, const TSourceLoc&); + TIntermAggregate* makeAggregate(const TSourceLoc&); + TIntermTyped* setAggregateOperator(TIntermNode*, TOperator, const TType& type, TSourceLoc); + bool areAllChildConst(TIntermAggregate* aggrNode); + TIntermSelection* addSelection(TIntermTyped* cond, TIntermNodePair code, const TSourceLoc&); + TIntermTyped* addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc&); + TIntermTyped* addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc&); + TIntermTyped* addMethod(TIntermTyped*, const TType&, const TString*, const TSourceLoc&); + TIntermConstantUnion* addConstantUnion(const TConstUnionArray&, const TType&, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(signed char, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(unsigned char, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(signed short, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(unsigned short, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(int, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(unsigned int, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(long long, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(unsigned long long, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(bool, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(double, TBasicType, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(const TString*, const TSourceLoc&, bool literal = false) const; + TIntermTyped* promoteConstantUnion(TBasicType, TIntermConstantUnion*) const; + bool parseConstTree(TIntermNode*, TConstUnionArray, TOperator, const TType&, bool singleConstantParam = false); + TIntermLoop* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&); + TIntermAggregate* addForLoop(TIntermNode*, TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, + const TSourceLoc&, TIntermLoop*&); + TIntermBranch* addBranch(TOperator, const TSourceLoc&); + TIntermBranch* addBranch(TOperator, TIntermTyped*, const TSourceLoc&); + template TIntermTyped* addSwizzle(TSwizzleSelectors&, const TSourceLoc&); + + // Low level functions to add nodes (no conversions or other higher level transformations) + // If a type is provided, the node's type will be set to it. + TIntermBinary* addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc) const; + TIntermBinary* addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc, const TType&) const; + TIntermUnary* addUnaryNode(TOperator op, TIntermTyped* child, TSourceLoc) const; + TIntermUnary* addUnaryNode(TOperator op, TIntermTyped* child, TSourceLoc, const TType&) const; + + // Constant folding (in Constant.cpp) + TIntermTyped* fold(TIntermAggregate* aggrNode); + TIntermTyped* foldConstructor(TIntermAggregate* aggrNode); + TIntermTyped* foldDereference(TIntermTyped* node, int index, const TSourceLoc&); + TIntermTyped* foldSwizzle(TIntermTyped* node, TSwizzleSelectors& fields, const TSourceLoc&); + + // Tree ops + static const TIntermTyped* findLValueBase(const TIntermTyped*, bool swizzleOkay); + + // Linkage related + void addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage, TSymbolTable&); + void addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol&); + + void setUseStorageBuffer() + { + useStorageBuffer = true; + processes.addProcess("use-storage-buffer"); + } + bool usingStorageBuffer() const { return useStorageBuffer; } + void setDepthReplacing() { depthReplacing = true; } + bool isDepthReplacing() const { return depthReplacing; } + bool setLocalSize(int dim, int size) + { + if (localSizeNotDefault[dim]) + return size == localSize[dim]; + localSizeNotDefault[dim] = true; + localSize[dim] = size; + return true; + } + unsigned int getLocalSize(int dim) const { return localSize[dim]; } + bool setLocalSizeSpecId(int dim, int id) + { + if (localSizeSpecId[dim] != TQualifier::layoutNotSet) + return id == localSizeSpecId[dim]; + localSizeSpecId[dim] = id; + return true; + } + int getLocalSizeSpecId(int dim) const { return localSizeSpecId[dim]; } +#ifdef GLSLANG_WEB + void output(TInfoSink&, bool tree) { } + + bool isEsProfile() const { return false; } + bool getXfbMode() const { return false; } + bool isMultiStream() const { return false; } + TLayoutGeometry getOutputPrimitive() const { return ElgNone; } + bool getPostDepthCoverage() const { return false; } + bool getEarlyFragmentTests() const { return false; } + TLayoutDepth getDepth() const { return EldNone; } + bool getPixelCenterInteger() const { return false; } + void setOriginUpperLeft() { } + bool getOriginUpperLeft() const { return true; } + TInterlockOrdering getInterlockOrdering() const { return EioNone; } + + bool getAutoMapBindings() const { return false; } + bool getAutoMapLocations() const { return false; } + int getNumPushConstants() const { return 0; } + void addShaderRecordCount() { } + void addTaskNVCount() { } + void setUseVulkanMemoryModel() { } + bool usingVulkanMemoryModel() const { return false; } + bool usingPhysicalStorageBuffer() const { return false; } + bool usingVariablePointers() const { return false; } + unsigned getXfbStride(int buffer) const { return 0; } + bool hasLayoutDerivativeModeNone() const { return false; } + ComputeDerivativeMode getLayoutDerivativeModeNone() const { return LayoutDerivativeNone; } +#else + void output(TInfoSink&, bool tree); + + bool isEsProfile() const { return profile == EEsProfile; } + + void setShiftBinding(TResourceType res, unsigned int shift) + { + shiftBinding[res] = shift; + + const char* name = getResourceName(res); + if (name != nullptr) + processes.addIfNonZero(name, shift); + } + + unsigned int getShiftBinding(TResourceType res) const { return shiftBinding[res]; } + + void setShiftBindingForSet(TResourceType res, unsigned int shift, unsigned int set) + { + if (shift == 0) // ignore if there's no shift: it's a no-op. + return; + + shiftBindingForSet[res][set] = shift; + + const char* name = getResourceName(res); + if (name != nullptr) { + processes.addProcess(name); + processes.addArgument(shift); + processes.addArgument(set); + } + } + + int getShiftBindingForSet(TResourceType res, unsigned int set) const + { + const auto shift = shiftBindingForSet[res].find(set); + return shift == shiftBindingForSet[res].end() ? -1 : shift->second; + } + bool hasShiftBindingForSet(TResourceType res) const { return !shiftBindingForSet[res].empty(); } + + void setResourceSetBinding(const std::vector& shift) + { + resourceSetBinding = shift; + if (shift.size() > 0) { + processes.addProcess("resource-set-binding"); + for (int s = 0; s < (int)shift.size(); ++s) + processes.addArgument(shift[s]); + } + } + const std::vector& getResourceSetBinding() const { return resourceSetBinding; } + void setAutoMapBindings(bool map) + { + autoMapBindings = map; + if (autoMapBindings) + processes.addProcess("auto-map-bindings"); + } + bool getAutoMapBindings() const { return autoMapBindings; } + void setAutoMapLocations(bool map) + { + autoMapLocations = map; + if (autoMapLocations) + processes.addProcess("auto-map-locations"); + } + bool getAutoMapLocations() const { return autoMapLocations; } + +#ifdef ENABLE_HLSL + void setFlattenUniformArrays(bool flatten) + { + flattenUniformArrays = flatten; + if (flattenUniformArrays) + processes.addProcess("flatten-uniform-arrays"); + } + bool getFlattenUniformArrays() const { return flattenUniformArrays; } +#endif + void setNoStorageFormat(bool b) + { + useUnknownFormat = b; + if (useUnknownFormat) + processes.addProcess("no-storage-format"); + } + bool getNoStorageFormat() const { return useUnknownFormat; } + void setUseVulkanMemoryModel() + { + useVulkanMemoryModel = true; + processes.addProcess("use-vulkan-memory-model"); + } + bool usingVulkanMemoryModel() const { return useVulkanMemoryModel; } + void setUsePhysicalStorageBuffer() + { + usePhysicalStorageBuffer = true; + } + bool usingPhysicalStorageBuffer() const { return usePhysicalStorageBuffer; } + void setUseVariablePointers() + { + useVariablePointers = true; + processes.addProcess("use-variable-pointers"); + } + bool usingVariablePointers() const { return useVariablePointers; } + +#ifdef ENABLE_HLSL + template T addCounterBufferName(const T& name) const { return name + implicitCounterName; } + bool hasCounterBufferName(const TString& name) const { + size_t len = strlen(implicitCounterName); + return name.size() > len && + name.compare(name.size() - len, len, implicitCounterName) == 0; + } +#endif + + void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { textureSamplerTransformMode = mode; } + int getNumPushConstants() const { return numPushConstants; } + void addShaderRecordCount() { ++numShaderRecordBlocks; } + void addTaskNVCount() { ++numTaskNVBlocks; } + + bool setInvocations(int i) + { + if (invocations != TQualifier::layoutNotSet) + return invocations == i; + invocations = i; + return true; + } + int getInvocations() const { return invocations; } + bool setVertices(int m) + { + if (vertices != TQualifier::layoutNotSet) + return vertices == m; + vertices = m; + return true; + } + int getVertices() const { return vertices; } + bool setInputPrimitive(TLayoutGeometry p) + { + if (inputPrimitive != ElgNone) + return inputPrimitive == p; + inputPrimitive = p; + return true; + } + TLayoutGeometry getInputPrimitive() const { return inputPrimitive; } + bool setVertexSpacing(TVertexSpacing s) + { + if (vertexSpacing != EvsNone) + return vertexSpacing == s; + vertexSpacing = s; + return true; + } + TVertexSpacing getVertexSpacing() const { return vertexSpacing; } + bool setVertexOrder(TVertexOrder o) + { + if (vertexOrder != EvoNone) + return vertexOrder == o; + vertexOrder = o; + return true; + } + TVertexOrder getVertexOrder() const { return vertexOrder; } + void setPointMode() { pointMode = true; } + bool getPointMode() const { return pointMode; } + + bool setInterlockOrdering(TInterlockOrdering o) + { + if (interlockOrdering != EioNone) + return interlockOrdering == o; + interlockOrdering = o; + return true; + } + TInterlockOrdering getInterlockOrdering() const { return interlockOrdering; } + + void setXfbMode() { xfbMode = true; } + bool getXfbMode() const { return xfbMode; } + void setMultiStream() { multiStream = true; } + bool isMultiStream() const { return multiStream; } + bool setOutputPrimitive(TLayoutGeometry p) + { + if (outputPrimitive != ElgNone) + return outputPrimitive == p; + outputPrimitive = p; + return true; + } + TLayoutGeometry getOutputPrimitive() const { return outputPrimitive; } + void setPostDepthCoverage() { postDepthCoverage = true; } + bool getPostDepthCoverage() const { return postDepthCoverage; } + void setEarlyFragmentTests() { earlyFragmentTests = true; } + bool getEarlyFragmentTests() const { return earlyFragmentTests; } + bool setDepth(TLayoutDepth d) + { + if (depthLayout != EldNone) + return depthLayout == d; + depthLayout = d; + return true; + } + TLayoutDepth getDepth() const { return depthLayout; } + void setOriginUpperLeft() { originUpperLeft = true; } + bool getOriginUpperLeft() const { return originUpperLeft; } + void setPixelCenterInteger() { pixelCenterInteger = true; } + bool getPixelCenterInteger() const { return pixelCenterInteger; } + void addBlendEquation(TBlendEquationShift b) { blendEquations |= (1 << b); } + unsigned int getBlendEquations() const { return blendEquations; } + bool setXfbBufferStride(int buffer, unsigned stride) + { + if (xfbBuffers[buffer].stride != TQualifier::layoutXfbStrideEnd) + return xfbBuffers[buffer].stride == stride; + xfbBuffers[buffer].stride = stride; + return true; + } + unsigned getXfbStride(int buffer) const { return xfbBuffers[buffer].stride; } + int addXfbBufferOffset(const TType&); + unsigned int computeTypeXfbSize(const TType&, bool& contains64BitType, bool& contains32BitType, bool& contains16BitType) const; + unsigned int computeTypeXfbSize(const TType&, bool& contains64BitType) const; + void setLayoutOverrideCoverage() { layoutOverrideCoverage = true; } + bool getLayoutOverrideCoverage() const { return layoutOverrideCoverage; } + void setGeoPassthroughEXT() { geoPassthroughEXT = true; } + bool getGeoPassthroughEXT() const { return geoPassthroughEXT; } + void setLayoutDerivativeMode(ComputeDerivativeMode mode) { computeDerivativeMode = mode; } + bool hasLayoutDerivativeModeNone() const { return computeDerivativeMode != LayoutDerivativeNone; } + ComputeDerivativeMode getLayoutDerivativeModeNone() const { return computeDerivativeMode; } + bool setPrimitives(int m) + { + if (primitives != TQualifier::layoutNotSet) + return primitives == m; + primitives = m; + return true; + } + int getPrimitives() const { return primitives; } + const char* addSemanticName(const TString& name) + { + return semanticNameSet.insert(name).first->c_str(); + } + void addUniformLocationOverride(const char* nameStr, int location) + { + std::string name = nameStr; + uniformLocationOverrides[name] = location; + } + + int getUniformLocationOverride(const char* nameStr) const + { + std::string name = nameStr; + auto pos = uniformLocationOverrides.find(name); + if (pos == uniformLocationOverrides.end()) + return -1; + else + return pos->second; + } + + void setUniformLocationBase(int base) { uniformLocationBase = base; } + int getUniformLocationBase() const { return uniformLocationBase; } + + void setNeedsLegalization() { needToLegalize = true; } + bool needsLegalization() const { return needToLegalize; } + + void setBinaryDoubleOutput() { binaryDoubleOutput = true; } + bool getBinaryDoubleOutput() { return binaryDoubleOutput; } +#endif // GLSLANG_WEB + +#ifdef ENABLE_HLSL + void setHlslFunctionality1() { hlslFunctionality1 = true; } + bool getHlslFunctionality1() const { return hlslFunctionality1; } + void setHlslOffsets() + { + hlslOffsets = true; + if (hlslOffsets) + processes.addProcess("hlsl-offsets"); + } + bool usingHlslOffsets() const { return hlslOffsets; } + void setHlslIoMapping(bool b) + { + hlslIoMapping = b; + if (hlslIoMapping) + processes.addProcess("hlsl-iomap"); + } + bool usingHlslIoMapping() { return hlslIoMapping; } +#else + bool getHlslFunctionality1() const { return false; } + bool usingHlslOffsets() const { return false; } + bool usingHlslIoMapping() { return false; } +#endif + + void addToCallGraph(TInfoSink&, const TString& caller, const TString& callee); + void merge(TInfoSink&, TIntermediate&); + void finalCheck(TInfoSink&, bool keepUncalled); + + bool buildConvertOp(TBasicType dst, TBasicType src, TOperator& convertOp) const; + TIntermTyped* createConversion(TBasicType convertTo, TIntermTyped* node) const; + + void addIoAccessed(const TString& name) { ioAccessed.insert(name); } + bool inIoAccessed(const TString& name) const { return ioAccessed.find(name) != ioAccessed.end(); } + + int addUsedLocation(const TQualifier&, const TType&, bool& typeCollision); + int checkLocationRange(int set, const TIoRange& range, const TType&, bool& typeCollision); + int addUsedOffsets(int binding, int offset, int numOffsets); + bool addUsedConstantId(int id); + static int computeTypeLocationSize(const TType&, EShLanguage); + static int computeTypeUniformLocationSize(const TType&); + + static int getBaseAlignmentScalar(const TType&, int& size); + static int getBaseAlignment(const TType&, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor); + static int getScalarAlignment(const TType&, int& size, int& stride, bool rowMajor); + static int getMemberAlignment(const TType&, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor); + static bool improperStraddle(const TType& type, int size, int offset); + static void updateOffset(const TType& parentType, const TType& memberType, int& offset, int& memberSize); + static int getOffset(const TType& type, int index); + static int getBlockSize(const TType& blockType); + static int computeBufferReferenceTypeSize(const TType&); + bool promote(TIntermOperator*); + void setNanMinMaxClamp(bool setting) { nanMinMaxClamp = setting; } + bool getNanMinMaxClamp() const { return nanMinMaxClamp; } + + void setSourceFile(const char* file) { if (file != nullptr) sourceFile = file; } + const std::string& getSourceFile() const { return sourceFile; } + void addSourceText(const char* text, size_t len) { sourceText.append(text, len); } + const std::string& getSourceText() const { return sourceText; } + const std::map& getIncludeText() const { return includeText; } + void addIncludeText(const char* name, const char* text, size_t len) { includeText[name].assign(text,len); } + void addProcesses(const std::vector& p) + { + for (int i = 0; i < (int)p.size(); ++i) + processes.addProcess(p[i]); + } + void addProcess(const std::string& process) { processes.addProcess(process); } + void addProcessArgument(const std::string& arg) { processes.addArgument(arg); } + const std::vector& getProcesses() const { return processes.getProcesses(); } + + // Certain explicit conversions are allowed conditionally +#ifdef GLSLANG_WEB + bool getArithemeticInt8Enabled() const { return false; } + bool getArithemeticInt16Enabled() const { return false; } + bool getArithemeticFloat16Enabled() const { return false; } +#else + bool getArithemeticInt8Enabled() const { + return extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int8); + } + bool getArithemeticInt16Enabled() const { + return extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) || + extensionRequested(E_GL_AMD_gpu_shader_int16) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_int16); + } + + bool getArithemeticFloat16Enabled() const { + return extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types) || + extensionRequested(E_GL_AMD_gpu_shader_half_float) || + extensionRequested(E_GL_EXT_shader_explicit_arithmetic_types_float16); + } +#endif + +protected: + TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TConstUnionArray&, TIntermTyped* subtree, const TSourceLoc&); + void error(TInfoSink& infoSink, const char*); + void warn(TInfoSink& infoSink, const char*); + void mergeCallGraphs(TInfoSink&, TIntermediate&); + void mergeModes(TInfoSink&, TIntermediate&); + void mergeTrees(TInfoSink&, TIntermediate&); + void seedIdMap(TIdMaps& idMaps, int& maxId); + void remapIds(const TIdMaps& idMaps, int idShift, TIntermediate&); + void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals); + void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects); + void mergeImplicitArraySizes(TType&, const TType&); + void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, bool crossStage); + void checkCallGraphCycles(TInfoSink&); + void checkCallGraphBodies(TInfoSink&, bool keepUncalled); + void inOutLocationCheck(TInfoSink&); + TIntermAggregate* findLinkerObjects() const; + bool userOutputUsed() const; + bool isSpecializationOperation(const TIntermOperator&) const; + bool isNonuniformPropagating(TOperator) const; + bool promoteUnary(TIntermUnary&); + bool promoteBinary(TIntermBinary&); + void addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable&, const TString&); + bool promoteAggregate(TIntermAggregate&); + void pushSelector(TIntermSequence&, const TVectorSelector&, const TSourceLoc&); + void pushSelector(TIntermSequence&, const TMatrixSelector&, const TSourceLoc&); + bool specConstantPropagates(const TIntermTyped&, const TIntermTyped&); + void performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root); + bool isConversionAllowed(TOperator op, TIntermTyped* node) const; + std::tuple getConversionDestinatonType(TBasicType type0, TBasicType type1, TOperator op) const; + + // JohnK: I think this function should go away. + // This data structure is just a log to pass on to back ends. + // Versioning and extensions are handled in Version.cpp, with a rich + // set of functions for querying stages, versions, extension enable/disabled, etc. +#ifdef GLSLANG_WEB + bool extensionRequested(const char *extension) const { return false; } +#else + bool extensionRequested(const char *extension) const { + auto it = requestedExtensions.find(extension); + if (it != requestedExtensions.end()) { + return (it->second == EBhDisable) ? false : true; + } + return false; + } +#endif + + static const char* getResourceName(TResourceType); + + const EShLanguage language; // stage, known at construction time + std::string entryPointName; + std::string entryPointMangledName; + typedef std::list TGraph; + TGraph callGraph; + + EProfile profile; // source profile + int version; // source version + SpvVersion spvVersion; + TIntermNode* treeRoot; + std::map requestedExtensions; // cumulation of all enabled or required extensions; not connected to what subset of the shader used them + TBuiltInResource resources; + int numEntryPoints; + int numErrors; + int numPushConstants; + bool recursive; + bool invertY; + bool useStorageBuffer; + bool nanMinMaxClamp; // true if desiring min/max/clamp to favor non-NaN over NaN + bool depthReplacing; + int localSize[3]; + bool localSizeNotDefault[3]; + int localSizeSpecId[3]; +#ifndef GLSLANG_WEB +public: + const char* const implicitThisName; + const char* const implicitCounterName; +protected: + EShSource source; // source language, known a bit later + bool useVulkanMemoryModel; + int invocations; + int vertices; + TLayoutGeometry inputPrimitive; + TLayoutGeometry outputPrimitive; + bool pixelCenterInteger; + bool originUpperLeft; + TVertexSpacing vertexSpacing; + TVertexOrder vertexOrder; + TInterlockOrdering interlockOrdering; + bool pointMode; + bool earlyFragmentTests; + bool postDepthCoverage; + TLayoutDepth depthLayout; + bool hlslFunctionality1; + int blendEquations; // an 'or'ing of masks of shifts of TBlendEquationShift + bool xfbMode; + std::vector xfbBuffers; // all the data we need to track per xfb buffer + bool multiStream; + bool layoutOverrideCoverage; + bool geoPassthroughEXT; + int numShaderRecordBlocks; + ComputeDerivativeMode computeDerivativeMode; + int primitives; + int numTaskNVBlocks; + + // Base shift values + std::array shiftBinding; + + // Per-descriptor-set shift values + std::array, EResCount> shiftBindingForSet; + + std::vector resourceSetBinding; + bool autoMapBindings; + bool autoMapLocations; + bool flattenUniformArrays; + bool useUnknownFormat; + bool hlslOffsets; + bool hlslIoMapping; + bool useVariablePointers; + + std::set semanticNameSet; + + EShTextureSamplerTransformMode textureSamplerTransformMode; + + bool needToLegalize; + bool binaryDoubleOutput; + bool usePhysicalStorageBuffer; + + std::unordered_map uniformLocationOverrides; + int uniformLocationBase; +#endif + + std::unordered_set usedConstantId; // specialization constant ids used + std::vector usedAtomics; // sets of bindings used by atomic counters + std::vector usedIo[4]; // sets of used locations, one for each of in, out, uniform, and buffers + // set of names of statically read/written I/O that might need extra checking + std::set ioAccessed; + // source code of shader, useful as part of debug information + std::string sourceFile; + std::string sourceText; + + // Included text. First string is a name, second is the included text + std::map includeText; + + // for OpModuleProcessed, or equivalent + TProcesses processes; + +private: + void operator=(TIntermediate&); // prevent assignments +}; + +} // end namespace glslang + +#endif // _LOCAL_INTERMEDIATE_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/parseConst.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/parseConst.cpp new file mode 100644 index 000000000..7c04743ba --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/parseConst.cpp @@ -0,0 +1,214 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Traverse a tree of constants to create a single folded constant. +// It should only be used when the whole tree is known to be constant. +// + +#include "ParseHelper.h" + +namespace glslang { + +class TConstTraverser : public TIntermTraverser { +public: + TConstTraverser(const TConstUnionArray& cUnion, bool singleConstParam, TOperator constructType, const TType& t) + : unionArray(cUnion), type(t), + constructorType(constructType), singleConstantParam(singleConstParam), error(false), isMatrix(false), + matrixCols(0), matrixRows(0) { index = 0; tOp = EOpNull; } + + virtual void visitConstantUnion(TIntermConstantUnion* node); + virtual bool visitAggregate(TVisit, TIntermAggregate* node); + + int index; + TConstUnionArray unionArray; + TOperator tOp; + const TType& type; + TOperator constructorType; + bool singleConstantParam; + bool error; + int size; // size of the constructor ( 4 for vec4) + bool isMatrix; + int matrixCols; + int matrixRows; + +protected: + TConstTraverser(TConstTraverser&); + TConstTraverser& operator=(TConstTraverser&); +}; + +bool TConstTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node) +{ + if (! node->isConstructor() && node->getOp() != EOpComma) { + error = true; + + return false; + } + + bool flag = node->getSequence().size() == 1 && node->getSequence()[0]->getAsTyped()->getAsConstantUnion(); + if (flag) { + singleConstantParam = true; + constructorType = node->getOp(); + size = node->getType().computeNumComponents(); + + if (node->getType().isMatrix()) { + isMatrix = true; + matrixCols = node->getType().getMatrixCols(); + matrixRows = node->getType().getMatrixRows(); + } + } + + for (TIntermSequence::iterator p = node->getSequence().begin(); + p != node->getSequence().end(); p++) { + + if (node->getOp() == EOpComma) + index = 0; + + (*p)->traverse(this); + } + if (flag) + { + singleConstantParam = false; + constructorType = EOpNull; + size = 0; + isMatrix = false; + matrixCols = 0; + matrixRows = 0; + } + + return false; +} + +void TConstTraverser::visitConstantUnion(TIntermConstantUnion* node) +{ + TConstUnionArray leftUnionArray(unionArray); + int instanceSize = type.computeNumComponents(); + + if (index >= instanceSize) + return; + + if (! singleConstantParam) { + int rightUnionSize = node->getType().computeNumComponents(); + + const TConstUnionArray& rightUnionArray = node->getConstArray(); + for (int i = 0; i < rightUnionSize; i++) { + if (index >= instanceSize) + return; + leftUnionArray[index] = rightUnionArray[i]; + + index++; + } + } else { + int endIndex = index + size; + const TConstUnionArray& rightUnionArray = node->getConstArray(); + if (! isMatrix) { + int count = 0; + int nodeComps = node->getType().computeNumComponents(); + for (int i = index; i < endIndex; i++) { + if (i >= instanceSize) + return; + + leftUnionArray[i] = rightUnionArray[count]; + + (index)++; + + if (nodeComps > 1) + count++; + } + } else { + // constructing a matrix, but from what? + if (node->isMatrix()) { + // Matrix from a matrix; this has the outer matrix, node is the argument matrix. + // Traverse the outer, potentially bigger matrix, fill in missing pieces with the + // identity matrix. + for (int c = 0; c < matrixCols; ++c) { + for (int r = 0; r < matrixRows; ++r) { + int targetOffset = index + c * matrixRows + r; + if (r < node->getType().getMatrixRows() && c < node->getType().getMatrixCols()) { + int srcOffset = c * node->getType().getMatrixRows() + r; + leftUnionArray[targetOffset] = rightUnionArray[srcOffset]; + } else if (r == c) + leftUnionArray[targetOffset].setDConst(1.0); + else + leftUnionArray[targetOffset].setDConst(0.0); + } + } + } else { + // matrix from vector or scalar + int count = 0; + const int startIndex = index; + int nodeComps = node->getType().computeNumComponents(); + for (int i = startIndex; i < endIndex; i++) { + if (i >= instanceSize) + return; + if (nodeComps == 1) { + // If there is a single scalar parameter to a matrix + // constructor, it is used to initialize all the + // components on the matrix's diagonal, with the + // remaining components initialized to 0.0. + if (i == startIndex || (i - startIndex) % (matrixRows + 1) == 0 ) + leftUnionArray[i] = rightUnionArray[count]; + else + leftUnionArray[i].setDConst(0.0); + } else { + // construct the matrix in column-major order, from + // the components provided, in order + leftUnionArray[i] = rightUnionArray[count]; + } + + index++; + + if (nodeComps > 1) + count++; + } + } + } + } +} + +bool TIntermediate::parseConstTree(TIntermNode* root, TConstUnionArray unionArray, TOperator constructorType, const TType& t, bool singleConstantParam) +{ + if (root == 0) + return false; + + TConstTraverser it(unionArray, singleConstantParam, constructorType, t); + + root->traverse(&it); + if (it.error) + return true; + else + return false; +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/parseVersions.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/parseVersions.h new file mode 100644 index 000000000..aa1964fc2 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/parseVersions.h @@ -0,0 +1,236 @@ +// +// Copyright (C) 2015-2018 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// This is implemented in Versions.cpp + +#ifndef _PARSE_VERSIONS_INCLUDED_ +#define _PARSE_VERSIONS_INCLUDED_ + +#include "../Public/ShaderLang.h" +#include "../Include/InfoSink.h" +#include "Scan.h" + +#include + +namespace glslang { + +// +// Base class for parse helpers. +// This just has version-related information and checking. +// This class should be sufficient for preprocessing. +// +class TParseVersions { +public: + TParseVersions(TIntermediate& interm, int version, EProfile profile, + const SpvVersion& spvVersion, EShLanguage language, TInfoSink& infoSink, + bool forwardCompatible, EShMessages messages) + : +#ifndef GLSLANG_WEB + forwardCompatible(forwardCompatible), + profile(profile), +#endif + infoSink(infoSink), version(version), + language(language), + spvVersion(spvVersion), + intermediate(interm), messages(messages), numErrors(0), currentScanner(0) { } + virtual ~TParseVersions() { } + void requireStage(const TSourceLoc&, EShLanguageMask, const char* featureDesc); + void requireStage(const TSourceLoc&, EShLanguage, const char* featureDesc); +#ifdef GLSLANG_WEB + const EProfile profile = EEsProfile; + bool isEsProfile() const { return true; } + void requireProfile(const TSourceLoc& loc, int profileMask, const char* featureDesc) + { + if (! (EEsProfile & profileMask)) + error(loc, "not supported with this profile:", featureDesc, ProfileName(profile)); + } + void profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, int numExtensions, + const char* const extensions[], const char* featureDesc) + { + if ((EEsProfile & profileMask) && (minVersion == 0 || version < minVersion)) + error(loc, "not supported for this version or the enabled extensions", featureDesc, ""); + } + void profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, const char* extension, + const char* featureDesc) + { + profileRequires(loc, profileMask, minVersion, extension ? 1 : 0, &extension, featureDesc); + } + void initializeExtensionBehavior() { } + void checkDeprecated(const TSourceLoc&, int queryProfiles, int depVersion, const char* featureDesc) { } + void requireNotRemoved(const TSourceLoc&, int queryProfiles, int removedVersion, const char* featureDesc) { } + void requireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], + const char* featureDesc) { } + void ppRequireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], + const char* featureDesc) { } + TExtensionBehavior getExtensionBehavior(const char*) { return EBhMissing; } + bool extensionTurnedOn(const char* const extension) { return false; } + bool extensionsTurnedOn(int numExtensions, const char* const extensions[]) { return false; } + void updateExtensionBehavior(int line, const char* const extension, const char* behavior) { } + void updateExtensionBehavior(const char* const extension, TExtensionBehavior) { } + void checkExtensionStage(const TSourceLoc&, const char* const extension) { } + void fullIntegerCheck(const TSourceLoc&, const char* op) { } + void doubleCheck(const TSourceLoc&, const char* op) { } + bool float16Arithmetic() { return false; } + void requireFloat16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) { } + bool int16Arithmetic() { return false; } + void requireInt16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) { } + bool int8Arithmetic() { return false; } + void requireInt8Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) { } + void int64Check(const TSourceLoc&, const char* op, bool builtIn = false) { } + void explicitFloat32Check(const TSourceLoc&, const char* op, bool builtIn = false) { } + void explicitFloat64Check(const TSourceLoc&, const char* op, bool builtIn = false) { } + bool relaxedErrors() const { return false; } + bool suppressWarnings() const { return true; } + bool isForwardCompatible() const { return false; } +#else + bool forwardCompatible; // true if errors are to be given for use of deprecated features + EProfile profile; // the declared profile in the shader (core by default) + bool isEsProfile() const { return profile == EEsProfile; } + void requireProfile(const TSourceLoc& loc, int profileMask, const char* featureDesc); + void profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, int numExtensions, + const char* const extensions[], const char* featureDesc); + void profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, const char* extension, + const char* featureDesc); + virtual void initializeExtensionBehavior(); + virtual void checkDeprecated(const TSourceLoc&, int queryProfiles, int depVersion, const char* featureDesc); + virtual void requireNotRemoved(const TSourceLoc&, int queryProfiles, int removedVersion, const char* featureDesc); + virtual void requireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], + const char* featureDesc); + virtual void ppRequireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], + const char* featureDesc); + virtual TExtensionBehavior getExtensionBehavior(const char*); + virtual bool extensionTurnedOn(const char* const extension); + virtual bool extensionsTurnedOn(int numExtensions, const char* const extensions[]); + virtual void updateExtensionBehavior(int line, const char* const extension, const char* behavior); + virtual void updateExtensionBehavior(const char* const extension, TExtensionBehavior); + virtual bool checkExtensionsRequested(const TSourceLoc&, int numExtensions, const char* const extensions[], + const char* featureDesc); + virtual void checkExtensionStage(const TSourceLoc&, const char* const extension); + virtual void fullIntegerCheck(const TSourceLoc&, const char* op); + + virtual void unimplemented(const TSourceLoc&, const char* featureDesc); + virtual void doubleCheck(const TSourceLoc&, const char* op); + virtual void float16Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void float16ScalarVectorCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual bool float16Arithmetic(); + virtual void requireFloat16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc); + virtual void int16ScalarVectorCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual bool int16Arithmetic(); + virtual void requireInt16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc); + virtual void int8ScalarVectorCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual bool int8Arithmetic(); + virtual void requireInt8Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc); + virtual void float16OpaqueCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void int64Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitInt8Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitInt16Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitInt32Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitFloat32Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitFloat64Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void fcoopmatCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void intcoopmatCheck(const TSourceLoc&, const char *op, bool builtIn = false); + bool relaxedErrors() const { return (messages & EShMsgRelaxedErrors) != 0; } + bool suppressWarnings() const { return (messages & EShMsgSuppressWarnings) != 0; } + bool isForwardCompatible() const { return forwardCompatible; } +#endif // GLSLANG_WEB + virtual void spvRemoved(const TSourceLoc&, const char* op); + virtual void vulkanRemoved(const TSourceLoc&, const char* op); + virtual void requireVulkan(const TSourceLoc&, const char* op); + virtual void requireSpv(const TSourceLoc&, const char* op); + + +#if defined(GLSLANG_WEB) && !defined(GLSLANG_WEB_DEVEL) + void C_DECL error(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) { addError(); } + void C_DECL warn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) { } + void C_DECL ppError(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) { addError(); } + void C_DECL ppWarn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) { } +#else + virtual void C_DECL error(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) = 0; + virtual void C_DECL warn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) = 0; + virtual void C_DECL ppError(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) = 0; + virtual void C_DECL ppWarn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) = 0; +#endif + + void addError() { ++numErrors; } + int getNumErrors() const { return numErrors; } + + void setScanner(TInputScanner* scanner) { currentScanner = scanner; } + TInputScanner* getScanner() const { return currentScanner; } + const TSourceLoc& getCurrentLoc() const { return currentScanner->getSourceLoc(); } + void setCurrentLine(int line) { currentScanner->setLine(line); } + void setCurrentColumn(int col) { currentScanner->setColumn(col); } + void setCurrentSourceName(const char* name) { currentScanner->setFile(name); } + void setCurrentString(int string) { currentScanner->setString(string); } + + void getPreamble(std::string&); +#ifdef ENABLE_HLSL + bool isReadingHLSL() const { return (messages & EShMsgReadHlsl) == EShMsgReadHlsl; } + bool hlslEnable16BitTypes() const { return (messages & EShMsgHlslEnable16BitTypes) != 0; } + bool hlslDX9Compatible() const { return (messages & EShMsgHlslDX9Compatible) != 0; } +#else + bool isReadingHLSL() const { return false; } +#endif + + TInfoSink& infoSink; + + // compilation mode + int version; // version, updated by #version in the shader + EShLanguage language; // really the stage + SpvVersion spvVersion; + TIntermediate& intermediate; // helper for making and hooking up pieces of the parse tree + +protected: + TMap extensionBehavior; // for each extension string, what its current behavior is set to + EShMessages messages; // errors/warnings/rule-sets + int numErrors; // number of compile-time errors encountered + TInputScanner* currentScanner; + +private: + explicit TParseVersions(const TParseVersions&); + TParseVersions& operator=(const TParseVersions&); +}; + +} // end namespace glslang + +#endif // _PARSE_VERSIONS_INCLUDED_ diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp new file mode 100644 index 000000000..ec3935614 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -0,0 +1,1338 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include + +#include "PpContext.h" +#include "PpTokens.h" + +namespace glslang { + +// Handle #define +int TPpContext::CPPdefine(TPpToken* ppToken) +{ + MacroSymbol mac; + + // get the macro name + int token = scanToken(ppToken); + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#define", ""); + return token; + } + if (ppToken->loc.string >= 0) { + // We are in user code; check for reserved name use: + parseContext.reservedPpErrorCheck(ppToken->loc, ppToken->name, "#define"); + } + + // save the macro name + const int defAtom = atomStrings.getAddAtom(ppToken->name); + TSourceLoc defineLoc = ppToken->loc; // because ppToken might go to the next line before we report errors + + // gather parameters to the macro, between (...) + token = scanToken(ppToken); + if (token == '(' && !ppToken->space) { + mac.functionLike = 1; + do { + token = scanToken(ppToken); + if (mac.args.size() == 0 && token == ')') + break; + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "bad argument", "#define", ""); + + return token; + } + const int argAtom = atomStrings.getAddAtom(ppToken->name); + + // check for duplication of parameter name + bool duplicate = false; + for (size_t a = 0; a < mac.args.size(); ++a) { + if (mac.args[a] == argAtom) { + parseContext.ppError(ppToken->loc, "duplicate macro parameter", "#define", ""); + duplicate = true; + break; + } + } + if (! duplicate) + mac.args.push_back(argAtom); + token = scanToken(ppToken); + } while (token == ','); + if (token != ')') { + parseContext.ppError(ppToken->loc, "missing parenthesis", "#define", ""); + + return token; + } + + token = scanToken(ppToken); + } else if (token != '\n' && token != EndOfInput && !ppToken->space) { + parseContext.ppWarn(ppToken->loc, "missing space after macro name", "#define", ""); + + return token; + } + + // record the definition of the macro + while (token != '\n' && token != EndOfInput) { + mac.body.putToken(token, ppToken); + token = scanToken(ppToken); + if (token != '\n' && ppToken->space) + mac.body.putToken(' ', ppToken); + } + + // check for duplicate definition + MacroSymbol* existing = lookupMacroDef(defAtom); + if (existing != nullptr) { + if (! existing->undef) { + // Already defined -- need to make sure they are identical: + // "Two replacement lists are identical if and only if the + // preprocessing tokens in both have the same number, + // ordering, spelling, and white-space separation, where all + // white-space separations are considered identical." + if (existing->functionLike != mac.functionLike) { + parseContext.ppError(defineLoc, "Macro redefined; function-like versus object-like:", "#define", + atomStrings.getString(defAtom)); + } else if (existing->args.size() != mac.args.size()) { + parseContext.ppError(defineLoc, "Macro redefined; different number of arguments:", "#define", + atomStrings.getString(defAtom)); + } else { + if (existing->args != mac.args) { + parseContext.ppError(defineLoc, "Macro redefined; different argument names:", "#define", + atomStrings.getString(defAtom)); + } + // set up to compare the two + existing->body.reset(); + mac.body.reset(); + int newToken; + bool firstToken = true; + do { + int oldToken; + TPpToken oldPpToken; + TPpToken newPpToken; + oldToken = existing->body.getToken(parseContext, &oldPpToken); + newToken = mac.body.getToken(parseContext, &newPpToken); + // for the first token, preceding spaces don't matter + if (firstToken) { + newPpToken.space = oldPpToken.space; + firstToken = false; + } + if (oldToken != newToken || oldPpToken != newPpToken) { + parseContext.ppError(defineLoc, "Macro redefined; different substitutions:", "#define", + atomStrings.getString(defAtom)); + break; + } + } while (newToken != EndOfInput); + } + } + *existing = mac; + } else + addMacroDef(defAtom, mac); + + return '\n'; +} + +// Handle #undef +int TPpContext::CPPundef(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#undef", ""); + + return token; + } + + parseContext.reservedPpErrorCheck(ppToken->loc, ppToken->name, "#undef"); + + MacroSymbol* macro = lookupMacroDef(atomStrings.getAtom(ppToken->name)); + if (macro != nullptr) + macro->undef = 1; + token = scanToken(ppToken); + if (token != '\n') + parseContext.ppError(ppToken->loc, "can only be followed by a single macro name", "#undef", ""); + + return token; +} + +// Handle #else +/* Skip forward to appropriate spot. This is used both +** to skip to a #endif after seeing an #else, AND to skip to a #else, +** #elif, or #endif after a #if/#ifdef/#ifndef/#elif test was false. +*/ +int TPpContext::CPPelse(int matchelse, TPpToken* ppToken) +{ + int depth = 0; + int token = scanToken(ppToken); + + while (token != EndOfInput) { + if (token != '#') { + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + + if (token == EndOfInput) + return token; + + token = scanToken(ppToken); + continue; + } + + if ((token = scanToken(ppToken)) != PpAtomIdentifier) + continue; + + int nextAtom = atomStrings.getAtom(ppToken->name); + if (nextAtom == PpAtomIf || nextAtom == PpAtomIfdef || nextAtom == PpAtomIfndef) { + depth++; + if (ifdepth >= maxIfNesting || elsetracker >= maxIfNesting) { + parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#if/#ifdef/#ifndef", ""); + return EndOfInput; + } else { + ifdepth++; + elsetracker++; + } + } else if (nextAtom == PpAtomEndif) { + token = extraTokenCheck(nextAtom, ppToken, scanToken(ppToken)); + elseSeen[elsetracker] = false; + --elsetracker; + if (depth == 0) { + // found the #endif we are looking for + if (ifdepth > 0) + --ifdepth; + break; + } + --depth; + --ifdepth; + } else if (matchelse && depth == 0) { + if (nextAtom == PpAtomElse) { + elseSeen[elsetracker] = true; + token = extraTokenCheck(nextAtom, ppToken, scanToken(ppToken)); + // found the #else we are looking for + break; + } else if (nextAtom == PpAtomElif) { + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", ""); + /* we decrement ifdepth here, because CPPif will increment + * it and we really want to leave it alone */ + if (ifdepth > 0) { + --ifdepth; + elseSeen[elsetracker] = false; + --elsetracker; + } + + return CPPif(ppToken); + } + } else if (nextAtom == PpAtomElse) { + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#else after #else", "#else", ""); + else + elseSeen[elsetracker] = true; + token = extraTokenCheck(nextAtom, ppToken, scanToken(ppToken)); + } else if (nextAtom == PpAtomElif) { + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", ""); + } + } + + return token; +} + +// Call when there should be no more tokens left on a line. +int TPpContext::extraTokenCheck(int contextAtom, TPpToken* ppToken, int token) +{ + if (token != '\n' && token != EndOfInput) { + static const char* message = "unexpected tokens following directive"; + + const char* label; + if (contextAtom == PpAtomElse) + label = "#else"; + else if (contextAtom == PpAtomElif) + label = "#elif"; + else if (contextAtom == PpAtomEndif) + label = "#endif"; + else if (contextAtom == PpAtomIf) + label = "#if"; + else if (contextAtom == PpAtomLine) + label = "#line"; + else + label = ""; + + if (parseContext.relaxedErrors()) + parseContext.ppWarn(ppToken->loc, message, label, ""); + else + parseContext.ppError(ppToken->loc, message, label, ""); + + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + } + + return token; +} + +enum eval_prec { + MIN_PRECEDENCE, + COND, LOGOR, LOGAND, OR, XOR, AND, EQUAL, RELATION, SHIFT, ADD, MUL, UNARY, + MAX_PRECEDENCE +}; + +namespace { + + int op_logor(int a, int b) { return a || b; } + int op_logand(int a, int b) { return a && b; } + int op_or(int a, int b) { return a | b; } + int op_xor(int a, int b) { return a ^ b; } + int op_and(int a, int b) { return a & b; } + int op_eq(int a, int b) { return a == b; } + int op_ne(int a, int b) { return a != b; } + int op_ge(int a, int b) { return a >= b; } + int op_le(int a, int b) { return a <= b; } + int op_gt(int a, int b) { return a > b; } + int op_lt(int a, int b) { return a < b; } + int op_shl(int a, int b) { return a << b; } + int op_shr(int a, int b) { return a >> b; } + int op_add(int a, int b) { return a + b; } + int op_sub(int a, int b) { return a - b; } + int op_mul(int a, int b) { return a * b; } + int op_div(int a, int b) { return a == INT_MIN && b == -1 ? 0 : a / b; } + int op_mod(int a, int b) { return a == INT_MIN && b == -1 ? 0 : a % b; } + int op_pos(int a) { return a; } + int op_neg(int a) { return -a; } + int op_cmpl(int a) { return ~a; } + int op_not(int a) { return !a; } + +}; + +struct TBinop { + int token, precedence, (*op)(int, int); +} binop[] = { + { PpAtomOr, LOGOR, op_logor }, + { PpAtomAnd, LOGAND, op_logand }, + { '|', OR, op_or }, + { '^', XOR, op_xor }, + { '&', AND, op_and }, + { PpAtomEQ, EQUAL, op_eq }, + { PpAtomNE, EQUAL, op_ne }, + { '>', RELATION, op_gt }, + { PpAtomGE, RELATION, op_ge }, + { '<', RELATION, op_lt }, + { PpAtomLE, RELATION, op_le }, + { PpAtomLeft, SHIFT, op_shl }, + { PpAtomRight, SHIFT, op_shr }, + { '+', ADD, op_add }, + { '-', ADD, op_sub }, + { '*', MUL, op_mul }, + { '/', MUL, op_div }, + { '%', MUL, op_mod }, +}; + +struct TUnop { + int token, (*op)(int); +} unop[] = { + { '+', op_pos }, + { '-', op_neg }, + { '~', op_cmpl }, + { '!', op_not }, +}; + +#define NUM_ELEMENTS(A) (sizeof(A) / sizeof(A[0])) + +int TPpContext::eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken* ppToken) +{ + TSourceLoc loc = ppToken->loc; // because we sometimes read the newline before reporting the error + if (token == PpAtomIdentifier) { + if (strcmp("defined", ppToken->name) == 0) { + if (! parseContext.isReadingHLSL() && isMacroInput()) { + if (parseContext.relaxedErrors()) + parseContext.ppWarn(ppToken->loc, "nonportable when expanded from macros for preprocessor expression", + "defined", ""); + else + parseContext.ppError(ppToken->loc, "cannot use in preprocessor expression when expanded from macros", + "defined", ""); + } + bool needclose = 0; + token = scanToken(ppToken); + if (token == '(') { + needclose = true; + token = scanToken(ppToken); + } + if (token != PpAtomIdentifier) { + parseContext.ppError(loc, "incorrect directive, expected identifier", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + + MacroSymbol* macro = lookupMacroDef(atomStrings.getAtom(ppToken->name)); + res = macro != nullptr ? !macro->undef : 0; + token = scanToken(ppToken); + if (needclose) { + if (token != ')') { + parseContext.ppError(loc, "expected ')'", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + token = scanToken(ppToken); + } + } else { + token = evalToToken(token, shortCircuit, res, err, ppToken); + return eval(token, precedence, shortCircuit, res, err, ppToken); + } + } else if (token == PpAtomConstInt) { + res = ppToken->ival; + token = scanToken(ppToken); + } else if (token == '(') { + token = scanToken(ppToken); + token = eval(token, MIN_PRECEDENCE, shortCircuit, res, err, ppToken); + if (! err) { + if (token != ')') { + parseContext.ppError(loc, "expected ')'", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + token = scanToken(ppToken); + } + } else { + int op = NUM_ELEMENTS(unop) - 1; + for (; op >= 0; op--) { + if (unop[op].token == token) + break; + } + if (op >= 0) { + token = scanToken(ppToken); + token = eval(token, UNARY, shortCircuit, res, err, ppToken); + res = unop[op].op(res); + } else { + parseContext.ppError(loc, "bad expression", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + } + + token = evalToToken(token, shortCircuit, res, err, ppToken); + + // Perform evaluation of binary operation, if there is one, otherwise we are done. + while (! err) { + if (token == ')' || token == '\n') + break; + int op; + for (op = NUM_ELEMENTS(binop) - 1; op >= 0; op--) { + if (binop[op].token == token) + break; + } + if (op < 0 || binop[op].precedence <= precedence) + break; + int leftSide = res; + + // Setup short-circuiting, needed for ES, unless already in a short circuit. + // (Once in a short-circuit, can't turn off again, until that whole subexpression is done. + if (! shortCircuit) { + if ((token == PpAtomOr && leftSide == 1) || + (token == PpAtomAnd && leftSide == 0)) + shortCircuit = true; + } + + token = scanToken(ppToken); + token = eval(token, binop[op].precedence, shortCircuit, res, err, ppToken); + + if (binop[op].op == op_div || binop[op].op == op_mod) { + if (res == 0) { + parseContext.ppError(loc, "division by 0", "preprocessor evaluation", ""); + res = 1; + } + } + res = binop[op].op(leftSide, res); + } + + return token; +} + +// Expand macros, skipping empty expansions, to get to the first real token in those expansions. +int TPpContext::evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken* ppToken) +{ + while (token == PpAtomIdentifier && strcmp("defined", ppToken->name) != 0) { + switch (MacroExpand(ppToken, true, false)) { + case MacroExpandNotStarted: + case MacroExpandError: + parseContext.ppError(ppToken->loc, "can't evaluate expression", "preprocessor evaluation", ""); + err = true; + res = 0; + break; + case MacroExpandStarted: + break; + case MacroExpandUndef: + if (! shortCircuit && parseContext.isEsProfile()) { + const char* message = "undefined macro in expression not allowed in es profile"; + if (parseContext.relaxedErrors()) + parseContext.ppWarn(ppToken->loc, message, "preprocessor evaluation", ppToken->name); + else + parseContext.ppError(ppToken->loc, message, "preprocessor evaluation", ppToken->name); + } + break; + } + token = scanToken(ppToken); + if (err) + break; + } + + return token; +} + +// Handle #if +int TPpContext::CPPif(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + if (ifdepth >= maxIfNesting || elsetracker >= maxIfNesting) { + parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#if", ""); + return EndOfInput; + } else { + elsetracker++; + ifdepth++; + } + int res = 0; + bool err = false; + token = eval(token, MIN_PRECEDENCE, false, res, err, ppToken); + token = extraTokenCheck(PpAtomIf, ppToken, token); + if (!res && !err) + token = CPPelse(1, ppToken); + + return token; +} + +// Handle #ifdef +int TPpContext::CPPifdef(int defined, TPpToken* ppToken) +{ + int token = scanToken(ppToken); + if (ifdepth > maxIfNesting || elsetracker > maxIfNesting) { + parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#ifdef", ""); + return EndOfInput; + } else { + elsetracker++; + ifdepth++; + } + + if (token != PpAtomIdentifier) { + if (defined) + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#ifdef", ""); + else + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#ifndef", ""); + } else { + MacroSymbol* macro = lookupMacroDef(atomStrings.getAtom(ppToken->name)); + token = scanToken(ppToken); + if (token != '\n') { + parseContext.ppError(ppToken->loc, "unexpected tokens following #ifdef directive - expected a newline", "#ifdef", ""); + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + } + if (((macro != nullptr && !macro->undef) ? 1 : 0) != defined) + token = CPPelse(1, ppToken); + } + + return token; +} + +// Handle #include ... +// TODO: Handle macro expansions for the header name +int TPpContext::CPPinclude(TPpToken* ppToken) +{ + const TSourceLoc directiveLoc = ppToken->loc; + bool startWithLocalSearch = true; // to additionally include the extra "" paths + int token; + + // Find the first non-whitespace char after #include + int ch = getChar(); + while (ch == ' ' || ch == '\t') { + ch = getChar(); + } + if (ch == '<') { + // style + startWithLocalSearch = false; + token = scanHeaderName(ppToken, '>'); + } else if (ch == '"') { + // "header-name" style + token = scanHeaderName(ppToken, '"'); + } else { + // unexpected, get the full token to generate the error + ungetChar(); + token = scanToken(ppToken); + } + + if (token != PpAtomConstString) { + parseContext.ppError(directiveLoc, "must be followed by a header name", "#include", ""); + return token; + } + + // Make a copy of the name because it will be overwritten by the next token scan. + const std::string filename = ppToken->name; + + // See if the directive was well formed + token = scanToken(ppToken); + if (token != '\n') { + if (token == EndOfInput) + parseContext.ppError(ppToken->loc, "expected newline after header name:", "#include", "%s", filename.c_str()); + else + parseContext.ppError(ppToken->loc, "extra content after header name:", "#include", "%s", filename.c_str()); + return token; + } + + // Process well-formed directive + + // Find the inclusion, first look in "Local" ("") paths, if requested, + // otherwise, only search the "System" (<>) paths. + TShader::Includer::IncludeResult* res = nullptr; + if (startWithLocalSearch) + res = includer.includeLocal(filename.c_str(), currentSourceFile.c_str(), includeStack.size() + 1); + if (res == nullptr || res->headerName.empty()) { + includer.releaseInclude(res); + res = includer.includeSystem(filename.c_str(), currentSourceFile.c_str(), includeStack.size() + 1); + } + + // Process the results + if (res != nullptr && !res->headerName.empty()) { + if (res->headerData != nullptr && res->headerLength > 0) { + // path for processing one or more tokens from an included header, hand off 'res' + const bool forNextLine = parseContext.lineDirectiveShouldSetNextLine(); + std::ostringstream prologue; + std::ostringstream epilogue; + prologue << "#line " << forNextLine << " " << "\"" << res->headerName << "\"\n"; + epilogue << (res->headerData[res->headerLength - 1] == '\n'? "" : "\n") << + "#line " << directiveLoc.line + forNextLine << " " << directiveLoc.getStringNameOrNum() << "\n"; + pushInput(new TokenizableIncludeFile(directiveLoc, prologue.str(), res, epilogue.str(), this)); + parseContext.intermediate.addIncludeText(res->headerName.c_str(), res->headerData, res->headerLength); + // There's no "current" location anymore. + parseContext.setCurrentColumn(0); + } else { + // things are okay, but there is nothing to process + includer.releaseInclude(res); + } + } else { + // error path, clean up + std::string message = + res != nullptr ? std::string(res->headerData, res->headerLength) + : std::string("Could not process include directive"); + parseContext.ppError(directiveLoc, message.c_str(), "#include", "for header name: %s", filename.c_str()); + includer.releaseInclude(res); + } + + return token; +} + +// Handle #line +int TPpContext::CPPline(TPpToken* ppToken) +{ + // "#line must have, after macro substitution, one of the following forms: + // "#line line + // "#line line source-string-number" + + int token = scanToken(ppToken); + const TSourceLoc directiveLoc = ppToken->loc; + if (token == '\n') { + parseContext.ppError(ppToken->loc, "must by followed by an integral literal", "#line", ""); + return token; + } + + int lineRes = 0; // Line number after macro expansion. + int lineToken = 0; + bool hasFile = false; + int fileRes = 0; // Source file number after macro expansion. + const char* sourceName = nullptr; // Optional source file name. + bool lineErr = false; + bool fileErr = false; + disableEscapeSequences = true; + token = eval(token, MIN_PRECEDENCE, false, lineRes, lineErr, ppToken); + disableEscapeSequences = false; + if (! lineErr) { + lineToken = lineRes; + if (token == '\n') + ++lineRes; + + if (parseContext.lineDirectiveShouldSetNextLine()) + --lineRes; + parseContext.setCurrentLine(lineRes); + + if (token != '\n') { +#ifndef GLSLANG_WEB + if (token == PpAtomConstString) { + parseContext.ppRequireExtensions(directiveLoc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based #line"); + // We need to save a copy of the string instead of pointing + // to the name field of the token since the name field + // will likely be overwritten by the next token scan. + sourceName = atomStrings.getString(atomStrings.getAddAtom(ppToken->name)); + parseContext.setCurrentSourceName(sourceName); + hasFile = true; + token = scanToken(ppToken); + } else +#endif + { + token = eval(token, MIN_PRECEDENCE, false, fileRes, fileErr, ppToken); + if (! fileErr) { + parseContext.setCurrentString(fileRes); + hasFile = true; + } + } + } + } + if (!fileErr && !lineErr) { + parseContext.notifyLineDirective(directiveLoc.line, lineToken, hasFile, fileRes, sourceName); + } + token = extraTokenCheck(PpAtomLine, ppToken, token); + + return token; +} + +// Handle #error +int TPpContext::CPPerror(TPpToken* ppToken) +{ + disableEscapeSequences = true; + int token = scanToken(ppToken); + disableEscapeSequences = false; + std::string message; + TSourceLoc loc = ppToken->loc; + + while (token != '\n' && token != EndOfInput) { + if (token == PpAtomConstInt16 || token == PpAtomConstUint16 || + token == PpAtomConstInt || token == PpAtomConstUint || + token == PpAtomConstInt64 || token == PpAtomConstUint64 || + token == PpAtomConstFloat16 || + token == PpAtomConstFloat || token == PpAtomConstDouble) { + message.append(ppToken->name); + } else if (token == PpAtomIdentifier || token == PpAtomConstString) { + message.append(ppToken->name); + } else { + message.append(atomStrings.getString(token)); + } + message.append(" "); + token = scanToken(ppToken); + } + parseContext.notifyErrorDirective(loc.line, message.c_str()); + // store this msg into the shader's information log..set the Compile Error flag!!!! + parseContext.ppError(loc, message.c_str(), "#error", ""); + + return '\n'; +} + +// Handle #pragma +int TPpContext::CPPpragma(TPpToken* ppToken) +{ + char SrcStrName[2]; + TVector tokens; + + TSourceLoc loc = ppToken->loc; // because we go to the next line before processing + int token = scanToken(ppToken); + while (token != '\n' && token != EndOfInput) { + switch (token) { + case PpAtomIdentifier: + case PpAtomConstInt: + case PpAtomConstUint: + case PpAtomConstInt64: + case PpAtomConstUint64: + case PpAtomConstInt16: + case PpAtomConstUint16: + case PpAtomConstFloat: + case PpAtomConstDouble: + case PpAtomConstFloat16: + tokens.push_back(ppToken->name); + break; + default: + SrcStrName[0] = (char)token; + SrcStrName[1] = '\0'; + tokens.push_back(SrcStrName); + } + token = scanToken(ppToken); + } + + if (token == EndOfInput) + parseContext.ppError(loc, "directive must end with a newline", "#pragma", ""); + else + parseContext.handlePragma(loc, tokens); + + return token; +} + +// #version: This is just for error checking: the version and profile are decided before preprocessing starts +int TPpContext::CPPversion(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + + if (errorOnVersion || versionSeen) { + if (parseContext.isReadingHLSL()) + parseContext.ppError(ppToken->loc, "invalid preprocessor command", "#version", ""); + else + parseContext.ppError(ppToken->loc, "must occur first in shader", "#version", ""); + } + versionSeen = true; + + if (token == '\n') { + parseContext.ppError(ppToken->loc, "must be followed by version number", "#version", ""); + + return token; + } + + if (token != PpAtomConstInt) + parseContext.ppError(ppToken->loc, "must be followed by version number", "#version", ""); + + ppToken->ival = atoi(ppToken->name); + int versionNumber = ppToken->ival; + int line = ppToken->loc.line; + token = scanToken(ppToken); + + if (token == '\n') { + parseContext.notifyVersion(line, versionNumber, nullptr); + return token; + } else { + int profileAtom = atomStrings.getAtom(ppToken->name); + if (profileAtom != PpAtomCore && + profileAtom != PpAtomCompatibility && + profileAtom != PpAtomEs) + parseContext.ppError(ppToken->loc, "bad profile name; use es, core, or compatibility", "#version", ""); + parseContext.notifyVersion(line, versionNumber, ppToken->name); + token = scanToken(ppToken); + + if (token == '\n') + return token; + else + parseContext.ppError(ppToken->loc, "bad tokens following profile -- expected newline", "#version", ""); + } + + return token; +} + +// Handle #extension +int TPpContext::CPPextension(TPpToken* ppToken) +{ + int line = ppToken->loc.line; + int token = scanToken(ppToken); + char extensionName[MaxTokenLength + 1]; + + if (token=='\n') { + parseContext.ppError(ppToken->loc, "extension name not specified", "#extension", ""); + return token; + } + + if (token != PpAtomIdentifier) + parseContext.ppError(ppToken->loc, "extension name expected", "#extension", ""); + + snprintf(extensionName, sizeof(extensionName), "%s", ppToken->name); + + token = scanToken(ppToken); + if (token != ':') { + parseContext.ppError(ppToken->loc, "':' missing after extension name", "#extension", ""); + return token; + } + + token = scanToken(ppToken); + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "behavior for extension not specified", "#extension", ""); + return token; + } + + parseContext.updateExtensionBehavior(line, extensionName, ppToken->name); + parseContext.notifyExtensionDirective(line, extensionName, ppToken->name); + + token = scanToken(ppToken); + if (token == '\n') + return token; + else + parseContext.ppError(ppToken->loc, "extra tokens -- expected newline", "#extension",""); + + return token; +} + +int TPpContext::readCPPline(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + + if (token == PpAtomIdentifier) { + switch (atomStrings.getAtom(ppToken->name)) { + case PpAtomDefine: + token = CPPdefine(ppToken); + break; + case PpAtomElse: + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#else after #else", "#else", ""); + elseSeen[elsetracker] = true; + if (ifdepth == 0) + parseContext.ppError(ppToken->loc, "mismatched statements", "#else", ""); + token = extraTokenCheck(PpAtomElse, ppToken, scanToken(ppToken)); + token = CPPelse(0, ppToken); + break; + case PpAtomElif: + if (ifdepth == 0) + parseContext.ppError(ppToken->loc, "mismatched statements", "#elif", ""); + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", ""); + // this token is really a dont care, but we still need to eat the tokens + token = scanToken(ppToken); + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + token = CPPelse(0, ppToken); + break; + case PpAtomEndif: + if (ifdepth == 0) + parseContext.ppError(ppToken->loc, "mismatched statements", "#endif", ""); + else { + elseSeen[elsetracker] = false; + --elsetracker; + --ifdepth; + } + token = extraTokenCheck(PpAtomEndif, ppToken, scanToken(ppToken)); + break; + case PpAtomIf: + token = CPPif(ppToken); + break; + case PpAtomIfdef: + token = CPPifdef(1, ppToken); + break; + case PpAtomIfndef: + token = CPPifdef(0, ppToken); + break; + case PpAtomLine: + token = CPPline(ppToken); + break; +#ifndef GLSLANG_WEB + case PpAtomInclude: + if(!parseContext.isReadingHLSL()) { + parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_include_directive, "#include"); + } + token = CPPinclude(ppToken); + break; + case PpAtomPragma: + token = CPPpragma(ppToken); + break; +#endif + case PpAtomUndef: + token = CPPundef(ppToken); + break; + case PpAtomError: + token = CPPerror(ppToken); + break; + case PpAtomVersion: + token = CPPversion(ppToken); + break; + case PpAtomExtension: + token = CPPextension(ppToken); + break; + default: + parseContext.ppError(ppToken->loc, "invalid directive:", "#", ppToken->name); + break; + } + } else if (token != '\n' && token != EndOfInput) + parseContext.ppError(ppToken->loc, "invalid directive", "#", ""); + + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + + return token; +} + +// Context-dependent parsing of a #include . +// Assumes no macro expansions etc. are being done; the name is just on the current input. +// Always creates a name and returns PpAtomicConstString, unless we run out of input. +int TPpContext::scanHeaderName(TPpToken* ppToken, char delimit) +{ + bool tooLong = false; + + if (inputStack.empty()) + return EndOfInput; + + int len = 0; + ppToken->name[0] = '\0'; + do { + int ch = inputStack.back()->getch(); + + // done yet? + if (ch == delimit) { + ppToken->name[len] = '\0'; + if (tooLong) + parseContext.ppError(ppToken->loc, "header name too long", "", ""); + return PpAtomConstString; + } else if (ch == EndOfInput) + return EndOfInput; + + // found a character to expand the name with + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + else + tooLong = true; + } while (true); +} + +// Macro-expand a macro argument 'arg' to create 'expandedArg'. +// Does not replace 'arg'. +// Returns nullptr if no expanded argument is created. +TPpContext::TokenStream* TPpContext::PrescanMacroArg(TokenStream& arg, TPpToken* ppToken, bool newLineOkay) +{ + // expand the argument + TokenStream* expandedArg = new TokenStream; + pushInput(new tMarkerInput(this)); + pushTokenStreamInput(arg); + int token; + while ((token = scanToken(ppToken)) != tMarkerInput::marker && token != EndOfInput) { + token = tokenPaste(token, *ppToken); + if (token == PpAtomIdentifier) { + switch (MacroExpand(ppToken, false, newLineOkay)) { + case MacroExpandNotStarted: + break; + case MacroExpandError: + // toss the rest of the pushed-input argument by scanning until tMarkerInput + while ((token = scanToken(ppToken)) != tMarkerInput::marker && token != EndOfInput) + ; + break; + case MacroExpandStarted: + case MacroExpandUndef: + continue; + } + } + if (token == tMarkerInput::marker || token == EndOfInput) + break; + expandedArg->putToken(token, ppToken); + } + + if (token != tMarkerInput::marker) { + // Error, or MacroExpand ate the marker, so had bad input, recover + delete expandedArg; + expandedArg = nullptr; + } + + return expandedArg; +} + +// +// Return the next token for a macro expansion, handling macro arguments, +// whose semantics are dependent on being adjacent to ##. +// +int TPpContext::tMacroInput::scan(TPpToken* ppToken) +{ + int token; + do { + token = mac->body.getToken(pp->parseContext, ppToken); + } while (token == ' '); // handle white space in macro + + // Hash operators basically turn off a round of macro substitution + // (the round done on the argument before the round done on the RHS of the + // macro definition): + // + // "A parameter in the replacement list, unless preceded by a # or ## + // preprocessing token or followed by a ## preprocessing token (see below), + // is replaced by the corresponding argument after all macros contained + // therein have been expanded." + // + // "If, in the replacement list, a parameter is immediately preceded or + // followed by a ## preprocessing token, the parameter is replaced by the + // corresponding argument's preprocessing token sequence." + + bool pasting = false; + if (postpaste) { + // don't expand next token + pasting = true; + postpaste = false; + } + + if (prepaste) { + // already know we should be on a ##, verify + assert(token == PpAtomPaste); + prepaste = false; + postpaste = true; + } + + // see if are preceding a ## + if (mac->body.peekUntokenizedPasting()) { + prepaste = true; + pasting = true; + } + + // HLSL does expand macros before concatenation + if (pasting && pp->parseContext.isReadingHLSL()) + pasting = false; + + // TODO: preprocessor: properly handle whitespace (or lack of it) between tokens when expanding + if (token == PpAtomIdentifier) { + int i; + for (i = (int)mac->args.size() - 1; i >= 0; i--) + if (strcmp(pp->atomStrings.getString(mac->args[i]), ppToken->name) == 0) + break; + if (i >= 0) { + TokenStream* arg = expandedArgs[i]; + if (arg == nullptr || pasting) + arg = args[i]; + pp->pushTokenStreamInput(*arg, prepaste); + + return pp->scanToken(ppToken); + } + } + + if (token == EndOfInput) + mac->busy = 0; + + return token; +} + +// return a textual zero, for scanning a macro that was never defined +int TPpContext::tZeroInput::scan(TPpToken* ppToken) +{ + if (done) + return EndOfInput; + + ppToken->name[0] = '0'; + ppToken->name[1] = 0; + ppToken->ival = 0; + ppToken->space = false; + done = true; + + return PpAtomConstInt; +} + +// +// Check a token to see if it is a macro that should be expanded: +// - If it is, and defined, push a tInput that will produce the appropriate +// expansion and return MacroExpandStarted. +// - If it is, but undefined, and expandUndef is requested, push a tInput +// that will expand to 0 and return MacroExpandUndef. +// - Otherwise, there is no expansion, and there are two cases: +// * It might be okay there is no expansion, and no specific error was +// detected. Returns MacroExpandNotStarted. +// * The expansion was started, but could not be completed, due to an error +// that cannot be recovered from. Returns MacroExpandError. +// +MacroExpandResult TPpContext::MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay) +{ + ppToken->space = false; + int macroAtom = atomStrings.getAtom(ppToken->name); + switch (macroAtom) { + case PpAtomLineMacro: + ppToken->ival = parseContext.getCurrentLoc().line; + snprintf(ppToken->name, sizeof(ppToken->name), "%d", ppToken->ival); + UngetToken(PpAtomConstInt, ppToken); + return MacroExpandStarted; + + case PpAtomFileMacro: { + if (parseContext.getCurrentLoc().name) + parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based __FILE__"); + ppToken->ival = parseContext.getCurrentLoc().string; + snprintf(ppToken->name, sizeof(ppToken->name), "%s", ppToken->loc.getStringNameOrNum().c_str()); + UngetToken(PpAtomConstInt, ppToken); + return MacroExpandStarted; + } + + case PpAtomVersionMacro: + ppToken->ival = parseContext.version; + snprintf(ppToken->name, sizeof(ppToken->name), "%d", ppToken->ival); + UngetToken(PpAtomConstInt, ppToken); + return MacroExpandStarted; + + default: + break; + } + + MacroSymbol* macro = macroAtom == 0 ? nullptr : lookupMacroDef(macroAtom); + + // no recursive expansions + if (macro != nullptr && macro->busy) + return MacroExpandNotStarted; + + // not expanding undefined macros + if ((macro == nullptr || macro->undef) && ! expandUndef) + return MacroExpandNotStarted; + + // 0 is the value of an undefined macro + if ((macro == nullptr || macro->undef) && expandUndef) { + pushInput(new tZeroInput(this)); + return MacroExpandUndef; + } + + tMacroInput *in = new tMacroInput(this); + + TSourceLoc loc = ppToken->loc; // in case we go to the next line before discovering the error + in->mac = macro; + if (macro->functionLike) { + // We don't know yet if this will be a successful call of a + // function-like macro; need to look for a '(', but without trashing + // the passed in ppToken, until we know we are no longer speculative. + TPpToken parenToken; + int token = scanToken(&parenToken); + if (newLineOkay) { + while (token == '\n') + token = scanToken(&parenToken); + } + if (token != '(') { + // Function-like macro called with object-like syntax: okay, don't expand. + // (We ate exactly one token that might not be white space; put it back. + UngetToken(token, &parenToken); + delete in; + return MacroExpandNotStarted; + } + in->args.resize(in->mac->args.size()); + for (size_t i = 0; i < in->mac->args.size(); i++) + in->args[i] = new TokenStream; + in->expandedArgs.resize(in->mac->args.size()); + for (size_t i = 0; i < in->mac->args.size(); i++) + in->expandedArgs[i] = nullptr; + size_t arg = 0; + bool tokenRecorded = false; + do { + TVector nestStack; + while (true) { + token = scanToken(ppToken); + if (token == EndOfInput || token == tMarkerInput::marker) { + parseContext.ppError(loc, "End of input in macro", "macro expansion", atomStrings.getString(macroAtom)); + delete in; + return MacroExpandError; + } + if (token == '\n') { + if (! newLineOkay) { + parseContext.ppError(loc, "End of line in macro substitution:", "macro expansion", atomStrings.getString(macroAtom)); + delete in; + return MacroExpandError; + } + continue; + } + if (token == '#') { + parseContext.ppError(ppToken->loc, "unexpected '#'", "macro expansion", atomStrings.getString(macroAtom)); + delete in; + return MacroExpandError; + } + if (in->mac->args.size() == 0 && token != ')') + break; + if (nestStack.size() == 0 && (token == ',' || token == ')')) + break; + if (token == '(') + nestStack.push_back(')'); + else if (token == '{' && parseContext.isReadingHLSL()) + nestStack.push_back('}'); + else if (nestStack.size() > 0 && token == nestStack.back()) + nestStack.pop_back(); + in->args[arg]->putToken(token, ppToken); + tokenRecorded = true; + } + // end of single argument scan + + if (token == ')') { + // closing paren of call + if (in->mac->args.size() == 1 && !tokenRecorded) + break; + arg++; + break; + } + arg++; + } while (arg < in->mac->args.size()); + // end of all arguments scan + + if (arg < in->mac->args.size()) + parseContext.ppError(loc, "Too few args in Macro", "macro expansion", atomStrings.getString(macroAtom)); + else if (token != ')') { + // Error recover code; find end of call, if possible + int depth = 0; + while (token != EndOfInput && (depth > 0 || token != ')')) { + if (token == ')' || token == '}') + depth--; + token = scanToken(ppToken); + if (token == '(' || token == '{') + depth++; + } + + if (token == EndOfInput) { + parseContext.ppError(loc, "End of input in macro", "macro expansion", atomStrings.getString(macroAtom)); + delete in; + return MacroExpandError; + } + parseContext.ppError(loc, "Too many args in macro", "macro expansion", atomStrings.getString(macroAtom)); + } + + // We need both expanded and non-expanded forms of the argument, for whether or + // not token pasting will be applied later when the argument is consumed next to ##. + for (size_t i = 0; i < in->mac->args.size(); i++) + in->expandedArgs[i] = PrescanMacroArg(*in->args[i], ppToken, newLineOkay); + } + + pushInput(in); + macro->busy = 1; + macro->body.reset(); + + return MacroExpandStarted; +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp new file mode 100644 index 000000000..06c2333ef --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp @@ -0,0 +1,181 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include "PpContext.h" +#include "PpTokens.h" + +namespace { + +using namespace glslang; + +const struct { + int val; + const char* str; +} tokens[] = { + + { PPAtomAddAssign, "+=" }, + { PPAtomSubAssign, "-=" }, + { PPAtomMulAssign, "*=" }, + { PPAtomDivAssign, "/=" }, + { PPAtomModAssign, "%=" }, + + { PpAtomRight, ">>" }, + { PpAtomLeft, "<<" }, + { PpAtomAnd, "&&" }, + { PpAtomOr, "||" }, + { PpAtomXor, "^^" }, + + { PpAtomRightAssign, ">>=" }, + { PpAtomLeftAssign, "<<=" }, + { PpAtomAndAssign, "&=" }, + { PpAtomOrAssign, "|=" }, + { PpAtomXorAssign, "^=" }, + + { PpAtomEQ, "==" }, + { PpAtomNE, "!=" }, + { PpAtomGE, ">=" }, + { PpAtomLE, "<=" }, + + { PpAtomDecrement, "--" }, + { PpAtomIncrement, "++" }, + + { PpAtomColonColon, "::" }, + + { PpAtomDefine, "define" }, + { PpAtomUndef, "undef" }, + { PpAtomIf, "if" }, + { PpAtomElif, "elif" }, + { PpAtomElse, "else" }, + { PpAtomEndif, "endif" }, + { PpAtomIfdef, "ifdef" }, + { PpAtomIfndef, "ifndef" }, + { PpAtomLine, "line" }, + { PpAtomPragma, "pragma" }, + { PpAtomError, "error" }, + + { PpAtomVersion, "version" }, + { PpAtomCore, "core" }, + { PpAtomCompatibility, "compatibility" }, + { PpAtomEs, "es" }, + { PpAtomExtension, "extension" }, + + { PpAtomLineMacro, "__LINE__" }, + { PpAtomFileMacro, "__FILE__" }, + { PpAtomVersionMacro, "__VERSION__" }, + + { PpAtomInclude, "include" }, +}; + +} // end anonymous namespace + +namespace glslang { + +// +// Initialize the atom table. +// +TStringAtomMap::TStringAtomMap() +{ + badToken.assign(""); + + // Add single character tokens to the atom table: + const char* s = "~!%^&*()-+=|,.<>/?;:[]{}#\\"; + char t[2]; + + t[1] = '\0'; + while (*s) { + t[0] = *s; + addAtomFixed(t, s[0]); + s++; + } + + // Add multiple character scanner tokens : + for (size_t ii = 0; ii < sizeof(tokens)/sizeof(tokens[0]); ii++) + addAtomFixed(tokens[ii].str, tokens[ii].val); + + nextAtom = PpAtomLast; +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp new file mode 100644 index 000000000..1363ce2be --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp @@ -0,0 +1,120 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#include +#include + +#include "PpContext.h" + +namespace glslang { + +TPpContext::TPpContext(TParseContextBase& pc, const std::string& rootFileName, TShader::Includer& inclr) : + preamble(0), strings(0), previous_token('\n'), parseContext(pc), includer(inclr), inComment(false), + rootFileName(rootFileName), + currentSourceFile(rootFileName), + disableEscapeSequences(false) +{ + ifdepth = 0; + for (elsetracker = 0; elsetracker < maxIfNesting; elsetracker++) + elseSeen[elsetracker] = false; + elsetracker = 0; + + strtodStream.imbue(std::locale::classic()); +} + +TPpContext::~TPpContext() +{ + delete [] preamble; + + // free up the inputStack + while (! inputStack.empty()) + popInput(); +} + +void TPpContext::setInput(TInputScanner& input, bool versionWillBeError) +{ + assert(inputStack.size() == 0); + + pushInput(new tStringInput(this, input)); + + errorOnVersion = versionWillBeError; + versionSeen = false; +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpContext.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpContext.h new file mode 100644 index 000000000..714b5eadb --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpContext.h @@ -0,0 +1,703 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef PPCONTEXT_H +#define PPCONTEXT_H + +#include +#include +#include + +#include "../ParseHelper.h" +#include "PpTokens.h" + +/* windows only pragma */ +#ifdef _MSC_VER + #pragma warning(disable : 4127) +#endif + +namespace glslang { + +class TPpToken { +public: + TPpToken() { clear(); } + void clear() + { + space = false; + i64val = 0; + loc.init(); + name[0] = 0; + } + + // Used for comparing macro definitions, so checks what is relevant for that. + bool operator==(const TPpToken& right) const + { + return space == right.space && + ival == right.ival && dval == right.dval && i64val == right.i64val && + strncmp(name, right.name, MaxTokenLength) == 0; + } + bool operator!=(const TPpToken& right) const { return ! operator==(right); } + + TSourceLoc loc; + // True if a space (for white space or a removed comment) should also be + // recognized, in front of the token returned: + bool space; + // Numeric value of the token: + union { + int ival; + double dval; + long long i64val; + }; + // Text string of the token: + char name[MaxTokenLength + 1]; +}; + +class TStringAtomMap { +// +// Implementation is in PpAtom.cpp +// +// Maintain a bi-directional mapping between relevant preprocessor strings and +// "atoms" which a unique integers (small, contiguous, not hash-like) per string. +// +public: + TStringAtomMap(); + + // Map string -> atom. + // Return 0 if no existing string. + int getAtom(const char* s) const + { + auto it = atomMap.find(s); + return it == atomMap.end() ? 0 : it->second; + } + + // Map a new or existing string -> atom, inventing a new atom if necessary. + int getAddAtom(const char* s) + { + int atom = getAtom(s); + if (atom == 0) { + atom = nextAtom++; + addAtomFixed(s, atom); + } + return atom; + } + + // Map atom -> string. + const char* getString(int atom) const { return stringMap[atom]->c_str(); } + +protected: + TStringAtomMap(TStringAtomMap&); + TStringAtomMap& operator=(TStringAtomMap&); + + TUnorderedMap atomMap; + TVector stringMap; // these point into the TString in atomMap + int nextAtom; + + // Bad source characters can lead to bad atoms, so gracefully handle those by + // pre-filling the table with them (to avoid if tests later). + TString badToken; + + // Add bi-directional mappings: + // - string -> atom + // - atom -> string + void addAtomFixed(const char* s, int atom) + { + auto it = atomMap.insert(std::pair(s, atom)).first; + if (stringMap.size() < (size_t)atom + 1) + stringMap.resize(atom + 100, &badToken); + stringMap[atom] = &it->first; + } +}; + +class TInputScanner; + +enum MacroExpandResult { + MacroExpandNotStarted, // macro not expanded, which might not be an error + MacroExpandError, // a clear error occurred while expanding, no expansion + MacroExpandStarted, // macro expansion process has started + MacroExpandUndef // macro is undefined and will be expanded +}; + +// This class is the result of turning a huge pile of C code communicating through globals +// into a class. This was done to allowing instancing to attain thread safety. +// Don't expect too much in terms of OO design. +class TPpContext { +public: + TPpContext(TParseContextBase&, const std::string& rootFileName, TShader::Includer&); + virtual ~TPpContext(); + + void setPreamble(const char* preamble, size_t length); + + int tokenize(TPpToken& ppToken); + int tokenPaste(int token, TPpToken&); + + class tInput { + public: + tInput(TPpContext* p) : done(false), pp(p) { } + virtual ~tInput() { } + + virtual int scan(TPpToken*) = 0; + virtual int getch() = 0; + virtual void ungetch() = 0; + virtual bool peekPasting() { return false; } // true when about to see ## + virtual bool peekContinuedPasting(int) { return false; } // true when non-spaced tokens can paste + virtual bool endOfReplacementList() { return false; } // true when at the end of a macro replacement list (RHS of #define) + virtual bool isMacroInput() { return false; } + + // Will be called when we start reading tokens from this instance + virtual void notifyActivated() {} + // Will be called when we do not read tokens from this instance anymore + virtual void notifyDeleted() {} + protected: + bool done; + TPpContext* pp; + }; + + void setInput(TInputScanner& input, bool versionWillBeError); + + void pushInput(tInput* in) + { + inputStack.push_back(in); + in->notifyActivated(); + } + void popInput() + { + inputStack.back()->notifyDeleted(); + delete inputStack.back(); + inputStack.pop_back(); + } + + // + // From PpTokens.cpp + // + + // Capture the needed parts of a token stream for macro recording/playback. + class TokenStream { + public: + // Manage a stream of these 'Token', which capture the relevant parts + // of a TPpToken, plus its atom. + class Token { + public: + Token(int atom, const TPpToken& ppToken) : + atom(atom), + space(ppToken.space), + i64val(ppToken.i64val), + name(ppToken.name) { } + int get(TPpToken& ppToken) + { + ppToken.clear(); + ppToken.space = space; + ppToken.i64val = i64val; + snprintf(ppToken.name, sizeof(ppToken.name), "%s", name.c_str()); + return atom; + } + bool isAtom(int a) const { return atom == a; } + int getAtom() const { return atom; } + bool nonSpaced() const { return !space; } + protected: + Token() {} + int atom; + bool space; // did a space precede the token? + long long i64val; + TString name; + }; + + TokenStream() : currentPos(0) { } + + void putToken(int token, TPpToken* ppToken); + bool peekToken(int atom) { return !atEnd() && stream[currentPos].isAtom(atom); } + bool peekContinuedPasting(int atom) + { + // This is basically necessary because, for example, the PP + // tokenizer only accepts valid numeric-literals plus suffixes, so + // separates numeric-literals plus bad suffix into two tokens, which + // should get both pasted together as one token when token pasting. + // + // The following code is a bit more generalized than the above example. + if (!atEnd() && atom == PpAtomIdentifier && stream[currentPos].nonSpaced()) { + switch(stream[currentPos].getAtom()) { + case PpAtomConstInt: + case PpAtomConstUint: + case PpAtomConstInt64: + case PpAtomConstUint64: + case PpAtomConstInt16: + case PpAtomConstUint16: + case PpAtomConstFloat: + case PpAtomConstDouble: + case PpAtomConstFloat16: + case PpAtomConstString: + case PpAtomIdentifier: + return true; + default: + break; + } + } + + return false; + } + int getToken(TParseContextBase&, TPpToken*); + bool atEnd() { return currentPos >= stream.size(); } + bool peekTokenizedPasting(bool lastTokenPastes); + bool peekUntokenizedPasting(); + void reset() { currentPos = 0; } + + protected: + TVector stream; + size_t currentPos; + }; + + // + // From Pp.cpp + // + + struct MacroSymbol { + MacroSymbol() : functionLike(0), busy(0), undef(0) { } + TVector args; + TokenStream body; + unsigned functionLike : 1; // 0 means object-like, 1 means function-like + unsigned busy : 1; + unsigned undef : 1; + }; + + typedef TMap TSymbolMap; + TSymbolMap macroDefs; // map atoms to macro definitions + MacroSymbol* lookupMacroDef(int atom) + { + auto existingMacroIt = macroDefs.find(atom); + return (existingMacroIt == macroDefs.end()) ? nullptr : &(existingMacroIt->second); + } + void addMacroDef(int atom, MacroSymbol& macroDef) { macroDefs[atom] = macroDef; } + +protected: + TPpContext(TPpContext&); + TPpContext& operator=(TPpContext&); + + TStringAtomMap atomStrings; + char* preamble; // string to parse, all before line 1 of string 0, it is 0 if no preamble + int preambleLength; + char** strings; // official strings of shader, starting a string 0 line 1 + size_t* lengths; + int numStrings; // how many official strings there are + int currentString; // which string we're currently parsing (-1 for preamble) + + // Scanner data: + int previous_token; + TParseContextBase& parseContext; + + // Get the next token from *stack* of input sources, popping input sources + // that are out of tokens, down until an input source is found that has a token. + // Return EndOfInput when there are no more tokens to be found by doing this. + int scanToken(TPpToken* ppToken) + { + int token = EndOfInput; + + while (! inputStack.empty()) { + token = inputStack.back()->scan(ppToken); + if (token != EndOfInput || inputStack.empty()) + break; + popInput(); + } + + return token; + } + int getChar() { return inputStack.back()->getch(); } + void ungetChar() { inputStack.back()->ungetch(); } + bool peekPasting() { return !inputStack.empty() && inputStack.back()->peekPasting(); } + bool peekContinuedPasting(int a) + { + return !inputStack.empty() && inputStack.back()->peekContinuedPasting(a); + } + bool endOfReplacementList() { return inputStack.empty() || inputStack.back()->endOfReplacementList(); } + bool isMacroInput() { return inputStack.size() > 0 && inputStack.back()->isMacroInput(); } + + static const int maxIfNesting = 65; + + int ifdepth; // current #if-#else-#endif nesting in the cpp.c file (pre-processor) + bool elseSeen[maxIfNesting]; // Keep a track of whether an else has been seen at a particular depth + int elsetracker; // #if-#else and #endif constructs...Counter. + + class tMacroInput : public tInput { + public: + tMacroInput(TPpContext* pp) : tInput(pp), prepaste(false), postpaste(false) { } + virtual ~tMacroInput() + { + for (size_t i = 0; i < args.size(); ++i) + delete args[i]; + for (size_t i = 0; i < expandedArgs.size(); ++i) + delete expandedArgs[i]; + } + + virtual int scan(TPpToken*) override; + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + bool peekPasting() override { return prepaste; } + bool peekContinuedPasting(int a) override { return mac->body.peekContinuedPasting(a); } + bool endOfReplacementList() override { return mac->body.atEnd(); } + bool isMacroInput() override { return true; } + + MacroSymbol *mac; + TVector args; + TVector expandedArgs; + + protected: + bool prepaste; // true if we are just before ## + bool postpaste; // true if we are right after ## + }; + + class tMarkerInput : public tInput { + public: + tMarkerInput(TPpContext* pp) : tInput(pp) { } + virtual int scan(TPpToken*) override + { + if (done) + return EndOfInput; + done = true; + + return marker; + } + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + static const int marker = -3; + }; + + class tZeroInput : public tInput { + public: + tZeroInput(TPpContext* pp) : tInput(pp) { } + virtual int scan(TPpToken*) override; + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + }; + + std::vector inputStack; + bool errorOnVersion; + bool versionSeen; + + // + // from Pp.cpp + // + + // Used to obtain #include content. + TShader::Includer& includer; + + int CPPdefine(TPpToken * ppToken); + int CPPundef(TPpToken * ppToken); + int CPPelse(int matchelse, TPpToken * ppToken); + int extraTokenCheck(int atom, TPpToken* ppToken, int token); + int eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken * ppToken); + int evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken * ppToken); + int CPPif (TPpToken * ppToken); + int CPPifdef(int defined, TPpToken * ppToken); + int CPPinclude(TPpToken * ppToken); + int CPPline(TPpToken * ppToken); + int CPPerror(TPpToken * ppToken); + int CPPpragma(TPpToken * ppToken); + int CPPversion(TPpToken * ppToken); + int CPPextension(TPpToken * ppToken); + int readCPPline(TPpToken * ppToken); + int scanHeaderName(TPpToken* ppToken, char delimit); + TokenStream* PrescanMacroArg(TokenStream&, TPpToken*, bool newLineOkay); + MacroExpandResult MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay); + + // + // From PpTokens.cpp + // + void pushTokenStreamInput(TokenStream&, bool pasting = false); + void UngetToken(int token, TPpToken*); + + class tTokenInput : public tInput { + public: + tTokenInput(TPpContext* pp, TokenStream* t, bool prepasting) : + tInput(pp), + tokens(t), + lastTokenPastes(prepasting) { } + virtual int scan(TPpToken *ppToken) override { return tokens->getToken(pp->parseContext, ppToken); } + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + virtual bool peekPasting() override { return tokens->peekTokenizedPasting(lastTokenPastes); } + bool peekContinuedPasting(int a) override { return tokens->peekContinuedPasting(a); } + protected: + TokenStream* tokens; + bool lastTokenPastes; // true if the last token in the input is to be pasted, rather than consumed as a token + }; + + class tUngotTokenInput : public tInput { + public: + tUngotTokenInput(TPpContext* pp, int t, TPpToken* p) : tInput(pp), token(t), lval(*p) { } + virtual int scan(TPpToken *) override; + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + protected: + int token; + TPpToken lval; + }; + + // + // From PpScanner.cpp + // + class tStringInput : public tInput { + public: + tStringInput(TPpContext* pp, TInputScanner& i) : tInput(pp), input(&i) { } + virtual int scan(TPpToken*) override; + + // Scanner used to get source stream characters. + // - Escaped newlines are handled here, invisibly to the caller. + // - All forms of newline are handled, and turned into just a '\n'. + int getch() override + { + int ch = input->get(); + + if (ch == '\\') { + // Move past escaped newlines, as many as sequentially exist + do { + if (input->peek() == '\r' || input->peek() == '\n') { + bool allowed = pp->parseContext.lineContinuationCheck(input->getSourceLoc(), pp->inComment); + if (! allowed && pp->inComment) + return '\\'; + + // escape one newline now + ch = input->get(); + int nextch = input->get(); + if (ch == '\r' && nextch == '\n') + ch = input->get(); + else + ch = nextch; + } else + return '\\'; + } while (ch == '\\'); + } + + // handle any non-escaped newline + if (ch == '\r' || ch == '\n') { + if (ch == '\r' && input->peek() == '\n') + input->get(); + return '\n'; + } + + return ch; + } + + // Scanner used to backup the source stream characters. Newlines are + // handled here, invisibly to the caller, meaning have to undo exactly + // what getch() above does (e.g., don't leave things in the middle of a + // sequence of escaped newlines). + void ungetch() override + { + input->unget(); + + do { + int ch = input->peek(); + if (ch == '\r' || ch == '\n') { + if (ch == '\n') { + // correct for two-character newline + input->unget(); + if (input->peek() != '\r') + input->get(); + } + // now in front of a complete newline, move past an escape character + input->unget(); + if (input->peek() == '\\') + input->unget(); + else { + input->get(); + break; + } + } else + break; + } while (true); + } + + protected: + TInputScanner* input; + }; + + // Holds a reference to included file data, as well as a + // prologue and an epilogue string. This can be scanned using the tInput + // interface and acts as a single source string. + class TokenizableIncludeFile : public tInput { + public: + // Copies prologue and epilogue. The includedFile must remain valid + // until this TokenizableIncludeFile is no longer used. + TokenizableIncludeFile(const TSourceLoc& startLoc, + const std::string& prologue, + TShader::Includer::IncludeResult* includedFile, + const std::string& epilogue, + TPpContext* pp) + : tInput(pp), + prologue_(prologue), + epilogue_(epilogue), + includedFile_(includedFile), + scanner(3, strings, lengths, nullptr, 0, 0, true), + prevScanner(nullptr), + stringInput(pp, scanner) + { + strings[0] = prologue_.data(); + strings[1] = includedFile_->headerData; + strings[2] = epilogue_.data(); + + lengths[0] = prologue_.size(); + lengths[1] = includedFile_->headerLength; + lengths[2] = epilogue_.size(); + + scanner.setLine(startLoc.line); + scanner.setString(startLoc.string); + + scanner.setFile(startLoc.getFilenameStr(), 0); + scanner.setFile(startLoc.getFilenameStr(), 1); + scanner.setFile(startLoc.getFilenameStr(), 2); + } + + // tInput methods: + int scan(TPpToken* t) override { return stringInput.scan(t); } + int getch() override { return stringInput.getch(); } + void ungetch() override { stringInput.ungetch(); } + + void notifyActivated() override + { + prevScanner = pp->parseContext.getScanner(); + pp->parseContext.setScanner(&scanner); + pp->push_include(includedFile_); + } + + void notifyDeleted() override + { + pp->parseContext.setScanner(prevScanner); + pp->pop_include(); + } + + private: + TokenizableIncludeFile& operator=(const TokenizableIncludeFile&); + + // Stores the prologue for this string. + const std::string prologue_; + + // Stores the epilogue for this string. + const std::string epilogue_; + + // Points to the IncludeResult that this TokenizableIncludeFile represents. + TShader::Includer::IncludeResult* includedFile_; + + // Will point to prologue_, includedFile_->headerData and epilogue_ + // This is passed to scanner constructor. + // These do not own the storage and it must remain valid until this + // object has been destroyed. + const char* strings[3]; + // Length of str_, passed to scanner constructor. + size_t lengths[3]; + // Scans over str_. + TInputScanner scanner; + // The previous effective scanner before the scanner in this instance + // has been activated. + TInputScanner* prevScanner; + // Delegate object implementing the tInput interface. + tStringInput stringInput; + }; + + int ScanFromString(char* s); + void missingEndifCheck(); + int lFloatConst(int len, int ch, TPpToken* ppToken); + int characterLiteral(TPpToken* ppToken); + + void push_include(TShader::Includer::IncludeResult* result) + { + currentSourceFile = result->headerName; + includeStack.push(result); + } + + void pop_include() + { + TShader::Includer::IncludeResult* include = includeStack.top(); + includeStack.pop(); + includer.releaseInclude(include); + if (includeStack.empty()) { + currentSourceFile = rootFileName; + } else { + currentSourceFile = includeStack.top()->headerName; + } + } + + bool inComment; + std::string rootFileName; + std::stack includeStack; + std::string currentSourceFile; + + std::istringstream strtodStream; + bool disableEscapeSequences; +}; + +} // end namespace glslang + +#endif // PPCONTEXT_H diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp new file mode 100644 index 000000000..e0f44f8b4 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp @@ -0,0 +1,1315 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include + +#include "PpContext.h" +#include "PpTokens.h" +#include "../Scan.h" + +namespace glslang { + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Floating point constants: ///////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +// +// Scan a single- or double-precision floating point constant. +// Assumes that the scanner has seen at least one digit, +// followed by either a decimal '.' or the letter 'e', or a +// precision ending (e.g., F or LF). +// +// This is technically not correct, as the preprocessor should just +// accept the numeric literal along with whatever suffix it has, but +// currently, it stops on seeing a bad suffix, treating that as the +// next token. This effects things like token pasting, where it is +// relevant how many tokens something was broken into. +// +// See peekContinuedPasting(). +int TPpContext::lFloatConst(int len, int ch, TPpToken* ppToken) +{ + const auto saveName = [&](int ch) { + if (len <= MaxTokenLength) + ppToken->name[len++] = static_cast(ch); + }; + + // find the range of non-zero digits before the decimal point + int startNonZero = 0; + while (startNonZero < len && ppToken->name[startNonZero] == '0') + ++startNonZero; + int endNonZero = len; + while (endNonZero > startNonZero && ppToken->name[endNonZero-1] == '0') + --endNonZero; + int numWholeNumberDigits = endNonZero - startNonZero; + + // accumulate the range's value + bool fastPath = numWholeNumberDigits <= 15; // when the number gets too complex, set to false + unsigned long long wholeNumber = 0; + if (fastPath) { + for (int i = startNonZero; i < endNonZero; ++i) + wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0'); + } + int decimalShift = len - endNonZero; + + // Decimal point: + bool hasDecimalOrExponent = false; + if (ch == '.') { + hasDecimalOrExponent = true; + saveName(ch); + ch = getChar(); + int firstDecimal = len; + +#ifdef ENABLE_HLSL + // 1.#INF or -1.#INF + if (ch == '#' && (ifdepth > 0 || parseContext.intermediate.getSource() == EShSourceHlsl)) { + if ((len < 2) || + (len == 2 && ppToken->name[0] != '1') || + (len == 3 && ppToken->name[1] != '1' && !(ppToken->name[0] == '-' || ppToken->name[0] == '+')) || + (len > 3)) + parseContext.ppError(ppToken->loc, "unexpected use of", "#", ""); + else { + // we have 1.# or -1.# or +1.#, check for 'INF' + if ((ch = getChar()) != 'I' || + (ch = getChar()) != 'N' || + (ch = getChar()) != 'F') + parseContext.ppError(ppToken->loc, "expected 'INF'", "#", ""); + else { + // we have [+-].#INF, and we are targeting IEEE 754, so wrap it up: + saveName('I'); + saveName('N'); + saveName('F'); + ppToken->name[len] = '\0'; + if (ppToken->name[0] == '-') + ppToken->i64val = 0xfff0000000000000; // -Infinity + else + ppToken->i64val = 0x7ff0000000000000; // +Infinity + return PpAtomConstFloat; + } + } + } +#endif + + // Consume leading-zero digits after the decimal point + while (ch == '0') { + saveName(ch); + ch = getChar(); + } + int startNonZeroDecimal = len; + int endNonZeroDecimal = len; + + // Consume remaining digits, up to the exponent + while (ch >= '0' && ch <= '9') { + saveName(ch); + if (ch != '0') + endNonZeroDecimal = len; + ch = getChar(); + } + + // Compute accumulation up to the last non-zero digit + if (endNonZeroDecimal > startNonZeroDecimal) { + numWholeNumberDigits += endNonZeroDecimal - endNonZero - 1; // don't include the "." + if (numWholeNumberDigits > 15) + fastPath = false; + if (fastPath) { + for (int i = endNonZero; i < endNonZeroDecimal; ++i) { + if (ppToken->name[i] != '.') + wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0'); + } + } + decimalShift = firstDecimal - endNonZeroDecimal; + } + } + + // Exponent: + bool negativeExponent = false; + double exponentValue = 0.0; + int exponent = 0; + { + if (ch == 'e' || ch == 'E') { + hasDecimalOrExponent = true; + saveName(ch); + ch = getChar(); + if (ch == '+' || ch == '-') { + negativeExponent = ch == '-'; + saveName(ch); + ch = getChar(); + } + if (ch >= '0' && ch <= '9') { + while (ch >= '0' && ch <= '9') { + exponent = exponent * 10 + (ch - '0'); + saveName(ch); + ch = getChar(); + } + } else { + parseContext.ppError(ppToken->loc, "bad character in float exponent", "", ""); + } + } + + // Compensate for location of decimal + if (negativeExponent) + exponent -= decimalShift; + else { + exponent += decimalShift; + if (exponent < 0) { + negativeExponent = true; + exponent = -exponent; + } + } + if (exponent > 22) + fastPath = false; + + if (fastPath) { + // Compute the floating-point value of the exponent + exponentValue = 1.0; + if (exponent > 0) { + double expFactor = 10; + while (exponent > 0) { + if (exponent & 0x1) + exponentValue *= expFactor; + expFactor *= expFactor; + exponent >>= 1; + } + } + } + } + + // Suffix: + bool isDouble = false; + bool isFloat16 = false; +#ifndef GLSLANG_WEB + if (ch == 'l' || ch == 'L') { + if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl) + parseContext.doubleCheck(ppToken->loc, "double floating-point suffix"); + if (ifdepth == 0 && !hasDecimalOrExponent) + parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); + if (parseContext.intermediate.getSource() == EShSourceGlsl) { + int ch2 = getChar(); + if (ch2 != 'f' && ch2 != 'F') { + ungetChar(); + ungetChar(); + } else { + saveName(ch); + saveName(ch2); + isDouble = true; + } + } else if (parseContext.intermediate.getSource() == EShSourceHlsl) { + saveName(ch); + isDouble = true; + } + } else if (ch == 'h' || ch == 'H') { + if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl) + parseContext.float16Check(ppToken->loc, "half floating-point suffix"); + if (ifdepth == 0 && !hasDecimalOrExponent) + parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); + if (parseContext.intermediate.getSource() == EShSourceGlsl) { + int ch2 = getChar(); + if (ch2 != 'f' && ch2 != 'F') { + ungetChar(); + ungetChar(); + } else { + saveName(ch); + saveName(ch2); + isFloat16 = true; + } + } else if (parseContext.intermediate.getSource() == EShSourceHlsl) { + saveName(ch); + isFloat16 = true; + } + } else +#endif + if (ch == 'f' || ch == 'F') { +#ifndef GLSLANG_WEB + if (ifdepth == 0) + parseContext.profileRequires(ppToken->loc, EEsProfile, 300, nullptr, "floating-point suffix"); + if (ifdepth == 0 && !parseContext.relaxedErrors()) + parseContext.profileRequires(ppToken->loc, ~EEsProfile, 120, nullptr, "floating-point suffix"); +#endif + if (ifdepth == 0 && !hasDecimalOrExponent) + parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); + saveName(ch); + } else + ungetChar(); + + // Patch up the name and length for overflow + + if (len > MaxTokenLength) { + len = MaxTokenLength; + parseContext.ppError(ppToken->loc, "float literal too long", "", ""); + } + ppToken->name[len] = '\0'; + + // Compute the numerical value + if (fastPath) { + // compute the floating-point value of the exponent + if (exponentValue == 0.0) + ppToken->dval = (double)wholeNumber; + else if (negativeExponent) + ppToken->dval = (double)wholeNumber / exponentValue; + else + ppToken->dval = (double)wholeNumber * exponentValue; + } else { + // slow path + ppToken->dval = 0.0; + + // remove suffix + TString numstr(ppToken->name); + if (numstr.back() == 'f' || numstr.back() == 'F') + numstr.pop_back(); + if (numstr.back() == 'h' || numstr.back() == 'H') + numstr.pop_back(); + if (numstr.back() == 'l' || numstr.back() == 'L') + numstr.pop_back(); + + // use platform library + strtodStream.clear(); + strtodStream.str(numstr.c_str()); + strtodStream >> ppToken->dval; + if (strtodStream.fail()) { + // Assume failure combined with a large exponent was overflow, in + // an attempt to set INF. + if (!negativeExponent && exponent + numWholeNumberDigits > 300) + ppToken->i64val = 0x7ff0000000000000; // +Infinity + // Assume failure combined with a small exponent was overflow. + if (negativeExponent && exponent + numWholeNumberDigits > 300) + ppToken->dval = 0.0; + // Unknown reason for failure. Theory is that either + // - the 0.0 is still there, or + // - something reasonable was written that is better than 0.0 + } + } + + // Return the right token type + if (isDouble) + return PpAtomConstDouble; + else if (isFloat16) + return PpAtomConstFloat16; + else + return PpAtomConstFloat; +} + +// Recognize a character literal. +// +// The first ' has already been accepted, read the rest, through the closing '. +// +// Always returns PpAtomConstInt. +// +int TPpContext::characterLiteral(TPpToken* ppToken) +{ + ppToken->name[0] = 0; + ppToken->ival = 0; + + if (parseContext.intermediate.getSource() != EShSourceHlsl) { + // illegal, except in macro definition, for which case we report the character + return '\''; + } + + int ch = getChar(); + switch (ch) { + case '\'': + // As empty sequence: '' + parseContext.ppError(ppToken->loc, "unexpected", "\'", ""); + return PpAtomConstInt; + case '\\': + // As escape sequence: '\XXX' + switch (ch = getChar()) { + case 'a': + ppToken->ival = 7; + break; + case 'b': + ppToken->ival = 8; + break; + case 't': + ppToken->ival = 9; + break; + case 'n': + ppToken->ival = 10; + break; + case 'v': + ppToken->ival = 11; + break; + case 'f': + ppToken->ival = 12; + break; + case 'r': + ppToken->ival = 13; + break; + case 'x': + case '0': + parseContext.ppError(ppToken->loc, "octal and hex sequences not supported", "\\", ""); + break; + default: + // This catches '\'', '\"', '\?', etc. + // Also, things like '\C' mean the same thing as 'C' + // (after the above cases are filtered out). + ppToken->ival = ch; + break; + } + break; + default: + ppToken->ival = ch; + break; + } + ppToken->name[0] = (char)ppToken->ival; + ppToken->name[1] = '\0'; + ch = getChar(); + if (ch != '\'') { + parseContext.ppError(ppToken->loc, "expected", "\'", ""); + // Look ahead for a closing ' + do { + ch = getChar(); + } while (ch != '\'' && ch != EndOfInput && ch != '\n'); + } + + return PpAtomConstInt; +} + +// +// Scanner used to tokenize source stream. +// +// N.B. Invalid numeric suffixes are not consumed.// +// This is technically not correct, as the preprocessor should just +// accept the numeric literal along with whatever suffix it has, but +// currently, it stops on seeing a bad suffix, treating that as the +// next token. This effects things like token pasting, where it is +// relevant how many tokens something was broken into. +// See peekContinuedPasting(). +// +int TPpContext::tStringInput::scan(TPpToken* ppToken) +{ + int AlreadyComplained = 0; + int len = 0; + int ch = 0; + int ii = 0; + unsigned long long ival = 0; + const auto floatingPointChar = [&](int ch) { return ch == '.' || ch == 'e' || ch == 'E' || + ch == 'f' || ch == 'F' || + ch == 'h' || ch == 'H'; }; + + static const char* const Int64_Extensions[] = { + E_GL_ARB_gpu_shader_int64, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int64 }; + static const int Num_Int64_Extensions = sizeof(Int64_Extensions) / sizeof(Int64_Extensions[0]); + + static const char* const Int16_Extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16 }; + static const int Num_Int16_Extensions = sizeof(Int16_Extensions) / sizeof(Int16_Extensions[0]); + + ppToken->ival = 0; + ppToken->i64val = 0; + ppToken->space = false; + ch = getch(); + for (;;) { + while (ch == ' ' || ch == '\t') { + ppToken->space = true; + ch = getch(); + } + + ppToken->loc = pp->parseContext.getCurrentLoc(); + len = 0; + switch (ch) { + default: + // Single character token, including EndOfInput, '#' and '\' (escaped newlines are handled at a lower level, so this is just a '\' token) + if (ch > PpAtomMaxSingle) + ch = PpAtomBadToken; + return ch; + + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': + case 'Z': case '_': + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': + case 'z': + do { + if (len < MaxTokenLength) { + ppToken->name[len++] = (char)ch; + ch = getch(); + } else { + if (! AlreadyComplained) { + pp->parseContext.ppError(ppToken->loc, "name too long", "", ""); + AlreadyComplained = 1; + } + ch = getch(); + } + } while ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + ch == '_'); + + // line continuation with no token before or after makes len == 0, and need to start over skipping white space, etc. + if (len == 0) + continue; + + ppToken->name[len] = '\0'; + ungetch(); + return PpAtomIdentifier; + case '0': + ppToken->name[len++] = (char)ch; + ch = getch(); + if (ch == 'x' || ch == 'X') { + // must be hexadecimal + + bool isUnsigned = false; + bool isInt64 = false; + bool isInt16 = false; + ppToken->name[len++] = (char)ch; + ch = getch(); + if ((ch >= '0' && ch <= '9') || + (ch >= 'A' && ch <= 'F') || + (ch >= 'a' && ch <= 'f')) { + + ival = 0; + do { + if (len < MaxTokenLength && ival <= 0x0fffffffffffffffull) { + ppToken->name[len++] = (char)ch; + if (ch >= '0' && ch <= '9') { + ii = ch - '0'; + } else if (ch >= 'A' && ch <= 'F') { + ii = ch - 'A' + 10; + } else if (ch >= 'a' && ch <= 'f') { + ii = ch - 'a' + 10; + } else + pp->parseContext.ppError(ppToken->loc, "bad digit in hexadecimal literal", "", ""); + ival = (ival << 4) | ii; + } else { + if (! AlreadyComplained) { + if(len < MaxTokenLength) + pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too big", "", ""); + else + pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too long", "", ""); + AlreadyComplained = 1; + } + ival = 0xffffffffffffffffull; + } + ch = getch(); + } while ((ch >= '0' && ch <= '9') || + (ch >= 'A' && ch <= 'F') || + (ch >= 'a' && ch <= 'f')); + } else { + pp->parseContext.ppError(ppToken->loc, "bad digit in hexadecimal literal", "", ""); + } + if (ch == 'u' || ch == 'U') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isUnsigned = true; + +#ifndef GLSLANG_WEB + int nextCh = getch(); + if (nextCh == 'l' || nextCh == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt64 = true; + } else + ungetch(); + + nextCh = getch(); + if ((nextCh == 's' || nextCh == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt16 = true; + } else + ungetch(); + } else if (ch == 'l' || ch == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt64 = true; + } else if ((ch == 's' || ch == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt16 = true; +#endif + } else + ungetch(); + ppToken->name[len] = '\0'; + + if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (pp->ifdepth == 0) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "64-bit hexadecimal literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int64_Extensions, Int64_Extensions, "64-bit hexadecimal literal"); + } + ppToken->i64val = ival; + return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64; + } else if (isInt16) { + if (pp->ifdepth == 0) { + if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "16-bit hexadecimal literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int16_Extensions, Int16_Extensions, "16-bit hexadecimal literal"); + } + } + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16; + } else { + if (ival > 0xffffffffu && !AlreadyComplained) + pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too big", "", ""); + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint : PpAtomConstInt; + } + } else { + // could be octal integer or floating point, speculative pursue octal until it must be floating point + + bool isUnsigned = false; + bool isInt64 = false; + bool isInt16 = false; + bool octalOverflow = false; + bool nonOctal = false; + ival = 0; + + // see how much octal-like stuff we can read + while (ch >= '0' && ch <= '7') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + else if (! AlreadyComplained) { + pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", ""); + AlreadyComplained = 1; + } + if (ival <= 0x1fffffffffffffffull) { + ii = ch - '0'; + ival = (ival << 3) | ii; + } else + octalOverflow = true; + ch = getch(); + } + + // could be part of a float... + if (ch == '8' || ch == '9') { + nonOctal = true; + do { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + else if (! AlreadyComplained) { + pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", ""); + AlreadyComplained = 1; + } + ch = getch(); + } while (ch >= '0' && ch <= '9'); + } + if (floatingPointChar(ch)) + return pp->lFloatConst(len, ch, ppToken); + + // wasn't a float, so must be octal... + if (nonOctal) + pp->parseContext.ppError(ppToken->loc, "octal literal digit too large", "", ""); + + if (ch == 'u' || ch == 'U') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isUnsigned = true; + +#ifndef GLSLANG_WEB + int nextCh = getch(); + if (nextCh == 'l' || nextCh == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt64 = true; + } else + ungetch(); + + nextCh = getch(); + if ((nextCh == 's' || nextCh == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt16 = true; + } else + ungetch(); + } else if (ch == 'l' || ch == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt64 = true; + } else if ((ch == 's' || ch == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt16 = true; +#endif + } else + ungetch(); + ppToken->name[len] = '\0'; + + if (!isInt64 && ival > 0xffffffffu) + octalOverflow = true; + + if (octalOverflow) + pp->parseContext.ppError(ppToken->loc, "octal literal too big", "", ""); + + if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (pp->ifdepth == 0) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "64-bit octal literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int64_Extensions, Int64_Extensions, "64-bit octal literal"); + } + ppToken->i64val = ival; + return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64; + } else if (isInt16) { + if (pp->ifdepth == 0) { + if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "16-bit octal literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int16_Extensions, Int16_Extensions, "16-bit octal literal"); + } + } + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16; + } else { + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint : PpAtomConstInt; + } + } + break; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + // can't be hexadecimal or octal, is either decimal or floating point + + do { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + else if (! AlreadyComplained) { + pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", ""); + AlreadyComplained = 1; + } + ch = getch(); + } while (ch >= '0' && ch <= '9'); + if (floatingPointChar(ch)) + return pp->lFloatConst(len, ch, ppToken); + else { + // Finish handling signed and unsigned integers + int numericLen = len; + bool isUnsigned = false; + bool isInt64 = false; + bool isInt16 = false; + if (ch == 'u' || ch == 'U') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isUnsigned = true; + +#ifndef GLSLANG_WEB + int nextCh = getch(); + if (nextCh == 'l' || nextCh == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt64 = true; + } else + ungetch(); + + nextCh = getch(); + if ((nextCh == 's' || nextCh == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt16 = true; + } else + ungetch(); + } else if (ch == 'l' || ch == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt64 = true; + } else if ((ch == 's' || ch == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt16 = true; +#endif + } else + ungetch(); + + ppToken->name[len] = '\0'; + ival = 0; + const unsigned oneTenthMaxInt = 0xFFFFFFFFu / 10; + const unsigned remainderMaxInt = 0xFFFFFFFFu - 10 * oneTenthMaxInt; + const unsigned long long oneTenthMaxInt64 = 0xFFFFFFFFFFFFFFFFull / 10; + const unsigned long long remainderMaxInt64 = 0xFFFFFFFFFFFFFFFFull - 10 * oneTenthMaxInt64; + const unsigned short oneTenthMaxInt16 = 0xFFFFu / 10; + const unsigned short remainderMaxInt16 = 0xFFFFu - 10 * oneTenthMaxInt16; + for (int i = 0; i < numericLen; i++) { + ch = ppToken->name[i] - '0'; + bool overflow = false; + if (isInt64) + overflow = (ival > oneTenthMaxInt64 || (ival == oneTenthMaxInt64 && (unsigned long long)ch > remainderMaxInt64)); + else if (isInt16) + overflow = (ival > oneTenthMaxInt16 || (ival == oneTenthMaxInt16 && (unsigned short)ch > remainderMaxInt16)); + else + overflow = (ival > oneTenthMaxInt || (ival == oneTenthMaxInt && (unsigned)ch > remainderMaxInt)); + if (overflow) { + pp->parseContext.ppError(ppToken->loc, "numeric literal too big", "", ""); + ival = 0xFFFFFFFFFFFFFFFFull; + break; + } else + ival = ival * 10 + ch; + } + + if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (pp->ifdepth == 0) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "64-bit literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int64_Extensions, Int64_Extensions, "64-bit literal"); + } + ppToken->i64val = ival; + return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64; + } else if (isInt16) { + if (pp->ifdepth == 0 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "16-bit literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int16_Extensions, Int16_Extensions, "16-bit literal"); + } + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16; + } else { + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint : PpAtomConstInt; + } + } + break; + case '-': + ch = getch(); + if (ch == '-') { + return PpAtomDecrement; + } else if (ch == '=') { + return PPAtomSubAssign; + } else { + ungetch(); + return '-'; + } + case '+': + ch = getch(); + if (ch == '+') { + return PpAtomIncrement; + } else if (ch == '=') { + return PPAtomAddAssign; + } else { + ungetch(); + return '+'; + } + case '*': + ch = getch(); + if (ch == '=') { + return PPAtomMulAssign; + } else { + ungetch(); + return '*'; + } + case '%': + ch = getch(); + if (ch == '=') { + return PPAtomModAssign; + } else { + ungetch(); + return '%'; + } + case '^': + ch = getch(); + if (ch == '^') { + return PpAtomXor; + } else { + if (ch == '=') + return PpAtomXorAssign; + else{ + ungetch(); + return '^'; + } + } + + case '=': + ch = getch(); + if (ch == '=') { + return PpAtomEQ; + } else { + ungetch(); + return '='; + } + case '!': + ch = getch(); + if (ch == '=') { + return PpAtomNE; + } else { + ungetch(); + return '!'; + } + case '|': + ch = getch(); + if (ch == '|') { + return PpAtomOr; + } else if (ch == '=') { + return PpAtomOrAssign; + } else { + ungetch(); + return '|'; + } + case '&': + ch = getch(); + if (ch == '&') { + return PpAtomAnd; + } else if (ch == '=') { + return PpAtomAndAssign; + } else { + ungetch(); + return '&'; + } + case '<': + ch = getch(); + if (ch == '<') { + ch = getch(); + if (ch == '=') + return PpAtomLeftAssign; + else { + ungetch(); + return PpAtomLeft; + } + } else if (ch == '=') { + return PpAtomLE; + } else { + ungetch(); + return '<'; + } + case '>': + ch = getch(); + if (ch == '>') { + ch = getch(); + if (ch == '=') + return PpAtomRightAssign; + else { + ungetch(); + return PpAtomRight; + } + } else if (ch == '=') { + return PpAtomGE; + } else { + ungetch(); + return '>'; + } + case '.': + ch = getch(); + if (ch >= '0' && ch <= '9') { + ungetch(); + return pp->lFloatConst(0, '.', ppToken); + } else { + ungetch(); + return '.'; + } + case '/': + ch = getch(); + if (ch == '/') { + pp->inComment = true; + do { + ch = getch(); + } while (ch != '\n' && ch != EndOfInput); + ppToken->space = true; + pp->inComment = false; + + return ch; + } else if (ch == '*') { + ch = getch(); + do { + while (ch != '*') { + if (ch == EndOfInput) { + pp->parseContext.ppError(ppToken->loc, "End of input in comment", "comment", ""); + return ch; + } + ch = getch(); + } + ch = getch(); + if (ch == EndOfInput) { + pp->parseContext.ppError(ppToken->loc, "End of input in comment", "comment", ""); + return ch; + } + } while (ch != '/'); + ppToken->space = true; + // loop again to get the next token... + break; + } else if (ch == '=') { + return PPAtomDivAssign; + } else { + ungetch(); + return '/'; + } + break; + case '\'': + return pp->characterLiteral(ppToken); + case '"': + // #include uses scanHeaderName() to ignore these escape sequences. + ch = getch(); + while (ch != '"' && ch != '\n' && ch != EndOfInput) { + if (len < MaxTokenLength) { + if (ch == '\\' && !pp->disableEscapeSequences) { + int nextCh = getch(); + switch (nextCh) { + case '\'': ch = 0x27; break; + case '"': ch = 0x22; break; + case '?': ch = 0x3f; break; + case '\\': ch = 0x5c; break; + case 'a': ch = 0x07; break; + case 'b': ch = 0x08; break; + case 'f': ch = 0x0c; break; + case 'n': ch = 0x0a; break; + case 'r': ch = 0x0d; break; + case 't': ch = 0x09; break; + case 'v': ch = 0x0b; break; + case 'x': + // Hex value, arbitrary number of characters. Terminated by the first + // non-hex digit + { + int numDigits = 0; + ch = 0; + while (true) { + nextCh = getch(); + if (nextCh >= '0' && nextCh <= '9') + nextCh -= '0'; + else if (nextCh >= 'A' && nextCh <= 'F') + nextCh -= 'A' - 10; + else if (nextCh >= 'a' && nextCh <= 'f') + nextCh -= 'a' - 10; + else { + ungetch(); + break; + } + numDigits++; + ch = ch * 0x10 + nextCh; + } + if (numDigits == 0) { + pp->parseContext.ppError(ppToken->loc, "Expected hex value in escape sequence", "string", ""); + } + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + // Octal value, up to three octal digits + { + int numDigits = 1; + ch = nextCh - '0'; + while (numDigits < 3) { + nextCh = getch(); + if (nextCh >= '0' && nextCh <= '7') + nextCh -= '0'; + else { + ungetch(); + break; + } + numDigits++; + ch = ch * 8 + nextCh; + } + break; + } + default: + pp->parseContext.ppError(ppToken->loc, "Invalid escape sequence", "string", ""); + break; + } + } + ppToken->name[len] = (char)ch; + len++; + ch = getch(); + } else + break; + }; + ppToken->name[len] = '\0'; + if (ch != '"') { + ungetch(); + pp->parseContext.ppError(ppToken->loc, "End of line in string", "string", ""); + } + return PpAtomConstString; + case ':': + ch = getch(); + if (ch == ':') + return PpAtomColonColon; + ungetch(); + return ':'; + } + + ch = getch(); + } +} + +// +// The main functional entry point into the preprocessor, which will +// scan the source strings to figure out and return the next processing token. +// +// Return the token, or EndOfInput when no more tokens. +// +int TPpContext::tokenize(TPpToken& ppToken) +{ + for(;;) { + int token = scanToken(&ppToken); + + // Handle token-pasting logic + token = tokenPaste(token, ppToken); + + if (token == EndOfInput) { + missingEndifCheck(); + return EndOfInput; + } + if (token == '#') { + if (previous_token == '\n') { + token = readCPPline(&ppToken); + if (token == EndOfInput) { + missingEndifCheck(); + return EndOfInput; + } + continue; + } else { + parseContext.ppError(ppToken.loc, "preprocessor directive cannot be preceded by another token", "#", ""); + return EndOfInput; + } + } + previous_token = token; + + if (token == '\n') + continue; + + // expand macros + if (token == PpAtomIdentifier) { + switch (MacroExpand(&ppToken, false, true)) { + case MacroExpandNotStarted: + break; + case MacroExpandError: + return EndOfInput; + case MacroExpandStarted: + case MacroExpandUndef: + continue; + } + } + + switch (token) { + case PpAtomIdentifier: + case PpAtomConstInt: + case PpAtomConstUint: + case PpAtomConstFloat: + case PpAtomConstInt64: + case PpAtomConstUint64: + case PpAtomConstInt16: + case PpAtomConstUint16: + case PpAtomConstDouble: + case PpAtomConstFloat16: + if (ppToken.name[0] == '\0') + continue; + break; + case PpAtomConstString: + // HLSL allows string literals. + // GLSL allows string literals with GL_EXT_debug_printf. + if (ifdepth == 0 && parseContext.intermediate.getSource() != EShSourceHlsl) { + parseContext.requireExtensions(ppToken.loc, 1, &E_GL_EXT_debug_printf, "string literal"); + if (!parseContext.extensionTurnedOn(E_GL_EXT_debug_printf)) + continue; + } + break; + case '\'': + parseContext.ppError(ppToken.loc, "character literals not supported", "\'", ""); + continue; + default: + snprintf(ppToken.name, sizeof(ppToken.name), "%s", atomStrings.getString(token)); + break; + } + + return token; + } +} + +// +// Do all token-pasting related combining of two pasted tokens when getting a +// stream of tokens from a replacement list. Degenerates to no processing if a +// replacement list is not the source of the token stream. +// +int TPpContext::tokenPaste(int token, TPpToken& ppToken) +{ + // starting with ## is illegal, skip to next token + if (token == PpAtomPaste) { + parseContext.ppError(ppToken.loc, "unexpected location", "##", ""); + return scanToken(&ppToken); + } + + int resultToken = token; // "foo" pasted with "35" is an identifier, not a number + + // ## can be chained, process all in the chain at once + while (peekPasting()) { + TPpToken pastedPpToken; + + // next token has to be ## + token = scanToken(&pastedPpToken); + assert(token == PpAtomPaste); + + // This covers end of macro expansion + if (endOfReplacementList()) { + parseContext.ppError(ppToken.loc, "unexpected location; end of replacement list", "##", ""); + break; + } + + // Get the token(s) after the ##. + // Because of "space" semantics, and prior tokenization, what + // appeared a single token, e.g. "3A", might have been tokenized + // into two tokens "3" and "A", but the "A" will have 'space' set to + // false. Accumulate all of these to recreate the original lexical + // appearing token. + do { + token = scanToken(&pastedPpToken); + + // This covers end of argument expansion + if (token == tMarkerInput::marker) { + parseContext.ppError(ppToken.loc, "unexpected location; end of argument", "##", ""); + return resultToken; + } + + // get the token text + switch (resultToken) { + case PpAtomIdentifier: + // already have the correct text in token.names + break; + case '=': + case '!': + case '-': + case '~': + case '+': + case '*': + case '/': + case '%': + case '<': + case '>': + case '|': + case '^': + case '&': + case PpAtomRight: + case PpAtomLeft: + case PpAtomAnd: + case PpAtomOr: + case PpAtomXor: + snprintf(ppToken.name, sizeof(ppToken.name), "%s", atomStrings.getString(resultToken)); + snprintf(pastedPpToken.name, sizeof(pastedPpToken.name), "%s", atomStrings.getString(token)); + break; + default: + parseContext.ppError(ppToken.loc, "not supported for these tokens", "##", ""); + return resultToken; + } + + // combine the tokens + if (strlen(ppToken.name) + strlen(pastedPpToken.name) > MaxTokenLength) { + parseContext.ppError(ppToken.loc, "combined tokens are too long", "##", ""); + return resultToken; + } + snprintf(&ppToken.name[0] + strlen(ppToken.name), sizeof(ppToken.name) - strlen(ppToken.name), + "%s", pastedPpToken.name); + + // correct the kind of token we are making, if needed (identifiers stay identifiers) + if (resultToken != PpAtomIdentifier) { + int newToken = atomStrings.getAtom(ppToken.name); + if (newToken > 0) + resultToken = newToken; + else + parseContext.ppError(ppToken.loc, "combined token is invalid", "##", ""); + } + } while (peekContinuedPasting(resultToken)); + } + + return resultToken; +} + +// Checks if we've seen balanced #if...#endif +void TPpContext::missingEndifCheck() +{ + if (ifdepth > 0) + parseContext.ppError(parseContext.getCurrentLoc(), "missing #endif", "", ""); +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp new file mode 100755 index 000000000..7ed58703f --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp @@ -0,0 +1,221 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +// +// For recording and playing back the stream of tokens in a macro definition. +// + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/) +#define snprintf sprintf_s +#endif + +#include +#include +#include +#include + +#include "PpContext.h" +#include "PpTokens.h" + +namespace glslang { + +// Add a token (including backing string) to the end of a macro +// token stream, for later playback. +void TPpContext::TokenStream::putToken(int atom, TPpToken* ppToken) +{ + TokenStream::Token streamToken(atom, *ppToken); + stream.push_back(streamToken); +} + +// Read the next token from a macro token stream. +int TPpContext::TokenStream::getToken(TParseContextBase& parseContext, TPpToken *ppToken) +{ + if (atEnd()) + return EndOfInput; + + int atom = stream[currentPos++].get(*ppToken); + ppToken->loc = parseContext.getCurrentLoc(); + +#ifndef GLSLANG_WEB + // Check for ##, unless the current # is the last character + if (atom == '#') { + if (peekToken('#')) { + parseContext.requireProfile(ppToken->loc, ~EEsProfile, "token pasting (##)"); + parseContext.profileRequires(ppToken->loc, ~EEsProfile, 130, 0, "token pasting (##)"); + currentPos++; + atom = PpAtomPaste; + } + } +#endif + + return atom; +} + +// We are pasting if +// 1. we are preceding a pasting operator within this stream +// or +// 2. the entire macro is preceding a pasting operator (lastTokenPastes) +// and we are also on the last token +bool TPpContext::TokenStream::peekTokenizedPasting(bool lastTokenPastes) +{ + // 1. preceding ##? + + size_t savePos = currentPos; + // skip white space + while (peekToken(' ')) + ++currentPos; + if (peekToken(PpAtomPaste)) { + currentPos = savePos; + return true; + } + + // 2. last token and we've been told after this there will be a ## + + if (! lastTokenPastes) + return false; + // Getting here means the last token will be pasted, after this + + // Are we at the last non-whitespace token? + savePos = currentPos; + bool moreTokens = false; + do { + if (atEnd()) + break; + if (!peekToken(' ')) { + moreTokens = true; + break; + } + ++currentPos; + } while (true); + currentPos = savePos; + + return !moreTokens; +} + +// See if the next non-white-space tokens are two consecutive # +bool TPpContext::TokenStream::peekUntokenizedPasting() +{ + // don't return early, have to restore this + size_t savePos = currentPos; + + // skip white-space + while (peekToken(' ')) + ++currentPos; + + // check for ## + bool pasting = false; + if (peekToken('#')) { + ++currentPos; + if (peekToken('#')) + pasting = true; + } + + currentPos = savePos; + + return pasting; +} + +void TPpContext::pushTokenStreamInput(TokenStream& ts, bool prepasting) +{ + pushInput(new tTokenInput(this, &ts, prepasting)); + ts.reset(); +} + +int TPpContext::tUngotTokenInput::scan(TPpToken* ppToken) +{ + if (done) + return EndOfInput; + + int ret = token; + *ppToken = lval; + done = true; + + return ret; +} + +void TPpContext::UngetToken(int token, TPpToken* ppToken) +{ + pushInput(new tUngotTokenInput(this, token, ppToken)); +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h new file mode 100644 index 000000000..7b0f81550 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h @@ -0,0 +1,179 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef PARSER_H +#define PARSER_H + +namespace glslang { + +// Multi-character tokens +enum EFixedAtoms { + // single character tokens get their own char value as their token; start here for multi-character tokens + PpAtomMaxSingle = 127, + + // replace bad character tokens with this, to avoid accidental aliasing with the below + PpAtomBadToken, + + // Operators + + PPAtomAddAssign, + PPAtomSubAssign, + PPAtomMulAssign, + PPAtomDivAssign, + PPAtomModAssign, + + PpAtomRight, + PpAtomLeft, + + PpAtomRightAssign, + PpAtomLeftAssign, + PpAtomAndAssign, + PpAtomOrAssign, + PpAtomXorAssign, + + PpAtomAnd, + PpAtomOr, + PpAtomXor, + + PpAtomEQ, + PpAtomNE, + PpAtomGE, + PpAtomLE, + + PpAtomDecrement, + PpAtomIncrement, + + PpAtomColonColon, + + PpAtomPaste, + + // Constants + + PpAtomConstInt, + PpAtomConstUint, + PpAtomConstInt64, + PpAtomConstUint64, + PpAtomConstInt16, + PpAtomConstUint16, + PpAtomConstFloat, + PpAtomConstDouble, + PpAtomConstFloat16, + PpAtomConstString, + + // Identifiers + PpAtomIdentifier, + + // preprocessor "keywords" + + PpAtomDefine, + PpAtomUndef, + + PpAtomIf, + PpAtomIfdef, + PpAtomIfndef, + PpAtomElse, + PpAtomElif, + PpAtomEndif, + + PpAtomLine, + PpAtomPragma, + PpAtomError, + + // #version ... + PpAtomVersion, + PpAtomCore, + PpAtomCompatibility, + PpAtomEs, + + // #extension + PpAtomExtension, + + // __LINE__, __FILE__, __VERSION__ + + PpAtomLineMacro, + PpAtomFileMacro, + PpAtomVersionMacro, + + // #include + PpAtomInclude, + + PpAtomLast, +}; + +} // end namespace glslang + +#endif /* not PARSER_H */ diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/propagateNoContraction.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/propagateNoContraction.cpp new file mode 100644 index 000000000..9def592ba --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/propagateNoContraction.cpp @@ -0,0 +1,870 @@ +// +// Copyright (C) 2015-2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Visit the nodes in the glslang intermediate tree representation to +// propagate the 'noContraction' qualifier. +// + +#ifndef GLSLANG_WEB + +#include "propagateNoContraction.h" + +#include +#include +#include +#include +#include + +#include "localintermediate.h" +namespace { + +// Use a string to hold the access chain information, as in most cases the +// access chain is short and may contain only one element, which is the symbol +// ID. +// Example: struct {float a; float b;} s; +// Object s.a will be represented with: /0 +// Object s.b will be represented with: /1 +// Object s will be represented with: +// For members of vector, matrix and arrays, they will be represented with the +// same symbol ID of their container symbol objects. This is because their +// preciseness is always the same as their container symbol objects. +typedef std::string ObjectAccessChain; + +// The delimiter used in the ObjectAccessChain string to separate symbol ID and +// different level of struct indices. +const char ObjectAccesschainDelimiter = '/'; + +// Mapping from Symbol IDs of symbol nodes, to their defining operation +// nodes. +typedef std::unordered_multimap NodeMapping; +// Mapping from object nodes to their access chain info string. +typedef std::unordered_map AccessChainMapping; + +// Set of object IDs. +typedef std::unordered_set ObjectAccesschainSet; +// Set of return branch nodes. +typedef std::unordered_set ReturnBranchNodeSet; + +// A helper function to tell whether a node is 'noContraction'. Returns true if +// the node has 'noContraction' qualifier, otherwise false. +bool isPreciseObjectNode(glslang::TIntermTyped* node) +{ + return node->getType().getQualifier().isNoContraction(); +} + +// Returns true if the opcode is a dereferencing one. +bool isDereferenceOperation(glslang::TOperator op) +{ + switch (op) { + case glslang::EOpIndexDirect: + case glslang::EOpIndexDirectStruct: + case glslang::EOpIndexIndirect: + case glslang::EOpVectorSwizzle: + case glslang::EOpMatrixSwizzle: + return true; + default: + return false; + } +} + +// Returns true if the opcode leads to an assignment operation. +bool isAssignOperation(glslang::TOperator op) +{ + switch (op) { + case glslang::EOpAssign: + case glslang::EOpAddAssign: + case glslang::EOpSubAssign: + case glslang::EOpMulAssign: + case glslang::EOpVectorTimesMatrixAssign: + case glslang::EOpVectorTimesScalarAssign: + case glslang::EOpMatrixTimesScalarAssign: + case glslang::EOpMatrixTimesMatrixAssign: + case glslang::EOpDivAssign: + case glslang::EOpModAssign: + case glslang::EOpAndAssign: + case glslang::EOpLeftShiftAssign: + case glslang::EOpRightShiftAssign: + case glslang::EOpInclusiveOrAssign: + case glslang::EOpExclusiveOrAssign: + + case glslang::EOpPostIncrement: + case glslang::EOpPostDecrement: + case glslang::EOpPreIncrement: + case glslang::EOpPreDecrement: + return true; + default: + return false; + } +} + +// A helper function to get the unsigned int from a given constant union node. +// Note the node should only hold a uint scalar. +unsigned getStructIndexFromConstantUnion(glslang::TIntermTyped* node) +{ + assert(node->getAsConstantUnion() && node->getAsConstantUnion()->isScalar()); + unsigned struct_dereference_index = node->getAsConstantUnion()->getConstArray()[0].getUConst(); + return struct_dereference_index; +} + +// A helper function to generate symbol_label. +ObjectAccessChain generateSymbolLabel(glslang::TIntermSymbol* node) +{ + ObjectAccessChain symbol_id = + std::to_string(node->getId()) + "(" + node->getName().c_str() + ")"; + return symbol_id; +} + +// Returns true if the operation is an arithmetic operation and valid for +// the 'NoContraction' decoration. +bool isArithmeticOperation(glslang::TOperator op) +{ + switch (op) { + case glslang::EOpAddAssign: + case glslang::EOpSubAssign: + case glslang::EOpMulAssign: + case glslang::EOpVectorTimesMatrixAssign: + case glslang::EOpVectorTimesScalarAssign: + case glslang::EOpMatrixTimesScalarAssign: + case glslang::EOpMatrixTimesMatrixAssign: + case glslang::EOpDivAssign: + case glslang::EOpModAssign: + + case glslang::EOpNegative: + + case glslang::EOpAdd: + case glslang::EOpSub: + case glslang::EOpMul: + case glslang::EOpDiv: + case glslang::EOpMod: + + case glslang::EOpVectorTimesScalar: + case glslang::EOpVectorTimesMatrix: + case glslang::EOpMatrixTimesVector: + case glslang::EOpMatrixTimesScalar: + case glslang::EOpMatrixTimesMatrix: + + case glslang::EOpDot: + + case glslang::EOpPostIncrement: + case glslang::EOpPostDecrement: + case glslang::EOpPreIncrement: + case glslang::EOpPreDecrement: + return true; + default: + return false; + } +} + +// A helper class to help manage the populating_initial_no_contraction_ flag. +template class StateSettingGuard { +public: + StateSettingGuard(T* state_ptr, T new_state_value) + : state_ptr_(state_ptr), previous_state_(*state_ptr) + { + *state_ptr = new_state_value; + } + StateSettingGuard(T* state_ptr) : state_ptr_(state_ptr), previous_state_(*state_ptr) {} + void setState(T new_state_value) { *state_ptr_ = new_state_value; } + ~StateSettingGuard() { *state_ptr_ = previous_state_; } + +private: + T* state_ptr_; + T previous_state_; +}; + +// A helper function to get the front element from a given ObjectAccessChain +ObjectAccessChain getFrontElement(const ObjectAccessChain& chain) +{ + size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter); + return pos_delimiter == std::string::npos ? chain : chain.substr(0, pos_delimiter); +} + +// A helper function to get the access chain starting from the second element. +ObjectAccessChain subAccessChainFromSecondElement(const ObjectAccessChain& chain) +{ + size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter); + return pos_delimiter == std::string::npos ? "" : chain.substr(pos_delimiter + 1); +} + +// A helper function to get the access chain after removing a given prefix. +ObjectAccessChain getSubAccessChainAfterPrefix(const ObjectAccessChain& chain, + const ObjectAccessChain& prefix) +{ + size_t pos = chain.find(prefix); + if (pos != 0) + return chain; + return chain.substr(prefix.length() + sizeof(ObjectAccesschainDelimiter)); +} + +// +// A traverser which traverses the whole AST and populates: +// 1) A mapping from symbol nodes' IDs to their defining operation nodes. +// 2) A set of access chains of the initial precise object nodes. +// +class TSymbolDefinitionCollectingTraverser : public glslang::TIntermTraverser { +public: + TSymbolDefinitionCollectingTraverser(NodeMapping* symbol_definition_mapping, + AccessChainMapping* accesschain_mapping, + ObjectAccesschainSet* precise_objects, + ReturnBranchNodeSet* precise_return_nodes); + + bool visitUnary(glslang::TVisit, glslang::TIntermUnary*) override; + bool visitBinary(glslang::TVisit, glslang::TIntermBinary*) override; + void visitSymbol(glslang::TIntermSymbol*) override; + bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*) override; + bool visitBranch(glslang::TVisit, glslang::TIntermBranch*) override; + +protected: + TSymbolDefinitionCollectingTraverser& operator=(const TSymbolDefinitionCollectingTraverser&); + + // The mapping from symbol node IDs to their defining nodes. This should be + // populated along traversing the AST. + NodeMapping& symbol_definition_mapping_; + // The set of symbol node IDs for precise symbol nodes, the ones marked as + // 'noContraction'. + ObjectAccesschainSet& precise_objects_; + // The set of precise return nodes. + ReturnBranchNodeSet& precise_return_nodes_; + // A temporary cache of the symbol node whose defining node is to be found + // currently along traversing the AST. + ObjectAccessChain current_object_; + // A map from object node to its access chain. This traverser stores + // the built access chains into this map for each object node it has + // visited. + AccessChainMapping& accesschain_mapping_; + // The pointer to the Function Definition node, so we can get the + // preciseness of the return expression from it when we traverse the + // return branch node. + glslang::TIntermAggregate* current_function_definition_node_; +}; + +TSymbolDefinitionCollectingTraverser::TSymbolDefinitionCollectingTraverser( + NodeMapping* symbol_definition_mapping, AccessChainMapping* accesschain_mapping, + ObjectAccesschainSet* precise_objects, + std::unordered_set* precise_return_nodes) + : TIntermTraverser(true, false, false), symbol_definition_mapping_(*symbol_definition_mapping), + precise_objects_(*precise_objects), precise_return_nodes_(*precise_return_nodes), + current_object_(), accesschain_mapping_(*accesschain_mapping), + current_function_definition_node_(nullptr) {} + +// Visits a symbol node, set the current_object_ to the +// current node symbol ID, and record a mapping from this node to the current +// current_object_, which is the just obtained symbol +// ID. +void TSymbolDefinitionCollectingTraverser::visitSymbol(glslang::TIntermSymbol* node) +{ + current_object_ = generateSymbolLabel(node); + accesschain_mapping_[node] = current_object_; +} + +// Visits an aggregate node, traverses all of its children. +bool TSymbolDefinitionCollectingTraverser::visitAggregate(glslang::TVisit, + glslang::TIntermAggregate* node) +{ + // This aggregate node might be a function definition node, in which case we need to + // cache this node, so we can get the preciseness information of the return value + // of this function later. + StateSettingGuard current_function_definition_node_setting_guard( + ¤t_function_definition_node_); + if (node->getOp() == glslang::EOpFunction) { + // This is function definition node, we need to cache this node so that we can + // get the preciseness of the return value later. + current_function_definition_node_setting_guard.setState(node); + } + // Traverse the items in the sequence. + glslang::TIntermSequence& seq = node->getSequence(); + for (int i = 0; i < (int)seq.size(); ++i) { + current_object_.clear(); + seq[i]->traverse(this); + } + return false; +} + +bool TSymbolDefinitionCollectingTraverser::visitBranch(glslang::TVisit, + glslang::TIntermBranch* node) +{ + if (node->getFlowOp() == glslang::EOpReturn && node->getExpression() && + current_function_definition_node_ && + current_function_definition_node_->getType().getQualifier().noContraction) { + // This node is a return node with an expression, and its function has a + // precise return value. We need to find the involved objects in its + // expression and add them to the set of initial precise objects. + precise_return_nodes_.insert(node); + node->getExpression()->traverse(this); + } + return false; +} + +// Visits a unary node. This might be an implicit assignment like i++, i--. etc. +bool TSymbolDefinitionCollectingTraverser::visitUnary(glslang::TVisit /* visit */, + glslang::TIntermUnary* node) +{ + current_object_.clear(); + node->getOperand()->traverse(this); + if (isAssignOperation(node->getOp())) { + // We should always be able to get an access chain of the operand node. + assert(!current_object_.empty()); + + // If the operand node object is 'precise', we collect its access chain + // for the initial set of 'precise' objects. + if (isPreciseObjectNode(node->getOperand())) { + // The operand node is an 'precise' object node, add its + // access chain to the set of 'precise' objects. This is to collect + // the initial set of 'precise' objects. + precise_objects_.insert(current_object_); + } + // Gets the symbol ID from the object's access chain. + ObjectAccessChain id_symbol = getFrontElement(current_object_); + // Add a mapping from the symbol ID to this assignment operation node. + symbol_definition_mapping_.insert(std::make_pair(id_symbol, node)); + } + // A unary node is not a dereference node, so we clear the access chain which + // is under construction. + current_object_.clear(); + return false; +} + +// Visits a binary node and updates the mapping from symbol IDs to the definition +// nodes. Also collects the access chains for the initial precise objects. +bool TSymbolDefinitionCollectingTraverser::visitBinary(glslang::TVisit /* visit */, + glslang::TIntermBinary* node) +{ + // Traverses the left node to build the access chain info for the object. + current_object_.clear(); + node->getLeft()->traverse(this); + + if (isAssignOperation(node->getOp())) { + // We should always be able to get an access chain for the left node. + assert(!current_object_.empty()); + + // If the left node object is 'precise', it is an initial precise object + // specified in the shader source. Adds it to the initial work list to + // process later. + if (isPreciseObjectNode(node->getLeft())) { + // The left node is an 'precise' object node, add its access chain to + // the set of 'precise' objects. This is to collect the initial set + // of 'precise' objects. + precise_objects_.insert(current_object_); + } + // Gets the symbol ID from the object access chain, which should be the + // first element recorded in the access chain. + ObjectAccessChain id_symbol = getFrontElement(current_object_); + // Adds a mapping from the symbol ID to this assignment operation node. + symbol_definition_mapping_.insert(std::make_pair(id_symbol, node)); + + // Traverses the right node, there may be other 'assignment' + // operations in the right. + current_object_.clear(); + node->getRight()->traverse(this); + + } else if (isDereferenceOperation(node->getOp())) { + // The left node (parent node) is a struct type object. We need to + // record the access chain information of the current node into its + // object id. + if (node->getOp() == glslang::EOpIndexDirectStruct) { + unsigned struct_dereference_index = getStructIndexFromConstantUnion(node->getRight()); + current_object_.push_back(ObjectAccesschainDelimiter); + current_object_.append(std::to_string(struct_dereference_index)); + } + accesschain_mapping_[node] = current_object_; + + // For a dereference node, there is no need to traverse the right child + // node as the right node should always be an integer type object. + + } else { + // For other binary nodes, still traverse the right node. + current_object_.clear(); + node->getRight()->traverse(this); + } + return false; +} + +// Traverses the AST and returns a tuple of four members: +// 1) a mapping from symbol IDs to the definition nodes (aka. assignment nodes) of these symbols. +// 2) a mapping from object nodes in the AST to the access chains of these objects. +// 3) a set of access chains of precise objects. +// 4) a set of return nodes with precise expressions. +std::tuple +getSymbolToDefinitionMappingAndPreciseSymbolIDs(const glslang::TIntermediate& intermediate) +{ + auto result_tuple = std::make_tuple(NodeMapping(), AccessChainMapping(), ObjectAccesschainSet(), + ReturnBranchNodeSet()); + + TIntermNode* root = intermediate.getTreeRoot(); + if (root == 0) + return result_tuple; + + NodeMapping& symbol_definition_mapping = std::get<0>(result_tuple); + AccessChainMapping& accesschain_mapping = std::get<1>(result_tuple); + ObjectAccesschainSet& precise_objects = std::get<2>(result_tuple); + ReturnBranchNodeSet& precise_return_nodes = std::get<3>(result_tuple); + + // Traverses the AST and populate the results. + TSymbolDefinitionCollectingTraverser collector(&symbol_definition_mapping, &accesschain_mapping, + &precise_objects, &precise_return_nodes); + root->traverse(&collector); + + return result_tuple; +} + +// +// A traverser that determine whether the left node (or operand node for unary +// node) of an assignment node is 'precise', containing 'precise' or not, +// according to the access chain a given precise object which share the same +// symbol as the left node. +// +// Post-orderly traverses the left node subtree of an binary assignment node and: +// +// 1) Propagates the 'precise' from the left object nodes to this object node. +// +// 2) Builds object access chain along the traversal, and also compares with +// the access chain of the given 'precise' object along with the traversal to +// tell if the node to be defined is 'precise' or not. +// +class TNoContractionAssigneeCheckingTraverser : public glslang::TIntermTraverser { + + enum DecisionStatus { + // The object node to be assigned to may contain 'precise' objects and also not 'precise' objects. + Mixed = 0, + // The object node to be assigned to is either a 'precise' object or a struct objects whose members are all 'precise'. + Precise = 1, + // The object node to be assigned to is not a 'precise' object. + NotPreicse = 2, + }; + +public: + TNoContractionAssigneeCheckingTraverser(const AccessChainMapping& accesschain_mapping) + : TIntermTraverser(true, false, false), accesschain_mapping_(accesschain_mapping), + precise_object_(nullptr) {} + + // Checks the preciseness of a given assignment node with a precise object + // represented as access chain. The precise object shares the same symbol + // with the assignee of the given assignment node. Return a tuple of two: + // + // 1) The preciseness of the assignee node of this assignment node. True + // if the assignee contains 'precise' objects or is 'precise', false if + // the assignee is not 'precise' according to the access chain of the given + // precise object. + // + // 2) The incremental access chain from the assignee node to its nested + // 'precise' object, according to the access chain of the given precise + // object. This incremental access chain can be empty, which means the + // assignee is 'precise'. Otherwise it shows the path to the nested + // precise object. + std::tuple + getPrecisenessAndRemainedAccessChain(glslang::TIntermOperator* node, + const ObjectAccessChain& precise_object) + { + assert(isAssignOperation(node->getOp())); + precise_object_ = &precise_object; + ObjectAccessChain assignee_object; + if (glslang::TIntermBinary* BN = node->getAsBinaryNode()) { + // This is a binary assignment node, we need to check the + // preciseness of the left node. + assert(accesschain_mapping_.count(BN->getLeft())); + // The left node (assignee node) is an object node, traverse the + // node to let the 'precise' of nesting objects being transfered to + // nested objects. + BN->getLeft()->traverse(this); + // After traversing the left node, if the left node is 'precise', + // we can conclude this assignment should propagate 'precise'. + if (isPreciseObjectNode(BN->getLeft())) { + return make_tuple(true, ObjectAccessChain()); + } + // If the preciseness of the left node (assignee node) can not + // be determined by now, we need to compare the access chain string + // of the assignee object with the given precise object. + assignee_object = accesschain_mapping_.at(BN->getLeft()); + + } else if (glslang::TIntermUnary* UN = node->getAsUnaryNode()) { + // This is a unary assignment node, we need to check the + // preciseness of the operand node. For unary assignment node, the + // operand node should always be an object node. + assert(accesschain_mapping_.count(UN->getOperand())); + // Traverse the operand node to let the 'precise' being propagated + // from lower nodes to upper nodes. + UN->getOperand()->traverse(this); + // After traversing the operand node, if the operand node is + // 'precise', this assignment should propagate 'precise'. + if (isPreciseObjectNode(UN->getOperand())) { + return make_tuple(true, ObjectAccessChain()); + } + // If the preciseness of the operand node (assignee node) can not + // be determined by now, we need to compare the access chain string + // of the assignee object with the given precise object. + assignee_object = accesschain_mapping_.at(UN->getOperand()); + } else { + // Not a binary or unary node, should not happen. + assert(false); + } + + // Compare the access chain string of the assignee node with the given + // precise object to determine if this assignment should propagate + // 'precise'. + if (assignee_object.find(precise_object) == 0) { + // The access chain string of the given precise object is a prefix + // of assignee's access chain string. The assignee should be + // 'precise'. + return make_tuple(true, ObjectAccessChain()); + } else if (precise_object.find(assignee_object) == 0) { + // The assignee's access chain string is a prefix of the given + // precise object, the assignee object contains 'precise' object, + // and we need to pass the remained access chain to the object nodes + // in the right. + return make_tuple(true, getSubAccessChainAfterPrefix(precise_object, assignee_object)); + } else { + // The access chain strings do not match, the assignee object can + // not be labeled as 'precise' according to the given precise + // object. + return make_tuple(false, ObjectAccessChain()); + } + } + +protected: + TNoContractionAssigneeCheckingTraverser& operator=(const TNoContractionAssigneeCheckingTraverser&); + + bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override; + void visitSymbol(glslang::TIntermSymbol* node) override; + + // A map from object nodes to their access chain string (used as object ID). + const AccessChainMapping& accesschain_mapping_; + // A given precise object, represented in it access chain string. This + // precise object is used to be compared with the assignee node to tell if + // the assignee node is 'precise', contains 'precise' object or not + // 'precise'. + const ObjectAccessChain* precise_object_; +}; + +// Visits a binary node. If the node is an object node, it must be a dereference +// node. In such cases, if the left node is 'precise', this node should also be +// 'precise'. +bool TNoContractionAssigneeCheckingTraverser::visitBinary(glslang::TVisit, + glslang::TIntermBinary* node) +{ + // Traverses the left so that we transfer the 'precise' from nesting object + // to its nested object. + node->getLeft()->traverse(this); + // If this binary node is an object node, we should have it in the + // accesschain_mapping_. + if (accesschain_mapping_.count(node)) { + // A binary object node must be a dereference node. + assert(isDereferenceOperation(node->getOp())); + // If the left node is 'precise', this node should also be precise, + // otherwise, compare with the given precise_object_. If the + // access chain of this node matches with the given precise_object_, + // this node should be marked as 'precise'. + if (isPreciseObjectNode(node->getLeft())) { + node->getWritableType().getQualifier().noContraction = true; + } else if (accesschain_mapping_.at(node) == *precise_object_) { + node->getWritableType().getQualifier().noContraction = true; + } + } + return false; +} + +// Visits a symbol node, if the symbol node ID (its access chain string) matches +// with the given precise object, this node should be 'precise'. +void TNoContractionAssigneeCheckingTraverser::visitSymbol(glslang::TIntermSymbol* node) +{ + // A symbol node should always be an object node, and should have been added + // to the map from object nodes to their access chain strings. + assert(accesschain_mapping_.count(node)); + if (accesschain_mapping_.at(node) == *precise_object_) { + node->getWritableType().getQualifier().noContraction = true; + } +} + +// +// A traverser that only traverses the right side of binary assignment nodes +// and the operand node of unary assignment nodes. +// +// 1) Marks arithmetic operations as 'NoContraction'. +// +// 2) Find the object which should be marked as 'precise' in the right and +// update the 'precise' object work list. +// +class TNoContractionPropagator : public glslang::TIntermTraverser { +public: + TNoContractionPropagator(ObjectAccesschainSet* precise_objects, + const AccessChainMapping& accesschain_mapping) + : TIntermTraverser(true, false, false), + precise_objects_(*precise_objects), added_precise_object_ids_(), + remained_accesschain_(), accesschain_mapping_(accesschain_mapping) {} + + // Propagates 'precise' in the right nodes of a given assignment node with + // access chain record from the assignee node to a 'precise' object it + // contains. + void + propagateNoContractionInOneExpression(glslang::TIntermTyped* defining_node, + const ObjectAccessChain& assignee_remained_accesschain) + { + remained_accesschain_ = assignee_remained_accesschain; + if (glslang::TIntermBinary* BN = defining_node->getAsBinaryNode()) { + assert(isAssignOperation(BN->getOp())); + BN->getRight()->traverse(this); + if (isArithmeticOperation(BN->getOp())) { + BN->getWritableType().getQualifier().noContraction = true; + } + } else if (glslang::TIntermUnary* UN = defining_node->getAsUnaryNode()) { + assert(isAssignOperation(UN->getOp())); + UN->getOperand()->traverse(this); + if (isArithmeticOperation(UN->getOp())) { + UN->getWritableType().getQualifier().noContraction = true; + } + } + } + + // Propagates 'precise' in a given precise return node. + void propagateNoContractionInReturnNode(glslang::TIntermBranch* return_node) + { + remained_accesschain_ = ""; + assert(return_node->getFlowOp() == glslang::EOpReturn && return_node->getExpression()); + return_node->getExpression()->traverse(this); + } + +protected: + TNoContractionPropagator& operator=(const TNoContractionPropagator&); + + // Visits an aggregate node. The node can be a initializer list, in which + // case we need to find the 'precise' or 'precise' containing object node + // with the access chain record. In other cases, just need to traverse all + // the children nodes. + bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate* node) override + { + if (!remained_accesschain_.empty() && node->getOp() == glslang::EOpConstructStruct) { + // This is a struct initializer node, and the remained + // access chain is not empty, we need to refer to the + // assignee_remained_access_chain_ to find the nested + // 'precise' object. And we don't need to visit other nodes in this + // aggregate node. + + // Gets the struct dereference index that leads to 'precise' object. + ObjectAccessChain precise_accesschain_index_str = + getFrontElement(remained_accesschain_); + unsigned precise_accesschain_index = (unsigned)strtoul(precise_accesschain_index_str.c_str(), nullptr, 10); + // Gets the node pointed by the access chain index extracted before. + glslang::TIntermTyped* potential_precise_node = + node->getSequence()[precise_accesschain_index]->getAsTyped(); + assert(potential_precise_node); + // Pop the front access chain index from the path, and visit the nested node. + { + ObjectAccessChain next_level_accesschain = + subAccessChainFromSecondElement(remained_accesschain_); + StateSettingGuard setup_remained_accesschain_for_next_level( + &remained_accesschain_, next_level_accesschain); + potential_precise_node->traverse(this); + } + return false; + } + return true; + } + + // Visits a binary node. A binary node can be an object node, e.g. a dereference node. + // As only the top object nodes in the right side of an assignment needs to be visited + // and added to 'precise' work list, this traverser won't visit the children nodes of + // an object node. If the binary node does not represent an object node, it should + // go on to traverse its children nodes and if it is an arithmetic operation node, this + // operation should be marked as 'noContraction'. + bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override + { + if (isDereferenceOperation(node->getOp())) { + // This binary node is an object node. Need to update the precise + // object set with the access chain of this node + remained + // access chain . + ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node); + if (remained_accesschain_.empty()) { + node->getWritableType().getQualifier().noContraction = true; + } else { + new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_; + } + // Cache the access chain as added precise object, so we won't add the + // same object to the work list again. + if (!added_precise_object_ids_.count(new_precise_accesschain)) { + precise_objects_.insert(new_precise_accesschain); + added_precise_object_ids_.insert(new_precise_accesschain); + } + // Only the upper-most object nodes should be visited, so do not + // visit children of this object node. + return false; + } + // If this is an arithmetic operation, marks this node as 'noContraction'. + if (isArithmeticOperation(node->getOp()) && node->getBasicType() != glslang::EbtInt) { + node->getWritableType().getQualifier().noContraction = true; + } + // As this node is not an object node, need to traverse the children nodes. + return true; + } + + // Visits a unary node. A unary node can not be an object node. If the operation + // is an arithmetic operation, need to mark this node as 'noContraction'. + bool visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) override + { + // If this is an arithmetic operation, marks this with 'noContraction' + if (isArithmeticOperation(node->getOp())) { + node->getWritableType().getQualifier().noContraction = true; + } + return true; + } + + // Visits a symbol node. A symbol node is always an object node. So we + // should always be able to find its in our collected mapping from object + // nodes to access chains. As an object node, a symbol node can be either + // 'precise' or containing 'precise' objects according to unused + // access chain information we have when we visit this node. + void visitSymbol(glslang::TIntermSymbol* node) override + { + // Symbol nodes are object nodes and should always have an + // access chain collected before matches with it. + assert(accesschain_mapping_.count(node)); + ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node); + // If the unused access chain is empty, this symbol node should be + // marked as 'precise'. Otherwise, the unused access chain should be + // appended to the symbol ID to build a new access chain which points to + // the nested 'precise' object in this symbol object. + if (remained_accesschain_.empty()) { + node->getWritableType().getQualifier().noContraction = true; + } else { + new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_; + } + // Add the new 'precise' access chain to the work list and make sure we + // don't visit it again. + if (!added_precise_object_ids_.count(new_precise_accesschain)) { + precise_objects_.insert(new_precise_accesschain); + added_precise_object_ids_.insert(new_precise_accesschain); + } + } + + // A set of precise objects, represented as access chains. + ObjectAccesschainSet& precise_objects_; + // Visited symbol nodes, should not revisit these nodes. + ObjectAccesschainSet added_precise_object_ids_; + // The left node of an assignment operation might be an parent of 'precise' objects. + // This means the left node might not be an 'precise' object node, but it may contains + // 'precise' qualifier which should be propagated to the corresponding child node in + // the right. So we need the path from the left node to its nested 'precise' node to + // tell us how to find the corresponding 'precise' node in the right. + ObjectAccessChain remained_accesschain_; + // A map from node pointers to their access chains. + const AccessChainMapping& accesschain_mapping_; +}; +} + +namespace glslang { + +void PropagateNoContraction(const glslang::TIntermediate& intermediate) +{ + // First, traverses the AST, records symbols with their defining operations + // and collects the initial set of precise symbols (symbol nodes that marked + // as 'noContraction') and precise return nodes. + auto mappings_and_precise_objects = + getSymbolToDefinitionMappingAndPreciseSymbolIDs(intermediate); + + // The mapping of symbol node IDs to their defining nodes. This enables us + // to get the defining node directly from a given symbol ID without + // traversing the tree again. + NodeMapping& symbol_definition_mapping = std::get<0>(mappings_and_precise_objects); + + // The mapping of object nodes to their access chains recorded. + AccessChainMapping& accesschain_mapping = std::get<1>(mappings_and_precise_objects); + + // The initial set of 'precise' objects which are represented as the + // access chain toward them. + ObjectAccesschainSet& precise_object_accesschains = std::get<2>(mappings_and_precise_objects); + + // The set of 'precise' return nodes. + ReturnBranchNodeSet& precise_return_nodes = std::get<3>(mappings_and_precise_objects); + + // Second, uses the initial set of precise objects as a work list, pops an + // access chain, extract the symbol ID from it. Then: + // 1) Check the assignee object, see if it is 'precise' object node or + // contains 'precise' object. Obtain the incremental access chain from the + // assignee node to its nested 'precise' node (if any). + // 2) If the assignee object node is 'precise' or it contains 'precise' + // objects, traverses the right side of the assignment operation + // expression to mark arithmetic operations as 'noContration' and update + // 'precise' access chain work list with new found object nodes. + // Repeat above steps until the work list is empty. + TNoContractionAssigneeCheckingTraverser checker(accesschain_mapping); + TNoContractionPropagator propagator(&precise_object_accesschains, accesschain_mapping); + + // We have two initial precise work lists to handle: + // 1) precise return nodes + // 2) precise object access chains + // We should process the precise return nodes first and the involved + // objects in the return expression should be added to the precise object + // access chain set. + while (!precise_return_nodes.empty()) { + glslang::TIntermBranch* precise_return_node = *precise_return_nodes.begin(); + propagator.propagateNoContractionInReturnNode(precise_return_node); + precise_return_nodes.erase(precise_return_node); + } + + while (!precise_object_accesschains.empty()) { + // Get the access chain of a precise object from the work list. + ObjectAccessChain precise_object_accesschain = *precise_object_accesschains.begin(); + // Get the symbol id from the access chain. + ObjectAccessChain symbol_id = getFrontElement(precise_object_accesschain); + // Get all the defining nodes of that symbol ID. + std::pair range = + symbol_definition_mapping.equal_range(symbol_id); + // Visits all the assignment nodes of that symbol ID and + // 1) Check if the assignee node is 'precise' or contains 'precise' + // objects. + // 2) Propagate the 'precise' to the top layer object nodes + // in the right side of the assignment operation, update the 'precise' + // work list with new access chains representing the new 'precise' + // objects, and mark arithmetic operations as 'noContraction'. + for (NodeMapping::iterator defining_node_iter = range.first; + defining_node_iter != range.second; defining_node_iter++) { + TIntermOperator* defining_node = defining_node_iter->second; + // Check the assignee node. + auto checker_result = checker.getPrecisenessAndRemainedAccessChain( + defining_node, precise_object_accesschain); + bool& contain_precise = std::get<0>(checker_result); + ObjectAccessChain& remained_accesschain = std::get<1>(checker_result); + // If the assignee node is 'precise' or contains 'precise', propagate the + // 'precise' to the right. Otherwise just skip this assignment node. + if (contain_precise) { + propagator.propagateNoContractionInOneExpression(defining_node, + remained_accesschain); + } + } + // Remove the last processed 'precise' object from the work list. + precise_object_accesschains.erase(precise_object_accesschain); + } +} +}; + +#endif // GLSLANG_WEB diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/propagateNoContraction.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/propagateNoContraction.h new file mode 100644 index 000000000..8521ad7d6 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/propagateNoContraction.h @@ -0,0 +1,55 @@ +// +// Copyright (C) 2015-2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Visit the nodes in the glslang intermediate tree representation to +// propagate 'noContraction' qualifier. +// + +#pragma once + +#include "../Include/intermediate.h" + +namespace glslang { + +// Propagates the 'precise' qualifier for objects (objects marked with +// 'noContraction' qualifier) from the shader source specified 'precise' +// variables to all the involved objects, and add 'noContraction' qualifier for +// the involved arithmetic operations. +// Note that the same qualifier: 'noContraction' is used in both object nodes +// and arithmetic operation nodes, but has different meaning. For object nodes, +// 'noContraction' means the object is 'precise'; and for arithmetic operation +// nodes, it means the operation should not be contracted. +void PropagateNoContraction(const glslang::TIntermediate& intermediate); +}; diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/reflection.cpp b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/reflection.cpp new file mode 100644 index 000000000..287693323 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/reflection.cpp @@ -0,0 +1,1204 @@ +// +// Copyright (C) 2013-2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef GLSLANG_WEB + +#include "../Include/Common.h" +#include "reflection.h" +#include "LiveTraverser.h" +#include "localintermediate.h" + +#include "gl_types.h" + +// +// Grow the reflection database through a friend traverser class of TReflection and a +// collection of functions to do a liveness traversal that note what uniforms are used +// in semantically non-dead code. +// +// Can be used multiple times, once per stage, to grow a program reflection. +// +// High-level algorithm for one stage: +// +// 1. Put the entry point on the list of live functions. +// +// 2. Traverse any live function, while skipping if-tests with a compile-time constant +// condition of false, and while adding any encountered function calls to the live +// function list. +// +// Repeat until the live function list is empty. +// +// 3. Add any encountered uniform variables and blocks to the reflection database. +// +// Can be attempted with a failed link, but will return false if recursion had been detected, or +// there wasn't exactly one entry point. +// + +namespace glslang { + +// +// The traverser: mostly pass through, except +// - processing binary nodes to see if they are dereferences of an aggregates to track +// - processing symbol nodes to see if they are non-aggregate objects to track +// +// This ignores semantically dead code by using TLiveTraverser. +// +// This is in the glslang namespace directly so it can be a friend of TReflection. +// + +class TReflectionTraverser : public TLiveTraverser { +public: + TReflectionTraverser(const TIntermediate& i, TReflection& r) : + TLiveTraverser(i), reflection(r) { } + + virtual bool visitBinary(TVisit, TIntermBinary* node); + virtual void visitSymbol(TIntermSymbol* base); + + // Add a simple reference to a uniform variable to the uniform database, no dereference involved. + // However, no dereference doesn't mean simple... it could be a complex aggregate. + void addUniform(const TIntermSymbol& base) + { + if (processedDerefs.find(&base) == processedDerefs.end()) { + processedDerefs.insert(&base); + + // Use a degenerate (empty) set of dereferences to immediately put as at the end of + // the dereference change expected by blowUpActiveAggregate. + TList derefs; + blowUpActiveAggregate(base.getType(), base.getName(), derefs, derefs.end(), -1, -1, 0, 0, + base.getQualifier().storage, true); + } + } + + void addPipeIOVariable(const TIntermSymbol& base) + { + if (processedDerefs.find(&base) == processedDerefs.end()) { + processedDerefs.insert(&base); + + const TString &name = base.getName(); + const TType &type = base.getType(); + const bool input = base.getQualifier().isPipeInput(); + + TReflection::TMapIndexToReflection &ioItems = + input ? reflection.indexToPipeInput : reflection.indexToPipeOutput; + + + TReflection::TNameToIndex &ioMapper = + input ? reflection.pipeInNameToIndex : reflection.pipeOutNameToIndex; + + if (reflection.options & EShReflectionUnwrapIOBlocks) { + bool anonymous = IsAnonymous(name); + + TString baseName; + if (type.getBasicType() == EbtBlock) { + baseName = anonymous ? TString() : type.getTypeName(); + } else { + baseName = anonymous ? TString() : name; + } + + // by convention if this is an arrayed block we ignore the array in the reflection + if (type.isArray() && type.getBasicType() == EbtBlock) { + blowUpIOAggregate(input, baseName, TType(type, 0)); + } else { + blowUpIOAggregate(input, baseName, type); + } + } else { + TReflection::TNameToIndex::const_iterator it = ioMapper.find(name.c_str()); + if (it == ioMapper.end()) { + // seperate pipe i/o params from uniforms and blocks + // in is only for input in first stage as out is only for last stage. check traverse in call stack. + ioMapper[name.c_str()] = static_cast(ioItems.size()); + ioItems.push_back( + TObjectReflection(name.c_str(), type, 0, mapToGlType(type), mapToGlArraySize(type), 0)); + EShLanguageMask& stages = ioItems.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } else { + EShLanguageMask& stages = ioItems[it->second].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } + } + } + + // Lookup or calculate the offset of all block members at once, using the recursively + // defined block offset rules. + void getOffsets(const TType& type, TVector& offsets) + { + const TTypeList& memberList = *type.getStruct(); + + int memberSize = 0; + int offset = 0; + for (size_t m = 0; m < offsets.size(); ++m) { + // if the user supplied an offset, snap to it now + if (memberList[m].type->getQualifier().hasOffset()) + offset = memberList[m].type->getQualifier().layoutOffset; + + // calculate the offset of the next member and align the current offset to this member + intermediate.updateOffset(type, *memberList[m].type, offset, memberSize); + + // save the offset of this member + offsets[m] = offset; + + // update for the next member + offset += memberSize; + } + } + + // Calculate the stride of an array type + int getArrayStride(const TType& baseType, const TType& type) + { + int dummySize; + int stride; + + // consider blocks to have 0 stride, so that all offsets are relative to the start of their block + if (type.getBasicType() == EbtBlock) + return 0; + + TLayoutMatrix subMatrixLayout = type.getQualifier().layoutMatrix; + intermediate.getMemberAlignment(type, dummySize, stride, + baseType.getQualifier().layoutPacking, + subMatrixLayout != ElmNone + ? subMatrixLayout == ElmRowMajor + : baseType.getQualifier().layoutMatrix == ElmRowMajor); + + return stride; + } + + // count the total number of leaf members from iterating out of a block type + int countAggregateMembers(const TType& parentType) + { + if (! parentType.isStruct()) + return 1; + + const bool strictArraySuffix = (reflection.options & EShReflectionStrictArraySuffix); + + bool blockParent = (parentType.getBasicType() == EbtBlock && parentType.getQualifier().storage == EvqBuffer); + + const TTypeList &memberList = *parentType.getStruct(); + + int ret = 0; + + for (size_t i = 0; i < memberList.size(); i++) + { + const TType &memberType = *memberList[i].type; + int numMembers = countAggregateMembers(memberType); + // for sized arrays of structs, apply logic to expand out the same as we would below in + // blowUpActiveAggregate + if (memberType.isArray() && ! memberType.getArraySizes()->hasUnsized() && memberType.isStruct()) { + if (! strictArraySuffix || ! blockParent) + numMembers *= memberType.getArraySizes()->getCumulativeSize(); + } + ret += numMembers; + } + + return ret; + } + + // Traverse the provided deref chain, including the base, and + // - build a full reflection-granularity name, array size, etc. entry out of it, if it goes down to that granularity + // - recursively expand any variable array index in the middle of that traversal + // - recursively expand what's left at the end if the deref chain did not reach down to reflection granularity + // + // arraySize tracks, just for the final dereference in the chain, if there was a specific known size. + // A value of 0 for arraySize will mean to use the full array's size. + void blowUpActiveAggregate(const TType& baseType, const TString& baseName, const TList& derefs, + TList::const_iterator deref, int offset, int blockIndex, int arraySize, + int topLevelArrayStride, TStorageQualifier baseStorage, bool active) + { + // when strictArraySuffix is enabled, we closely follow the rules from ARB_program_interface_query. + // Broadly: + // * arrays-of-structs always have a [x] suffix. + // * with array-of-struct variables in the root of a buffer block, only ever return [0]. + // * otherwise, array suffixes are added whenever we iterate, even if that means expanding out an array. + const bool strictArraySuffix = (reflection.options & EShReflectionStrictArraySuffix); + + // is this variable inside a buffer block. This flag is set back to false after we iterate inside the first array element. + bool blockParent = (baseType.getBasicType() == EbtBlock && baseType.getQualifier().storage == EvqBuffer); + + // process the part of the dereference chain that was explicit in the shader + TString name = baseName; + const TType* terminalType = &baseType; + for (; deref != derefs.end(); ++deref) { + TIntermBinary* visitNode = *deref; + terminalType = &visitNode->getType(); + int index; + switch (visitNode->getOp()) { + case EOpIndexIndirect: { + int stride = getArrayStride(baseType, visitNode->getLeft()->getType()); + + if (topLevelArrayStride == 0) + topLevelArrayStride = stride; + + // Visit all the indices of this array, and for each one add on the remaining dereferencing + for (int i = 0; i < std::max(visitNode->getLeft()->getType().getOuterArraySize(), 1); ++i) { + TString newBaseName = name; + if (strictArraySuffix && blockParent) + newBaseName.append(TString("[0]")); + else if (strictArraySuffix || baseType.getBasicType() != EbtBlock) + newBaseName.append(TString("[") + String(i) + "]"); + TList::const_iterator nextDeref = deref; + ++nextDeref; + blowUpActiveAggregate(*terminalType, newBaseName, derefs, nextDeref, offset, blockIndex, arraySize, + topLevelArrayStride, baseStorage, active); + + if (offset >= 0) + offset += stride; + } + + // it was all completed in the recursive calls above + return; + } + case EOpIndexDirect: { + int stride = getArrayStride(baseType, visitNode->getLeft()->getType()); + + index = visitNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (strictArraySuffix && blockParent) { + name.append(TString("[0]")); + } else if (strictArraySuffix || baseType.getBasicType() != EbtBlock) { + name.append(TString("[") + String(index) + "]"); + + if (offset >= 0) + offset += stride * index; + } + + if (topLevelArrayStride == 0) + topLevelArrayStride = stride; + + blockParent = false; + break; + } + case EOpIndexDirectStruct: + index = visitNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (offset >= 0) + offset += intermediate.getOffset(visitNode->getLeft()->getType(), index); + if (name.size() > 0) + name.append("."); + name.append((*visitNode->getLeft()->getType().getStruct())[index].type->getFieldName()); + break; + default: + break; + } + } + + // if the terminalType is still too coarse a granularity, this is still an aggregate to expand, expand it... + if (! isReflectionGranularity(*terminalType)) { + // the base offset of this node, that children are relative to + int baseOffset = offset; + + if (terminalType->isArray()) { + // Visit all the indices of this array, and for each one, + // fully explode the remaining aggregate to dereference + + int stride = 0; + if (offset >= 0) + stride = getArrayStride(baseType, *terminalType); + + if (topLevelArrayStride == 0) + topLevelArrayStride = stride; + + int arrayIterateSize = std::max(terminalType->getOuterArraySize(), 1); + + // for top-level arrays in blocks, only expand [0] to avoid explosion of items + if (strictArraySuffix && blockParent) + arrayIterateSize = 1; + + for (int i = 0; i < arrayIterateSize; ++i) { + TString newBaseName = name; + newBaseName.append(TString("[") + String(i) + "]"); + TType derefType(*terminalType, 0); + if (offset >= 0) + offset = baseOffset + stride * i; + + blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), offset, blockIndex, 0, + topLevelArrayStride, baseStorage, active); + } + } else { + // Visit all members of this aggregate, and for each one, + // fully explode the remaining aggregate to dereference + const TTypeList& typeList = *terminalType->getStruct(); + + TVector memberOffsets; + + if (baseOffset >= 0) { + memberOffsets.resize(typeList.size()); + getOffsets(*terminalType, memberOffsets); + } + + for (int i = 0; i < (int)typeList.size(); ++i) { + TString newBaseName = name; + if (newBaseName.size() > 0) + newBaseName.append("."); + newBaseName.append(typeList[i].type->getFieldName()); + TType derefType(*terminalType, i); + if (offset >= 0) + offset = baseOffset + memberOffsets[i]; + + int arrayStride = topLevelArrayStride; + if (terminalType->getBasicType() == EbtBlock && terminalType->getQualifier().storage == EvqBuffer && + derefType.isArray()) { + arrayStride = getArrayStride(baseType, derefType); + } + + blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), offset, blockIndex, 0, + arrayStride, baseStorage, active); + } + } + + // it was all completed in the recursive calls above + return; + } + + if ((reflection.options & EShReflectionBasicArraySuffix) && terminalType->isArray()) { + name.append(TString("[0]")); + } + + // Finally, add a full string to the reflection database, and update the array size if necessary. + // If the dereferenced entity to record is an array, compute the size and update the maximum size. + + // there might not be a final array dereference, it could have been copied as an array object + if (arraySize == 0) + arraySize = mapToGlArraySize(*terminalType); + + TReflection::TMapIndexToReflection& variables = reflection.GetVariableMapForStorage(baseStorage); + + TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str()); + if (it == reflection.nameToIndex.end()) { + int uniformIndex = (int)variables.size(); + reflection.nameToIndex[name.c_str()] = uniformIndex; + variables.push_back(TObjectReflection(name.c_str(), *terminalType, offset, mapToGlType(*terminalType), + arraySize, blockIndex)); + if (terminalType->isArray()) { + variables.back().arrayStride = getArrayStride(baseType, *terminalType); + if (topLevelArrayStride == 0) + topLevelArrayStride = variables.back().arrayStride; + } + + if ((reflection.options & EShReflectionSeparateBuffers) && terminalType->isAtomic()) + reflection.atomicCounterUniformIndices.push_back(uniformIndex); + + variables.back().topLevelArrayStride = topLevelArrayStride; + + if ((reflection.options & EShReflectionAllBlockVariables) && active) { + EShLanguageMask& stages = variables.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } else { + if (arraySize > 1) { + int& reflectedArraySize = variables[it->second].size; + reflectedArraySize = std::max(arraySize, reflectedArraySize); + } + + if ((reflection.options & EShReflectionAllBlockVariables) && active) { + EShLanguageMask& stages = variables[it->second].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } + } + + // similar to blowUpActiveAggregate, but with simpler rules and no dereferences to follow. + void blowUpIOAggregate(bool input, const TString &baseName, const TType &type) + { + TString name = baseName; + + // if the type is still too coarse a granularity, this is still an aggregate to expand, expand it... + if (! isReflectionGranularity(type)) { + if (type.isArray()) { + // Visit all the indices of this array, and for each one, + // fully explode the remaining aggregate to dereference + for (int i = 0; i < std::max(type.getOuterArraySize(), 1); ++i) { + TString newBaseName = name; + newBaseName.append(TString("[") + String(i) + "]"); + TType derefType(type, 0); + + blowUpIOAggregate(input, newBaseName, derefType); + } + } else { + // Visit all members of this aggregate, and for each one, + // fully explode the remaining aggregate to dereference + const TTypeList& typeList = *type.getStruct(); + + for (int i = 0; i < (int)typeList.size(); ++i) { + TString newBaseName = name; + if (newBaseName.size() > 0) + newBaseName.append("."); + newBaseName.append(typeList[i].type->getFieldName()); + TType derefType(type, i); + + blowUpIOAggregate(input, newBaseName, derefType); + } + } + + // it was all completed in the recursive calls above + return; + } + + if ((reflection.options & EShReflectionBasicArraySuffix) && type.isArray()) { + name.append(TString("[0]")); + } + + TReflection::TMapIndexToReflection &ioItems = + input ? reflection.indexToPipeInput : reflection.indexToPipeOutput; + + std::string namespacedName = input ? "in " : "out "; + namespacedName += name.c_str(); + + TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(namespacedName); + if (it == reflection.nameToIndex.end()) { + reflection.nameToIndex[namespacedName] = (int)ioItems.size(); + ioItems.push_back( + TObjectReflection(name.c_str(), type, 0, mapToGlType(type), mapToGlArraySize(type), 0)); + + EShLanguageMask& stages = ioItems.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } else { + EShLanguageMask& stages = ioItems[it->second].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } + + // Add a uniform dereference where blocks/struct/arrays are involved in the access. + // Handles the situation where the left node is at the correct or too coarse a + // granularity for reflection. (That is, further dereferences up the tree will be + // skipped.) Earlier dereferences, down the tree, will be handled + // at the same time, and logged to prevent reprocessing as the tree is traversed. + // + // Note: Other things like the following must be caught elsewhere: + // - a simple non-array, non-struct variable (no dereference even conceivable) + // - an aggregrate consumed en masse, without a dereference + // + // So, this code is for cases like + // - a struct/block dereferencing a member (whether the member is array or not) + // - an array of struct + // - structs/arrays containing the above + // + void addDereferencedUniform(TIntermBinary* topNode) + { + // See if too fine-grained to process (wait to get further down the tree) + const TType& leftType = topNode->getLeft()->getType(); + if ((leftType.isVector() || leftType.isMatrix()) && ! leftType.isArray()) + return; + + // We have an array or structure or block dereference, see if it's a uniform + // based dereference (if not, skip it). + TIntermSymbol* base = findBase(topNode); + if (! base || ! base->getQualifier().isUniformOrBuffer()) + return; + + // See if we've already processed this (e.g., in the middle of something + // we did earlier), and if so skip it + if (processedDerefs.find(topNode) != processedDerefs.end()) + return; + + // Process this uniform dereference + + int offset = -1; + int blockIndex = -1; + bool anonymous = false; + + // See if we need to record the block itself + bool block = base->getBasicType() == EbtBlock; + if (block) { + offset = 0; + anonymous = IsAnonymous(base->getName()); + + const TString& blockName = base->getType().getTypeName(); + TString baseName; + + if (! anonymous) + baseName = blockName; + + if (base->getType().isArray()) { + TType derefType(base->getType(), 0); + + assert(! anonymous); + for (int e = 0; e < base->getType().getCumulativeArraySize(); ++e) + blockIndex = addBlockName(blockName + "[" + String(e) + "]", derefType, + intermediate.getBlockSize(base->getType())); + baseName.append(TString("[0]")); + } else + blockIndex = addBlockName(blockName, base->getType(), intermediate.getBlockSize(base->getType())); + + if (reflection.options & EShReflectionAllBlockVariables) { + // Use a degenerate (empty) set of dereferences to immediately put as at the end of + // the dereference change expected by blowUpActiveAggregate. + TList derefs; + + // because we don't have any derefs, the first thing blowUpActiveAggregate will do is iterate over each + // member in the struct definition. This will lose any information about whether the parent was a buffer + // block. So if we're using strict array rules which don't expand the first child of a buffer block we + // instead iterate over the children here. + const bool strictArraySuffix = (reflection.options & EShReflectionStrictArraySuffix); + bool blockParent = (base->getType().getBasicType() == EbtBlock && base->getQualifier().storage == EvqBuffer); + + if (strictArraySuffix && blockParent) { + TType structDerefType(base->getType(), 0); + + const TType &structType = base->getType().isArray() ? structDerefType : base->getType(); + const TTypeList& typeList = *structType.getStruct(); + + TVector memberOffsets; + + memberOffsets.resize(typeList.size()); + getOffsets(structType, memberOffsets); + + for (int i = 0; i < (int)typeList.size(); ++i) { + TType derefType(structType, i); + TString name = baseName; + if (name.size() > 0) + name.append("."); + name.append(typeList[i].type->getFieldName()); + + // if this member is an array, store the top-level array stride but start the explosion from + // the inner struct type. + if (derefType.isArray() && derefType.isStruct()) { + name.append("[0]"); + blowUpActiveAggregate(TType(derefType, 0), name, derefs, derefs.end(), memberOffsets[i], + blockIndex, 0, getArrayStride(structType, derefType), + base->getQualifier().storage, false); + } else { + blowUpActiveAggregate(derefType, name, derefs, derefs.end(), memberOffsets[i], blockIndex, + 0, 0, base->getQualifier().storage, false); + } + } + } else { + // otherwise - if we're not using strict array suffix rules, or this isn't a block so we are + // expanding root arrays anyway, just start the iteration from the base block type. + blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.end(), 0, blockIndex, 0, 0, + base->getQualifier().storage, false); + } + } + } + + // Process the dereference chain, backward, accumulating the pieces for later forward traversal. + // If the topNode is a reflection-granularity-array dereference, don't include that last dereference. + TList derefs; + for (TIntermBinary* visitNode = topNode; visitNode; visitNode = visitNode->getLeft()->getAsBinaryNode()) { + if (isReflectionGranularity(visitNode->getLeft()->getType())) + continue; + + derefs.push_front(visitNode); + processedDerefs.insert(visitNode); + } + processedDerefs.insert(base); + + // See if we have a specific array size to stick to while enumerating the explosion of the aggregate + int arraySize = 0; + if (isReflectionGranularity(topNode->getLeft()->getType()) && topNode->getLeft()->isArray()) { + if (topNode->getOp() == EOpIndexDirect) + arraySize = topNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst() + 1; + } + + // Put the dereference chain together, forward + TString baseName; + if (! anonymous) { + if (block) + baseName = base->getType().getTypeName(); + else + baseName = base->getName(); + } + blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.begin(), offset, blockIndex, arraySize, 0, + base->getQualifier().storage, true); + } + + int addBlockName(const TString& name, const TType& type, int size) + { + TReflection::TMapIndexToReflection& blocks = reflection.GetBlockMapForStorage(type.getQualifier().storage); + + int blockIndex; + TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str()); + if (reflection.nameToIndex.find(name.c_str()) == reflection.nameToIndex.end()) { + blockIndex = (int)blocks.size(); + reflection.nameToIndex[name.c_str()] = blockIndex; + blocks.push_back(TObjectReflection(name.c_str(), type, -1, -1, size, -1)); + + blocks.back().numMembers = countAggregateMembers(type); + + EShLanguageMask& stages = blocks.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } else { + blockIndex = it->second; + + EShLanguageMask& stages = blocks[blockIndex].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + + return blockIndex; + } + + // Are we at a level in a dereference chain at which individual active uniform queries are made? + bool isReflectionGranularity(const TType& type) + { + return type.getBasicType() != EbtBlock && type.getBasicType() != EbtStruct && !type.isArrayOfArrays(); + } + + // For a binary operation indexing into an aggregate, chase down the base of the aggregate. + // Return 0 if the topology does not fit this situation. + TIntermSymbol* findBase(const TIntermBinary* node) + { + TIntermSymbol *base = node->getLeft()->getAsSymbolNode(); + if (base) + return base; + TIntermBinary* left = node->getLeft()->getAsBinaryNode(); + if (! left) + return nullptr; + + return findBase(left); + } + + // + // Translate a glslang sampler type into the GL API #define number. + // + int mapSamplerToGlType(TSampler sampler) + { + if (! sampler.image) { + // a sampler... + switch (sampler.type) { + case EbtFloat: + switch ((int)sampler.dim) { + case Esd1D: + switch ((int)sampler.shadow) { + case false: return sampler.arrayed ? GL_SAMPLER_1D_ARRAY : GL_SAMPLER_1D; + case true: return sampler.arrayed ? GL_SAMPLER_1D_ARRAY_SHADOW : GL_SAMPLER_1D_SHADOW; + } + case Esd2D: + switch ((int)sampler.ms) { + case false: + switch ((int)sampler.shadow) { + case false: return sampler.arrayed ? GL_SAMPLER_2D_ARRAY : GL_SAMPLER_2D; + case true: return sampler.arrayed ? GL_SAMPLER_2D_ARRAY_SHADOW : GL_SAMPLER_2D_SHADOW; + } + case true: return sampler.arrayed ? GL_SAMPLER_2D_MULTISAMPLE_ARRAY : GL_SAMPLER_2D_MULTISAMPLE; + } + case Esd3D: + return GL_SAMPLER_3D; + case EsdCube: + switch ((int)sampler.shadow) { + case false: return sampler.arrayed ? GL_SAMPLER_CUBE_MAP_ARRAY : GL_SAMPLER_CUBE; + case true: return sampler.arrayed ? GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW : GL_SAMPLER_CUBE_SHADOW; + } + case EsdRect: + return sampler.shadow ? GL_SAMPLER_2D_RECT_SHADOW : GL_SAMPLER_2D_RECT; + case EsdBuffer: + return GL_SAMPLER_BUFFER; + } + case EbtFloat16: + switch ((int)sampler.dim) { + case Esd1D: + switch ((int)sampler.shadow) { + case false: return sampler.arrayed ? GL_FLOAT16_SAMPLER_1D_ARRAY_AMD : GL_FLOAT16_SAMPLER_1D_AMD; + case true: return sampler.arrayed ? GL_FLOAT16_SAMPLER_1D_ARRAY_SHADOW_AMD : GL_FLOAT16_SAMPLER_1D_SHADOW_AMD; + } + case Esd2D: + switch ((int)sampler.ms) { + case false: + switch ((int)sampler.shadow) { + case false: return sampler.arrayed ? GL_FLOAT16_SAMPLER_2D_ARRAY_AMD : GL_FLOAT16_SAMPLER_2D_AMD; + case true: return sampler.arrayed ? GL_FLOAT16_SAMPLER_2D_ARRAY_SHADOW_AMD : GL_FLOAT16_SAMPLER_2D_SHADOW_AMD; + } + case true: return sampler.arrayed ? GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_ARRAY_AMD : GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_AMD; + } + case Esd3D: + return GL_FLOAT16_SAMPLER_3D_AMD; + case EsdCube: + switch ((int)sampler.shadow) { + case false: return sampler.arrayed ? GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_AMD : GL_FLOAT16_SAMPLER_CUBE_AMD; + case true: return sampler.arrayed ? GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_SHADOW_AMD : GL_FLOAT16_SAMPLER_CUBE_SHADOW_AMD; + } + case EsdRect: + return sampler.shadow ? GL_FLOAT16_SAMPLER_2D_RECT_SHADOW_AMD : GL_FLOAT16_SAMPLER_2D_RECT_AMD; + case EsdBuffer: + return GL_FLOAT16_SAMPLER_BUFFER_AMD; + } + case EbtInt: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_INT_SAMPLER_1D_ARRAY : GL_INT_SAMPLER_1D; + case Esd2D: + switch ((int)sampler.ms) { + case false: return sampler.arrayed ? GL_INT_SAMPLER_2D_ARRAY : GL_INT_SAMPLER_2D; + case true: return sampler.arrayed ? GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY + : GL_INT_SAMPLER_2D_MULTISAMPLE; + } + case Esd3D: + return GL_INT_SAMPLER_3D; + case EsdCube: + return sampler.arrayed ? GL_INT_SAMPLER_CUBE_MAP_ARRAY : GL_INT_SAMPLER_CUBE; + case EsdRect: + return GL_INT_SAMPLER_2D_RECT; + case EsdBuffer: + return GL_INT_SAMPLER_BUFFER; + } + case EbtUint: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_1D_ARRAY : GL_UNSIGNED_INT_SAMPLER_1D; + case Esd2D: + switch ((int)sampler.ms) { + case false: return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_2D_ARRAY : GL_UNSIGNED_INT_SAMPLER_2D; + case true: return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY + : GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE; + } + case Esd3D: + return GL_UNSIGNED_INT_SAMPLER_3D; + case EsdCube: + return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY : GL_UNSIGNED_INT_SAMPLER_CUBE; + case EsdRect: + return GL_UNSIGNED_INT_SAMPLER_2D_RECT; + case EsdBuffer: + return GL_UNSIGNED_INT_SAMPLER_BUFFER; + } + default: + return 0; + } + } else { + // an image... + switch (sampler.type) { + case EbtFloat: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_IMAGE_1D_ARRAY : GL_IMAGE_1D; + case Esd2D: + switch ((int)sampler.ms) { + case false: return sampler.arrayed ? GL_IMAGE_2D_ARRAY : GL_IMAGE_2D; + case true: return sampler.arrayed ? GL_IMAGE_2D_MULTISAMPLE_ARRAY : GL_IMAGE_2D_MULTISAMPLE; + } + case Esd3D: + return GL_IMAGE_3D; + case EsdCube: + return sampler.arrayed ? GL_IMAGE_CUBE_MAP_ARRAY : GL_IMAGE_CUBE; + case EsdRect: + return GL_IMAGE_2D_RECT; + case EsdBuffer: + return GL_IMAGE_BUFFER; + } + case EbtFloat16: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_FLOAT16_IMAGE_1D_ARRAY_AMD : GL_FLOAT16_IMAGE_1D_AMD; + case Esd2D: + switch ((int)sampler.ms) { + case false: return sampler.arrayed ? GL_FLOAT16_IMAGE_2D_ARRAY_AMD : GL_FLOAT16_IMAGE_2D_AMD; + case true: return sampler.arrayed ? GL_FLOAT16_IMAGE_2D_MULTISAMPLE_ARRAY_AMD : GL_FLOAT16_IMAGE_2D_MULTISAMPLE_AMD; + } + case Esd3D: + return GL_FLOAT16_IMAGE_3D_AMD; + case EsdCube: + return sampler.arrayed ? GL_FLOAT16_IMAGE_CUBE_MAP_ARRAY_AMD : GL_FLOAT16_IMAGE_CUBE_AMD; + case EsdRect: + return GL_FLOAT16_IMAGE_2D_RECT_AMD; + case EsdBuffer: + return GL_FLOAT16_IMAGE_BUFFER_AMD; + } + case EbtInt: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_INT_IMAGE_1D_ARRAY : GL_INT_IMAGE_1D; + case Esd2D: + switch ((int)sampler.ms) { + case false: return sampler.arrayed ? GL_INT_IMAGE_2D_ARRAY : GL_INT_IMAGE_2D; + case true: return sampler.arrayed ? GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY : GL_INT_IMAGE_2D_MULTISAMPLE; + } + case Esd3D: + return GL_INT_IMAGE_3D; + case EsdCube: + return sampler.arrayed ? GL_INT_IMAGE_CUBE_MAP_ARRAY : GL_INT_IMAGE_CUBE; + case EsdRect: + return GL_INT_IMAGE_2D_RECT; + case EsdBuffer: + return GL_INT_IMAGE_BUFFER; + } + case EbtUint: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_1D_ARRAY : GL_UNSIGNED_INT_IMAGE_1D; + case Esd2D: + switch ((int)sampler.ms) { + case false: return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_2D_ARRAY : GL_UNSIGNED_INT_IMAGE_2D; + case true: return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY + : GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE; + } + case Esd3D: + return GL_UNSIGNED_INT_IMAGE_3D; + case EsdCube: + return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY : GL_UNSIGNED_INT_IMAGE_CUBE; + case EsdRect: + return GL_UNSIGNED_INT_IMAGE_2D_RECT; + case EsdBuffer: + return GL_UNSIGNED_INT_IMAGE_BUFFER; + } + default: + return 0; + } + } + } + + // + // Translate a glslang type into the GL API #define number. + // Ignores arrayness. + // + int mapToGlType(const TType& type) + { + switch (type.getBasicType()) { + case EbtSampler: + return mapSamplerToGlType(type.getSampler()); + case EbtStruct: + case EbtBlock: + case EbtVoid: + return 0; + default: + break; + } + + if (type.isVector()) { + int offset = type.getVectorSize() - 2; + switch (type.getBasicType()) { + case EbtFloat: return GL_FLOAT_VEC2 + offset; + case EbtDouble: return GL_DOUBLE_VEC2 + offset; + case EbtFloat16: return GL_FLOAT16_VEC2_NV + offset; + case EbtInt: return GL_INT_VEC2 + offset; + case EbtUint: return GL_UNSIGNED_INT_VEC2 + offset; + case EbtInt64: return GL_INT64_ARB + offset; + case EbtUint64: return GL_UNSIGNED_INT64_ARB + offset; + case EbtBool: return GL_BOOL_VEC2 + offset; + case EbtAtomicUint: return GL_UNSIGNED_INT_ATOMIC_COUNTER + offset; + default: return 0; + } + } + if (type.isMatrix()) { + switch (type.getBasicType()) { + case EbtFloat: + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT_MAT2; + case 3: return GL_FLOAT_MAT2x3; + case 4: return GL_FLOAT_MAT2x4; + default: return 0; + } + case 3: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT_MAT3x2; + case 3: return GL_FLOAT_MAT3; + case 4: return GL_FLOAT_MAT3x4; + default: return 0; + } + case 4: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT_MAT4x2; + case 3: return GL_FLOAT_MAT4x3; + case 4: return GL_FLOAT_MAT4; + default: return 0; + } + } + case EbtDouble: + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: return GL_DOUBLE_MAT2; + case 3: return GL_DOUBLE_MAT2x3; + case 4: return GL_DOUBLE_MAT2x4; + default: return 0; + } + case 3: + switch (type.getMatrixRows()) { + case 2: return GL_DOUBLE_MAT3x2; + case 3: return GL_DOUBLE_MAT3; + case 4: return GL_DOUBLE_MAT3x4; + default: return 0; + } + case 4: + switch (type.getMatrixRows()) { + case 2: return GL_DOUBLE_MAT4x2; + case 3: return GL_DOUBLE_MAT4x3; + case 4: return GL_DOUBLE_MAT4; + default: return 0; + } + } + case EbtFloat16: + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT16_MAT2_AMD; + case 3: return GL_FLOAT16_MAT2x3_AMD; + case 4: return GL_FLOAT16_MAT2x4_AMD; + default: return 0; + } + case 3: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT16_MAT3x2_AMD; + case 3: return GL_FLOAT16_MAT3_AMD; + case 4: return GL_FLOAT16_MAT3x4_AMD; + default: return 0; + } + case 4: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT16_MAT4x2_AMD; + case 3: return GL_FLOAT16_MAT4x3_AMD; + case 4: return GL_FLOAT16_MAT4_AMD; + default: return 0; + } + } + default: + return 0; + } + } + if (type.getVectorSize() == 1) { + switch (type.getBasicType()) { + case EbtFloat: return GL_FLOAT; + case EbtDouble: return GL_DOUBLE; + case EbtFloat16: return GL_FLOAT16_NV; + case EbtInt: return GL_INT; + case EbtUint: return GL_UNSIGNED_INT; + case EbtInt64: return GL_INT64_ARB; + case EbtUint64: return GL_UNSIGNED_INT64_ARB; + case EbtBool: return GL_BOOL; + case EbtAtomicUint: return GL_UNSIGNED_INT_ATOMIC_COUNTER; + default: return 0; + } + } + + return 0; + } + + int mapToGlArraySize(const TType& type) + { + return type.isArray() ? type.getOuterArraySize() : 1; + } + + TReflection& reflection; + std::set processedDerefs; + +protected: + TReflectionTraverser(TReflectionTraverser&); + TReflectionTraverser& operator=(TReflectionTraverser&); +}; + +// +// Implement the traversal functions of interest. +// + +// To catch dereferenced aggregates that must be reflected. +// This catches them at the highest level possible in the tree. +bool TReflectionTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node) +{ + switch (node->getOp()) { + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + addDereferencedUniform(node); + break; + default: + break; + } + + // still need to visit everything below, which could contain sub-expressions + // containing different uniforms + return true; +} + +// To reflect non-dereferenced objects. +void TReflectionTraverser::visitSymbol(TIntermSymbol* base) +{ + if (base->getQualifier().storage == EvqUniform) + addUniform(*base); + + if ((intermediate.getStage() == reflection.firstStage && base->getQualifier().isPipeInput()) || + (intermediate.getStage() == reflection.lastStage && base->getQualifier().isPipeOutput())) + addPipeIOVariable(*base); +} + +// +// Implement TObjectReflection methods. +// + +TObjectReflection::TObjectReflection(const std::string &pName, const TType &pType, int pOffset, int pGLDefineType, + int pSize, int pIndex) + : name(pName), offset(pOffset), glDefineType(pGLDefineType), size(pSize), index(pIndex), counterIndex(-1), + numMembers(-1), arrayStride(0), topLevelArrayStride(0), stages(EShLanguageMask(0)), type(pType.clone()) +{ +} + +int TObjectReflection::getBinding() const +{ + if (type == nullptr || !type->getQualifier().hasBinding()) + return -1; + return type->getQualifier().layoutBinding; +} + +void TObjectReflection::dump() const +{ + printf("%s: offset %d, type %x, size %d, index %d, binding %d, stages %d", name.c_str(), offset, glDefineType, size, + index, getBinding(), stages); + + if (counterIndex != -1) + printf(", counter %d", counterIndex); + + if (numMembers != -1) + printf(", numMembers %d", numMembers); + + if (arrayStride != 0) + printf(", arrayStride %d", arrayStride); + + if (topLevelArrayStride != 0) + printf(", topLevelArrayStride %d", topLevelArrayStride); + + printf("\n"); +} + +// +// Implement TReflection methods. +// + +// Track any required attribute reflection, such as compute shader numthreads. +// +void TReflection::buildAttributeReflection(EShLanguage stage, const TIntermediate& intermediate) +{ + if (stage == EShLangCompute) { + // Remember thread dimensions + for (int dim=0; dim<3; ++dim) + localSize[dim] = intermediate.getLocalSize(dim); + } +} + +// build counter block index associations for buffers +void TReflection::buildCounterIndices(const TIntermediate& intermediate) +{ +#ifdef ENABLE_HLSL + // search for ones that have counters + for (int i = 0; i < int(indexToUniformBlock.size()); ++i) { + const TString counterName(intermediate.addCounterBufferName(indexToUniformBlock[i].name).c_str()); + const int index = getIndex(counterName); + + if (index >= 0) + indexToUniformBlock[i].counterIndex = index; + } +#endif +} + +// build Shader Stages mask for all uniforms +void TReflection::buildUniformStageMask(const TIntermediate& intermediate) +{ + if (options & EShReflectionAllBlockVariables) + return; + + for (int i = 0; i < int(indexToUniform.size()); ++i) { + indexToUniform[i].stages = static_cast(indexToUniform[i].stages | 1 << intermediate.getStage()); + } + + for (int i = 0; i < int(indexToBufferVariable.size()); ++i) { + indexToBufferVariable[i].stages = + static_cast(indexToBufferVariable[i].stages | 1 << intermediate.getStage()); + } +} + +// Merge live symbols from 'intermediate' into the existing reflection database. +// +// Returns false if the input is too malformed to do this. +bool TReflection::addStage(EShLanguage stage, const TIntermediate& intermediate) +{ + if (intermediate.getTreeRoot() == nullptr || + intermediate.getNumEntryPoints() != 1 || + intermediate.isRecursive()) + return false; + + buildAttributeReflection(stage, intermediate); + + TReflectionTraverser it(intermediate, *this); + + // put the entry point on the list of functions to process + it.pushFunction(intermediate.getEntryPointMangledName().c_str()); + + // process all the functions + while (! it.functions.empty()) { + TIntermNode* function = it.functions.back(); + it.functions.pop_back(); + function->traverse(&it); + } + + buildCounterIndices(intermediate); + buildUniformStageMask(intermediate); + + return true; +} + +void TReflection::dump() +{ + printf("Uniform reflection:\n"); + for (size_t i = 0; i < indexToUniform.size(); ++i) + indexToUniform[i].dump(); + printf("\n"); + + printf("Uniform block reflection:\n"); + for (size_t i = 0; i < indexToUniformBlock.size(); ++i) + indexToUniformBlock[i].dump(); + printf("\n"); + + printf("Buffer variable reflection:\n"); + for (size_t i = 0; i < indexToBufferVariable.size(); ++i) + indexToBufferVariable[i].dump(); + printf("\n"); + + printf("Buffer block reflection:\n"); + for (size_t i = 0; i < indexToBufferBlock.size(); ++i) + indexToBufferBlock[i].dump(); + printf("\n"); + + printf("Pipeline input reflection:\n"); + for (size_t i = 0; i < indexToPipeInput.size(); ++i) + indexToPipeInput[i].dump(); + printf("\n"); + + printf("Pipeline output reflection:\n"); + for (size_t i = 0; i < indexToPipeOutput.size(); ++i) + indexToPipeOutput[i].dump(); + printf("\n"); + + if (getLocalSize(0) > 1) { + static const char* axis[] = { "X", "Y", "Z" }; + + for (int dim=0; dim<3; ++dim) + if (getLocalSize(dim) > 1) + printf("Local size %s: %u\n", axis[dim], getLocalSize(dim)); + + printf("\n"); + } + + // printf("Live names\n"); + // for (TNameToIndex::const_iterator it = nameToIndex.begin(); it != nameToIndex.end(); ++it) + // printf("%s: %d\n", it->first.c_str(), it->second); + // printf("\n"); +} + +} // end namespace glslang + +#endif // GLSLANG_WEB diff --git a/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/reflection.h b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/reflection.h new file mode 100644 index 000000000..0c33de459 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/MachineIndependent/reflection.h @@ -0,0 +1,223 @@ +// +// Copyright (C) 2013-2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef GLSLANG_WEB + +#ifndef _REFLECTION_INCLUDED +#define _REFLECTION_INCLUDED + +#include "../Public/ShaderLang.h" +#include "../Include/Types.h" + +#include +#include + +// +// A reflection database and its interface, consistent with the OpenGL API reflection queries. +// + +namespace glslang { + +class TIntermediate; +class TIntermAggregate; +class TReflectionTraverser; + +// The full reflection database +class TReflection { +public: + TReflection(EShReflectionOptions opts, EShLanguage first, EShLanguage last) + : options(opts), firstStage(first), lastStage(last), badReflection(TObjectReflection::badReflection()) + { + for (int dim=0; dim<3; ++dim) + localSize[dim] = 0; + } + + virtual ~TReflection() {} + + // grow the reflection stage by stage + bool addStage(EShLanguage, const TIntermediate&); + + // for mapping a uniform index to a uniform object's description + int getNumUniforms() { return (int)indexToUniform.size(); } + const TObjectReflection& getUniform(int i) const + { + if (i >= 0 && i < (int)indexToUniform.size()) + return indexToUniform[i]; + else + return badReflection; + } + + // for mapping a block index to the block's description + int getNumUniformBlocks() const { return (int)indexToUniformBlock.size(); } + const TObjectReflection& getUniformBlock(int i) const + { + if (i >= 0 && i < (int)indexToUniformBlock.size()) + return indexToUniformBlock[i]; + else + return badReflection; + } + + // for mapping an pipeline input index to the input's description + int getNumPipeInputs() { return (int)indexToPipeInput.size(); } + const TObjectReflection& getPipeInput(int i) const + { + if (i >= 0 && i < (int)indexToPipeInput.size()) + return indexToPipeInput[i]; + else + return badReflection; + } + + // for mapping an pipeline output index to the output's description + int getNumPipeOutputs() { return (int)indexToPipeOutput.size(); } + const TObjectReflection& getPipeOutput(int i) const + { + if (i >= 0 && i < (int)indexToPipeOutput.size()) + return indexToPipeOutput[i]; + else + return badReflection; + } + + // for mapping from an atomic counter to the uniform index + int getNumAtomicCounters() const { return (int)atomicCounterUniformIndices.size(); } + const TObjectReflection& getAtomicCounter(int i) const + { + if (i >= 0 && i < (int)atomicCounterUniformIndices.size()) + return getUniform(atomicCounterUniformIndices[i]); + else + return badReflection; + } + + // for mapping a buffer variable index to a buffer variable object's description + int getNumBufferVariables() { return (int)indexToBufferVariable.size(); } + const TObjectReflection& getBufferVariable(int i) const + { + if (i >= 0 && i < (int)indexToBufferVariable.size()) + return indexToBufferVariable[i]; + else + return badReflection; + } + + // for mapping a storage block index to the storage block's description + int getNumStorageBuffers() const { return (int)indexToBufferBlock.size(); } + const TObjectReflection& getStorageBufferBlock(int i) const + { + if (i >= 0 && i < (int)indexToBufferBlock.size()) + return indexToBufferBlock[i]; + else + return badReflection; + } + + // for mapping any name to its index (block names, uniform names and input/output names) + int getIndex(const char* name) const + { + TNameToIndex::const_iterator it = nameToIndex.find(name); + if (it == nameToIndex.end()) + return -1; + else + return it->second; + } + + // see getIndex(const char*) + int getIndex(const TString& name) const { return getIndex(name.c_str()); } + + + // for mapping any name to its index (only pipe input/output names) + int getPipeIOIndex(const char* name, const bool inOrOut) const + { + TNameToIndex::const_iterator it = inOrOut ? pipeInNameToIndex.find(name) : pipeOutNameToIndex.find(name); + if (it == (inOrOut ? pipeInNameToIndex.end() : pipeOutNameToIndex.end())) + return -1; + else + return it->second; + } + + // see gePipeIOIndex(const char*, const bool) + int getPipeIOIndex(const TString& name, const bool inOrOut) const { return getPipeIOIndex(name.c_str(), inOrOut); } + + // Thread local size + unsigned getLocalSize(int dim) const { return dim <= 2 ? localSize[dim] : 0; } + + void dump(); + +protected: + friend class glslang::TReflectionTraverser; + + void buildCounterIndices(const TIntermediate&); + void buildUniformStageMask(const TIntermediate& intermediate); + void buildAttributeReflection(EShLanguage, const TIntermediate&); + + // Need a TString hash: typedef std::unordered_map TNameToIndex; + typedef std::map TNameToIndex; + typedef std::vector TMapIndexToReflection; + typedef std::vector TIndices; + + TMapIndexToReflection& GetBlockMapForStorage(TStorageQualifier storage) + { + if ((options & EShReflectionSeparateBuffers) && storage == EvqBuffer) + return indexToBufferBlock; + return indexToUniformBlock; + } + TMapIndexToReflection& GetVariableMapForStorage(TStorageQualifier storage) + { + if ((options & EShReflectionSeparateBuffers) && storage == EvqBuffer) + return indexToBufferVariable; + return indexToUniform; + } + + EShReflectionOptions options; + + EShLanguage firstStage; + EShLanguage lastStage; + + TObjectReflection badReflection; // return for queries of -1 or generally out of range; has expected descriptions with in it for this + TNameToIndex nameToIndex; // maps names to indexes; can hold all types of data: uniform/buffer and which function names have been processed + TNameToIndex pipeInNameToIndex; // maps pipe in names to indexes, this is a fix to seperate pipe I/O from uniforms and buffers. + TNameToIndex pipeOutNameToIndex; // maps pipe out names to indexes, this is a fix to seperate pipe I/O from uniforms and buffers. + TMapIndexToReflection indexToUniform; + TMapIndexToReflection indexToUniformBlock; + TMapIndexToReflection indexToBufferVariable; + TMapIndexToReflection indexToBufferBlock; + TMapIndexToReflection indexToPipeInput; + TMapIndexToReflection indexToPipeOutput; + TIndices atomicCounterUniformIndices; + + unsigned int localSize[3]; +}; + +} // end namespace glslang + +#endif // _REFLECTION_INCLUDED + +#endif // GLSLANG_WEB diff --git a/armorcore/tools/to_spirv/glslang/glslang/OSDependent/Unix/ossource.cpp b/armorcore/tools/to_spirv/glslang/glslang/OSDependent/Unix/ossource.cpp new file mode 100644 index 000000000..3f029f023 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/OSDependent/Unix/ossource.cpp @@ -0,0 +1,207 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// This file contains the Linux-specific functions +// +#include "../osinclude.h" +#include "../../../OGLCompilersDLL/InitializeDll.h" + +#include +#include +#include +#include +#include +#include +#include + +#if !defined(__Fuchsia__) +#include +#endif + +namespace glslang { + +// +// Thread cleanup +// + +// +// Wrapper for Linux call to DetachThread. This is required as pthread_cleanup_push() expects +// the cleanup routine to return void. +// +static void DetachThreadLinux(void *) +{ + DetachThread(); +} + +// +// Registers cleanup handler, sets cancel type and state, and executes the thread specific +// cleanup handler. This function will be called in the Standalone.cpp for regression +// testing. When OpenGL applications are run with the driver code, Linux OS does the +// thread cleanup. +// +void OS_CleanupThreadData(void) +{ +#if defined(__ANDROID__) || defined(__Fuchsia__) + DetachThreadLinux(NULL); +#else + int old_cancel_state, old_cancel_type; + void *cleanupArg = NULL; + + // + // Set thread cancel state and push cleanup handler. + // + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancel_state); + pthread_cleanup_push(DetachThreadLinux, (void *) cleanupArg); + + // + // Put the thread in deferred cancellation mode. + // + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old_cancel_type); + + // + // Pop cleanup handler and execute it prior to unregistering the cleanup handler. + // + pthread_cleanup_pop(1); + + // + // Restore the thread's previous cancellation mode. + // + pthread_setcanceltype(old_cancel_state, NULL); +#endif +} + +// +// Thread Local Storage Operations +// +inline OS_TLSIndex PthreadKeyToTLSIndex(pthread_key_t key) +{ + return (OS_TLSIndex)((uintptr_t)key + 1); +} + +inline pthread_key_t TLSIndexToPthreadKey(OS_TLSIndex nIndex) +{ + return (pthread_key_t)((uintptr_t)nIndex - 1); +} + +OS_TLSIndex OS_AllocTLSIndex() +{ + pthread_key_t pPoolIndex; + + // + // Create global pool key. + // + if ((pthread_key_create(&pPoolIndex, NULL)) != 0) { + assert(0 && "OS_AllocTLSIndex(): Unable to allocate Thread Local Storage"); + return OS_INVALID_TLS_INDEX; + } + else + return PthreadKeyToTLSIndex(pPoolIndex); +} + +bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue) +{ + if (nIndex == OS_INVALID_TLS_INDEX) { + assert(0 && "OS_SetTLSValue(): Invalid TLS Index"); + return false; + } + + if (pthread_setspecific(TLSIndexToPthreadKey(nIndex), lpvValue) == 0) + return true; + else + return false; +} + +void* OS_GetTLSValue(OS_TLSIndex nIndex) +{ + // + // This function should return 0 if nIndex is invalid. + // + assert(nIndex != OS_INVALID_TLS_INDEX); + return pthread_getspecific(TLSIndexToPthreadKey(nIndex)); +} + +bool OS_FreeTLSIndex(OS_TLSIndex nIndex) +{ + if (nIndex == OS_INVALID_TLS_INDEX) { + assert(0 && "OS_SetTLSValue(): Invalid TLS Index"); + return false; + } + + // + // Delete the global pool key. + // + if (pthread_key_delete(TLSIndexToPthreadKey(nIndex)) == 0) + return true; + else + return false; +} + +namespace { + pthread_mutex_t gMutex; +} + +void InitGlobalLock() +{ + pthread_mutexattr_t mutexattr; + pthread_mutexattr_init(&mutexattr); + pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&gMutex, &mutexattr); +} + +void GetGlobalLock() +{ + pthread_mutex_lock(&gMutex); +} + +void ReleaseGlobalLock() +{ + pthread_mutex_unlock(&gMutex); +} + +// #define DUMP_COUNTERS + +void OS_DumpMemoryCounters() +{ +#ifdef DUMP_COUNTERS + struct rusage usage; + + if (getrusage(RUSAGE_SELF, &usage) == 0) + printf("Working set size: %ld\n", usage.ru_maxrss * 1024); +#else + printf("Recompile with DUMP_COUNTERS defined to see counters.\n"); +#endif +} + +} // end namespace glslang diff --git a/armorcore/tools/to_spirv/glslang/glslang/OSDependent/osinclude.h b/armorcore/tools/to_spirv/glslang/glslang/OSDependent/osinclude.h new file mode 100644 index 000000000..218abe4f2 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/OSDependent/osinclude.h @@ -0,0 +1,63 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef __OSINCLUDE_H +#define __OSINCLUDE_H + +namespace glslang { + +// +// Thread Local Storage Operations +// +typedef void* OS_TLSIndex; +#define OS_INVALID_TLS_INDEX ((void*)0) + +OS_TLSIndex OS_AllocTLSIndex(); +bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue); +bool OS_FreeTLSIndex(OS_TLSIndex nIndex); +void* OS_GetTLSValue(OS_TLSIndex nIndex); + +void InitGlobalLock(); +void GetGlobalLock(); +void ReleaseGlobalLock(); + +typedef unsigned int (*TThreadEntrypoint)(void*); + +void OS_CleanupThreadData(void); + +void OS_DumpMemoryCounters(); + +} // end namespace glslang + +#endif // __OSINCLUDE_H diff --git a/armorcore/tools/to_spirv/glslang/glslang/Public/ShaderLang.h b/armorcore/tools/to_spirv/glslang/glslang/Public/ShaderLang.h new file mode 100755 index 000000000..acb2a0785 --- /dev/null +++ b/armorcore/tools/to_spirv/glslang/glslang/Public/ShaderLang.h @@ -0,0 +1,927 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013-2016 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef _COMPILER_INTERFACE_INCLUDED_ +#define _COMPILER_INTERFACE_INCLUDED_ + +#include "../Include/ResourceLimits.h" +#include "../MachineIndependent/Versions.h" + +#include +#include + +#ifdef _WIN32 +#define C_DECL __cdecl +//#ifdef SH_EXPORTING +// #define SH_IMPORT_EXPORT __declspec(dllexport) +//#else +// #define SH_IMPORT_EXPORT __declspec(dllimport) +//#endif +#define SH_IMPORT_EXPORT +#else +#define SH_IMPORT_EXPORT +#define C_DECL +#endif + +// +// This is the platform independent interface between an OGL driver +// and the shading language compiler/linker. +// + +#ifdef __cplusplus + extern "C" { +#endif + +// This should always increase, as some paths to do not consume +// a more major number. +// It should increment by one when new functionality is added. +#define GLSLANG_MINOR_VERSION 13 + +// +// Call before doing any other compiler/linker operations. +// +// (Call once per process, not once per thread.) +// +SH_IMPORT_EXPORT int ShInitialize(); + +// +// Call this at process shutdown to clean up memory. +// +SH_IMPORT_EXPORT int ShFinalize(); + +// +// Types of languages the compiler can consume. +// +typedef enum { + EShLangVertex, + EShLangTessControl, + EShLangTessEvaluation, + EShLangGeometry, + EShLangFragment, + EShLangCompute, + EShLangRayGen, + EShLangRayGenNV = EShLangRayGen, + EShLangIntersect, + EShLangIntersectNV = EShLangIntersect, + EShLangAnyHit, + EShLangAnyHitNV = EShLangAnyHit, + EShLangClosestHit, + EShLangClosestHitNV = EShLangClosestHit, + EShLangMiss, + EShLangMissNV = EShLangMiss, + EShLangCallable, + EShLangCallableNV = EShLangCallable, + EShLangTaskNV, + EShLangMeshNV, + LAST_ELEMENT_MARKER(EShLangCount), +} EShLanguage; // would be better as stage, but this is ancient now + +typedef enum { + EShLangVertexMask = (1 << EShLangVertex), + EShLangTessControlMask = (1 << EShLangTessControl), + EShLangTessEvaluationMask = (1 << EShLangTessEvaluation), + EShLangGeometryMask = (1 << EShLangGeometry), + EShLangFragmentMask = (1 << EShLangFragment), + EShLangComputeMask = (1 << EShLangCompute), + EShLangRayGenMask = (1 << EShLangRayGen), + EShLangRayGenNVMask = EShLangRayGenMask, + EShLangIntersectMask = (1 << EShLangIntersect), + EShLangIntersectNVMask = EShLangIntersectMask, + EShLangAnyHitMask = (1 << EShLangAnyHit), + EShLangAnyHitNVMask = EShLangAnyHitMask, + EShLangClosestHitMask = (1 << EShLangClosestHit), + EShLangClosestHitNVMask = EShLangClosestHitMask, + EShLangMissMask = (1 << EShLangMiss), + EShLangMissNVMask = EShLangMissMask, + EShLangCallableMask = (1 << EShLangCallable), + EShLangCallableNVMask = EShLangCallableMask, + EShLangTaskNVMask = (1 << EShLangTaskNV), + EShLangMeshNVMask = (1 << EShLangMeshNV), + LAST_ELEMENT_MARKER(EShLanguageMaskCount), +} EShLanguageMask; + +namespace glslang { + +class TType; + +typedef enum { + EShSourceNone, + EShSourceGlsl, // GLSL, includes ESSL (OpenGL ES GLSL) + EShSourceHlsl, // HLSL + LAST_ELEMENT_MARKER(EShSourceCount), +} EShSource; // if EShLanguage were EShStage, this could be EShLanguage instead + +typedef enum { + EShClientNone, // use when there is no client, e.g. for validation + EShClientVulkan, + EShClientOpenGL, + LAST_ELEMENT_MARKER(EShClientCount), +} EShClient; + +typedef enum { + EShTargetNone, + EShTargetSpv, // SPIR-V (preferred spelling) + EshTargetSpv = EShTargetSpv, // legacy spelling + LAST_ELEMENT_MARKER(EShTargetCount), +} EShTargetLanguage; + +typedef enum { + EShTargetVulkan_1_0 = (1 << 22), // Vulkan 1.0 + EShTargetVulkan_1_1 = (1 << 22) | (1 << 12), // Vulkan 1.1 + EShTargetVulkan_1_2 = (1 << 22) | (2 << 12), // Vulkan 1.2 + EShTargetOpenGL_450 = 450, // OpenGL + LAST_ELEMENT_MARKER(EShTargetClientVersionCount), +} EShTargetClientVersion; + +typedef EShTargetClientVersion EshTargetClientVersion; + +typedef enum { + EShTargetSpv_1_0 = (1 << 16), // SPIR-V 1.0 + EShTargetSpv_1_1 = (1 << 16) | (1 << 8), // SPIR-V 1.1 + EShTargetSpv_1_2 = (1 << 16) | (2 << 8), // SPIR-V 1.2 + EShTargetSpv_1_3 = (1 << 16) | (3 << 8), // SPIR-V 1.3 + EShTargetSpv_1_4 = (1 << 16) | (4 << 8), // SPIR-V 1.4 + EShTargetSpv_1_5 = (1 << 16) | (5 << 8), // SPIR-V 1.5 + LAST_ELEMENT_MARKER(EShTargetLanguageVersionCount), +} EShTargetLanguageVersion; + +struct TInputLanguage { + EShSource languageFamily; // redundant information with other input, this one overrides when not EShSourceNone + EShLanguage stage; // redundant information with other input, this one overrides when not EShSourceNone + EShClient dialect; + int dialectVersion; // version of client's language definition, not the client (when not EShClientNone) +}; + +struct TClient { + EShClient client; + EShTargetClientVersion version; // version of client itself (not the client's input dialect) +}; + +struct TTarget { + EShTargetLanguage language; + EShTargetLanguageVersion version; // version to target, if SPIR-V, defined by "word 1" of the SPIR-V header + bool hlslFunctionality1; // can target hlsl_functionality1 extension(s) +}; + +// All source/client/target versions and settings. +// Can override previous methods of setting, when items are set here. +// Expected to grow, as more are added, rather than growing parameter lists. +struct TEnvironment { + TInputLanguage input; // definition of the input language + TClient client; // what client is the overall compilation being done for? + TTarget target; // what to generate +}; + +const char* StageName(EShLanguage); + +} // end namespace glslang + +// +// Types of output the linker will create. +// +typedef enum { + EShExVertexFragment, + EShExFragment +} EShExecutable; + +// +// Optimization level for the compiler. +// +typedef enum { + EShOptNoGeneration, + EShOptNone, + EShOptSimple, // Optimizations that can be done quickly + EShOptFull, // Optimizations that will take more time + LAST_ELEMENT_MARKER(EshOptLevelCount), +} EShOptimizationLevel; + +// +// Texture and Sampler transformation mode. +// +typedef enum { + EShTexSampTransKeep, // keep textures and samplers as is (default) + EShTexSampTransUpgradeTextureRemoveSampler, // change texture w/o embeded sampler into sampled texture and throw away all samplers + LAST_ELEMENT_MARKER(EShTexSampTransCount), +} EShTextureSamplerTransformMode; + +// +// Message choices for what errors and warnings are given. +// +enum EShMessages { + EShMsgDefault = 0, // default is to give all required errors and extra warnings + EShMsgRelaxedErrors = (1 << 0), // be liberal in accepting input + EShMsgSuppressWarnings = (1 << 1), // suppress all warnings, except those required by the specification + EShMsgAST = (1 << 2), // print the AST intermediate representation + EShMsgSpvRules = (1 << 3), // issue messages for SPIR-V generation + EShMsgVulkanRules = (1 << 4), // issue messages for Vulkan-requirements of GLSL for SPIR-V + EShMsgOnlyPreprocessor = (1 << 5), // only print out errors produced by the preprocessor + EShMsgReadHlsl = (1 << 6), // use HLSL parsing rules and semantics + EShMsgCascadingErrors = (1 << 7), // get cascading errors; risks error-recovery issues, instead of an early exit + EShMsgKeepUncalled = (1 << 8), // for testing, don't eliminate uncalled functions + EShMsgHlslOffsets = (1 << 9), // allow block offsets to follow HLSL rules instead of GLSL rules + EShMsgDebugInfo = (1 << 10), // save debug information + EShMsgHlslEnable16BitTypes = (1 << 11), // enable use of 16-bit types in SPIR-V for HLSL + EShMsgHlslLegalization = (1 << 12), // enable HLSL Legalization messages + EShMsgHlslDX9Compatible = (1 << 13), // enable HLSL DX9 compatible mode (right now only for samplers) + EShMsgBuiltinSymbolTable = (1 << 14), // print the builtin symbol table + LAST_ELEMENT_MARKER(EShMsgCount), +}; + +// +// Options for building reflection +// +typedef enum { + EShReflectionDefault = 0, // default is original behaviour before options were added + EShReflectionStrictArraySuffix = (1 << 0), // reflection will follow stricter rules for array-of-structs suffixes + EShReflectionBasicArraySuffix = (1 << 1), // arrays of basic types will be appended with [0] as in GL reflection + EShReflectionIntermediateIO = (1 << 2), // reflect inputs and outputs to program, even with no vertex shader + EShReflectionSeparateBuffers = (1 << 3), // buffer variables and buffer blocks are reflected separately + EShReflectionAllBlockVariables = (1 << 4), // reflect all variables in blocks, even if they are inactive + EShReflectionUnwrapIOBlocks = (1 << 5), // unwrap input/output blocks the same as with uniform blocks + LAST_ELEMENT_MARKER(EShReflectionCount), +} EShReflectionOptions; + +// +// Build a table for bindings. This can be used for locating +// attributes, uniforms, globals, etc., as needed. +// +typedef struct { + const char* name; + int binding; +} ShBinding; + +typedef struct { + int numBindings; + ShBinding* bindings; // array of bindings +} ShBindingTable; + +// +// ShHandle held by but opaque to the driver. It is allocated, +// managed, and de-allocated by the compiler/linker. It's contents +// are defined by and used by the compiler and linker. For example, +// symbol table information and object code passed from the compiler +// to the linker can be stored where ShHandle points. +// +// If handle creation fails, 0 will be returned. +// +typedef void* ShHandle; + +// +// Driver calls these to create and destroy compiler/linker +// objects. +// +SH_IMPORT_EXPORT ShHandle ShConstructCompiler(const EShLanguage, int debugOptions); // one per shader +SH_IMPORT_EXPORT ShHandle ShConstructLinker(const EShExecutable, int debugOptions); // one per shader pair +SH_IMPORT_EXPORT ShHandle ShConstructUniformMap(); // one per uniform namespace (currently entire program object) +SH_IMPORT_EXPORT void ShDestruct(ShHandle); + +// +// The return value of ShCompile is boolean, non-zero indicating +// success. +// +// The info-log should be written by ShCompile into +// ShHandle, so it can answer future queries. +// +SH_IMPORT_EXPORT int ShCompile( + const ShHandle, + const char* const shaderStrings[], + const int numStrings, + const int* lengths, + const EShOptimizationLevel, + const TBuiltInResource *resources, + int debugOptions, + int defaultVersion = 110, // use 100 for ES environment, overridden by #version in shader + bool forwardCompatible = false, // give errors for use of deprecated features + EShMessages messages = EShMsgDefault // warnings and errors + ); + +SH_IMPORT_EXPORT int ShLinkExt( + const ShHandle, // linker object + const ShHandle h[], // compiler objects to link together + const int numHandles); + +// +// ShSetEncrpytionMethod is a place-holder for specifying +// how source code is encrypted. +// +SH_IMPORT_EXPORT void ShSetEncryptionMethod(ShHandle); + +// +// All the following return 0 if the information is not +// available in the object passed down, or the object is bad. +// +SH_IMPORT_EXPORT const char* ShGetInfoLog(const ShHandle); +SH_IMPORT_EXPORT const void* ShGetExecutable(const ShHandle); +SH_IMPORT_EXPORT int ShSetVirtualAttributeBindings(const ShHandle, const ShBindingTable*); // to detect user aliasing +SH_IMPORT_EXPORT int ShSetFixedAttributeBindings(const ShHandle, const ShBindingTable*); // to force any physical mappings +// +// Tell the linker to never assign a vertex attribute to this list of physical attributes +// +SH_IMPORT_EXPORT int ShExcludeAttributes(const ShHandle, int *attributes, int count); + +// +// Returns the location ID of the named uniform. +// Returns -1 if error. +// +SH_IMPORT_EXPORT int ShGetUniformLocation(const ShHandle uniformMap, const char* name); + +#ifdef __cplusplus + } // end extern "C" +#endif + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Deferred-Lowering C++ Interface +// ----------------------------------- +// +// Below is a new alternate C++ interface, which deprecates the above +// opaque handle-based interface. +// +// The below is further designed to handle multiple compilation units per stage, where +// the intermediate results, including the parse tree, are preserved until link time, +// rather than the above interface which is designed to have each compilation unit +// lowered at compile time. In the above model, linking occurs on the lowered results, +// whereas in this model intra-stage linking can occur at the parse tree +// (treeRoot in TIntermediate) level, and then a full stage can be lowered. +// + +#include +#include +#include + +class TCompiler; +class TInfoSink; + +namespace glslang { + +const char* GetEsslVersionString(); +const char* GetGlslVersionString(); +int GetKhronosToolId(); + +class TIntermediate; +class TProgram; +class TPoolAllocator; + +// Call this exactly once per process before using anything else +bool InitializeProcess(); + +// Call once per process to tear down everything +void FinalizeProcess(); + +// Resource type for IO resolver +enum TResourceType { + EResSampler, + EResTexture, + EResImage, + EResUbo, + EResSsbo, + EResUav, + EResCount +}; + +// Make one TShader per shader that you will link into a program. Then +// - provide the shader through setStrings() or setStringsWithLengths() +// - optionally call setEnv*(), see below for more detail +// - optionally use setPreamble() to set a special shader string that will be +// processed before all others but won't affect the validity of #version +// - optionally call addProcesses() for each setting/transform, +// see comment for class TProcesses +// - call parse(): source language and target environment must be selected +// either by correct setting of EShMessages sent to parse(), or by +// explicitly calling setEnv*() +// - query the info logs +// +// N.B.: Does not yet support having the same TShader instance being linked into +// multiple programs. +// +// N.B.: Destruct a linked program *before* destructing the shaders linked into it. +// +class TShader { +public: + explicit TShader(EShLanguage); + virtual ~TShader(); + void setStrings(const char* const* s, int n); + void setStringsWithLengths(const char* const* s, const int* l, int n); + void setStringsWithLengthsAndNames( + const char* const* s, const int* l, const char* const* names, int n); + void setPreamble(const char* s) { preamble = s; } + void setEntryPoint(const char* entryPoint); + void setSourceEntryPoint(const char* sourceEntryPointName); + void addProcesses(const std::vector&); + + // IO resolver binding data: see comments in ShaderLang.cpp + void setShiftBinding(TResourceType res, unsigned int base); + void setShiftSamplerBinding(unsigned int base); // DEPRECATED: use setShiftBinding + void setShiftTextureBinding(unsigned int base); // DEPRECATED: use setShiftBinding + void setShiftImageBinding(unsigned int base); // DEPRECATED: use setShiftBinding + void setShiftUboBinding(unsigned int base); // DEPRECATED: use setShiftBinding + void setShiftUavBinding(unsigned int base); // DEPRECATED: use setShiftBinding + void setShiftCbufferBinding(unsigned int base); // synonym for setShiftUboBinding + void setShiftSsboBinding(unsigned int base); // DEPRECATED: use setShiftBinding + void setShiftBindingForSet(TResourceType res, unsigned int base, unsigned int set); + void setResourceSetBinding(const std::vector& base); + void setAutoMapBindings(bool map); + void setAutoMapLocations(bool map); + void addUniformLocationOverride(const char* name, int loc); + void setUniformLocationBase(int base); + void setInvertY(bool invert); +#ifdef ENABLE_HLSL + void setHlslIoMapping(bool hlslIoMap); + void setFlattenUniformArrays(bool flatten); +#endif + void setNoStorageFormat(bool useUnknownFormat); + void setNanMinMaxClamp(bool nanMinMaxClamp); + void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode); + + // For setting up the environment (cleared to nothingness in the constructor). + // These must be called so that parsing is done for the right source language and + // target environment, either indirectly through TranslateEnvironment() based on + // EShMessages et. al., or directly by the user. + // + // setEnvInput: The input source language and stage. If generating code for a + // specific client, the input client semantics to use and the + // version of the that client's input semantics to use, otherwise + // use EShClientNone and version of 0, e.g. for validation mode. + // Note 'version' does not describe the target environment, + // just the version of the source dialect to compile under. + // + // See the definitions of TEnvironment, EShSource, EShLanguage, + // and EShClient for choices and more detail. + // + // setEnvClient: The client that will be hosting the execution, and it's version. + // Note 'version' is not the version of the languages involved, but + // the version of the client environment. + // Use EShClientNone and version of 0 if there is no client, e.g. + // for validation mode. + // + // See EShTargetClientVersion for choices. + // + // setEnvTarget: The language to translate to when generating code, and that + // language's version. + // Use EShTargetNone and version of 0 if there is no client, e.g. + // for validation mode. + // + void setEnvInput(EShSource lang, EShLanguage envStage, EShClient client, int version) + { + environment.input.languageFamily = lang; + environment.input.stage = envStage; + environment.input.dialect = client; + environment.input.dialectVersion = version; + } + void setEnvClient(EShClient client, EShTargetClientVersion version) + { + environment.client.client = client; + environment.client.version = version; + } + void setEnvTarget(EShTargetLanguage lang, EShTargetLanguageVersion version) + { + environment.target.language = lang; + environment.target.version = version; + } + + void getStrings(const char* const* &s, int& n) { s = strings; n = numStrings; } + +#ifdef ENABLE_HLSL + void setEnvTargetHlslFunctionality1() { environment.target.hlslFunctionality1 = true; } + bool getEnvTargetHlslFunctionality1() const { return environment.target.hlslFunctionality1; } +#else + bool getEnvTargetHlslFunctionality1() const { return false; } +#endif + + // Interface to #include handlers. + // + // To support #include, a client of Glslang does the following: + // 1. Call setStringsWithNames to set the source strings and associated + // names. For example, the names could be the names of the files + // containing the shader sources. + // 2. Call parse with an Includer. + // + // When the Glslang parser encounters an #include directive, it calls + // the Includer's include method with the requested include name + // together with the current string name. The returned IncludeResult + // contains the fully resolved name of the included source, together + // with the source text that should replace the #include directive + // in the source stream. After parsing that source, Glslang will + // release the IncludeResult object. + class Includer { + public: + // An IncludeResult contains the resolved name and content of a source + // inclusion. + struct IncludeResult { + IncludeResult(const std::string& headerName, const char* const headerData, const size_t headerLength, void* userData) : + headerName(headerName), headerData(headerData), headerLength(headerLength), userData(userData) { } + // For a successful inclusion, the fully resolved name of the requested + // include. For example, in a file system-based includer, full resolution + // should convert a relative path name into an absolute path name. + // For a failed inclusion, this is an empty string. + const std::string headerName; + // The content and byte length of the requested inclusion. The + // Includer producing this IncludeResult retains ownership of the + // storage. + // For a failed inclusion, the header + // field points to a string containing error details. + const char* const headerData; + const size_t headerLength; + // Include resolver's context. + void* userData; + protected: + IncludeResult& operator=(const IncludeResult&); + IncludeResult(); + }; + + // For both include methods below: + // + // Resolves an inclusion request by name, current source name, + // and include depth. + // On success, returns an IncludeResult containing the resolved name + // and content of the include. + // On failure, returns a nullptr, or an IncludeResult + // with an empty string for the headerName and error details in the + // header field. + // The Includer retains ownership of the contents + // of the returned IncludeResult value, and those contents must + // remain valid until the releaseInclude method is called on that + // IncludeResult object. + // + // Note "local" vs. "system" is not an "either/or": "local" is an + // extra thing to do over "system". Both might get called, as per + // the C++ specification. + + // For the "system" or <>-style includes; search the "system" paths. + virtual IncludeResult* includeSystem(const char* /*headerName*/, + const char* /*includerName*/, + size_t /*inclusionDepth*/) { return nullptr; } + + // For the "local"-only aspect of a "" include. Should not search in the + // "system" paths, because on returning a failure, the parser will + // call includeSystem() to look in the "system" locations. + virtual IncludeResult* includeLocal(const char* /*headerName*/, + const char* /*includerName*/, + size_t /*inclusionDepth*/) { return nullptr; } + + // Signals that the parser will no longer use the contents of the + // specified IncludeResult. + virtual void releaseInclude(IncludeResult*) = 0; + virtual ~Includer() {} + }; + + // Fail all Includer searches + class ForbidIncluder : public Includer { + public: + virtual void releaseInclude(IncludeResult*) override { } + }; + + bool parse(const TBuiltInResource*, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages, Includer&); + + bool parse(const TBuiltInResource* res, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages messages) + { + TShader::ForbidIncluder includer; + return parse(res, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, forwardCompatible, messages, includer); + } + + // Equivalent to parse() without a default profile and without forcing defaults. + bool parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages) + { + return parse(builtInResources, defaultVersion, ENoProfile, false, forwardCompatible, messages); + } + + bool parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages, + Includer& includer) + { + return parse(builtInResources, defaultVersion, ENoProfile, false, forwardCompatible, messages, includer); + } + + // NOTE: Doing just preprocessing to obtain a correct preprocessed shader string + // is not an officially supported or fully working path. + bool preprocess(const TBuiltInResource* builtInResources, + int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages message, std::string* outputString, + Includer& includer); + + const char* getInfoLog(); + const char* getInfoDebugLog(); + EShLanguage getStage() const { return stage; } + TIntermediate* getIntermediate() const { return intermediate; } + +protected: + TPoolAllocator* pool; + EShLanguage stage; + TCompiler* compiler; + TIntermediate* intermediate; + TInfoSink* infoSink; + // strings and lengths follow the standard for glShaderSource: + // strings is an array of numStrings pointers to string data. + // lengths can be null, but if not it is an array of numStrings + // integers containing the length of the associated strings. + // if lengths is null or lengths[n] < 0 the associated strings[n] is + // assumed to be null-terminated. + // stringNames is the optional names for all the strings. If stringNames + // is null, then none of the strings has name. If a certain element in + // stringNames is null, then the corresponding string does not have name. + const char* const* strings; // explicit code to compile, see previous comment + const int* lengths; + const char* const* stringNames; + int numStrings; // size of the above arrays + const char* preamble; // string of implicit code to compile before the explicitly provided code + + // a function in the source string can be renamed FROM this TO the name given in setEntryPoint. + std::string sourceEntryPointName; + + TEnvironment environment; + + friend class TProgram; + +private: + TShader& operator=(TShader&); +}; + +#ifndef GLSLANG_WEB + +// +// A reflection database and its interface, consistent with the OpenGL API reflection queries. +// + +// Data needed for just a single object at the granularity exchanged by the reflection API +class TObjectReflection { +public: + TObjectReflection(const std::string& pName, const TType& pType, int pOffset, int pGLDefineType, int pSize, int pIndex); + + const TType* getType() const { return type; } + int getBinding() const; + void dump() const; + static TObjectReflection badReflection() { return TObjectReflection(); } + + std::string name; + int offset; + int glDefineType; + int size; // data size in bytes for a block, array size for a (non-block) object that's an array + int index; + int counterIndex; + int numMembers; + int arrayStride; // stride of an array variable + int topLevelArrayStride; // stride of the top-level variable in a storage buffer member + EShLanguageMask stages; + +protected: + TObjectReflection() + : offset(-1), glDefineType(-1), size(-1), index(-1), counterIndex(-1), numMembers(-1), arrayStride(0), + topLevelArrayStride(0), stages(EShLanguageMask(0)), type(nullptr) + { + } + + const TType* type; +}; + +class TReflection; +class TIoMapper; +struct TVarEntryInfo; + +// Allows to customize the binding layout after linking. +// All used uniform variables will invoke at least validateBinding. +// If validateBinding returned true then the other resolveBinding, +// resolveSet, and resolveLocation are invoked to resolve the binding +// and descriptor set index respectively. +// +// Invocations happen in a particular order: +// 1) all shader inputs +// 2) all shader outputs +// 3) all uniforms with binding and set already defined +// 4) all uniforms with binding but no set defined +// 5) all uniforms with set but no binding defined +// 6) all uniforms with no binding and no set defined +// +// mapIO will use this resolver in two phases. The first +// phase is a notification phase, calling the corresponging +// notifiy callbacks, this phase ends with a call to endNotifications. +// Phase two starts directly after the call to endNotifications +// and calls all other callbacks to validate and to get the +// bindings, sets, locations, component and color indices. +// +// NOTE: that still limit checks are applied to bindings and sets +// and may result in an error. +class TIoMapResolver +{ +public: + virtual ~TIoMapResolver() {} + + // Should return true if the resulting/current binding would be okay. + // Basic idea is to do aliasing binding checks with this. + virtual bool validateBinding(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current binding should be overridden. + // Return -1 if the current binding (including no binding) should be kept. + virtual int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current set should be overridden. + // Return -1 if the current set (including no set) should be kept. + virtual int resolveSet(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current location should be overridden. + // Return -1 if the current location (including no location) should be kept. + virtual int resolveUniformLocation(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return true if the resulting/current setup would be okay. + // Basic idea is to do aliasing checks and reject invalid semantic names. + virtual bool validateInOut(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current location should be overridden. + // Return -1 if the current location (including no location) should be kept. + virtual int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current component index should be overridden. + // Return -1 if the current component index (including no index) should be kept. + virtual int resolveInOutComponent(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current color index should be overridden. + // Return -1 if the current color index (including no index) should be kept. + virtual int resolveInOutIndex(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Notification of a uniform variable + virtual void notifyBinding(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Notification of a in or out variable + virtual void notifyInOut(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Called by mapIO when it starts its notify pass for the given stage + virtual void beginNotifications(EShLanguage stage) = 0; + // Called by mapIO when it has finished the notify pass + virtual void endNotifications(EShLanguage stage) = 0; + // Called by mipIO when it starts its resolve pass for the given stage + virtual void beginResolve(EShLanguage stage) = 0; + // Called by mapIO when it has finished the resolve pass + virtual void endResolve(EShLanguage stage) = 0; + // Called by mapIO when it starts its symbol collect for teh given stage + virtual void beginCollect(EShLanguage stage) = 0; + // Called by mapIO when it has finished the symbol collect + virtual void endCollect(EShLanguage stage) = 0; + // Called by TSlotCollector to resolve storage locations or bindings + virtual void reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) = 0; + // Called by TSlotCollector to resolve resource locations or bindings + virtual void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) = 0; + // Called by mapIO.addStage to set shader stage mask to mark a stage be added to this pipeline + virtual void addStage(EShLanguage stage) = 0; +}; + +#endif // GLSLANG_WEB + +// Make one TProgram per set of shaders that will get linked together. Add all +// the shaders that are to be linked together. After calling shader.parse() +// for all shaders, call link(). +// +// N.B.: Destruct a linked program *before* destructing the shaders linked into it. +// +class TProgram { +public: + TProgram(); + virtual ~TProgram(); + void addShader(TShader* shader) { stages[shader->stage].push_back(shader); } + std::list& getShaders(EShLanguage stage) { return stages[stage]; } + // Link Validation interface + bool link(EShMessages); + const char* getInfoLog(); + const char* getInfoDebugLog(); + + TIntermediate* getIntermediate(EShLanguage stage) const { return intermediate[stage]; } + +#ifndef GLSLANG_WEB + + // Reflection Interface + + // call first, to do liveness analysis, index mapping, etc.; returns false on failure + bool buildReflection(int opts = EShReflectionDefault); + unsigned getLocalSize(int dim) const; // return dim'th local size + int getReflectionIndex(const char *name) const; + int getReflectionPipeIOIndex(const char* name, const bool inOrOut) const; + int getNumUniformVariables() const; + const TObjectReflection& getUniform(int index) const; + int getNumUniformBlocks() const; + const TObjectReflection& getUniformBlock(int index) const; + int getNumPipeInputs() const; + const TObjectReflection& getPipeInput(int index) const; + int getNumPipeOutputs() const; + const TObjectReflection& getPipeOutput(int index) const; + int getNumBufferVariables() const; + const TObjectReflection& getBufferVariable(int index) const; + int getNumBufferBlocks() const; + const TObjectReflection& getBufferBlock(int index) const; + int getNumAtomicCounters() const; + const TObjectReflection& getAtomicCounter(int index) const; + + // Legacy Reflection Interface - expressed in terms of above interface + + // can be used for glGetProgramiv(GL_ACTIVE_UNIFORMS) + int getNumLiveUniformVariables() const { return getNumUniformVariables(); } + + // can be used for glGetProgramiv(GL_ACTIVE_UNIFORM_BLOCKS) + int getNumLiveUniformBlocks() const { return getNumUniformBlocks(); } + + // can be used for glGetProgramiv(GL_ACTIVE_ATTRIBUTES) + int getNumLiveAttributes() const { return getNumPipeInputs(); } + + // can be used for glGetUniformIndices() + int getUniformIndex(const char *name) const { return getReflectionIndex(name); } + + int getPipeIOIndex(const char *name, const bool inOrOut) const + { return getReflectionPipeIOIndex(name, inOrOut); } + + // can be used for "name" part of glGetActiveUniform() + const char *getUniformName(int index) const { return getUniform(index).name.c_str(); } + + // returns the binding number + int getUniformBinding(int index) const { return getUniform(index).getBinding(); } + + // returns Shaders Stages where a Uniform is present + EShLanguageMask getUniformStages(int index) const { return getUniform(index).stages; } + + // can be used for glGetActiveUniformsiv(GL_UNIFORM_BLOCK_INDEX) + int getUniformBlockIndex(int index) const { return getUniform(index).index; } + + // can be used for glGetActiveUniformsiv(GL_UNIFORM_TYPE) + int getUniformType(int index) const { return getUniform(index).glDefineType; } + + // can be used for glGetActiveUniformsiv(GL_UNIFORM_OFFSET) + int getUniformBufferOffset(int index) const { return getUniform(index).offset; } + + // can be used for glGetActiveUniformsiv(GL_UNIFORM_SIZE) + int getUniformArraySize(int index) const { return getUniform(index).size; } + + // returns a TType* + const TType *getUniformTType(int index) const { return getUniform(index).getType(); } + + // can be used for glGetActiveUniformBlockName() + const char *getUniformBlockName(int index) const { return getUniformBlock(index).name.c_str(); } + + // can be used for glGetActiveUniformBlockiv(UNIFORM_BLOCK_DATA_SIZE) + int getUniformBlockSize(int index) const { return getUniformBlock(index).size; } + + // returns the block binding number + int getUniformBlockBinding(int index) const { return getUniformBlock(index).getBinding(); } + + // returns block index of associated counter. + int getUniformBlockCounterIndex(int index) const { return getUniformBlock(index).counterIndex; } + + // returns a TType* + const TType *getUniformBlockTType(int index) const { return getUniformBlock(index).getType(); } + + // can be used for glGetActiveAttrib() + const char *getAttributeName(int index) const { return getPipeInput(index).name.c_str(); } + + // can be used for glGetActiveAttrib() + int getAttributeType(int index) const { return getPipeInput(index).glDefineType; } + + // returns a TType* + const TType *getAttributeTType(int index) const { return getPipeInput(index).getType(); } + + void dumpReflection(); + // I/O mapping: apply base offsets and map live unbound variables + // If resolver is not provided it uses the previous approach + // and respects auto assignment and offsets. + bool mapIO(TIoMapResolver* pResolver = nullptr, TIoMapper* pIoMapper = nullptr); +#endif + +protected: + bool linkStage(EShLanguage, EShMessages); + + TPoolAllocator* pool; + std::list stages[EShLangCount]; + TIntermediate* intermediate[EShLangCount]; + bool newedIntermediate[EShLangCount]; // track which intermediate were "new" versus reusing a singleton unit in a stage + TInfoSink* infoSink; +#ifndef GLSLANG_WEB + TReflection* reflection; +#endif + bool linked; + +private: + TProgram(TProgram&); + TProgram& operator=(TProgram&); +}; + +} // end namespace glslang + +#endif // _COMPILER_INTERFACE_INCLUDED_ diff --git a/armorcore/tools/to_spirv/license.txt b/armorcore/tools/to_spirv/license.txt new file mode 100644 index 000000000..cd2b6c926 --- /dev/null +++ b/armorcore/tools/to_spirv/license.txt @@ -0,0 +1,19 @@ +Copyright (c) 2017 the krafix development team + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. diff --git a/armorcore/tools/to_spirv/project.js b/armorcore/tools/to_spirv/project.js new file mode 100644 index 000000000..7a44293a8 --- /dev/null +++ b/armorcore/tools/to_spirv/project.js @@ -0,0 +1,9 @@ +let project = new Project("to_spirv"); + +project.add_define("KRAFIX_LIBRARY"); +project.add_cfiles("to_spirv.cpp"); + +project.add_include_dir("glslang"); +project.add_cfiles("glslang/**"); + +return project; diff --git a/armorcore/tools/to_spirv/to_spirv.cpp b/armorcore/tools/to_spirv/to_spirv.cpp new file mode 100644 index 000000000..bfda47a8a --- /dev/null +++ b/armorcore/tools/to_spirv/to_spirv.cpp @@ -0,0 +1,1891 @@ + +// To be replaced with https://github.com/Kode/Kongruent + +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013-2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../glslang/StandAlone/ResourceLimits.h" +#include "../glslang/StandAlone/Worklist.h" +#include "../glslang/SPIRV/GlslangToSpv.h" +#include "../glslang/SPIRV/spirv.hpp" +#include "../glslang/glslang/Public/ShaderLang.h" + +namespace krafix { + enum TargetLanguage { + SpirV + }; + + enum ShaderStage { + StageVertex, + StageGeometry, + StageFragment, + StageCompute + }; + + enum TargetSystem { + Linux, + Android, + Unknown + }; + + struct Target { + int version; + TargetSystem system; + }; + + class Instruction { + public: + Instruction(std::vector& spirv, unsigned& index); + Instruction(int opcode, unsigned* operands, unsigned length); + int opcode; + unsigned* operands; + unsigned length; + const char* string; + }; + + class SpirVTranslator { + public: + SpirVTranslator(std::vector& spirv, ShaderStage stage); + void outputCode(const Target& target, const char* sourcefilename, const char* filename, char* output, std::map& attributes); + int outputLength; + int writeInstructions(const char* filename, char* output, std::vector& instructions); + int writeInstructions(std::vector& output, std::vector& instructions); + std::vector& spirv; + std::vector instructions; + ShaderStage stage; + spv::ExecutionModel executionModel(); + unsigned magicNumber; + unsigned version; + unsigned generator; + unsigned bound; + unsigned schema; + }; +} + +using namespace krafix; + +Instruction::Instruction(std::vector& spirv, unsigned& index) { + using namespace spv; + + int wordCount = spirv[index] >> 16; + opcode = (Op)(spirv[index] & 0xffff); + + operands = wordCount > 1 ? &spirv[index + 1] : NULL; + length = wordCount - 1; + + switch (opcode) { + case OpString: + string = (char*)&spirv[index + 2]; + break; + case OpName: + string = (char*)&spirv[index + 2]; + break; + case OpMemberName: + string = (char*)&spirv[index + 3]; + break; + case OpEntryPoint: + string = (char*)&spirv[index + 3]; + break; + case OpSourceExtension: + string = (char*)&spirv[index + 1]; + break; + default: + string = NULL; + break; + } + + index += wordCount; +} + +Instruction::Instruction(int opcode, unsigned* operands, unsigned length) + : opcode(opcode), operands(operands), length(length), string(NULL) { +} + +namespace { + enum SpirVState { + SpirVStart, + SpirVDebugInformation, + SpirVAnnotations, + SpirVTypes, + SpirVFunctions + }; + + void writeInstruction(std::ostream* out, unsigned word) { + out->put(word & 0xff); + out->put((word >> 8) & 0xff); + out->put((word >> 16) & 0xff); + out->put((word >> 24) & 0xff); + } + + void writeInstruction(std::vector& out, unsigned word) { + out.push_back(word); + } + + bool isDebugInformation(Instruction& instruction) { + return instruction.opcode == spv::OpSource || instruction.opcode == spv::OpSourceExtension + || instruction.opcode == spv::OpName || instruction.opcode == spv::OpMemberName; + } + + bool isAnnotation(Instruction& instruction) { + return instruction.opcode == spv::OpDecorate || instruction.opcode == spv::OpMemberDecorate; + } + + bool isType(Instruction& instruction) { + return instruction.opcode == spv::OpTypeArray || instruction.opcode == spv::OpTypeBool || instruction.opcode == spv::OpTypeFloat || instruction.opcode == spv::OpTypeFunction + || instruction.opcode == spv::OpTypeInt || instruction.opcode == spv::OpTypePointer || instruction.opcode == spv::OpTypeVector || instruction.opcode == spv::OpTypeVoid; + } + + struct Var { + std::string name; + unsigned id; + unsigned type; + unsigned pointertype; + }; + + bool varcompare(const Var& a, const Var& b) { + return strcmp(a.name.c_str(), b.name.c_str()) < 0; + } + + unsigned copyname(const std::string& name, unsigned* instructionsData, unsigned& instructionsDataIndex) { + unsigned length = 0; + bool zeroset = false; + for (unsigned i2 = 0; i2 < name.size(); i2 += 4) { + char* data = (char*)&instructionsData[instructionsDataIndex]; + for (unsigned i3 = 0; i3 < 4; ++i3) { + if (i2 + i3 < name.size()) data[i3] = name[i2 + i3]; + else { + data[i3] = 0; + zeroset = true; + } + } + ++length; + ++instructionsDataIndex; + } + if (!zeroset) { + instructionsData[instructionsDataIndex++] = 0; + ++length; + } + return length; + } +} + +SpirVTranslator::SpirVTranslator(std::vector& spirv, ShaderStage stage) : stage(stage), spirv(spirv) { + if (spirv.size() < 5) { return; } + + unsigned index = 0; + magicNumber = spirv[index++]; + version = spirv[index++]; + generator = spirv[index++]; + bound = spirv[index++]; + schema = spirv[index++]; + + while (index < spirv.size()) { + instructions.push_back(Instruction(spirv, index)); + } +} + +spv::ExecutionModel SpirVTranslator::executionModel() { + switch (stage) { + case StageVertex: + return spv::ExecutionModelVertex; + case StageGeometry: + return spv::ExecutionModelGeometry; + case StageFragment: + return spv::ExecutionModelFragment; + case StageCompute: + return spv::ExecutionModelGLCompute; + default: + throw "Unknown shader stage"; + } +} + +int SpirVTranslator::writeInstructions(const char* filename, char* output, std::vector& instructions) { + std::ofstream fileout; + std::ostrstream arrayout(output, 1024 * 1024); + std::ostream* out; + + if (output) { + out = &arrayout; + } + else { + fileout.open(filename, std::ios::binary | std::ios::out); + out = &fileout; + } + + int length = 0; + writeInstruction(out, magicNumber); + length += 4; + writeInstruction(out, version); + length += 4; + writeInstruction(out, generator); + length += 4; + writeInstruction(out, bound); + length += 4; + writeInstruction(out, schema); + length += 4; + + for (unsigned i = 0; i < instructions.size(); ++i) { + Instruction& inst = instructions[i]; + writeInstruction(out, ((inst.length + 1) << 16) | (unsigned)inst.opcode); + length += 4; + for (unsigned i2 = 0; i2 < inst.length; ++i2) { + writeInstruction(out, inst.operands[i2]); + length += 4; + } + } + + if (!output) { + fileout.close(); + } + + return length; +} + +int SpirVTranslator::writeInstructions(std::vector& output, std::vector& instructions) { + int length = 0; + writeInstruction(output, magicNumber); + length += 4; + writeInstruction(output, version); + length += 4; + writeInstruction(output, generator); + length += 4; + writeInstruction(output, bound); + length += 4; + writeInstruction(output, schema); + length += 4; + + for (unsigned i = 0; i < instructions.size(); ++i) { + Instruction& inst = instructions[i]; + writeInstruction(output, ((inst.length + 1) << 16) | (unsigned)inst.opcode); + length += 4; + for (unsigned i2 = 0; i2 < inst.length; ++i2) { + writeInstruction(output, inst.operands[i2]); + length += 4; + } + } + + return length; +} + +namespace { + using namespace spv; + + uint32_t alignOffset(uint32_t offset, uint32_t alignment) { + uint32_t mask = alignment - 1; + if ((offset & mask) == 0) { + return offset; + } + else { + return (offset + alignment - 1) & ~mask; + } + } + + void outputNames(unsigned* instructionsData, unsigned& instructionsDataIndex, std::vector& structtypeindices, unsigned& structvarindex, std::vector& newinstructions, std::vector& uniforms) { + if (uniforms.size() > 0) { + Instruction structtypename(OpName, &instructionsData[instructionsDataIndex], 0); + structtypeindices.push_back(instructionsDataIndex); + instructionsData[instructionsDataIndex++] = 0; + structtypename.length = 1 + copyname("_k_global_uniform_buffer_type", instructionsData, instructionsDataIndex); + newinstructions.push_back(structtypename); + + Instruction structname(OpName, &instructionsData[instructionsDataIndex], 0); + structvarindex = instructionsDataIndex; + instructionsData[instructionsDataIndex++] = 0; + structname.length = 1 + copyname("_k_global_uniform_buffer", instructionsData, instructionsDataIndex); + newinstructions.push_back(structname); + + for (unsigned i = 0; i < uniforms.size(); ++i) { + Instruction name(OpMemberName, &instructionsData[instructionsDataIndex], 0); + structtypeindices.push_back(instructionsDataIndex); + instructionsData[instructionsDataIndex++] = 0; + instructionsData[instructionsDataIndex++] = i; + name.length = 2 + copyname(uniforms[i].name, instructionsData, instructionsDataIndex); + newinstructions.push_back(name); + } + } + } + + unsigned booltype = 0; + unsigned inttype = 0; + unsigned uinttype = 0; + unsigned floattype = 0; + unsigned vec4type = 0; + unsigned vec3type = 0; + unsigned vec2type = 0; + unsigned mat4type = 0; + unsigned mat3type = 0; + unsigned mat2type = 0; + unsigned floatarraytype = 0; + unsigned vec2arraytype = 0; + unsigned vec3arraytype = 0; + unsigned vec4arraytype = 0; + + void outputDecorations(unsigned* instructionsData, unsigned& instructionsDataIndex, std::vector& structtypeindices, std::vector& structidindices, std::vector& newinstructions, std::vector& uniforms, + std::map& pointers, std::vector& invars, std::vector& outvars, std::vector& images, std::map arraySizes, ShaderStage stage) { + + unsigned location = 0; + for (auto var : invars) { + Instruction newinst(OpDecorate, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = var.id; + instructionsData[instructionsDataIndex++] = DecorationLocation; + instructionsData[instructionsDataIndex++] = location; + newinstructions.push_back(newinst); + ++location; + } + location = 0; + for (auto var : outvars) { + Instruction newinst(OpDecorate, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = var.id; + instructionsData[instructionsDataIndex++] = DecorationLocation; + instructionsData[instructionsDataIndex++] = location; + newinstructions.push_back(newinst); + ++location; + } + unsigned binding = 2; + for (auto var : images) { + Instruction newinst(OpDecorate, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = var.id; + instructionsData[instructionsDataIndex++] = DecorationBinding; + instructionsData[instructionsDataIndex++] = binding; + newinstructions.push_back(newinst); + ++binding; + } + unsigned offset = 0; + for (unsigned i = 0; i < uniforms.size(); ++i) { + Instruction nonwr(OpMemberDecorate, &instructionsData[instructionsDataIndex], 3); + structtypeindices.push_back(instructionsDataIndex); + instructionsData[instructionsDataIndex++] = 0; + instructionsData[instructionsDataIndex++] = i; + instructionsData[instructionsDataIndex++] = DecorationNonWritable; + newinstructions.push_back(nonwr); + + Instruction newinst(OpMemberDecorate, &instructionsData[instructionsDataIndex], 4); + structtypeindices.push_back(instructionsDataIndex); + instructionsData[instructionsDataIndex++] = 0; + instructionsData[instructionsDataIndex++] = i; + instructionsData[instructionsDataIndex++] = DecorationOffset; + unsigned int* offsetPointer = &instructionsData[instructionsDataIndex++]; + newinstructions.push_back(newinst); + + int utype = pointers[uniforms[i].type]; + + if (utype == mat2type || utype == mat3type || utype == mat4type) { + Instruction dec2(OpMemberDecorate, &instructionsData[instructionsDataIndex], 3); + structtypeindices.push_back(instructionsDataIndex); + instructionsData[instructionsDataIndex++] = 0; + instructionsData[instructionsDataIndex++] = i; + instructionsData[instructionsDataIndex++] = DecorationColMajor; + newinstructions.push_back(dec2); + + Instruction dec3(OpMemberDecorate, &instructionsData[instructionsDataIndex], 4); + structtypeindices.push_back(instructionsDataIndex); + instructionsData[instructionsDataIndex++] = 0; + instructionsData[instructionsDataIndex++] = i; + instructionsData[instructionsDataIndex++] = DecorationMatrixStride; + instructionsData[instructionsDataIndex++] = 16; + newinstructions.push_back(dec3); + } + else if (utype == floatarraytype || utype == vec2arraytype || utype == vec3arraytype || utype == vec4arraytype) { + Instruction dec3(OpDecorate, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = utype; + instructionsData[instructionsDataIndex++] = DecorationArrayStride; + if (utype == floatarraytype) { + instructionsData[instructionsDataIndex++] = 1 * 4; + } + if (utype == vec2arraytype) { + instructionsData[instructionsDataIndex++] = 2 * 4; + } + if (utype == vec3arraytype) { + instructionsData[instructionsDataIndex++] = 3 * 4; + } + if (utype == vec4arraytype) { + instructionsData[instructionsDataIndex++] = 4 * 4; + } + newinstructions.push_back(dec3); + } + + if (utype == booltype || utype == inttype || utype == floattype || utype == uinttype) { + offset = alignOffset(offset, 4); + } + else if (utype == vec2type) { + offset = alignOffset(offset, 8); + } + else if (utype == vec3type) { + offset = alignOffset(offset, 16); + } + else if (utype == vec4type) { + offset = alignOffset(offset, 16); + } + else if (utype == mat2type) { + offset = alignOffset(offset, 16); + } + else if (utype == mat3type) { + offset = alignOffset(offset, 48); + } + else if (utype == mat4type) { + offset = alignOffset(offset, 64); + } + else if (utype == floatarraytype) { + offset = alignOffset(offset, 16); + } + else if (utype == vec2arraytype) { + offset = alignOffset(offset, 16); + } + else if (utype == vec3arraytype) { + offset = alignOffset(offset, 16); + } + else if (utype == vec4arraytype) { + offset = alignOffset(offset, 16); + } + + *offsetPointer = offset; + + if (utype == booltype || utype == inttype || utype == floattype || utype == uinttype) { + offset += 4; + } + else if (utype == vec2type) { + offset += 8; + } + else if (utype == vec3type) { + offset += 12; + } + else if (utype == vec4type) { + offset += 16; + } + else if (utype == mat2type) { + offset += 16; + } + else if (utype == mat3type) { + offset += 48; // 36 + 12 padding for DecorationMatrixStride of 16 + } + else if (utype == mat4type) offset += 64; + else if (utype == floatarraytype) { + offset += arraySizes[floatarraytype] * 4; + if (offset % 8 != 0) { + offset += 4; + } + } + else if (utype == vec2arraytype) { + offset += arraySizes[vec2arraytype] * 4 * 2; + } + else if (utype == vec3arraytype) { + offset += arraySizes[vec3arraytype] * 4 * 3; + if (offset % 8 != 0) { + offset += 4; + } + } + else if (utype == vec4arraytype) { + offset += arraySizes[vec4arraytype] * 4 * 4; + } + else { + offset += 1; // Type not handled + } + } + if (uniforms.size() > 0) { + Instruction decbind(OpDecorate, &instructionsData[instructionsDataIndex], 3); + structidindices.push_back(instructionsDataIndex); + instructionsData[instructionsDataIndex++] = 0; + instructionsData[instructionsDataIndex++] = DecorationBinding; + instructionsData[instructionsDataIndex++] = stage == StageVertex ? 0 : 1; + newinstructions.push_back(decbind); + + Instruction decdescset(OpDecorate, &instructionsData[instructionsDataIndex], 3); + structidindices.push_back(instructionsDataIndex); + instructionsData[instructionsDataIndex++] = 0; + instructionsData[instructionsDataIndex++] = DecorationDescriptorSet; + instructionsData[instructionsDataIndex++] = 0; + newinstructions.push_back(decdescset); + + Instruction dec1(OpDecorate, &instructionsData[instructionsDataIndex], 2); + structtypeindices.push_back(instructionsDataIndex); + instructionsData[instructionsDataIndex++] = 0; + instructionsData[instructionsDataIndex++] = DecorationBufferBlock; + newinstructions.push_back(dec1); + } + } + + void outputTypes(unsigned* instructionsData, unsigned& instructionsDataIndex, std::vector& structtypeindices, std::vector& structidindices, unsigned& structvarindex, std::vector& newinstructions, std::vector& uniforms, + std::map& pointers, std::map& constants, unsigned& currentId, unsigned& structid, unsigned& floatpointertype, + unsigned& dotfive, unsigned& two, unsigned& three, unsigned& tempposition, ShaderStage stage) { + if (uniforms.size() > 0) { + Instruction typestruct(OpTypeStruct, &instructionsData[instructionsDataIndex], 1 + (unsigned)uniforms.size()); + unsigned structtype = instructionsData[instructionsDataIndex++] = currentId++; + for (unsigned i = 0; i < uniforms.size(); ++i) { + instructionsData[instructionsDataIndex++] = pointers[uniforms[i].type]; + } + for (auto index : structtypeindices) instructionsData[index] = structtype; + newinstructions.push_back(typestruct); + Instruction typepointer(OpTypePointer, &instructionsData[instructionsDataIndex], 3); + unsigned pointertype = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = StorageClassUniform; + instructionsData[instructionsDataIndex++] = structtype; + newinstructions.push_back(typepointer); + Instruction variable(OpVariable, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = pointertype; + structid = instructionsData[instructionsDataIndex++] = currentId++; + for (auto index : structidindices) instructionsData[index] = structid; + instructionsData[structvarindex] = structid; + instructionsData[instructionsDataIndex++] = StorageClassUniform; + newinstructions.push_back(variable); + + if (uinttype == 0) { + Instruction typeint(OpTypeInt, &instructionsData[instructionsDataIndex], 3); + uinttype = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = 32; + instructionsData[instructionsDataIndex++] = 0; + newinstructions.push_back(typeint); + } + for (unsigned i = 0; i < uniforms.size(); ++i) { + Instruction constant(OpConstant, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = uinttype; + unsigned constantid = currentId++; + instructionsData[instructionsDataIndex++] = constantid; + constants[i] = constantid; + instructionsData[instructionsDataIndex++] = i; + newinstructions.push_back(constant); + Instruction typepointer(OpTypePointer, &instructionsData[instructionsDataIndex], 3); + uniforms[i].pointertype = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = StorageClassUniform; + instructionsData[instructionsDataIndex++] = pointers[uniforms[i].type]; + newinstructions.push_back(typepointer); + } + } + + if (stage == StageVertex) { + if (floattype == 0) { + Instruction floaty(OpTypeFloat, &instructionsData[instructionsDataIndex], 2); + floattype = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = 32; + newinstructions.push_back(floaty); + } + + Instruction floatpointer(OpTypePointer, &instructionsData[instructionsDataIndex], 3); + floatpointertype = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = StorageClassPrivate; + instructionsData[instructionsDataIndex++] = floattype; + newinstructions.push_back(floatpointer); + + Instruction dotfiveconstant(OpConstant, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = floattype; + dotfive = instructionsData[instructionsDataIndex++] = currentId++; + *(float*)&instructionsData[instructionsDataIndex++] = 0.5f; + newinstructions.push_back(dotfiveconstant); + + if (uinttype == 0) { + Instruction inty(OpTypeInt, &instructionsData[instructionsDataIndex], 3); + uinttype = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = 32; + instructionsData[instructionsDataIndex++] = 0; + newinstructions.push_back(inty); + } + + Instruction twoconstant(OpConstant, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = uinttype; + two = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = 2; + newinstructions.push_back(twoconstant); + + Instruction threeconstant(OpConstant, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = uinttype; + three = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = 3; + newinstructions.push_back(threeconstant); + + if (vec4type == 0) { + Instruction vec4(OpTypeVector, &instructionsData[instructionsDataIndex], 3); + vec4type = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = floattype; + instructionsData[instructionsDataIndex++] = 4; + newinstructions.push_back(vec4); + } + + Instruction vec4pointer(OpTypePointer, &instructionsData[instructionsDataIndex], 3); + unsigned vec4pointertype = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = StorageClassPrivate; + instructionsData[instructionsDataIndex++] = vec4type; + newinstructions.push_back(vec4pointer); + + Instruction varinst(OpVariable, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = vec4pointertype; + tempposition = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = StorageClassPrivate; + newinstructions.push_back(varinst); + } + } +} + +void SpirVTranslator::outputCode(const Target& target, const char* sourcefilename, const char* filename, char* output, std::map& attributes) { + booltype = 0; + inttype = 0; + uinttype = 0; + floattype = 0; + vec4type = 0; + vec3type = 0; + vec2type = 0; + mat4type = 0; + mat3type = 0; + mat2type = 0; + floatarraytype = 0; + vec2arraytype = 0; + vec3arraytype = 0; + vec4arraytype = 0; + + using namespace spv; + + std::map names; + std::vector invars; + std::vector outvars; + std::vector tempvars; + std::vector images; + std::vector uniforms; + std::map imageTypes; + std::map pointers; + std::map constants; + std::map accessChains; + std::map arraySizeConstants; + std::map arraySizes; + unsigned position; + + for (unsigned i = 0; i < instructions.size(); ++i) { + Instruction& inst = instructions[i]; + switch (inst.opcode) { + case OpName: { + unsigned id = inst.operands[0]; + if (strcmp(inst.string, "") != 0) { + names[id] = inst.string; + } + break; + } + case OpDecorate: { + unsigned id = inst.operands[0]; + Decoration decoration = (Decoration)inst.operands[1]; + if (decoration == DecorationBuiltIn) { + names[id] = ""; + } + break; + } + case OpAccessChain: { + unsigned id = inst.operands[1]; + unsigned accessId = inst.operands[2]; + accessChains[id] = accessId; + break; + } + case OpTypeSampledImage: { + unsigned id = inst.operands[0]; + imageTypes[id] = true; + break; + } + case OpTypeImage: { + unsigned id = inst.operands[0]; + imageTypes[id] = true; + break; + } + case OpTypeSampler: { + unsigned id = inst.operands[0]; + imageTypes[id] = true; + break; + } + case OpTypePointer: { + unsigned id = inst.operands[0]; + unsigned type = inst.operands[2]; + if (imageTypes[type]) imageTypes[id] = true; + pointers[id] = type; + break; + } + case OpTypeBool: { + unsigned id = inst.operands[0]; + booltype = id; + break; + } + case OpTypeInt: { + unsigned id = inst.operands[0]; + unsigned width = inst.operands[1]; + unsigned signedness = inst.operands[2]; + if (width == 32 && signedness == 1) { + inttype = id; + } + else if (width == 32 && signedness == 0) { + uinttype = id; + } + break; + } + case OpTypeFloat: { + unsigned id = inst.operands[0]; + unsigned width = inst.operands[1]; + if (width == 32) { + floattype = id; + } + break; + } + case OpTypeVector: { + unsigned id = inst.operands[0]; + unsigned componentType = inst.operands[1]; + unsigned componentCount = inst.operands[2]; + if (componentType == floattype) { + if (componentCount == 4) { + vec4type = id; + } + else if (componentCount == 3) { + vec3type = id; + } + else if (componentCount == 2) { + vec2type = id; + } + } + break; + } + case OpTypeMatrix: { + unsigned id = inst.operands[0]; + // unsigned columnType = inst.operands[1]; + unsigned columnCount = inst.operands[2]; + if (columnCount == 4) { + mat4type = id; + } + else if (columnCount == 3) { + mat3type = id; + } + else if (columnCount == 2) { + mat2type = id; + } + + break; + } + case OpConstant: { + unsigned id = inst.operands[1]; + if (arraySizeConstants[id] == 0) { + arraySizeConstants[id] = inst.operands[2]; + } + break; + } + case OpTypeArray: { + unsigned id = inst.operands[0]; + unsigned componentType = inst.operands[1]; + if (imageTypes[componentType]) { + imageTypes[id] = true; + } + arraySizes[id] = arraySizeConstants[inst.operands[2]]; + if (componentType == floattype) { + floatarraytype = id; + } + else if (componentType == vec2type) { + vec2arraytype = id; + } + else if (componentType == vec3type) { + vec3arraytype = id; + } + else if (componentType == vec4type) { + vec4arraytype = id; + } + break; + } + case OpVariable: { + unsigned type = inst.operands[0]; + unsigned id = inst.operands[1]; + StorageClass storage = (StorageClass)inst.operands[2]; + Var var; + var.name = names[id]; + var.id = id; + var.type = type; + if (var.name != "") { + if (storage == StorageClassInput) invars.push_back(var); + if (storage == StorageClassOutput) outvars.push_back(var); + if (storage == StorageClassUniformConstant) { + if (imageTypes[type]) { + images.push_back(var); + } + else { + uniforms.push_back(var); + } + } + } + else tempvars.push_back(var); + break; + } + case OpStore: { + unsigned to = inst.operands[0]; + int accessId = accessChains[to]; + for (unsigned j = 0; j < tempvars.size(); ++j) { + if (tempvars[j].id == accessId) { + for (const auto& pair : pointers) { + if (tempvars[j].type == pair.first) { + if (strcmp(names[pair.second].c_str(), "gl_PerVertex") == 0) { + position = to; + break; + } + } + } + } + } + break; + } + } + } + + std::sort(invars.begin(), invars.end(), varcompare); + std::sort(outvars.begin(), outvars.end(), varcompare); + std::sort(images.begin(), images.end(), varcompare); + + SpirVState state = SpirVStart; + std::vector newinstructions; + unsigned instructionsData[4096]; + unsigned instructionsDataIndex = 0; + unsigned currentId = bound; + unsigned structid; + std::vector structtypeindices, structidindices; + unsigned structvarindex; + unsigned tempposition; + unsigned floatpointertype; + unsigned dotfive; + unsigned two; + unsigned three; + bool namesInserted = false; + bool decorationsInserted = false; + for (unsigned i = 0; i < instructions.size(); ++i) { + Instruction& inst = instructions[i]; + + switch (state) { + case SpirVStart: + if (isDebugInformation(inst)) { + state = SpirVDebugInformation; + if (!namesInserted) { + outputNames(instructionsData, instructionsDataIndex, structtypeindices, structvarindex, newinstructions, uniforms); + namesInserted = true; + } + } + break; + case SpirVDebugInformation: + if (isAnnotation(inst)) { + state = SpirVAnnotations; + if (!namesInserted) { + outputNames(instructionsData, instructionsDataIndex, structtypeindices, structvarindex, newinstructions, uniforms); + namesInserted = true; + } + if (!decorationsInserted) { + outputDecorations(instructionsData, instructionsDataIndex, structtypeindices, structidindices, newinstructions, uniforms, pointers, invars, outvars, images, arraySizes, stage); + decorationsInserted = true; + } + } + if (isType(inst)) { + state = SpirVTypes; + + if (!namesInserted) { + outputNames(instructionsData, instructionsDataIndex, structtypeindices, structvarindex, newinstructions, uniforms); + namesInserted = true; + } + if (!decorationsInserted) { + outputDecorations(instructionsData, instructionsDataIndex, structtypeindices, structidindices, newinstructions, uniforms, pointers, invars, outvars, images, arraySizes, stage); + decorationsInserted = true; + } + } + break; + case SpirVAnnotations: + if (!isAnnotation(inst)) { + state = SpirVTypes; + + if (!namesInserted) { + outputNames(instructionsData, instructionsDataIndex, structtypeindices, structvarindex, newinstructions, uniforms); + namesInserted = true; + } + if (!decorationsInserted) { + outputDecorations(instructionsData, instructionsDataIndex, structtypeindices, structidindices, newinstructions, uniforms, pointers, invars, outvars, images, arraySizes, stage); + decorationsInserted = true; + } + } + break; + case SpirVTypes: + if (inst.opcode == OpFunction) { + outputTypes(instructionsData, instructionsDataIndex, structtypeindices, structidindices, structvarindex, newinstructions, uniforms, pointers, constants, currentId, + structid, floatpointertype, dotfive, two, three, tempposition, stage); + state = SpirVFunctions; + } + break; + } + + if (inst.opcode == OpEntryPoint) { + unsigned executionModel = inst.operands[0]; + if (executionModel == 4) { // Fragment Shader + unsigned i = 2; + for (; ; ++i) { + char* chars = (char*)&inst.operands[i]; + if (chars[0] == 0 || chars[1] == 0 || chars[2] == 0 || chars[3] == 0) break; + } + Instruction newinst(OpEntryPoint, &instructionsData[instructionsDataIndex], 0); + unsigned length = 0; + for (unsigned i2 = 0; i2 <= i; ++i2) { + instructionsData[instructionsDataIndex++] = inst.operands[i2]; + ++length; + } + for (auto var : invars) { + instructionsData[instructionsDataIndex++] = var.id; + ++length; + } + for (auto var : outvars) { + instructionsData[instructionsDataIndex++] = var.id; + ++length; + } + newinst.length = length; + newinstructions.push_back(newinst); + } + else { + newinstructions.push_back(inst); + } + } + else if (inst.opcode == OpName) { + bool isInput = false; + for (auto var : invars) { + if (inst.operands[0] == var.id) { + isInput = true; + } + } + + bool isImage = false; + for (auto image : images) { + if (inst.operands[0] == image.id) { + isImage = true; + } + } + + if (isInput || isImage) { + newinstructions.push_back(inst); + } + } + else if (inst.opcode == OpMemberName) { + + } + else if (inst.opcode == OpSource) { + + } + else if (inst.opcode == OpSourceContinued) { + + } + else if (inst.opcode == OpSourceExtension) { + + } + else if (inst.opcode == OpExecutionMode) { + unsigned executionMode = inst.operands[1]; + if (executionMode == 8) { + Instruction copy = inst; + copy.operands[1] = 7; + newinstructions.push_back(copy); + } + else { + newinstructions.push_back(inst); + } + } + else if (inst.opcode == OpTypeImage) { + Instruction copy = inst; + if (stage == StageCompute) { + copy.length -= 1; + } + else { + copy.length -= 2; + } + newinstructions.push_back(copy); + } + else if (inst.opcode == OpVariable) { + unsigned type = inst.operands[0]; + unsigned id = inst.operands[1]; + StorageClass storage = (StorageClass)inst.operands[2]; + if (storage != StorageClassUniformConstant || imageTypes[type]) { + newinstructions.push_back(inst); + } + } + else if (inst.opcode == OpTypePointer) { + // Putting uniforms into a uniform-block changes the types from UniformConstant to Uniform + unsigned resultId = inst.operands[0]; + unsigned storageClass = inst.operands[1]; + unsigned typeId = inst.operands[2]; + + bool replaced = false; + + if (storageClass == 0) { + if (!imageTypes[typeId]) { + Instruction typePointer(OpTypePointer, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = resultId; + instructionsData[instructionsDataIndex++] = 2; // Uniform + instructionsData[instructionsDataIndex++] = typeId; + newinstructions.push_back(typePointer); + replaced = true; + } + } + + if (!replaced) { + newinstructions.push_back(inst); + } + } + else if (inst.opcode == OpAccessChain) { + // replace all accesses to global uniforms + unsigned resultType = inst.operands[0]; + unsigned resultId = inst.operands[1]; + unsigned base = inst.operands[2]; + + Var uniform; + unsigned index; + bool found = false; + for (unsigned i = 0; i < uniforms.size(); ++i) { + if (uniforms[i].id == base) { + uniform = uniforms[i]; + index = i; + found = true; + break; + } + } + + if (found) { + // OpAccessChain can be a chain of any size so we just sneak in the access to the + // uniform-struct at the front + Instruction access(OpAccessChain, &instructionsData[instructionsDataIndex], inst.length + 1); + instructionsData[instructionsDataIndex++] = resultType; + instructionsData[instructionsDataIndex++] = resultId; + instructionsData[instructionsDataIndex++] = structid; + instructionsData[instructionsDataIndex++] = constants[index]; + for (unsigned i = 3; i < inst.length; ++i) { + instructionsData[instructionsDataIndex++] = inst.operands[i]; + } + newinstructions.push_back(access); + } + else { + newinstructions.push_back(inst); + } + } + else if (inst.opcode == OpLoad) { + // replace all loads from global uniforms + unsigned type = inst.operands[0]; + unsigned id = inst.operands[1]; + unsigned pointer = inst.operands[2]; + + Var uniform; + unsigned index; + bool found = false; + for (unsigned i = 0; i < uniforms.size(); ++i) { + if (uniforms[i].id == pointer) { + uniform = uniforms[i]; + index = i; + found = true; + break; + } + } + + if (found) { + Instruction access(OpAccessChain, &instructionsData[instructionsDataIndex], 4); + instructionsData[instructionsDataIndex++] = uniform.pointertype; + unsigned newpointer = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = structid; + instructionsData[instructionsDataIndex++] = constants[index]; + newinstructions.push_back(access); + Instruction load(OpLoad, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = type; + instructionsData[instructionsDataIndex++] = id; + instructionsData[instructionsDataIndex++] = newpointer; + newinstructions.push_back(load); + } + else { + newinstructions.push_back(inst); + } + } + else if (inst.opcode == OpStore) { + if (stage == StageVertex) { + //gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5; + unsigned to = inst.operands[0]; + unsigned from = inst.operands[1]; + if (to == position) { + //OpStore tempposition from + Instruction store1(OpStore, &instructionsData[instructionsDataIndex], 2); + instructionsData[instructionsDataIndex++] = tempposition; + instructionsData[instructionsDataIndex++] = from; + newinstructions.push_back(store1); + + //%27 = OpAccessChain floatpointer tempposition two + Instruction access1(OpAccessChain, &instructionsData[instructionsDataIndex], 4); + instructionsData[instructionsDataIndex++] = floatpointertype; + unsigned _27 = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = tempposition; + instructionsData[instructionsDataIndex++] = two; + newinstructions.push_back(access1); + + //%28 = OpLoad float %27 + Instruction load1(OpLoad, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = floattype; + unsigned _28 = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = _27; + newinstructions.push_back(load1); + + //%30 = OpAccessChain floatpointer tempposition three + Instruction access2(OpAccessChain, &instructionsData[instructionsDataIndex], 4); + instructionsData[instructionsDataIndex++] = floatpointertype; + unsigned _30 = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = tempposition; + instructionsData[instructionsDataIndex++] = three; + newinstructions.push_back(access2); + + //%31 = OpLoad float %30 + Instruction load2(OpLoad, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = floattype; + unsigned _31 = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = _30; + newinstructions.push_back(load2); + + //%32 = OpFAdd float %28 %31 + Instruction add(OpFAdd, &instructionsData[instructionsDataIndex], 4); + instructionsData[instructionsDataIndex++] = floattype; + unsigned _32 = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = _28; + instructionsData[instructionsDataIndex++] = _31; + newinstructions.push_back(add); + + //%34 = OpFMul float %32 dotfive + Instruction mult(OpFMul, &instructionsData[instructionsDataIndex], 4); + instructionsData[instructionsDataIndex++] = floattype; + unsigned _34 = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = _32; + instructionsData[instructionsDataIndex++] = dotfive; + newinstructions.push_back(mult); + + //%35 = OpAccessChain floatpointer tempposition two + Instruction access3(OpAccessChain, &instructionsData[instructionsDataIndex], 4); + instructionsData[instructionsDataIndex++] = floatpointertype; + unsigned _35 = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = tempposition; + instructionsData[instructionsDataIndex++] = two; + newinstructions.push_back(access3); + + //OpStore %35 %34 + Instruction store2(OpStore, &instructionsData[instructionsDataIndex], 2); + instructionsData[instructionsDataIndex++] = _35; + instructionsData[instructionsDataIndex++] = _34; + newinstructions.push_back(store2); + + //%38 = OpLoad vec4 tempposition + Instruction load3(OpLoad, &instructionsData[instructionsDataIndex], 3); + instructionsData[instructionsDataIndex++] = vec4type; + unsigned _38 = instructionsData[instructionsDataIndex++] = currentId++; + instructionsData[instructionsDataIndex++] = tempposition; + newinstructions.push_back(load3); + + //OpStore position %38 + Instruction store3(OpStore, &instructionsData[instructionsDataIndex], 2); + instructionsData[instructionsDataIndex++] = position; + instructionsData[instructionsDataIndex++] = _38; + newinstructions.push_back(store3); + } + else { + newinstructions.push_back(inst); + } + } + else { + newinstructions.push_back(inst); + } + } + else if (inst.opcode == OpDecorate) { + Decoration decoration = (Decoration)inst.operands[1]; + if (decoration == DecorationBuiltIn && inst.operands[2] == BuiltInVertexId) { + // VertexId is not allowed in Vulkan + Instruction copy = inst; + copy.operands[2] = BuiltInVertexIndex; + newinstructions.push_back(copy); + } + else if (decoration != DecorationBinding) { + newinstructions.push_back(inst); + } + } + else { + newinstructions.push_back(inst); + } + } + + bound = currentId + 1; + + std::vector spirv; + outputLength = writeInstructions(spirv, newinstructions); + std::vector optimizedSpirv; + optimizedSpirv = spirv; + + outputLength = (int)(optimizedSpirv.size() * 4); + if (output) { + memcpy(output, optimizedSpirv.data(), outputLength); + } + else { + FILE* file = fopen(filename, "wb"); + fwrite(optimizedSpirv.data(), 4, optimizedSpirv.size(), file); + fclose(file); + } +} + +enum TOptions { + EOptionNone = 0, + EOptionIntermediate = (1 << 0), + EOptionSuppressInfolog = (1 << 1), + EOptionMemoryLeakMode = (1 << 2), + EOptionRelaxedErrors = (1 << 3), + EOptionGiveWarnings = (1 << 4), + EOptionLinkProgram = (1 << 5), + EOptionMultiThreaded = (1 << 6), + EOptionDumpConfig = (1 << 7), + EOptionDumpReflection = (1 << 8), + EOptionSuppressWarnings = (1 << 9), + EOptionDumpVersions = (1 << 10), + EOptionSpv = (1 << 11), + EOptionHumanReadableSpv = (1 << 12), + EOptionVulkanRules = (1 << 13), + EOptionDefaultDesktop = (1 << 14), + EOptionOutputPreprocessed = (1 << 15), + EOptionOutputHexadecimal = (1 << 16), + EOptionReadHlsl = (1 << 17), + EOptionCascadingErrors = (1 << 18), + EOptionAutoMapBindings = (1 << 19), + EOptionFlattenUniformArrays = (1 << 20), + EOptionNoStorageFormat = (1 << 21), + EOptionKeepUncalled = (1 << 22), +}; + +enum TFailCode { + ESuccess = 0, + EFailUsage, + EFailCompile, + EFailLink, + EFailCompilerCreate, + EFailThreadCreate, + EFailLinkerCreate +}; + +EShLanguage FindLanguage(const std::string& name, bool parseSuffix = true); +void CompileFile(const char* fileName, ShHandle); +void usage(); +void FreeFileData(char** data); +char** ReadFileData(const char* fileName); + +bool CompileFailed = false; +bool LinkFailed = false; +static bool quiet = false; +int NumShaderStrings; +TBuiltInResource Resources; +std::string ConfigFile; + +void ProcessConfigFile() { + char** configStrings = 0; + char* config = 0; + if (ConfigFile.size() > 0) { + configStrings = ReadFileData(ConfigFile.c_str()); + if (configStrings) + config = *configStrings; + else { + printf("Error opening configuration file; will instead use the default configuration\n"); + usage(); + } + } + + if (config == 0) { + Resources = glslang::DefaultTBuiltInResource; + return; + } + + glslang::DecodeResourceLimits(&Resources, config); + + if (configStrings) + FreeFileData(configStrings); + else + delete[] config; +} + +glslang::TWorklist Worklist; +glslang::TWorkItem** Work = 0; +int NumWorkItems = 0; + +int Options = 0; +const char* ExecutableName = nullptr; +const char* binaryFileName = nullptr; +const char* entryPointName = nullptr; +const char* sourceEntryPointName = nullptr; +const char* shaderStageName = nullptr; +const char* variableName = nullptr; + +std::array baseSamplerBinding; +std::array baseTextureBinding; +std::array baseImageBinding; +std::array baseUboBinding; +std::array baseSsboBinding; + +bool SetConfigFile(const std::string& name) { + if (name.size() < 5) + return false; + + if (name.compare(name.size() - 5, 5, ".conf") == 0) { + ConfigFile = name; + return true; + } + + return false; +} + +void Error(const char* message) { + printf("%s: Error %s (use -h for usage)\n", ExecutableName, message); + exit(EFailUsage); +} + +void SetMessageOptions(EShMessages& messages) { + if (Options & EOptionRelaxedErrors) + messages = (EShMessages)(messages | EShMsgRelaxedErrors); + if (Options & EOptionIntermediate) + messages = (EShMessages)(messages | EShMsgAST); + if (Options & EOptionSuppressWarnings) + messages = (EShMessages)(messages | EShMsgSuppressWarnings); + if (Options & EOptionSpv) + messages = (EShMessages)(messages | EShMsgSpvRules); + if (Options & EOptionVulkanRules) + messages = (EShMessages)(messages | EShMsgVulkanRules); + if (Options & EOptionOutputPreprocessed) + messages = (EShMessages)(messages | EShMsgOnlyPreprocessor); + if (Options & EOptionReadHlsl) + messages = (EShMessages)(messages | EShMsgReadHlsl); + if (Options & EOptionCascadingErrors) + messages = (EShMessages)(messages | EShMsgCascadingErrors); + if (Options & EOptionKeepUncalled) + messages = (EShMessages)(messages | EShMsgKeepUncalled); +} + +void PutsIfNonEmpty(const char* str) { + if (str && str[0]) { + puts(str); + } +} + +void StderrIfNonEmpty(const char* str) { + if (str && str[0]) { + fprintf(stderr, "%s\n", str); + } +} + +struct ShaderCompUnit { + EShLanguage stage; + std::string fileName; + char** text; // memory owned/managed externally + const char* fileNameList[1]; + + // Need to have a special constructors to adjust the fileNameList, since back end needs a list of ptrs + ShaderCompUnit(EShLanguage istage, std::string& ifileName, char** itext) + { + stage = istage; + fileName = ifileName; + text = itext; + fileNameList[0] = fileName.c_str(); + } + + ShaderCompUnit(const ShaderCompUnit& rhs) + { + stage = rhs.stage; + fileName = rhs.fileName; + text = rhs.text; + fileNameList[0] = fileName.c_str(); + } + +}; + +class NullIncluder : public glslang::TShader::Includer { +public: + NullIncluder() {} + + IncludeResult* includeSystem(const char* headerName, const char* includerName, size_t inclusionDepth) override { + return includeLocal(headerName, includerName, inclusionDepth); + } + + IncludeResult* includeLocal(const char* headerName, const char* includerName, size_t inclusionDepth) override { + return nullptr; + } + + void releaseInclude(IncludeResult* result) override {} +}; + +krafix::ShaderStage shLanguageToShaderStage(EShLanguage lang) { + switch (lang) { + case EShLangVertex: return krafix::StageVertex; + case EShLangGeometry: return krafix::StageGeometry; + case EShLangFragment: return krafix::StageFragment; + case EShLangCompute: return krafix::StageCompute; + case EShLangCount: + default: + return krafix::StageCompute; + } +} + +static void preprocessSpirv(std::vector& spirv) { + unsigned binding = 0; + for (unsigned index = 0; index < spirv.size(); ++index) { + int wordCount = spirv[index] >> 16; + int opcode = spirv[index] & 0xffff; + + unsigned* operands = wordCount > 1 ? &spirv[index + 1] : NULL; + int length = wordCount - 1; + + if (opcode == 71 && length >= 2) { + if (operands[1] == 33) { + operands[2] = binding++; + } + } + } +} + +void CompileAndLinkShaderUnits(std::vector compUnits, krafix::Target target, const char* sourcefilename, const char* filename, const char* tempdir, char* output, int* length, + glslang::TShader::Includer& includer, const char* defines, bool relax) { + + std::list shaders; + EShMessages messages = EShMsgDefault; + SetMessageOptions(messages); + + glslang::TProgram& program = *new glslang::TProgram; + for (auto it = compUnits.cbegin(); it != compUnits.cend(); ++it) { + const auto& compUnit = *it; + glslang::TShader* shader = new glslang::TShader(compUnit.stage); + shader->setStringsWithLengthsAndNames(compUnit.text, NULL, compUnit.fileNameList, 1); + if (entryPointName) // HLSL todo: this needs to be tracked per compUnits + shader->setEntryPoint(entryPointName); + if (sourceEntryPointName) + shader->setSourceEntryPoint(sourceEntryPointName); + + shader->setShiftSamplerBinding(baseSamplerBinding[compUnit.stage]); + shader->setShiftTextureBinding(baseTextureBinding[compUnit.stage]); + shader->setShiftImageBinding(baseImageBinding[compUnit.stage]); + shader->setShiftUboBinding(baseUboBinding[compUnit.stage]); + shader->setShiftSsboBinding(baseSsboBinding[compUnit.stage]); + // shader->setFlattenUniformArrays((Options & EOptionFlattenUniformArrays) != 0); + shader->setNoStorageFormat((Options & EOptionNoStorageFormat) != 0); + shader->setPreamble(defines); + + if (Options & EOptionAutoMapBindings) + shader->setAutoMapBindings(true); + + shaders.push_back(shader); + + const int defaultVersion = Options & EOptionDefaultDesktop ? 110 : 100; + + if (Options & EOptionOutputPreprocessed) { + std::string str; + //glslang::TShader::ForbidIncluder includer; + if (shader->preprocess(&Resources, defaultVersion, ENoProfile, false, false, + messages, &str, includer)) { + PutsIfNonEmpty(str.c_str()); + } + else { + CompileFailed = true; + } + StderrIfNonEmpty(shader->getInfoLog()); + StderrIfNonEmpty(shader->getInfoDebugLog()); + continue; + } + if (!shader->parse(&Resources, defaultVersion, ENoProfile, false, false, messages, includer)) + CompileFailed = true; + + program.addShader(shader); + + if (!(Options & EOptionSuppressInfolog) && + !(Options & EOptionMemoryLeakMode)) { + //PutsIfNonEmpty(compUnit.fileName.c_str()); + PutsIfNonEmpty(shader->getInfoLog()); + PutsIfNonEmpty(shader->getInfoDebugLog()); + } + } + + // Link + if (!(Options & EOptionOutputPreprocessed) && !program.link(messages)) + LinkFailed = true; + + // Map IO + if (Options & EOptionSpv) { + if (!program.mapIO()) + LinkFailed = true; + } + + // Report + if (!(Options & EOptionSuppressInfolog) && + !(Options & EOptionMemoryLeakMode)) { + PutsIfNonEmpty(program.getInfoLog()); + PutsIfNonEmpty(program.getInfoDebugLog()); + } + + // Reflect + if (Options & EOptionDumpReflection) { + program.buildReflection(); + program.dumpReflection(); + } + + // Dump SPIR-V + if (Options & EOptionSpv) { + if (CompileFailed || LinkFailed) + printf("SPIR-V is not generated for failed compile or link\n"); + else { + for (int stage = 0; stage < EShLangCount; ++stage) { + if (program.getIntermediate((EShLanguage)stage)) { + std::vector spirv; + std::string warningsErrors; + spv::SpvBuildLogger logger; + glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv, &logger); + preprocessSpirv(spirv); + krafix::SpirVTranslator* translator = NULL; + std::map attributes; + translator = new krafix::SpirVTranslator(spirv, shLanguageToShaderStage((EShLanguage)stage)); + translator->outputCode(target, sourcefilename, filename, output, attributes); + if (output != nullptr) { + *length = dynamic_cast(translator)->outputLength; + } + delete translator; + } + } + } + } + + delete& program; + while (shaders.size() > 0) { + delete shaders.back(); + shaders.pop_back(); + } +} + +krafix::TargetSystem getSystem(const char* system) { + if (strcmp(system, "linux") == 0) return krafix::Linux; + if (strcmp(system, "android") == 0) return krafix::Android; + return krafix::Unknown; +} + +void CompileAndLinkShaderFiles(krafix::Target target, const char* sourcefilename, const char* filename, const char* tempdir, const char* source, char* output, int* length, glslang::TShader::Includer& includer, const char* defines, bool relax) +{ + std::vector compUnits; + + char* sources[] = { (char*)source, nullptr, nullptr, nullptr, nullptr }; + + glslang::TWorkItem* workItem; + while (Worklist.remove(workItem)) { + ShaderCompUnit compUnit( + FindLanguage(workItem->name), + workItem->name, + source != nullptr ? sources : ReadFileData(workItem->name.c_str()) + ); + + if (!compUnit.text) { + usage(); + return; + } + + compUnits.push_back(compUnit); + } + + for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) { + for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) + CompileAndLinkShaderUnits(compUnits, target, sourcefilename, filename, tempdir, output, length, includer, defines, relax); + + if (Options & EOptionMemoryLeakMode) + glslang::OS_DumpMemoryCounters(); + } + + if (source == nullptr) { + for (auto it = compUnits.begin(); it != compUnits.end(); ++it) + FreeFileData(it->text); + } +} + +int compile(const char* targetlang, const char* from, std::string to, const char* tempdir, const char* source, char* output, int* length, const char* system, + glslang::TShader::Includer& includer, std::string defines, int version, bool relax) { + CompileFailed = false; + + Options |= EOptionSpv; + Options |= EOptionLinkProgram; + + NumWorkItems = 1; + Work = new glslang::TWorkItem * [NumWorkItems]; + Work[0] = 0; + + if (from) { + std::string name(from); + if (!SetConfigFile(name)) { + Work[0] = new glslang::TWorkItem(name); + Worklist.add(Work[0]); + } + } + else { + std::string name = std::string("nothing.") + to; + Work[0] = new glslang::TWorkItem(name); + Worklist.add(Work[0]); + } + + glslang::InitializeProcess(); + + krafix::Target target; + target.system = getSystem(system); + target.version = version > 0 ? version : 1; + defines += "#define SPIRV " + std::to_string(target.version) + "\n"; + CompileAndLinkShaderFiles(target, from, to.c_str(), tempdir, source, output, length, includer, defines.c_str(), relax); + if (!CompileFailed && !quiet) { + std::cerr << "#file:" << to << std::endl; + } + + glslang::FinalizeProcess(); + + if (CompileFailed || LinkFailed) return 1; + else return 0; +} + +int compileOptionallyRelaxed(const char* targetlang, const char* from, std::string to, std::string ext, const char* tempdir, const char* source, char* output, int* length, const char* system, + glslang::TShader::Includer& includer, std::string defines, int version, bool relax) { + int regularErrors = 0, relaxErrors = 0, es3Errors = 0; + + regularErrors = compile(targetlang, from, to + ext, tempdir, source, output, length, system, includer, defines, version, false); + if (relax) { + relaxErrors = compile(targetlang, from, to + "-relaxed" + ext, tempdir, source, output, length, system, includer, defines, version, true); + return std::min(regularErrors, relaxErrors); + } + else { + return regularErrors; + } +} + +int compileOptionallyInstanced(const char* targetlang, const char* from, std::string to, std::string ext, const char* tempdir, const char* source, char* output, int* length, const char* system, + glslang::TShader::Includer& includer, std::string defines, int version, bool instanced, bool relax) { + int errors = 0; + if (instanced) { + errors += compileOptionallyRelaxed(targetlang, from, to + "-noinst", ext, tempdir, source, output, length, system, includer, defines, version, relax); + errors += compileOptionallyRelaxed(targetlang, from, to + "-inst", ext, tempdir, source, output, length, system, includer, defines + "#define INSTANCED_RENDERING\n", version, relax); + } + else { + errors += compileOptionallyRelaxed(targetlang, from, to, ext, tempdir, source, output, length, system, includer, defines, version, relax); + } + return errors; +} + +int compileWithTextureUnits(const char* targetlang, const char* from, std::string to, std::string ext, const char* tempdir, const char* source, char* output, int* length, const char* system, + glslang::TShader::Includer& includer, std::string defines, int version, const std::vector& textureUnitCounts, bool usesTextureUnitsCount, bool instanced, bool relax) { + int errors = 0; + if (usesTextureUnitsCount && textureUnitCounts.size() > 0) { + for (size_t i = 0; i < textureUnitCounts.size(); ++i) { + int texcount = textureUnitCounts[i]; + std::stringstream toto; + toto << to << "-tex" << texcount << ext; + std::stringstream definesplustex; + definesplustex << defines << "#define MAX_TEXTURE_UNITS=" << texcount << "\n"; + errors += compileOptionallyInstanced(targetlang, from, toto.str(), ext, tempdir, source, output, length, system, includer, definesplustex.str(), version, instanced, relax); + } + } + else { + errors += compileOptionallyInstanced(targetlang, from, to, ext, tempdir, source, output, length, system, includer, defines, version, instanced, relax); + } + return errors; +} + +extern "C" int krafix_compile(const char *source, char *output, int *length, const char *targetlang, const char *system, const char *shadertype, int version) { + CompileFailed = false; + LinkFailed = false; + + std::string defines; + std::vector textureUnitCounts; + bool instancedoptional = false; + bool relax = false; + quiet = true; + + ProcessConfigFile(); + + NullIncluder includer; + + bool usesTextureUnitsCount = false; + bool usesInstancedoptional = false; + + char from[256]; + strcpy(from, "."); + strcat(from, shadertype); + strcat(from, ".glsl"); + + return compileWithTextureUnits(targetlang, from, "", shadertype, nullptr, source, output, length, system, includer, defines, version, textureUnitCounts, usesTextureUnitsCount, instancedoptional && usesInstancedoptional, relax); +} + +EShLanguage FindLanguage(const std::string& name, bool parseSuffix) { + size_t ext = 0; + std::string suffix; + + if (shaderStageName) + suffix = shaderStageName; + else { + if (parseSuffix) { + ext = name.rfind('.'); + if (ext == std::string::npos) { + usage(); + return EShLangVertex; + } + ++ext; + } + suffix = name.substr(ext, std::string::npos); + } + + if (suffix == "glsl") { + size_t ext2 = name.substr(0, ext - 1).rfind('.'); + suffix = name.substr(ext2 + 1, ext - ext2 - 2); + } + + if (suffix == "vert") + return EShLangVertex; + else if (suffix == "geom") + return EShLangGeometry; + else if (suffix == "frag") + return EShLangFragment; + else if (suffix == "comp") + return EShLangCompute; + + usage(); + return EShLangVertex; +} + +void CompileFile(const char* fileName, ShHandle compiler) { + int ret = 0; + char** shaderStrings = ReadFileData(fileName); + if (!shaderStrings) { + usage(); + } + + int* lengths = new int[NumShaderStrings]; + + // move to length-based strings, rather than null-terminated strings + for (int s = 0; s < NumShaderStrings; ++s) + lengths[s] = (int)strlen(shaderStrings[s]); + + if (!shaderStrings) { + CompileFailed = true; + return; + } + + EShMessages messages = EShMsgDefault; + SetMessageOptions(messages); + + for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) { + for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) { + ret = ShCompile(compiler, shaderStrings, NumShaderStrings, nullptr, EShOptNone, &Resources, Options, (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages); + } + + if (Options & EOptionMemoryLeakMode) + glslang::OS_DumpMemoryCounters(); + } + + delete[] lengths; + FreeFileData(shaderStrings); + + if (ret == 0) + CompileFailed = true; +} + +void usage() { + printf("Usage: krafix profile in out tempdir system\n"); + exit(EFailUsage); +} + +char** ReadFileData(const char* fileName) { + FILE *in = fopen(fileName, "r"); + + int count = 0; + const int maxSourceStrings = 5; // for testing splitting shader/tokens across multiple strings + char** return_data = (char**)malloc(sizeof(char*) * (maxSourceStrings + 1)); // freed in FreeFileData() + + if (in == nullptr) + Error("unable to open input file"); + + while (fgetc(in) != EOF) + count++; + + fseek(in, 0, SEEK_SET); + + char* fdata = (char*)malloc(count + 2); // freed before return of this function + if (!fdata) + Error("can't allocate memory"); + + if ((int)fread(fdata, 1, count, in) != count) { + free(fdata); + Error("can't read input file"); + } + + fdata[count] = '\0'; + fclose(in); + + if (count == 0) { + // recover from empty file + return_data[0] = (char*)malloc(count + 2); // freed in FreeFileData() + return_data[0][0] = '\0'; + NumShaderStrings = 0; + free(fdata); + + return return_data; + } + else + NumShaderStrings = 1; // Set to larger than 1 for testing multiple strings + + // compute how to split up the file into multiple strings, for testing multiple strings + int len = (int)(ceil)((float)count / (float)NumShaderStrings); + int ptr_len = 0; + int i = 0; + while (count > 0) { + return_data[i] = (char*)malloc(len + 2); // freed in FreeFileData() + memcpy(return_data[i], fdata + ptr_len, len); + return_data[i][len] = '\0'; + count -= len; + ptr_len += len; + if (count < len) { + if (count == 0) { + NumShaderStrings = i + 1; + break; + } + len = count; + } + ++i; + } + + free(fdata); + + return return_data; +} + +void FreeFileData(char** data) { + for (int i = 0; i < NumShaderStrings; i++) + free(data[i]); + + free(data); +} diff --git a/armorcore/tools/wasm/README.md b/armorcore/tools/wasm/README.md new file mode 100644 index 000000000..29c6e3e4a --- /dev/null +++ b/armorcore/tools/wasm/README.md @@ -0,0 +1,11 @@ + +**Wasm (Linux, macOS or WSL)** +```bash +../../make --target wasm --compile +# Copy resulting armorcore.wasm file to build/out +# Copy index.html to build/out +# Copy https://github.com/armory3d/armorcore/tree/main/sources/backends/data/wasm/JS-Sources to build/out +# Todo: +# start.js has hard-coded .wasm file name: https://github.com/armory3d/armorcore/tree/main/sources/backends/data/wasm/JS-Sources/start.js#L48 +# memory.c has hard-coded size: https://github.com/armory3d/armorcore/tree/main/sources/libs/miniClib/memory.c#L6 +``` diff --git a/armorcore/tools/wasm/index.html b/armorcore/tools/wasm/index.html new file mode 100644 index 000000000..27449793a --- /dev/null +++ b/armorcore/tools/wasm/index.html @@ -0,0 +1,11 @@ + + + + + ArmorCore + + + + + + diff --git a/armorcore/tools/wasm/project.js b/armorcore/tools/wasm/project.js new file mode 100644 index 000000000..c397c4888 --- /dev/null +++ b/armorcore/tools/wasm/project.js @@ -0,0 +1,7 @@ + +let flags = globalThis.flags; +flags.name = "armorcore"; +flags.package = "org.armorcore"; + +let project = new Project(flags.name); +return project;

    T%l-iQ|DIqFIVV}*MRXbzv3`H0Uhj74!16tJ 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 literal 0 HcmV?d00001 diff --git a/armorcore/tools/icon.png b/armorcore/tools/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8d069644514e7a44e701606926f7c28fed0bfddb GIT binary patch literal 11663 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 bG!PYw%i^=F`2^rFHAqE4Q@-+*Ma2IB|A9gB literal 0 HcmV?d00001 diff --git a/armorcore/tools/io_export_arm.py b/armorcore/tools/io_export_arm.py new file mode 100644 index 000000000..4b7eeb468 --- /dev/null +++ b/armorcore/tools/io_export_arm.py @@ -0,0 +1,807 @@ +"""Armory Mesh Exporter""" +# +# https://github.com/armory3d/armorcore/blob/main/Tools/io_export_arm.py +# +# Based on Open Game Engine Exchange +# https://opengex.org/ +# Export plugin for Blender by Eric Lengyel +# Copyright 2015, Terathon Software LLC +# +# This software is licensed under the Creative Commons +# Attribution-ShareAlike 3.0 Unported License: +# http://creativecommons.org/licenses/by-sa/3.0/deed.en_US + +import io +import os +import struct +import time +import bpy +from bpy_extras.io_utils import ExportHelper +from mathutils import Vector +import numpy as np + +bl_info = { + "name": "Armory Mesh Exporter", + "category": "Import-Export", + "location": "File -> Export", + "description": "Armory mesh data", + "author": "Armory3D.org", + "version": (2024, 3, 0), + "blender": (3, 6, 0), + "doc_url": "", + "tracker_url": "", +} + +NodeTypeBone = 1 +NodeTypeMesh = 2 +structIdentifier = ["object", "bone_object", "mesh_object"] + +class ArmoryExporter(bpy.types.Operator, ExportHelper): + """Export to Armory format""" + + bl_idname = "export_scene.arm" + bl_label = "Export Armory" + filename_ext = ".arm" + + def execute(self, context): + profile_time = time.time() + current_frame = context.scene.frame_current + current_subframe = context.scene.frame_subframe + self.scene = context.scene + self.output = {} + self.bobjectArray = {} + self.bobjectBoneArray = {} + self.meshArray = {} + self.boneParentArray = {} + self.bone_tracks = [] + self.depsgraph = context.evaluated_depsgraph_get() + scene_objects = self.scene.collection.all_objects + + for bobject in scene_objects: + if not bobject.parent: + self.process_bobject(bobject) + + self.process_skinned_meshes() + + self.output["name"] = self.scene.name + self.output["objects"] = [] + for bo in scene_objects: + if not bo.parent: + self.export_object(bo, self.scene) + + self.output["mesh_datas"] = [] + for o in self.meshArray.items(): + self.export_mesh(o) + + self.output["light_datas"] = None + self.output["camera_datas"] = None + self.output["camera_ref"] = None + self.output["material_datas"] = None + self.output["particle_datas"] = None + self.output["shader_datas"] = None + self.output["speaker_datas"] = None + self.output["world_datas"] = None + self.output["world_ref"] = None + self.output["embedded_datas"] = None + + self.write_arm(self.filepath, self.output) + self.scene.frame_set(current_frame, subframe=current_subframe) + + print(f"Scene exported in {str(time.time() - profile_time)}") + + return {"FINISHED"} + + def write_arm(self, filepath, output): + with open(filepath, "wb") as f: + f.write(packb(output)) + + def write_matrix(self, matrix): + return [ + matrix[0][0], + matrix[0][1], + matrix[0][2], + matrix[0][3], + matrix[1][0], + matrix[1][1], + matrix[1][2], + matrix[1][3], + matrix[2][0], + matrix[2][1], + matrix[2][2], + matrix[2][3], + matrix[3][0], + matrix[3][1], + matrix[3][2], + matrix[3][3], + ] + + def find_bone(self, name): + return next( + ( + bobject_ref + for bobject_ref in self.bobjectBoneArray.items() + if bobject_ref[0].name == name + ), + None, + ) + + def collect_bone_animation(self, armature, name): + path = 'pose.bones["' + name + '"].' + curve_array = [] + if armature.animation_data: + if action := armature.animation_data.action: + curve_array.extend( + fcurve + for fcurve in action.fcurves + if fcurve.data_path.startswith(path) + ) + return curve_array + + def export_bone(self, armature, bone, scene, o, action): + if bobjectRef := self.bobjectBoneArray.get(bone): + o["name"] = bobjectRef["structName"] + o["type"] = structIdentifier[bobjectRef["objectType"]] + self.export_bone_transform(armature, bone, o, action) + o["children"] = [] + for subbobject in bone.children: + so = {} + self.export_bone(armature, subbobject, scene, so, action) + o["children"].append(so) + + def export_pose_markers(self, oanim, action): + if action.pose_markers is None or len(action.pose_markers) == 0: + return + oanim["marker_frames"] = [] + oanim["marker_names"] = [] + for m in action.pose_markers: + oanim["marker_frames"].append(int(m.frame)) + oanim["marker_names"].append(m.name) + + def process_bone(self, bone): + self.bobjectBoneArray[bone] = { + "objectType": NodeTypeBone, + "structName": bone.name, + } + for subbobject in bone.children: + self.process_bone(subbobject) + + def process_bobject(self, bobject): + if bobject.type not in ["MESH", "ARMATURE"]: + return + + btype = NodeTypeMesh if bobject.type == "MESH" else 0 + self.bobjectArray[bobject] = {"objectType": btype, "structName": bobject.name} + + if bobject.type == "ARMATURE": + if skeleton := bobject.data: + for bone in skeleton.bones: + if not bone.parent: + self.process_bone(bone) + + for subbobject in bobject.children: + self.process_bobject(subbobject) + + def process_skinned_meshes(self): + for bobjectRef in self.bobjectArray.items(): + if bobjectRef[1]["objectType"] == NodeTypeMesh: + if armature := bobjectRef[0].find_armature(): + for bone in armature.data.bones: + boneRef = self.find_bone(bone.name) + if boneRef: + boneRef[1]["objectType"] = NodeTypeBone + + def export_bone_transform(self, armature, bone, o, action): + pose_bone = armature.pose.bones.get(bone.name) + transform = bone.matrix_local.copy() + if bone.parent is not None: + transform = bone.parent.matrix_local.inverted_safe() @ transform + + o["transform"] = {} + o["transform"]["values"] = self.write_matrix(transform) + + curve_array = self.collect_bone_animation(armature, bone.name) + animation = len(curve_array) != 0 + + if animation and pose_bone: + begin_frame = int(action.frame_range[0]) + end_frame = int(action.frame_range[1]) + tracko = {} + o["anim"] = {} + o["anim"]["tracks"] = [tracko] + tracko["target"] = "transform" + tracko["frames"] = [ + i - begin_frame for i in range(begin_frame, end_frame + 1) + ] + tracko["values"] = [] + self.bone_tracks.append((tracko["values"], pose_bone)) + + def write_bone_matrices(self, scene, action): + if len(self.bone_tracks) > 0: + begin_frame = int(action.frame_range[0]) + end_frame = int(action.frame_range[1]) + for i in range(begin_frame, end_frame + 1): + scene.frame_set(i) + for track in self.bone_tracks: + values, pose_bone = track[0], track[1] + if parent := pose_bone.parent: + values += self.write_matrix( + (parent.matrix.inverted_safe() @ pose_bone.matrix) + ) + else: + values += self.write_matrix(pose_bone.matrix) + + def export_object(self, bobject, scene, parento=None): + if bobjectRef := self.bobjectArray.get(bobject): + o = {} + o["name"] = bobjectRef["structName"] + o["type"] = structIdentifier[bobjectRef["objectType"]] + o["data_ref"] = None + o["transform"] = self.write_matrix(bobject.matrix_local) + o["dimensions"] = None + o["visible"] = True + o["spawn"] = True + o["particles"] = None + o["anim"] = None + o["material_refs"] = None + o["children"] = None + + if bobject.parent_type == "BONE": + o["anim"]["parent_bone"] = bobject.parent_bone + + if bobjectRef["objectType"] == NodeTypeMesh: + objref = bobject.data + if objref not in self.meshArray: + self.meshArray[objref] = { + "structName": objref.name, + "objectTable": [bobject], + } + else: + self.meshArray[objref]["objectTable"].append(bobject) + oid = self.meshArray[objref]["structName"] + o["data_ref"] = oid + o["dimensions"] = self.calc_aabb(bobject) + + # If the object is parented to a bone and is not relative, undo the + # bone's transform + if bobject.parent_type == "BONE": + armature = bobject.parent.data + bone = armature.bones[bobject.parent_bone] + o["anim"]["parent_bone_connected"] = bone.use_connect + if bone.use_connect: + bone_translation = Vector((0, bone.length, 0)) + bone.head + o["anim"]["parent_bone_tail"] = [ + bone_translation[0], + bone_translation[1], + bone_translation[2], + ] + else: + bone_translation = bone.tail - bone.head + o["anim"]["parent_bone_tail"] = [ + bone_translation[0], + bone_translation[1], + bone_translation[2], + ] + pose_bone = bobject.parent.pose.bones[bobject.parent_bone] + bone_translation_pose = pose_bone.tail - pose_bone.head + o["anim"]["parent_bone_tail_pose"] = [ + bone_translation_pose[0], + bone_translation_pose[1], + bone_translation_pose[2], + ] + + if bobject.type == "ARMATURE" and bobject.data is not None: + bdata = bobject.data + action = None + adata = bobject.animation_data + + # Active action + if adata is not None: + action = adata.action + if action is None: + bobject.animation_data_create() + actions = bpy.data.actions + action = actions.get("armory_pose") + if action is None: + action = actions.new(name="armory_pose") + + # Collect export actions + export_actions = [action] + if hasattr(adata, "nla_tracks") and adata.nla_tracks is not None: + for track in adata.nla_tracks: + if track.strips is None: + continue + for strip in track.strips: + if strip.action is None: + continue + if strip.action.name == action.name: + continue + export_actions.append(strip.action) + + basename = os.path.basename(self.filepath)[:-4] + o["anim"]["bone_actions"] = [] + for action in export_actions: + o["anim"]["bone_actions"].append(basename + "_" + action.name) + + orig_action = bobject.animation_data.action + for action in export_actions: + bobject.animation_data.action = action + bones = [] + self.bone_tracks = [] + for bone in bdata.bones: + if not bone.parent: + boneo = {} + self.export_bone(bobject, bone, scene, boneo, action) + bones.append(boneo) + self.write_bone_matrices(scene, action) + if len(bones) > 0 and "anim" in bones[0]: + self.export_pose_markers(bones[0]["anim"], action) + # Save action separately + action_obj = {} + action_obj["name"] = action.name + action_obj["objects"] = bones + self.write_arm( + self.filepath[:-4] + "_" + action.name + ".arm", action_obj + ) + + bobject.animation_data.action = orig_action + + if parento is None: + self.output["objects"].append(o) + else: + parento["children"].append(o) + + if not hasattr(o, "children") and len(bobject.children) > 0: + o["children"] = [] + + for subbobject in bobject.children: + self.export_object(subbobject, scene, o) + + def export_skin(self, bobject, armature, exportMesh, o): + # This function exports all skinning data, which includes the skeleton + # and per-vertex bone influence data + oskin = {} + o["skin"] = oskin + + # Write the skin bind pose transform + otrans = {} + oskin["transform"] = otrans + otrans["values"] = self.write_matrix(bobject.matrix_world) + + bone_array = armature.data.bones + bone_count = len(bone_array) + max_bones = 128 + bone_count = min(bone_count, max_bones) + + # Write the bone object reference array + oskin["bone_ref_array"] = np.empty(bone_count, dtype=object) + oskin["bone_len_array"] = np.empty(bone_count, dtype="= 0: # and bone_weight != 0.0: + bone_values.append((bone_weight, bone_index)) + total_weight += bone_weight + bone_count += 1 + + if bone_count > 4: + bone_count = 4 + bone_values.sort(reverse=True) + bone_values = bone_values[:4] + + bone_count_array[index] = bone_count + for bv in bone_values: + bone_weight_array[count] = bv[0] + bone_index_array[count] = bv[1] + count += 1 + + if total_weight not in (0.0, 1.0): + normalizer = 1.0 / total_weight + for i in range(bone_count): + bone_weight_array[count - i - 1] *= normalizer + + bone_index_array = bone_index_array[:count] + bone_weight_array = bone_weight_array[:count] + bone_weight_array *= 32767 + bone_weight_array = np.array(bone_weight_array, dtype=" 0 + has_tex1 = num_uv_layers > 1 + has_col = num_colors > 0 + has_tang = False + + # Scale for packed coords + aabb = self.calc_aabb(bobject) + maxdim = max(aabb[0], max(aabb[1], aabb[2])) + if maxdim > 2: + o["scale_pos"] = maxdim / 2 + else: + o["scale_pos"] = 1.0 + if has_armature: # Allow up to 2x bigger bounds for skinned mesh + o["scale_pos"] *= 2.0 + + pdata = np.empty(num_verts * 4, dtype=" maxdim: + maxdim = abs(v.uv[0]) + if abs(v.uv[1]) > maxdim: + maxdim = abs(v.uv[1]) + if has_tex1: + lay1 = uv_layers[t1map] + for v in lay1.data: + if abs(v.uv[0]) > maxdim: + maxdim = abs(v.uv[0]) + if abs(v.uv[1]) > maxdim: + maxdim = abs(v.uv[1]) + if maxdim > 1: + o["scale_tex"] = maxdim + invscale_tex = (1 / o["scale_tex"]) * 32767 + else: + o["scale_tex"] = 1.0 + invscale_tex = 1 * 32767 + if has_tang: + exportMesh.calc_tangents(uvmap=lay0.name) + tangdata = np.empty(num_verts * 4, dtype=" 1: + for i in range(len(mats)): # Multi-mat mesh + if mats[i] == mats[index]: # Default material for empty slots + ia["material"] = i + break + ia["values"] = prim + o["index_arrays"].append(ia) + + def export_mesh(self, objectRef): + # This function exports a single mesh object + table = objectRef[1]["objectTable"] + bobject = table[0] + oid = objectRef[1]["structName"] + o = {} + o["name"] = oid + + armature = bobject.find_armature() + apply_modifiers = not armature + + bobject_eval = ( + bobject.evaluated_get(self.depsgraph) if apply_modifiers else bobject + ) + exportMesh = bobject_eval.to_mesh() + + self.export_mesh_data(exportMesh, bobject, o, has_armature=armature is not None) + if armature: + self.export_skin(bobject, armature, exportMesh, o) + + self.output["mesh_datas"].append(o) + bobject_eval.to_mesh_clear() + +def menu_func(self, context): + self.layout.operator(ArmoryExporter.bl_idname, text="Armory (.arm)") + +def register(): + bpy.utils.register_class(ArmoryExporter) + bpy.types.TOPBAR_MT_file_export.append(menu_func) + +def unregister(): + bpy.types.TOPBAR_MT_file_export.remove(menu_func) + bpy.utils.unregister_class(ArmoryExporter) + +if __name__ == "__main__": + register() + +# Msgpack parser with typed arrays +# Based on u-msgpack-python v2.4.1 - v at sergeev.io +# https://github.com/vsergeev/u-msgpack-python +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +def _pack_integer(obj, fp): + fp.write(b"\xd2" + struct.pack(" 0 and isinstance(obj[0], float): + fp.write(b"\xca") + for e in obj: + fp.write(struct.pack(" 0 and isinstance(obj[0], bool): + for e in obj: + pack(e, fp) + elif len(obj) > 0 and isinstance(obj[0], int): + fp.write(b"\xd2") + for e in obj: + fp.write(struct.pack(" 0 and isinstance(obj[0], np.float32): + fp.write(b"\xca") + fp.write(obj.tobytes()) + # Int32 + elif len(obj) > 0 and isinstance(obj[0], np.int32): + fp.write(b"\xd2") + fp.write(obj.tobytes()) + # Int16 + elif len(obj) > 0 and isinstance(obj[0], np.int16): + fp.write(b"\xd1") + fp.write(obj.tobytes()) + # Regular + else: + for e in obj: + pack(e, fp) + +def _pack_map(obj, fp): + fp.write(b"\xdf" + struct.pack("=8e3)&&(n=new ArrayBuffer(8e3),e=0),this.h=new Uint8Array(n,e,80),this.o=new Int32Array(n,e,20),e+=80}return t.prototype.update=function(t){if("string"==typeof t)return this.u(t);if(null==t)throw new TypeError("Invalid type: "+typeof t);var r=t.byteOffset,i=t.byteLength,n=i/64|0,s=0;if(n&&!(3&r)&&!(this.t%64)){for(var h=new Int32Array(t.buffer,r,16*n);n--;)this.v(h,s>>2),s+=64;this.t+=s}if(1!==t.BYTES_PER_ELEMENT&&t.buffer){var e=new Uint8Array(t.buffer,r+s,i-s);return this.p(e)}return s===i?this:this.p(t,s)},t.prototype.p=function(t,r){var i=this.h,n=this.o,s=t.length;for(r|=0;r=64&&this.v(n),this.t+=e-h}return this},t.prototype.u=function(t){for(var r=this.h,i=this.o,n=t.length,s=this.i,h=0;h>>6,r[f++]=128|63&o):o<55296||o>57343?(r[f++]=224|o>>>12,r[f++]=128|o>>>6&63,r[f++]=128|63&o):s?(o=((1023&s)<<10)+(1023&o)+65536,r[f++]=240|o>>>18,r[f++]=128|o>>>12&63,r[f++]=128|o>>>6&63,r[f++]=128|63&o,s=0):s=o}f>=64&&(this.v(i),i[0]=i[16]),this.t+=f-e}return this.i=s,this},t.prototype.v=function(t,i){var n=this,s=n.A,e=n.B,f=n.C,w=n.D,y=n.E,A=0;for(i|=0;A<16;)h[A++]=o(t[i++]);for(A=16;A<80;A++)h[A]=u(h[A-3]^h[A-8]^h[A-14]^h[A-16]);for(A=0;A<80;A++){var p=A/20|0,d=a(s)+v(p,e,f,w)+y+h[A]+r[p]|0;y=w,w=f,f=c(e),e=s,s=d}this.A=s+this.A|0,this.B=e+this.B|0,this.C=f+this.C|0,this.D=w+this.D|0,this.E=y+this.E|0},t.prototype.digest=function(t){var r=this.h,i=this.o,n=this.t%64|0;for(r[n++]=128;3&n;)r[n++]=0;if((n>>=2)>14){for(;n<16;)i[n++]=0;n=0,this.v(i)}for(;n<16;)i[n++]=0;var s=8*this.t,h=(4294967295&s)>>>0,e=(s-h)/4294967296;return e&&(i[14]=o(e)),h&&(i[15]=o(h)),this.v(i),"hex"===t?this.I():this.U()},t.prototype.I=function(){var t=this,r=t.A,i=t.B,n=t.C,s=t.D,h=t.E;return f(r)+f(i)+f(n)+f(s)+f(h)},t.prototype.U=function(){var t=this,r=t.A,i=t.B,n=t.C,s=t.D,h=t.E,e=t.h,f=t.o;return f[0]=o(r),f[1]=o(i),f[2]=o(n),f[3]=o(s),f[4]=o(h),e.slice(0,20)},t}(),h=new Int32Array(80),e=0,f=function(t){return(t+4294967296).toString(16).substr(-8)},o=254===new Uint8Array(new Uint16Array([65279]).buffer)[0]?function(t){return t}:function(t){return t<<24&4278190080|t<<8&16711680|t>>8&65280|t>>24&255},u=function(t){return t<<1|t>>>31},a=function(t){return t<<5|t>>>27},c=function(t){return t<<30|t>>>2};function v(t,r,i,n){return 0===t?r&i|~r&n:2===t?r&i|r&n|i&n:r^i^n}}(); + +function uuidv5(path, namespace) { + let hash = SHA1.createHash("sha1"); + hash.update(namespace); + hash.update(path); + let value = hash.digest("hex"); + return value.substring(0, 8) + "-" + value.substring(8, 12) + "-" + value.substring(12, 16) + "-" + value.substring(16, 20) + "-" + value.substring(20, 32); +} + +function path_join() { + let args = Array.from(arguments); + return path_normalize(args.join(path_sep)); +} + +function path_isabs(p) { + return p[0] == "/" || p[1] == ":" || (p[0] == "\\" && p[1] == "\\"); +} + +function _path_resolve(base, relative) { + let stack = base.split("/"); + let parts = relative.split("/"); + for (let i = 0; i < parts.length; i++) { + if (parts[i] == ".") { + continue; + } + if (parts[i] == "..") { + stack.pop(); + } + else { + stack.push(parts[i]); + } + } + return stack.join("/"); +} + +function path_resolve() { + let args = Array.from(arguments); + if (!path_isabs(args[0])) { + args.unshift(os_cwd()); + } + + let i = args.length - 1; + let p = args[i]; + p = path_normalize(p); + while (!path_isabs(p)) { + i--; + p = _path_resolve(args[i], p); + p = path_normalize(p); + } + return p; +} + +function path_relative(from, to) { + let a = from.split(path_sep); + let b = to.split(path_sep); + + while (a[0] == b[0]) { + a.shift(); + b.shift(); + if (a.length == 0 || b.length == 0) { + break; + } + } + let base = ""; + for (let i = 0; i < a.length; ++i) { + base += ".." + path_sep; + } + base += b.join(path_sep); + return base; +} + +function path_normalize(p) { + p = p.replaceAll(other_path_sep, path_sep); + while (p.indexOf(path_sep + path_sep) != -1) { + p = p.replaceAll(path_sep + path_sep, path_sep); + } + if (p.endsWith(path_sep)) { + p = p.substring(0, p.length - 1); + } + let ar = p.split(path_sep); + let i = 0; + while (i < ar.length) { + if (i > 0 && ar[i] == ".." && ar[i - 1] != "..") { + ar.splice(i - 1, 2); + i--; + } + else { + i++; + } + } + return ar.join(path_sep); +} + +function exe_ext() { + return os_platform() == "win32" ? ".exe" : ""; +} + +function path_extname(p) { + return p.substring(p.lastIndexOf("."), p.length); +} + +function path_basename(p) { + return p.substring(p.lastIndexOf(path_sep) + 1, p.length); +} + +function path_basename_noext(p) { + return p.substring(p.lastIndexOf(path_sep) + 1, p.lastIndexOf(".")); +} + +function path_dirname(p) { + return p.substring(0, p.lastIndexOf(path_sep)); +} + +function sys_dir() { + if (os_platform() === "linux") { + // if (os_arch() === "arm64") return "linux_arm64"; + return "linux_x64"; + } + else if (os_platform() === "win32") { + return "windows_x64"; + } + else { + return "macos"; + } +} + +function matches(text, pattern) { + let regexstring = pattern.replace(/\./g, "\\.").replace(/\*\*/g, ".?").replace(/\*/g, "[^/]*").replace(/\?/g, "*"); + let regex = new RegExp("^" + regexstring + "$", "g"); + return regex.test(text); +} + +function stringify(p) { + return p.replaceAll("\\", "/"); +} + +// ███████╗██╗ ██╗██████╗ ██████╗ ██████╗ ████████╗███████╗██████╗ ███████╗ +// ██╔════╝╚██╗██╔╝██╔══██╗██╔═══██╗██╔══██╗╚══██╔══╝██╔════╝██╔══██╗██╔════╝ +// █████╗ ╚███╔╝ ██████╔╝██║ ██║██████╔╝ ██║ █████╗ ██████╔╝███████╗ +// ██╔══╝ ██╔██╗ ██╔═══╝ ██║ ██║██╔══██╗ ██║ ██╔══╝ ██╔══██╗╚════██║ +// ███████╗██╔╝ ██╗██║ ╚██████╔╝██║ ██║ ██║ ███████╗██║ ██║███████║ +// ╚══════╝╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝ + +class Exporter { + constructor() { + this.path = null; + this.outFile = null; + } + + write_file(file) { + this.path = file; + this.outFile = ""; + } + + close_file() { + fs_writefile(this.path, this.outFile); + this.outFile = ""; + } + + p(line = "", indent = 0) { + let tabs = ""; + for (let i = 0; i < indent; ++i) { + tabs += "\t"; + } + this.outFile += tabs + line + "\n"; + } + + nice_path(from, to, filepath) { + return filepath; + // let absolute = path_normalize(filepath); + // if (!path_isabs(absolute)) { + // absolute = path_resolve(from, filepath); + // } + // return path_relative(to, absolute); + } +} + +function get_dir_from_string(file, base) { + file = file.replace(/\\/g, '/'); + if (file.indexOf("/") >= 0) { + let dir = file.substr(0, file.lastIndexOf("/")); + return path_join(base, path_relative(base, dir)).replace(/\\/g, "/"); + } + else { + return base; + } +} + +function get_dir(file) { + return get_dir_from_string(file.file, file.projectName); +} + +class VisualStudioExporter extends Exporter { + constructor() { + super(); + } + + get_debug_dir(from, project) { + let debugdir = project.get_debug_dir(); + if (path_isabs(debugdir)) { + debugdir = debugdir.replace(/\//g, "\\"); + } + else { + debugdir = path_resolve(from, debugdir).replace(/\//g, "\\"); + } + return debugdir; + } + + export_user_file(from, to, project, platform) { + if (project.get_debug_dir() === "") + return; + this.write_file(path_resolve(to, project.get_safe_name() + ".vcxproj.user")); + this.p(''); + this.p(''); + this.p('', 1); + if (platform === "windows") { + this.p('' + this.get_debug_dir(from, project) + '', 2); + this.p('WindowsLocalDebugger', 2); + project.cmdArgs.push(this.get_debug_dir(from, project)); + if (project.cmdArgs.length > 0) { + this.p('' + project.cmdArgs.join(' ') + '', 2); + } + } + this.p('', 1); + this.p(''); + this.close_file(); + } + + write_project_declarations(project, solutionUuid) { + this.p('Project("{' + solutionUuid.toUpperCase() + '}") = "' + project.get_safe_name() + '", "' + project.get_safe_name() + '.vcxproj", "{' + project.get_uuid().toString().toUpperCase() + '}"'); + if (project.getSubProjects().length > 0) { + this.p('ProjectSection(ProjectDependencies) = postProject', 1); + for (let proj of project.getSubProjects()) { + this.p('{' + proj.get_uuid().toString().toUpperCase() + '} = {' + proj.get_uuid().toString().toUpperCase() + '}', 2); + } + this.p('EndProjectSection', 1); + } + this.p('EndProject'); + for (let proj of project.getSubProjects()) + this.write_project_declarations(proj, solutionUuid); + } + + get_configs() { + return ["Debug", "Develop", "Release"]; + } + + get_systems() { + return ["x64"]; + } + + write_project_builds(project, platform) { + for (let config of this.get_configs()) { + for (let system of this.get_systems()) { + this.p('{' + project.get_uuid().toString().toUpperCase() + '}.' + config + '|' + system + '.ActiveCfg = ' + config + '|' + system, 2); + this.p('{' + project.get_uuid().toString().toUpperCase() + '}.' + config + '|' + system + '.Build.0 = ' + config + '|' + system, 2); + } + } + for (let proj of project.getSubProjects()) + this.write_project_builds(proj, platform); + } + + export_solution(project) { + let from = path_resolve("."); + let to = path_resolve("build"); + let platform = goptions.target; + this.write_file(path_resolve(to, project.get_safe_name() + '.sln')); + if (goptions.visualstudio === 'vs2022') { + this.p('Microsoft Visual Studio Solution File, Format Version 12.00'); + this.p('# Visual Studio Version 17'); + this.p('VisualStudioVersion = 17.0.31903.59'); + this.p('MinimumVisualStudioVersion = 10.0.40219.1'); + } + let solutionUuid = crypto_random_uuid(); + this.write_project_declarations(project, solutionUuid); + this.p('Global'); + this.p('GlobalSection(SolutionConfigurationPlatforms) = preSolution', 1); + for (let config of this.get_configs()) { + for (let system of this.get_systems()) { + this.p(config + '|' + system + ' = ' + config + '|' + system, 2); + } + } + this.p('EndGlobalSection', 1); + this.p('GlobalSection(ProjectConfigurationPlatforms) = postSolution', 1); + this.write_project_builds(project, platform); + this.p('EndGlobalSection', 1); + this.p('GlobalSection(SolutionProperties) = preSolution', 1); + this.p('HideSolutionNode = FALSE', 2); + this.p('EndGlobalSection', 1); + this.p('EndGlobal'); + this.close_file(); + this.export_project(from, to, project, platform, false, goptions); + this.export_filters(from, to, project, platform); + this.export_user_file(from, to, project, platform); + if (platform === 'windows') { + this.export_resource_script(to); + export_ico(project.icon, path_resolve(to, 'icon.ico'), from); + } + } + + export_resource_script(to) { + this.write_file(path_resolve(to, "resources.rc")); + this.p('107 ICON "icon.ico"'); + this.close_file(); + } + + pretty_dir(dir) { + let pretty_dir = dir; + while (pretty_dir.startsWith("../")) { + pretty_dir = pretty_dir.substring(3); + } + return pretty_dir.replace(/\//g, "\\"); + } + + item_group(from, to, project, type, filter) { + let lastdir = ""; + this.p('', 1); + for (let file of project.getFiles()) { + let dir = get_dir(file); + if (dir !== lastdir) + lastdir = dir; + if (filter(file)) { + let filepath = ""; + if (project.noFlatten && !path_isabs(file.file)) { + filepath = path_resolve(path_join(project.basedir, file.file)); + } + else { + filepath = this.nice_path(from, to, file.file); + } + this.p('<' + type + ' Include="' + filepath + '">', 2); + this.p('' + this.pretty_dir(dir) + '', 3); + this.p('', 2); + } + } + this.p('', 1); + } + + export_filters(from, to, project, platform) { + for (let proj of project.getSubProjects()) + this.export_filters(from, to, proj, platform); + this.write_file(path_resolve(to, project.get_safe_name() + '.vcxproj.filters')); + this.p(''); + this.p(''); + let lastdir = ''; + let dirs = []; + for (let file of project.getFiles()) { + let dir = get_dir(file); + if (dir !== lastdir) { + let subdir = dir; + while (subdir.indexOf('/') >= 0) { + subdir = subdir.substr(0, subdir.lastIndexOf('/')); + if (!dirs.includes(subdir)) + dirs.push(subdir); + } + dirs.push(dir); + lastdir = dir; + } + } + let assets = []; + this.p('', 1); + for (let dir of dirs) { + let pretty = this.pretty_dir(dir); + if (pretty !== '..') { + this.p('', 2); + this.p('{' + crypto_random_uuid().toString().toUpperCase() + '}', 3); + this.p('', 2); + } + } + this.p('', 1); + this.item_group(from, to, project, 'ClInclude', (file) => { + return file.file.endsWith(".h"); + }); + this.item_group(from, to, project, 'ClCompile', (file) => { + return file.file.endsWith(".cpp") || file.file.endsWith(".c") || file.file.endsWith(".cc"); + }); + this.item_group(from, to, project, 'CustomBuild', (file) => { + return file.file.endsWith(".hlsl") || file.file.endsWith(".glsl"); + }); + if (platform === "windows") { + this.item_group(from, to, project, "ResourceCompile", (file) => { + return file.file.endsWith(".rc"); + }); + } + this.p(''); + this.close_file(); + } + + get_platform_toolset() { + // return 'v143'; + return 'ClangCL'; + } + + configuration(config, indent, project) { + this.p('', indent); + this.p('Application', indent + 1); + this.p('' + (config === "Release" ? "false" : "true") + '', indent + 1); + this.p('' + this.get_platform_toolset() + '', indent + 1); + this.p('x64', indent + 1); + if (config === "Release" && project.lto) { + this.p('true', indent + 1); + } + this.p('Unicode', indent + 1); + this.p('', indent); + } + + get_optimization(config) { + switch (config) { + case "Debug": + default: + return "Disabled"; + case "Develop": + return "Full"; + case "Release": + return "MaxSpeed"; + } + } + + cStd(project) { + switch (project.cStd.toLowerCase()) { + case "c99": + return ""; + case "c11": + return "stdc11"; + case "c17": + return "stdc17"; + case "c2x": + return "stdc17"; + } + } + + cppStd(project) { + switch (project.cppStd.toLowerCase()) { + case "c++17": + return "stdcpp17"; + case "c++23": + return "stdcpplatest"; + } + } + + item_definition(config, system, includes, debugDefines, releaseDefines, indent, debuglibs, releaselibs, project) { + this.p('', indent); + this.p('', indent + 1); + this.p('' + includes + '', indent + 2); + this.p('/bigobj %(AdditionalOptions)', indent + 2); + this.p('Level3', indent + 2); + this.p('' + this.get_optimization(config) + '', indent + 2); + if (config === 'Release') { + this.p('true', indent + 2); + this.p('true', indent + 2); + } + this.p('' + (config === 'Release' ? releaseDefines : debugDefines) + ((system === 'x64') ? 'SYS_64;' : '') + 'WIN32;_WINDOWS;%(PreprocessorDefinitions)', indent + 2); + this.p('' + (config === 'Release' ? 'MultiThreaded' : 'MultiThreadedDebug') + '', indent + 2); + this.p('true', indent + 2); + this.p('false', indent + 2); + if (config === 'Develop') { + this.p('Default', indent + 2); + } + let cStd = this.cStd(project); + this.p('' + cStd + '', indent + 2); + let cppStd = this.cppStd(project); + this.p('' + cppStd + '', indent + 2); + + this.p('', indent + 1); + this.p('', indent + 1); + if (project.name == "amake") { // TODO + this.p('Console', indent + 2); + } + else { + this.p('Windows', indent + 2); + } + this.p('true', indent + 2); + if (config === 'Release') { + this.p('true', indent + 2); + this.p('true', indent + 2); + } + + let libs = config === 'Release' ? releaselibs : debuglibs; + this.p('' + libs + 'kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)', indent + 2); + this.p('', indent + 1); + this.p('', indent + 1); + this.p('PerMonitorHighDPIAware', indent + 2); + this.p('', indent + 1); + this.p('', indent); + } + + windowsSDKs() { + // Environment* env = Environment::GetCurrent(args); + // Isolate* isolate = env->isolate(); + // std::vector> result; + // HKEY key; + // LSTATUS status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, KEY_READ | KEY_QUERY_VALUE, &key); + // char name[256]; + // DWORD nameLength; + // for (DWORD i = 0;; ++i) { + // nameLength = sizeof(name); + // status = RegEnumKeyExA(key, i, &name[0], &nameLength, NULL, NULL, NULL, NULL); + // if (status != ERROR_SUCCESS) { + // break; + // } + // result.emplace_back(String::NewFromUtf8(isolate, name).ToLocalChecked()); + // } + // RegCloseKey(key); + // args.GetReturnValue().Set(Array::New(isolate, result.data(), result.size())); + } + + findWindowsSdk() { + let sdks = windowsSDKs(); + let best = [0, 0, 0, 0]; + for (let key of sdks) { + let elements = key.split('\\'); + let last = elements[elements.length - 1]; + if (last.indexOf('.') >= 0) { + let numstrings = last.split('.'); + let nums = []; + for (let str of numstrings) { + nums.push(parseInt(str)); + } + if (nums[0] > best[0]) { + best = nums; + } + else if (nums[0] === best[0]) { + if (nums[1] > best[1]) { + best = nums; + } + else if (nums[1] === best[1]) { + if (nums[2] > best[2]) { + best = nums; + } + else if (nums[2] === best[2]) { + if (nums[3] > best[3]) { + best = nums; + } + } + } + } + } + } + if (best[0] > 0) { + return best[0] + '.' + best[1] + '.' + best[2] + '.' + best[3]; + } + else { + return null; + } + } + + globals(indent) { + if (goptions.visualstudio === 'vs2022') { + this.p('16.0', indent); + this.p('10.0', indent); + } + } + + extension_settings(indent) { + this.p('', indent); + } + + extension_targets(indent) { + this.p('', indent); + } + + export_project(from, to, project, platform, cmd, options) { + for (let proj of project.getSubProjects()) { + this.export_project(from, to, proj, platform, cmd, options); + } + this.write_file(path_resolve(to, project.get_safe_name() + '.vcxproj')); + this.p(''); + this.p(''); + this.p('', 1); + for (let system of this.get_systems()) { + for (let config of this.get_configs()) { + this.p('', 2); + this.p('' + config + '', 3); + this.p('' + system + '', 3); + this.p('', 2); + } + } + this.p('', 1); + this.p('', 1); + this.p('{' + project.get_uuid().toString().toUpperCase() + '}', 2); + this.globals(2); + this.p('', 1); + this.p('', 1); + for (let config of this.get_configs()) { + for (let system of this.get_systems()) { + this.configuration(config, 1, project); + } + } + this.p('', 1); + this.p('', 1); + this.extension_settings(2); + this.p('', 1); + this.p('', 1); + if (project.get_executable_name()) { + this.p('', 1); + this.p('' + project.get_executable_name() + '', 2); + this.p('', 1); + } + if (platform === 'windows') { + for (let system of this.get_systems()) { + this.p('', 1); + this.p('', 2); + this.p('', 1); + } + } + let debugDefines = "_DEBUG;"; + let releaseDefines = "NDEBUG;"; + for (let define of project.getDefines()) { + debugDefines += define + ";"; + releaseDefines += define + ";"; + } + let incstring = ""; + let includedirs = project.getIncludeDirs(); + for (let include of includedirs) { + let relativized = path_relative(to, path_resolve(from, include)); + if (relativized === "") { + relativized = "."; + } + incstring += relativized + ";"; + } + if (incstring.length > 0) + incstring = incstring.substr(0, incstring.length - 1); + let debuglibs = ""; + for (let proj of project.getSubProjects()) { + if (proj.noFlatten) { + debuglibs += project.basedir + "\\build\\x64\\Debug\\" + proj.get_safe_name() + ".lib;"; + } + else { + debuglibs += "Debug\\" + proj.get_safe_name() + ".lib;"; + } + } + for (let lib of project.getLibs()) { + if (fs_exists(path_resolve(from, lib + ".lib"))) { + debuglibs += path_relative(to, path_resolve(from, lib)) + ".lib;"; + } + else { + debuglibs += lib + ".lib;"; + } + } + let releaselibs = ""; + for (let proj of project.getSubProjects()) { + if (proj.noFlatten) { + releaselibs += project.basedir + "\\build\\x64\\Release\\" + proj.get_safe_name() + ".lib;"; + } + else { + releaselibs += "Release\\" + proj.get_safe_name() + ".lib;"; + } + } + for (let proj of project.getSubProjects()) + releaselibs += "Release\\" + proj.get_safe_name() + ".lib;"; + for (let lib of project.getLibs()) { + if (fs_exists(path_resolve(from, lib + ".lib"))) { + releaselibs += path_relative(to, path_resolve(from, lib)) + ".lib;"; + } + else { + releaselibs += lib + ".lib;"; + } + } + for (let config of this.get_configs()) { + for (let system of this.get_systems()) { + this.item_definition(config, system, incstring, debugDefines, releaseDefines, 2, debuglibs, releaselibs, project); + } + } + this.p('', 1); + for (let file of project.getFiles()) { + let filepath = ""; + if (project.noFlatten && !path_isabs(file.file)) { + filepath = path_resolve(project.basedir + "/" + file.file); + } + else { + filepath = this.nice_path(from, to, file.file); + } + if (file.file.endsWith(".h")) + this.p('', 2); + } + this.p('', 1); + this.p('', 1); + let objects = {}; + for (let fileobject of project.getFiles()) { + let file = fileobject.file; + if (file.endsWith(".cpp") || file.endsWith(".c") || file.endsWith("cc")) { + let name = file.toLowerCase(); + if (name.indexOf("/") >= 0) + name = name.substr(name.lastIndexOf("/") + 1); + name = name.substr(0, name.lastIndexOf(".")); + let filepath = ""; + if (project.noFlatten && !path_isabs(file)) { + filepath = path_resolve(project.basedir + "/" + file); + } + else { + filepath = this.nice_path(from, to, file); + } + if (!objects[name]) { + this.p('', 2); + objects[name] = true; + } + else { + while (objects[name]) { + name = name + '_'; + } + this.p('', 2); + this.p('$(IntDir)\\' + name + '.obj', 3); + this.p('', 2); + objects[name] = true; + } + } + } + this.p('', 1); + this.p('', 1); + for (let file of project.getFiles()) { + if (file.file.endsWith('.natvis')) { + this.p('', 2); + } + } + this.p('', 1); + if (platform === "windows") { + this.p('', 1); + for (let file of project.customs) { + this.p('', 2); + this.p('Document', 2); + this.p('' + file.command + '', 2); + this.p('' + file.output + '', 2); + this.p('%(Filename)%(Extension)', 2); + this.p('', 2); + } + this.p(''); + this.p('', 1); + this.p('', 2); + this.p('', 1); + this.p('', 1); + this.p('', 2); + for (let file of project.getFiles()) { + if (file.file.endsWith('.rc')) { + this.p('', 2); + } + } + this.p('', 1); + } + this.p('', 1); + this.p('', 1); + this.extension_targets(2); + this.p('', 1); + this.p(''); + this.close_file(); + } +} + +class WasmExporter extends Exporter { + constructor() { + super(); + this.compile_commands = new CompilerCommandsExporter(); + let compiler = "clang"; + let compilerFlags = "--target=wasm32 -nostdlib -matomics -mbulk-memory"; + this.make = new MakeExporter(compiler, compiler, compilerFlags, compilerFlags, '--target=wasm32 -nostdlib -matomics -mbulk-memory "-Wl,--import-memory,--shared-memory"', '.wasm'); + } + + export_solution(project) { + this.make.export_solution(project); + this.compile_commands.export_solution(project); + } +} + +function new_path_id(path) { + return uuidv5(path, "7448ebd8-cfc8-4f45-8b3d-5df577ceea6d").toUpperCase(); +} + +function get_dir2(file) { + if (file.file.indexOf("/") >= 0) { + let dir = file.file.substr(0, file.file.lastIndexOf("/")); + return path_join(file.projectName, path_relative(file.projectDir, dir)).replace(/\\/g, "/"); + } + else { + return file.projectName; + } +} + +class Directory { + constructor(dirname) { + this.dirname = dirname; + this.id = new_path_id(dirname); + } + + getName() { + return this.dirname; + } + + getLastName() { + if (this.dirname.indexOf("/") < 0) + return this.dirname; + return this.dirname.substr(this.dirname.lastIndexOf("/") + 1); + } + + getId() { + return this.id; + } +} + +class File { + constructor(filename, dir) { + this.filename = filename; + this.dir = dir; + this.buildid = new_path_id(dir + filename + "_buildid"); + this.fileid = new_path_id(dir + filename + "_fileid"); + } + + getBuildId() { + return this.buildid; + } + + getFileId() { + return this.fileid; + } + + isBuildFile() { + return this.filename.endsWith(".c") || this.filename.endsWith(".cpp") || this.filename.endsWith(".m") || this.filename.endsWith(".mm") || this.filename.endsWith(".cc") || this.filename.endsWith(".metal") || this.filename.endsWith(".storyboard"); + } + + getName() { + return this.filename; + } + + getLastName() { + if (this.filename.indexOf("/") < 0) + return this.filename; + return this.filename.substr(this.filename.lastIndexOf("/") + 1); + } + + get_dir() { + return this.dir; + } + + toString() { + return this.getName(); + } +} + +class Framework { + constructor(name) { + this.name = name; + this.buildid = new_path_id(name + "_buildid"); + this.fileid = new_path_id(name + "_fileid"); + this.localPath = null; + } + + toString() { + if (this.name.indexOf(".") < 0) + return this.name + ".framework"; + else + return this.name; + } + + getBuildId() { + return this.buildid.toString().toUpperCase(); + } + + getFileId() { + return this.fileid.toString().toUpperCase(); + } +} + +function findDirectory(dirname, directories) { + for (let dir of directories) { + if (dir.getName() === dirname) { + return dir; + } + } + return null; +} + +function addDirectory(dirname, directories) { + let dir = findDirectory(dirname, directories); + if (dir === null) { + dir = new Directory(dirname); + directories.push(dir); + while (dirname.indexOf("/") >= 0) { + dirname = dirname.substr(0, dirname.lastIndexOf("/")); + addDirectory(dirname, directories); + } + } + return dir; +} + +class IconImage { + constructor(idiom, size, scale) { + this.idiom = idiom; + this.size = size; + this.scale = scale; + } +} + +class XCodeExporter extends Exporter { + constructor() { + super(); + } + + exportWorkspace(to, project) { + let dir = path_resolve(to, project.get_safe_name() + ".xcodeproj", "project.xcworkspace"); + fs_ensuredir(dir); + this.write_file(path_resolve(to, project.get_safe_name() + ".xcodeproj", "project.xcworkspace", "contents.xcworkspacedata")); + this.p(''); + this.p(''); + this.p(''); + this.p(''); + this.p(''); + this.close_file(); + } + + export_solution(project) { + let from = path_resolve("."); + let to = path_resolve("build"); + let platform = goptions.target; + let xdir = path_resolve(to, project.get_safe_name() + ".xcodeproj"); + fs_ensuredir(xdir); + this.exportWorkspace(to, project); + function add_icons(icons, idiom, sizes, scales) { + for (let i = 0; i < sizes.length; ++i) { + icons.push(new IconImage(idiom, sizes[i], scales[i])); + } + } + let icons = []; + if (platform === "ios") { + add_icons(icons, "iphone", [20, 20, 29, 29, 40, 40, 60, 60], [2, 3, 2, 3, 2, 3, 2, 3]); + add_icons(icons, "ipad", [20, 20, 29, 29, 40, 40, 76, 76, 83.5], [1, 2, 1, 2, 1, 2, 1, 2, 2]); + icons.push(new IconImage("ios-marketing", 1024, 1)); + } + else { + add_icons(icons, "mac", [16, 16, 32, 32, 128, 128, 256, 256, 512, 512], [1, 2, 1, 2, 1, 2, 1, 2, 1, 2]); + } + let iconsdir = path_resolve(to, "Images.xcassets", "AppIcon.appiconset"); + fs_ensuredir(iconsdir); + this.write_file(path_resolve(to, "Images.xcassets", "AppIcon.appiconset", "Contents.json")); + this.p('{'); + this.p('"images" : [', 1); + for (let i = 0; i < icons.length; ++i) { + let icon = icons[i]; + this.p('{', 2); + this.p('"idiom" : "' + icon.idiom + '",', 3); + this.p('"size" : "' + icon.size + 'x' + icon.size + '",', 3); + this.p('"filename" : "' + icon.idiom + icon.scale + 'x' + icon.size + '.png",', 3); + this.p('"scale" : "' + icon.scale + 'x"', 3); + if (i === icons.length - 1) + this.p('}', 2); + else + this.p('},', 2); + } + this.p('],', 1); + this.p('"info" : {', 1); + this.p('"version" : 1,', 2); + this.p('"author" : "xcode"', 2); + this.p('}', 1); + this.p('}'); + this.close_file(); + for (let i = 0; i < icons.length; ++i) { + let icon = icons[i]; + export_png_icon(project.icon, path_resolve(to, 'Images.xcassets', 'AppIcon.appiconset', icon.idiom + icon.scale + 'x' + icon.size + '.png'), icon.size * icon.scale, icon.size * icon.scale, from); + } + let plistname = ""; + let files = []; + let directories = []; + for (let fileobject of project.getFiles()) { + let filename = fileobject.file; + if (filename.endsWith(".plist")) { + plistname = filename; + } + let dir = addDirectory(get_dir2(fileobject), directories); + let file = new File(filename, dir); + files.push(file); + } + if (plistname.length === 0) { + throw "no plist found"; + } + let frameworks = []; + for (let lib of project.getLibs()) { + frameworks.push(new Framework(lib)); + } + let target_options = { + bundle: 'org.$(PRODUCT_NAME)', + version: "1.0", + build: "1", + organizationName: "Armory3D", + developmentTeam: "" + }; + if (project.target_options && project.target_options.ios) { + let userOptions = project.target_options.ios; + if (userOptions.bundle) + target_options.bundle = userOptions.bundle; + if (userOptions.version) + target_options.version = userOptions.version; + if (userOptions.build) + target_options.build = userOptions.build; + if (userOptions.organizationName) + target_options.organizationName = userOptions.organizationName; + if (userOptions.developmentTeam) + target_options.developmentTeam = userOptions.developmentTeam; + } + let projectId = new_path_id("_projectId"); + let appFileId = new_path_id("_appFileId"); + let frameworkBuildId = new_path_id("_frameworkBuildId"); + let sourceBuildId = new_path_id("_sourceBuildId"); + let frameworksGroupId = new_path_id("_frameworksGroupId"); + let productsGroupId = new_path_id("_productsGroupId"); + let mainGroupId = new_path_id("_mainGroupId"); + let targetId = new_path_id("_targetId"); + let nativeBuildConfigListId = new_path_id("_nativeBuildConfigListId"); + let projectBuildConfigListId = new_path_id("_projectBuildConfigListId"); + let debugId = new_path_id("_debugId"); + let releaseId = new_path_id("_releaseId"); + let nativeDebugId = new_path_id("_nativeDebugId"); + let nativeReleaseId = new_path_id("_nativeReleaseId"); + let debugDirFileId = new_path_id("_debugDirFileId"); + let debugDirBuildId = new_path_id("_debugDirBuildId"); + let resourcesBuildId = new_path_id("_resourcesBuildId"); + let iconFileId = new_path_id("_iconFileId"); + let iconBuildId = new_path_id("_iconBuildId"); + this.write_file(path_resolve(to, project.get_safe_name() + ".xcodeproj", "project.pbxproj")); + this.p('// !$*UTF8*$!'); + this.p('{'); + this.p('archiveVersion = 1;', 1); + this.p('classes = {', 1); + this.p('};', 1); + this.p('objectVersion = 46;', 1); + this.p('objects = {', 1); + this.p(); + this.p('/* Begin PBXBuildFile section */'); + for (let framework of frameworks) { + this.p(framework.getBuildId() + ' /* ' + framework.toString() + ' in Frameworks */ = {isa = PBXBuildFile; fileRef = ' + framework.getFileId() + ' /* ' + framework.toString() + ' */; };', 2); + } + this.p(debugDirBuildId + ' /* Deployment in Resources */ = {isa = PBXBuildFile; fileRef = ' + debugDirFileId + ' /* Deployment */; };', 2); + for (let file of files) { + if (file.isBuildFile()) { + this.p(file.getBuildId() + ' /* ' + file.toString() + ' in Sources */ = {isa = PBXBuildFile; fileRef = ' + file.getFileId() + ' /* ' + file.toString() + ' */; };', 2); + } + } + this.p(iconBuildId + ' /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ' + iconFileId + ' /* Images.xcassets */; };', 2); + this.p('/* End PBXBuildFile section */'); + this.p(); + this.p('/* Begin PBXFileReference section */'); + let executable_name = project.get_safe_name(); + if (project.get_executable_name()) { + executable_name = project.get_executable_name(); + } + this.p(appFileId + ' /* ' + project.get_safe_name() + '.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "' + executable_name + '.app"; sourceTree = BUILT_PRODUCTS_DIR; };', 2); + for (let framework of frameworks) { + if (framework.toString().endsWith('.framework')) { + // Local framework - a directory is specified + if (framework.toString().indexOf('/') >= 0) { + framework.localPath = path_resolve(from, framework.toString()); + this.p(framework.getFileId() + ' /* ' + framework.toString() + ' */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ' + framework.toString() + '; path = ' + framework.localPath + '; sourceTree = ""; };', 2); + } + // XCode framework + else { + this.p(framework.getFileId() + ' /* ' + framework.toString() + ' */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ' + framework.toString() + '; path = System/Library/Frameworks/' + framework.toString() + '; sourceTree = SDKROOT; };', 2); + } + } + else if (framework.toString().endsWith('.dylib')) { + if (framework.toString().indexOf('/') >= 0) { + framework.localPath = path_resolve(from, framework.toString()); + this.p(framework.getFileId() + ' /* ' + framework.toString() + ' */ = {isa = PBXFileReference; lastKnownFileType = compiled.mach-o.dylib; name = ' + framework.toString() + '; path = ' + framework.localPath + '; sourceTree = ""; };', 2); + } + else { + this.p(framework.getFileId() + ' /* ' + framework.toString() + ' */ = {isa = PBXFileReference; lastKnownFileType = compiled.mach-o.dylib; name = ' + framework.toString() + '; path = usr/lib/' + framework.toString() + '; sourceTree = SDKROOT; };', 2); + } + } + else { + framework.localPath = path_resolve(from, framework.toString()); + this.p(framework.getFileId() + ' /* ' + framework.toString() + ' */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = ' + framework.toString() + '; path = ' + framework.localPath + '; sourceTree = ""; };', 2); + } + } + this.p(debugDirFileId + ' /* Deployment */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Deployment; path = "' + path_resolve(from, project.get_debug_dir()) + '"; sourceTree = ""; };', 2); + for (let file of files) { + let filetype = "unknown"; + let fileencoding = ""; + if (file.getName().endsWith(".storyboard")) + filetype = "file.storyboard"; + if (file.getName().endsWith(".plist")) + filetype = "text.plist.xml"; + if (file.getName().endsWith(".h")) + filetype = "sourcecode.c.h"; + if (file.getName().endsWith(".m")) + filetype = "sourcecode.c.objc"; + if (file.getName().endsWith(".c")) + filetype = "sourcecode.c.c"; + if (file.getName().endsWith(".cpp")) + filetype = "sourcecode.c.cpp"; + if (file.getName().endsWith(".cc")) + filetype = "sourcecode.c.cpp"; + if (file.getName().endsWith(".mm")) + filetype = "sourcecode.c.objcpp"; + if (file.getName().endsWith(".metal")) { + filetype = "sourcecode.metal"; + fileencoding = "fileEncoding = 4; "; + } + if (!file.getName().endsWith(".DS_Store")) { + this.p(file.getFileId() + ' /* ' + file.toString() + ' */ = {isa = PBXFileReference; ' + fileencoding + 'lastKnownFileType = ' + filetype + '; name = "' + file.getLastName() + '"; path = "' + path_resolve(from, file.toString()) + '"; sourceTree = ""; };', 2); + } + } + this.p(iconFileId + ' /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };', 2); + this.p('/* End PBXFileReference section */'); + this.p(); + this.p('/* Begin PBXFrameworksBuildPhase section */'); + this.p(frameworkBuildId + ' /* Frameworks */ = {', 2); + this.p('isa = PBXFrameworksBuildPhase;', 3); + this.p('buildActionMask = 2147483647;', 3); + this.p('files = (', 3); + for (let framework of frameworks) { + this.p(framework.getBuildId() + ' /* ' + framework.toString() + ' in Frameworks */,', 4); + } + this.p(');', 3); + this.p('runOnlyForDeploymentPostprocessing = 0;', 3); + this.p('};', 2); + this.p('/* End PBXFrameworksBuildPhase section */'); + this.p(); + this.p('/* Begin PBXGroup section */'); + this.p(mainGroupId + ' = {', 2); + this.p('isa = PBXGroup;', 3); + this.p('children = (', 3); + this.p(iconFileId + ' /* Images.xcassets */,', 4); + this.p(debugDirFileId + ' /* Deployment */,', 4); + for (let dir of directories) { + if (dir.getName().indexOf('/') < 0) + this.p(dir.getId() + ' /* ' + dir.getName() + ' */,', 4); + } + this.p(frameworksGroupId + ' /* Frameworks */,', 4); + this.p(productsGroupId + ' /* Products */,', 4); + this.p(');', 3); + this.p('sourceTree = "";', 3); + this.p('};', 2); + this.p(productsGroupId + ' /* Products */ = {', 2); + this.p('isa = PBXGroup;', 3); + this.p('children = (', 3); + this.p(appFileId + ' /* ' + project.get_safe_name() + '.app */,', 4); + this.p(');', 3); + this.p('name = Products;', 3); + this.p('sourceTree = "";', 3); + this.p('};', 2); + this.p(frameworksGroupId + ' /* Frameworks */ = {', 2); + this.p('isa = PBXGroup;', 3); + this.p('children = (', 3); + for (let framework of frameworks) { + this.p(framework.getFileId() + ' /* ' + framework.toString() + ' */,', 4); + } + this.p(');', 3); + this.p('name = Frameworks;', 3); + this.p('sourceTree = "";', 3); + this.p('};', 2); + for (let dir of directories) { + this.p(dir.getId() + ' /* ' + dir.getName() + ' */ = {', 2); + this.p('isa = PBXGroup;', 3); + this.p('children = (', 3); + for (let dir2 of directories) { + if (dir2 === dir) + continue; + if (dir2.getName().startsWith(dir.getName())) { + if (dir2.getName().substr(dir.getName().length + 1).indexOf('/') < 0) + this.p(dir2.getId() + ' /* ' + dir2.getName() + ' */,', 4); + } + } + for (let file of files) { + if (file.get_dir() === dir && !file.getName().endsWith('.DS_Store')) + this.p(file.getFileId() + ' /* ' + file.toString() + ' */,', 4); + } + this.p(');', 3); + if (dir.getName().indexOf('/') < 0) { + this.p('path = ../;', 3); + this.p('name = "' + dir.getLastName() + '";', 3); + } + else + this.p('name = "' + dir.getLastName() + '";', 3); + this.p('sourceTree = "";', 3); + this.p('};', 2); + } + this.p('/* End PBXGroup section */'); + this.p(); + this.p('/* Begin PBXNativeTarget section */'); + this.p(targetId + ' /* ' + project.get_safe_name() + ' */ = {', 2); + this.p('isa = PBXNativeTarget;', 3); + this.p('buildConfigurationList = ' + nativeBuildConfigListId + ' /* Build configuration list for PBXNativeTarget "' + project.get_safe_name() + '" */;', 3); + this.p('buildPhases = (', 3); + this.p(sourceBuildId + ' /* Sources */,', 4); + this.p(frameworkBuildId + ' /* Frameworks */,', 4); + this.p(resourcesBuildId + ' /* Resources */,', 4); + this.p(');', 3); + this.p('buildRules = (', 3); + this.p(');', 3); + this.p('dependencies = (', 3); + this.p(');', 3); + this.p('name = "' + project.getName() + '";', 3); + this.p('productName = "' + project.getName() + '";', 3); + this.p('productReference = ' + appFileId + ' /* ' + project.get_safe_name() + '.app */;', 3); + if (project.name == "amake") { // TODO + this.p('productType = "com.apple.product-type.tool";', 3); + } + else { + this.p('productType = "com.apple.product-type.application";', 3); + } + this.p('};', 2); + this.p('/* End PBXNativeTarget section */'); + this.p(); + this.p('/* Begin PBXProject section */'); + this.p(projectId + ' /* Project object */ = {', 2); + this.p('isa = PBXProject;', 3); + this.p('attributes = {', 3); + this.p('LastUpgradeCheck = 1230;', 4); + this.p('ORGANIZATIONNAME = "' + target_options.organizationName + '";', 4); + this.p('TargetAttributes = {', 4); + this.p(targetId + ' = {', 5); + this.p('CreatedOnToolsVersion = 6.1.1;', 6); + if (target_options.developmentTeam) { + this.p('DevelopmentTeam = ' + target_options.developmentTeam + ';', 6); + } + this.p('};', 5); + this.p('};', 4); + this.p('};', 3); + this.p('buildConfigurationList = ' + projectBuildConfigListId + ' /* Build configuration list for PBXProject "' + project.get_safe_name() + '" */;', 3); + this.p('compatibilityVersion = "Xcode 3.2";', 3); + this.p('developmentRegion = en;', 3); + this.p('hasScannedForEncodings = 0;', 3); + this.p('knownRegions = (', 3); + this.p('en,', 4); + this.p('Base,', 4); + this.p(');', 3); + this.p('mainGroup = ' + mainGroupId + ';', 3); + this.p('productRefGroup = ' + productsGroupId + ' /* Products */;', 3); + this.p('projectDirPath = "";', 3); + this.p('projectRoot = "";', 3); + this.p('targets = (', 3); + this.p(targetId + ' /* ' + project.get_safe_name() + ' */,', 4); + this.p(');', 3); + this.p('};', 2); + this.p('/* End PBXProject section */'); + this.p(); + this.p('/* Begin PBXResourcesBuildPhase section */'); + this.p(resourcesBuildId + ' /* Resources */ = {', 2); + this.p('isa = PBXResourcesBuildPhase;', 3); + this.p('buildActionMask = 2147483647;', 3); + this.p('files = (', 3); + this.p(debugDirBuildId + ' /* Deployment in Resources */,', 4); + this.p(iconBuildId + ' /* Images.xcassets in Resources */,', 4); + this.p(');', 3); + this.p('runOnlyForDeploymentPostprocessing = 0;', 3); + this.p('};', 2); + this.p('/* End PBXResourcesBuildPhase section */'); + this.p(); + this.p('/* Begin PBXSourcesBuildPhase section */'); + this.p(sourceBuildId + ' /* Sources */ = {', 2); + this.p('isa = PBXSourcesBuildPhase;', 3); + this.p('buildActionMask = 2147483647;', 3); + this.p('files = (', 3); + for (let file of files) { + if (file.isBuildFile()) + this.p(file.getBuildId() + ' /* ' + file.toString() + ' in Sources */,', 4); + } + this.p(');', 3); + this.p('runOnlyForDeploymentPostprocessing = 0;'); + this.p('};'); + this.p('/* End PBXSourcesBuildPhase section */'); + this.p(); + this.p('/* Begin XCBuildConfiguration section */'); + this.p(debugId + ' /* Debug */ = {', 2); + this.p('isa = XCBuildConfiguration;', 3); + this.p('buildSettings = {', 3); + this.p('ALWAYS_SEARCH_USER_PATHS = NO;', 4); + this.p('CLANG_CXX_LANGUAGE_STANDARD = "' + project.cppStd + '";', 4); + this.p('CLANG_CXX_LIBRARY = "compiler-default";', 4); + this.p('CLANG_ENABLE_MODULES = YES;', 4); + this.p('CLANG_ENABLE_OBJC_ARC = YES;', 4); + this.p('CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;', 4); + this.p('CLANG_WARN_BOOL_CONVERSION = YES;', 4); + this.p('CLANG_WARN_COMMA = YES;', 4); + this.p('CLANG_WARN_CONSTANT_CONVERSION = YES;', 4); + this.p('CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;', 4); + this.p('CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;', 4); + this.p('CLANG_WARN_EMPTY_BODY = YES;', 4); + this.p('CLANG_WARN_ENUM_CONVERSION = YES;', 4); + this.p('CLANG_WARN_INFINITE_RECURSION = YES;', 4); + this.p('CLANG_WARN_INT_CONVERSION = YES;', 4); + this.p('CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;', 4); + this.p('CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;', 4); + this.p('CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;', 4); + this.p('CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;', 4); + this.p('CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;', 4); + this.p('CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;', 4); + this.p('CLANG_WARN_STRICT_PROTOTYPES = YES;', 4); + this.p('CLANG_WARN_SUSPICIOUS_MOVE = YES;', 4); + this.p('CLANG_WARN_UNREACHABLE_CODE = YES;', 4); + this.p('CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;', 4); + if (platform === 'ios') { + this.p('"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";', 4); + } + else { + this.p('CODE_SIGN_IDENTITY = "-";', 4); + // this.p('"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";', 4); + } + this.p('COPY_PHASE_STRIP = NO;', 4); + this.p('ENABLE_STRICT_OBJC_MSGSEND = YES;', 4); + this.p('ENABLE_TESTABILITY = YES;', 4); + this.p('GCC_C_LANGUAGE_STANDARD = "' + project.cStd + '";', 4); + this.p('GCC_DYNAMIC_NO_PIC = NO;', 4); + this.p('GCC_NO_COMMON_BLOCKS = YES;', 4); + this.p('GCC_OPTIMIZATION_LEVEL = 0;', 4); + this.p('GCC_PREPROCESSOR_DEFINITIONS = (', 4); + this.p('"DEBUG=1",', 5); + for (let define of project.getDefines()) { + if (define.indexOf('=') >= 0) + this.p('"' + define.replace(/\"/g, '\\\\\\"') + '",', 5); + else + this.p(define + ',', 5); + } + this.p('"$(inherited)",', 5); + this.p(');', 4); + this.p('GCC_SYMBOLS_PRIVATE_EXTERN = NO;', 4); + this.p('GCC_WARN_64_TO_32_BIT_CONVERSION = YES;', 4); + this.p('GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;', 4); + this.p('GCC_WARN_UNDECLARED_SELECTOR = YES;', 4); + this.p('GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;', 4); + this.p('GCC_WARN_UNUSED_FUNCTION = YES;', 4); + this.p('GCC_WARN_UNUSED_VARIABLE = YES;', 4); + if (platform === 'ios') { + this.p('IPHONEOS_DEPLOYMENT_TARGET = 16.0;', 4); + } + else { + this.p('MACOSX_DEPLOYMENT_TARGET = 13.0;', 4); + } + this.p('MTL_ENABLE_DEBUG_INFO = YES;', 4); + this.p('ONLY_ACTIVE_ARCH = YES;', 4); + if (platform === 'ios') { + this.p('SDKROOT = iphoneos;', 4); + this.p('TARGETED_DEVICE_FAMILY = "1,2";', 4); + } + else { + this.p('SDKROOT = macosx;', 4); + } + this.p('};', 3); + this.p('name = Debug;', 3); + this.p('};', 2); + this.p(releaseId + ' /* Release */ = {', 2); + this.p('isa = XCBuildConfiguration;', 3); + this.p('buildSettings = {', 3); + this.p('ALWAYS_SEARCH_USER_PATHS = NO;', 4); + this.p('CLANG_CXX_LANGUAGE_STANDARD = "' + project.cppStd + '";', 4); + this.p('CLANG_CXX_LIBRARY = "compiler-default";', 4); + this.p('CLANG_ENABLE_MODULES = YES;', 4); + this.p('CLANG_ENABLE_OBJC_ARC = YES;', 4); + this.p('CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;', 4); + this.p('CLANG_WARN_BOOL_CONVERSION = YES;', 4); + this.p('CLANG_WARN_COMMA = YES;', 4); + this.p('CLANG_WARN_CONSTANT_CONVERSION = YES;', 4); + this.p('CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;', 4); + this.p('CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;', 4); + this.p('CLANG_WARN_EMPTY_BODY = YES;', 4); + this.p('CLANG_WARN_ENUM_CONVERSION = YES;', 4); + this.p('CLANG_WARN_INFINITE_RECURSION = YES;', 4); + this.p('CLANG_WARN_INT_CONVERSION = YES;', 4); + this.p('CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;', 4); + this.p('CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;', 4); + this.p('CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;', 4); + this.p('CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;', 4); + this.p('CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;', 4); + this.p('CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;', 4); + this.p('CLANG_WARN_STRICT_PROTOTYPES = YES;', 4); + this.p('CLANG_WARN_SUSPICIOUS_MOVE = YES;', 4); + this.p('CLANG_WARN_UNREACHABLE_CODE = YES;', 4); + this.p('CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;', 4); + if (platform === 'ios') { + this.p('"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";', 4); + } + else { + this.p('CODE_SIGN_IDENTITY = "-";', 4); + // this.p('"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";', 4); + } + this.p('COPY_PHASE_STRIP = YES;', 4); + if (platform === 'macos') { + this.p('DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";', 4); + } + this.p('ENABLE_NS_ASSERTIONS = NO;', 4); + this.p('ENABLE_STRICT_OBJC_MSGSEND = YES;', 4); + this.p('GCC_C_LANGUAGE_STANDARD = "' + project.cStd + '";', 4); + this.p('GCC_NO_COMMON_BLOCKS = YES;', 4); + this.p('GCC_PREPROCESSOR_DEFINITIONS = (', 4); + this.p('NDEBUG,', 5); + for (let define of project.getDefines()) { + if (define.indexOf('=') >= 0) + this.p('"' + define.replace(/\"/g, '\\\\\\"') + '",', 5); + else + this.p(define + ',', 5); + } + this.p('"$(inherited)",', 5); + this.p(');', 4); + this.p('GCC_WARN_64_TO_32_BIT_CONVERSION = YES;', 4); + this.p('GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;', 4); + this.p('GCC_WARN_UNDECLARED_SELECTOR = YES;', 4); + this.p('GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;', 4); + this.p('GCC_WARN_UNUSED_FUNCTION = YES;', 4); + this.p('GCC_WARN_UNUSED_VARIABLE = YES;', 4); + if (platform === 'ios') { + this.p('IPHONEOS_DEPLOYMENT_TARGET = 16.0;', 4); + } + else { + this.p('MACOSX_DEPLOYMENT_TARGET = 13.0;', 4); + } + this.p('MTL_ENABLE_DEBUG_INFO = NO;', 4); + this.p('ONLY_ACTIVE_ARCH = YES;', 4); + if (platform === 'ios') { + this.p('SDKROOT = iphoneos;', 4); + this.p('TARGETED_DEVICE_FAMILY = "1,2";', 4); + this.p('VALIDATE_PRODUCT = YES;', 4); + } + else { + this.p('SDKROOT = macosx;', 4); + } + this.p('};', 3); + this.p('name = Release;', 3); + this.p('};', 2); + this.p(nativeDebugId + ' /* Debug */ = {', 2); + this.p('isa = XCBuildConfiguration;', 3); + this.p('buildSettings = {', 3); + this.p('ARCHS = arm64;', 4); + this.p('ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;', 4); + this.p('CODE_SIGN_STYLE = Automatic;', 4); + if (platform === 'macos') { + this.p('COMBINE_HIDPI_IMAGES = YES;', 4); + } + this.p('ENABLE_HARDENED_RUNTIME = YES;', 4); + this.p('FRAMEWORK_SEARCH_PATHS = (', 4); + this.p('"$(inherited)",', 5); + // Search paths to local frameworks + for (let framework of frameworks) { + if (framework.localPath != null) + this.p(framework.localPath.substr(0, framework.localPath.lastIndexOf('/')) + ',', 5); + } + this.p(');', 4); + this.p('HEADER_SEARCH_PATHS = (', 4); + this.p('"$(inherited)",', 5); + this.p('"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",', 5); + for (let projectpath of project.getIncludeDirs()) + this.p('"' + path_resolve(from, projectpath).replace(/ /g, '\\\\ ') + '",', 5); + this.p(');', 4); + this.p('LIBRARY_SEARCH_PATHS = (', 4); + for (let framework of frameworks) { + if ((framework.toString().endsWith('.dylib') || framework.toString().endsWith('.a')) && framework.localPath != null) { + this.p(framework.localPath.substr(0, framework.localPath.lastIndexOf('/')) + ',', 5); + } + } + this.p(');', 4); + this.p('INFOPLIST_EXPAND_BUILD_SETTINGS = "YES";', 4); + this.p('INFOPLIST_FILE = "' + path_resolve(from, plistname) + '";', 4); + this.p('LD_RUNPATH_SEARCH_PATHS = (', 4); + this.p('"$(inherited)",', 5); + if (platform === 'ios') { + this.p('"@executable_path/Frameworks",', 5); + } + for (let framework of frameworks) { + if (framework.toString().endsWith('.dylib') && framework.localPath != null) { + this.p(framework.localPath.substr(0, framework.localPath.lastIndexOf('/')) + ',', 5); + } + } + this.p(');', 4); + if (project.cFlags.length > 0) { + this.p('OTHER_CFLAGS = (', 4); + for (let cFlag of project.cFlags) { + this.p('"' + cFlag + '",', 5); + } + this.p(');', 4); + } + if (project.cppFlags.length > 0) { + this.p('OTHER_CPLUSPLUSFLAGS = (', 4); + for (let cppFlag of project.cppFlags) { + this.p('"' + cppFlag + '",', 5); + } + this.p(');', 4); + } + this.p('PRODUCT_BUNDLE_IDENTIFIER = "' + target_options.bundle + '";', 4); + this.p('BUNDLE_VERSION = "' + target_options.version + '";', 4); + this.p('BUILD_VERSION = "' + target_options.build + '";', 4); + this.p('CODE_SIGN_IDENTITY = "-";', 4); + this.p('PRODUCT_NAME = "$(TARGET_NAME)";', 4); + this.p('};', 3); + this.p('name = Debug;', 3); + this.p('};', 2); + this.p(nativeReleaseId + ' /* Release */ = {', 2); + this.p('isa = XCBuildConfiguration;', 3); + this.p('buildSettings = {', 3); + this.p('ARCHS = arm64;', 4); + this.p('ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;', 4); + this.p('CODE_SIGN_STYLE = Automatic;', 4); + if (platform === 'macos') { + this.p('COMBINE_HIDPI_IMAGES = YES;', 4); + } + this.p('ENABLE_HARDENED_RUNTIME = YES;', 4); + this.p('FRAMEWORK_SEARCH_PATHS = (', 4); + this.p('"$(inherited)",', 5); + // Search paths to local frameworks + for (let framework of frameworks) { + if (framework.localPath != null) + this.p(framework.localPath.substr(0, framework.localPath.lastIndexOf('/')) + ',', 5); + } + this.p(');', 4); + this.p('HEADER_SEARCH_PATHS = (', 4); + this.p('"$(inherited)",', 5); + this.p('"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include",', 5); + for (let p of project.getIncludeDirs()) + this.p('"' + path_resolve(from, p).replace(/ /g, '\\\\ ') + '",', 5); + this.p(');', 4); + this.p('LIBRARY_SEARCH_PATHS = (', 4); + for (let framework of frameworks) { + if ((framework.toString().endsWith('.dylib') || framework.toString().endsWith('.a')) && framework.localPath != null) { + this.p(framework.localPath.substr(0, framework.localPath.lastIndexOf('/')) + ',', 5); + } + } + this.p(');', 4); + this.p('INFOPLIST_EXPAND_BUILD_SETTINGS = "YES";', 4); + this.p('INFOPLIST_FILE = "' + path_resolve(from, plistname) + '";', 4); + this.p('LD_RUNPATH_SEARCH_PATHS = (', 4); + this.p('"$(inherited)",', 5); + if (platform === 'ios') { + this.p('"@executable_path/Frameworks",', 5); + } + for (let framework of frameworks) { + if (framework.toString().endsWith('.dylib') && framework.localPath != null) { + this.p(framework.localPath.substr(0, framework.localPath.lastIndexOf('/')) + ',', 5); + } + } + this.p(');', 4); + if (project.cFlags.length > 0) { + this.p('OTHER_CFLAGS = (', 4); + for (let cFlag of project.cFlags) { + this.p('"' + cFlag + '",', 5); + } + this.p(');', 4); + } + if (project.cppFlags.length > 0) { + this.p('OTHER_CPLUSPLUSFLAGS = (', 4); + for (let cppFlag of project.cppFlags) { + this.p('"' + cppFlag + '",', 5); + } + this.p(');', 4); + } + this.p('PRODUCT_BUNDLE_IDENTIFIER = "' + target_options.bundle + '";', 4); + this.p('BUNDLE_VERSION = "' + target_options.version + '";', 4); + this.p('BUILD_VERSION = "' + target_options.build + '";', 4); + this.p('CODE_SIGN_IDENTITY = "-";', 4); + this.p('PRODUCT_NAME = "$(TARGET_NAME)";', 4); + this.p('};', 3); + this.p('name = Release;', 3); + this.p('};', 2); + this.p('/* End XCBuildConfiguration section */'); + this.p(); + this.p('/* Begin XCConfigurationList section */'); + this.p(projectBuildConfigListId + ' /* Build configuration list for PBXProject "' + project.get_safe_name() + '" */ = {', 2); + this.p('isa = XCConfigurationList;', 3); + this.p('buildConfigurations = (', 3); + this.p(debugId + ' /* Debug */,', 4); + this.p(releaseId + ' /* Release */,', 4); + this.p(');', 3); + this.p('defaultConfigurationIsVisible = 0;', 3); + this.p('defaultConfigurationName = Release;', 3); + this.p('};', 2); + this.p(nativeBuildConfigListId + ' /* Build configuration list for PBXNativeTarget "' + project.get_safe_name() + '" */ = {', 2); + this.p('isa = XCConfigurationList;', 3); + this.p('buildConfigurations = (', 3); + this.p(nativeDebugId + ' /* Debug */,', 4); + this.p(nativeReleaseId + ' /* Release */,', 4); + this.p(');', 3); + this.p('defaultConfigurationIsVisible = 0;', 3); + this.p('defaultConfigurationName = Release;', 3); + this.p('};', 2); + this.p('/* End XCConfigurationList section */'); + this.p('};', 1); + this.p('rootObject = ' + projectId + ' /* Project object */;', 1); + this.p('}'); + this.close_file(); + } +} + +class MakeExporter extends Exporter { + constructor(cCompiler, cppCompiler, cFlags, cppFlags, linkerFlags, outputExtension, libsLine = null) { + super(); + this.cCompiler = cCompiler; + this.cppCompiler = cppCompiler; + this.cFlags = cFlags; + this.cppFlags = cppFlags; + this.linkerFlags = linkerFlags; + this.outputExtension = outputExtension; + if (libsLine != null) { + this.libsLine = libsLine; + } + } + + libsLine(project) { + let libs = ""; + for (let lib of project.getLibs()) { + libs += " -l" + lib; + } + return libs; + } + + export_solution(project) { + let from = path_resolve("."); + let to = path_resolve("build"); + let objects = {}; + let ofiles = {}; + let output_path = path_resolve(to, goptions.build_path); + fs_ensuredir(output_path); + for (let fileobject of project.getFiles()) { + let file = fileobject.file; + if (file.endsWith(".cpp") || file.endsWith(".c") || file.endsWith(".cc")) { + let name = file.toLowerCase(); + if (name.indexOf("/") >= 0) { + name = name.substr(name.lastIndexOf("/") + 1); + } + name = name.substr(0, name.lastIndexOf(".")); + if (!objects[name]) { + objects[name] = true; + ofiles[file] = name; + } + else { + while (objects[name]) { + name = name + "_"; + } + objects[name] = true; + ofiles[file] = name; + } + } + } + let ofilelist = ""; + for (let o in objects) { + ofilelist += o + ".o "; + } + this.write_file(path_resolve(output_path, "makefile")); + let incline = "-I./ "; // local directory to pick up the precompiled headers + for (let inc of project.getIncludeDirs()) { + inc = path_relative(output_path, path_resolve(from, inc)); + incline += "-I" + inc + " "; + } + this.p("INC=" + incline); + this.p("LIB=" + this.linkerFlags + this.libsLine(project)); + let defline = ""; + for (let def of project.getDefines()) { + defline += "-D" + def.replace(/\"/g, '\\"') + " "; + } + if (!goptions.debug) { + defline += "-DNDEBUG "; + } + this.p("DEF=" + defline); + this.p(); + let cline = this.cFlags; + cline = "-std=" + project.cStd + " "; + for (let flag of project.cFlags) { + cline += flag + ' '; + } + this.p("CFLAGS=" + cline); + let cppline = this.cppFlags; + cppline = "-std=" + project.cppStd + " "; + for (let flag of project.cppFlags) { + cppline += flag + " "; + } + this.p("CPPFLAGS=" + cppline); + let optimization = ""; + if (!goptions.debug) { + optimization = "-O2"; + } + else + optimization = "-g"; + let executable_name = project.get_safe_name(); + if (project.get_executable_name()) { + executable_name = project.get_executable_name(); + } + this.p(executable_name + this.outputExtension + ": " + ofilelist); + let output = '-o "' + executable_name + this.outputExtension + '"'; + this.p('\t' + this.cppCompiler + ' ' + output + ' ' + optimization + ' ' + ofilelist + ' $(LIB)'); + for (let fileobject of project.getFiles()) { + let file = fileobject.file; + if (file.endsWith(".c") || file.endsWith(".cpp") || file.endsWith(".cc")) { + this.p(); + let name = ofiles[file]; + let realfile = path_relative(output_path, path_resolve(from, file)); + this.p("-include " + name + ".d"); + this.p(name + ".o: " + realfile); + let compiler = this.cppCompiler; + let flags = '$(CPPFLAGS)'; + if (file.endsWith(".c")) { + compiler = this.cCompiler; + flags = '$(CFLAGS)'; + } + this.p('\t' + compiler + ' ' + optimization + ' $(INC) $(DEF) -MD ' + flags + ' -c ' + realfile + ' -o ' + name + '.o'); + } + } + this.close_file(); + } +} + +class LinuxExporter extends Exporter { + constructor() { + super(); + let compilerFlags = ""; + let linkerFlags = "-static-libgcc -static-libstdc++ -pthread"; + if (this.getCCompiler() == "gcc") { + compilerFlags += "-flto"; + linkerFlags += " -flto"; + } + this.make = new MakeExporter(this.getCCompiler(), this.getCPPCompiler(), compilerFlags, compilerFlags, linkerFlags, ''); + this.compile_commands = new CompilerCommandsExporter(); + } + + export_solution(project) { + this.make.export_solution(project); + this.compile_commands.export_solution(project); + } + + getCCompiler() { + return goptions.ccompiler; + } + + getCPPCompiler() { + return goptions.cppcompiler; + } +} + +class AndroidExporter extends Exporter { + constructor() { + super(); + this.compile_commands = new CompilerCommandsExporter(); + } + + export_solution(project) { + let from = path_resolve("."); + let to = path_resolve("build"); + this.safe_name = project.get_safe_name(); + let outdir = path_join(to.toString(), this.safe_name); + fs_ensuredir(outdir); + let target_options = { + package: "org.armory3d", + installLocation: "internalOnly", + versionCode: 1, + versionName: "1.0", + compileSdkVersion: 33, + minSdkVersion: 24, + targetSdkVersion: 33, + screenOrientation: "sensor", + permissions: ["android.permission.VIBRATE"], + disableStickyImmersiveMode: false, + metadata: [], + abiFilters: [] + }; + if (project.target_options != null && project.target_options.android != null) { + let userOptions = project.target_options.android; + for (let key in userOptions) { + if (userOptions[key] == null) + continue; + switch (key) { + default: + target_options[key] = userOptions[key]; + } + } + } + fs_writefile(path_join(outdir, 'build.gradle.kts'), get_text_data('android/build.gradle.kts')); + fs_writefile(path_join(outdir, 'gradle.properties'), get_text_data('android/gradle.properties')); + fs_writefile(path_join(outdir, 'gradlew'), get_text_data('android/gradlew')); + if (os_platform() !== 'win32') { + os_chmod(path_join(outdir, 'gradlew'), "+x"); + } + fs_writefile(path_join(outdir, 'gradlew.bat'), get_text_data('android/gradlew.bat')); + let settings = get_text_data('android/settings.gradle.kts'); + settings = settings.replace(/{name}/g, project.getName()); + fs_writefile(path_join(outdir, 'settings.gradle.kts'), settings); + fs_ensuredir(path_join(outdir, 'app')); + fs_writefile(path_join(outdir, 'app', 'proguard-rules.pro'), get_text_data('android/app/proguard-rules.pro')); + this.write_app_gradle(project, outdir, from, target_options); + this.write_cmake_lists(project, outdir, from); + fs_ensuredir(path_join(outdir, 'app', 'src')); + fs_ensuredir(path_join(outdir, 'app', 'src', 'main')); + this.write_manifest(outdir, target_options); + let strings = get_text_data('android/main/res/values/strings.xml'); + strings = strings.replace(/{name}/g, project.getName()); + fs_ensuredir(path_join(outdir, 'app', 'src', 'main', 'res', 'values')); + fs_writefile(path_join(outdir, 'app', 'src', 'main', 'res', 'values', 'strings.xml'), strings); + this.export_icons(project.icon, outdir, from, to); + fs_ensuredir(path_join(outdir, 'gradle', 'wrapper')); + fs_writefile(path_join(outdir, 'gradle', 'wrapper', 'gradle-wrapper.jar'), get_binary_data('android/gradle/wrapper/gradle-wrapper.jar')); + fs_writefile(path_join(outdir, 'gradle', 'wrapper', 'gradle-wrapper.properties'), get_text_data('android/gradle/wrapper/gradle-wrapper.properties')); + fs_copydir(path_resolve(from, project.get_debug_dir()), path_resolve(to, this.safe_name, 'app', 'src', 'main', 'assets')); + this.compile_commands.export_solution(project); + } + + write_app_gradle(project, outdir, from, target_options) { + let cflags = ''; + for (let flag of project.cFlags) + cflags += flag + ' '; + let cppflags = ''; + for (let flag of project.cppFlags) + cppflags += flag + ' '; + let gradle = get_text_data('android/app/build.gradle.kts'); + gradle = gradle.replace(/{package}/g, target_options.package); + gradle = gradle.replace(/{versionCode}/g, target_options.versionCode.toString()); + gradle = gradle.replace(/{versionName}/g, target_options.versionName); + gradle = gradle.replace(/{compileSdkVersion}/g, target_options.compileSdkVersion.toString()); + gradle = gradle.replace(/{minSdkVersion}/g, target_options.minSdkVersion.toString()); + gradle = gradle.replace(/{targetSdkVersion}/g, target_options.targetSdkVersion.toString()); + let arch = ''; + if (target_options.abiFilters.length > 0) { + for (let item of target_options.abiFilters) { + if (arch.length === 0) { + arch = '"' + item + '"'; + } + else { + arch = arch + ', "' + item + '"'; + } + } + arch = `ndk { abiFilters += listOf(${arch}) }`; + } + else { + switch (goptions.arch) { + case 'default': + arch = ''; + break; + case 'arm8': + arch = 'arm64-v8a'; + break; + } + if (goptions.arch !== 'default') { + arch = `ndk {abiFilters += listOf("${arch}")}`; + } + } + gradle = gradle.replace(/{architecture}/g, arch); + gradle = gradle.replace(/{cflags}/g, cflags); + cppflags = '-frtti -fexceptions ' + cppflags; + cppflags = '-std=' + project.cppStd + ' ' + cppflags; + gradle = gradle.replace(/{cppflags}/g, cppflags); + let javasources = ''; + for (let dir of project.getJavaDirs()) { + javasources += '"' + path_relative(path_join(outdir, 'app'), path_resolve(from, dir)).replace(/\\/g, '/') + '", '; + } + javasources += '"' + path_join(armorcoredir, 'sources', 'backends', 'android', 'java').replace(/\\/g, '/') + '"'; + gradle = gradle.replace(/{javasources}/g, javasources); + fs_writefile(path_join(outdir, 'app', 'build.gradle.kts'), gradle); + } + + write_cmake_lists(project, outdir, from) { + let cmake = get_text_data('android/app/CMakeLists.txt'); + let debugDefines = ''; + for (let def of project.getDefines()) { + debugDefines += ' -D' + def.replace(/\"/g, '\\\\\\\"'); + } + cmake = cmake.replace(/{debug_defines}/g, debugDefines); + let releaseDefines = ''; + for (let def of project.getDefines()) { + releaseDefines += ' -D' + def.replace(/\"/g, '\\\\\\\"'); + } + cmake = cmake.replace(/{release_defines}/g, releaseDefines); + let includes = ''; + for (let inc of project.getIncludeDirs()) { + includes += ' "' + path_resolve(inc).replace(/\\/g, '/') + '"\n'; + } + cmake = cmake.replace(/{includes}/g, includes); + let files = ''; + for (let file of project.getFiles()) { + if (file.file.endsWith('.c') || file.file.endsWith('.cc') + || file.file.endsWith('.cpp') || file.file.endsWith('.h')) { + if (path_isabs(file.file)) { + files += ' "' + path_resolve(file.file).replace(/\\/g, '/') + '"\n'; + } + else { + files += ' "' + path_resolve(path_join(from, file.file)).replace(/\\/g, '/') + '"\n'; + } + } + } + cmake = cmake.replace(/{files}/g, files); + let libraries1 = ''; + let libraries2 = ''; + for (let lib of project.getLibs()) { + libraries1 += 'find_library(' + lib + '-lib ' + lib + ')\n'; + libraries2 += ' ${' + lib + '-lib}\n'; + } + cmake = cmake.replace(/{libraries1}/g, libraries1) + .replace(/{libraries2}/g, libraries2); + let cmakePath = path_join(outdir, 'app', 'CMakeLists.txt'); + fs_writefile(cmakePath, cmake); + } + + write_manifest(outdir, target_options) { + let manifest = get_text_data('android/main/AndroidManifest.xml'); + manifest = manifest.replace(/{package}/g, target_options.package); + manifest = manifest.replace(/{installLocation}/g, target_options.installLocation); + manifest = manifest.replace(/{versionCode}/g, target_options.versionCode.toString()); + manifest = manifest.replace(/{versionName}/g, target_options.versionName); + manifest = manifest.replace(/{screenOrientation}/g, target_options.screenOrientation); + manifest = manifest.replace(/{targetSdkVersion}/g, target_options.targetSdkVersion); + manifest = manifest.replace(/{permissions}/g, target_options.permissions.map((p) => { return '\n\t'; }).join('')); + let metadata = target_options.disableStickyImmersiveMode ? '\n\t\t' : ''; + for (let meta of target_options.metadata) { + metadata += '\n\t\t' + meta; + } + manifest = manifest.replace(/{metadata}/g, metadata); + fs_ensuredir(path_join(outdir, 'app', 'src', 'main')); + fs_writefile(path_join(outdir, 'app', 'src', 'main', 'AndroidManifest.xml'), manifest); + } + + export_icons(icon, outdir, from, to) { + let folders = ['mipmap-mdpi', 'mipmap-hdpi', 'mipmap-xhdpi', 'mipmap-xxhdpi', 'mipmap-xxxhdpi']; + let dpis = [48, 72, 96, 144, 192]; + for (let i = 0; i < dpis.length; ++i) { + let folder = folders[i]; + let dpi = dpis[i]; + fs_ensuredir(path_join(outdir, 'app', 'src', 'main', 'res', folder)); + export_png_icon(icon, path_resolve(to, this.safe_name, 'app', 'src', 'main', 'res', folder, 'ic_launcher.png'), dpi, dpi, from); + export_png_icon(icon, path_resolve(to, this.safe_name, 'app', 'src', 'main', 'res', folder, 'ic_launcher_round.png'), dpi, dpi, from); + } + } +} + +class CompilerCommandsExporter extends Exporter { + constructor() { + super(); + } + + export_solution(project) { + let from = path_resolve("."); + let to = path_resolve("build"); + let platform = goptions.target; + from = path_resolve(os_cwd(), from); + this.write_file(path_resolve(to, 'compile_commands.json')); + let includes = []; + for (let inc of project.getIncludeDirs()) { + includes.push('-I'); + includes.push(path_resolve(from, inc)); + } + let defines = []; + for (let def of project.getDefines()) { + defines.push('-D'); + defines.push(def.replace(/\"/g, '\\"')); + } + let objects = {}; + let ofiles = {}; + for (let fileobject of project.getFiles()) { + let file = fileobject.file; + if (file.endsWith('.cpp') || file.endsWith('.c') || file.endsWith('.cc')) { + let name = file.toLowerCase(); + if (name.indexOf('/') >= 0) + name = name.substr(name.lastIndexOf('/') + 1); + name = name.substr(0, name.lastIndexOf('.')); + if (!objects[name]) { + objects[name] = true; + ofiles[file] = name; + } + else { + while (objects[name]) { + name = name + '_'; + } + objects[name] = true; + ofiles[file] = name; + } + } + } + + let default_args = []; + if (platform === 'android') { + default_args.push('--target=aarch64-none-linux-android21'); + default_args.push('-DANDROID'); + function ndkFromSdkRoot() { + let _a = os_env('ANDROID_HOME'); + let sdkEnv = _a !== null ? _a : os_env('ANDROID_SDK_ROOT'); + if (!sdkEnv) + return null; + let ndk_dir = path_join(sdkEnv, 'ndk'); + if (!fs_exists(ndk_dir)) { + return null; + } + let ndks = fs_readdir(ndk_dir); + ndks = ndks.filter(item => !item.startsWith(".")); + if (ndks.length < 1) { + return null; + } + return path_join(ndk_dir, ndks[0]); + } + let _a = os_env('ANDROID_NDK'); + let android_ndk = _a !== null ? _a : ndkFromSdkRoot(); + if (android_ndk) { + let host_tag = ''; + switch (os_platform()) { + case 'linux': + host_tag = 'linux-x86_64'; + break; + case 'darwin': + host_tag = 'darwin-x86_64'; + break; + case 'win32': + host_tag = 'windows-x86_64'; + break; + } + let ndk_toolchain = path_join(android_ndk, `toolchains/llvm/prebuilt/${host_tag}`); + if (host_tag !== '' && fs_exists(ndk_toolchain)) { + default_args.push(`--gcc-toolchain=${ndk_toolchain}`); + default_args.push(`--sysroot=${ndk_toolchain}/sysroot`); + } + else { + // fallback to the first found toolchain + let toolchains = fs_readdir(path_join(android_ndk, `toolchains/llvm/prebuilt/`)); + if (toolchains.length > 0) { + let host_tag = toolchains[0]; + let ndk_toolchain = path_join(android_ndk, `toolchains/llvm/prebuilt/${host_tag}`); + default_args.push(`--gcc-toolchain=${ndk_toolchain}`); + default_args.push(`--sysroot=${ndk_toolchain}/sysroot`); + console.log(`Found android ndk toolchain in ${ndk_toolchain}.`); + } + } + } + } + + let commands = []; + for (let fileobject of project.getFiles()) { + let file = fileobject.file; + if (file.endsWith('.c') || file.endsWith('.cpp') || file.endsWith('.cc')) { + let args = ['/usr/bin/clang', '-c', '-o', (goptions.debug ? 'Debug' : 'Release') + ofiles[file] + '.o']; + if (file.endsWith('.c')) { + args.push('-std=c99'); + } + args.push(...default_args); + args.push(path_resolve(from, file)); + let command = { + directory: from, + file: path_resolve(from, file), + output: path_resolve(to, ofiles[file] + '.o'), + arguments: args.concat(includes).concat(defines) + }; + commands.push(command); + } + } + this.p(JSON.stringify(commands)); + this.close_file(); + } +} + +// ███╗ ███╗ █████╗ ██╗ ██╗███████╗ +// ████╗ ████║██╔══██╗██║ ██╔╝██╔════╝ +// ██╔████╔██║███████║█████╔╝ █████╗ +// ██║╚██╔╝██║██╔══██║██╔═██╗ ██╔══╝ +// ██║ ╚═╝ ██║██║ ██║██║ ██╗███████╗ +// ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ + +function export_ico(icon, to, from) { + if (fs_exists(to) && fs_mtime(to) > fs_mtime(from)) { + return; + } + if (!fs_exists(path_join(from, icon))) { + from = makedir; + icon = "icon.png"; + } + amake.export_ico(path_join(from, icon), to); +} + +function export_png_icon(icon, to, width, height, from) { + if (fs_exists(to) && fs_mtime(to) > fs_mtime(from)) { + return; + } + if (!fs_exists(path_join(from, icon))) { + from = makedir; + icon = "icon.png"; + } + amake.export_png(path_join(from, icon), to, width, height); +} + +function contains_define(array, value) { + return array.indexOf(value) > -1; +} + +function contains_fancy_define(array, value) { + let name = value.substring(0, value.indexOf("=")); + for (let element of array) { + let index = element.indexOf("="); + if (index >= 0) { + let otherName = element.substring(0, index); + if (name === otherName) { + return true; + } + } + } + return false; +} + +function load_project(directory, is_root_project) { + __dirname = path_resolve(directory); + + if (is_root_project) { + globalThis.platform = goptions.target; + globalThis.graphics = goptions.graphics; + globalThis.flags = { + name: "Armory", + package: "org.armory3d", + dirname: __dirname, + release: os_argv().indexOf("--debug") == -1, + with_d3dcompiler: false, + with_nfd: false, + with_compress: false, + with_image_write: false, + with_mpeg_write: false, + with_audio: false, + with_iron: false, + with_eval: false, + embed: false + }; + } + + let project = eval("function _(){" + fs_readfile(path_resolve(directory, "project.js")) + "} _();"); + + if (is_root_project) { + try { + export_armorcore_project(project, goptions); + } + catch (error) { + console.log(error); + os_exit(1); + } + } + + return project; +} + +function search_files2(current_dir, pattern) { + let result = []; + if (!fs_exists(current_dir)) { + return result; + } + current_dir = path_join(current_dir); //// + let files = fs_readdir(current_dir); + for (let f in files) { + let file = path_join(current_dir, files[f]); + if (fs_isdir(file)) + continue; + file = path_relative(current_dir, file); + if (matches(stringify(file), stringify(pattern))) { + result.push(path_join(current_dir, stringify(file))); + } + } + if (pattern.endsWith("**")) { + let dirs = fs_readdir(current_dir); + for (let d of dirs) { + let dir = path_join(current_dir, d); + if (d.startsWith('.')) + continue; + if (!fs_isdir(dir)) + continue; + result = result.concat(search_files2(dir, pattern)); + } + } + return result; +} + +class AssetConverter { + constructor(exporter, options, asset_matchers) { + this.exporter = exporter; + this.options = options; + this.asset_matchers = asset_matchers; + } + + static replace_pattern(pattern, value, filepath, from) { + let base_path = from; + let dir_value = path_relative(base_path, path_dirname(filepath)); + if (base_path.length > 0 && base_path[base_path.length - 1] === path_sep + && dir_value.length > 0 && dir_value[dir_value.length - 1] !== path_sep) { + dir_value += path_sep; + } + let dir_regex = dir_value === '' + ? /{dir}\//g + : /{dir}/g; + return pattern.replace(/{name}/g, value).replace(dir_regex, dir_value); + } + + static create_export_info(filepath, keep_ext, options, from) { + let name_value = path_basename_noext(filepath); + let destination = path_basename_noext(filepath); + if (keep_ext || options.noprocessing) { + destination += path_extname(filepath); + } + if (options.destination) { + destination = AssetConverter.replace_pattern(options.destination, destination, filepath, from); + } + if (keep_ext) { + name_value += path_extname(filepath); + } + if (options.name) { + name_value = AssetConverter.replace_pattern(options.name, name_value, filepath, from); + } + return { name: name_value, destination: path_normalize(destination) }; + } + + watch(match, options) { + match = path_normalize(match); + let basedir = match.substring(0, match.lastIndexOf(path_sep)); + let pattern = match; + if (path_isabs(pattern)) { + pattern = path_relative(basedir, pattern); + } + let files = search_files2(basedir, pattern); + let self = this; + let parsed_files = []; + + let index = 0; + for (let file of files) { + console.log('Exporting asset ' + (index + 1) + ' of ' + files.length + ' (' + path_basename(file) + ').'); + let ext = path_extname(file).toLowerCase(); + switch (ext) { + case '.png': + case '.jpg': + case '.hdr': { + let export_info = AssetConverter.create_export_info(file, false, options, "."); + let images; + if (options.noprocessing) { + images = self.exporter.copy_blob(file, export_info.destination, globalThis.flags.embed && !options.noembed); + } + else { + images = self.exporter.copy_image(file, export_info.destination, globalThis.flags.embed && !options.noembed); + } + parsed_files.push({ name: export_info.name, from: file, type: 'image', files: images, original_width: options.original_width, original_height: options.original_height, readable: options.readable, noembed: options.noembed }); + break; + } + default: { + let export_info = AssetConverter.create_export_info(file, true, options, "."); + let blobs = self.exporter.copy_blob(file, export_info.destination, globalThis.flags.embed && !options.noembed); + parsed_files.push({ name: export_info.name, from: file, type: 'blob', files: blobs, original_width: undefined, original_height: undefined, readable: undefined, noembed: options.noembed }); + break; + } + } + + index += 1; + } + return parsed_files; + } + + run() { + let files = []; + for (let matcher of this.asset_matchers) { + files = files.concat(this.watch(matcher.match, matcher.options)); + } + return files; + } +} + +class CompiledShader { + constructor() { + this.files = []; + } +} + +function shader_find_type(options) { + if (options.graphics === 'default') { + if (os_platform() === 'win32') { + return 'hlsl'; + } + else if (os_platform() === 'darwin') { + return 'msl'; + } + else { + return 'glsl'; + } + } + else if (options.graphics === 'vulkan') { + return 'spirv'; + } + else if (options.graphics === 'metal') { + return 'msl'; + } + else if (options.graphics === 'opengl') { + return 'glsl'; + } + else if (options.graphics === 'direct3d11' || options.graphics === 'direct3d12') { + return 'hlsl'; + } +} + +class ShaderCompiler { + constructor(exporter, compiler, to, temp, options, shader_matchers) { + this.exporter = exporter; + this.compiler = compiler; + this.type = shader_find_type(options); + this.options = options; + this.to = to; + this.temp = temp; + this.shader_matchers = shader_matchers; + } + + watch(match, options) { + match = path_normalize(match); + let basedir = match.substring(0, match.lastIndexOf(path_sep)); + let pattern = match; + if (path_isabs(pattern)) { + pattern = path_relative(basedir, pattern); + } + + let shaders = search_files2(basedir, pattern); + let self = this; + let compiled_shaders = []; + + let index = 0; + for (let shader of shaders) { + console.log('Compiling shader ' + (index + 1) + ' of ' + shaders.length + ' (' + path_basename(shader) + ').'); + let compiled_shader = null; + try { + compiled_shader = self.compile_shader(shader, options); + } + catch (error) { + console.log('Compiling shader ' + (index + 1) + ' of ' + shaders.length + ' (' + path_basename(shader) + ') failed:'); + console.log(error); + } + if (compiled_shader === null) { + compiled_shader = new CompiledShader(); + } + let type = self.type; + if (type == "hlsl") { + type = "d3d11"; + } + compiled_shader.files = [path_resolve('build', 'temp', path_basename_noext(shader) + '.' + type)]; + + compiled_shader.name = AssetConverter.create_export_info(shader, false, options, ".").name; + compiled_shaders.push(compiled_shader); + ++index; + } + + return compiled_shaders; + } + + run() { + let shaders = []; + for (let matcher of this.shader_matchers) { + shaders = shaders.concat(this.watch(matcher.match, matcher.options)); + } + return shaders; + } + + compile_shader(file, options) { + let from = file; + let to = path_join(this.to, path_basename_noext(file) + '.' + this.type); + + let from_time = 0; + let to_time; + if (fs_exists(from)) from_time = fs_mtime(from); + if (fs_exists(to)) to_time = fs_mtime(to); + + if (options.noprocessing) { + if (!to_time || to_time < from_time) { + fs_copyfile(from, to); + } + let compiled_shader = new CompiledShader(); + return compiled_shader; + } + + if (!from_time || (to_time && to_time > from_time)) { + return null; + } + else { + let parameters = [this.type, from, to, this.temp, "iron"]; + fs_ensuredir(this.temp); + + if (goptions.target === "android" || goptions.target === "wasm") { + parameters[0] = "essl"; + } + + parameters[1] = path_resolve(parameters[1]); + parameters[2] = path_resolve(parameters[2]); + parameters[3] = path_resolve(parameters[3]); + + amake.ashader(this.type, from, to); + + // let child = os_exec(this.compiler, parameters); + // if (child.status !== 0) { + // console.log('Shader compiler error.') + // } + + let compiled_shader = new CompiledShader(); + return compiled_shader; + } + } +} + +function export_k(from, to) { + to += ".k"; + if (fs_exists(to) && fs_mtime(to) > fs_mtime(from)) { + return "k"; + } + fs_ensuredir(path_dirname(to)); + amake.export_k(from, to); +} + +class ArmorCoreExporter { + constructor(project, options) { + this.options = options; + this.sources = []; + if (project.defines.indexOf("NO_IRON_API") == -1) { + this.add_source_directory(path_join(armorcoredir, "sources", "ts")); + } + } + + ts_options(defines) { + let graphics = this.options.graphics; + if (graphics === "default") { + if (os_platform() === "win32") { + graphics = "direct3d11"; + } + else if (os_platform() === "darwin") { + graphics = "metal"; + } + else { + graphics = "opengl"; + } + } + defines.push("arm_" + graphics); + defines.push("arm_" + goptions.target); + return { + from: ".", + sources: this.sources, + defines: defines + }; + } + + export() { + fs_ensuredir(path_join("build", "out")); + } + + copy_image(from, to, embed) { + let to_full = path_join("build", "out", to); + if (embed) { + to_full = path_join("build", "temp", to); + } + export_k(from, to_full); + return [to + ".k"]; + } + + copy_blob(from, to, embed) { + fs_ensuredir(path_join("build", "out", path_dirname(to))); + let to_full = path_join("build", "out", to); + if (embed && + !to.endsWith(".txt") && + !to.endsWith(".md") && + !to.endsWith(".json") && + !to.endsWith(".js") + ) { + fs_ensuredir(path_join("build", "temp", path_dirname(to))); + to_full = path_join("build", "temp", to); + } + fs_copyfile(from, to_full); + return [to]; + } + + add_source_directory(path) { + this.sources.push(path); + } +} + +function ts_contains_define(define) { + let b = false; + for (let s of globalThis.options.defines) { + if (define.includes(s)) { + b = true; + break; + }; + } + if (define.includes("!")) { + b = !b; + } + return b; +} + +function ts_preprocessor(file, file_path) { + let stack = []; + let found = []; + let lines = file.split("\n"); + for (let i = 0; i < lines.length; ++i) { + let line = lines[i].trimStart(); + if (line.startsWith("///if")) { + let define = line.substr(6); + stack.push(ts_contains_define(define)); + found.push(stack[stack.length - 1]); + } + else if (line.startsWith("///elseif")) { + let define = line.substr(10); + if (!found[found.length - 1] && ts_contains_define(define)) { + stack[stack.length - 1] = true; + found[found.length - 1] = true; + } + else { + stack[stack.length - 1] = false; + } + } + else if (line.startsWith("///else")) { + stack[stack.length - 1] = !found[found.length - 1]; + } + else if (line.startsWith("///end")) { + stack.pop(); + found.pop(); + } + else if (stack.length > 0) { + let comment = false; + for (let b of stack) { + if (!b) { + comment = true; + break; + } + } + if (comment) { + lines[i] = "///" + lines[i]; + } + } + if (lines[i].indexOf("__ID__") > -1 && !lines[i].startsWith("declare")) { + // #define ID__(x, y) x ":" #y + // #define ID_(x, y) ID__(x, y) + // #define ID ID_(__FILE__, __LINE__) + lines[i] = lines[i].replace("__ID__", "\"" + path_basename(file_path) + ":" + i + "\""); + } + } + return lines.join("\n"); +} + +function write_ts_project(projectdir, options) { + let tsdata = { + include: [] + }; + + let main_ts = null; + + for (let i = 0; i < options.sources.length; ++i) { + let files = fs_readdir(options.sources[i]); + for (let file of files) { + if (file.endsWith(".ts")) { + // Prevent duplicates, keep the newly added file + for (let included of tsdata.include){ + if (path_basename(included) == file) { + tsdata.include.splice(tsdata.include.indexOf(included), 1); + break; + } + } + tsdata.include.push(options.sources[i] + path_sep + file); + if (file == "main.ts") main_ts = options.sources[i] + path_sep + file; + } + } + } + + // Include main.ts last + if (main_ts != null) { + tsdata.include.splice(tsdata.include.indexOf(main_ts), 1); + tsdata.include.push(main_ts); + } + + fs_ensuredir(projectdir); + fs_writefile(path_join(projectdir, 'tsconfig.json'), JSON.stringify(tsdata, null, 4)); + + // alang compiler + globalThis.options = options; + let source = ''; + let file_paths = tsdata.include; + for (let file_path of file_paths) { + let file = fs_readfile(file_path); + file = ts_preprocessor(file, file_path); + source += file; + } + + if (goptions.alangjs) { + globalThis.std = std; + globalThis.fs_readfile = fs_readfile; + globalThis.fs_writefile = fs_writefile; + globalThis.flags.alang_source = source; + globalThis.flags.alang_output = os_cwd() + path_sep + "build" + path_sep + "iron.c"; + let alang = armorcoredir + '/tools/amake/alang.js'; + (1, eval)(fs_readfile(alang)); + } + else { + // let alang_input = os_cwd() + path_sep + "build" + path_sep + "iron.ts"; + // fs_writefile(alang_input, source); + let alang_output = os_cwd() + path_sep + "build" + path_sep + "iron.c"; + let start = Date.now(); + amake.alang(source, alang_output); + console.log("alang took " + (Date.now() - start) + "ms."); + } +} + +function export_project_files(name, options, exporter, defines) { + let ts_options = exporter.ts_options(defines); + write_ts_project("build", ts_options); + exporter.export(); + return name; +} + +function export_armorcore_project(project, options) { + let temp = path_join("build", "temp"); + fs_ensuredir(temp); + + let exporter = new ArmorCoreExporter(project, options); + fs_ensuredir(path_join("build", "out")); + + for (let source of project.sources) { + exporter.add_source_directory(source); + } + + let asset_converter = new AssetConverter(exporter, options, project.asset_matchers); + let assets = asset_converter.run(); + + let shaderdir = path_join("build", "out", "data"); + if (globalThis.flags.embed) { + shaderdir = path_join("build", "temp"); + } + fs_ensuredir(shaderdir); + + let exported_shaders = []; + let krafix = path_join(armorcoredir, "tools", "bin", sys_dir(), "krafix" + exe_ext()); + let shader_compiler = new ShaderCompiler(exporter, krafix, shaderdir, temp, options, project.shader_matchers); + exported_shaders = shader_compiler.run(); + + // Write embed.h + if (globalThis.flags.embed) { + let embed_files = []; + for (let asset of assets) { + if (asset.noembed || + asset.from.endsWith(".txt") || + asset.from.endsWith(".md") || + asset.from.endsWith(".json") || + asset.from.endsWith(".js")) { + continue; + } + embed_files.push(path_resolve("build", "temp", asset.files[0])); + } + for (let shader of exported_shaders) { + embed_files.push(shader.files[0]); + } + + if (embed_files.length > 0) { + let embed_header = "#pragma once\n"; + for (let file of embed_files) { + embed_header += "const unsigned char " + path_basename(file).replaceAll(".", "_") + "[] = {\n" + if (platform === "windows") { + file = file.replaceAll("\\", "/"); + } + embed_header += "#embed \"" + file + "\"\n"; + embed_header += "};\n" + } + embed_header += "char *embed_keys[] = {\n" + for (let file of embed_files) { + embed_header += "\"./data/" + path_basename(file) + "\",\n"; + } + embed_header += "};\n" + embed_header += "const unsigned char *embed_values[] = {\n" + for (let file of embed_files) { + embed_header += path_basename(file).replaceAll(".", "_") + ",\n"; + } + embed_header += "};\n" + embed_header += "const int embed_sizes[] = {\n"; + for (let file of embed_files) { + embed_header += "sizeof(" + path_basename(file).replaceAll(".", "_") + "),\n" + } + embed_header += "};\n" + embed_header += "int embed_count = " + embed_files.length + ";\n"; + fs_writefile(path_join("build", "embed.h"), embed_header); + } + } + + export_project_files(project.name, options, exporter, project.defines); +} + +class Project { + constructor(name) { + this.cppStd = "c++17"; + this.cStd = "c11"; + this.cmdArgs = []; + this.cFlags = []; + this.cppFlags = []; + this.icon = "icon.png"; + this.lto = true; + this.noFlatten = true; + this.name = name; + this.safe_name = name.replace(/[^A-z0-9\-\_]/g, "-"); + this.version = "1.0"; + this.debugdir = "build/out"; + this.basedir = __dirname; + this.uuid = crypto_random_uuid(); + this.files = []; + this.customs = []; + this.javadirs = []; + this.subProjects = []; + this.includedirs = []; + this.defines = []; + this.libs = []; + this.includes = []; + this.target_options = { + android: {}, + }; + this.executable_name = null; + this.sources = []; + this.asset_matchers = []; + this.shader_matchers = []; + } + + get_executable_name() { + return this.executable_name; + } + + flatten_subprojects() { + for (let sub of this.subProjects) { + sub.noFlatten = false; + sub.flatten_subprojects(); + } + } + + flatten() { + this.noFlatten = false; + this.flatten_subprojects(); + } + + internal_flatten() { + let out = []; + for (let sub of this.subProjects) { + sub.internal_flatten(); + } + for (let sub of this.subProjects) { + if (sub.noFlatten) { + out.push(sub); + } + else { + if (!sub.lto) { + this.lto = false; + } + if (sub.icon) { + this.icon = sub.icon; + } + let subbasedir = sub.basedir; + for (let tkey of Object.keys(sub.target_options)) { + let target = sub.target_options[tkey]; + for (let key of Object.keys(target)) { + let options = this.target_options[tkey]; + let option = target[key]; + if (options[key] == null) + options[key] = option; + // push library properties to current array instead + else if (Array.isArray(options[key]) && Array.isArray(option)) { + for (let value of option) { + if (!options[key].includes(value)) + options[key].push(value); + } + } + } + } + for (let d of sub.defines) { + if (d.indexOf("=") >= 0) { + if (!contains_fancy_define(this.defines, d)) { + this.defines.push(d); + } + } + else { + if (!contains_define(this.defines, d)) { + this.defines.push(d); + } + } + } + for (let file of sub.files) { + let absolute = file.file; + if (!path_isabs(absolute)) { + absolute = path_join(subbasedir, file.file); + } + this.files.push({ file: absolute.replace(/\\/g, "/"), options: file.options, projectDir: subbasedir, projectName: sub.name }); + } + for (let custom of sub.customs) { + let absolute = custom.file; + if (!path_isabs(absolute)) { + absolute = path_join(subbasedir, custom.file); + } + this.customs.push({ file: absolute.replace(/\\/g, "/"), command: custom.command, output: custom.output }); + } + for (let i of sub.includedirs) + if (!this.includedirs.includes(path_resolve(subbasedir, i))) + this.includedirs.push(path_resolve(subbasedir, i)); + for (let j of sub.javadirs) + if (!this.javadirs.includes(path_resolve(subbasedir, j))) + this.javadirs.push(path_resolve(subbasedir, j)); + for (let lib of sub.libs) { + if (!this.libs.includes(lib)) + this.libs.push(lib); + } + for (let flag of sub.cFlags) { + if (!this.cFlags.includes(flag)) { + this.cFlags.push(flag); + } + } + for (let flag of sub.cppFlags) { + if (!this.cppFlags.includes(flag)) { + this.cppFlags.push(flag); + } + } + } + } + this.subProjects = out; + } + + getName() { + return this.name; + } + + get_safe_name() { + return this.safe_name; + } + + get_uuid() { + return this.uuid; + } + + addCFlag(flag) { + this.cFlags.push(flag); + } + + addCFlags() { + for (let i = 0; i < arguments.length; ++i) { + if (typeof arguments[i] === "string") { + this.addCFlag(arguments[i]); + } + } + } + + add_file_for_real(file, options) { + for (let index in this.files) { + if (this.files[index].file === file) { + this.files[index] = { file: file, options: options, projectDir: this.basedir, projectName: this.name }; + return; + } + } + this.files.push({ file: file, options: options, projectDir: this.basedir, projectName: this.name }); + } + + search_files(current) { + if (current === undefined) { + for (let sub of this.subProjects) + sub.search_files(undefined); + this.search_files(this.basedir); + for (let includeobject of this.includes) { + if (path_isabs(includeobject.file) && includeobject.file.includes("**")) { + let starIndex = includeobject.file.indexOf("**"); + let endIndex = includeobject.file.substring(0, starIndex).replace(/\\/g, "/").lastIndexOf("/"); + this.search_files(includeobject.file.substring(0, endIndex)); + } + if (includeobject.file.startsWith("../")) { + let start = "../"; + while (includeobject.file.startsWith(start)) { + start += "../"; + } + this.search_files(path_resolve(this.basedir, start)); + } + } + return; + } + let files = fs_readdir(current); + for (let f in files) { + let file = path_join(current, files[f]); + let follow = true; + try { + if (fs_isdir(file)) { + follow = false; + } + } + catch (err) { + follow = false; + } + if (!follow) { + continue; + } + + file = path_relative(this.basedir, file); + for (let includeobject of this.includes) { + let include = includeobject.file; + if (path_isabs(include)) { + let inc = include; + inc = path_relative(this.basedir, inc); + include = inc; + } + if (matches(stringify(file), stringify(include))) { + this.add_file_for_real(stringify(file), includeobject.options); + } + } + } + let dirs = fs_readdir(current); + for (let d of dirs) { + let dir = path_join(current, d); + if (d.startsWith(".")) + continue; + let follow = true; + try { + if (!fs_isdir(dir)) { + follow = false; + } + } + catch (err) { + follow = false; + } + if (!follow) { + continue; + } + this.search_files(dir); + } + } + + add_cfiles(file, options) { + this.includes.push({ file: file, options: options }); + } + + add_define(define) { + if (contains_define(this.defines, define)) { + return; + } + this.defines.push(define); + } + + add_include_dir(include) { + if (this.includedirs.includes(include)) + return; + this.includedirs.push(include); + } + + add_lib(lib) { + this.libs.push(lib); + } + + add_assets(match, options) { + if (!options) + options = {}; + if (!path_isabs(match)) { + let base = stringify(path_resolve(this.basedir)); + if (!base.endsWith('/')) { + base += '/'; + } + match = base + match.replace(/\\/g, '/'); + } + this.asset_matchers.push({ match: match, options: options }); + } + + add_tsfiles(source) { + this.sources.push(path_resolve(path_join(this.basedir, source))); + } + + add_shaders(match, options) { + if (!options) + options = {}; + if (!path_isabs(match)) { + let base = stringify(path_resolve(this.basedir)); + if (!base.endsWith('/')) { + base += '/'; + } + match = base + match.replace(/\\/g, '/'); + } + this.shader_matchers.push({ match: match, options: options }); + } + + getFiles() { + return this.files; + } + + getJavaDirs() { + return this.javadirs; + } + + getSubProjects() { + return this.subProjects; + } + + getIncludeDirs() { + return this.includedirs; + } + + getDefines() { + return this.defines; + } + + getLibs() { + return this.libs; + } + + get_debug_dir() { + return this.debugdir; + } + + add_project(directory) { + let from = path_isabs(directory) ? directory : path_join(this.basedir, directory); + let project = load_project(from, false); + this.subProjects.push(project); + this.asset_matchers = this.asset_matchers.concat(project.asset_matchers); + this.sources = this.sources.concat(project.sources); + this.shader_matchers = this.shader_matchers.concat(project.shader_matchers); + this.defines = this.defines.concat(project.defines); + return project; + } + + static create(directory) { + let project = load_project(path_resolve(directory), true); + let defines = []; + for (let define of defines) { + project.add_define(define); + } + return project; + } +} + +function export_koremake_project() { + console.log('Creating ' + goptions.target + ' project files.'); + + let project = Project.create("."); + if (goptions.graphics === "metal") { + project.add_cfiles(path_join("build", 'sources', '*'), {}); + } + project.search_files(undefined); + project.internal_flatten(); + fs_ensuredir("build"); + + let exporter = null; + if (goptions.target === 'ios' || goptions.target === 'macos') { + exporter = new XCodeExporter(); + } + else if (goptions.target === 'android') { + exporter = new AndroidExporter(); + } + else if (goptions.target === 'wasm') { + exporter = new WasmExporter(); + } + else if (goptions.target === 'linux') { + exporter = new LinuxExporter(); + } + else { + exporter = new VisualStudioExporter(); + } + + exporter.export_solution(project); + return project; +} + +function compile_project(make, project) { + if (make.status != 0) { + os_exit(1); + } + let executable_name = project.get_safe_name(); + if (project.get_executable_name()) { + executable_name = project.get_executable_name(); + } + if (goptions.target === "linux") { + let from = path_resolve(path_join("build", goptions.build_path), executable_name); + let to = path_resolve(".", project.get_debug_dir(), executable_name); + fs_copyfile(from, to); + os_chmod(to, "+x"); + } + else if (goptions.target === "windows") { + let from = path_join("x64", goptions.debug ? "Debug" : "Release", executable_name + ".exe"); + let to = path_resolve("..", project.get_debug_dir(), executable_name + ".exe"); + fs_copyfile(from, to); + } + if (goptions.run) { + if (goptions.target === "macos") { + os_exec("build/" + (goptions.debug ? "Debug" : "Release") + "/" + project.name + ".app/Contents/MacOS/" + project.name, [], { cwd: "build" }); + } + else if (goptions.target === "linux") { + os_exec(path_resolve(".", project.get_debug_dir(), executable_name), [], { cwd: path_resolve(".", project.get_debug_dir()) }); + } + else if (goptions.target === "windows") { + os_exec(path_resolve("..", project.get_debug_dir(), executable_name), [], { cwd: path_resolve(".", project.get_debug_dir()) }); + } + } +} + +function main() { + console.log('Using ArmorCore from ' + armorcoredir); + goptions.build_path = goptions.debug ? 'Debug' : 'Release'; + let project = export_koremake_project(); + + let project_name = project.get_safe_name(); + if (goptions.compile && project_name !== '') { + console.log('Compiling...'); + let make = null; + if (goptions.target == 'linux' || goptions.target == 'wasm') { + let cores = os_cpus_length(); + make = os_exec('make', ['-j', cores.toString()], { cwd: path_join("build", goptions.build_path) }); + } + else if (goptions.target == 'macos' || goptions.target == 'ios') { + let xcode_options = ['-configuration', goptions.debug ? 'Debug' : 'Release', '-project', project_name + '.xcodeproj']; + make = os_exec('xcodebuild', xcode_options, { cwd: "build" }); + } + else if (goptions.target == 'windows') { + // let vswhere = path_join(os_env('ProgramFiles(x86)'), 'Microsoft Visual Studio', 'Installer', 'vswhere.exe'); + // let vsvars = os_exec(vswhere, ['-products', '*', '-latest', '-find', 'VC\\Auxiliary\\Build\\vcvars64.bat']).trim(); + let vsvars = "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat"; + fs_writefile(path_join("build", 'build.bat'), '@call "' + vsvars + '"\n' + '@MSBuild.exe "' + path_resolve("build", project_name + '.vcxproj') + '" /m /clp:ErrorsOnly /p:Configuration=' + (goptions.debug ? 'Debug' : 'Release') + ',Platform=x64'); + make = os_exec('build.bat', [], { cwd: "build" }); + } + else if (goptions.target == 'android') { + let gradlew = (os_platform() === 'win32') ? 'gradlew.bat' : 'bash'; + let args = (os_platform() === 'win32') ? [] : ['gradlew']; + args.push('assemble' + (goptions.debug ? 'Debug' : 'Release')); + make = os_exec(gradlew, args, { cwd: path_join("build", project_name) }); + } + if (make !== null) { + compile_project(make, project); + } + } +} + +function default_target() { + if (os_platform() === 'linux') { + return 'linux'; + } + else if (os_platform() === 'win32') { + return 'windows'; + } + else { + return 'macos'; + } +} + +let goptions = { + target: default_target(), + graphics: 'default', + visualstudio: 'vs2022', + compile: false, + run: false, + debug: false, + ccompiler: 'clang', + cppcompiler: 'clang++', + arch: 'default', + alangjs: false, + js: false, + ashader: false, + hlslbin: false, +}; + +let args = scriptArgs; +for (let i = 1; i < args.length; ++i) { + let arg = args[i]; + if (arg.startsWith("--")) { + let name = arg.substring(2); + let value = true; + if (i < args.length - 1 && !args[i + 1].startsWith("--")) { + ++i; + value = args[i]; + } + goptions[name] = value; + } +} + +if (goptions.js) { + globalThis.std = std; + globalThis.fs_readfile = fs_readfile; + globalThis.fs_writefile = fs_writefile; + globalThis.fs_exists = fs_exists; + globalThis.fs_readdir = fs_readdir; + (1, eval)(fs_readfile(goptions.js)); + std.exit(); +} + +if (goptions.ashader) { + let type = args[3]; + let from = args[4]; + let to = args[5]; + amake.ashader(type, from, to); + std.exit(); +} + +if (goptions.hlslbin) { + let from = args[3]; + let to = args[4]; + amake.hlslbin(from, to); + std.exit(); +} + +if (goptions.run) { + goptions.compile = true; +} + +let start = Date.now(); +main(); +console.log("Done in " + (Date.now() - start) + "ms."); diff --git a/armorcore/tools/platform.sh b/armorcore/tools/platform.sh new file mode 100644 index 000000000..0a71d510f --- /dev/null +++ b/armorcore/tools/platform.sh @@ -0,0 +1,10 @@ +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + MACHINE_TYPE=`uname -m` + if [[ "$MACHINE_TYPE" == "aarch64"* ]]; then + KINC_PLATFORM=linux_arm64 + elif [[ "$MACHINE_TYPE" == "x86_64"* ]]; then + KINC_PLATFORM=linux_x64 + fi +elif [[ "$OSTYPE" == "darwin"* ]]; then + KINC_PLATFORM=macos +fi diff --git a/armorcore/tools/tcc/COPYING b/armorcore/tools/tcc/COPYING new file mode 100644 index 000000000..223ede7de --- /dev/null +++ b/armorcore/tools/tcc/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/armorcore/tools/tcc/VERSION b/armorcore/tools/tcc/VERSION new file mode 100644 index 000000000..9a542234e --- /dev/null +++ b/armorcore/tools/tcc/VERSION @@ -0,0 +1 @@ +0.9.27 diff --git a/armorcore/tools/tcc/include/float.h b/armorcore/tools/tcc/include/float.h new file mode 100644 index 000000000..f16f1f0cb --- /dev/null +++ b/armorcore/tools/tcc/include/float.h @@ -0,0 +1,57 @@ +#ifndef _FLOAT_H_ +#define _FLOAT_H_ + +#define FLT_RADIX 2 + +/* IEEE float */ +#define FLT_MANT_DIG 24 +#define FLT_DIG 6 +#define FLT_ROUNDS 1 +#define FLT_EPSILON 1.19209290e-07F +#define FLT_MIN_EXP (-125) +#define FLT_MIN 1.17549435e-38F +#define FLT_MIN_10_EXP (-37) +#define FLT_MAX_EXP 128 +#define FLT_MAX 3.40282347e+38F +#define FLT_MAX_10_EXP 38 + +/* IEEE double */ +#define DBL_MANT_DIG 53 +#define DBL_DIG 15 +#define DBL_EPSILON 2.2204460492503131e-16 +#define DBL_MIN_EXP (-1021) +#define DBL_MIN 2.2250738585072014e-308 +#define DBL_MIN_10_EXP (-307) +#define DBL_MAX_EXP 1024 +#define DBL_MAX 1.7976931348623157e+308 +#define DBL_MAX_10_EXP 308 + +/* horrible intel long double */ +#if defined __i386__ || defined __x86_64__ + +#define LDBL_MANT_DIG 64 +#define LDBL_DIG 18 +#define LDBL_EPSILON 1.08420217248550443401e-19L +#define LDBL_MIN_EXP (-16381) +#define LDBL_MIN 3.36210314311209350626e-4932L +#define LDBL_MIN_10_EXP (-4931) +#define LDBL_MAX_EXP 16384 +#define LDBL_MAX 1.18973149535723176502e+4932L +#define LDBL_MAX_10_EXP 4932 + +#else + +/* same as IEEE double */ +#define LDBL_MANT_DIG 53 +#define LDBL_DIG 15 +#define LDBL_EPSILON 2.2204460492503131e-16 +#define LDBL_MIN_EXP (-1021) +#define LDBL_MIN 2.2250738585072014e-308 +#define LDBL_MIN_10_EXP (-307) +#define LDBL_MAX_EXP 1024 +#define LDBL_MAX 1.7976931348623157e+308 +#define LDBL_MAX_10_EXP 308 + +#endif + +#endif /* _FLOAT_H_ */ diff --git a/armorcore/tools/tcc/include/stdarg.h b/armorcore/tools/tcc/include/stdarg.h new file mode 100644 index 000000000..10ce733b4 --- /dev/null +++ b/armorcore/tools/tcc/include/stdarg.h @@ -0,0 +1,79 @@ +#ifndef _STDARG_H +#define _STDARG_H + +#ifdef __x86_64__ +#ifndef _WIN64 + +//This should be in sync with the declaration on our lib/libtcc1.c +/* GCC compatible definition of va_list. */ +typedef struct { + unsigned int gp_offset; + unsigned int fp_offset; + union { + unsigned int overflow_offset; + char *overflow_arg_area; + }; + char *reg_save_area; +} __va_list_struct; + +typedef __va_list_struct va_list[1]; + +void __va_start(__va_list_struct *ap, void *fp); +void *__va_arg(__va_list_struct *ap, int arg_type, int size, int align); + +#define va_start(ap, last) __va_start(ap, __builtin_frame_address(0)) +#define va_arg(ap, type) \ + (*(type *)(__va_arg(ap, __builtin_va_arg_types(type), sizeof(type), __alignof__(type)))) +#define va_copy(dest, src) (*(dest) = *(src)) +#define va_end(ap) + +/* avoid conflicting definition for va_list on Macs. */ +#define _VA_LIST_T + +#else /* _WIN64 */ +typedef char *va_list; +#define va_start(ap,last) __builtin_va_start(ap,last) +#define va_arg(ap, t) ((sizeof(t) > 8 || (sizeof(t) & (sizeof(t) - 1))) \ + ? **(t **)((ap += 8) - 8) : *(t *)((ap += 8) - 8)) +#define va_copy(dest, src) ((dest) = (src)) +#define va_end(ap) +#endif + +#elif __arm__ +typedef char *va_list; +#define _tcc_alignof(type) ((int)&((struct {char c;type x;} *)0)->x) +#define _tcc_align(addr,type) (((unsigned)addr + _tcc_alignof(type) - 1) \ + & ~(_tcc_alignof(type) - 1)) +#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3) +#define va_arg(ap,type) (ap = (void *) ((_tcc_align(ap,type)+sizeof(type)+3) \ + &~3), *(type *)(ap - ((sizeof(type)+3)&~3))) +#define va_copy(dest, src) (dest) = (src) +#define va_end(ap) + +#elif defined(__aarch64__) +typedef struct { + void *__stack; + void *__gr_top; + void *__vr_top; + int __gr_offs; + int __vr_offs; +} va_list; +#define va_start(ap, last) __va_start(ap, last) +#define va_arg(ap, type) __va_arg(ap, type) +#define va_end(ap) +#define va_copy(dest, src) ((dest) = (src)) + +#else /* __i386__ */ +typedef char *va_list; +/* only correct for i386 */ +#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3) +#define va_arg(ap,type) (ap += (sizeof(type)+3)&~3, *(type *)(ap - ((sizeof(type)+3)&~3))) +#define va_copy(dest, src) (dest) = (src) +#define va_end(ap) +#endif + +/* fix a buggy dependency on GCC in libio.h */ +typedef va_list __gnuc_va_list; +#define _VA_LIST_DEFINED + +#endif /* _STDARG_H */ diff --git a/armorcore/tools/tcc/include/stdbool.h b/armorcore/tools/tcc/include/stdbool.h new file mode 100644 index 000000000..d2ee446e7 --- /dev/null +++ b/armorcore/tools/tcc/include/stdbool.h @@ -0,0 +1,11 @@ +#ifndef _STDBOOL_H +#define _STDBOOL_H + +/* ISOC99 boolean */ + +#define bool _Bool +#define true 1 +#define false 0 +#define __bool_true_false_are_defined 1 + +#endif /* _STDBOOL_H */ diff --git a/armorcore/tools/tcc/include/stddef.h b/armorcore/tools/tcc/include/stddef.h new file mode 100644 index 000000000..694d50375 --- /dev/null +++ b/armorcore/tools/tcc/include/stddef.h @@ -0,0 +1,54 @@ +#ifndef _STDDEF_H +#define _STDDEF_H + +typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ssize_t; +typedef __WCHAR_TYPE__ wchar_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef __PTRDIFF_TYPE__ intptr_t; +typedef __SIZE_TYPE__ uintptr_t; + +#ifndef __int8_t_defined +#define __int8_t_defined +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +#ifdef __LP64__ +typedef signed long int int64_t; +#else +typedef signed long long int int64_t; +#endif +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +#ifdef __LP64__ +typedef unsigned long int uint64_t; +#else +typedef unsigned long long int uint64_t; +#endif +#endif + +#ifndef NULL +#define NULL ((void*)0) +#endif + +#define offsetof(type, field) ((size_t)&((type *)0)->field) + +void *alloca(size_t size); + +#endif + +/* Older glibc require a wint_t from (when requested + by __need_wint_t, as otherwise stddef.h isn't allowed to + define this type). Note that this must be outside the normal + _STDDEF_H guard, so that it works even when we've included the file + already (without requiring wint_t). Some other libs define _WINT_T + if they've already provided that type, so we can use that as guard. + TCC defines __WINT_TYPE__ for us. */ +#if defined (__need_wint_t) +#ifndef _WINT_T +#define _WINT_T +typedef __WINT_TYPE__ wint_t; +#endif +#undef __need_wint_t +#endif diff --git a/armorcore/tools/tcc/include/varargs.h b/armorcore/tools/tcc/include/varargs.h new file mode 100644 index 000000000..d614366ed --- /dev/null +++ b/armorcore/tools/tcc/include/varargs.h @@ -0,0 +1,12 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the w64 mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER within this package. + */ +#ifndef _VARARGS_H +#define _VARARGS_H + +#error "TinyCC no longer implements ." +#error "Revise your code to use ." + +#endif diff --git a/armorcore/tools/tcc/libtcc1.a b/armorcore/tools/tcc/libtcc1.a new file mode 100644 index 0000000000000000000000000000000000000000..42e571f39a81a73bb8412f3348a3e6b8d582c102 GIT binary patch literal 239412 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˵n{WV#}8h^Ko^FQD%($0FD_fpBrpY&0)4QrMFZBGlzxKxZG3Ac z9XITxiRAl`V?b8^oq(Ev9rBmW1stZztJ+J;3!8_aR}w|WF2 z{0ZZu`d1`Go5g*0E>qkq55A`4U}-_csgbYaO~zQnmRBd{V`^n?tlg?U_nM9SMus1%yr6Q-m^p;G_zqBZvKUiM z-BMNsw#DV|9N{ZjT%5YBnJ6^DHu7xG*3k&jLF{dEv zz{G+RMW+Osu_OKWnyp7}Ej2KM?_7CFWf41v{yeYX6Q`a-;pWIS{ASCCn*5z30pP&O zMk)(v|H02=pc$DgLZMI{Fd z3SJTsGM2JsRw=ao+kLR%qv<%4u{x8 zGkGJ*TdU4wS=Ta?^~i0WGY+2Qy2ZB7n{_mdBK0&9QHZp}( zS>LtsOptnnujKxn!HeX#qEsg8{xSKy2+NARSKlY`3ELT)|NA!o@^L~8**|~Qt3II{ zDO1NmPMnwjQ_}xAe_s9G^8(_;Ke`s=CD&w{T$Ym=uYdHbPv$zwKeqlC{>3Ex-k5?d zi`6`mVje%qf2O?T{Pq6ay!dm%#OvYTd)5En1lZ*Z^O^o zZgzc?=F9*ONgCsO1*>>L_ACsIe)B=>i*H^)bcori;khX%(-W*pi)#e16)nQ&;q zIDe#|x5&RGwtq@&-J&h4eK(2=6t8HDiZ*2@f#E#z{a30ty#><_QG|2?l@+rNw%z0qsFy<+* zG~ZUoj*moc!B$(uspaeLoDAxvut-bn_>9Q%*zvDNnqtSxBVRS;#Sg4|GIsp)-M3)s zU{BXZDd9pXVI#;gpJmJ2jg^P&1(j(5h4zv#x{%ECy^rR%SU9k1iS_A@hyEXlX zBHxl4e_|@DJU~@zA0)(y%Ux1Z$Tx<6>Df6kAEKLb`i@E+PX8i?de-_%wnn|U_`CF)Ek7G0*X9WBa_k}8Ng>!n z5G-TfO_O>Z%+;h6;@K<+#NMo0oLI}fK>NV~by(2U@^ejMK~bGkow7AzXR2~wqfg6B zD?QdGzgSx5?lYjAfdK$)8Jz^nAquvzWMS}Zi=EV%^jF2sK*l!1$?>246Y?S~N$^uSTjcV8=ZV7wFq2=*OC#IZZ$`hIhOYGRDZ6 zFokKG`Etvs>Z*K8Gi_xQF2Pb1yjN_-1PuhE_^;THO)SLP{p)nXjbfY4eL*->E)LzDiSgWWgZc5dC~(?3$MN;; z7Z+P;`<&R(yvR7Qro4aeoE=VlFG;kv)13a7#eZbH(=Ykdc5Up)IA$c?0!u4*F&A3N zr~}#Ot3`Fm)unaGCb%j0qra2F`K_djf{{7u!Nt>dFhVnb83 z#4Ac83uF7H^d2pZTvfR%_QsURdBc|)d;}TV1|0_?w~6SrPD`g2&1tLoOIuBJhlpM7 z4#S_}&9roqwiGvghK?OWUx2Yg;)98Gz`eGJn@wWdN`tYjrRm+UjP*hn3$XCDun5C8 z!@GHnzbwA*Rqh)8j^vBIeObVTYa9u>??QdX`lMWJJfm`#^T-XQp}zfB6@+5PFf%WG z?mY5^+@O3WpTrr+jfslb8&2|$k=RIqbJynxtxjHt0p4x3?)E_J7-3Thi+xB(B-I1z z|5`l92zgpPgot&b#Q2{$PXEX81rlc*+Kj&#|N8-_qquMXb1;(kosRL-#1l+%Q(Mq| z(2H^>0S^2OZ#x(?DwoI&NZF4=`0_=2^`AUCrK(WE=b#^pfnHp3MRX1Ej7avCEyNpqb3 zDCMNGN5yvvo2b&1*{EWwwi&4e`u=g~7y#{$7^GpAVu3tw zOR#*$7i>aGX8DqBF)X_8?NZ^x*LI%7n=v7}9ow3=D48gB!HENpzKiv*M+>PIgM_hx zE9yO?bppw1Q+E7LE)3v^OvEF;=Xng$7gOV?zb8{T~$XxM#s>l>>(sp5c#7 zbsoN^(0yj$EmpO==In?}92_cy;PaloXF*}MzC#PgRbi7B5+8mpD<8{kO|07fprQjL z7nIR=F90Ga)Vlw9Saquo&MM1T)nTo<1Q@4hT#5lV#f&kZ)wXeZoiV5E%mRU z3_jWa15z?GOENS7PEKq`X$HT}xb7#NBnD4kp4=64w+l_udmCJyz%A-{ooN z`-=<94n662;>XG9JUoA#^YBt%@#fgjrr7=}8c)C@H9p16T<4J`<1A)EgKv#@cgFUC zqJoHn*|RyDoojc*2i&JI%r5D`-r&%9CZTm77VikTQf>`%`Z-+u@G!CahqUZO&1Cwo zz~v$?=1mCNOq}U!AVdS-=_7kFhdh<(VSZ^X^8ktFPf5R*iC6I|;64>dXgi`e2nOR9 z!`6CV+gn|nB3mFax$Mx^Ua=UTTZZ1zo>|`{KJVQMHkN%VdtC2s?%VI{WqK@rEBf~z zomjv~-%9Q77mMf^y_e~uQzx$as7gONkELtT2Z%D@w{i*Cua{cF>U%p#&yHevml1#= zeKS(NbQ0wk?x=HDfz)7W$nAuM?zPOMzf3abOuAyE99}2o=d3<6{9l?<<8HONl~1sY zNUVv-g&yC7w!lVU1KdvTIDNy3eK;;s41QFwLxrSLxyO0w#BzcW?`d0a-m!W zTe(pzGFGE=K(gbaH_QrFG-xH&LJX|ZEItyq`^7GDlGGwu2L|6P47h1-J-p5`-TBE$ z<5{#(r)GEO7cKj8S}aN$I!hE>D7Fu|7R|Z)3xsYQ@5`HlW#6q%Y#y&c_j$F#DKJe5 zZty-i)k_+KKv!xTUMDaeODkQA4u)fi9xW1&IY`2Q~l)SW2H| zh`gUSk#!flg8|X~z%5k$6GZJnf>Z# z1l^hpF>cv(@Ej{Jcrroq5-Cd8BW6Ks7i+=fy(qR4J1p8jI^HlBYM`TyKhFAcM|Jn$i#AHV*H$g(YF!CGxH_$PR}g?{)YU8jw&b&uA%2ZM7CV)t)- z&>ajWs%9iA56fu>fAsV2;ZexBpw0@cO(V65Cyogeb;%pJ%4?gy?+t^jH5Ii^&9R{S zIiNbzv*2uKJm{|R*UlM;{vA;Ti`_hIn~Tg~igX^C$6EZVQ0&N*NYHs?AeNeqeqrs$ zgR!@&g4T3;3|)rupTuoeS75;zlP1HzH|=5dW0{x=pkqrLg?{*eC0(o2nzJK(5f;qH z2b1+43{H7f6+zdQI_1H7JU95A^7`d?8Y-OfWt|xr-!kMe<;$WT0sPaTDtA7sE(=x; zxJM4Zz}1OJSi!-ealyP7DAdP~In3#(hmCYBt57B?%Sv5kI_1}wQTu=nm1$g|#n~`I0FQbU@{H`s{nU}R41l}MO2trN*luH}$_;hl?he7v4kZM{g;FfO- zmOrmm9o|D;587bU0p2nI>!nVEuY;W7KMwnNU|;6xg>w3UTFqi5J&PeFcOBLW<3`{R zol7lRBeoz+iY3W~d1e8Ffob$geoII)k!%SjYE)#~P{G`f99SqjDyPX!k7w7p@1he# zv0A~FZ_pMOhGZpzEx4tlwx>slr>5sG%KZXiG z=KCVIzLXmaG>!O*mU`(kfg)%*H~q0?vOg<>C8IyHkKcV4N- ze-fW`cNhg*q%L;kw8%w$`*kzp7|g{N`9kbSiF3CAVy`E|T5;3lzYgX-GqT_CGofE$cOb z8qV)03VG&>?$N-~*uJZ*0&n@K!Lf{cL5^PJ+_!(X@quX0+CquuhNuzD8qRKk<@)M;BIA;?5Y6W z6PWYs=x;<|O%Pq{rVc%s7dKib??Q0u-R`m9NH8SM+o zt!np8WM55HzC#q^hhI8(M<7vZ70=&7OIP~XEZ_;@W0FLuRU!&;W6l((e}LLes|Cbr zcOP&>LXgn9*wdAaq0$W%q>#4geT;4f)WI`#eHA;_tz(^EU{fT$dY-Z}6Qff+-YS75mMs*#1(d zdTY8(Gx|38I2|%9#Ay0AJD;R0Nnx6oCBOBRhtfw;_GQv9MF?6F9DEagY`RtFJJZ+Y zC%EaL>nx=|=aJh>MLn~nN{b{S{5EZqt?nG|1wB_=WyLP9yX;j=X2Kdi!>$|TQomsA z*jbSU?h%%m>)qXvw`))j&8(aAd{?;@loVgVQmA##&d9UQBf&F`cQbQPRdCMMFn0mk z@nN?Kg5;hr_=EwRX(sE7x=Wl#xH9l62!HCN-spwdd356SfNVurYRt-UAA3|XiGtBD5}m=5kbK-Dm#XH_kteITtIRO--tAq{a#f#?qi?}vu!=c!c`wc?*#NW)4*GjNL7m^n z>G`=+zP!qP&izfOZ{NUuPWh_(*qc{{`ra7%Fx0nwR^GO%Drrk7Hd5)7cP_W`sZ;YC zr@W_gqYT@S8?2W*@BOKte!}P51eUxRDEIPWCw@~A5RJaTSm-Gb8$m}EJ5k`ozXX%z zl^b!d#rKn5E^?(XMgI9P$Rp!x+}HbFT{q%@MeQBJuiX)dy?zy*8-{Qi4!Y+!HjMll&GJ?C1SCm!%lw!m=g;IoJX!<-aFX)`u)W_ z9#hV<&0R16UBP^gId^`XE`7{LYqXu8;BAvIp>#ezLK`y&30(vPR={rMLDX^-h{_j=ltkM z`0P!+c)|CC$QigLn}C2c%zI9?H^#KV?o-Yq3uu!F=#|`MJx+4HbGme#bN6YKml#+1 zvipusDyKUSZb{zsokI7k0~w<198iK*CljbJZIdv%BUD|yd`J2UxVeYVhR>R@-~R}W z_xhvqwRE@QyqEgH`|qpl5s=~kNAj6Ps9g-CKc^|HEug6@fm=aNhcu!JGGM-OkM;hh z>L*M?zbk$w zD!pQzH@jZ3WGRa#Sue>_Nmfa@WhKm>D58vv_i9n*WZ>BS>fznNrk4$4sTXiE0B@2b zxVeCh<}?4z5E;#9((B)qTgKqw>!@ce8Vy*U(n`V4m-R6C02 z#P22BM-xT5B1?~)pL60*K*Q8mC|bT!88uUcR7EL?gg9QVL=42X6;!*#*4YiJU-$(O z<&e%JRi&Y6Y`Q`bunMHXmBX@wXzZ}BbN1I1};ne zKLA!L@Jp4WsJ~N!de9MR!)0!jDOILUnJ_ar=>LG8(s8Q8ZzMZLWq>XRb(^+>7n>f+ z{cSe=lQuC2P~KDQnK63-nX1cQ%?{f+!_KvP+!u9eqgi$jA$pUE%Zp;EJg&%gpG51R zEaCShxg#vo+S&BNK_~X_;Du&Y4d4XPuS*Gd>xXcb9TYJ`%O5+|xdZ(04l8rWsfcw} zw1Sm;^{yRNZ+2ws4P<6M99A@?Wd}s$1x1N023BPj15W=WcHvE*W%fU+-Lz7DhNtX) z75Xn_uye1P9SA{qj7l!=oX!;dZN^qspwHrw)P5NcBJ=s(ExLS=A%Q@k!^GX&`&=Fi z^dUiIm4#1ZLhiC1b_A!lqc9Wrb$)hWX6avWpK!K5%d{DZic1exz*i7G*6)FyxHw2 z1oA_l@AUrzq#%cdl%cqPNYzZ^-QXcHk##u>&}ZR)&Y`XOkBJG#v zYwze~Nc!TwQ`dCx>g4S6ZFeYRtwVJqYm~)#Lntu%_MsN(J6gdd#C{rer~rBMFqspV zyGPs)2akMm@WaWmq4BXd#@FTz48HRzr~hw=4Ln+et7A&^!GZF319Og6JJ&u}i}tSC z-M)LseV4%%+`V7M*XDCK2M+J5Ij?>sP`s<=9v*tgrn>yONR90`Rc=_$U+q4RV0+Cr zDOt*M2Y0_gnSs2);O@7Q*Nhx~C2(Hn$hprHzZama@@-M{LfWz%-Ddh}odZxPY&M-F z0@FrW@|uxeIMK2d#f&=X2gyPgO?2+QM$(yWhDA-2{xz8Q?#Mo)j|nCgRrJj<{k4Pi ziCHYD^&HJ+p^c^h3KmDZB4k4iy3J$(iC_Bvs#3$!*s0E#Aa&lZdy?n>w ztObZY7;l4YGdzAsZCBA$K~YV3pr-t+$jjEO?R2Zn zmx+Iv_&{+{x|Rs`vtqaT2#5{kkD+}!zp@3Uel>SQ_7zz|; z5>6oOJ*RY@|Gj{7Mke&Xf$Mz<<(N`7^c6y-(BZ!s%@LHp1`}Nsw3>aiN9lWr#9T+` z0rW%7+f`B5G&pRoX0-VA_(#C~K>5cqi^NeZ1l=~rs2EsxPh=g6`KzGi z&{h_TvYrc|6F(qAYyiUM=*LrBUB?M_IQZ|VoJ-Iau;4Y{eZEjn1Cf_R`)r zst`9^c+MrH@}vi4Mb)m~((Le((E48rjiqP8Rt&#OeMhk9X)SLk)l(C|3E8kvOFyX` zpjy6h@Y*9x^mY$kRaJ4(~b8K-8PiS(fH-c0A*R}ZKnUC=OJ<;yxX*vxXhufSVp zwZnE9;J(OiJ6a&`(nwhiS6DUCJ2HwYBon(7sZY~HPmFVy8DZBe+bis?)ZxRz8RkkR zp^M(%^Wz8xDQXu;bt^E0tXb z^iv?wXXMYCot~hLQm|f;qev+tMQS4HK0;Lh=oz-BEf7wnt;=m^csx#APYtMqW z+e#H1gU1U{AC3>Wudo`@e8N41DfksSfn5IE~#daG#uso zqT@x8i>>r7H?lK{YCr8B($Yl6P}#jAHH#eC)vssq6DpZ{u3nz}G1hO5M z6}DyR^Q@pqpsRKdrz*;`#ADAK$THyHpi^CN&_%Kykad5ss=E9*{>-W$v6LfOY|^pr zzjjaV`b+Nl?w6C+wN1Y>7%?r9Gro`xO-!v67M)r6q>m_y`=vyPnFK7BM&}f+IZHCh z5K{QlCDgz;)faW78_=o5e}%9N${+WWw2u8cFnS?gj>s66ZAy0EajIW{?^c%&Tm3^t zY6qofYha4>__B!R6{-I%(HXv+ZlazL{6H)B#$a|^xnt}Q$P#>ZFq|o?Mx}8G*{mYX z3NpP!8BZ15+OMaNn6;ivPh#m}=Vj0&dg%1O6J#J5rHIPzPv~qQl0UmDiNa|!z=Tj> z(39qUdI*oHVkgQTLDR7bkN5I?Oj_#Hyu=|-dr}jh87)EgjSw@HY8w2c@-dFgF0!3Z zJ>AQk&Ec=o?0kNUAfkD~60Y;(e3^lu{KP4r$I5vTZNH9{^Orm4?1>bH+%MOUp6jCL z%RDDrCF?gZ3d|xXy#fjtE$JvCa<)^T5B3?9R6ziORL_iRj2!T$UbxCPpi1G*q>+266YBRgh&mO%enFi^x2t&I_q?278LV(mDe#nWd||mcx0;`Y2h4 zEnM7%zKfmw1-#7wxxQ z9A7X;aZhZD!-zOX#J5L>I6eUs6{Db-Q8}PrIVmUz{8n%{EE7_xv{mk@mhY$dmO-Lt zNsA3Z(bY1(BxRALA#Mi4Y&sahX#?J#oP+rH;HqCbn=0kWIL%&PK8_O1`4&6Moi^&UR3 zXb*4(zB>9lR7&p7d_wm^&?|F2N3_kyK))ipkvrv}!`Fe1b21Og5;4Amp_ZKgQf%nB z(|`*-zWFva8NQqq!&T`_{NisJTJp4lJ_=Q3F(zoE|peu+dT z7Y*1+3>C&uns1cB4Dc%j#?RFJ&&G~}=gk}hI(|{4s2{b>FJR6=>Gl5m$e{i4O@d;_ zzsi0pp5&4dUF+R}zwfw6f=+*zCO9A6qXzz)28L*&wC6gW=`Z2rph)L+iz29?+oC1@ zcO6h=JK)rAUrSXA45#OMlO=vEO%UN-cbi~q(UT!~%Yz$fita-p^ z^?=D5AcYDpB8bC7UbraYb{>cx@gzN_$a#M0|NbWcvG2Ha+Xm^-$00@J#~0GSM?%e+ z&)sx-Aw@E9Bh8j<;|ItJ8SK<|8*HBpBn-s{%+Z!zA z%+MV}f1)3SG3+IqC)M%I7=Vlxvvj^W%5vr7(6Kf$IA;)Sa-6Bqd2q9zPdbk)#n1)) zvSJIm&%z;8PpB<*`u~%h5NQ0@JkwDc=E>&_Tvib0KFxXC-EXEZ1cBPGtlLiarT;R6 zPuYtte=g0}Wj3DobDv9}pg5DBcjuv_>9;kY%Ez>nE%zOvC3h!LQq1As1$T>^9*}?y zU`zgbD@THrPX#NV4$gV1%K65&^bd(8Y))F=9>~6Zb6ff@`}QpApl#_M^Hxks-frei zs;PV~-GFyAAnCen&Ih)ougtzts%%$Ejg(6L=kX@x6hD{#q?=iZM2L3jRJqbuB{w>0UZFN#c`iEvv^=o%9R3IX6 z#BP>3X%C6A6-ORSF8M8A^(p^~?Pp&4Yrur+lzGPSr#F(plMi7Ekun<5vq+{w_hA`Y zhfyEd!Nh@M8v2k9xC1=_()4^G6X`rOsN}aTeIE&QXDMC9{*{>#NPFvJl;w@ebgPu2 zlZ6!XDHDG9h2ckn?sK-xy}`qWhlhEWNPYRXg8dc>DCIj$u0Ei*94tOx(B{GF{w_U^ zDl{GKO+Urn*7~K!V2GZoQ_0z3x0)sE&{doQL^2QoYzUJbvm=Fb4&PC0m*XmNSzV9% zGYh7wQtwpT=}Z86{LYLsUgR^b^u)9v*D(Iv5=Jldfx;e@xoGVpxwV->)0gP(8}kE7 z^QMNbmo>&JiF|yveVz#b?16-{xgb)xf@?dP*D==Q- zO=r;yJ`%<=P7;KzHpDJ|0L5(Ro3cE(R0&d5b<%1$JAJIzMUbq(5T4mG%+YADEM6NY^i}7YEkgc`&jd&F4 zG)gtTAiq>}0#3Oudb3L*Cq8bx%!LoATsZLfGrj|$AzyrI&M#u|K%Pr(&6-r zToaI;iT>X)71i{=9Gww+6QxEyUqPP6#4g_e4gUTdr~e4ihhGZ1lgsvgw(P|r^2bh4 z=uU!TCyJeTJEQL9RsKE8{ArOxl>P%8-ge^G%PWK@y7%}(8Jvl$i*&Q4_l^4#Yf9Pl zJlR*4{my@jfYAZ+ZaJKu5;(J0QMs1OJ3RMJaT>+xTun+wwaN<*8-MOjGCw1AQ-cj>0xi@)ES-t&Uf<(rAE;zN`4+Ta`cBW&4dBJmc$`n}qjH0NIg5c+L_+%$8`(JfN z9bCCazAUid=VMNPKHZlM+Z1e~ zK%?_b79P-IUT2fyg#KGqRRhewfSf*G#txXq-sUHK)v|3^$>Fa5dcenn!l z{d700-_wmjOQKJu3bln<`V6`qW9joF)p<{+pwB6^E`1~I2%yb@I?&)ukzvPl!zZtt z!PmvuOWrV(gKlH`empIHOs3XH#Yx`)@Rr?-+Nwz3M)S41h5yM;#UE34%j~N&FZNsn zgg4M<$Qh$U^Tt&<@jD3eq!lEWThab`J6Q64-r{taVwvAke07d$J$fE9xIgn9#pA-8hW~5yI#aV5{^==k#m{vOgKal z`d@{BtaULt!;t)OnVh;vP53!flC_ytxjl8{A=MqDG3P*x>HAE77w-`BI~77%qo~4@ z4@T#Zrp7<6WN5mNJqff~=tJdn!#&`DmX9#WId@$QEWyNA*d4gN6a^#AxpP@W%NTIShe4pKnVS9VwJk5mbGX0I!FF`qz*&E&3rzKc<+aS`7TWmh%M0 zWK(L*e;6Y}VyUOv9jLB+`ln72Fiz!VTGmM60Rw%%IP7y4a^7j6f53Ih(IC=`7cE(KjiZD21n31exqk`{t1~`?P~o14?%QnDLtje zwa&rgeP9GhDs#5q6IGv3plbO(yz&t1BmF>JYN&NL5KFxVx5L&F_cz8cbvcIA;s`}AH_TkwiOQ34^UAEk6 zcc*0a^G0~at(22{`S)2U&00P8gqR&nJ=-(k;Bfshi-a}P`?1l6^4RfIsu{V|a`Kyv- zE~$Olqw1S9cgq`@+-6DYnk~gmqR&*++OpNq)09NfXsc1l9`8Gp++8Cl{u7{NdZ_od zy0)wbJII*NE5+h@77szi&I4OwM{ozQT=ekEu`NYniS&6`_}a7ebkqqk({)B5*>8|@ z7hOxuWz@1%AgwBt}pf+gmyEwGk>$>+p*?JYb6T;wtVqmeZ4M3|^Y=Xi z1L@^sMaO-Iw!XMs(&!ywhxi(GaeDh>9xyW%ODJKLiZF)(;0i8kMlvEPxYT0`1VKowlFer1B*&u zKxuBy)0`GO=naSJ;`d>0$dY1wmG1d_(mDO>d2#NaZV)id>3_rcE^^|#q?{*yj{8S? z{>9(ho)qIK8{xN@RB7_m?nl!Y_(m_u}tg-2QTU&x_7q z$+;UR{#o2TFYI~2Q~o&dorG+{GY;-78XT~ z9ITy8d81nuctW_!D5^ejz_!gJ;q)h^fW8m@7b%_oZPKp34}`TuCkCGQTb}7%@IS)7 zD+%Hc2`ZoXri9Dpop6w9cRx(zap|qpSCLfG5hAqj31ME=qj&04&fy)$`q3Ve{wvEH zp_e+wn7_{5n#snTdXJpJB6X$!PDul`9~#y+&CudGic^ugLCX3Y!Tl4mvy(nYKz>5b zp{FO~75g&iAzrK+df)kS)lm9-VD`mjLw3}r^T_ykVUhGG#P*lx?v5Ru9C17b%d%?+ z31Yu0)v4KizwcE^3vZ78Zg}^4hHKT(d(Ih***#VMJv`D?6t5j&r?IpcKMXt}G?Nzn zbL>|iOV{&!WqW!JF|VZ4H}lw&1xW17zH&5uJ&)1igq9i~YMki1? zD5H-?-KZkyC>dr7GK?IP;VYP{V(E*~k9G6?yOB>()3axidnQLP%Iq-1eMQ9XG;hB3 z*(2j~QqdJT2mVKAj($lqz946!6fQaUGD_J{SSIDAK6nZKm9v=5h|PKuC+GZ{Mz~vs z?^TpiXust51+K5$t_Q!zEXpvsF7j=j^WY$h;pC)ei%4wkSQ%EJ2k>g$*R0%E{wo6? zmSRiw=ydth#=-%s2#(_EA-uE8Qgs3^1IMs}%n}tsF{K9Gv&&-scT#1f8-tQB%R)>K z=*e`Oz7rKZ=f$Kf4>`MsY>(Pwl^pK*uNO}$hp*Tv=X~;MIhRw8;>6&YGe5TH;r`0Q zumCWY2!ievWwt7z2d6y$)W$RvB{?Ujt^u0d!>igUvXmH*?*#L6WWnCo?(6;a7t9&< zT&jwmCWK*5E6KVdbKb;QT2s^_dasf*Y@@q$nZRVtT7?4pcD-76)?VIF5WZrze3kAIup79P`1Pa4zI{Cg$4z zm%X=-kE^N{{wFWAX;X88RE&Z;U@D0gnjm@`C_2+l$^@oR<;5x>4QWavX%mx~w!E}W zoSC-AVcII(d-b{M#fy4TQSn}Zf?7I{UK ziU#sbwfHD1!!p;Gi~#lo8!1IIz&oTFeqW6_#oEy|(bRhD@2OR05Un+uTTyTAL{e#( zrACZ<3BDrphM`4n7(IjggSNLsgY~i+L;Yc0mk^Wf=no3+HFdv z&oTG@zx3FLs3G3fjv24q)Tv!H{JS90aw(r?P{5o5O0?mya}y2oIS9Qsp1XWhQQ8IQ zK+h=?6+6u`Q6v&{JXR*kKCZoBi6maqIL=cA7&7_FQUGZHsYI)o$&XOsvEK%0ru+C? zu9I|mSwdQH)$I}KuLS|e$`geI>ARQsD!!goi~;2+ zmeCK(zh-;wcIrPjbr##U`mU6in(p(t@$Ca;AP0BxOL>!o2_Txxg{mkKfLB4uR`8Z4 zzjS+cOZ}EM~p*>y@c`6BTGXkr>39-)`@3(PKYuAhpb$ zJOXKp@$9UL?+C)0NM!fxK?3U7_;vedV>P^z<9Idf{YFFs$W?wng7`^2UJNAq>)R`9 zDC6Al=YfXX6)&O{z3(pBPaT2jCGi}*8Js}e8b0nsKV?Y037W%kc?B=MwLH}uOI z`Q=>&`Lx}^c~QCE>lexftgl9IJU6XluEKs5zJai`YKHN8tFT{Ev#-jg2vqpFu+l%? z^)Qj7+8#?zh;JJSrz*$p-3Mzd(??_=u@@fLz_%emPn;n=BpbDI42@`Q)?jEO7g)Mz z<)w}M1+orB2R8%CiZL}u6t>$!T z`1|I>Ts+N$jBXEk?K%pF$PgNu7mE1pXUVzA*n{CE z^&HUH(!>Bc0!rIlH;@Z{Eo)a1h3AxPah=nQA=4ya!}jE~_To*{7q*Z-EAFLYN1bz)4x?J95AEOmJ+Z&G1no1pFf%^BdP>UcYZ&S~Dm-3o=^;na7c9`+?R#986N2VZLX&u0a zz+Zk>d4E>!)8FilR(MP3Z`~vFUIeG0$m4vh{b@}K>21~9MytK66dU-r{HCp?>Z=%=Csuv}137YEEX-y!G?TevN zps&o&K)}$O?X<9ydPlLA=S1)s9Su{en356sFBhhPt3=8Uw-Y@YpCfbLSIn}6Q#MD} z^YiP{TPHg4?b}AHq7`?uLAM2>#?WfR#=+rJ07Im30}@;D?n|8i4u#C5Wou{ahlTBu zF#c9#-9qpuJ2S|}M&>aFxuGo}>ra>6LuW%j=%Vh1$!hWjk@QxU#yw}M*)ow0LOdA< zO8jDH3-`B5bp!wOu)xl=f&{SZ0Rkgm>|_=SK@F~<

  • d5;MQ&twk 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